From 7d5c041a04cbf310ee7856a732bd4190e885fee4 Mon Sep 17 00:00:00 2001 From: c7w Date: Tue, 30 Jun 2026 20:37:28 +0800 Subject: [PATCH 1/4] Add Medal Score metric, v1-lite split, and released score artifacts - Medal Score: peer-relative gold/silver/bronze podium (normalized to [0,1]), reported on v1 (47 tasks) and the v1-lite subset (10 tasks). READMEs now lead with Medal Score; average rank stays on the website leaderboard. - leaderboard/: ship the frozen podium baselines (medal_podium.csv), published leaderboard (medal_leaderboard.csv), raw score table (exp1_models_raw.csv), a submission scorer (score_submission.py), and an example submission. Un-ignore leaderboard/*.csv. - v1-lite: add frontier_eval/conf/batch/v1_lite.yaml (10-task subset across all five categories, distinct families, gradual-improvement tasks). Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 2 + README.md | 38 +++++--- README_zh-CN.md | 31 ++++--- frontier_eval/conf/batch/v1_lite.yaml | 83 +++++++++++++++++ leaderboard/README.md | 62 +++++++++++++ leaderboard/exp1_models_raw.csv | 48 ++++++++++ leaderboard/medal_leaderboard.csv | 9 ++ leaderboard/medal_podium.csv | 48 ++++++++++ leaderboard/score_submission.py | 124 ++++++++++++++++++++++++++ leaderboard/submission_example.csv | 48 ++++++++++ 10 files changed, 469 insertions(+), 24 deletions(-) create mode 100644 frontier_eval/conf/batch/v1_lite.yaml create mode 100644 leaderboard/README.md create mode 100644 leaderboard/exp1_models_raw.csv create mode 100644 leaderboard/medal_leaderboard.csv create mode 100644 leaderboard/medal_podium.csv create mode 100644 leaderboard/score_submission.py create mode 100644 leaderboard/submission_example.csv diff --git a/.gitignore b/.gitignore index 8a45ae5c..bf3b45cf 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ benchmarks/Astrodynamics/MannedLunarLanding/results.txt benchmarks/Astrodynamics/MannedLunarLanding/outputlog.txt .vscode *.csv +!leaderboard/*.csv *.h5ad *.h5ad.tmp *.h5ad.tmp.* @@ -46,6 +47,7 @@ outputlog.txt frontier_eval/conf/batch/* !frontier_eval/conf/batch/example_matrix.yaml !frontier_eval/conf/batch/v1.yaml +!frontier_eval/conf/batch/v1_lite.yaml metrics.json artifacts.json debug-*.log diff --git a/README.md b/README.md index 0fdf60bf..98acef96 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ Frontier-Eng is a benchmark for **generative optimization**: agents iteratively The benchmark currently covers **47 tasks** across computing, quantum information, operations research, robotics and control, optics and communications, and physical sciences. The project homepage and paper frame it as a missing evaluation axis between pass/fail coding benchmarks and real engineering work: most engineering problems start from a feasible baseline and reward iterative improvement, not one-shot correctness. +## News + +- **2026-06-30** — **New scoring metric: the Medal Score (gold/silver/bronze).** Alongside average rank, we now release a peer-relative *Medal Score* (normalized to `[0,1]`). On each task the top-3 best-feasible scores in the v1 snapshot are frozen as gold/silver/bronze baselines; a model earns 1.00 / 0.67 / 0.33 for reaching each, averaged over the task set, and is reported on both v1 (47 tasks) and v1-lite (10 tasks). It rewards only reaching each task's frontier and ignores negligible long-tail margins, making cross-task aggregation fairer. Per-task podium values and the leaderboard live in [`leaderboard/`](leaderboard/README.md). +- **2026-06-30** — **`v1-lite` released.** A 10-task representative subset of `v1` covering all five categories with distinct benchmark families, selected for tasks whose scores climb gradually under budget (not one-shot-saturated or all-or-nothing). Run it with `frontier_eval/conf/batch/v1_lite.yaml`. ## 0. Host Requirements @@ -97,6 +101,14 @@ bash scripts/batch/validate_v1_task_envs.sh That command runs the batch config for the `v1` problem set with `algorithm.iterations=0`, which evaluates each task's shipped baseline instead of asking an LLM to improve it. +### `v1-lite` quick subset + +For fast iteration and ablations, use the 10-task `v1-lite` matrix +([`frontier_eval/conf/batch/v1_lite.yaml`](frontier_eval/conf/batch/v1_lite.yaml)) +instead of the full `v1` config. It spans all five categories with distinct +benchmark families and favors tasks whose scores improve gradually under budget, +so a short run still exercises the full optimization loop. + If you want the full `v1` problem set with normal optimization runs later, see [`run.md`](run.md). ## Where To Go Next @@ -108,18 +120,20 @@ If you want the full `v1` problem set with normal optimization runs later, see [ ## Leaderboard -Detailed leaderboard: [lab.einsia.ai/frontier-eng/leaderboard.html](https://lab.einsia.ai/frontier-eng/leaderboard.html) - -| Rank | Model | Average Rank | -| :--: | :--- | --: | -| 1 | GPT-5.4 | 3.54 | -| 2 | Claude Opus 4.6 | 3.63 | -| 3 | GLM-5 | 4.34 | -| 4 | DeepSeek V3.2 | 4.76 | -| 5 | Gemini 3.1 Pro Preview | 5.53 | -| 6 | Grok 4.20 | 5.82 | -| 7 | SEED 2.0 Pro | 5.86 | -| 8 | Qwen3 Coder Next | 6.71 | +Detailed leaderboard (incl. average rank): [lab.einsia.ai/frontier-eng/leaderboard](https://lab.einsia.ai/frontier-eng/leaderboard). Released score tables and the per-task medal podium: [`leaderboard/`](leaderboard/README.md). + +**Medal Score** (gold/silver/bronze podium, higher is better, normalized to `[0,1]` = mean per-task podium credit). On each task the top-3 best scores in the **v1 snapshot (2026-04-14)** are frozen as gold/silver/bronze baselines; a model earns 1.00 / 0.67 / 0.33 for reaching each. Reported on both the full **v1** set (47 tasks) and the **v1-lite** subset (10 tasks); gold/silver/bronze counts are for v1 (see [`leaderboard/`](leaderboard/README.md)): + +| Rank | Model | Medal (v1) | Medal (v1-lite) | 🥇 | 🥈 | 🥉 | +| :--: | :--- | --: | --: | --: | --: | --: | +| 1 | GPT-5.4 | 0.596 | 0.667 | 24 | 5 | 2 | +| 2 | Claude Opus 4.6 | 0.490 | 0.501 | 9 | 18 | 6 | +| 3 | GLM-5 | 0.312 | 0.233 | 4 | 10 | 12 | +| 4 | DeepSeek V3.2 | 0.248 | 0.166 | 3 | 9 | 8 | +| 5 | Gemini 3.1 Pro Preview | 0.213 | 0.200 | 3 | 6 | 9 | +| 6 | Seed 2.0 Pro | 0.185 | 0.100 | 3 | 7 | 3 | +| 7 | Grok 4.20 | 0.184 | 0.133 | 3 | 6 | 5 | +| 8 | Qwen3 Coder Next | 0.121 | 0.000 | 3 | 3 | 2 | ## Contributing diff --git a/README_zh-CN.md b/README_zh-CN.md index 75ddd213..26e66e5b 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -8,6 +8,11 @@ Frontier-Eng 是一个面向 **generative optimization** 的 benchmark:Agent 当前版本包含 **47 个任务**,覆盖计算系统、量子信息、运筹优化、机器人控制、光学通信、物理与工程设计。主页和论文的核心观点是:真实工程问题通常从一个可行 baseline 出发,价值来自持续优化,而不是 pass/fail。 +## News + +- **2026-06-30** — **新增评测指标:金银铜 Medal Score。** 在 average rank 之外,我们发布同侪相对的 *Medal Score*(归一化到 `[0,1]`):每道题取 v1 snapshot 中最好的前三名分数冻结为金/银/铜 baseline,模型达到金/银/铜分别得 1.00 / 0.67 / 0.33,对题集求均值;同时汇报 v1(47 题)与 v1-lite(10 题)。它只奖励"达到该题最前沿(领奖台)",忽略长尾里可忽略的微小差距,使跨题汇总更公平。每题 podium 分数与榜单见 [`leaderboard/`](leaderboard/README.md)。 +- **2026-06-30** — **发布 `v1-lite`。** `v1` 的 10 题代表性子集,覆盖全部五大类、family 各不相同,专选"分数随预算逐步提升(而非一步做满或非高即低)"的题,配置见 `frontier_eval/conf/batch/v1_lite.yaml`。 + ## 这个 benchmark 在测什么 和传统 agent benchmark 相比,Frontier-Eng 更关注三件事: @@ -110,18 +115,20 @@ bash scripts/batch/validate_v1_task_envs.sh ## Leaderboard -详细榜单见 [lab.einsia.ai/frontier-eng/leaderboard.html](https://lab.einsia.ai/frontier-eng/leaderboard.html)。 - -| 排名 | Model | Average Rank | -| :--: | :--- | --: | -| 1 | Claude Opus 4.6 | 3.18 | -| 2 | GLM-5 | 4.02 | -| 3 | DeepSeek V3.2 | 4.41 | -| 4 | Gemini 3.1 Pro Preview | 5.34 | -| 5 | Grok 4.20 | 5.60 | -| 6 | SEED 2.0 Pro | 5.63 | -| 7 | GPT-5.4 | 5.68 | -| 8 | Qwen3 Coder Next | 6.68 | +详细榜单(含 average rank)见 [lab.einsia.ai/frontier-eng/leaderboard](https://lab.einsia.ai/frontier-eng/leaderboard)。发布的分数表与每题金银铜 podium 见 [`leaderboard/`](leaderboard/README.md)。 + +**Medal Score**(金银铜 podium,越高越好,归一化到 `[0,1]`,即每题领奖台得分的均值)。每题取 **v1 snapshot (2026-04-14)** 的前三名分数冻结为金/银/铜 baseline,模型达到金/银/铜分别得 1.00 / 0.67 / 0.33。同时汇报 **v1**(47 题)与 **v1-lite**(10 题)两个集合;金银铜次数为 v1(`gpt-5.4` 采用其 47 题全量重测结果): + +| 排名 | Model | Medal (v1) | Medal (v1-lite) | 🥇 | 🥈 | 🥉 | +| :--: | :--- | --: | --: | --: | --: | --: | +| 1 | GPT-5.4 | 0.596 | 0.667 | 24 | 5 | 2 | +| 2 | Claude Opus 4.6 | 0.490 | 0.501 | 9 | 18 | 6 | +| 3 | GLM-5 | 0.312 | 0.233 | 4 | 10 | 12 | +| 4 | DeepSeek V3.2 | 0.248 | 0.166 | 3 | 9 | 8 | +| 5 | Gemini 3.1 Pro Preview | 0.213 | 0.200 | 3 | 6 | 9 | +| 6 | Seed 2.0 Pro | 0.185 | 0.100 | 3 | 7 | 3 | +| 7 | Grok 4.20 | 0.184 | 0.133 | 3 | 6 | 5 | +| 8 | Qwen3 Coder Next | 0.121 | 0.000 | 3 | 3 | 2 | ## 贡献 diff --git a/frontier_eval/conf/batch/v1_lite.yaml b/frontier_eval/conf/batch/v1_lite.yaml new file mode 100644 index 00000000..284cc45a --- /dev/null +++ b/frontier_eval/conf/batch/v1_lite.yaml @@ -0,0 +1,83 @@ +# Frontier-Eng v1-lite batch matrix. +# +# v1-lite is a 10-task representative subset of the full 47-task v1 set, chosen +# for fast iteration and ablation. Tasks are selected to (a) cover all five +# engineering categories with distinct benchmark families and (b) reward +# incremental optimization -- their best-feasible scores climb gradually with +# budget instead of saturating in one shot or splitting all-or-nothing. The +# selection rule (top-graduality task per family, 2 per category) is documented +# and reproduced by Einsia-lab/scripts/build_frontier_metrics.py. +# +# Overrides for each task mirror frontier_eval/conf/batch/v1.yaml exactly. +# +version: 1 +tasks: +- name: unified + label: ComputerSystems/MallocLab + overrides: + - task.benchmark=ComputerSystems/MallocLab +- name: unified + label: QuantumComputing/task_01_routing_qftentangled + overrides: + - task.benchmark=QuantumComputing/task_01_routing_qftentangled + - task.runtime.env_name=frontier-v1-main +- name: unified + label: JobShop/abz + overrides: + - task.benchmark=JobShop/abz + - task.runtime.python_path=uv-env:frontier-v1-main +- name: unified + label: InventoryOptimization/disruption_eoqd + overrides: + - task.benchmark=InventoryOptimization/disruption_eoqd + - task.runtime.env_name=frontier-v1-main +- name: unified + label: EnergyStorage/BatteryFastChargingSPMe + overrides: + - task.benchmark=EnergyStorage/BatteryFastChargingSPMe +- name: unified + label: Robotics/RobotArmCycleTimeOptimization + overrides: + - task.benchmark=Robotics/RobotArmCycleTimeOptimization + - task.runtime.env_name=frontier-v1-main + - algorithm.oe.evaluator.timeout=600 +- name: unified + label: Optics/holographic_multiplane_focusing + overrides: + - task.benchmark=Optics/holographic_multiplane_focusing + - task.runtime.env_name=frontier-v1-main + - algorithm.oe.evaluator.timeout=600 +- name: unified + label: WirelessChannelSimulation/HighReliableSimulation + overrides: + - task.benchmark=WirelessChannelSimulation/HighReliableSimulation +- name: unified + label: ReactionOptimisation/snar_multiobjective + overrides: + - task.benchmark=ReactionOptimisation/snar_multiobjective + - task.runtime.python_path=uv-env:frontier-v1-summit + - algorithm.oe.evaluator.timeout=600 +- name: unified + label: StructuralOptimization/TopologyOptimization + overrides: + - task.benchmark=StructuralOptimization/TopologyOptimization + - task.runtime.env_name=frontier-v1-main +algorithms: +- name: openevolve + overrides: + - algorithm.iterations=100 + - algorithm.oe.evaluator.timeout=300 +llms: +- name: v1 + api_base: ${oc.env:OPENAI_API_BASE,https://api.openai.com/v1} + model: ${oc.env:OPENAI_MODEL,gpt-4o-mini} + api_key_env: OPENAI_API_KEY +llm_config: openai_compatible +common_overrides: +- llm.temperature=0.7 +- llm.timeout=60 +run: + name: v1_lite + base_dir: runs/batch + max_parallel: 4 + fail_fast: false diff --git a/leaderboard/README.md b/leaderboard/README.md new file mode 100644 index 00000000..25e039b9 --- /dev/null +++ b/leaderboard/README.md @@ -0,0 +1,62 @@ +# Leaderboard & Medal Score + +Released score artifacts for the Frontier-Eng `v1` set (Experiment 1: foundation +models under `openevolve`, 100 iterations, same initial programs and frozen +verifiers; `gpt-5.4` uses its full 47-task retest). + +| File | Contents | +|---|---| +| `medal_podium.csv` | Frozen per-task **gold / silver / bronze** threshold scores and the model that set each. | +| `medal_leaderboard.csv` | Per-model normalized **Medal Score** on v1 and v1-lite, with gold/silver/bronze counts. | +| `exp1_models_raw.csv` | Best-feasible score of each model on each of the 47 tasks (higher is better); source of the podium. | +| `score_submission.py` | Scores a new submission against the frozen podium. | +| `submission_example.csv` | Example submission (claude-opus-4.6) — scoring it reproduces its leaderboard line. | + +## Medal Score + +On each task the top-3 best scores in the **v1 snapshot (2026-04-14)** are frozen +as peer baselines — gold (1st), silver (2nd), bronze (3rd). A model earns +**1.00** for reaching the gold score, **0.67** for silver, **0.33** for bronze, +otherwise 0; its Medal Score is the **mean** of this credit over a task set +(normalized to `[0,1]`). It credits only reaching each task's frontier (the +podium) and ignores negligible margins in the long tail — a fairer aggregate +than crediting every ordinal rank when the question is "how often does a model +reach the best-known solutions?" We report it on both the full **v1** set +(47 tasks) and the **v1-lite** subset (10 tasks). (Average rank and other +diagnostics are on the [website leaderboard](https://lab.einsia.ai/frontier-eng/leaderboard).) + +> `gpt-oss-120b` is part of the paper's 9-model rank tables, but its per-task raw +> scores were not retained; the released podium is therefore computed over the 8 +> models with available raw scores. + +## Medal leaderboard (normalized; gold/silver/bronze counts are for v1) + +| Rank | Model | Medal (v1) | Medal (v1-lite) | 🥇 | 🥈 | 🥉 | +| :--: | :--- | --: | --: | --: | --: | --: | +| 1 | gpt-5.4 | 0.596 | 0.667 | 24 | 5 | 2 | +| 2 | claude-opus-4.6 | 0.490 | 0.501 | 9 | 18 | 6 | +| 3 | glm-5 | 0.312 | 0.233 | 4 | 10 | 12 | +| 4 | deepseek-v3.2 | 0.248 | 0.166 | 3 | 9 | 8 | +| 5 | gemini-3.1-pro-preview | 0.213 | 0.200 | 3 | 6 | 9 | +| 6 | seed-2.0-pro | 0.185 | 0.100 | 3 | 7 | 3 | +| 7 | grok-4.20 | 0.184 | 0.133 | 3 | 6 | 5 | +| 8 | qwen3-coder-next | 0.121 | 0.000 | 3 | 3 | 2 | + +## Score your own model + +Put your model's best score per task in a CSV (`Task,Score`, one row per task, +task names as in `medal_podium.csv`), then: + +```bash +python leaderboard/score_submission.py your_scores.csv +# -> Medal Score (v1, 47 tasks) : 0.xxx (gold .., silver .., bronze ..) +# Medal Score (v1-lite, 10 tasks) : 0.xxx +``` + +Sanity check (reproduces claude-opus-4.6's line, 0.490 / 0.501): + +```bash +python leaderboard/score_submission.py leaderboard/submission_example.csv +``` + +Interactive view: [lab.einsia.ai/frontier-eng/leaderboard](https://lab.einsia.ai/frontier-eng/leaderboard) diff --git a/leaderboard/exp1_models_raw.csv b/leaderboard/exp1_models_raw.csv new file mode 100644 index 00000000..1b720953 --- /dev/null +++ b/leaderboard/exp1_models_raw.csv @@ -0,0 +1,48 @@ +Task,Baseline,claude-opus-4.6_best,deepseek-v3.2_best,gemini-3.1-pro-preview_best,glm-5_best,gpt-5.4_best,grok-4.20_best,qwen3-coder-next_best,seed-2.0-pro_best,,,,,,,,,, +Aerodynamics_CarAerodynamicsSensing,0.9617,0.9624,0.9632,0.9632,0.9628,0.9630695838481188,0.9624,0.9632,0.9624,,,,,,,,,, +Astrodynamics_MannedLunarLanding,4577.437,6027.3126,6079.2455,4674.9462,6839.0331,6660.942428,4577.437,4577.437,4733.0435,,,,,,,,,, +ComputerSystems_MallocLab,28,96,53,48,86,28,57,32,38,,,,,,,,,, +Cryptographic_AES-128,7.5209,11.8617,12.4591,10.2396,7.9669,39.824967043300866,10.8615,5.5501,7.9481,,,,,,,,,, +Cryptographic_SHA-256,9.8274,16.7955,9.718,9.942,15.1655,26.34045367870492,17.2504,9.8475,15.2838,,,,,,,,,, +Cryptographic_SHA3-256,16.0932,17.4003,17.0749,16.2255,17.5778,37.44512785396786,16.0594,16.5292,18.3478,,,,,,,,,, +EnergyStorage_BatteryFastChargingProfile,71.2806,120.8025,111.4518,116.6532,118.7678,121.99136502281442,99.6875,89.8416,115.6882,,,,,,,,,, +EnergyStorage_BatteryFastChargingSPMe,66.1636,71.8225,91.0079,92.3198,78.0896,122.94304361063023,76.4657,79.0273,76.4122,,,,,,,,,, +EngDesign,1.3571,1.3571,21.7143,27,25.5714,1.3571428571428572,27,25.5714,27,,,,,,,,,, +InventoryOptimization_disruption_eoqd,0.3642,0.6473,0.6381,0.639,0.6303,1,0.6359,0.6225,0.6321,,,,,,,,,, +InventoryOptimization_finite_horizon_dp,0.3673,0.9596,0.8025,0.7559,0.7965,0.9606835281410351,0.8547,0.4413,0.7323,,,,,,,,,, +InventoryOptimization_general_meio,0.1825,0.9929,0.9893,0.9839,0.9165,0.9999999999999999,0.9236,0.7819,0.6973,,,,,,,,,, +InventoryOptimization_joint_replenishment,0.3034,0.8822,0.8822,0.8822,0.8822,1,0.8822,0.8821,0.8822,,,,,,,,,, +InventoryOptimization_tree_gsm_safety_stock,0.3813,0.75,0.6606,0.6606,0.6606,1,0.6606,0.6606,0.6606,,,,,,,,,, +JobShop_abz,80.5042,96.1035,88.3614,86.751,88.4924,91.23143065488635,87.6717,85.603,86.672,,,,,,,,,, +JobShop_swv,81.6325,89.4966,82.3575,82.3141,87.1611,87.33430826602005,85.5068,82.6129,82.4153,,,,,,,,,, +JobShop_ta,78.8,90.8322,84.9043,85.7065,86.8095,86.16070055174835,84.9136,85.5489,83.9694,,,,,,,,,, +KernelEngineering_FlashAttention,55.2957,983.5001,987.2034,991.8896,381.6257,182687.44188255747,324.919,525.5567,1218.5163,,,,,,,,,, +KernelEngineering_MLA,0.7828,1000.3859,0.8936,1253.2017,20.1972,1132.0659025372765,19.8651,0.9271,19.987,,,,,,,,,, +KernelEngineering_TriMul,47.1274,357.1636,85.5923,54.5774,110.8785,47.88292233116043,165.0294,49.1232,84.9069,,,,,,,,,, +Optics_adaptive_fault_tolerant_fusion,0.3959,0.6398,0.64,0.6398,0.6398,0.455046169,0.6398,0.6398,0.6398,,,,,,,,,, +Optics_adaptive_temporal_smooth_control,0.3152,0.8419,0.8419,0.8419,0.8417,0.841880414,0.842,0.8421,0.8421,,,,,,,,,, +Optics_fiber_guardband_spectrum_packing,0.3861,0.6692,0.657,0.6629,0.6692,0.6754289215686274,0.6629,0.657,0.657,,,,,,,,,, +Optics_fiber_mcs_power_scheduling,0.3297,0.6542,0.5182,0.4796,0.6491,0.6608370951757289,0.4557,0.4458,0.6491,,,,,,,,,, +Optics_fiber_wdm_channel_power_allocation,0.3255,0.6675,0.6679,0.6619,0.6686,0.6964207451370852,0.6664,0.6666,0.6654,,,,,,,,,, +Optics_holographic_multifocus_power_ratio,0.3927,0.8072,0.8265,0.5368,0.711,0.9999999999663148,0.4058,0.5875,0.5626,,,,,,,,,, +Optics_holographic_multiplane_focusing,0.3302,0.6002,0.7196,0.4398,0.4516,0.9999999999886867,0.474,0.5631,0.5303,,,,,,,,,, +Optics_phase_dammann_uniform_orders,26.8969,99.7995,97.3436,97.9498,97.8709,99.99999999999999,94.4055,95.9998,69.0576,,,,,,,,,, +Optics_phase_fourier_pattern_holography,32.6457,82.1276,74.5838,76.6371,76.0127,99.99998936790779,74.217,67.3393,72.4578,,,,,,,,,, +PyPortfolioOpt_robust_mvo_rebalance,32.9804,99.9946,84.941,77.165,82.8015,99.99460428985267,99.983,85.5194,83.0681,,,,,,,,,, +QuantumComputing_task_01_routing_qftentangled,0.209,5.0479,3.6155,0.209,3.7681,6.507945106686525,3.7655,3.2471,3.6783,,,,,,,,,, +QuantumComputing_task_02_clifford_t_synthesis,1.7134,1.6633,1.7134,1.7134,7.4236,1.7133669376223557,1.6633,1.7134,1.7134,,,,,,,,,, +QuantumComputing_task_03_cross_target_qaoa,2.4149,2.5781,5.103,2.9782,5.0301,2.4149139615375192,2.6363,2.4517,2.9782,,,,,,,,,, +ReactionOptimisation_mit_case1_mixed,87.3082,98.6621,98.6041,96.5437,95.9314,98.66214557690091,87.3082,95.3732,95.4297,,,,,,,,,, +ReactionOptimisation_reizman_suzuki_pareto,63.5202,82.3427,82.0329,79.473,82.9901,82.24612252072882,63.5202,81.4666,79.7011,,,,,,,,,, +ReactionOptimisation_snar_multiobjective,57.5234,87.3657,82.7881,80.1521,81.7614,100,72.3909,72.8477,79.427,,,,,,,,,, +Robotics_DynamicObstacleAvoidanceNavigation,0.0722,0.086,0.0856,0.0834,0.0857,0.08571428571428559,0.0817,0.0765,0.0855,,,,,,,,,, +Robotics_PIDTuning,0.0366,0.1632,0.151,0.1521,0.1515,0.1511172761100511,0.1585,0.1422,0.1514,,,,,,,,,, +Robotics_QuadrupedGaitOptimization,0.0218,0.0219,0.0749,0.0218,0.1085,0.022154337029969478,0.0227,0.0232,0.0218,,,,,,,,,, +Robotics_RobotArmCycleTimeOptimization,0.2922,0.4158,0.3923,0.4305,0.4219,0.4356212836221511,0.3923,0.3155,0.3256,,,,,,,,,, +Robotics_UAVInspectionCoverageWithWind,28.8519,28.8519,38.8024,28.8519,35.1121,30.121714802877325,55.9109,32.8468,32.1552,,,,,,,,,, +SingleCellAnalysis_predict_modality,0.5467,0.5467,0.5467,0.5467,0.5467,1,0.5467,0.5467,0.5467,,,,,,,,,, +StructuralOptimization_ISCSO2015,-5401.589,-968.4567,-1120.212,-5401.589,-1139.3354,-5401.589002,-1318.7566,-1308.2575,-1302.2288,,,,,,,,,, +StructuralOptimization_ISCSO2023,-77813242.9,-16477799.48,-55182772.3,-20092179.33,-17840974.17,-77813242.9,-30028112.28,-66126744.97,-42625693.78,,,,,,,,,, +StructuralOptimization_TopologyOptimization,-195.9153,-190.1498,-190.3706,-189.3039,-188.4673,-195.9152621,-185.7983,-192.8488,-190.0603,,,,,,,,,, +SustainableDataCenterControl_hand_written_control,8.3294,21.5657,15.292,12.9088,19.5978,8.5903,14.2432,30.1873,29.2868,,,,,,,,,, +WirelessChannelSimulation_HighReliableSimulation,192.5193,292.3228,291.9451,232.9071,248.0119,231.22403446412542,245.7082,259.9776,304.0437,,,,,,,,,, \ No newline at end of file diff --git a/leaderboard/medal_leaderboard.csv b/leaderboard/medal_leaderboard.csv new file mode 100644 index 00000000..20b35004 --- /dev/null +++ b/leaderboard/medal_leaderboard.csv @@ -0,0 +1,9 @@ +Rank,Model,Medal_v1,Medal_v1lite,Gold,Silver,Bronze +1,gpt-5.4,0.596,0.667,24,5,2 +2,claude-opus-4.6,0.49,0.501,9,18,6 +3,glm-5,0.312,0.233,4,10,12 +4,deepseek-v3.2,0.248,0.166,3,9,8 +5,gemini-3.1-pro-preview,0.213,0.2,3,6,9 +6,seed-2.0-pro,0.185,0.1,3,7,3 +7,grok-4.20,0.184,0.133,3,6,5 +8,qwen3-coder-next,0.121,0.0,3,3,2 diff --git a/leaderboard/medal_podium.csv b/leaderboard/medal_podium.csv new file mode 100644 index 00000000..8059fced --- /dev/null +++ b/leaderboard/medal_podium.csv @@ -0,0 +1,48 @@ +Task,Baseline,Gold,Gold_model,Silver,Silver_model,Bronze,Bronze_model +Aerodynamics_CarAerodynamicsSensing,0.9617,0.9632,deepseek-v3.2/gemini-3.1-pro-preview/qwen3-coder-next,0.9632,deepseek-v3.2/gemini-3.1-pro-preview/qwen3-coder-next,0.9632,deepseek-v3.2/gemini-3.1-pro-preview/qwen3-coder-next +Astrodynamics_MannedLunarLanding,4577.437,6839.0331,glm-5,6660.942428,gpt-5.4,6079.2455,deepseek-v3.2 +ComputerSystems_MallocLab,28,96.0,claude-opus-4.6,86.0,glm-5,57.0,grok-4.20 +Cryptographic_AES-128,7.5209,39.824967043300866,gpt-5.4,12.4591,deepseek-v3.2,11.8617,claude-opus-4.6 +Cryptographic_SHA-256,9.8274,26.34045367870492,gpt-5.4,17.2504,grok-4.20,16.7955,claude-opus-4.6 +Cryptographic_SHA3-256,16.0932,37.44512785396786,gpt-5.4,18.3478,seed-2.0-pro,17.5778,glm-5 +EnergyStorage_BatteryFastChargingProfile,71.2806,121.99136502281442,gpt-5.4,120.8025,claude-opus-4.6,118.7678,glm-5 +EnergyStorage_BatteryFastChargingSPMe,66.1636,122.94304361063023,gpt-5.4,92.3198,gemini-3.1-pro-preview,91.0079,deepseek-v3.2 +EngDesign,1.3571,27.0,gemini-3.1-pro-preview/grok-4.20/seed-2.0-pro,27.0,gemini-3.1-pro-preview/grok-4.20/seed-2.0-pro,27.0,gemini-3.1-pro-preview/grok-4.20/seed-2.0-pro +InventoryOptimization_disruption_eoqd,0.3642,1.0,gpt-5.4,0.6473,claude-opus-4.6,0.639,gemini-3.1-pro-preview +InventoryOptimization_finite_horizon_dp,0.3673,0.9606835281410351,gpt-5.4,0.9596,claude-opus-4.6,0.8547,grok-4.20 +InventoryOptimization_general_meio,0.1825,0.9999999999999999,gpt-5.4,0.9929,claude-opus-4.6,0.9893,deepseek-v3.2 +InventoryOptimization_joint_replenishment,0.3034,1.0,gpt-5.4,0.8822,claude-opus-4.6/deepseek-v3.2/gemini-3.1-pro-preview/glm-5/grok-4.20/seed-2.0-pro,0.8822,claude-opus-4.6/deepseek-v3.2/gemini-3.1-pro-preview/glm-5/grok-4.20/seed-2.0-pro +InventoryOptimization_tree_gsm_safety_stock,0.3813,1.0,gpt-5.4,0.75,claude-opus-4.6,0.6606,deepseek-v3.2/gemini-3.1-pro-preview/glm-5/grok-4.20/qwen3-coder-next/seed-2.0-pro +JobShop_abz,80.5042,96.1035,claude-opus-4.6,91.23143065488635,gpt-5.4,88.4924,glm-5 +JobShop_swv,81.6325,89.4966,claude-opus-4.6,87.33430826602005,gpt-5.4,87.1611,glm-5 +JobShop_ta,78.8,90.8322,claude-opus-4.6,86.8095,glm-5,86.16070055174835,gpt-5.4 +KernelEngineering_FlashAttention,55.2957,182687.44188255747,gpt-5.4,1218.5163,seed-2.0-pro,991.8896,gemini-3.1-pro-preview +KernelEngineering_MLA,0.7828,1253.2017,gemini-3.1-pro-preview,1132.0659025372765,gpt-5.4,1000.3859,claude-opus-4.6 +KernelEngineering_TriMul,47.1274,357.1636,claude-opus-4.6,165.0294,grok-4.20,110.8785,glm-5 +Optics_adaptive_fault_tolerant_fusion,0.3959,0.64,deepseek-v3.2,0.6398,claude-opus-4.6/gemini-3.1-pro-preview/glm-5/grok-4.20/qwen3-coder-next/seed-2.0-pro,0.6398,claude-opus-4.6/gemini-3.1-pro-preview/glm-5/grok-4.20/qwen3-coder-next/seed-2.0-pro +Optics_adaptive_temporal_smooth_control,0.3152,0.8421,qwen3-coder-next/seed-2.0-pro,0.8421,qwen3-coder-next/seed-2.0-pro,0.842,grok-4.20 +Optics_fiber_guardband_spectrum_packing,0.3861,0.6754289215686274,gpt-5.4,0.6692,claude-opus-4.6/glm-5,0.6692,claude-opus-4.6/glm-5 +Optics_fiber_mcs_power_scheduling,0.3297,0.6608370951757289,gpt-5.4,0.6542,claude-opus-4.6,0.6491,glm-5/seed-2.0-pro +Optics_fiber_wdm_channel_power_allocation,0.3255,0.6964207451370852,gpt-5.4,0.6686,glm-5,0.6679,deepseek-v3.2 +Optics_holographic_multifocus_power_ratio,0.3927,0.9999999999663148,gpt-5.4,0.8265,deepseek-v3.2,0.8072,claude-opus-4.6 +Optics_holographic_multiplane_focusing,0.3302,0.9999999999886867,gpt-5.4,0.7196,deepseek-v3.2,0.6002,claude-opus-4.6 +Optics_phase_dammann_uniform_orders,26.8969,99.99999999999999,gpt-5.4,99.7995,claude-opus-4.6,97.9498,gemini-3.1-pro-preview +Optics_phase_fourier_pattern_holography,32.6457,99.99998936790779,gpt-5.4,82.1276,claude-opus-4.6,76.6371,gemini-3.1-pro-preview +PyPortfolioOpt_robust_mvo_rebalance,32.9804,99.99460428985267,gpt-5.4,99.9946,claude-opus-4.6,99.983,grok-4.20 +QuantumComputing_task_01_routing_qftentangled,0.209,6.507945106686525,gpt-5.4,5.0479,claude-opus-4.6,3.7681,glm-5 +QuantumComputing_task_02_clifford_t_synthesis,1.7134,7.4236,glm-5,1.7134,deepseek-v3.2/gemini-3.1-pro-preview/qwen3-coder-next/seed-2.0-pro,1.7134,deepseek-v3.2/gemini-3.1-pro-preview/qwen3-coder-next/seed-2.0-pro +QuantumComputing_task_03_cross_target_qaoa,2.4149,5.103,deepseek-v3.2,5.0301,glm-5,2.9782,gemini-3.1-pro-preview/seed-2.0-pro +ReactionOptimisation_mit_case1_mixed,87.3082,98.66214557690091,gpt-5.4,98.6621,claude-opus-4.6,98.6041,deepseek-v3.2 +ReactionOptimisation_reizman_suzuki_pareto,63.5202,82.9901,glm-5,82.3427,claude-opus-4.6,82.24612252072882,gpt-5.4 +ReactionOptimisation_snar_multiobjective,57.5234,100.0,gpt-5.4,87.3657,claude-opus-4.6,82.7881,deepseek-v3.2 +Robotics_DynamicObstacleAvoidanceNavigation,0.0722,0.086,claude-opus-4.6,0.08571428571428559,gpt-5.4,0.0857,glm-5 +Robotics_PIDTuning,0.0366,0.1632,claude-opus-4.6,0.1585,grok-4.20,0.1521,gemini-3.1-pro-preview +Robotics_QuadrupedGaitOptimization,0.0218,0.1085,glm-5,0.0749,deepseek-v3.2,0.0232,qwen3-coder-next +Robotics_RobotArmCycleTimeOptimization,0.2922,0.4356212836221511,gpt-5.4,0.4305,gemini-3.1-pro-preview,0.4219,glm-5 +Robotics_UAVInspectionCoverageWithWind,28.8519,55.9109,grok-4.20,38.8024,deepseek-v3.2,35.1121,glm-5 +SingleCellAnalysis_predict_modality,0.5467,1.0,gpt-5.4,0.5467,claude-opus-4.6/deepseek-v3.2/gemini-3.1-pro-preview/glm-5/grok-4.20/qwen3-coder-next/seed-2.0-pro,0.5467,claude-opus-4.6/deepseek-v3.2/gemini-3.1-pro-preview/glm-5/grok-4.20/qwen3-coder-next/seed-2.0-pro +StructuralOptimization_ISCSO2015,-5401.589,-968.4567,claude-opus-4.6,-1120.212,deepseek-v3.2,-1139.3354,glm-5 +StructuralOptimization_ISCSO2023,-77813242.9,-16477799.48,claude-opus-4.6,-17840974.17,glm-5,-20092179.33,gemini-3.1-pro-preview +StructuralOptimization_TopologyOptimization,-195.9153,-185.7983,grok-4.20,-188.4673,glm-5,-189.3039,gemini-3.1-pro-preview +SustainableDataCenterControl_hand_written_control,8.3294,30.1873,qwen3-coder-next,29.2868,seed-2.0-pro,21.5657,claude-opus-4.6 +WirelessChannelSimulation_HighReliableSimulation,192.5193,304.0437,seed-2.0-pro,292.3228,claude-opus-4.6,291.9451,deepseek-v3.2 diff --git a/leaderboard/score_submission.py b/leaderboard/score_submission.py new file mode 100644 index 00000000..1c25561e --- /dev/null +++ b/leaderboard/score_submission.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Score a submission against the frozen Frontier-Eng Medal podium. + +The gold/silver/bronze baselines are frozen at the v1 snapshot (2026-04-14) and +shipped in ``medal_podium.csv``. This script takes a new model's best-feasible +score on each task and reports its Medal Score, so anyone can be scored against +the released benchmark without rerunning the reference models. + +Usage +----- + python leaderboard/score_submission.py [--verbose] + +Submission CSV format (header required): two columns, ``Task,Score``, one row +per task, using the task names from ``medal_podium.csv`` (e.g. ``JobShop_abz``). +Higher score is better on every task. Missing tasks score 0. See +``submission_example.csv`` (the claude-opus-4.6 column) for a working example; +scoring it reproduces its leaderboard line (Medal v1 = 0.490, v1-lite = 0.501). + +Metric +------ +On each task a submission earns 1.00 / 0.67 / 0.33 for reaching the gold / +silver / bronze score, else 0. The Medal Score is the mean of this credit, +normalized to [0, 1], reported on the full v1 set (47 tasks) and the v1-lite +subset (10 tasks). +""" + +import argparse +import csv +from pathlib import Path + +HERE = Path(__file__).resolve().parent +GOLD, SILVER, BRONZE = 1.00, 0.67, 0.33 + +# v1-lite: 10-task representative subset (frontier_eval/conf/batch/v1_lite.yaml). +V1_LITE = { + "QuantumComputing_task_01_routing_qftentangled", "ComputerSystems_MallocLab", + "JobShop_abz", "InventoryOptimization_disruption_eoqd", + "EnergyStorage_BatteryFastChargingSPMe", "Robotics_RobotArmCycleTimeOptimization", + "Optics_holographic_multiplane_focusing", "WirelessChannelSimulation_HighReliableSimulation", + "ReactionOptimisation_snar_multiobjective", "StructuralOptimization_TopologyOptimization", +} + + +def load_podium(path): + """task -> (gold, silver, bronze) thresholds (higher is better).""" + podium = {} + with open(path, encoding="utf-8-sig") as f: + for row in csv.DictReader(f): + podium[row["Task"]] = ( + float(row["Gold"]), float(row["Silver"]), float(row["Bronze"])) + return podium + + +def load_submission(path): + """task -> score. Accepts a 'Task,Score' header or any two-column CSV.""" + scores = {} + with open(path, encoding="utf-8-sig") as f: + reader = csv.reader(f) + first = next(reader) + if not (first[1].strip().lower() in ("score", "best", "value")): + f.seek(0) # no recognizable header -> treat all rows as data + reader = csv.reader(f) + for row in reader: + if len(row) < 2 or not row[0].strip(): + continue + try: + scores[row[0].strip()] = float(row[1]) + except ValueError: + continue # skip header/garbage rows + return scores + + +def tier(score, gold, silver, bronze): + if score >= gold: + return GOLD, "gold" + if score >= silver: + return SILVER, "silver" + if score >= bronze: + return BRONZE, "bronze" + return 0.0, None + + +def score(podium, submission, verbose=False): + per_task = {} + counts = {"gold": 0, "silver": 0, "bronze": 0} + missing = [] + for task, (g, s, b) in podium.items(): + if task not in submission: + per_task[task] = 0.0 + missing.append(task) + continue + pts, name = tier(submission[task], g, s, b) + per_task[task] = pts + if name: + counts[name] += 1 + medal_v1 = sum(per_task.values()) / len(podium) + lite = [t for t in podium if t in V1_LITE] + medal_lite = sum(per_task[t] for t in lite) / len(lite) + + print(f"Medal Score (v1, 47 tasks) : {medal_v1:.3f}" + f" (gold {counts['gold']}, silver {counts['silver']}, bronze {counts['bronze']})") + print(f"Medal Score (v1-lite, 10 tasks) : {medal_lite:.3f}") + if missing: + print(f"\n[warn] {len(missing)} task(s) absent from submission (scored 0): " + f"{', '.join(missing[:5])}{' ...' if len(missing) > 5 else ''}") + if verbose: + print("\nper-task credit:") + for task in podium: + print(f" {per_task[task]:.2f} {task}") + return medal_v1, medal_lite + + +def main(): + ap = argparse.ArgumentParser(description="Score a submission against the frozen Medal podium.") + ap.add_argument("submission", help="CSV with columns Task,Score (one row per task)") + ap.add_argument("--podium", default=str(HERE / "medal_podium.csv"), + help="frozen gold/silver/bronze baselines (default: leaderboard/medal_podium.csv)") + ap.add_argument("--verbose", action="store_true", help="print per-task medal credit") + args = ap.parse_args() + score(load_podium(args.podium), load_submission(args.submission), args.verbose) + + +if __name__ == "__main__": + main() diff --git a/leaderboard/submission_example.csv b/leaderboard/submission_example.csv new file mode 100644 index 00000000..82f4c1a8 --- /dev/null +++ b/leaderboard/submission_example.csv @@ -0,0 +1,48 @@ +Task,Score +Aerodynamics_CarAerodynamicsSensing,0.9624 +Astrodynamics_MannedLunarLanding,6027.3126 +ComputerSystems_MallocLab,96 +Cryptographic_AES-128,11.8617 +Cryptographic_SHA-256,16.7955 +Cryptographic_SHA3-256,17.4003 +EnergyStorage_BatteryFastChargingProfile,120.8025 +EnergyStorage_BatteryFastChargingSPMe,71.8225 +EngDesign,1.3571 +InventoryOptimization_disruption_eoqd,0.6473 +InventoryOptimization_finite_horizon_dp,0.9596 +InventoryOptimization_general_meio,0.9929 +InventoryOptimization_joint_replenishment,0.8822 +InventoryOptimization_tree_gsm_safety_stock,0.75 +JobShop_abz,96.1035 +JobShop_swv,89.4966 +JobShop_ta,90.8322 +KernelEngineering_FlashAttention,983.5001 +KernelEngineering_MLA,1000.3859 +KernelEngineering_TriMul,357.1636 +Optics_adaptive_fault_tolerant_fusion,0.6398 +Optics_adaptive_temporal_smooth_control,0.8419 +Optics_fiber_guardband_spectrum_packing,0.6692 +Optics_fiber_mcs_power_scheduling,0.6542 +Optics_fiber_wdm_channel_power_allocation,0.6675 +Optics_holographic_multifocus_power_ratio,0.8072 +Optics_holographic_multiplane_focusing,0.6002 +Optics_phase_dammann_uniform_orders,99.7995 +Optics_phase_fourier_pattern_holography,82.1276 +PyPortfolioOpt_robust_mvo_rebalance,99.9946 +QuantumComputing_task_01_routing_qftentangled,5.0479 +QuantumComputing_task_02_clifford_t_synthesis,1.6633 +QuantumComputing_task_03_cross_target_qaoa,2.5781 +ReactionOptimisation_mit_case1_mixed,98.6621 +ReactionOptimisation_reizman_suzuki_pareto,82.3427 +ReactionOptimisation_snar_multiobjective,87.3657 +Robotics_DynamicObstacleAvoidanceNavigation,0.086 +Robotics_PIDTuning,0.1632 +Robotics_QuadrupedGaitOptimization,0.0219 +Robotics_RobotArmCycleTimeOptimization,0.4158 +Robotics_UAVInspectionCoverageWithWind,28.8519 +SingleCellAnalysis_predict_modality,0.5467 +StructuralOptimization_ISCSO2015,-968.4567 +StructuralOptimization_ISCSO2023,-16477799.48 +StructuralOptimization_TopologyOptimization,-190.1498 +SustainableDataCenterControl_hand_written_control,21.5657 +WirelessChannelSimulation_HighReliableSimulation,292.3228 From 66f1ab16ca470d94365b33e434a18975662a79bb Mon Sep 17 00:00:00 2001 From: y-ji24 Date: Thu, 9 Jul 2026 19:29:32 +0800 Subject: [PATCH 2/4] Add FPGA Placement Optimization benchmark --- .../ClockAwarePlacement_DesignValidation.md | 324 + .../ClockAwarePlacement_Design_Report.md | 766 + .../FPGA/FPGAPlacementOptimization/README.md | 235 + .../FPGAPlacementOptimization/README_zh-CN.md | 157 + .../FPGA/FPGAPlacementOptimization/Task.md | 251 + .../FPGAPlacementOptimization/Task_zh-CN.md | 247 + .../baseline/CMakeLists.txt | 138 + .../baseline/README.md | 230 + .../baseline/cmake/FindCairo.cmake | 81 + .../baseline/cmake/TorchExtension.cmake | 132 + .../baseline/dreamplacefpga/BasicPlace.py | 781 + .../baseline/dreamplacefpga/CMakeLists.txt | 6 + .../baseline/dreamplacefpga/EvalMetrics.py | 198 + .../baseline/dreamplacefpga/IFWriter.py | 2152 + .../NesterovAcceleratedGradientOptimizer.py | 152 + .../baseline/dreamplacefpga/NonLinearPlace.py | 1044 + .../baseline/dreamplacefpga/Params.py | 179 + .../baseline/dreamplacefpga/PlaceDB.py | 1156 + .../baseline/dreamplacefpga/PlaceObj.py | 876 + .../baseline/dreamplacefpga/Placer.py | 130 + .../baseline/dreamplacefpga/__init__.py | 5 + .../baseline/dreamplacefpga/configure.py | 26 + .../baseline/dreamplacefpga/configure.py.in | 26 + .../dreamplacefpga/ops/CMakeLists.txt | 30 + .../baseline/dreamplacefpga/ops/__init__.py | 6 + .../ops/adjust_node_area/CMakeLists.txt | 46 + .../ops/adjust_node_area/__init__.py | 0 .../ops/adjust_node_area/adjust_node_area.py | 407 + .../ops/adjust_node_area/src/.gitignore | 5 + .../adjust_node_area/src/adjust_node_area.cpp | 114 + .../src/adjust_node_area_cuda.cpp | 72 + .../src/adjust_node_area_cuda_kernel.cu | 105 + .../adjust_node_area/src/scaling_function.h | 67 + .../src/update_pin_offset.cpp | 72 + .../src/update_pin_offset_cuda.cpp | 55 + .../src/update_pin_offset_cuda_kernel.cu | 67 + .../clustering_compatibility/CMakeLists.txt | 36 + .../ops/clustering_compatibility/__init__.py | 6 + .../clustering_compatibility.py | 237 + .../clustering_compatibility/src/.gitignore | 5 + .../src/flop_compatibility.cpp | 328 + .../src/flop_compatibility_cuda.cpp | 151 + .../src/flop_compatibility_cuda_kernel.cu | 330 + .../clustering_compatibility/src/functions.h | 131 + .../src/lut_compatibility.cpp | 283 + .../src/lut_compatibility_cuda.cpp | 165 + .../src/lut_compatibility_cuda_kernel.cu | 309 + .../dreamplacefpga/ops/dct/CMakeLists.txt | 68 + .../dreamplacefpga/ops/dct/__init__.py | 5 + .../baseline/dreamplacefpga/ops/dct/dct.py | 399 + .../dreamplacefpga/ops/dct/dct2_fft2.py | 150 + .../dreamplacefpga/ops/dct/dct_lee.py | 426 + .../ops/dct/discrete_spectral_transform.py | 430 + .../baseline/dreamplacefpga/ops/dct/naive.py | 106 + .../dreamplacefpga/ops/dct/src/.gitignore | 5 + .../dreamplacefpga/ops/dct/src/dct.cpp | 255 + .../baseline/dreamplacefpga/ops/dct/src/dct.h | 427 + .../dreamplacefpga/ops/dct/src/dct2_fft2.cpp | 148 + .../dreamplacefpga/ops/dct/src/dct2_fft2.h | 613 + .../ops/dct/src/dct2_fft2_cuda.cpp | 148 + .../ops/dct/src/dct2_fft2_cuda.h | 70 + .../ops/dct/src/dct2_fft2_cuda_kernel.cu | 728 + .../dreamplacefpga/ops/dct/src/dct_2N.cpp | 187 + .../ops/dct/src/dct_2N_cuda.cpp | 188 + .../dreamplacefpga/ops/dct/src/dct_cuda.cpp | 265 + .../dreamplacefpga/ops/dct/src/dct_cuda.h | 119 + .../ops/dct/src/dct_cuda_kernel.cu | 523 + .../dreamplacefpga/ops/dct/src/dct_lee.cpp | 337 + .../dreamplacefpga/ops/dct/src/dct_lee_cpu.h | 309 + .../ops/dct/src/dct_lee_cuda.cpp | 330 + .../dreamplacefpga/ops/dct/src/dct_lee_cuda.h | 32 + .../ops/dct/src/dct_lee_cuda_kernel.cu | 294 + .../ops/dct/src/dct_lee_cuda_kernel.h | 169 + .../dreamplacefpga/ops/dct/src/dst.cpp | 83 + .../dreamplacefpga/ops/dct/src/dst_cuda.cpp | 75 + .../ops/dct/src/dst_cuda_kernel.cu | 131 + .../dreamplacefpga/ops/dct/src/dxt.cpp | 448 + .../dreamplacefpga/ops/dct/src/dxt_cuda.cpp | 465 + .../dreamplacefpga/ops/dct/torch_fft_api.py | 148 + .../ops/demandMap/CMakeLists.txt | 34 + .../dreamplacefpga/ops/demandMap/__init__.py | 5 + .../dreamplacefpga/ops/demandMap/demandMap.py | 105 + .../ops/demandMap/src/.gitignore | 5 + .../ops/demandMap/src/demandMap.cpp | 149 + .../ops/demandMap/src/demandMap_cuda.cpp | 71 + .../demandMap/src/demandMap_cuda_kernel.cu | 148 + .../ops/demandMap/src/demand_function.h | 23 + .../ops/density_map/CMakeLists.txt | 35 + .../ops/density_map/density_map.py | 211 + .../ops/density_map/src/.gitignore | 5 + .../ops/density_map/src/density_map.cpp | 246 + .../ops/density_map/src/density_map_cuda.cpp | 175 + .../src/density_map_cuda_kernel.cu | 146 + .../ops/density_potential/CMakeLists.txt | 36 + .../ops/density_potential/__init__.py | 5 + .../density_potential/density_potential.py | 408 + .../ops/density_potential/src/.gitignore | 5 + .../src/density_overflow_cuda_kernel.cu | 159 + .../src/density_potential.cpp | 615 + .../src/density_potential_cuda.cpp | 414 + .../src/density_potential_cuda_kernel.cu | 317 + .../dreamplacefpga/ops/draw_place/.gitignore | 6 + .../ops/draw_place/CMakeLists.txt | 33 + .../ops/draw_place/PlaceDrawer.py | 218 + .../dreamplacefpga/ops/draw_place/__init__.py | 5 + .../ops/draw_place/draw_place.py | 238 + .../ops/draw_place/src/.gitignore | 5 + .../ops/draw_place/src/PlaceDrawFPGA.h | 437 + .../ops/draw_place/src/PlaceDrawer.h | 462 + .../ops/draw_place/src/draw_place.cpp | 150 + .../ops/draw_place/src/draw_place.h | 132 + .../ops/dsp_ram_legalization/CMakeLists.txt | 22 + .../ops/dsp_ram_legalization/__init__.py | 6 + .../dsp_ram_legalization.py | 109 + .../ops/dsp_ram_legalization/src/legalize.cpp | 271 + .../src/legalize_auction.h | 164 + .../ops/electric_potential/.gitignore | 2 + .../ops/electric_potential/CMakeLists.txt | 38 + .../ops/electric_potential/__init__.py | 5 + .../electric_potential/electric_overflow.py | 210 + .../electric_potential/electric_potential.py | 430 + .../ops/electric_potential/src/.gitignore | 5 + .../electric_potential/src/density_function.h | 24 + .../src/electric_density_map.cpp | 247 + .../src/electric_density_map_cuda.cpp | 169 + .../src/electric_density_map_cuda_kernel.cu | 182 + .../electric_potential/src/electric_force.cpp | 168 + .../src/electric_force_cuda.cpp | 119 + .../src/electric_force_cuda_kernel.cu | 131 + .../dreamplacefpga/ops/hpwl/CMakeLists.txt | 48 + .../dreamplacefpga/ops/hpwl/__init__.py | 5 + .../baseline/dreamplacefpga/ops/hpwl/hpwl.py | 128 + .../dreamplacefpga/ops/hpwl/src/.gitignore | 5 + .../dreamplacefpga/ops/hpwl/src/hpwl.cpp | 130 + .../ops/hpwl/src/hpwl_atomic.cpp | 122 + .../dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp | 148 + .../ops/hpwl/src/hpwl_cuda_atomic.cpp | 98 + .../ops/hpwl/src/hpwl_cuda_atomic_kernel.cu | 103 + .../ops/hpwl/src/hpwl_cuda_kernel.cu | 238 + .../ops/logsumexp_wirelength/CMakeLists.txt | 45 + .../ops/logsumexp_wirelength/__init__.py | 5 + .../logsumexp_wirelength.py | 200 + .../ops/logsumexp_wirelength/src/.gitignore | 5 + .../src/integrate_net_weights_cuda_kernel.cu | 103 + .../src/logsumexp_wirelength_cuda_atomic.cpp | 194 + ...logsumexp_wirelength_cuda_atomic_kernel.cu | 461 + .../src/logsumexp_wirelength_cuda_merged.cpp | 167 + ...logsumexp_wirelength_cuda_merged_kernel.cu | 124 + .../src/logsumexp_wirelength_merged.cpp | 231 + .../ops/lut_ff_legalization/CMakeLists.txt | 37 + .../ops/lut_ff_legalization/__init__.py | 6 + .../lut_ff_legalization.py | 946 + .../ops/lut_ff_legalization/src/.gitignore | 5 + .../src/graph_matching.cuh | 1297 + .../src/lut_ff_legalization.cpp | 7319 ++ .../src/lut_ff_legalization_cuda.cpp | 760 + .../src/lut_ff_legalization_cuda_kernel.cu | 4322 + .../ops/move_boundary/CMakeLists.txt | 35 + .../ops/move_boundary/__init__.py | 5 + .../ops/move_boundary/move_boundary.py | 90 + .../ops/move_boundary/src/.gitignore | 5 + .../ops/move_boundary/src/move_boundary.cpp | 91 + .../move_boundary/src/move_boundary_cuda.cpp | 61 + .../src/move_boundary_cuda_kernel.cu | 90 + .../dreamplacefpga/ops/pin_pos/CMakeLists.txt | 43 + .../dreamplacefpga/ops/pin_pos/__init__.py | 4 + .../dreamplacefpga/ops/pin_pos/pin_pos.py | 217 + .../ops/pin_pos/src/pin_pos.cpp | 221 + .../ops/pin_pos/src/pin_pos_cuda.cpp | 121 + .../ops/pin_pos/src/pin_pos_cuda_kernel.cu | 134 + .../ops/pin_pos/src/pin_pos_cuda_segment.cpp | 124 + .../src/pin_pos_cuda_segment_kernel.cu | 140 + .../ops/pin_utilization/CMakeLists.txt | 35 + .../ops/pin_utilization/__init__.py | 6 + .../ops/pin_utilization/pin_utilization.py | 106 + .../ops/pin_utilization/src/.gitignore | 5 + .../src/pin_utilization_map.cpp | 170 + .../src/pin_utilization_map_cuda.cpp | 112 + .../src/pin_utilization_map_cuda_kernel.cu | 164 + .../ops/place_io/CMakeLists.txt | 59 + .../dreamplacefpga/ops/place_io/__init__.py | 5 + .../dreamplacefpga/ops/place_io/place_io.py | 52 + .../ops/place_io/src/.gitignore | 5 + .../ops/place_io/src/BenchMetrics.cpp | 38 + .../ops/place_io/src/BenchMetrics.h | 50 + .../dreamplacefpga/ops/place_io/src/Bin.h | 197 + .../ops/place_io/src/BinMap.cpp | 51 + .../dreamplacefpga/ops/place_io/src/BinMap.h | 114 + .../ops/place_io/src/BookshelfWriter.cpp | 83 + .../ops/place_io/src/BookshelfWriter.h | 51 + .../dreamplacefpga/ops/place_io/src/Box.h | 451 + .../dreamplacefpga/ops/place_io/src/Enums.cpp | 133 + .../dreamplacefpga/ops/place_io/src/Enums.h | 222 + .../ops/place_io/src/GeometryApi.h | 87 + .../dreamplacefpga/ops/place_io/src/Group.cpp | 41 + .../dreamplacefpga/ops/place_io/src/Group.h | 53 + .../ops/place_io/src/HrchyList.h | 262 + .../dreamplacefpga/ops/place_io/src/Index.h | 69 + .../ops/place_io/src/Interval.h | 266 + .../ops/place_io/src/Iterators.h | 403 + .../dreamplacefpga/ops/place_io/src/LibCell.h | 171 + .../dreamplacefpga/ops/place_io/src/Macro.h | 160 + .../ops/place_io/src/MacroObs.h | 102 + .../ops/place_io/src/MacroPin.h | 205 + .../dreamplacefpga/ops/place_io/src/Net.cpp | 43 + .../dreamplacefpga/ops/place_io/src/Net.h | 62 + .../dreamplacefpga/ops/place_io/src/Node.cpp | 57 + .../dreamplacefpga/ops/place_io/src/Node.h | 88 + .../dreamplacefpga/ops/place_io/src/Object.h | 47 + .../ops/place_io/src/Params.cpp | 29 + .../dreamplacefpga/ops/place_io/src/Params.h | 33 + .../dreamplacefpga/ops/place_io/src/Pin.h | 85 + .../ops/place_io/src/PlaceDB.cpp | 1235 + .../dreamplacefpga/ops/place_io/src/PlaceDB.h | 682 + .../ops/place_io/src/PlaceWriter.h | 30 + .../dreamplacefpga/ops/place_io/src/Point.h | 189 + .../ops/place_io/src/PyPlaceDB.cpp | 580 + .../ops/place_io/src/PyPlaceDB.h | 228 + .../ops/place_io/src/PybindPlaceDB.cpp | 258 + .../ops/place_io/src/PybindPyPlaceDB.cpp | 161 + .../ops/place_io/src/Region.cpp | 38 + .../dreamplacefpga/ops/place_io/src/Region.h | 58 + .../dreamplacefpga/ops/place_io/src/Row.h | 239 + .../ops/place_io/src/RowMap.cpp | 206 + .../dreamplacefpga/ops/place_io/src/RowMap.h | 208 + .../dreamplacefpga/ops/place_io/src/Site.h | 87 + .../ops/place_io/src/SubRowNodeMap.cpp | 260 + .../ops/place_io/src/SubRowNodeMap.h | 169 + .../ops/place_io/src/SubRowNodeMapHelper.h | 133 + .../dreamplacefpga/ops/place_io/src/Util.h | 214 + .../ops/place_io/src/place_io.cpp | 143 + .../ops/precondWL/CMakeLists.txt | 33 + .../dreamplacefpga/ops/precondWL/__init__.py | 5 + .../dreamplacefpga/ops/precondWL/precondWL.py | 73 + .../ops/precondWL/src/.gitignore | 5 + .../ops/precondWL/src/precondWL.cpp | 105 + .../ops/precondWL/src/precondWL_cuda.cpp | 73 + .../precondWL/src/precondWL_cuda_kernel.cu | 78 + .../dreamplacefpga/ops/rmst_wl/CMakeLists.txt | 24 + .../dreamplacefpga/ops/rmst_wl/__init__.py | 5 + .../dreamplacefpga/ops/rmst_wl/rmst_wl.py | 50 + .../dreamplacefpga/ops/rmst_wl/src/.gitignore | 5 + .../ops/rmst_wl/src/rmst_wl.cpp | 120 + .../dreamplacefpga/ops/rudy/CMakeLists.txt | 36 + .../dreamplacefpga/ops/rudy/__init__.py | 6 + .../baseline/dreamplacefpga/ops/rudy/rudy.py | 106 + .../dreamplacefpga/ops/rudy/src/.gitignore | 5 + .../dreamplacefpga/ops/rudy/src/parameters.h | 49 + .../dreamplacefpga/ops/rudy/src/rudy.cpp | 188 + .../dreamplacefpga/ops/rudy/src/rudy_cuda.cpp | 93 + .../ops/rudy/src/rudy_cuda_kernel.cu | 190 + .../ops/sortNode2Pin/CMakeLists.txt | 35 + .../ops/sortNode2Pin/__init__.py | 5 + .../ops/sortNode2Pin/sortNode2Pin.py | 58 + .../ops/sortNode2Pin/src/sortNode2Pin.cpp | 78 + .../sortNode2Pin/src/sortNode2Pin_cuda.cpp | 60 + .../src/sortNode2Pin_cuda_kernel.cu | 83 + .../dreamplacefpga/ops/utility/CMakeLists.txt | 19 + .../dreamplacefpga/ops/utility/src/Box.h | 120 + .../ops/utility/src/ComplexNumber.h | 92 + .../ops/utility/src/DetailedPlaceDB.cuh | 566 + .../ops/utility/src/DetailedPlaceDB.h | 393 + .../ops/utility/src/FlatNestedVector.cuh | 142 + .../ops/utility/src/LegalizationDB.h | 87 + .../dreamplacefpga/ops/utility/src/Msg.cpp | 112 + .../dreamplacefpga/ops/utility/src/Msg.h | 72 + .../ops/utility/src/PitchNestedVector.cuh | 146 + .../ops/utility/src/atomic_ops.cuh | 61 + .../ops/utility/src/atomic_ops.h | 56 + .../dreamplacefpga/ops/utility/src/defs.h | 78 + .../ops/utility/src/diamond_search.h | 141 + .../dreamplacefpga/ops/utility/src/limits.h | 316 + .../ops/utility/src/make_placedb.h | 194 + .../dreamplacefpga/ops/utility/src/math.h | 61 + .../ops/utility/src/namespace.h | 14 + .../dreamplacefpga/ops/utility/src/print.cuh | 49 + .../dreamplacefpga/ops/utility/src/timer.cuh | 32 + .../dreamplacefpga/ops/utility/src/timer.h | 31 + .../dreamplacefpga/ops/utility/src/torch.h | 108 + .../ops/utility/src/torch_fft_api.h | 121 + .../dreamplacefpga/ops/utility/src/utils.cuh | 133 + .../dreamplacefpga/ops/utility/src/utils.h | 37 + .../ops/utility/src/utils_cub.cuh | 21 + .../weighted_average_wirelength/.gitignore | 2 + .../CMakeLists.txt | 67 + .../weighted_average_wirelength/__init__.py | 6 + .../src/.gitignore | 5 + .../src/functional.h | 197 + .../src/functional_cuda.h | 719 + .../src/integrate_net_weights_cuda_kernel.cu | 66 + .../src/weighted_average_wirelength.cpp | 290 + .../weighted_average_wirelength_atomic.cpp | 245 + .../src/weighted_average_wirelength_cuda.cpp | 217 + ...eighted_average_wirelength_cuda_atomic.cpp | 228 + ...d_average_wirelength_cuda_atomic_kernel.cu | 155 + ...weighted_average_wirelength_cuda_kernel.cu | 106 + ...eighted_average_wirelength_cuda_merged.cpp | 302 + ...d_average_wirelength_cuda_merged_kernel.cu | 264 + .../weighted_average_wirelength_merged.cpp | 269 + .../weighted_average_wirelength.py | 352 + .../baseline/dreamplacefpga/paramsFPGA.json | 255 + .../baseline/paramsFPGA.json | 255 + .../baseline/requirements.txt | 10 + .../baseline/result_log.txt | 16 + .../baseline/solution.py | 43 + .../baseline/unitest/CMakeLists.txt | 4 + .../baseline/unitest/ops/.gitignore | 2 + .../baseline/unitest/ops/CMakeLists.txt | 4 + .../unitest/ops/adjust_node_area_unittest.py | 194 + .../ops/dct_electric_potential_unitest.py | 121 + .../baseline/unitest/ops/dct_unitest.py | 1089 + .../unitest/ops/density_potential_unitest.py | 210 + .../unitest/ops/draw_place_unitest.py | 68 + .../unitest/ops/electric_potential_unitest.py | 326 + .../baseline/unitest/ops/hpwl_unitest.py | 152 + .../ops/logsumexp_wirelength_unitest.py | 197 + .../unitest/ops/move_boundary_unitest.py | 75 + .../baseline/unitest/ops/pin_pos_unitest.py | 128 + .../unitest/ops/pin_utilization_unittest.py | 112 + .../baseline/unitest/ops/rudy_unittest.py | 99 + .../weighted_average_wirelength_unitest.py | 433 + .../frontier_eval/agent_files.txt | 4 + .../frontier_eval/artifact_files.txt | 2 + .../frontier_eval/candidate_destination.txt | 1 + .../frontier_eval/constraints.txt | 14 + .../frontier_eval/copy_files.txt | 1 + .../frontier_eval/eval_command.txt | 1 + .../frontier_eval/eval_cwd.txt | 1 + .../frontier_eval/evaluator.py | 27 + .../frontier_eval/initial_program.txt | 1 + .../frontier_eval/readonly_files.txt | 5 + .../frontier_eval/run_eval.py | 122 + .../references/README | 64 + .../references/design.aux | 2 + .../references/design.dcp | Bin 0 -> 635315 bytes .../references/design.lib | 922 + .../references/design.nets | 22267 +++++ .../references/design.nodes | 3336 + .../references/design.pl | 72 + .../references/design.scl | 69789 ++++++++++++++++ .../references/design.wts | 1 + .../references/flow.tcl | 11 + .../FPGAPlacementOptimization/scripts/init.py | 314 + .../verification/canonical.py | 309 + .../verification/evaluator.py | 312 + .../verification/requirements.txt | 1 + 346 files changed, 165460 insertions(+) create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_DesignValidation.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_Design_Report.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/README.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/Task.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/result_log.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/solution.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/agent_files.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/artifact_files.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/candidate_destination.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/constraints.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/copy_files.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_command.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_cwd.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/evaluator.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/initial_program.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/readonly_files.txt create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/run_eval.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/README create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.aux create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.dcp create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.lib create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.nets create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.nodes create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.pl create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.scl create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/design.wts create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/references/flow.tcl create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/verification/canonical.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py create mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/verification/requirements.txt diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_DesignValidation.md b/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_DesignValidation.md new file mode 100644 index 00000000..b3e163c0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_DesignValidation.md @@ -0,0 +1,324 @@ +# Clock-Aware FPGA Placement — Design Validation Report + +> **Purpose**: Verify design assumptions against source code before implementation +> **Date**: 2026-07-09 +> **Scope**: aug-elfPlace baseline, ISPD benchmark data, Frontier-Engineering patterns + +--- + +## Question 1: Where is the "Clock-Aware" part of the benchmark? + +### Investigation Method + +Searched exhaustively across: +- All Python source files (dreamplacefpga/) for "clock", "clk", "Clock", "CLK" +- All C++ source files (ops/place_io/src/) for "clock", "clk", "Clock", "CLK" +- All benchmark .scl files (36 files) for clock region definitions +- All benchmark .lc files for clock constraints +- All benchmark .lib files for clock pin definitions +- The Limbo bookshelf parser for clock region grammar +- The Python bindings (PybindPlaceDB.cpp) for clock region exposure + +### Finding 1: Clock Pin Metadata Exists (in .lib files) + +The `.lib` file defines which pins are clock pins: +``` +CELL FDRE + PIN C INPUT CLOCK + ... +``` + +This is parsed by the C++ PlaceDB and stored in `LibCell.m_clkPins`. +**Location**: `PlaceDB.cpp:805-813` (`add_clk_pin`), `LibCell.h:107-109` (`clkPinArray`) + +**But**: This information is NEVER used in the placement optimization. It is only stored as metadata. + +### Finding 2: Clock Region Parsing Infrastructure Exists (Dead Code) + +The `BookshelfDriver.h` defines callback interfaces for parsing clock regions from the `.scl` file: +``` +void initClockRegionsCbk(int xReg, int yReg); // BookshelfDriver.h:131 +void addClockRegionCbk(string const&, int, int, int, int, int, int); // :133 +``` + +The `PlaceDB.h` has a full data structure and methods: +``` +struct clk_region { int xl, yl, xm, ym, xh, yh; }; // PlaceDB.h:26-34 +vector m_clkRegionDB; // PlaceDB.h:512 +vector m_clkRegions; // PlaceDB.h:513 +int m_clkRegX, m_clkRegY; // PlaceDB.h:514-515 +void resize_clk_regions(int xReg, int yReg); // PlaceDB.cpp:707-711 +void add_clk_region(string const&, int, int, int, int, int, int); // :713-723 +``` + +**Location**: `PlaceDB.cpp:707-723`, `PlaceDB.h:26-34,475-476,512-515` + +**Critical finding**: +1. The PybindPlaceDB binding explicitly comments out clock region exposure: + `PybindPlaceDB.cpp:29: //PYBIND11_MAKE_OPAQUE(std::vector);` +2. No Python code references `m_clkRegionDB`, `m_clkRegions`, `m_clkRegX`, or `m_clkRegY` +3. The clock region data STRUCTURE exists in C++ but is **never exposed to the Python layer** + +### Finding 3: No Benchmark Contains Clock Regions + +**ALL 36 benchmark .scl files were checked** (12 ISPD2016 + 22 Titan23 + 1 test + 1 example). +**NONE** contain `CLOCKREGION` or `clock_region` definitions. + +The .scl files contain only: `SITE`, `RESOURCES`, `SITEMAP` sections. + +### Finding 4: The Only Clock-Aware Code is FF Control Signal Compatibility + +The only operational "clock awareness" in the placer is during **LUT/FF legalization**: +- `FFSLICE HALF CLK 1` in .scl: "1 clock signal available per half-SLICE" +- This constrains which FFs can share a slice based on clock signal compatibility +- Implemented in `lut_ff_legalization.py:240` and C++ `lut_ff_legalization.cpp` +- This is about **slice packing legality**, NOT about clock distribution or clock region constraints + +### Finding 5: Clock-Aware Net Weights Are Not Used + +`PlaceDB.py` initializes all net weights to 1.0 (line 333): +```python +self.net_weights = np.array(np.ones(len(self.net_names)), dtype=self.dtype) +``` +The `.wts` file is typically empty. There is no special weighting for clock nets. + +### Conclusion for Question 1 + +**The "Clock-Aware" part of the benchmark is essentially non-existent in the baseline.** + +What exists: +- ✅ Clock pin metadata parsing (stored but unused) +- ✅ Clock region parsing infrastructure (dead code, never exposed to Python) +- ✅ FF clock signal compatibility legalization (operational, but about slice packing, not clock distribution) + +What does NOT exist: +- ❌ Clock region constraints in any benchmark file +- ❌ Clock-aware optimization objectives or constraints +- ❌ Clock skew minimization or timing-driven placement +- ❌ Clock distribution network modeling + +The benchmark name "ClockAwarePlacement" is aspirational — it describes what the benchmark *should* test, not what the baseline currently implements. + +--- + +## Question 2: Is "Clock is a hard validation gate" supported? + +### Investigation Method + +Searched for: +- `clock.*legal|legal.*clock|clock.*valid|clock.*constraint|clock.*region.*check` +- `check.*clock|clock.*gate|clock.*domain.*violat` +- Any validation, assertion, or exit related to clock constraints + +### Result + +**NO CLOCK LEGALITY CHECKING FOUND ANYWHERE IN THE CODEBASE.** + +No function, no assertion, no validation warning, no exit condition related to clock legality exists. The `clk_region` data structure is parsed but never used for any purpose — not for validation, not for optimization, not for reporting. + +### Evidence + +- `PlaceDB.py`: Zero references to `clk_region`, `m_clkRegion`, or `clockRegion` +- `NonLinearPlace.py`: Zero references to clock in any stopping criterion, validation, or logging +- `EvalMetrics.py`: Zero clock-related metrics +- All legalization code: FF control signal compatibility is checked, but this is about **which FFs can share a slice based on control signals**, not about clock region legality +- `BasicPlace.py`: Zero clock-related validation + +### Conclusion for Question 2 + +**This assumption is unsupported.** There is literally zero code in the baseline that validates or enforces clock constraints. Adding such a gate would require: +1. Defining clock region constraints (as new input data — no benchmark has this) +2. Implementing a clock legality checker +3. Integrating it into the evaluator + +--- + +## Question 3: How is HPWL actually used? + +### Finding 1: HPWL vs Weighted-Average Wirelength (Separation) + +The codebase distinguishes between **two wirelength metrics**: + +| Metric | Op | Used for | Differentiable? | +|--------|-----|----------|-----------------| +| **Weighted-Average Wirelength** | `weighted_average_wirelength` | **Optimization objective** (`PlaceObj.obj_fn`) | ✅ Yes (smooth surrogate) | +| **HPWL** | `hpwl` | **Reporting & stopping criteria** (`EvalMetrics`, `NonLinearPlace`) | ❌ No (subgradient only) | + +**Evidence**: +- `PlaceObj.py:166`: `self.op_collections.wirelength_op = build_weighted_average_wl()` +- `PlaceObj.py:439`: `wirelength = self.op_collections.wirelength_op(pos)` in objective function +- `BasicPlace.py:495`: `self.op_collections.hpwl_op = self.build_hpwl()` — separate op +- `NonLinearPlace.py`: Evaluates hpwl via `eval_ops = {"hpwl": self.op_collections.hpwl_op, ...}` + +### Finding 2: What the optimizer actually minimizes + +The objective function (`PlaceObj.obj_fn`, line 439-449): +```python +wirelength = self.op_collections.wirelength_op(pos) # weighted average wirelength +density = self.op_collections.fence_region_density_merged_op(pos) +density = density * (1 + quad_penalty_coeff * density) +result = wirelength + self.density_weight_u.dot(density) +``` + +The optimizer minimizes: **Weighted-Average Wirelength + Density-Weighted Electric Potential** + +Weighted-average wirelength is a smooth approximation of HPWL where pin contributions are weighted by Gaussian kernels controlled by the `gamma` parameter. As `gamma → 0`, weighted-average wirelength → HPWL. + +### Finding 3: How HPWL is used + +HPWL is used for: +1. **Reporting metric** — logged every iteration via `EvalMetrics` +2. **Stopping criteria** — `Lgamma_stop_criterion` checks if HPWL increases after overflow is low +3. **Density weight update** — RePlAce algorithm compares HPWL change against bounds to adjust density weight +4. **Output** — reported as the primary quality metric in logs + +### Finding 4: ISPD Contest Ranking + +The ISPD 2016 FPGA contest does not define a single composite score. Based on the README and `BenchMetrics.cpp`, the primary quality metrics reported are HPWL and design statistics. The contest evaluated placers on: +- Wirelength (HPWL) +- Legality (all instances at valid sites) +- Runtime + +### Conclusion for Question 3 + +The assumption "Score = -HPWL" is **partially correct**: + +✅ HPWL is the primary quality metric for ranking +⚠ The optimizer does NOT minimize HPWL directly — it minimizes a smooth surrogate (weighted-average wirelength) +✅ HPWL is used in reporting and evaluation +⚠ There is no single composite "score" in the baseline — the metrics are multi-dimensional + +For a Frontier-Engineering benchmark: +- `combined_score` = `-HPWL` is a reasonable design choice for fully legal solutions +- But this should be documented as: HPWL is the **reported quality metric**, not the exact optimization target + +--- + +## Question 4: Audit of Previous Design Report Decisions + +### Decision: "The benchmark is about clock-aware placement" + +| Verdict | Evidence | +|---------|----------| +| ⚠ Partially supported | The benchmark NAME implies clock awareness, but the baseline has NO clock-aware optimization | +| | Clock pin metadata exists but is unused | +| | Clock region parsing infrastructure is dead code | +| | The only operational clock-related code is FF control signal sharing in legalization | + +**Recommendation**: Either: +1. Rename the benchmark to remove "ClockAware" (becomes "FPGAPlacement") +2. Or explicitly DESIGN clock-aware constraints as a new contribution (not extracting from baseline) + +### Decision: "Clock is a hard validation gate (G4)" + +| Verdict | Evidence | +|---------|----------| +| ❌ Unsupported assumption | No clock legality checking exists anywhere in the codebase | +| | No benchmark file contains clock region constraints | +| | Adding this requires new constraint definitions AND new validation code | + +**Recommendation**: Drop G4 as a hard gate from initial design. If the benchmark is to be clock-aware, clock constraints must be designed from scratch. + +### Decision: "Score = -HPWL" + +| Verdict | Evidence | +|---------|----------| +| ⚠ Partially supported | HPWL IS the primary quality metric for reporting and ranking ✅ | +| | But the optimizer minimizes WEIGHTED-AVERAGE wirelength, not HPWL ❌ | +| | No single composite "score" exists in the baseline ⚠ | + +**Recommendation**: +- `combined_score = -HPWL` is acceptable for the benchmark scoring +- Document clearly that this is the evaluation metric, distinguishing it from the optimization proxy +- Consider normalizing by baseline HPWL for cross-design comparability + +### Decision: "Alternative C: Output-based scoring (evaluator only scores, doesn't run placer)" + +| Verdict | Evidence | +|---------|----------| +| ✅ Verified by source | HPWL computation requires only pin positions + netlist topology | +| | EvalMetrics already computes HPWL without re-running placement | +| | The hpwl.cpp algorithm is straightforward (max-min per net) | +| | Pure-Python HPWL = ~8 lines of NumPy | + +**Recommendation**: This is the correct approach. The evaluator can independently compute HPWL from a candidate's output .pl file. + +### Decision: "Clock constraint files (.clk) need to be created" + +| Verdict | Evidence | +|---------|----------| +| ❌ Unsupported assumption | No benchmark data contains clock region information | +| | Creating .clk files would be designing entirely new benchmark data | +| | The Bookshelf parser supports clock regions, but no parser binds them to Python | + +**Recommendation**: If clock constraints are desired, this is a **greenfield design effort**, not an extraction from existing code. Consider whether the benchmark should be clock-aware at all, given the complexity. + +### Decision: "Four hard validation gates (site-type, capacity, carry-chain, clock-region)" + +| Verdict | Evidence | +|---------|----------| +| ✅ Site-type: Supported | PlaceDB enforces site-type mapping; legalization checks compatibility | +| ✅ Capacity: Supported | LUT/FF packer enforces per-slice resource capacity | +| ✅ Carry-chain: Supported | `lut_ff_legalization.py` has `carry_chain_checker()` | +| ❌ Clock-region: Unsupported | No clock legality code exists | + +**Recommendation**: Implement only 3 gates initially (G1-G3). Add G4 only if clock constraints are designed and implemented. + +### Decision: "Use ISPD 2016 benchmarks (12 designs)" + +| Verdict | Evidence | +|---------|----------| +| ✅ Verified | 12 ISPD 2016 benchmarks exist under `benchmarks/ispd2016/FPGA01-FPGA12` | +| | These use the .lc file format with legality constraints | +| | Titan23 benchmarks also available (Stratix-IV architecture) | + +**Recommendation**: Use ISPD 2016 as primary benchmark suite. Add Titan23 as secondary/optional. + +### Decision: "aug-elfPlace is the baseline placer" + +| Verdict | Evidence | +|---------|----------| +| ✅ Verified | aug-elfPlace IS the baseline, available under `baseline/aug-elfPlace/` | +| | aug-elfPlace is itself a fork of DREAMPlaceFPGA | +| | The reference implementation should be the aug-elfPlace pipeline, not DREAMPlaceFPGA alone | + +### Decision: "C++ compilation dependency is a risk" + +| Verdict | Evidence | +|---------|----------| +| ✅ Verified by source | All ops require C++ compilation (CMake + g++/clang + Boost + Zlib + optionally CUDA) | +| | PyTorch dependency is heavy (~2GB) | +| | But for an output-scoring evaluator, compilation is NOT needed | +| | The HPWL computation can be pure NumPy | + +**Recommendation**: The evaluator must NOT require C++ compilation. Pure-Python scoring is essential. + +--- + +## Summary of Audit + +| Design Decision | Status | Action Required | +|----------------|--------|-----------------| +| "Clock-Aware" benchmark name | ⚠ Partially supported | Either rename to "FPGAPlacement" or design clock constraints from scratch | +| Clock is a hard validation gate (G4) | ❌ Unsupported | Drop G4 from initial design; add later if clock constraints are created | +| Score = -HPWL | ⚠ Partially supported | Acceptable; document that it differs from optimization proxy | +| Output-based scoring (Alternative C) | ✅ Verified | Proceed with pure-Python HPWL evaluator | +| .clk clock constraint files | ❌ Unsupported | Remove from initial design; requires greenfield effort | +| Site-type gate (G1) | ✅ Verified | Implement from existing code | +| Capacity gate (G2) | ✅ Verified | Implement from existing code | +| Carry-chain gate (G3) | ✅ Verified | Implement from existing code | +| ISPD 2016 benchmarks | ✅ Verified | Use FPGA01-FPGA12 | +| aug-elfPlace as baseline | ✅ Verified | Baseline confirmed | +| C++ compilation risk | ✅ Verified | Evaluator must be pure-Python | + +## Revised Recommendation + +**The benchmark should initially NOT be "clock-aware"** in the sense of clock region constraints, clock distribution, or clock skew optimization. The existing codebase does not support this framing. + +Instead, the benchmark should be positioned as **FPGA Placement Optimization** — a wirelength-driven placement benchmark with legality constraints, using the ISPD 2016 benchmarks and aug-elfPlace as the baseline. This is what the existing codebase actually implements and can verify. + +If "clock-aware" placement remains the goal, it must be designed as a **new contribution**: +1. Define clock region constraints (new file format + parser) +2. Define a clock quality metric (e.g., clock skew penalty) +3. Implement clock legality checking +4. Add clock-aware optimization to the evolvable code diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_Design_Report.md b/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_Design_Report.md new file mode 100644 index 00000000..7fa86561 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/ClockAwarePlacement_Design_Report.md @@ -0,0 +1,766 @@ +# Clock-Aware FPGA Placement — Benchmark Design Report + +> **Status**: Design Phase (pre-implementation) +> **Target**: Frontier-Engineering `benchmarks/FPGA/ClockAwarePlacement/` +> **Baseline**: aug-elfPlace (DREAMPlaceFPGA) +> **Reference Data**: ISPD'2016 FPGA Contest Benchmarks + FPGA-example1 + +--- + +## Table of Contents + +1. [Repository Investigation Notes](#1-repository-investigation-notes) +2. [Baseline Investigation Notes](#2-baseline-investigation-notes) +3. [Benchmark Architecture Design](#3-benchmark-architecture-design) +4. [Evaluation Design](#4-evaluation-design) +5. [Task.md Outline](#5-taskmd-outline) +6. [Risk Review](#6-risk-review) +7. [Implementation Roadmap](#7-implementation-roadmap) + +--- + +## 1. Repository Investigation Notes + +### 1.1 Existing benchmarks studied + +| Benchmark | Domain | Pattern | Notes | +|-----------|--------|---------|-------| +| TopologyOptimization | StructuralOptimization | Unified | Cleanest example of unified pattern | +| HighReliableSimulation | WirelessChannelSimulation | Unified | Has runtime/ directory for shared modules | +| DiffSimThermalControl | AdditiveManufacturing | Unified | Uses verification/evaluator.py directly | +| MannedLunarLanding | Astrodynamics | Custom (legacy) | Has eval/ dir, own evaluator code | + +### 1.2 Common patterns (Unified task) + +#### Directory layout mandate: +Every task MUST have: `Task.md`, `README.md`, `references/`, `verification/`, `scripts/`, `frontier_eval/`. +`baseline/` is OPTIONAL but recommended. + +#### frontier_eval/ metadata files (each a single text file): + +| File | Purpose | Typical content | +|------|---------|----------------| +| initial_program.txt | Editable entry point path | scripts/init.py | +| eval_command.txt | How to invoke evaluation | {python} frontier_eval/run_eval.py --candidate {candidate} --metrics-out metrics.json --artifacts-out artifacts.json | +| eval_cwd.txt | Working directory for eval | . | +| agent_files.txt | Files shown to the agent | README.md, Task.md, scripts/init.py, frontier_eval/constraints.txt | +| readonly_files.txt | Non-editable files | references, verification, runtime, frontier_eval, README.md, Task.md | +| constraints.txt | Task-specific rules for agent | UnifiedTask template + task-specific bullets | +| copy_files.txt | Files to copy into candidate workspace | . | +| candidate_destination.txt | Where to place candidate | scripts/init.py | +| artifact_files.txt | Output files to collect | Usually empty (handled by framework) | + +Two evaluator wrapper variants: + +1. **With wrapper** (TopologyOptimization, HighReliableSimulation): + - frontier_eval/evaluator.py — thin wrapper that loads verification/evaluator.py + - frontier_eval/run_eval.py — CLI runner that imports evaluator.py and calls evaluate() + - eval_command.txt = {python} frontier_eval/run_eval.py ... + +2. **Direct** (DiffSimThermalControl): + - No frontier_eval/evaluator.py or run_eval.py + - eval_command.txt = {python} verification/evaluator.py {candidate} ... + +#### verification/evaluator.py interface: + +```python +def evaluate(program_path: str, *, repo_root: Path | None = None) -> dict: + \"\"\" + Full evaluation pipeline + \"\"\" + metrics = { + "combined_score": INVALID_COMBINED_SCORE, # -1e18 + "valid": 0.0, + "runtime_s": 0.0, + } + # ... scoring logic ... + return _wrap(metrics, artifacts) +``` + +#### Scoring conventions: +- combined_score is always the primary optimization target (higher = better) +- valid is 1.0 if all hard gates pass, 0.0 otherwise +- Invalid solutions get combined_score = -1e18 +- For minimization problems: combined_score = -raw_metric +- Results wrapped via _wrap() using openevolve.evaluation_result.EvaluationResult + +#### EVOLVE-BLOCK convention: +```python +# EVOLVE-BLOCK-START +\"\"\" +Task Name — Description +- ALLOWED TO MODIFY: +- NOT ALLOWED TO MODIFY: +\"\"\" +import ... + +# — DATA LOADING (NOT ALLOWED TO MODIFY) — +def load_input(): ... + +# — OPTIMIZATION ALGORITHM (ALLOWED TO MODIFY) — +def optimize(...): ... + +# — OUTPUT (NOT ALLOWED TO MODIFY) — +def main(): + # output format must match evaluator expectations + ... + +if __name__ == "__main__": + main() +# EVOLVE-BLOCK-END +``` + +### 1.3 README.md conventions + +``` +# + +One-paragraph summary. Then File Structure, Quick Start, Run with frontier_eval. +``` + +### 1.4 Task.md conventions + +``` +# + +## 1. Problem +## 2. Design Variables / Input +## 3. Physical Model / Background +## 4. Objective +## 5. Constraints +## 6. Submission Format / Output +## 7. Scoring +``` + +## 2. Baseline Investigation Notes + +### 2.1 aug-elfPlace pipeline + +``` +Benchmark files (.nodes/.nets/.pl/.scl/.lib/.lc) + | + v + PlaceDBFPGA.read() <- C++ parser (place_io) + | + v + NonLinearPlaceFPGA.__call__() + | + |-- Global placement (Nesterov accelerated gradient descent) + | |-- 3-level nested: L_gamma -> L_lambda -> L_sub + | |-- Objective: wirelength + sum density_weight[r] * density_penalty[r] + | |-- gamma controls wirelength smoothness + | |-- lambda (density_weight) controls density penalty strength + | |-- Overflow tracking -> stopping criteria + | + |-- DSP/RAM legalization (auction-based, runs during GP) + | + |-- LUT/FF packer-legalization (runs after GP) + | + +-- Output: .pl file (name x y z [/FIXED]) +``` + +### 2.2 Key files and their roles + +| File | Role | Editable by agent? | +|------|------|--------------------| +| dreamplacefpga/Placer.py | Entry point | No - boilerplate | +| dreamplacefpga/Params.py | Configuration loader | Seed - agent provides param values | +| dreamplacefpga/PlaceDB.py | Database + I/O | No - infrastructure | +| dreamplacefpga/NonLinearPlace.py | Placement engine | Partial - the optimization loop | +| dreamplacefpga/PlaceObj.py | Objective function | Partial - the objective terms | +| dreamplacefpga/BasicPlace.py | Base class | No - infrastructure | +| dreamplacefpga/EvalMetrics.py | Metric tracking | No - used by evaluator | +| dreamplacefpga/ops/place_io/ | File parser | No - infrastructure | + +### 2.3 Configurable parameters + +**Optimization hyper-parameters** (candidates for evolution): +- global_place_stages: multi-stage configs (bins, iterations, learning_rate) +- density_weight: initial density penalty weight +- target_density: target density per region +- stop_overflow: stopping criterion +- gamma: wirelength smoothing parameter +- RePlAce_ref_hpwl: reference HPWL for density weight update +- gp_noise_ratio: initial noise for perturbation + +**Architecture parameters** (fixed by benchmark): +- scl_file, net_file, nodes_file etc. - file paths +- num_bins_x, num_bins_y - density grid resolution + +### 2.4 Output format + +``` + [/FIXED] +inst_0 42 15 0 +inst_2 103 0 25 FIXED +``` + +- Movable instances: name x y z (no suffix) +- Fixed instances: name x y z /FIXED +- z encodes site position within a column (for FPGA sites) + +## 3. Benchmark Architecture Design + +### 3.1 Design Alternatives + +#### Alternative A: Full pipeline compilation +- **What agent does**: Modifies scripts/init.py with full placer source code (optimization loop, objective) +- **Evaluation**: Compile C++ ops -> run placement -> score +- **Dependencies**: C++ compiler, PyTorch, Boost, Zlib, (optional CUDA) +- **Runtime per eval**: ~1-60 minutes per benchmark +- **Pros**: Most freedom; can modify any algorithm part +- **Cons**: Impractical for iterative optimization (compilation time); fragile; large dependency surface + +#### Alternative B: Parameter-only evolution +- **What agent does**: Outputs a JSON parameter file +- **Evaluation**: Pre-compiled baseline reads parameters -> runs placement -> score +- **Dependencies**: Same as A, but evaluator only needs compilation once +- **Runtime per eval**: ~1-60 minutes +- **Pros**: Lighter agent workspace; compile-once for evaluator +- **Cons**: Limits innovation to parameter space; cannot change algorithm structure + +#### Alternative C (SELECTED): Output-based scoring +- **What agent does**: Produces a valid .pl placement file +- **Evaluation**: Pure-Python reader + HPWL calculator + legality checker +- **Dependencies**: NumPy, SciPy (for legality checks) +- **Runtime per eval**: ~seconds (just scoring) +- **Pros**: Minimal dependencies; fast iteration; clean separation of concerns +- **Cons**: Agent must run placer externally; evaluator cannot enforce HOW placement is done + +#### Alternative D: Hybrid (recommended future path) +- **What agent does**: Runs provided Docker image with full placer; modifies placer code +- **Evaluation**: Agent-modified placer produces .pl file -> pure-Python scoring +- **Dependencies**: For evaluation: NumPy only. For agent: Docker/Python env with full compilation +- **Runtime per eval**: ~minutes for placer + ~seconds for scoring +- **Pros**: Best tradeoff; agent has full algorithmic freedom; evaluator is lightweight +- **Cons**: Two separate environments to maintain + +**Final Decision**: **Alternative C initially** (pure output scoring), with migration path to Alternative D if maintainers require full reproducibility. + +**Rationale**: +1. Evaluator must be fast, deterministic, and dependency-light for iterative optimization +2. HPWL computation needs only netlist topology + pin positions - no need to re-run placement +3. Legality checking (site capacity, carry chains) can be done from output .pl + static benchmark data +4. Agent is still free to use full aug-elfPlace baseline (or any other placer) to produce solution +5. Follows Frontier-Engineering principle of "frozen verifier scoring" + +### 3.2 Selected Design: Agent Contract + +``` +Agent PROVIDES: + - A valid .pl file (Bookshelf format placement solution) + +Evaluator COMPUTES: + - HPWL (via independent NumPy computation over netlist) + - Legality gates (site-type, capacity, carry-chain, clock-region) + - Overflow (density-based) + - Combined score: -HPWL if legal, INVALID_COMBINED_SCORE otherwise + +FIXED (read-only for agent): + - All benchmark files (.nodes, .nets, .scl, .lib, .lc, .clk) + - The evaluator code (verification/evaluator.py) + - The evaluation framework (frontier_eval/) + - Task documentation (README.md, Task.md) + +Editable by agent: + - scripts/init.py (the placer algorithm, with EVOLVE-BLOCK markers) + - Any additional helper files the agent adds under scripts/ +``` + +### 3.3 Final Directory Structure + +``` +benchmarks/FPGA/ClockAwarePlacement/ +|-- README.md Navigation doc +|-- README_zh-CN.md (Optional) Chinese version +|-- Task.md Core task contract +|-- Task_zh-CN.md (Optional) Chinese version +| +|-- references/ Benchmark datasets (read-only) +| |-- fpga-example1/ ISPD FPGA-example1 benchmark +| | |-- design.aux, .nodes, .nets, .pl, .scl, .lib, .wts, .lc +| |-- ispd2016/ Full ISPD 2016 suite (12 designs) +| | |-- FPGA01/ .. FPGA12/ +| |-- clock_constraints/ NEW: Clock region constraint files +| | |-- fpga-example1.clk +| | |-- FPGA01.clk .. FPGA12.clk +| +-- problem_config.json Unified problem configuration +| +|-- baseline/ Reference solution (aug-elfPlace) +| |-- dreamplacefpga/ Core placer package +| |-- CMakeLists.txt, requirements.txt, paramsFPGA.json +| +-- README.md +| +|-- scripts/ Agent-editable code +| |-- init.py Initial baseline (with EVOLVE-BLOCK) +| +-- requirements.txt Agent dependencies +| +|-- verification/ Frozen evaluator (NOT editable) +| |-- evaluator.py Core scoring entry point +| |-- canonical.py Ref: HPWL computation, parsers, legality checks +| +-- requirements.txt Evaluator dependencies (NumPy) +| +|-- frontier_eval/ Unified-task metadata +| |-- initial_program.txt, eval_command.txt, eval_cwd.txt +| |-- agent_files.txt, readonly_files.txt, constraints.txt +| |-- copy_files.txt, candidate_destination.txt, artifact_files.txt +| |-- evaluator.py Thin wrapper -> verification/evaluator.py +| +-- run_eval.py Boilerplate runner +| +|-- runtime/ (Optional) Shared runtime modules ++-- tests/ (Optional) Test harness +``` + +**Key additions to existing directory**: +- references/clock_constraints/ -- NEW: clock region constraint files +- references/problem_config.json -- NEW: unified configuration +- scripts/requirements.txt -- NEW: agent dependency file +- verification/canonical.py -- NEW: independent reference (parsers, HPWL, legality) +- runtime/ -- (Optional) shared modules + +## 4. Evaluation Design + +### 4.1 Architectural Decision: Pure-Python Output Evaluator + +The evaluator reads the candidate output .pl file and computes scores independently. +It does NOT run the placer itself. Rationale: + +1. **Speed**: Scoring takes seconds, not hours +2. **Determinism**: No compilation variance, no GPU nondeterminism +3. **Portability**: Pure Python + NumPy only +4. **Fairness**: All candidates judged by the same frozen code + +### 4.2 Evaluator Interface (pseudocode) + +```python +# verification/evaluator.py + +INVALID_COMBINED_SCORE = -1e18 + +def evaluate(program_path: str, *, repo_root: Path | None = None) -> dict: + \"\"\" + Full evaluation pipeline. + + 1. Run candidate program to produce .pl file + 2. Parse output .pl to extract node positions + 3. Load benchmark data + 4. Compute HPWL (independent NumPy computation) + 5. Check legality gates + 6. Compute combined score + 7. Return metrics dict + \"\"\" + start = time.time() + metrics = { + "combined_score": INVALID_COMBINED_SCORE, + "valid": 0.0, + "runtime_s": 0.0, + "hpwl": float("inf"), + "overflow": float("inf"), + "legality_site_type": 0.0, + "legality_capacity": 0.0, + "legality_carry_chain": 0.0, + "legality_clock_region": 0.0, + } + + # Step 1: Run candidate + result = subprocess.run([sys.executable, program_path], ...) + + # Step 2: Parse output .pl + pl_path = find_output_pl(work_dir, program_path) + node_positions = parse_pl(pl_path) + + # Step 3: Load benchmark + benchmark = load_benchmark(repo_root) + + # Step 4: Compute HPWL (independent - in canonical.py) + hpwl = compute_hpwl_canonical( + node_positions, benchmark.pin_offsets, + benchmark.flat_net2pin, benchmark.netpin_start, + benchmark.net_weights, benchmark.wl_weight_x, benchmark.wl_weight_y, + ) + metrics["hpwl"] = hpwl + + # Step 5: Legality gates + metrics["legality_site_type"] = 1.0 if check_site_type_compatibility(...) else 0.0 + metrics["legality_capacity"] = 1.0 if check_site_capacity(...) else 0.0 + metrics["legality_carry_chain"] = 1.0 if check_carry_chain(...) else 0.0 + metrics["legality_clock_region"] = 1.0 if check_clock_region(...) else 0.0 + + # Step 6: Overflow + metrics["overflow"] = compute_density_overflow(...) + + # Step 7: Combined score + all_legal = all([ + metrics["legality_site_type"], + metrics["legality_capacity"], + metrics["legality_carry_chain"], + metrics["legality_clock_region"], + ]) + if all_legal: + metrics["valid"] = 1.0 + overflow_penalty = max(0.0, metrics["overflow"] - 0.10) * HPWL_SCALE + metrics["combined_score"] = -(hpwl + overflow_penalty) + else: + metrics["valid"] = 0.0 + metrics["combined_score"] = INVALID_COMBINED_SCORE + + metrics["runtime_s"] = time.time() - start + return _wrap(metrics, artifacts) +``` + +### 4.3 Score Formula + +``` +combined_score = -(HPWL + overflow_penalty) + = -(HPWL + max(0, overflow - 0.10) * HPWL_SCALE) +``` + +### 4.4 JSON Output Schema + +```json +{ + "combined_score": -12345.67, + "valid": 1.0, + "runtime_s": 42.5, + "hpwl": 12345.67, + "overflow": 0.05, + "legality_site_type": 1.0, + "legality_capacity": 1.0, + "legality_carry_chain": 1.0, + "legality_clock_region": 1.0, + "timeout": 0.0 +} +``` + +### 4.5 Independent Reference: canonical.py + +```python +# verification/canonical.py (pseudocode) + +def compute_hpwl_canonical(...) -> float: + \"\"\"HPWL using pure NumPy. Matches hpwl.cpp algorithm exactly.\"\"\" + # For each net, get pin positions, compute (max-min)*weight, sum + +def parse_pl(filepath) -> dict: ... +def parse_nodes(filepath) -> dict: ... +def parse_nets(filepath) -> dict: ... +def parse_scl(filepath) -> dict: ... +def parse_lib(filepath) -> dict: ... +def parse_lc(filepath) -> dict: ... + +def check_site_type_compatibility(...) -> bool: ... +def check_site_capacity(...) -> bool: ... +def check_carry_chain_integrity(...) -> bool: ... +def check_clock_region_constraints(...) -> bool: ... +def compute_density_overflow(...) -> float: ... +``` + +### 4.6 Evaluation Modes + +| Mode | What happens | When used | +|------|-------------|-----------| +| Scoring | Full eval: run candidate -> score | During optimization | +| Validation | Score-only: read existing .pl -> score | Baseline verification | +| Debug | Score-only with verbose logging | Development | + +## 5. Task.md Outline + +### 5.1 Outline Structure + +```markdown +# Clock-Aware FPGA Placement + +## 1. Background +- FPGA placement problem overview +- Why clock distribution matters in FPGAs +- Clock region constraints in modern FPGA architectures +- The wirelength-vs-clock-quality tradeoff +- Relevance: ISPD 2016 contest benchmarks, aug-elfPlace baseline + +## 2. Problem Formulation +- **Input**: ISPD bookshelf format (.nodes, .nets, .pl, .scl, .lib, .lc) + clock constraints (.clk) +- **Output**: Legal placement file (.pl) in Bookshelf format +- **Optimization objective**: Minimize half-perimeter wirelength (HPWL) +- **Subject to**: Site legality + clock region constraints + +## 3. Input File Specification +- .nodes: Instance list with cell types +- .nets: Netlist connectivity +- .pl: Initial placement (reference positions) +- .scl: Site constraints (types, capacities, grid) +- .lib: Cell library (pin directions, clock/control signals) +- .lc: Legality constraints (site dimensions, LUT fracture, FF control signals) +- .clk: Clock region definitions (NEW: boundaries, domain assignments, constraints) + +## 4. Design Variables +- For each movable instance: (x, y, z) site coordinates +- x, y are integer site grid coordinates +- z encodes intra-site position (for multi-element sites) + +## 5. Scoring + +### 5.1 Primary Metric +- **HPWL** (half-perimeter wirelength) — lower is better +- Computed by the frozen evaluator using an independent NumPy implementation +- Aggregated across multiple benchmark designs (12 ISPD designs) + +### 5.2 Hard Validation Gates +- **G1 — Site-type compatibility**: Every instance on a site matching its resource type +- **G2 — Site capacity**: No site exceeds resource capacity (LUTs/slice, FFs/slice) +- **G3 — Carry-chain integrity**: Carry chains in adjacent sites, correct order +- **G4 — Clock region constraint**: Clock-domain-aware placement within legal regions + +### 5.3 Combined Score +- Fully legal: combined_score = -HPWL (negated for maximization) +- Any gate fails: combined_score = INVALID_COMBINED_SCORE (-1e18) +- Soft overflow penalty: combined_score = -(HPWL + max(0, overflow-0.10) * scale) + +## 6. Submission Format +- Output file: solution.pl (or equivalent .pl path) +- Format: name x y z per line, /FIXED suffix for fixed instances +- The evaluator reads this file to compute scores + +## 7. Evaluation +- Frozen evaluator at verification/evaluator.py +- Independent reference at verification/canonical.py +- Pure Python + NumPy — no compilation needed +- Run: python verification/evaluator.py scripts/init.py + +## 8. Baseline +- aug-elfPlace (DREAMPlaceFPGA) with default parameters +- Provides wirelength-optimized placement without clock awareness +- Available under baseline/ + +## 9. Verification +- python verification/evaluator.py scripts/init.py +- python -m frontier_eval task=unified task.benchmark=FPGA/ClockAwarePlacement algorithm.iterations=0 +``` + +### 5.2 Key Design Decisions for Task.md + +1. **Clock constraints are new input files** (.clk) defining clock domains and clock region boundaries +2. **The clock gate is a hard validation gate** — scattershot placement across clock regions is invalid +3. **HPWL remains the primary continuous metric** — clock adds a constraint, not a new objective term +4. **Multi-benchmark aggregation** — evaluation runs across multiple ISPD designs for robustness + +## 6. Risk Review + +### R1: Large benchmark dataset size +- **Risk**: ISPD suite ~166 MB, Titan23 ~500 MB +- **Solution**: Use FPGA-example1 for fast iteration, ISPD suite for final scoring +- **Mitigation**: Include all 12 ISPD designs but compress as tar.gz + +### R2: Long evaluation runtime +- **Risk**: Full placement takes 1-60 minutes per design +- **Solution**: Evaluator does NOT run placement; only scores output (~seconds) +- **Mitigation**: Clear documentation that agent must produce .pl externally + +### R3: Missing dependencies for agent +- **Risk**: Full baseline requires C++ compiler, CMake, Boost, Zlib, PyTorch, (CUDA) +- **Solution**: Provide pre-built Docker image with compiled baseline +- **Mitigation**: Document two paths: light (NumPy-only scoring) and full (with baseline Docker) + +### R4: Non-determinism +- **Risk**: GPU placement can be non-deterministic; overflow depends on binning +- **Solution**: Use CPU-only evaluation; set seed=42; use fixed binning resolution +- **Mitigation**: Evaluator is deterministic by design (pure NumPy) + +### R5: PyTorch dependency +- **Risk**: Full baseline requires PyTorch (~2GB) +- **Solution**: Evaluator is PyTorch-free; agent may need PyTorch +- **Mitigation**: Provide Docker for agent environment + +### R6: C++ compilation +- **Risk**: All ops require compilation via CMake +- **Solution**: Provide pre-compiled wheels or Docker image +- **Mitigation**: Target Linux; document Windows as unsupported for baseline compilation + +### R7: Clock constraint definition +- **Risk**: aug-elfPlace has zero clock awareness built in +- **Solution**: Clock constraints are NEW — not present in baseline +- **Mitigation**: Provide clock constraint parser; reference literature on clock-aware placement + +### R8: Frontier unified-task compatibility +- **Risk**: Unified task requires specific metadata format +- **Solution**: Follow exact pattern from TopologyOptimization +- **Mitigation**: Verify against CONTRIBUTING.md and existing unified tasks + +### R9: Agent workspace size +- **Risk**: Full baseline ~1.5 GB with third-party; copy_files=. would be too large +- **Solution**: Set copy_files.txt to minimal set (only scripts/init.py) +- **Mitigation**: Reference data stays readonly; agent only needs editable entry point + +### R10: Scoring fairness across designs +- **Risk**: Larger designs have larger HPWL -> dominate aggregate score +- **Solution**: Normalize HPWL by baseline HPWL per design +- **Mitigation**: Aggregate as mean(HPWL_norm) = mean(HPWL_design / HPWL_baseline_design) + +## 7. Implementation Roadmap + +### Phase A: Foundation (Week 1) + +**Step A1**: Update README.md and Task.md with proper descriptions (not placeholders) +- *Testable*: Render documents and review + +**Step A2**: Create verification/canonical.py with pure-Python parsers +- Files: .nodes, .nets, .pl, .scl, .lib, .lc, .clk +- *Testable*: python -c "from canonical import parse_nodes; ..." on real benchmark files + +**Step A3**: Implement HPWL computation in canonical.py +- Match hpwl.cpp algorithm exactly +- *Testable*: Compare HPWL against baseline computation on same solution + +**Step A4**: Create verification/evaluator.py scaffold +- evaluate(program_path) -> metrics dict +- *Testable*: python evaluator.py scripts/init.py on known-good solution + +### Phase B: Core Scoring (Week 2) + +**Step B1**: Implement legality gates in canonical.py +- Site-type, capacity, carry-chain gates +- *Testable*: Each gate returns boolean on known-legal and known-illegal solutions + +**Step B2**: Implement clock region gate in canonical.py +- Parse .clk files; check clock domain -> region mapping +- *Testable*: Gate on known clock-aware placement + +**Step B3**: Implement overflow computation in canonical.py +- Density binning + overflow ratio +- *Testable*: Compare against baseline EvalMetricsFPGA + +**Step B4**: Wire up verification/evaluator.py with scoring +- Combined score formula, metrics output +- *Testable*: python evaluator.py baseline/solution.pl -> metrics.json + +### Phase C: Benchmark Data (Week 2-3) + +**Step C1**: Create clock constraint files for each benchmark +- references/clock_constraints/FPGA01.clk etc. +- Extract clock domains from original design data +- Define clock region boundaries +- *Testable*: Parse each .clk file and validate + +**Step C2**: Create references/problem_config.json +- Unified config for all benchmark designs +- *Testable*: All fields parse correctly + +**Step C3**: Copy full ISPD 2016 suite into references/ispd2016/ +- *Testable*: Each design passes I/O parsing + +### Phase D: Scripts and Integration (Week 3) + +**Step D1**: Create scripts/init.py with EVOLVE-BLOCK markers +- Baseline = run aug-elfPlace with default params +- Clear ALLOWED/NOT ALLOWED documentation +- *Testable*: python scripts/init.py produces a .pl file + +**Step D2**: Create scripts/requirements.txt +- Dependencies for running the placer +- *Testable*: pip install -r scripts/requirements.txt + +**Step D3**: Create verification/requirements.txt +- NumPy only +- *Testable*: pip install succeeds in clean environment + +**Step D4**: Create frontier_eval/ metadata files +- All 11 metadata files following unified pattern +- *Testable*: python -m frontier_eval task=unified task.benchmark=FPGA/ClockAwarePlacement algorithm.iterations=0 + +### Phase E: Validation (Week 3-4) + +**Step E1**: Validate scoring on baseline solution +- Run baseline -> .pl -> score -> verify HPWL matches +- *Testable*: python verification/evaluator.py scripts/init.py produces expected metrics + +**Step E2**: Validate on known-illegal solutions +- Artificially corrupt solution -> confirm gates catch it +- *Testable*: Each gate independently triggers failure + +**Step E3**: Validate unified framework integration +- Full task=unified smoke test +- *Testable*: algorithm.iterations=0 passes + +**Step E4**: Performance benchmark +- Measure scoring time across all 12 ISPD designs +- Target: < 5 seconds per design for scoring +- *Testable*: Timing report produced + +### Phase F: Polish (Week 4) + +**Step F1**: Write README.md (full version) +**Step F2**: Write Task.md (full version) +**Step F3**: Add Chinese translations (zh-CN variants) +**Step F4**: Final review against CONTRIBUTING.md checklist +**Step F5**: Create Docker image for reproducible agent environment (if needed) + +--- + +## Appendix: EVOLVE-BLOCK Design for scripts/init.py + +```python +# EVOLVE-BLOCK-START +\"\"\" +Clock-Aware FPGA Placement + +- ALLOWED TO MODIFY: optimize_placement() - the core optimization algorithm +- ALLOWED TO MODIFY: run_placement() - the main entry point for the placer +- NOT ALLOWED TO MODIFY: load_benchmark() - interface must match evaluator +- NOT ALLOWED TO MODIFY: The output .pl file format +- NOT ALLOWED TO MODIFY: The function signature of run_placement() +\"\"\" + +import ... (standard library imports) + +# ============================================================ +# DATA LOADING (NOT ALLOWED TO MODIFY) +# ============================================================ +def load_benchmark(benchmark_path: str) -> dict: + \"\"\"Load benchmark data. Must match evaluator input contract.\"\"\" + ... + +def load_clock_constraints(clk_path: str) -> list: + \"\"\"Load clock region constraints. Must match evaluator format.\"\"\" + ... + +# ============================================================ +# PLACEMENT OPTIMIZATION (ALLOWED TO MODIFY) +# ============================================================ +def optimize_placement(benchmark: dict, clock_constraints: list) -> dict: + \"\"\" + Run placement optimization. This is the main evolvable function. + + Default: Uses aug-elfPlace with default parameters. + Agent should improve by: + 1. Adding clock-aware density terms to objective + 2. Modifying optimization stages and hyperparameters + 3. Adding clock-region constraint handling + 4. Improving legalization for clock regions + \"\"\" + placement = run_aug_elfplace(benchmark) + return placement + +# ============================================================ +# OUTPUT (NOT ALLOWED TO MODIFY - format must match evaluator) +# ============================================================ +def write_placement(placement: dict, output_path: str): + \"\"\"Write .pl file in Bookshelf format.\"\"\" + ... + +def main(): + benchmark = load_benchmark(...) + clock_constraints = load_clock_constraints(...) + placement = optimize_placement(benchmark, clock_constraints) + write_placement(placement, "solution.pl") + +if __name__ == "__main__": + main() +# EVOLVE-BLOCK-END +``` + +--- + +## Revision History + +| Version | Date | Changes | +|---------|------|---------| +| v1 | 2026-07-09 | Initial design report | diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/README.md b/benchmarks/FPGA/FPGAPlacementOptimization/README.md new file mode 100644 index 00000000..b61d0b8b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/README.md @@ -0,0 +1,235 @@ +# FPGA Placement Optimization + +Optimize the placement of digital logic on an FPGA device: assign each instance +(LUT, FF, DSP, BRAM, carry chain) to a legal site to minimize wirelength while +satisfying all FPGA-specific legality constraints. + +This benchmark is based on the **ISPD 2016 FPGA Placement Contest** benchmarks. + +## Benchmark Philosophy + +This benchmark evaluates an agent's ability to **design and iteratively improve an FPGA placement algorithm**. FPGA placement is a classical electronic-design-automation problem with real engineering constraints: a placement must be fully legal before it can be used, and wirelength directly impacts circuit timing, power, and routability. + +The editable artifact (`scripts/init.py`) is intentionally a **lightweight feasible implementation** — a naive row-scan placer that produces a legal but high-wirelength placement using only the Python standard library. This design choice serves two purposes: + +- It provides a **clear baseline to improve upon** rather than asking agents to tune an already-optimized production placer. +- It keeps the **entry barrier low**: agents can focus on placement algorithm design without managing external dependencies, GPU toolchains, or proprietary frameworks. + +This benchmark is **not** designed to evolve or tune an existing production FPGA placer such as aug-elfPlace or DreamPlaceFPGA. Those systems represent years of engineering effort and are better treated as reference material. Instead, the benchmark challenges agents to **design a placement strategy from a feasible starting point**, with full freedom to replace the algorithm entirely while respecting the benchmark interface. + +The evaluator enforces a sharp separation between the candidate algorithm and the scoring pipeline: it runs the candidate program, checks legality, and computes HPWL — all independently of the candidate's internal implementation. This means an agent could use analytical placement, simulated annealing, constructive heuristics, or machine learning, and the evaluator would treat each equally as long as the output is legal and the wirelength is minimized. + +## Agent Task + +The **editable artifact** is `scripts/init.py`. + +This file implements a lightweight, deterministic row-scan placer that produces +a legal but high-wirelength placement. It is intentionally simple: + +- It uses **only Python standard library** — no external dependencies. +- It is **not a wrapper** around aug-elfPlace, DreamPlaceFPGA, or any production placer. +- It is a **starting point** — a feasible but suboptimal placement that the agent + is expected to redesign and improve. + +The agent has **full freedom** to redesign the placement algorithm. +The only constraints are: + +1. The program must accept the same command-line interface (`--nodes`, `--pl`, `--scl`, `--output`). +2. The program must produce `solution.pl` in the same format. +3. The placement must satisfy the three legality gates (G1, G2, G3). + +Everything inside the `EVOLVE-BLOCK` in `scripts/init.py` — including parsers, +data structures, and the placement strategy — may be modified, replaced, or +removed. + +## Baseline + +The `baseline/` directory contains the same row-scan implementation as a +**reference score** for human comparison. Agents do not modify the baseline. +The evaluator never compares candidate output against the baseline; it scores +candidate output independently. + +## Evaluation + +The evaluator (`verification/evaluator.py`) scores a candidate by: + +1. Running the candidate program (`scripts/init.py`) to produce `solution.pl`. +2. Computing **HPWL** (half-perimeter wirelength) using an independent NumPy + implementation. +3. Checking **hard validation gates**: site-type compatibility (G1), resource + capacity (G2), and carry-chain integrity (G3). +4. Returning `combined_score = -HPWL` for fully legal placements, or + `combined_score = -1e18` for invalid placements. + +The evaluator is **independent** from the candidate program. It does not compare +against the baseline. It scores only the candidate output. + +## Datasets + +This benchmark provides two tiers of evaluation data: + +### Bundled: fpga-example1 (default) + +The **fpga-example1** design (`references/design.*`) is bundled with the +repository. It is a lightweight benchmark (~1 MB, ~3000 instances) that serves +as the default evaluation target. This is a deliberate design choice: + +- **Fast evaluation**: Frontier-Agent performs many evaluation iterations during + evolution. A lightweight benchmark keeps iteration times under 1 second. +- **Deterministic ground truth**: The small design makes it practical to verify + correctness and debug placement algorithms. +- **Sufficient complexity**: Despite its small size, fpga-example1 exercises all + three legality gates (SLICE/DSP/BRAM site types, resource capacity, carry + chains) and produces meaningful HPWL comparisons. + +### Separate download: ISPD 2016 suite + +The complete **ISPD 2016 benchmark suite** (12 designs: FPGA01--FPGA12, approx. +1 GB uncompressed) is **not** bundled with the repository due to its size. It +must be downloaded separately from the official contest release. + +The evaluator supports these designs via the `--benchmark` flag once the dataset +is set up locally: + +## Dataset Setup + +### ISPD 2016 benchmark suite + +1. Download the official ISPD 2016 FPGA Placement Contest benchmarks: + [ISPD 2016 Contest benchmarks](http://www.ispd.cc/contests/16/benchmarks.html) + (direct links are provided on the contest page for each of the 12 designs). + +2. Extract each design into the `references/ispd2016/` directory so that the + structure matches: + + ``` + references/ispd2016/ + ├── FPGA01/ + │ ├── design.nodes + │ ├── design.nets + │ ├── design.pl + │ ├── design.scl + │ ├── design.lib + │ ├── design.lc + │ ├── design.wts + │ └── design.aux + ├── FPGA02/ + ... + └── FPGA12/ + ``` + + Note: Some design files in the official release are packaged as `.tar.gz` + archives and must be extracted before use. + +3. Verify the setup by running the evaluator against one of the designs: + + ```bash + python verification/evaluator.py scripts/init.py --benchmark FPGA01 + ``` + + Expected output: a valid HPWL score with all three legality gates passing. + +### Alternative: copy from DREAMPlaceFPGA / aug-elfPlace + +If you have the aug-elfPlace repository available locally, its benchmark +directory (`benchmarks/ispd2016/`) contains the complete ISPD 2016 suite +already extracted. Copy the `FPGA01`--`FPGA12` directories into +`references/ispd2016/`. + +```bash +python verification/evaluator.py scripts/init.py --benchmark FPGA01 +``` + +If the requested benchmark directory is missing, the evaluator will print a +clear error message with setup instructions. + +## File Structure + +``` +FPGAPlacementOptimization/ +├── README.md Navigation doc (this file) +├── Task.md Core task contract +├── references/ Benchmark datasets (read-only) +│ ├── design.* fpga-example1 (default benchmark) +│ └── ispd2016/ ISPD 2016 suite (separate download, see Dataset Setup) +├── baseline/ Reference baseline (row-scan placer) +│ ├── solution.py Reference implementation +│ └── result_log.txt Expected results +├── scripts/ +│ └── init.py Editable placer entry point +├── verification/ +│ ├── canonical.py Reference parsers, HPWL, legality gates +│ ├── evaluator.py Frozen scoring pipeline +│ └── requirements.txt Dependencies (numpy) +├── frontier_eval/ Unified-task metadata +├── ClockAwarePlacement_Design_Report.md Design exploration (archival) +└── ClockAwarePlacement_DesignValidation.md Design validation (archival) +``` + +## Quick Start + +### 1. Dependencies + +```bash +pip install numpy scipy +``` + +### 2. Run the Initial Solver + +```bash +cd benchmarks/FPGA/FPGAPlacementOptimization +python scripts/init.py +# Produces: solution.pl +# Expected: HPWL ~210721, all gates pass +``` + +### 3. Evaluate a Candidate + +Default benchmark (fpga-example1, bundled): + +```bash +python verification/evaluator.py scripts/init.py +``` + +Evaluate against an ISPD 2016 design (requires separate dataset download): + +```bash +python verification/evaluator.py scripts/init.py --benchmark FPGA01 +python verification/evaluator.py scripts/init.py --benchmark FPGA07 +python verification/evaluator.py scripts/init.py --benchmark FPGA12 +``` + +Available designs: `FPGA01` through `FPGA12`. + +Output is a JSON object with `combined_score`, `hpwl`, `valid`, and per-gate results. + +### 4. Run with frontier_eval (unified) + +```bash +python -m frontier_eval task=unified task.benchmark=FPGA/FPGAPlacementOptimization algorithm.iterations=0 +``` + +## Benchmarks + +| Dataset | Location | Designs | Size | Use | +|---------|----------|---------|------|-----| +| fpga-example1 (default) | references/ | 1 design | ~1 MB | Fast iteration during evolution | +| ISPD 2016 | references/ispd2016/ (separate download) | 12 designs (FPGA01-FPGA12) | ~1 GB | Extended evaluation | + +## References + +- **ISPD 2016 FPGA Placement Contest** -- [Contest page](http://www.ispd.cc/contests/16/FAQ.html) +- **aug-elfPlace** -- Rachel Selina Rajarathnam et al., "Better Together: + Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. + [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA) (reference implementation) + +## Design Documents + +- ClockAwarePlacement_Design_Report.md -- Initial benchmark architecture design. +- ClockAwarePlacement_DesignValidation.md -- Source-code validation of design + assumptions; documents why "clock-aware" constraints are not present in the + baseline and why the benchmark is reformulated as pure placement optimization. + + + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md b/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md new file mode 100644 index 00000000..8df3bdb4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md @@ -0,0 +1,157 @@ +# FPGA 布局优化(FPGA Placement Optimization) + +将数字逻辑实例(LUT、FF、DSP、BRAM、carry chain)分配到 FPGA 器件上的合法站点,在满足所有 FPGA 特定合法性约束的前提下最小化线长(wirelength)。 + +本 benchmark 基于 **ISPD 2016 FPGA Placement Contest** 基准测试集。 + +## 设计理念(Benchmark Philosophy) + +本 benchmark 评估 AI agent **设计并迭代改进 FPGA 布局算法(placement algorithm)** 的能力。FPGA 布局是经典电子设计自动化(Electronic Design Automation, EDA)问题,具有真实的工程约束:布局必须完全合法(legal)方可使用,线长直接影响电路时序、功耗和可布线能力。 + +可编辑组件(editable artifact, scripts/init.py)被有意设计为一个**轻量级可运行实现**——一个朴素的逐行扫描布局器(row-scan placer),仅使用 Python 标准库即可生成合法但线长较高的布局。这一设计选择基于两个目的: + +- 提供**明确的改进起点**,而非要求 agent 调整已高度优化的工业布局器。 +- 保持**低入门门槛**:agent 可专注于布局算法设计,无需管理外部依赖、GPU 工具链或专有框架。 + +本 benchmark **并非**为进化或调优现有生产级 FPGA 布局器(如 aug-elfPlace 或 DreamPlaceFPGA)而设计。这些系统代表了多年的工程积累,更适合作为参考材料。相反,本 benchmark 鼓励 agent **从可行起点出发设计布局策略**,在尊重 benchmark 接口的前提下拥有完全替换算法的自由。 + +评测器(evaluator)在候选算法与评分流水线之间实施严格分离:它运行候选程序、检查合法性并计算半周长线长(Half-Perimeter Wirelength, HPWL)——所有这些均独立于候选程序的内部实现。这意味着 agent 可以使用解析布局(analytical placement)、模拟退火(simulated annealing)、构造启发式或机器学习等方法,只要输出结果合法且线长最小,评测器将一视同仁。 + +## Agent 任务(Agent Task) + +**可编辑组件** 是 scripts/init.py。 + +该文件实现了一个轻量级、确定性的逐行扫描布局器,能够生成合法但线长较高的布局。其设计有意保持简单: + +- 仅使用 **Python 标准库**——无外部依赖。 +- **不是** aug-elfPlace、DreamPlaceFPGA 或其他生产布局器的包装。 +- 是一个**起始点**——一个可行但次优的布局,agent 应在此基础上重新设计并改进。 + +agent 拥有**完全自由**来重新设计布局算法。唯一约束是: + +1. 程序必须接受相同的命令行接口(--nodes, --pl, --scl, --output)。 +2. 程序必须按相同格式输出 solution.pl。 +3. 布局必须满足三个合法性门(G1, G2, G3)。 + +scripts/init.py 中 EVOLVE-BLOCK 内的所有内容——包括解析器、数据结构和布局策略——均可修改、替换或删除。 + +## 基线(Baseline) + +aseline/ 目录包含与初始求解器相同的逐行扫描实现,作为**人工比较的参考分数**。Agent 不修改基线。评测器从不将候选输出与基线比较;它独立评分候选输出。 + +## 评测流程(Evaluation) + +评测器( erification/evaluator.py)按以下步骤评分候选程序: + +1. 运行候选程序(scripts/init.py)生成 solution.pl。 +2. 使用独立的 NumPy 实现计算 **HPWL**(半周长线长)。 +3. 检查 **硬性验证门**:站点类型兼容性(G1)、资源容量(G2)、进位链完整性(G3)。 +4. 返回 combined_score = -HPWL(合法布局)或 combined_score = -1e18(非法布局)。 + +评测器**独立于**候选程序。它不与环境基线比较,仅评分候选输出。 + +## 默认 Benchmark + +评测器默认使用 **fpga-example1** 设计( +eferences/design.*)。这是有意的设计选择: + +- **快速评测**:Frontier-Agent 在进化过程中会执行大量评测迭代。轻量级 benchmark(约 1 MB,约 3000 个实例)将每次迭代时间控制在 1 秒以内,支持快速实验。 +- **确定性基准**:小规模设计便于验证正确性和调试布局算法。 +- **足够复杂度**:尽管规模小,fpga-example1 可同时检验三种合法性门(SLICE/DSP/BRAM 站点类型、资源容量、进位链),并产出有意义的 HPWL 比较。 + +评测器通过 --benchmark 参数支持完整的 **ISPD 2016 benchmark 套件**: + +` +# 针对特定 ISPD 2016 设计进行评测: +python verification/evaluator.py scripts/init.py --benchmark FPGA01 + +# 可用设计:FPGA01 .. FPGA12 +` + +ISPD 2016 benchmark( +eferences/ispd2016/)可用于扩展评测。这些更大规模的设计(总计约 166 MB,单个设计最多 15 万个实例)适合在布局算法稳定后进行最终验证。 + +## 文件结构(File Structure) + +` +FPGAPlacementOptimization/ +├── README.md 导航文档(本文件) +├── README_zh-CN.md 中文版导航文档 +├── Task.md 核心任务契约 +├── Task_zh-CN.md 中文版任务契约 +├── references/ Benchmark 数据集(只读) +│ ├── design.* fpga-example1(默认 benchmark) +│ └── ispd2016/ ISPD 2016 套件(单独下载,参见数据集配置) +├── baseline/ 参考基线(row-scan placer) +│ ├── solution.py 参考实现 +│ └── result_log.txt 预期结果 +├── scripts/ +│ └── init.py 可编辑布局器入口点 +├── verification/ +│ ├── canonical.py 参考解析器、HPWL、合法性门 +│ ├── evaluator.py 冻结的评分流水线 +│ └── requirements.txt 依赖项(numpy) +├── frontier_eval/ 统一任务元数据 +├── ClockAwarePlacement_Design_Report.md 设计探索(存档) +└── ClockAwarePlacement_DesignValidation.md 设计验证(存档) +` + +## 快速开始(Quick Start) + +### 1. 安装依赖 + +` +pip install numpy scipy +` + +### 2. 运行初始求解器 + +` +cd benchmarks/FPGA/FPGAPlacementOptimization +python scripts/init.py +# 生成:solution.pl +# 预期:HPWL ~210721,所有合法性门通过 +` + +### 3. 评测候选方案 + +默认 benchmark(fpga-example1): + +` +python verification/evaluator.py scripts/init.py +` + +针对 ISPD 2016 设计进行评测: + +` +python verification/evaluator.py scripts/init.py --benchmark FPGA01 +python verification/evaluator.py scripts/init.py --benchmark FPGA07 +python verification/evaluator.py scripts/init.py --benchmark FPGA12 +` + +可用设计:FPGA01 至 FPGA12。 + +输出为 JSON 对象,包含 combined_score、hpwl、 alid 及各合法性门的检查结果。 + +### 4. 使用 frontier_eval(统一接口)运行 + +` +python -m frontier_eval task=unified task.benchmark=FPGA/FPGAPlacementOptimization algorithm.iterations=0 +` + +## Benchmark 数据集 + +| 数据集 | 位置 | 设计数 | 大小 | 用途 | +|---------|----------|---------|------|------| +| fpga-example1(默认) | references/ | 1 个设计 | ~1 MB | 进化过程中快速迭代 | +| ISPD 2016 | references/ispd2016/(单独下载) | 12 个设计(FPGA01-FPGA12) | ~1 GB | 扩展评测 | + +## 参考文献(References) + +- **ISPD 2016 FPGA Placement Contest** -- [竞赛页面](http://www.ispd.cc/contests/16/FAQ.html) +- **aug-elfPlace** -- Rachel Selina Rajarathnam 等人, "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA)(参考实现) + +## 设计文档(Design Documents) + +- ClockAwarePlacement_Design_Report.md -- 初始 benchmark 架构设计文档。 +- ClockAwarePlacement_DesignValidation.md -- 设计假设的源代码验证;说明为何 "clock-aware" 约束在基线中不存在,以及 benchmark 为何重新表述为纯布局优化问题。 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/Task.md b/benchmarks/FPGA/FPGAPlacementOptimization/Task.md new file mode 100644 index 00000000..1e08e69a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/Task.md @@ -0,0 +1,251 @@ +# Task: FPGA Placement Optimization + +## 1. Problem + +Improve the placement algorithm implemented in `scripts/init.py`. This program reads an FPGA benchmark circuit (in ISPD Bookshelf format) and assigns each logic instance (LUT, FF, DSP, BRAM) to a legal site on the FPGA grid. The goal is to **minimize half-perimeter wirelength (HPWL)** while satisfying all FPGA-specific legality constraints. + +This benchmark is based on the **ISPD 2016 FPGA Placement Contest** benchmarks and the **FPGA Bookshelf format** (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`, `.lc`). + +The task: **improve the placement algorithm in `scripts/init.py` to produce lower-wirelength placements.** The file contains a naive row-scan placer that produces a legal but high-wirelength placement. An improved algorithm should reduce HPWL while maintaining legality. + +## 2. What the Agent May Modify + +This benchmark intentionally does **not** prescribe any specific placement algorithm. The agent has full freedom to redesign the placement strategy within `scripts/init.py`. + +Possible approaches include, but are not limited to: + +- **Constructive placement** place instances greedily using heuristics (row-scan, quadratic assignment, or partitioning-based methods). +- **Analytical placement** formulate placement as a continuous optimization problem with differentiable wirelength proxies and density penalties. +- **Simulated annealing** start from an initial placement and iteratively perturb and improve. +- **Reinforcement learning** train a policy to place instances sequentially. +- **Integer programming** formulate legality and wirelength as an exact optimization problem. +- **Hybrid approaches** combine multiple strategies (e.g., analytical global placement followed by legalization). + +The only requirements are: + +1. The program must accept the same command-line interface (`--nodes`, `--pl`, `--scl`, `--output`). +2. The program must produce `solution.pl` in the Bookshelf format described in Section 7 (Submission Contract). +3. The placement must satisfy all three legality gates (G1, G2, G3) described in Section 6 (Constraints). + +The evaluator judges **only the generated placement quality and legality**, not the internal implementation. An agent that replaces the entire placement algorithm with a completely different approach is treated identically to one that makes incremental modifications to the row-scan placer both are scored solely by the resulting HPWL and legality of the output placement. + +## 3. Input Format (ISPD Bookshelf for FPGA) + +Benchmark designs use the **Bookshelf format** extended for FPGA placement. Each design has the following files in `references/`: + +| File | Extension | Description | +|------|-----------|-------------| +| Auxiliary | `.aux` | Root file listing all other design files | +| Nodes | `.nodes` | List of instances (movable and fixed) with their master cell types | +| Nets | `.nets` | Netlist: each net connects a set of instance pins | +| Placement | `.pl` | Instance locations (x, y, z/BEL) -- input provides fixed IO locations only | +| SCL | `.scl` | Site/clock layout: site definitions, resources per site, and site map grid | +| Library | `.lib` | Cell library: pin definitions, directions (INPUT/OUTPUT), clock/control attributes | +| Weights | `.wts` | Net weights (typically all 1.0) | +| Legality Constraints | `.lc` | Architecture-specific legality constraint parameters | + +The `.aux` file is the entry point: + +``` +design : design.nodes design.nets design.wts design.pl design.scl design.lib +``` + +### Nodes file + +Each line: ` ` + +``` +inst_7 FDRE +inst_8 FDRE +... +inst_3340 IBUF +``` + +Fixed instances (IO pads, PLLs) are listed in the input `.pl` file and must not be moved. + +### Nets file + +Each net: `net ` followed by pin references, terminated by `endnet`: + +``` +net net_1 3 + inst_7 C + inst_8 C + inst_3340 O +endnet +``` + +### SCL file + +Defines the FPGA grid: site types (SLICE, DSP, BRAM, IO), per-site resource capacities, and the site map layout. + +### Library file + +Defines each cell type: pin names, directions, clock/control attributes. Example: + +``` +CELL FDRE + PIN C INPUT CLOCK + PIN CE INPUT + PIN D INPUT + PIN Q OUTPUT +ENDCELL +``` + +## 4. Design Variables + +For each **movable** instance `i`: + +- **x-coordinate** -- horizontal position on the FPGA grid (integer site column index) +- **y-coordinate** -- vertical position on the FPGA grid (integer site row index) +- **z / BEL index** -- BEL (Basic Element Location) within the site (integer, e.g., 0-15 for SLICE sites) + +Fixed instances (IO pads, PLLs) have predetermined (x, y, z) and must remain at their input locations. + +## 5. Objective + +Minimize **half-perimeter wirelength (HPWL)**: + +``` +HPWL = sum_{net n} (max_{i in n} x_i - min_{i in n} x_i + max_{i in n} y_i - min_{i in n} y_i) +``` + +HPWL is computed from the **final legal placement** using the centroid of each instance site as its position. + +## 6. Constraints (Hard Validation Gates) + +A placement solution must satisfy three legality gates. Any violation makes the placement invalid. + +### G1: Site-Type Compatibility + +Each instance must be placed on a site type compatible with its cell type: + +| Cell Type | Compatible Site Type | +|-----------|---------------------| +| LUT6, LUT5, LUT4, LUT3, LUT2, LUT1 | SLICE | +| FDRE, FDCE, FDPE, LDCE | SLICE | +| CARRY4, CARRY8 | SLICE | +| DSP48E2, DSP48E1 | DSP | +| RAMB36E2, RAMB18E2, RAMB36E1, RAMB18E1 | BRAM | +| IBUF, OBUF, BUFGCE, BUFG | IO | + +### G2: Resource Capacity + +Each site has a maximum capacity for each resource type. For a SLICE site (simplified Ultrascale): + +- **LUT capacity**: 16 per SLICE (8 per half-SLICE) +- **FF capacity**: 16 per SLICE (8 per half-SLICE) +- **DSP capacity**: 1 per DSP site +- **BRAM capacity**: 1 per BRAM site + +Placement must not exceed these per-site resource limits. + +### G3: Carry-Chain Integrity + +Carry-chain instances (CARRY4/CARRY8) must: + +- Be placed on adjacent sites in the correct order +- Maintain vertical adjacency (carry propagation direction) + +## 7. Submission Contract + +Your candidate program (`scripts/init.py`) must: + +1. Read the benchmark input files (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`, `.lc`) +2. Compute a placement for all movable instances +3. Write a `.pl` file to the specified output path + +### Output file: solution.pl + +``` + +... +``` + +Example: + +``` +inst_7 10 15 8 +inst_8 10 15 9 +... +inst_3 5 3 0 +``` + +Format rules: +- Fields are space-separated +- x and y are integer site column/row coordinates +- z is the integer BEL index within the site +- Fixed instances must appear at their original (x, y, z) from the input .pl file +- Every movable instance from .nodes must appear exactly once + +## 8. Feasibility Rules + +A submission is invalid (infeasible) if any of the following hold: + +1. solution.pl is missing or unreadable +2. Not all movable instances from .nodes are present in solution.pl +3. Any fixed instance has been moved from its input location +4. **G1 violation**: an instance is placed on an incompatible site type +5. **G2 violation**: a site exceeds its resource capacity +6. **G3 violation**: carry-chain instances violate ordering/adjacency rules +7. Any position field (x, y, z) is non-integer, negative, or out of the device grid bounds +8. The solution.pl file contains additional undefined instances beyond what .nodes declares + +## 9. Evaluation Workflow + +The evaluator (`verification/evaluator.py`): + +1. Runs `python scripts/init.py` to produce `solution.pl` +2. Parses the input benchmark files (.nodes, .nets, .pl, .scl) +3. Validates solution.pl completeness and format +4. Checks all three legality gates (G1, G2, G3) +5. Computes HPWL using an independent NumPy implementation +6. Returns metrics and combined score + +Run from the repository root: + +```bash +python benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py +``` + +Or from the benchmark directory: + +```bash +cd benchmarks/FPGA/FPGAPlacementOptimization +python verification/evaluator.py scripts/init.py +``` + +## 10. Scoring + +- **Legal placement (all gates pass)**: combined_score = -HPWL + + HPWL is the raw half-perimeter wirelength summed across all nets. Lower HPWL is better; the negation in combined_score ensures a higher value is better. + +- **Invalid placement (any gate fails or feasibility rule violated)**: combined_score = -1e18, valid = 0 + +## 11. References + +- **ISPD 2016 FPGA Placement Contest**: http://www.ispd.cc/contests/16/FAQ.html +- **aug-elfPlace**: Rachel Selina Rajarathnam et al., "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA) (reference implementation available at the repository root `baseline/aug-elfPlace/`). + +- **ISPD 2016 Benchmark Format**: `references/README` describes the FPGA Bookshelf format extensions. +- **Benchmark data**: references/ (fpga-example1) + +## 12. Quick Start + +```bash +# From the benchmark directory: +cd benchmarks/FPGA/FPGAPlacementOptimization + +# Run the initial solver: +python scripts/init.py + +# Evaluate the result: +python verification/evaluator.py scripts/init.py + +# Expected output: HPWL ~210721, all gates pass +``` + + + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md b/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md new file mode 100644 index 00000000..5eb5e373 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md @@ -0,0 +1,247 @@ +# 任务:FPGA 布局优化(FPGA Placement Optimization) + +## 1. 问题描述(Problem) + +改进 `scripts/init.py` 中实现的布局算法。该程序读取 FPGA benchmark 电路(ISPD Bookshelf 格式),将每个逻辑实例(LUT、FF、DSP、BRAM)分配到 FPGA 网格上的合法站点。目标是**最小化半周长线长(Half-Perimeter Wirelength, HPWL)**,同时满足所有 FPGA 特定的合法性约束。 + +本 benchmark 基于 **ISPD 2016 FPGA Placement Contest** 基准测试集和 **FPGA Bookshelf 格式**(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`、`.lc`)。 + +任务:**改进 `scripts/init.py` 中的布局算法以产生更低线长的布局。** 该文件包含一个朴素的逐行扫描布局器(row-scan placer),可生成合法但线长较高的布局。改进后的算法应在保持合法性的前提下降低 HPWL。 + +## 2. Agent 可修改内容(What the Agent May Modify) + +本 benchmark 有意**不指定**任何特定的布局算法。agent 拥有在 `scripts/init.py` 中完全重新设计布局策略的自由。 + +可能的方法包括但不限于: + +- **构造式布局(Constructive placement)** — 使用启发式方法贪婪地放置实例(逐行扫描、二次分配或基于分区的方法)。 +- **解析布局(Analytical placement)** — 将布局表述为带有可微线长代理和密度惩罚的连续优化问题。 +- **模拟退火(Simulated annealing)** — 从初始布局开始,迭代扰动并改进。 +- **强化学习(Reinforcement learning)** — 训练策略网络顺序放置实例。 +- **整数规划(Integer programming)** — 将合法性和线长表述为精确优化问题。 +- **混合方法(Hybrid approaches)** — 结合多种策略(例如,解析全局布局后接合法化)。 + +唯一要求: + +1. 程序必须接受相同的命令行接口(`--nodes`、`--pl`、`--scl`、`--output`)。 +2. 程序必须按第 7 节(提交契约)描述的 Bookshelf 格式输出 `solution.pl`。 +3. 布局必须满足第 6 节(约束)描述的全部三个合法性门(G1、G2、G3)。 + +评测器**仅评判生成的布局质量和合法性**,而非内部实现。将整个布局算法替换为完全不同方法的 agent,与对逐行扫描布局器进行增量修改的 agent 受到同等对待——两者均仅根据输出布局的 HPWL 和合法性进行评分。 + +## 3. 输入格式(Input Format, ISPD Bookshelf for FPGA) + +Benchmark 设计使用针对 FPGA 布局扩展的 **Bookshelf 格式**。每个设计在 `references/` 中包含以下文件: + +| 文件 | 扩展名 | 描述 | +|------|--------|------| +| Auxiliary | `.aux` | 根文件,列出所有其他设计文件 | +| Nodes | `.nodes` | 实例列表(可移动和固定),包含其主单元类型 | +| Nets | `.nets` | 网表:每个 net 连接一组实例引脚 | +| Placement | `.pl` | 实例位置(x, y, z/BEL)——输入仅提供固定 IO 位置 | +| SCL | `.scl` | 站点/时钟布局:站点定义、每站点资源及站点地图网格 | +| Library | `.lib` | 单元库:引脚定义、方向(INPUT/OUTPUT)、时钟/控制属性 | +| Weights | `.wts` | 网权重(通常均为 1.0) | +| Legality Constraints | `.lc` | 架构特定的合法性约束参数 | + +`.aux` 文件是入口点: + +``` +design : design.nodes design.nets design.wts design.pl design.scl design.lib +``` + +### Nodes 文件 + +每行格式:` ` + +``` +inst_7 FDRE +inst_8 FDRE +... +inst_3340 IBUF +``` + +固定实例(IO 焊盘、PLL)在输入 `.pl` 文件中列出,不得移动。 + +### Nets 文件 + +每个 net:`net ` 后接引脚引用,以 `endnet` 结束: + +``` +net net_1 3 + inst_7 C + inst_8 C + inst_3340 O +endnet +``` + +### SCL 文件 + +定义 FPGA 网格:站点类型(SLICE、DSP、BRAM、IO)、每站点资源容量及站点地图布局。 + +### Library 文件 + +定义每个单元类型:引脚名称、方向、时钟/控制属性。示例: + +``` +CELL FDRE + PIN C INPUT CLOCK + PIN CE INPUT + PIN D INPUT + PIN Q OUTPUT +ENDCELL +``` + +## 4. 设计变量(Design Variables) + +对于每个**可移动**实例 `i`: + +- **x 坐标** -- FPGA 网格上的水平位置(整数站点列索引) +- **y 坐标** -- FPGA 网格上的垂直位置(整数站点行索引) +- **z / BEL 索引** -- 站点内的基本元素位置(Basic Element Location, BEL)(整数,如 SLICE 站点为 0-15) + +固定实例(IO 焊盘、PLL)具有预定的 (x, y, z) 坐标,必须保持在其输入位置。 + +## 5. 优化目标(Objective) + +最小化**半周长线长(Half-Perimeter Wirelength, HPWL)**: + +``` +HPWL = sum_{net n} (max_{i in n} x_i - min_{i in n} x_i + max_{i in n} y_i - min_{i in n} y_i) +``` + +HPWL 根据**最终合法布局**计算,以每个实例所在站点的中心作为其位置。 + +## 6. 约束条件(Constraints, Hard Validation Gates) + +布局解必须满足三个合法性门。任何违反都会使布局失效。 + +### G1:站点类型兼容性(Site-Type Compatibility) + +每个实例必须放置在与其单元类型兼容的站点类型上: + +| 单元类型 | 兼容站点类型 | +|----------|-------------| +| LUT6, LUT5, LUT4, LUT3, LUT2, LUT1 | SLICE | +| FDRE, FDCE, FDPE, LDCE | SLICE | +| CARRY4, CARRY8 | SLICE | +| DSP48E2, DSP48E1 | DSP | +| RAMB36E2, RAMB18E2, RAMB36E1, RAMB18E1 | BRAM | +| IBUF, OBUF, BUFGCE, BUFG | IO | + +### G2:资源容量(Resource Capacity) + +每个站点对每种资源类型有最大容量。对于 SLICE 站点(简化的 Ultrascale 架构): + +- **LUT 容量**:每个 SLICE 16 个(每个 half-SLICE 8 个) +- **FF 容量**:每个 SLICE 16 个(每个 half-SLICE 8 个) +- **DSP 容量**:每个 DSP 站点 1 个 +- **BRAM 容量**:每个 BRAM 站点 1 个 + +布局不得超过这些每站点资源限制。 + +### G3:进位链完整性(Carry-Chain Integrity) + +进位链实例(CARRY4/CARRY8)必须: + +- 按正确顺序放置在相邻站点上 +- 保持垂直相邻(进位传播方向) + +## 7. 提交契约(Submission Contract) + +候选程序(`scripts/init.py`)必须: + +1. 读取 benchmark 输入文件(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`、`.lc`) +2. 为所有可移动实例计算布局 +3. 将 `.pl` 文件写入指定输出路径 + +### 输出文件:solution.pl + +``` + +... +``` + +示例: + +``` +inst_7 10 15 8 +inst_8 10 15 9 +... +inst_3 5 3 0 +``` + +格式规则: +- 字段以空格分隔 +- x 和 y 为整数站点列/行坐标 +- z 为站点内的整数 BEL 索引 +- 固定实例必须保持其在输入 .pl 文件中的原始 (x, y, z) 坐标 +- `.nodes` 中的每个可移动实例必须恰好出现一次 + +## 8. 可行性规则(Feasibility Rules) + +满足以下任一条件时,提交无效(不可行): + +1. solution.pl 缺失或无法读取 +2. `.nodes` 中的可移动实例未全部出现在 solution.pl 中 +3. 任何固定实例被移离其输入位置 +4. **G1 违反**:实例放置在不相容的站点类型上 +5. **G2 违反**:站点超过其资源容量 +6. **G3 违反**:进位链实例违反排序/相邻规则 +7. 任何位置字段 (x, y, z) 为非整数、负数或超出器件网格边界 +8. solution.pl 包含 `.nodes` 声明之外未定义的额外实例 + +## 9. 评测流程(Evaluation Workflow) + +评测器(`verification/evaluator.py`): + +1. 运行 `python scripts/init.py` 生成 `solution.pl` +2. 解析输入 benchmark 文件(.nodes、.nets、.pl、.scl) +3. 验证 solution.pl 的完整性和格式 +4. 检查全部三个合法性门(G1、G2、G3) +5. 使用独立的 NumPy 实现计算 HPWL +6. 返回指标和组合分数 + +从仓库根目录运行: + +```bash +python benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py +``` + +或从 benchmark 目录运行: + +```bash +cd benchmarks/FPGA/FPGAPlacementOptimization +python verification/evaluator.py scripts/init.py +``` + +## 10. 评分(Scoring) + +- **合法布局(所有门通过)**:combined_score = -HPWL + + HPWL 是所有 net 的原始半周长线长之和。HPWL 越低越好;combined_score 取负值确保数值越大越好。 + +- **非法布局(任意门未通过或违反可行性规则)**:combined_score = -1e18,valid = 0 + +## 11. 参考文献(References) + +- **ISPD 2016 FPGA Placement Contest**:http://www.ispd.cc/contests/16/FAQ.html +- **aug-elfPlace**:Rachel Selina Rajarathnam 等人, "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA)(参考实现,位于仓库根目录 `baseline/aug-elfPlace/`) + +- **ISPD 2016 Benchmark 格式**:`references/README` 描述了 FPGA Bookshelf 格式的扩展内容。 +- **Benchmark 数据**:references/(fpga-example1) + +## 12. 快速开始(Quick Start) + +```bash +# 从 benchmark 目录: +cd benchmarks/FPGA/FPGAPlacementOptimization + +# 运行初始求解器: +python scripts/init.py + +# 评测结果: +python verification/evaluator.py scripts/init.py + +# 预期输出:HPWL ~210721,所有合法性门通过 +``` diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt new file mode 100644 index 00000000..0a409ec5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.14) + +project(DREAMPlaceFPGA LANGUAGES CXX) +set(CMAKE_VERBOSE_MAKEFILE ON) + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set (CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "Prefix prepended to install directories" FORCE ) +endif() +message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release." + FORCE) +endif(NOT CMAKE_BUILD_TYPE) +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +# This is a dirty fix for CMake on some machines that generates default CXX standard and override the custom settings, +# because CUDA does not support C++17 and higher. +# You can check the CMAKE_CXX_FLAGS in CMakeCache.txt to verify the issue. +if(CMAKE_CXX_FLAGS) +string(REGEX REPLACE "-std=c\\+\\+([0-9a-z]+)" " " CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS}) +endif(CMAKE_CXX_FLAGS) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT}" CACHE STRING "Flags used by the compiler during all build types." FORCE) + +if(NOT CMAKE_CXX_ABI) + set(CMAKE_CXX_ABI 0 CACHE STRING + "Choose the value for _GLIBCXX_USE_CXX11_ABI, options are: 0|1." + FORCE) +endif(NOT CMAKE_CXX_ABI) +message(STATUS "CMAKE_CXX_ABI: _GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}") +add_definitions(-D_GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# for CUDA, must be put before finding PyTorch +# link dynamic libraries rather than static ones +set(CUDA_USE_STATIC_CUDA_RUNTIME OFF) +# critical for cuda_add_library, as we need to turn off -O flags +# to make sure the symbols generated by nvcc and gcc are the same +set(CUDA_PROPAGATE_HOST_FLAGS ON) +#set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};--expt-extended-lambda) +# required for executable to run at the install directory +# it will change the RPATH when installing +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +include(cmake/TorchExtension.cmake) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# without this, clang will complain about linking +#set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set_property(GLOBAL PROPERTY POSITION_INDEPENDENT_CODE TRUE) + +find_package(OpenMP REQUIRED) +#find_program(PYTHON "python" REQUIRED) +find_package(ZLIB REQUIRED) +set(Boost_NO_BOOST_CMAKE TRUE) +find_package(Boost 1.55.0 REQUIRED) +message(STATUS "Boost_INCLUDE_DIRS = ${Boost_INCLUDE_DIRS}") + +#find_package(CUDA 9.0) +find_package(Cairo) +message(STATUS "Cairo: ${CAIRO_INCLUDE_DIRS}") +message(STATUS "Cairo: ${CAIRO_LIBRARIES}") + +get_filename_component(OPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dreamplacefpga/ops ABSOLUTE) +get_filename_component(UTILITY_LIBRARY_DIRS ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/ops/utility ABSOLUTE) +message(STATUS "OPS_DIR ${OPS_DIR}") +message(STATUS "UTILITY_LIBRARY_DIRS ${UTILITY_LIBRARY_DIRS}") + +# thirdparty libraries +# flute for steiner tree generation +find_path(FLUTE_INCLUDE_DIRS flute.h PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/*) +string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} FLUTE_LINK_DIRS ${FLUTE_INCLUDE_DIRS}) +message(STATUS "FLUTE_INCLUDE_DIRS ${FLUTE_INCLUDE_DIRS}") +message(STATUS "FLUTE_LINK_DIRS ${FLUTE_LINK_DIRS}") + +# Limbo for parsers +set(LIMBO_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo) +message(STATUS "LIMBO_SOURCE_DIR ${LIMBO_SOURCE_DIR}") +set(LIMBO_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/Limbo) +message(STATUS "LIMBO_BINARY_DIR ${LIMBO_BINARY_DIR}") +if(CUDA_FOUND) + if (${CUDA_VERSION_MAJOR} VERSION_GREATER_EQUAL "11") + set(CUB_DIR ${CUDA_INCLUDE_DIRS}) + else() + set(CUB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cub) + endif() +endif() +message(STATUS "CUB_DIR ${CUB_DIR}") + +# munkres-cpp for Hungarian algorithm +set(MUNKRES_CPP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/munkres-cpp/src) +set(MUNKRES_CPP_LINK_DIRS ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/munkres-cpp) +message(STATUS "MUNKRES_CPP_INCLUDE_DIRS ${MUNKRES_CPP_INCLUDE_DIRS}") +message(STATUS "MUNKRES_CPP_LINK_DIRS ${MUNKRES_CPP_LINK_DIRS}") + +# lemon for network flow algorithms +find_path(LEMON_SOURCE_DIR lemon PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo/limbo/thirdparty/* + NO_DEFAULT_PATH + ) +set(LEMON_INCLUDE_DIRS "${LEMON_SOURCE_DIR}") +string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} LEMON_BINARY_DIR ${LEMON_SOURCE_DIR}) +list(APPEND LEMON_INCLUDE_DIRS "${LEMON_BINARY_DIR}") +set(LEMON_LINK_DIRS ${LEMON_BINARY_DIR}/lemon) +message(STATUS "LEMON_INCLUDE_DIRS ${LEMON_INCLUDE_DIRS}") +message(STATUS "LEMON_LINK_DIRS ${LEMON_LINK_DIRS}") + +if (CUDA_FOUND) + if (NOT CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES 6.0 6.1 7.0) + if (${CUDA_VERSION_MAJOR} VERSION_GREATER "9") + list(APPEND CMAKE_CUDA_ARCHITECTURES 7.5) + endif(${CUDA_VERSION_MAJOR} VERSION_GREATER "9") + if (${CUDA_VERSION_MAJOR} VERSION_GREATER "10") + list(APPEND CMAKE_CUDA_ARCHITECTURES 8.0) + endif(${CUDA_VERSION_MAJOR} VERSION_GREATER "10") + endif(NOT CMAKE_CUDA_ARCHITECTURES) + + # for cuda_add_library + cuda_select_nvcc_arch_flags(CUDA_ARCH_FLAGS ${CMAKE_CUDA_ARCHITECTURES}) + message(STATUS "CUDA_ARCH_FLAGS: ${CUDA_ARCH_FLAGS}") + list(APPEND CUDA_NVCC_FLAGS ${CUDA_ARCH_FLAGS} --compiler-options;-fPIC) +endif(CUDA_FOUND) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dreamplacefpga/configure.py.in ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/configure.py) + +add_subdirectory(thirdparty) +add_subdirectory(dreamplacefpga) +add_subdirectory(unitest) +#add_subdirectory(benchmarks) +#add_subdirectory(test) + +#install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_INSTALL_PREFIX}/__init__.py)") + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/configure.py DESTINATION dreamplacefpga) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md new file mode 100644 index 00000000..65ab2838 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md @@ -0,0 +1,230 @@ +## Table of Contents + +* [aug-elfPlace](#aug-elfPlace) + - [Hybrid Placement Framework](#overview) + - [Target Architecture](#target_arch) +* [Publication(s)](#publications) +* [Developer(s)](#developers) +* [Cloning the Repository](#cloning) +* [Build Instructions](#build) + - [To install Python dependency](#python_dependency) + - [To install with Docker](#Docker) + - [To Build](#build_dreamplacefpga) +* [Benchmarks](#benchmarks) +* [Running aug-elfPlace](#running) + - [Integration with VPR](#integrate_vpr) +* [Bug Report](#bug) +* [Copyright](#copyright) + +# ``aug-elfPlace`` +``aug-elfPlace``, built on the [DREAMPlaceFPGA (commit 9b86a09)](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/9b86a09437e08947fb65c2a0cd351d004256bcc5) framework, is a wirelength-driven generalizable flat analytical FPGA placer that consists of a global placer and packer-legalizer. +The main features of ``aug-elfPlace`` include: + - a generalized architecture modeling that reads in the architecture-specific details and legality constraints as an additional bookshelf input file (`design.lc`); + - employing the scalable auction algorithm to legalize a large number of digital signal processors (DSPs) and memory blocks in the design; + - using placeholder fillers to effectively handle memory logic array block (MLAB) instances in the design; + - employ a *partial macro representation* for carry chains; + - enhance the instance area update and packer-legalizer algorithms for look-up tables (LUTs) and flip-flops (FFs) to ensure legal placement for different architectures; and + - [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)-compatible flat output placement format. + +``aug-elfPlace`` can integrate with the open-source [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/) CAD tool to further improve the quality of results through annealing as part of the hybrid placement framework. Please refer to our [paper](#publications) for detailed information. + +### Hybrid Placement Framework +The hybrid placement framework integrates a flat analytical placer such as ``aug-elfPlace`` with [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s place and route tool using the [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place). + +

+ +

+ +The [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) reads in a flat placement solution and constructs a cluster-level netlist for [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s placer. The [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) also repairs any legality or mode-related errors in the clusters, thus allowing any external flat placer to integrate with [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s place and route tool. + +The placement solution from ``aug-elfPlace`` can be routed in [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s router and validated to evaluate overall performance. In addition, [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s annealing-based placer can further refine the ``aug-elfPlace`` solution to improve the overall quality of results. + +Please refer to our [paper](#publications) for more details on the performance of ``aug-elfPlace`` as part of the hybrid placement framework on the [Titan23 benchmarks](https://www.eecg.utoronto.ca/~kmurray/titan.html) benchmarks. + +### Target Architecture +``aug-elfPlace`` can target simplified versions of the Ultrascale and Stratix-IV architectures and requires the locations of the fixed input-output (IO) and phase-locked loop (PLL) blocks to be provided as part of the input, similar to [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA). + +

+ +

+ +FPGA architectures consist of DSP blocks and different memory blocks - BRAM, M9K, and M144K, with Slice blocks that consist of LUT, FF, and adder instances. +> Carry adder instances in the Ultrascale architecture are not shown in the Figure, as the benchmarks do not contain any. + +Slice blocks are configurable logic blocks (CLBs) in Ultrascale architecture and logic array blocks (LABs) in Stratix-IV architecture. The architectures differ significantly in the configuration of the Slice blocks and the legality constraints for LUTs and FFs. + +#### Simplified Ultrascale Architecture +- The [ISPD'2016 benchmarks](http://www.ispd.cc/contests/16/FAQ.html) targeting a simplified [AMD/Xilinx Ultrascale](https://docs.amd.com/v/u/en-US/ds890-ultrascale-overview) architecture. *``aug-elfPlace`` can be run on GPU and CPU for Ultrascale-like architectures.* + +#### Simplified Stratix-IV Architecture +- The [Titan23 benchmarks](https://www.eecg.utoronto.ca/~kmurray/titan.html) targeting a simplified [Intel/Altera Stratix-IV](https://www.intel.com/content/www/us/en/content-details/654799/stratix-iv-device-handbook.html) architecture. *Due to large packer-legalizer runtime for the Stratix-IV-like architecture, ``aug-elfPlace`` is run on CPU.* + +> Note: ``aug-elfPlace`` is not tested on the AMD/Xilinx Ultrascale+ or other architecture. + +## Publication(s) + +* Rachel Selina Rajarathnam, Kate Thurmer, Vaughn Betz, Mahesh A. Iyer, and [David Z. Pan](http://users.ece.utexas.edu/~dpan), "**Better Together: Combining Analytical and Annealing Methods for FPGA Placement**," *34th International Conference on Field-Programmable Logic and Applications (FPL)*, 2024 (accepted). + +## Developer(s) + +- Rachel Selina Rajarathnam, [UTDA](https://www.cerc.utexas.edu/utda), ECE Department, The University of Texas at Austin + +## Cloning the Repository + +External dependencies are the same as [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/9b86a09437e08947fb65c2a0cd351d004256bcc5?tab=readme-ov-file#dependencies). + +To pull git submodules in the root directory +``` +git submodule init +git submodule update +``` + +Alternatively, pull all the submodules when cloning the repository. +``` +git clone --recursive https://github.com/rachelselinar/aug-elfPlace.git +``` + +## Build Instructions + +### To install Python dependency + +There is an alternative way to install ``aug-elfPlace`` using Docker. If you want to use Docker, skip this step and go to [Docker installation](#Docker). + +At the root directory: +``` +pip install -r requirements.txt +``` +> For example, if the repository was cloned in directory ***~/Downloads***, then the root directory is ***~/Downloads/aug-elfPlace*** + +> You can also use a [python virtual environment](https://docs.python.org/3/library/venv.html) to install all the required packages to run ``aug-elfPlace`` + +### To install with Docker + +You can use the Docker container to avoid building all the dependencies yourself. + +1. Install Docker on [Linux](https://docs.docker.com/install/) (Win and Mac are not tested). +2. To enable the GPU features, install [NVIDIA-docker](https://github.com/NVIDIA/nvidia-docker); otherwise, skip this step. +3. Get the docker image using one of the options + Build the image locally. + ``` + docker build . --file Dockerfile --tag /dreamplacefpga:1.0 + ``` + Replace `` with a username, for instance, 'utda_placer.' +4. Enter the bash environment of the container. + Mount the repo and all the Designs into the Docker, which allows the Docker container to access and modify these files directly. + + To run on a Linux machine without GPU: + ``` + docker run -it -v $(pwd):/aug-elfPlace /dreamplacefpga:1.0 bash + ``` + To run on a Linux machine with GPU: (Docker verified on NVIDIA GPUs with compute capability 6.1, 7.5, and 8.0) + ``` + docker run --gpus 1 -it -v $(pwd):/aug-elfPlace /dreamplacefpga:1.0 bash + ``` + + For example, to run on a Linux machine without GPU: + ``` + docker run -it -v $(pwd):/aug-elfPlace utda_placer/dreamplacefpga:1.0 bash + ``` +5. Go to the `aug-elfPlace` directory in the Docker, which is the root directory of the project + ``` + cd /aug-elfPlace + ``` + + +### To Build + +At the root directory, +``` +mkdir build +cd build +cmake .. -DCMAKE_INSTALL_PREFIX=path_to_root_dir +make +make install +``` + +If you are using Docker, use the following at the root directory, + ``` +rm -rf build +mkdir build +cd build +cmake .. -DCMAKE_INSTALL_PREFIX=/aug-elfPlace -DPYTHON_EXECUTABLE=$(which python) +make +make install +``` + +Third-party submodules are automatically built except for [Boost](https://www.boost.org). + +> For example, + +> ***~/Downloads/aug-elfPlace:*** *mkdir build; cd build* + +> ***~/Downloads/aug-elfPlace/build:*** *cmake . . -DCMAKE_INSTALL_PREFIX=~/Downloads/aug-elfPlace* + +> ***~/Downloads/aug-elfPlace/build:*** *make; make install* + +> The directory ***~/Downloads/aug-elfPlace/build*** is the install dir + +When packages or parser code are changed, the contents of the ***build*** directory must be deleted for a clean build and proper operation. +``` +rm -r build +``` +> For example, + +> ***~/Downloads/aug-elfPlace:*** *rm -r build* + +For cmake options, refer to [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/main?tab=readme-ov-file#cmake). + +## Benchmarks + +``aug-elfPlace`` only accepts inputs in the [Bookshelf](./benchmarks/sample_ispd2016_benchmarks/README) format and requires IO/PLL locations to be fixed. +- 12 designs for *AMD/Xilinx Ultrascale Architecture* in the updated bookshelf format with fixed IOs are provided from the [ISPD'2016 contest](http://www.ispd.cc/contests/16/FAQ.html). +- [Titan23](https://www.eecg.utoronto.ca/~kmurray/titan.html) designs based on the simplified *Intel/Altera Stratix-IV Architecture*, generated by [VPR](https://docs.verilogtorouting.org/en/latest/vpr/), are included in bookshelf format. + +All the designs are in the [benchmarks](./benchmarks) directory, and sample JSON configuration files are in the [test](./test) directory. For the complete list of available options in the JSON file, please refer to [paramsFPGA.json](./dreamplacefpga/paramsFPGA.json). + +## Running aug-elfPlace + +Before running, ensure that all python dependent packages have been installed. +Go to the ***root directory*** and run with the JSON configuration file. +``` +python dreamplacefpga/Placer.py .json +``` +> Run from ***~/Downloads/aug-elfPlace*** directory + +For example: +``` +python dreamplacefpga/Placer.py test/FPGA01.json +``` +> ***~/Downloads/aug-elfPlace:*** *python dreamplacefpga/Placer.py test/FPGA01.json* + +> If you are not using the GPU, change the gpu flag in the *.json file to 0. + +Unit tests for some of the pytorch operators are provided. For instance, to run the unit test for hpwl, use the below command: +``` +python unitest/ops/hpwl_unitest.py +``` +> Note: If your machine does not have an NVIDIA GPU, set the '***gpu***' flag in the JSON configuration file to '***0***' to run on the CPU. + +### Integration with [VPR](https://docs.verilogtorouting.org/en/latest/vpr/) +- Generate flat placement solution from ``aug-elfPlace`` in [VPR compatible format](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) as a *.pl* file: +``` + x y s z +``` +Where '*s*' refers to subtile location, set to zero '*s=0*' for IO/PLL instances, whereas '*z=0*' for DSP/memory/Slice instances. + +- Use [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) to construct a cluster-level netlist for VPR after fixing any legality or mode-related failures. A '*.net*' clustered netlist and '*.fix_clusters*' placement file is generated from the VPR legalizer's output. + +- To validate the placement solution without refinement in VPR, use the '*.fix_clusters*' file generated as '*.place*' input placement file to VPR. For details, refer to this [comment](https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/2484#issuecomment-1938993673). VPR router is run on the input placement followed by validation. + +To refine the placement solution in VPR, list only IO/PLL instances in the '*.fix_clusters*' file and let the VPR placer refine the placement before routing and validation. +> Note: Integration with VPR is verified only for the Titan23 benchmarks. + +## Bug Report + +Please file an [issue](https://github.com/rachelselinar/aug-elfPlace/issues) to report a bug. + +## Copyright + +This software is released under a BSD 3-Clause "New" or "Revised" License. Please refer to [LICENSE](./LICENSE) for details. + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake new file mode 100644 index 00000000..1a768e32 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake @@ -0,0 +1,81 @@ +# - Try to find Cairo +# Once done, this will define +# +# CAIRO_FOUND - system has Cairo +# CAIRO_INCLUDE_DIRS - the Cairo include directories +# CAIRO_LIBRARIES - link these to use Cairo +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_CAIRO cairo) # FIXME: After we require CMake 2.8.2 we can pass QUIET to this call. + +FIND_PATH(CAIRO_INCLUDE_DIRS + NAMES cairo.h + HINTS ${PC_CAIRO_INCLUDEDIR} + ${PC_CAIRO_INCLUDE_DIRS} + PATH_SUFFIXES cairo +) + +FIND_LIBRARY(CAIRO_LIBRARIES + NAMES cairo + HINTS ${PC_CAIRO_LIBDIR} + ${PC_CAIRO_LIBRARY_DIRS} +) + +IF (CAIRO_INCLUDE_DIRS) + IF (EXISTS "${CAIRO_INCLUDE_DIRS}/cairo-version.h") + FILE(READ "${CAIRO_INCLUDE_DIRS}/cairo-version.h" CAIRO_VERSION_CONTENT) + + STRING(REGEX MATCH "#define +CAIRO_VERSION_MAJOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") + SET(CAIRO_VERSION_MAJOR "${CMAKE_MATCH_1}") + + STRING(REGEX MATCH "#define +CAIRO_VERSION_MINOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") + SET(CAIRO_VERSION_MINOR "${CMAKE_MATCH_1}") + + STRING(REGEX MATCH "#define +CAIRO_VERSION_MICRO +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") + SET(CAIRO_VERSION_MICRO "${CMAKE_MATCH_1}") + + SET(CAIRO_VERSION "${CAIRO_VERSION_MAJOR}.${CAIRO_VERSION_MINOR}.${CAIRO_VERSION_MICRO}") + ENDIF () +ENDIF () + +# FIXME: Should not be needed anymore once we start depending on CMake 2.8.3 +SET(VERSION_OK TRUE) +IF (Cairo_FIND_VERSION) + IF (Cairo_FIND_VERSION_EXACT) + IF ("${Cairo_FIND_VERSION}" VERSION_EQUAL "${CAIRO_VERSION}") + # FIXME: Use IF (NOT ...) with CMake 2.8.2+ to get rid of the ELSE block + ELSE () + SET(VERSION_OK FALSE) + ENDIF () + ELSE () + IF ("${Cairo_FIND_VERSION}" VERSION_GREATER "${CAIRO_VERSION}") + SET(VERSION_OK FALSE) + ENDIF () + ENDIF () +ENDIF () + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cairo DEFAULT_MSG CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES VERSION_OK) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake new file mode 100644 index 00000000..8c7fbfa2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake @@ -0,0 +1,132 @@ +# @file TorchExtension.cmake +# @author Zizheng Guo +# @brief Use CMake to compile PyTorch extensions + +add_subdirectory(thirdparty/pybind11) + +execute_process(COMMAND ${PYTHON_EXECUTABLE} -c + "import torch; print(torch.__path__[0]); print(int(torch.cuda.is_available())); print(torch.__version__);" + OUTPUT_VARIABLE TORCH_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) +string(REPLACE "\n" ";" TORCH_OUTPUT_LIST ${TORCH_OUTPUT}) +list(GET TORCH_OUTPUT_LIST 0 TORCH_INSTALL_PREFIX) +list(GET TORCH_OUTPUT_LIST 1 TORCH_ENABLE_CUDA) +list(GET TORCH_OUTPUT_LIST 2 TORCH_VERSION) +string(REPLACE "." ";" TORCH_VERSION_LIST ${TORCH_VERSION}) +list(GET TORCH_VERSION_LIST 0 TORCH_VERSION_MAJOR) +list(GET TORCH_VERSION_LIST 1 TORCH_VERSION_MINOR) + +message(STATUS TORCH_INSTALL_PREFIX=${TORCH_INSTALL_PREFIX}) +message(STATUS TORCH_VERSION=${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}) + +if ("${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}" VERSION_LESS 1.6) + message(SEND_ERROR "require PyTorch version >=1.6") +#elseif ("${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}" VERSION_GREATER_EQUAL 1.8) +# message(SEND_ERROR "require PyTorch version < 1.8") +endif() + +if (TORCH_ENABLE_CUDA) + find_package(CUDA 9.0) + if (NOT CUDA_FOUND) + set(TORCH_ENABLE_CUDA 0 CACHE BOOL "Whether enable CUDA" FORCE) + endif(NOT CUDA_FOUND) +endif() +message(STATUS TORCH_ENABLE_CUDA=${TORCH_ENABLE_CUDA}) + +add_library(torch STATIC IMPORTED) +find_library(TORCH_PYTHON_LIBRARY torch_python PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) +find_library(TORCH_LIBRARY torch PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) +find_library(C10_LIBRARY c10 PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) +find_library(C10_CUDA_LIBRARY c10_cuda PATHS "${TORCH_INSTALL_PREFIX}/lib") +find_library(TORCH_CPU_LIBRARY torch_cpu PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) +find_library(TORCH_CUDA_LIBRARY torch_cuda PATHS "${TORCH_INSTALL_PREFIX}/lib") + +if (EXISTS ${TORCH_INSTALL_PREFIX}/include) + # torch version 1.4+ + set(TORCH_HEADER_PREFIX ${TORCH_INSTALL_PREFIX}/include) +elseif (EXISTS ${TORCH_INSTALL_PREFIX}/lib/include) + # torch version 1.0 + set(TORCH_HEADER_PREFIX ${TORCH_INSTALL_PREFIX}/lib/include) +endif() +set(TORCH_INCLUDE_DIRS + ${TORCH_HEADER_PREFIX} + ${TORCH_HEADER_PREFIX}/torch/csrc/api/include) + +set(LINK_LIBS ${C10_LIBRARY} ${TORCH_CPU_LIBRARY}) +if (TORCH_ENABLE_CUDA) + set(LINK_LIBS ${LINK_LIBS} + ${C10_CUDA_LIBRARY} + ${TORCH_CUDA_LIBRARY}) +endif() + +set_target_properties(torch PROPERTIES + IMPORTED_LOCATION "${TORCH_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TORCH_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${LINK_LIBS}" + INTERFACE_COMPILE_OPTIONS "-D_GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}" + ) + +# CXX only +function(add_torch_extension target_name) + set(multiValueArgs EXTRA_INCLUDE_DIRS EXTRA_LINK_LIBRARIES EXTRA_DEFINITIONS) + cmake_parse_arguments(ARG "" "" "${multiValueArgs}" ${ARGN}) + if (TORCH_ENABLE_CUDA) + cuda_add_library(${target_name} STATIC ${ARG_UNPARSED_ARGUMENTS}) + else() + # remove cuda files + list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cu$") + list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cuh$") + add_library(${target_name} STATIC ${ARG_UNPARSED_ARGUMENTS}) + endif() + target_include_directories(${target_name} PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) + target_link_libraries(${target_name} ${ARG_EXTRA_LINK_LIBRARIES} torch pybind11::module) + target_compile_definitions(${target_name} PRIVATE + TORCH_EXTENSION_NAME=${target_name} + TORCH_VERSION_MAJOR=${TORCH_VERSION_MAJOR} + TORCH_VERSION_MINOR=${TORCH_VERSION_MINOR} + ENABLE_CUDA=${TORCH_ENABLE_CUDA} + ${ARG_EXTRA_DEFINITIONS}) + set_target_properties(${target_name} PROPERTIES + POSITION_INDEPENDENT_CODE ON + CXX_VISIBILITY_PRESET "hidden" + CUDA_VISIBILITY_PRESET "hidden" + ) +endfunction() + +function(add_pytorch_extension target_name) + set(multiValueArgs EXTRA_INCLUDE_DIRS EXTRA_LINK_LIBRARIES EXTRA_DEFINITIONS) + cmake_parse_arguments(ARG "" "" "${multiValueArgs}" ${ARGN}) + if (TORCH_ENABLE_CUDA) + set(CUDA_SRCS "${ARG_UNPARSED_ARGUMENTS}") + list(FILTER CUDA_SRCS INCLUDE REGEX ".*cu$") + if (CUDA_SRCS) + cuda_add_library(${target_name}_cuda_tmp STATIC ${CUDA_SRCS}) + target_include_directories(${target_name}_cuda_tmp PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) + target_link_libraries(${target_name}_cuda_tmp ${ARG_EXTRA_LINK_LIBRARIES}) + target_compile_definitions(${target_name}_cuda_tmp PRIVATE + TORCH_EXTENSION_NAME=${target_name} + TORCH_MAJOR_VERSION=${TORCH_MAJOR_VERSION} + TORCH_MINOR_VERSION=${TORCH_MINOR_VERSION} + ENABLE_CUDA=${TORCH_ENABLE_CUDA} + ${ARG_EXTRA_DEFINITIONS}) + set_target_properties(${target_name}_cuda_tmp PROPERTIES + POSITION_INDEPENDENT_CODE ON + CXX_VISIBILITY_PRESET "hidden" + CUDA_VISIBILITY_PRESET "hidden" + ) + endif() + endif() + list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cu$") + pybind11_add_module(${target_name} MODULE ${ARG_UNPARSED_ARGUMENTS}) + target_include_directories(${target_name} PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) + if (TORCH_ENABLE_CUDA AND CUDA_SRCS) + target_link_libraries(${target_name} PRIVATE ${target_name}_cuda_tmp ${ARG_EXTRA_LINK_LIBRARIES} torch ${TORCH_PYTHON_LIBRARY}) + else() + target_link_libraries(${target_name} PRIVATE ${ARG_EXTRA_LINK_LIBRARIES} torch ${TORCH_PYTHON_LIBRARY}) + endif() + target_compile_definitions(${target_name} PRIVATE + TORCH_EXTENSION_NAME=${target_name} + TORCH_VERSION_MAJOR=${TORCH_VERSION_MAJOR} + TORCH_VERSION_MINOR=${TORCH_VERSION_MINOR} + ENABLE_CUDA=${TORCH_ENABLE_CUDA} + ${ARG_EXTRA_DEFINITIONS}) +endfunction() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py new file mode 100644 index 00000000..f6f28eb2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py @@ -0,0 +1,781 @@ +## +# @file BasicPlace.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief Base placement class +# + +import os +import sys +import time +import gzip +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle +import re +import numpy as np +import logging +import torch +import torch.nn as nn +import dreamplacefpga.ops.move_boundary.move_boundary as move_boundary +import dreamplacefpga.ops.hpwl.hpwl as hpwl +import dreamplacefpga.ops.electric_potential.electric_overflow as electric_overflow +import dreamplacefpga.ops.draw_place.draw_place as draw_place +import dreamplacefpga.ops.pin_pos.pin_pos as pin_pos +import dreamplacefpga.ops.precondWL.precondWL as precondWL +import dreamplacefpga.ops.demandMap.demandMap as demandMap +import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin as sortNode2Pin +import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization as lut_ff_legalization +import pdb + +datatypes = { + 'float32' : torch.float32, + 'float64' : torch.float64 + } + +class PlaceDataCollectionFPGA(object): + """ + @brief A wraper for all data tensors on device for building ops + """ + def __init__(self, pos, params, placedb, device): + """ + @brief initialization + @param pos locations of cells + @param params parameters + @param placedb placement database + @param device cpu or cuda + """ + self.device = device + self.dtype = datatypes[params.dtype] + torch.set_num_threads(params.num_threads) + # position should be parameter + self.pos = pos + + with torch.no_grad(): + # other tensors required to build ops + + self.node_size_x = torch.from_numpy(placedb.node_size_x).to(device) + self.node_size_y = torch.from_numpy(placedb.node_size_y).to(device) + self.resource_size_x = torch.from_numpy(placedb.resource_size_x).to(device) + self.resource_size_y = torch.from_numpy(placedb.resource_size_y).to(device) + self.node_x = torch.from_numpy(placedb.node_x).to(device) + self.node_y = torch.from_numpy(placedb.node_y).to(device) + self.node_z = torch.from_numpy(placedb.node_z.astype(np.int32)).to(device) + self.site_type_map = torch.from_numpy(placedb.site_type_map.astype(np.int32)).to(device) + self.lg_siteXYs = torch.from_numpy(placedb.lg_siteXYs).to(device) + + if params.routability_opt_flag: + self.original_node_size_x = self.node_size_x.clone() + self.original_node_size_y = self.node_size_y.clone() + + self.pin_offset_x = torch.from_numpy(placedb.pin_offset_x).to(device) + self.pin_offset_y = torch.from_numpy(placedb.pin_offset_y).to(device) + self.lg_pin_offset_x = torch.from_numpy(placedb.lg_pin_offset_x).to(device) + self.lg_pin_offset_y = torch.from_numpy(placedb.lg_pin_offset_y).to(device) + + # original pin offset for legalization, since they will be adjusted in global placement + if params.routability_opt_flag: + self.original_pin_offset_x = self.pin_offset_x.clone() + self.original_pin_offset_y = self.pin_offset_y.clone() + + self.node_areas = self.node_size_x * self.node_size_y + self.movable_macro_mask = None + + self.pin2node_map = torch.from_numpy(placedb.pin2node_map).to(device) + self.flat_node2pin_map = torch.from_numpy(placedb.flat_node2pin_map).to(device) + self.flat_node2pin_start_map = torch.from_numpy(placedb.flat_node2pin_start_map).to(device) + self.node2outpinIdx_map = torch.from_numpy(placedb.node2outpinIdx_map).to(device) + self.node2pincount_map = torch.from_numpy(placedb.node2pincount_map).to(device) + self.net2pincount_map = torch.from_numpy(placedb.net2pincount_map).to(device) + + # number of pins for each cell + self.pin_weights = (self.flat_node2pin_start_map[1:] - + self.flat_node2pin_start_map[:-1]).to( + self.dtype) + ## Resource type masks + self.flop_mask = torch.from_numpy(placedb.flop_mask).to(device) + self.lut_mask = torch.from_numpy(placedb.lut_mask).to(device) + if placedb.sDSPIdx != -1: + self.dsp_mask = torch.from_numpy(placedb.dsp_mask).to(device) + if placedb.sBRAMIdx != -1 or placedb.sM9KIdx != -1: + self.ram0_mask = torch.from_numpy(placedb.ram0_mask).to(device) + if placedb.sM144KIdx != -1: + self.ram1_mask = torch.from_numpy(placedb.ram1_mask).to(device) + self.flop_lut_mask = self.flop_mask | self.lut_mask + + if placedb.sBRAMIdx == -1: + self.dsp_ram_mask = self.dsp_mask | self.ram0_mask | self.ram1_mask + else: + self.dsp_ram_mask = self.dsp_mask | self.ram0_mask + + self.io_mask = torch.from_numpy(placedb.io_mask).to(device) + self.fixed_rsrcIds = torch.from_numpy(placedb.fixed_rsrcIds).to(dtype=torch.int32,device=device) + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + self.is_mlab_node = torch.from_numpy(placedb.is_mlab_node).to(device) + + #LUT type list + self.lut_type = torch.from_numpy(placedb.lut_type).to(dtype=torch.int32,device=device) + self.cluster_lut_type = torch.from_numpy(placedb.cluster_lut_type).to(dtype=torch.int32,device=device) + self.pin_typeIds = torch.from_numpy(placedb.pin_typeIds).to(dtype=torch.int32,device=device) + + #FF control sets + self.flop_ctrlSets = torch.from_numpy(placedb.flat_ctrlSets).to(dtype=torch.int32,device=device) + #FF to ctrlset ID + self.flop2ctrlSetId_map = torch.from_numpy(placedb.flop2ctrlSetId_map).to(dtype=torch.int32,device=device) + #Spiral accessor for legalization + self.spiral_accessor = torch.from_numpy(placedb.spiral_accessor).to(dtype=torch.int32,device=device) + #Resource type indexing + self.flop_indices = torch.from_numpy(placedb.flop_indices).to(dtype=torch.int32,device=device) + self.lut_indices = torch.nonzero(self.lut_mask, as_tuple=True)[0].to(dtype=torch.int32) + self.flop_lut_indices = torch.nonzero(self.flop_lut_mask, as_tuple=True)[0].to(dtype=torch.int32) + self.dsp_ram_indices = torch.nonzero(self.dsp_ram_mask, as_tuple=True)[0].to(dtype=torch.int32) + self.pin_weights[self.flop_mask] = params.ffPinWeight + self.unit_pin_capacity = torch.empty(1, dtype=self.dtype, device=device) + self.unit_pin_capacity.data.fill_(params.unit_pin_capacity) + + # routing information + # project initial routing utilization map to one layer + self.initial_horizontal_utilization_map = None + self.initial_vertical_utilization_map = None + if params.routability_opt_flag and placedb.initial_horizontal_demand_map is not None: + self.initial_horizontal_utilization_map = torch.from_numpy( + placedb.initial_horizontal_demand_map).to(device).div_( + placedb.routing_grid_size_y * + placedb.unit_horizontal_capacity) + self.initial_vertical_utilization_map = torch.from_numpy( + placedb.initial_vertical_demand_map).to(device).div_( + placedb.routing_grid_size_x * + placedb.unit_vertical_capacity) + + self.pin2net_map = torch.from_numpy(placedb.pin2net_map.astype(np.int32)).to(device) + self.flat_net2pin_map = torch.from_numpy(placedb.flat_net2pin_map).to(device) + self.flat_net2pin_start_map = torch.from_numpy(placedb.flat_net2pin_start_map).to(device) + if np.amin(placedb.net_weights) == np.amax(placedb.net_weights): # empty tensor + logging.warning("net weights are all the same, ignored") + #self.net_weights = torch.Tensor().to(device) + self.net_weights = torch.from_numpy(placedb.net_weights).to(device) + + # regions + self.region_boxes = [torch.tensor(region).to(device) for region in placedb.region_boxes] + self.flat_region_boxes = torch.from_numpy( + placedb.flat_region_boxes).to(device) + self.flat_region_boxes_start = torch.from_numpy( + placedb.flat_region_boxes_start).to(device) + self.node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map).to(device) + + self.num_nodes = torch.tensor(placedb.num_nodes, dtype=torch.int32, device=device) + self.num_movable_nodes = torch.tensor(placedb.num_movable_nodes, dtype=torch.int32, device=device) + self.num_filler_nodes = torch.tensor(placedb.num_filler_nodes, dtype=torch.int32, device=device) + self.num_physical_nodes = torch.tensor(placedb.num_physical_nodes, dtype=torch.int32, device=device) + self.filler_start_map = torch.from_numpy(placedb.filler_start_map).to(device) + + ## this is for overflow op + self.total_movable_node_area_fence_region = torch.from_numpy(placedb.total_movable_node_area_fence_region).to(device) + ## this is for gamma update + self.num_movable_nodes_fence_region = torch.from_numpy(placedb.num_movable_nodes_fence_region).to(device) + ## this is not used yet + self.num_filler_nodes_fence_region = torch.from_numpy(placedb.num_filler_nodes_fence_region).to(device) + + self.net_mask_all = torch.from_numpy(np.ones(placedb.num_nets,dtype=np.uint8)).to(device) # all nets included + net_degrees = np.array([len(net2pin) for net2pin in placedb.net2pin_map]) + net_mask = np.logical_and(2 <= net_degrees, + net_degrees < params.ignore_net_degree).astype(np.uint8) + self.net_mask_ignore_large_degrees = torch.from_numpy(net_mask).to(device) # nets with large degrees are ignored + + # For WL computation + self.net_bounding_box_min = torch.zeros(placedb.num_nets * 2, dtype=self.dtype, device=self.device) + self.net_bounding_box_max = torch.zeros_like(self.net_bounding_box_min) + + # avoid computing gradient for fixed macros + # 1 is for fixed macros - IOs + self.pin_mask_ignore_fixed_macros = (self.pin2node_map >= placedb.num_movable_nodes) + + # sort nodes by size, return their sorted indices, designed for memory coalesce in electrical force + movable_size_x = self.node_size_x[:placedb.num_movable_nodes] + _, self.sorted_node_map = torch.sort(movable_size_x) + self.sorted_node_map = self.sorted_node_map.to(torch.int32) + + self.targetOverflow = torch.from_numpy(placedb.targetOverflow).to(dtype=self.dtype, device=device) + self.node_area_adjust_overflow = torch.from_numpy(placedb.node_area_adjust_overflow).to(dtype=self.dtype, device=device) + + #Filler start/end for FF and LUT for resource area update + self.ff_filler_start = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rFFIdx]] + self.ff_filler_end = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rFFIdx]+1] + self.lut_filler_start = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rLUTIdx]] + self.lut_filler_end = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rLUTIdx]+1] + + #Carry chain nodes as single entity + if placedb.num_ccNodes > 0: + self.org_node_x = torch.from_numpy(placedb.org_node_x).to(device) + self.org_node_y = torch.from_numpy(placedb.org_node_y).to(device) + self.org_node_z = torch.from_numpy(placedb.org_node_z.astype(np.int32)).to(device) + org_flop_lut_mask = torch.from_numpy(placedb.org_lut_flop_mask).to(device) + self.org_flop_lut_indices = torch.nonzero(org_flop_lut_mask, as_tuple=True)[0].to(dtype=torch.int32) + self.org_is_mlab_node = torch.from_numpy(placedb.org_is_mlab_node).to(device) + self.org_flop2ctrlSetId_map = torch.from_numpy(placedb.org_flop2ctrlSetId_map).to(dtype=torch.int32,device=device) + self.org_flop_ctrlSets = torch.from_numpy(placedb.flat_org_ctrlSets).to(dtype=torch.int32,device=device) + self.org_pin2node_map = torch.from_numpy(placedb.org_pin2node_map).to(device) + self.org_flat_node2pin_map = torch.from_numpy(placedb.org_flat_node2pin_map).to(device) + self.org_flat_node2pin_start_map = torch.from_numpy(placedb.org_flat_node2pin_start_map).to(device) + self.org_node2outpinIdx_map = torch.from_numpy(placedb.org_node2outpinIdx_map).to(device) + self.org_node2pincount_map = torch.from_numpy(placedb.org_node2pincount_map).to(device) + self.org_node2fence_region_map = torch.from_numpy(placedb.org_node2fence_region_map).to(device) + self.org_lut_type = torch.from_numpy(placedb.org_lut_type).to(dtype=torch.int32,device=device) + self.org_lg_pin_offset_x = torch.from_numpy(placedb.org_lg_pin_offset_x).to(device) + self.org_lg_pin_offset_y = torch.from_numpy(placedb.org_lg_pin_offset_y).to(device) + self.org_node_size_x = torch.from_numpy(placedb.org_node_size_x).to(device) + self.org_node_size_y = torch.from_numpy(placedb.org_node_size_y).to(device) + self.org_node_areas = self.org_node_size_x * self.org_node_size_y + +class PlaceOpCollectionFPGA(object): + """ + @brief A wrapper for all ops + """ + def __init__(self): + """ + @brief initialization + """ + self.demandMap_op = None + self.pin_pos_op = None + self.move_boundary_op = None + self.hpwl_op = None + self.precondwl_op = None + self.wirelength_op = None + self.update_gamma_op = None + self.density_op = None + self.update_density_weight_op = None + self.lg_precondition_op = None + self.noise_op = None + self.draw_place_op = None + self.route_utilization_map_op = None + self.pin_utilization_map_op = None + self.clustering_compatibility_lut_area_op= None + self.clustering_compatibility_ff_area_op= None + self.adjust_node_area_op = None + self.sort_node2pin_op = None + self.lut_ff_legalization_op = None + +class BasicPlaceFPGA(nn.Module): + """ + @brief Base placement class. + All placement engines should be derived from this class. + """ + def __init__(self, params, placedb): + """ + @brief initialization + @param params parameter + @param placedb placement database + """ + torch.manual_seed(params.random_seed) + super(BasicPlaceFPGA, self).__init__() + + #Assign carry chain net weighting if specified + if params.cc_net_weight: + placedb.carry_chain_net_weight = params.cc_net_weight + else: + placedb.carry_chain_net_weight = 1.0 + + ################################################### + ##IDENTIFY IF THERE ARE CARRY CHAINS IN THE DESIGN + ################################################### + if placedb.num_ccNodes == 0: + #nodes_with_carry_chain = np.zeros(placedb.num_physical_nodes, dtype=np.int32) + placedb.carry_chain_driver = np.ones(placedb.num_physical_nodes, dtype=np.int32) + placedb.carry_chain_driver *= -1 + placedb.carry_chain_sink = np.ones_like(placedb.carry_chain_driver) + placedb.carry_chain_sink *= -1 + placedb.carry_chain_nets = np.ones(placedb.num_nets, dtype=np.int32) + placedb.carry_chain_nets *= -1 + + lut_indices = np.nonzero(placedb.lut_mask)[0].astype(np.int32) + #Check for carry chains if cout-cin connections exist + if 30 in placedb.pin_typeIds and 31 in placedb.pin_typeIds: + #Obtain carry chain information + for instId in lut_indices: + pinIdBeg = placedb.flat_node2pin_start_map[instId] + pinIdEnd = placedb.flat_node2pin_start_map[instId+1] + for pinId in range(pinIdBeg, pinIdEnd, 1): + outPinId = placedb.flat_node2pin_map[pinId] + if placedb.pin_typeIds[outPinId] != 30: continue + outNetId = placedb.pin2net_map[outPinId] + pinIdxBeg = placedb.flat_net2pin_start_map[outNetId] + pinIdxEnd = placedb.flat_net2pin_start_map[outNetId+1] + for pinId in range(pinIdxBeg, pinIdxEnd, 1): + pinIdx = placedb.flat_net2pin_map[pinId] + nodeIdx = placedb.pin2node_map[pinIdx] + if placedb.pin_typeIds[pinIdx] == 31 and nodeIdx != instId: + placedb.net_weights[outNetId] = placedb.carry_chain_net_weight + placedb.carry_chain_nets[outNetId] = placedb.carry_chain_net_weight + placedb.carry_chain_sink[instId] = nodeIdx + placedb.carry_chain_driver[nodeIdx] = instId + ccd = placedb.carry_chain_driver > -1 + ccs = placedb.carry_chain_sink > -1 + #placedb.nodes_with_carry_chain=np.logical_or(ccd, ccs) + #placedb.nodes_cc_start=np.logical_and(~ccd, ccs) + #placedb.non_root_cc_nodes=np.logical_and(placedb.nodes_with_carry_chain,~placedb.nodes_cc_start) + #carry_chain_nodeIds = np.where(np.logical_or(ccd, ccs))[0].astype(np.int32) + #Instance ids that are the start of carry chains + placedb.carry_chain_start = np.where(np.logical_and(~ccd, ccs))[0].astype(np.int32) + placedb.num_carry_chains = placedb.carry_chain_start.shape[0] + + ##placedb.node_area = placedb.node_size_x * placedb.node_size_y + node_cc_id=np.ones(placedb.num_physical_nodes, dtype=np.int32) + node_cc_id*=-1 + node_cc_id[placedb.carry_chain_start]=np.arange(placedb.num_carry_chains) + + #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + ####Get number of nodes in each carry chain + #cc_element_count=np.zeros(placedb.num_carry_chains, dtype=np.int32) + #for el in placedb.carry_chain_start: + # ccId=node_cc_id[el] + # #Get node info + # cc_element_count[ccId]=cc_element_count[ccId]+1 + # sink_node=placedb.carry_chain_sink[el] + # while sink_node > -1: + # #Get node info + # cc_element_count[ccId]=cc_element_count[ccId]+1 + # #Next sink + # sink_node=placedb.carry_chain_sink[sink_node] + #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + + flat_cc2node_map = [] + flat_cc2node_start_map = [] + + flat_cc2node_start_map.append(0) + for el in placedb.carry_chain_start: + ccId=node_cc_id[el] + #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + #header="carry " + placedb.node_names[el] + " " + str(cc_element_count[ccId]) + #midportion="\t" + placedb.node_names[el] + "\n" + #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + flat_cc2node_map.append(el) + sink_node=placedb.carry_chain_sink[el] + while sink_node > -1: + flat_cc2node_map.append(sink_node) + #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + #midportion=midportion+"\t" + placedb.node_names[sink_node] + "\n" + #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + sink_node=placedb.carry_chain_sink[sink_node] + flat_cc2node_start_map.append(len(flat_cc2node_map)) + ### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + #footer="endcarry" + ##TODO - Uncomment below 3 lines to generate carry chain information + #print(header) + #print(midportion) + #print(footer) + ### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE + placedb.flat_cc2node_map = np.array(flat_cc2node_map, dtype=np.int32) + placedb.flat_cc2node_start_map = np.array(flat_cc2node_start_map, dtype=np.int32) + + if placedb.num_carry_chains > 0: + logging.info("There are %d carry chains across %d nodes and %d nets" % + (placedb.num_carry_chains, placedb.flat_cc2node_map.shape[0], (placedb.carry_chain_nets > -1).sum())) + else: + placedb.num_carry_chains = placedb.num_ccNodes + ################################################### + ##END OF CARRY CHAINS IDENTIFICATION + ################################################### + + + ## Random Initial Placement + self.init_pos = np.zeros(placedb.num_nodes * 2, dtype=placedb.dtype) + + ##Settings to ensure reproduciblity + manualSeed = 0 + np.random.seed(manualSeed) + torch.manual_seed(manualSeed) + if params.gpu: + torch.cuda.manual_seed(manualSeed) + torch.cuda.manual_seed_all(manualSeed) + + numPins = 0 + initLocX = 0 + initLocY = 0 + + if placedb.num_terminals > 0: + ##Use the average fixed pin location (weighted by pin count) as the initial location + for nodeID in range(placedb.num_movable_nodes,placedb.num_physical_nodes): + for pID in placedb.node2pin_map[nodeID]: + initLocX += placedb.node_x[nodeID] + placedb.pin_offset_x[pID] + initLocY += placedb.node_y[nodeID] + placedb.pin_offset_y[pID] + numPins += len(placedb.node2pin_map[nodeID]) + initLocX /= numPins + initLocY /= numPins + else: ##Design does not have IO pins - place in center + initLocX = 0.5 * (placedb.xh - placedb.xl) + initLocY = 0.5 * (placedb.yh - placedb.yl) + + # x position + self.init_pos[0:placedb.num_physical_nodes] = placedb.node_x + if params.global_place_flag and params.random_center_init_flag: # move to centroid of layout + #logging.info("Move cells to the centroid of fixed IOs with random noise") + self.init_pos[0:placedb.num_movable_nodes] = np.random.normal( + loc = initLocX, + scale = min(placedb.xh - placedb.xl, placedb.yh - placedb.yl) * 0.001, + size = placedb.num_movable_nodes) + self.init_pos[0:placedb.num_movable_nodes] -= (0.5 * placedb.node_size_x[0:placedb.num_movable_nodes]) + + # y position + self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes] = placedb.node_y + if params.global_place_flag and params.random_center_init_flag: # move to center of layout + self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes] = np.random.normal( + loc = initLocY, + scale = min(placedb.xh - placedb.xl, placedb.yh - placedb.yl) * 0.001, + size = placedb.num_movable_nodes) + self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes] -= (0.5 * placedb.node_size_y[0:placedb.num_movable_nodes]) + #logging.info("Random Init Place in python takes %.2f seconds" % (time.time() - tt)) + + if placedb.num_filler_nodes: # uniformly distribute filler cells in the layout + ### uniformly spread fillers in fence region + ### for cells in the fence region + for idx in range(placedb.regions): + i = placedb.rsrc2compId_map[idx] + if i != -1: + region = placedb.region_boxes[i] + #Construct Nx4 np array for region using placedb.flat_region_boxes + filler_beg, filler_end = placedb.filler_start_map[i:i+2] + if filler_end-filler_beg > 0: + num_region_fillers = filler_end-filler_beg + subregion_areas = (region[:,2]-region[:,0])*(region[:,3]-region[:,1]) + total_area = np.sum(subregion_areas) + subregion_area_ratio = subregion_areas / total_area + subregion_num_filler = np.floor((filler_end - filler_beg) * subregion_area_ratio) + rem_fillers = num_region_fillers - int(subregion_num_filler.sum()) + subregion_num_filler[:rem_fillers] += 1 + #subregion_num_filler[-1] = (filler_end - filler_beg) - np.sum(subregion_num_filler[:-1]) + subregion_num_filler_start_map = np.concatenate([np.zeros([1]),np.cumsum(subregion_num_filler)],0).astype(np.int32) + for j, subregion in enumerate(region): + sub_filler_beg, sub_filler_end = subregion_num_filler_start_map[j:j+2] + self.init_pos[placedb.num_physical_nodes+filler_beg+sub_filler_beg:placedb.num_physical_nodes+filler_beg+sub_filler_end]=np.random.uniform( + low=subregion[0], + high=subregion[2] - + placedb.filler_size_x_fence_region[i], + size=sub_filler_end-sub_filler_beg) + self.init_pos[placedb.num_nodes+placedb.num_physical_nodes+filler_beg+sub_filler_beg:placedb.num_nodes+placedb.num_physical_nodes+filler_beg+sub_filler_end]=np.random.uniform( + low=subregion[1], + high=subregion[3] - + placedb.filler_size_y_fence_region[i], + size=sub_filler_end-sub_filler_beg) + #Skip for IOs + else: + continue + + #logging.info("Random Init Place in Python takes %.2f seconds" % (time.time() - t2)) + + self.device = torch.device("cuda" if params.gpu else "cpu") + + # position should be parameter + # must be defined in BasicPlace + #tbp = time.time() + self.pos = nn.ParameterList( + [nn.Parameter(torch.from_numpy(self.init_pos).to(self.device))]) + #logging.info("build pos takes %.2f seconds" % (time.time() - tbp)) + # shared data on device for building ops to avoid constructing data from placedb again and again + #tt = time.time() + self.data_collections = PlaceDataCollectionFPGA(self.pos, params, placedb, self.device) + #logging.info("build data_collections takes %.2f seconds" % + # (time.time() - tt)) + + # All ops are wrapped + #tt = time.time() + self.op_collections = PlaceOpCollectionFPGA() + #logging.info("build op_collections takes %.2f seconds" % + # (time.time() - tt)) + + tt = time.time() + # Demand Map computation + self.op_collections.demandMap_op = self.build_demandMap(params, placedb, self.data_collections, self.device) + # position to pin position + self.op_collections.pin_pos_op = self.build_pin_pos(params, placedb, self.data_collections, self.device) + # bound nodes to layout region + self.op_collections.move_boundary_op = self.build_move_boundary(params, placedb, self.data_collections, self.device) + # hpwl and density overflow ops for evaluation + self.op_collections.hpwl_op = self.build_hpwl(params, placedb, self.data_collections, self.op_collections.pin_pos_op, self.device) + # WL preconditioner + self.op_collections.precondwl_op = self.build_precondwl(params, placedb, self.data_collections, self.device) + self.op_collections.lg_precondition_op = self.build_LGprecondwl(params, placedb, self.data_collections, self.device) + # Sorting node2pin map + self.op_collections.sort_node2pin_op = self.build_sortNode2Pin(params, placedb, self.data_collections, self.device) + # rectilinear minimum steiner tree wirelength from flute + # can only be called once + self.op_collections.density_overflow_op = self.build_electric_overflow(params, placedb, self.data_collections, self.device) + + ##Legalization + self.op_collections.lut_ff_legalization_op = self.build_lut_ff_legalization(params, placedb, self.data_collections, self.device) + + # draw placement + self.op_collections.draw_place_op = self.build_draw_placement(params, placedb) + + #logging.info("build BasicPlace ops takes %.2f seconds" % + # (time.time() - tt)) + + def __call__(self, params, placedb): + """ + @brief Solve placement. + placeholder for derived classes. + @param params parameters + @param placedb placement database + """ + pass + + def build_pin_pos(self, params, placedb, data_collections, device): + """ + @brief sum up the pins for each cell + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + # Yibo: I found CPU version of this is super slow, more than 2s for ISPD2005 bigblue4 with 10 threads. + # So I implemented a custom CPU version, which is around 20ms + #pin2node_map = data_collections.pin2node_map.long() + #def build_pin_pos_op(pos): + # pin_x = data_collections.pin_offset_x.add(torch.index_select(pos[0:placedb.num_physical_nodes], dim=0, index=pin2node_map)) + # pin_y = data_collections.pin_offset_y.add(torch.index_select(pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes], dim=0, index=pin2node_map)) + # pin_pos = torch.cat([pin_x, pin_y], dim=0) + + # return pin_pos + #return build_pin_pos_op + + return pin_pos.PinPos( + pin_offset_x=data_collections.pin_offset_x, + pin_offset_y=data_collections.pin_offset_y, + pin2node_map=data_collections.pin2node_map, + flat_node2pin_map=data_collections.flat_node2pin_map, + flat_node2pin_start_map=data_collections.flat_node2pin_start_map, + num_physical_nodes=placedb.num_physical_nodes, + num_threads=params.num_threads, + algorithm="node-by-node") + + def build_move_boundary(self, params, placedb, data_collections, device): + """ + @brief bound nodes into layout region + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + return move_boundary.MoveBoundary( + data_collections.node_size_x, + data_collections.node_size_y, + xl=placedb.xl, + yl=placedb.yl, + xh=placedb.xh, + yh=placedb.yh, + num_movable_nodes=placedb.num_movable_nodes, + num_filler_nodes=placedb.num_filler_nodes, + num_threads=params.num_threads) + + def build_hpwl(self, params, placedb, data_collections, pin_pos_op, device): + """ + @brief compute half-perimeter wirelength + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param pin_pos_op the op to compute pin locations according to cell locations + @param device cpu or cuda + """ + wirelength_for_pin_op = hpwl.HPWL( + placedb=placedb, + flat_netpin=data_collections.flat_net2pin_map, + netpin_start=data_collections.flat_net2pin_start_map, + pin2net_map=data_collections.pin2net_map, + net_weights=data_collections.net_weights, + num_carry_chains=placedb.num_carry_chains, + cc_net_weight=placedb.carry_chain_net_weight, + dir_net_weight=params.dir_net_weight, + #net_mask=data_collections.net_mask_all, + net_mask=data_collections.net_mask_ignore_large_degrees, + net_bounding_box_min=data_collections.net_bounding_box_min, + net_bounding_box_max=data_collections.net_bounding_box_max, + num_threads=params.num_threads, + algorithm='net-by-net') + + # wirelength for position + def build_wirelength_op(pos): + return wirelength_for_pin_op(pin_pos_op(pos)) + + return build_wirelength_op + + def build_demandMap(self, params, placedb, data_collections, device): + """ + @brief Build binCapMap and fixedDemandMap + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + return demandMap.DemandMap( + placedb=placedb, + site_type_map=data_collections.site_type_map, + site_size_x=data_collections.resource_size_x, + site_size_y=data_collections.resource_size_y, + deterministic_flag=params.deterministic_flag, + device=device, + num_threads=params.num_threads) + + def build_precondwl(self, params, placedb, data_collections, device): + """ + @brief compute wirelength precondtioner + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + return precondWL.PrecondWL( + flat_node2pin_start=data_collections.flat_node2pin_start_map, + flat_node2pin=data_collections.flat_node2pin_map, + pin2net_map=data_collections.pin2net_map, + flat_net2pin=data_collections.flat_net2pin_start_map, + net_weights=data_collections.net_weights, + num_nodes=placedb.num_nodes, + num_movable_nodes=placedb.num_physical_nodes,#Compute for fixed nodes as well for Legalization + device=device, + num_threads=params.num_threads) + + def build_LGprecondwl(self, params, placedb, data_collections, device): + """ + @brief compute wirelength precondtioner + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + if placedb.num_ccNodes > 0: + return precondWL.PrecondWL( + flat_node2pin_start=data_collections.org_flat_node2pin_start_map, + flat_node2pin=data_collections.org_flat_node2pin_map, + pin2net_map=data_collections.pin2net_map, + flat_net2pin=data_collections.flat_net2pin_start_map, + net_weights=data_collections.net_weights, + num_nodes=placedb.org_num_physical_nodes + placedb.num_filler_nodes, + num_movable_nodes=placedb.org_num_physical_nodes,#Compute for fixed nodes as well for Legalization + device=device, + num_threads=params.num_threads) + + def build_sortNode2Pin(self, params, placedb, data_collections, device): + """ + @brief sort instance node2pin mapping + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + #Only used for LG + if placedb.num_ccNodes == 0: + return sortNode2Pin.SortNode2Pin( + flat_node2pin_start=data_collections.flat_node2pin_start_map, + flat_node2pin=data_collections.flat_node2pin_map, + num_nodes=placedb.num_physical_nodes, + device=device, + num_threads=params.num_threads) + else: + return sortNode2Pin.SortNode2Pin( + flat_node2pin_start=data_collections.org_flat_node2pin_start_map, + flat_node2pin=data_collections.org_flat_node2pin_map, + num_nodes=placedb.org_num_physical_nodes, + device=device, + num_threads=params.num_threads) + + def build_electric_overflow(self, params, placedb, data_collections, device): + """ + @brief compute electric density overflow + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + return electric_overflow.ElectricOverflow( + node_size_x=data_collections.node_size_x, + node_size_y=data_collections.node_size_y, + xl=placedb.xl, + yl=placedb.yl, + xh=placedb.xh, + yh=placedb.yh, + bin_size_x=placedb.bin_size_x, + bin_size_y=placedb.bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=0, + deterministic_flag=params.deterministic_flag, + sorted_node_map=data_collections.sorted_node_map) + + + def build_lut_ff_legalization(self, params, placedb, data_collections, device): + """ + @brief legalization of LUT/FF Instances + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param device cpu or cuda + """ + # legalize LUT/FF + ###Avg areas + ##avgLUTArea = data_collections.node_areas[:placedb.num_physical_nodes][placedb.lut_mask].sum() + ##avgLUTArea /= placedb.node_count[placedb.rLUTIdx] + ##avgFFArea = data_collections.node_areas[:placedb.num_physical_nodes][placedb.flop_mask].sum() + ##avgFFArea /= placedb.node_count[placedb.rFFIdx] + ###Inst Areas + ##inst_areas = data_collections.node_areas[:placedb.num_physical_nodes].detach().clone() + ##inst_areas[~placedb.lut_flop_mask] = 0.0 #Area of non SLICE nodes set to 0.0 + ##inst_areas[placedb.lut_mask] /= avgLUTArea + ##inst_areas[placedb.flop_mask] /= avgFFArea + #Site types + site_types = data_collections.site_type_map.detach().clone() + site_types[site_types != placedb.sSLICEIdx] = 0 #Set non SLICE to 0 + + if (len(data_collections.net_weights)): + net_wts = data_collections.net_weights + else: + net_wts = torch.ones(placedb.num_nets, dtype=self.pos[0].dtype, device=device) + + return lut_ff_legalization.LegalizeCLB( + data_collections=data_collections, + placedb=placedb, + net_wts=net_wts, + #inst_areas=inst_areas, + site_types=site_types, + num_threads=params.num_threads, + device=device) + + def build_draw_placement(self, params, placedb): + """ + @brief plot placement + @param params parameters + @param placedb placement database + """ + return draw_place.DrawPlaceFPGA(placedb) + + def validate(self, placedb, pos, iteration): + """ + @brief validate placement + @param placedb placement database + @param pos locations of cells + @param iteration optimization step + """ + pos = torch.from_numpy(pos).to(self.device) + hpwl = self.op_collections.hpwl_op(pos) + overflow, max_density = self.op_collections.density_overflow_op(pos) + + return hpwl, overflow, max_density + + def plot(self, params, placedb, iteration, pos): + """ + @brief plot layout + @param params parameters + @param placedb placement database + @param iteration optimization step + @param pos locations of cells + """ + tt = time.time() + path = "%s/%s" % (params.result_dir, params.design_name()) + figname = "%s/plot/iter%s.png" % (path, '{:04}'.format(iteration)) + os.system("mkdir -p %s" % (os.path.dirname(figname))) + if isinstance(pos, np.ndarray): + pos = torch.from_numpy(pos) + self.op_collections.draw_place_op(pos, figname) + logging.info("plotting to %s takes %.3f seconds" % + (figname, time.time() - tt)) + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt new file mode 100644 index 00000000..c3ec0149 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(ops) + +file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py" "${CMAKE_CURRENT_SOURCE_DIR}/paramsFPGA.json") +install( + FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py new file mode 100644 index 00000000..d2b05ce3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py @@ -0,0 +1,198 @@ +## +# @file EvalMetrics.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief Evaluation metrics +# + +import time +import torch +import pdb + +class EvalMetrics (object): + """ + @brief evaluation metrics at one step + """ + def __init__(self, iteration=None, detailed_step=None): + """ + @brief initialization + @param iteration optimization step + """ + self.iteration = iteration + self.detailed_step = detailed_step + self.objective = None + self.wirelength = None + self.density = None + self.density_weight = None + self.hpwl = None + self.rmst_wl = None + self.overflow = None + self.route_utilization = None + self.pin_utilization = None + self.max_density = None + self.gamma = None + self.eval_time = None + + def __str__(self): + """ + @brief convert to string + """ + content = "" + if self.iteration is not None: + content = "iter: %4d" % (self.iteration) + #if self.detailed_step is not None: + # content += ", (%4d, %2d, %2d)" % (self.detailed_step[0], self.detailed_step[1], self.detailed_step[2]) + #if self.objective is not None: + # content += ", Obj %.6E" % (self.objective) + #if self.wirelength is not None: + # content += ", WL %.3E" % (self.wirelength) + #if self.density is not None: + # content += ", Density [%s]" % ", ".join(["%.3E" % i for i in self.density]) + #if self.density_weight is not None: + # content += ", DensityWeight [%s]" % ", ".join(["%.3E" % i for i in self.density_weight]) + if self.hpwl is not None: + content += ", HPWL %.6E" % (self.hpwl) + if self.overflow is not None: + content += ", Overflow [%s]" % ", ".join(["%.3E" % i for i in self.overflow]) + #if self.max_density is not None: + # content += ", MaxDensity [%s]" % ", ".join(["%.3E" % i for i in self.max_density]) + #if self.route_utilization is not None: + # content += ", RouteOverflow %.6E" % (self.route_utilization) + #if self.pin_utilization is not None: + # content += ", PinOverflow %.6E" % (self.pin_utilization) + #if self.gamma is not None: + # content += ", gamma %.6E" % (self.gamma) + if self.eval_time is not None: + content += ", time %.3fms" % (self.eval_time*1000) + + return content + + def __repr__(self): + """ + @brief print + """ + return self.__str__() + + def evaluate(self, placedb, ops, var, data_collections=None): + """ + @brief evaluate metrics + @param placedb placement database + @param ops a list of ops + @param var variables + """ + tt = time.time() + with torch.no_grad(): + if "objective" in ops: + self.objective = ops["objective"](var).data + if "wirelength" in ops: + self.wirelength = ops["wirelength"](var).data + if "density" in ops: + self.density = ops["density"](var).data + if "hpwl" in ops: + self.hpwl = ops["hpwl"](var).data + if "rmst_wls" in ops: + rmst_wls = ops["rmst_wls"](var) + self.rmst_wl = rmst_wls.sum().data + if "overflow" in ops: + overflow, max_density = ops["overflow"](var) + if(overflow.numel() == 1): + self.overflow = overflow.data / placedb.total_movable_node_area + self.max_density = max_density.data + else: + self.overflow = overflow.data / data_collections.total_movable_node_area_fence_region + self.max_density = max_density.data + if "goverflow" in ops: + overflow, max_density = ops["goverflow"](var) + self.goverflow = overflow.data / placedb.total_movable_node_area + self.gmax_density = max_density.data + if "route_utilization" in ops: + route_utilization_map = ops["route_utilization"](var) + route_utilization_map_sum = route_utilization_map.sum() + self.route_utilization = route_utilization_map.sub_(1).clamp_(min=0).sum() / route_utilization_map_sum + if "pin_utilization" in ops: + pin_utilization_map = ops["pin_utilization"](var) + pin_utilization_map_sum = pin_utilization_map.sum() + self.pin_utilization = pin_utilization_map.sub_(1).clamp_(min=0).sum() / pin_utilization_map_sum + self.eval_time = time.time()-tt + +#Added by Rachel +class EvalMetricsFPGA (object): + """ + @brief evaluation metrics at one step + """ + def __init__(self, iteration=None, detailed_step=None): + """ + @brief initialization + @param iteration optimization step + """ + self.iteration = iteration + self.detailed_step = detailed_step + self.objective = None + self.wirelength = None + self.density = None + self.density_weight = None + self.hpwl = None + self.overflow = None + self.max_density = None + self.gmax_density = None + self.gamma = None + self.eval_time = None + + def __str__(self): + """ + @brief convert to string + """ + content = "" + if self.iteration is not None: + content = "iter: %4d" % (self.iteration) + #if self.detailed_step is not None: + # content += ", (%4d, %2d, %2d)" % (self.detailed_step[0], self.detailed_step[1], self.detailed_step[2]) + #if self.objective is not None: + # content += ", Obj %.6E" % (self.objective) + #if self.wirelength is not None: + # content += ", WL %.3E" % (self.wirelength) + #if self.density is not None: + # content += ", Density [%s]" % ", ".join(["%.3E" % i for i in self.density]) + #if self.density_weight is not None: + # content += ", DensityWeight [%s]" % ", ".join(["%.3E" % i for i in self.density_weight]) + if self.hpwl is not None: + content += ", HPWL %.6E" % (self.hpwl) + if self.overflow is not None: + content += ", Overflow [%s]" % ", ".join(["%.3E" % i for i in self.overflow]) + #if self.max_density is not None: + # content += ", MaxDensity [%s]" % ", ".join(["%.3E" % i for i in self.max_density]) + #if self.gamma is not None: + # content += ", gamma %.6E" % (self.gamma) + if self.eval_time is not None: + content += ", time %.3fms" % (self.eval_time*1000) + + return content + + def __repr__(self): + """ + @brief print + """ + return self.__str__() + + def evaluate(self, placedb, ops, var, data_collections=None): + """ + @brief evaluate metrics + @param placedb placement database + @param ops a list of ops + @param var variables + """ + tt = time.time() + with torch.no_grad(): + if "objective" in ops: + self.objective = ops["objective"](var).data + if "wirelength" in ops: + self.wirelength = ops["wirelength"](var).data + if "density" in ops: + self.density = ops["density"](var).data + if "hpwl" in ops: + self.hpwl = ops["hpwl"](var).data + if "overflow" in ops: + overflow, max_density = ops["overflow"](var) + self.overflow = overflow.data / data_collections.total_movable_node_area_fence_region.clamp(min=1) + self.max_density = max_density.data + self.eval_time = time.time()-tt diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py new file mode 100644 index 00000000..76633dbc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py @@ -0,0 +1,2152 @@ +## +# @file IFWriter.py +# @author Zhili Xiong +# @date Dec 2022 +# @brief Convert bookshelf outputs to interchange .phys file. +# + +import capnp +import capnp.lib.capnp +capnp.remove_import_hook() + +import os +import sys +import time +import gzip +import enum +import numpy as np +import logging +import Params +import dreamplacefpga +import dreamplacefpga.ops.place_io.place_io as place_io +import pdb +from collections import namedtuple +from Params import * +from PlaceDB import * + + +NO_TRAVERSAL_LIMIT = 2**63 - 1 + +NESTING_LIMIT = 1024 + +PhysicalNet = namedtuple('PhysicalNet', 'name type sources stubs stubNodes') + + +class IFWriter(): + """ IFWirter calss to write out IF file from phys_netlist. + phys_netlist - object input, contains all the data of each field in PhysicalNetlist.capnp + """ + def __init__(self, schema_dir): + """ initialize and compile PhysicalNetlist.capnp """ + import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] + # import_path.append(os.path.join(schema_dir, '../../schema')) + import_path.append('IFsupport') + self.physical_netlist_capnp = capnp.load(os.path.join(schema_dir, 'PhysicalNetlist.capnp'), imports=import_path) + self.strList = [] + self.str2idx = {} + + def StringIdx(self, string): + """ build strList and return the index """ + if string not in self.str2idx: + self.str2idx[string] = len(self.strList) + self.strList.append(string) + + return self.str2idx[string] + + def build_IF(self, phys_netlist): + """ build IF object from phys_netlist + + phys_netlist - object input, contains all the data of each field in PhysicalNetlist.capnp + struct PhysNetlist { + part @0 : Text; + placements @1 : List(CellPlacement); + physNets @2 : List(PhysNet); + physCells @3 : List(PhysCell); + strList @4 : List(Text) $hashSet(); + siteInsts @5 : List(SiteInstance); + properties @6 : List(Property); + nullNet @7 : PhysNet; + } + """ + # initialize a new capnp object + physical_netlist = self.physical_netlist_capnp.PhysNetlist.new_message() + + # part name in textStringIdx + # part @0 : Text; + physical_netlist.part = phys_netlist.part + + # List of placement + # placements @1 : List(CellPlacement); + placements = physical_netlist.init('placements', len(phys_netlist.placements)) + for i in range(len(phys_netlist.placements)): + # struct CellPlacement { + # cellName @0 : StringIdx $stringRef(); + # type @1 : StringIdx $stringRef(); + # site @2 : StringIdx $stringRef(); + # bel @3 : StringIdx $stringRef(); + # pinMap @4 : List(PinMapping); + # otherBels @5 : List(StringIdx) $stringRef(); + # isBelFixed @6 : Bool; + # isSiteFixed @7 : Bool; + # altSiteType @8 : StringIdx $stringRef(); + # } + placement = phys_netlist.placements[i] + # cellName + placements[i].cellName = self.StringIdx(placement.cell_name) + # type + placements[i].type = self.StringIdx(placement.cell_type) + # site + placements[i].site = self.StringIdx(placement.site_name) + # bel + placements[i].bel = self.StringIdx(placement.bel_name) + # pinMap + pinMap = placements[i].init('pinMap', len(placement.pins)) + for j in range(len(placement.pins)): + # struct PinMapping { + # cellPin @0 : StringIdx $stringRef(); + # bel @1 : StringIdx $stringRef(); + # belPin @2 : StringIdx $stringRef(); + # isFixed @3 : Bool; + # union { + # multi @4 : Void; + # otherCell @5 : MultiCellPinMapping; + # } + pins = placement.pins[j] + # cellPin + pinMap[j].cellPin = self.StringIdx(pins[0]) + pinMap[j].bel = self.StringIdx(pins[1]) + pinMap[j].belPin = self.StringIdx(pins[2]) + pinMap[j].isFixed = False + + placements[i].isBelFixed = True + placements[i].isSiteFixed = True + placements[i].altSiteType = self.StringIdx('SLICE_X57Y129') + + # siteInsts + site_insts = physical_netlist.init('siteInsts', len(phys_netlist.siteInsts)) + for idx, (key, value) in enumerate(phys_netlist.siteInsts.items()): + site_insts[idx].site = self.StringIdx(key) + site_insts[idx].type = self.StringIdx(value) + + # physCells + phys_cells = physical_netlist.init('physCells', len(phys_netlist.physCells)) + for idx, (cell_name, cell_type) in enumerate(phys_netlist.physCells.items()): + phys_cells[idx].cellName = self.StringIdx(cell_name) + phys_cells[idx].physType = self.physical_netlist_capnp.PhysNetlist.PhysCellType.__dict__[ + cell_type.lower()] + + # properties + properties = physical_netlist.init('properties', 2) + properties[0].key = self.StringIdx('DISABLE_AUTO_IO_BUFFERS') + properties[0].value = self.StringIdx('0') + properties[1].key = self.StringIdx('OUT_OF_CONTEXT') + properties[1].value = self.StringIdx('0') + + # nullNet + physical_netlist.nullNet.name = self.StringIdx('SLICE_X57Y129') + + # PhysNet + nets = physical_netlist.init('physNets', len(phys_netlist.nets)) + + for idx, net in enumerate(phys_netlist.nets): + net_obj = nets[idx] + + net_obj.name = self.StringIdx(net.name) + net_obj.init('sources', len(net.sources)) + for root_obj, root in zip(net_obj.sources, net.sources): + root.output_interchange(root_obj, self.StringIdx) + + net_obj.init('stubs', len(net.stubs)) + for stub_obj, stub in zip(net_obj.stubs, net.stubs): + stub.output_interchange(stub_obj, self.StringIdx) + + net_obj.type = self.physical_netlist_capnp.PhysNetlist.NetType.__dict__[ + net.type.name.lower()] + + # strList + physical_netlist.init('strList', len(self.strList)) + + for idx, s in enumerate(self.strList): + physical_netlist.strList[idx] = s + + return physical_netlist + + def write_IF(self, physical_netlist, if_file): + """ Write out IF into file + The file name is the design_name + """ + # zipped the file into gzip file + with gzip.open(if_file, 'wb') as f_zip: + f_zip.write(physical_netlist.to_bytes()) + + +class Cellplacement(): + """ Cellplacement class for constructing placement of a single cell. + struct CellPlacement { + cellName @0 : StringIdx $stringRef(); + type @1 : StringIdx $stringRef(); + site @2 : StringIdx $stringRef(); + bel @3 : StringIdx $stringRef(); + pinMap @4 : List(PinMapping); + otherBels @5 : List(StringIdx) $stringRef(); + isBelFixed @6 : Bool; + isSiteFixed @7 : Bool; + altSiteType @8 : StringIdx $stringRef(); + } + """ + def __init__(self, cell_name, cell_type, site_name, bel_name): + """ initialize placement information """ + self.cell_name = cell_name + self.cell_type = cell_type + self.site_name = site_name + self.bel_name = bel_name + self.pins = [] + + + def add_pins(self, cellpin, belpin): + """ add pins for cell + struct PinMapping { + cellPin @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + belPin @2 : StringIdx $stringRef(); + isFixed @3 : Bool; + union { + multi @4 : Void; + otherCell @5 : MultiCellPinMapping; + } + """ + self.pins.append((cellpin, self.bel_name, belpin)) + + +def add_branch(branch_obj, phys_node, string_idx): + """ Add a branch to continue outputting the interchange to capnp object. + + branch_obj - One RouteBranch capnp object from PhysicalNetlist + phys_node - an object of PhysicalBelpin or PhysicalSitepip + + """ + branch_obj.init('branches', len(phys_node.branches)) + + for branch_obj, branch in zip(branch_obj.branches, phys_node.branches): + branch.output_interchange(branch_obj, string_idx) + + +class PhysicalBelPin(): + """ PhysicalBelpin class for intra-site routing. + struct PhysBelPin { + site @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + pin @2 : StringIdx $stringRef(); + } + """ + + def __init__(self, site_name, bel_name, pin_name): + self.site_name = site_name + self.bel_name = bel_name + self.pin_name = pin_name + + self.branches = [] + + def output_interchange(self, branch_obj, string_idx): + """ Add one route segment and all the branches under it. + branch_obj - One RouteBranch capnp object from PhysicalNetlist + + struct RouteBranch { + routeSegment : union { + belPin @0 : PhysBelPin; + sitePin @1 : PhysSitePin; + pip @2 : PhysPIP; + sitePIP @3 : PhysSitePIP; + } + branches @4 : List(RouteBranch); + } + string_idx - function that returns the index of strList in PhysicalNetlist + """ + branch_obj.routeSegment.init('belPin') + branch_obj.routeSegment.belPin.site = string_idx(self.site_name) + branch_obj.routeSegment.belPin.bel = string_idx(self.bel_name) + branch_obj.routeSegment.belPin.pin = string_idx(self.pin_name) + + add_branch(branch_obj, self, string_idx) + + def get_device_resource(self, site_types, device_resources): + """ Get device resource that corresponds to this class. """ + return device_resources.bel_pin(self.site_name, site_types[self.site_name], + self.bel_name, self.pin_name) + + def to_tuple(self): + """ Create tuple suitable for sorting this object. + This tuple is used for sorting against other routing branch objects + to generate a canonical routing tree. + """ + return ('bel_pin', self.site_name, self.bel_name, self.pin_name) + + +class PhysicalSitePip(): + """ PhysicalSitepip class for intra-site routing. + struct PhysSitePIP { + site @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + pin @2 : StringIdx $stringRef(); + isFixed @3 : Bool; + union { + isInverting @4 : Bool; + inverts @5 : Void; + } + } + """ + + def __init__(self, site_name, bel_name, pin_name): + self.site_name = site_name + self.bel_name = bel_name + self.pin_name = pin_name + self.is_inverting = False + + self.branches = [] + + def output_interchange(self, branch_obj, string_idx): + """ Add one route segment and all the branches under it. + """ + branch_obj.routeSegment.init('sitePIP') + branch_obj.routeSegment.sitePIP.site = string_idx(self.site_name) + branch_obj.routeSegment.sitePIP.bel = string_idx(self.bel_name) + branch_obj.routeSegment.sitePIP.pin = string_idx(self.pin_name) + branch_obj.routeSegment.sitePIP.isFixed = False + branch_obj.routeSegment.sitePIP.isInverting = False + + add_branch(branch_obj, self, string_idx) + + def get_device_resource(self, site_types, device_resources): + """ Get device resource that corresponds to this class. """ + return device_resources.site_pip(self.site_name, site_types[self.site_name], + self.bel_name, self.pin_name) + + def to_tuple(self): + """ Create tuple suitable for sorting this object. + This tuple is used for sorting against other routing branch objects + to generate a canonical routing tree. + """ + return ('site_pip', self.site_name, self.bel_name, self.pin_name, self.is_inverting) + + +def convert_tuple_to_object(site, tup): + """ Convert physical netlist tuple to object. + Physical netlist tuples are light weight ways to represent the physical + net tree. + site (Site) - Site object that tuple belongs too. + tup (tuple) - Tuple that is either a bel pin or site pip. + Returns - PhysicalBelPin or PhysicalSitePip based on + tuple. + """ + if tup[0] == 'bel_pin': + _, bel, pin = tup + return PhysicalBelPin(site.name, bel, pin) + elif tup[0] == 'site_pip': + _, bel, pin = tup + return PhysicalSitePip(site.name, bel, pin) + else: + return False + + +def add_site_routing_children(site, parent_obj, parent_key, site_routing): + """ Convert site_routing map into Physical* python objects. + site (Site) - Site object that contains site routing. + parent_obj (Physical* python object) - Parent Physical* object to add new + branches too. + parent_key (tuple) - Site routing tuple for current parent_obj. + site_routing (dict) - Map of parent site routing tuple to a set of + child site routing tuples. + inverted_root (list) - List of physical net sources for the inverted + signal (e.g. a constant 1 net inverts to the + constant 0 net) + """ + if parent_key in site_routing: + for child in site_routing[parent_key]: + + obj = convert_tuple_to_object(site, child) + parent_obj.branches.append(obj) + + add_site_routing_children(site, obj, child, site_routing) + + + +def create_site_routing(site, net_roots, site_routing): + """ Convert site_routing into map of nets to site local sources. + site (Site) - SiteInst object that contains site routing. + net_roots (dict) - Map of root site routing tuples to the net name for + this root. + site_routing (dict) - Map of parent site routing tuple to a set of + child site routing tuples. + Returns dict of nets to Physical* objects that represent the site local + sources for that net. + """ + nets = {} + + for root, net_name in net_roots.items(): + if net_name not in nets: + nets[net_name] = [] + + root_obj = convert_tuple_to_object(site, root) + add_site_routing_children(site, root_obj, root, site_routing) + + + nets[net_name].append(root_obj) + + return nets + +class SiteInst(): + """ + This class has the site router and site instances; + """ + def __init__(self, name): + self.name = name + self.cells = {} + self.net_belpins = {} + self.lut_map = {} + + def add_cells(self, node_name, cellplacement): + """ map from a node name in bookshelf to a list of cellplacement objetcs. """ + if node_name not in self.cells: + self.cells[node_name] = [] + + self.cells[node_name].append(cellplacement) + + def add_belpins(self, net_name, belpin_tup): + """ map from a net name to a list of belpin tuples """ + if net_name not in self.net_belpins: + self.net_belpins[net_name] = [] + + self.net_belpins[net_name].append(belpin_tup) + + def A6_LUT(self): + """ sorted luts """ + + vcc_tup = [] + A6_sitein = { + 'A': ('bel_pin', 'A6', 'A6'), + 'B': ('bel_pin', 'B6', 'B6'), + 'C': ('bel_pin', 'C6', 'C6'), + 'D': ('bel_pin', 'D6', 'D6'), + 'E': ('bel_pin', 'E6', 'E6'), + 'F': ('bel_pin', 'F6', 'F6'), + 'G': ('bel_pin', 'G6', 'G6'), + 'H': ('bel_pin', 'H6', 'H6') + } + self.lut_map = { + 'A': [], + 'B': [], + 'C': [], + 'D': [], + 'E': [], + 'F': [], + 'G': [], + 'H': [] + } + + for node, cellplacements in self.cells.items(): + for cell in cellplacements: + if cell.bel_name[-3:] == 'LUT': + self.lut_map[cell.bel_name[0]].append(cell) + + for key, cells in self.lut_map.items(): + if len(self.lut_map[key]) == 2 and '/' not in self.lut_map[key][0].cell_name: + vcc_tup.append(A6_sitein[key]) + + return vcc_tup + + + def site_router(self, routing_graph, site_in, site_out): + + nets = {} + net_roots = {} + site_routing = {} + net_source = {} + constant_nets = { + 0 : 'GLOBAL_LOGIC0', + 1 : 'GLOBAL_LOGIC1' + } + + clk_net = 'clk_BUFGP_net_top_wire' + + gnd_tup = ('bel_pin', 'HARD0GND', '0') + + # add the us+ rst + rst_tup = [('bel_pin', 'SRST_B1', 'SRST_B1'), ('bel_pin', 'SRST_B2', 'SRST_B2'), ('bel_pin', 'SRST1', 'SRST1'), ('bel_pin', 'SRST2', 'SRST2')] + + bufce_tup = [('bel_pin', 'CE_PRE_OPTINV', 'CE_PRE_OPTINV')] + + vcc_tup = self.A6_LUT() + + for net_name, belpins in self.net_belpins.items(): + + is_source = False + stubs = [] + skip_flag = False + + for belpin in belpins: + # the net has its source in this site + # belpin is source + if belpin in routing_graph: + net_roots[belpin] = net_name + site_routing[belpin] = [] + net_source[net_name] = belpin + is_source = True + + bel_name = belpin[1] + if bel_name[-4:] == '6LUT' and len(self.lut_map[bel_name[0]]) == 2: + skip_flag = True + + if skip_flag == False: + for child_tup in routing_graph[belpin]: + # through site pip + if child_tup[0] == 'site_pip': + sitepip_in = child_tup + sitepip_out = routing_graph[sitepip_in] + for out in routing_graph[sitepip_out]: + if out in site_out: + site_routing[belpin].append(sitepip_in) + site_routing[sitepip_in] = [] + site_routing[sitepip_in].append(sitepip_out) + site_routing[sitepip_out] = [] + + # belpin is stub + else: + stubs.append(belpin) + + for stub in stubs: + parent_found = False + + for parent in site_routing: + if stub in routing_graph[parent]: + site_routing[parent].append(stub) + parent_found = True + + if parent_found == True: + continue + + if is_source == True: + source = net_source[net_name] + # site wire + if stub in routing_graph[source]: + site_routing[source].append(stub) + parent_found = True + + # sitepip + else: + for child in routing_graph[source]: + if child[0] == 'site_pip': + sitepip_in = child + sitepip_out = routing_graph[sitepip_in] + if stub in routing_graph[sitepip_out]: + site_routing[source].append(sitepip_in) + site_routing[sitepip_in] = [] + site_routing[sitepip_in].append(sitepip_out) + site_routing[sitepip_out] = [] + site_routing[sitepip_out].append(stub) + parent_found = True + + if parent_found == True: + continue + + for site_pin in site_in: + site_in_found = False + # site wire + if stub in routing_graph[site_pin]: + net_roots[site_pin] = net_name + site_routing[site_pin] = [] + site_routing[site_pin].append(stub) + site_in_found = True + + # site pip + else: + for pin in routing_graph[site_pin]: + if pin[0] == 'site_pip': + sitepip_in = pin + sitepip_out = routing_graph[sitepip_in] + if stub in routing_graph[sitepip_out] : + net_roots[site_pin] = net_name + site_routing[site_pin] = [] + site_routing[site_pin].append(sitepip_in) + site_routing[sitepip_in] = [] + site_routing[sitepip_in].append(sitepip_out) + site_routing[sitepip_out] = [] + site_routing[sitepip_out].append(stub) + site_in_found = True + + if site_in_found == True: + break + + if self.name[:5] == 'SLICE': + # for rst_in in rst_tup: + # if rst_in not in site_routing and rst_in in site_in: + # net_roots[rst_in] = constant_nets[1] + # child = routing_graph[rst_in][0] + # site_routing[rst_in] = [] + # site_routing[rst_in].append(child) + # child2 = routing_graph[child] + # site_routing[child2] = [] + # net_roots[child2] = constant_nets[0] + + net_roots[gnd_tup] = constant_nets[0] + site_routing[gnd_tup] = [] + + + for vcc_in in vcc_tup: + net_roots[vcc_in] = constant_nets[1] + site_routing[vcc_in] = [] + + if self.name[:3] == 'DSP': + dsp_bel_map = {} + dsp_pin_map = {} + dsp_cell_pin = {} + for node_name in self.cells: + for cellplacement in self.cells[node_name]: + cell_name = cellplacement.cell_name + bel_name = cellplacement.bel_name + dsp_bel_map[bel_name] = node_name + for pin in cellplacement.pins: + pin_name = pin[0].replace('[', '<') + pin_name = pin_name.replace(']', '>') + dsp_pin_map[pin[2]] = pin_name + if pin[2][:7] == 'PATTERN': + cell_pin = pin[0].replace('_','') + dsp_cell_pin[pin[2]] = cell_pin + else: + dsp_cell_pin[pin[2]] = pin[0] + + + for bel_out in routing_graph: + if bel_out not in site_routing and bel_out[1] in dsp_bel_map: + site_routing[bel_out] = [] + for bel_in in routing_graph[bel_out]: + if bel_in not in site_out: + net_name = dsp_bel_map[bel_out[1]] + '/' + bel_out[1] + '.' + dsp_pin_map[bel_out[2]] + site_routing[bel_out].append(bel_in) + net_roots[bel_out] = net_name + else: + if bel_out[2] in dsp_cell_pin: + net_name = dsp_bel_map[bel_out[1]] + '/' + dsp_cell_pin[bel_out[2]] + net_roots[bel_out] = net_name + + + if self.name[:3] == 'RAM': + if gnd_tup in routing_graph: + net_roots[gnd_tup] = constant_nets[1] + site_routing[gnd_tup] = [] + for gnd_child in routing_graph[gnd_tup]: + site_routing[gnd_tup].append(gnd_child) + + + nets = create_site_routing(self, net_roots, site_routing) + + + return nets, net_source + + +class PhysicalNetType(enum.Enum): + # Net is just a signal, not a VCC or GND tied net. + Signal = 0 + # Net is tied to GND. + Gnd = 1 + # Net is tied to VCC. + Vcc = 2 + +class PhysicalNetlist: + """ Physical Netlist class for adding each field into an object + self.add_cellplacement() + self.add_site_instance() + self.add_physical_cell() + """ + def __init__(self, part): + self.part = part + self.placements = [] + self.nets = [] + self.physCells = {} + self.siteInsts = {} + self.null_nets = [] + + def add_cellplacement(self, cellplacement): + """ Add cellplacement into a list + cellplacement (object) - object of cellplacement + """ + self.placements.append(cellplacement) + + def add_site_instance(self, site_name, site_type): + """ Add site instance to a map """ + self.siteInsts[site_name] = site_type + + def add_physical_cell(self, cell_name, cell_type): + """ Add physical cell instance + cell_name (str) - Name of physical cell instance + cell_type (str) - Value of physical_netlist.PhysCellType + PhysicalSitePip + """ + self.physCells[cell_name] = cell_type + + def add_physical_net(self, + net_name, + sources, + stubs, + stubNodes, + net_type=PhysicalNetType.Signal): + """ Adds a physical net to the physical netlist. + net_name (str) - Name of net. + sources (list of + physical_netlist.PhysicalBelPin - or - + physical_netlist.PhysicalSitePin - or - + physical_netlist.PhysicalSitePip - or - + physical_netlist.PhysicalPip + ) - Sources of this net. + stubs (list of + physical_netlist.PhysicalBelPin - or - + physical_netlist.PhysicalSitePin - or - + physical_netlist.PhysicalSitePip - or - + physical_netlist.PhysicalPip + ) - Stubs of this net. + net_type (PhysicalNetType) - Type of net. + """ + self.nets.append( + PhysicalNet( + name=net_name, type=net_type, sources=sources, stubs=stubs, stubNodes=stubNodes)) + +class CellBel(): + """ Map cell into bel, mainly for the pin mapping. + strs - strList from DeviceResources + mapping - cellBelMap from DeviceResources + """ + def __init__(self, strs, mapping): + """ Build a pin map from cell pin to bel pin. + + Now only used the common pins. + + Find a cell's pin mapping by its [site_type, bel] + + """ + self.cell = strs[mapping.cell] + self.site_types_and_bels = set() + self.common_pins = {} + self.parameter_pins = {} + self.site_bel_map = {} + + # This is for common pin mapping + for common_pins in mapping.commonPins: + pin_map = {} + + for pin in common_pins.pins: + bel_pin = strs[pin.belPin] + pin_map[bel_pin] = strs[pin.cellPin] + + for site_type_and_bels in common_pins.siteTypes: + site_type = strs[site_type_and_bels.siteType] + for bel_idx in site_type_and_bels.bels: + bel = strs[bel_idx] + self.site_types_and_bels.add((site_type, bel)) + self.common_pins[site_type, bel] = pin_map + + for parameter_pins in mapping.parameterPins: + pin_map = {} + + for pin in parameter_pins.pins: + bel_pin = strs[pin.belPin] + + pin_map[bel_pin] = strs[pin.cellPin] + + for parameter_site_type_and_bel in parameter_pins.parametersSiteTypes: + site_type = strs[parameter_site_type_and_bel.siteType] + bel = strs[parameter_site_type_and_bel.bel] + + self.site_types_and_bels.add((site_type, bel)) + + parameter = parameter_site_type_and_bel.parameter + key = strs[parameter.key] + + parameter_which = parameter.which() + if parameter_which == 'textValue': + value = strs[parameter.textValue] + elif parameter_which == 'intValue': + value = str(parameter.intValue) + elif parameter_which == 'boolValue': + value = str(parameter.boolValue) + + + self.parameter_pins[site_type, bel, key, value] = pin_map + + + +class Cell(): + def __init__(self, name, capnp_index=0, property_map={}): + """ Create a new cell. """ + self.name = name + self.property_map = property_map + self.view = "netlist" + self.capnp_index = capnp_index + self.cell_instances = {} + +class Library(): + """ Library of cells. """ + + def __init__(self, name): + self.name = name + self.cells = {} + + def add_cell(self, cell): + assert cell.name not in self.cells, cell.name + self.cells[cell.name] = cell + +class Site( + namedtuple( + 'Site', + 'tile_index tile_name_index site_index tile_type_site_type_index site_type_index alt_index site_type_name' + )): + pass + + +class SiteWire( + namedtuple('SiteWire', 'tile_index site_index site_wire_index')): + def name(self, site_type): + """ + struct SiteWire { + name @0 : StringIdx $stringRef(); + pins @1 : List(BELPinIdx) $belPinRef(); + } + """ + return site_type.site_wire_names[self.site_wire_index] + + +class SitePinNames( + namedtuple('SitePinNames', + 'tile_name site_name site_type_name pin_name wire_name')): + pass + + +class Bel(): + def __init__(self, site_type, strs, bel): + self.site_type = site_type + self.name = strs[bel.name] + self.category = bel.category + self.type = strs[bel.type] + self.bel_pins = [bel_pin for bel_pin in bel.pins] + + def yield_pins(self, site, direction=None): + for bel_pin in self.bel_pins: + bel_name, bel_pin_name = self.site_type.bel_pin_index[bel_pin] + bel_pin = self.site_type.bel_pin(site, bel_name, bel_pin_name) + + if direction and bel_pin.direction == direction: + yield bel_pin + + +class SitePip(): + """ Site pip device resource object. """ + + def __init__(self, site, in_bel_pin_index, out_bel_pin_index, + in_site_wire_index, out_site_wire_index): + self.site = site + self.in_bel_pin_index = in_bel_pin_index + self.out_bel_pin_index = out_bel_pin_index + self.in_site_wire_index = in_site_wire_index + self.out_site_wire_index = out_site_wire_index + + +class BelPin(): + """ BEL Pin device resource object. """ + + def __init__(self, site, name, bel_pin_index, site_wire_index, direction, + is_site_pin): + self.site = site + self.site_wire_index = site_wire_index + self.name = name + self.bel_pin_index = bel_pin_index + self.direction = direction + self.is_site_pin = is_site_pin + + +class SitePin(): + """ Site pin device resource object. """ + + def __init__(self, site, site_pin_index, bel_pin_index, site_wire_index, direction): + self.site = site + self.site_pin_index = site_pin_index + self.bel_pin_index = bel_pin_index + self.site_wire_index = site_wire_index + self.direction = direction + + +class Direction(enum.Enum): + input = 0 + output = 1 + inout = 2 + + +class SiteType(): + """ Object for looking up device resources from a site type. + struct SiteType { + name @0 : StringIdx $stringRef(); + belPins @1 : List(BELPin); # All BEL Pins in site type + pins @2 : List(SitePin); + lastInput @3 : UInt32; # Index of the last input pin + bels @4 : List(BEL); + sitePIPs @5 : List(SitePIP); + siteWires @6 : List(SiteWire); + altSiteTypes @7 : List(SiteTypeIdx); + } + """ + def __init__(self, strs, site_type, site_type_index): + self.site_type = strs[site_type.name] + self.site_type_index = site_type_index + + + bel_pin_index_to_site_wire_index = {} + self.site_wire_names = [] + for site_wire_index, site_wire in enumerate(site_type.siteWires): + self.site_wire_names.append(strs[site_wire.name]) + for bel_pin_index in site_wire.pins: + bel_pin_index_to_site_wire_index[ + bel_pin_index] = site_wire_index + + self.bel_pin_index = [] + self.bel_pins = {} + for bel_pin_index, bel_pin in enumerate(site_type.belPins): + bel_name = strs[bel_pin.bel] + bel_pin_name = strs[bel_pin.name] + direction = Direction[bel_pin.dir] + if bel_pin_index in bel_pin_index_to_site_wire_index: + site_wire_index = bel_pin_index_to_site_wire_index[ + bel_pin_index] + else: + site_wire_index = None + + key = (bel_name, bel_pin_name) + self.bel_pins[key] = bel_pin_index, site_wire_index, direction + self.bel_pin_index.append(key) + + self.bel_pin_to_site_pins = {} + self.site_pins = {} + for site_pin_index, site_pin in enumerate(site_type.pins): + site_pin_name = strs[site_pin.name] + bel_pin_index = site_pin.belpin + + self.bel_pin_to_site_pins[bel_pin_index] = site_pin_index + + if bel_pin_index in bel_pin_index_to_site_wire_index: + site_wire_index = bel_pin_index_to_site_wire_index[ + bel_pin_index] + else: + site_wire_index = None + + self.site_pins[site_pin_name] = (site_pin_index, bel_pin_index, + site_wire_index, + Direction[site_pin.dir]) + + self.site_pips = {} + for site_pip in site_type.sitePIPs: + out_bel_pin = site_type.belPins[site_pip.outpin] + self.site_pips[site_pip.inpin] = strs[out_bel_pin.name] + + + + self.bels = [] + for bel in site_type.bels: + self.bels.append(Bel(self, strs, bel)) + + def bel_pin(self, site, bel, pin): + """ Return BelPin device resource for BEL pin in site. + site (Site) - Site tuple + bel (str) - BEL name + pin (str) - BEL pin name + """ + bel_pin_index, site_wire_index, direction = self.bel_pins[bel, pin] + + return BelPin( + site=site, + bel_pin_index=bel_pin_index, + name=pin, + site_wire_index=site_wire_index, + direction=direction, + is_site_pin=bel_pin_index in self.bel_pin_to_site_pins, + ) + + def site_pin(self, site, device_resources, pin): + """ Return SitePin device resource for site pin in site. + site (Site) - Site tuple + pin (str) - Site pin name + """ + + site_pin_index, bel_pin_index, site_wire_index, direction = self.site_pins[ + pin] + + site_pin_names = device_resources.get_site_pin(site, site_pin_index) + + return SitePin( + site=site, + site_pin_index=site_pin_index, + bel_pin_index=bel_pin_index, + site_wire_index=site_wire_index, + direction=direction) + + def site_pip(self, site, bel, pin): + """ Return SitePip device resource for site PIP in site. + site (Site) - Site tuple + bel (str) - BEL name containing site PIP. + pin (str) - BEL pin name for specific edge. + """ + + key = bel, pin + in_bel_pin_index, in_site_wire_index, direction = self.bel_pins[key] + + out_pin = self.site_pips[in_bel_pin_index] + out_bel_pin_index, out_site_wire_index, direction = self.bel_pins[ + bel, out_pin] + + return SitePip( + site=site, + in_bel_pin_index=in_bel_pin_index, + out_bel_pin_index=out_bel_pin_index, + in_site_wire_index=in_site_wire_index, + out_site_wire_index=out_site_wire_index) + + + def site_routing_graph(self): + """ Return routing graph for every site type + routing_graph (dict) - Map of parent site routing tuple to a set of + child site routing tuples. + + key - tuple of parent belpin + value - tuples of children belpin + 3 cases here: + 1. belpin -> belpin, through sitewire + 2. belpin -> sitepip, through sitewire + 3. sitepip -> belpin, through sitepip + """ + + # from one belpin to another belpins through either sitewire or sitepip + site_routing_graph = {} + site_in = [] + site_out = [] + + sitewire_parent = {} + children_sitewire = {} + + + for key, value in self.bel_pins.items(): + bel_name, bel_pin_name = key + bel_pin_index, site_wire_index, direction = value + + if bel_pin_index in self.site_pips and bel_name[2:] != 'LUT': + in_bel_pin_index = bel_pin_index + sitepip_in = 'site_pip', bel_name, bel_pin_name + + out_pin = self.site_pips[in_bel_pin_index] + sitepip_out = 'bel_pin', bel_name, out_pin + site_routing_graph[sitepip_in] = sitepip_out + + if direction in [Direction.output, Direction.inout]: + # parent tuple + parent_tup = 'bel_pin', bel_name, bel_pin_name + site_routing_graph[parent_tup] = [] + sitewire_parent[site_wire_index] = parent_tup + if bel_name in self.site_pins: + site_in.append(parent_tup) + + else: + child_key = bel_name, bel_pin_name + children_sitewire[child_key] = site_wire_index + + + + for child_key, site_wire_index in children_sitewire.items(): + bel_name, bel_pin_name = child_key + bel_pin_index, site_wire_index, direction = self.bel_pins[child_key] + + if site_wire_index in sitewire_parent: + parent_tup = sitewire_parent[site_wire_index] + + if bel_pin_index in self.site_pips and bel_name[2:] != 'LUT': + child_tup = 'site_pip', bel_name, bel_pin_name + site_routing_graph[parent_tup].append(child_tup) + + else: + child_tup = 'bel_pin', bel_name, bel_pin_name + site_routing_graph[parent_tup].append(child_tup) + if bel_name in self.site_pins: + site_out.append(child_tup) + + + return site_routing_graph, site_in, site_out + + +class LogicalNetlist: + """ + Parse Logical Netlist file. + This is for parameter pin mapping in BRAMs. + However, all the parameter pins have the same properties and value in ISPD16 benchmarks. + So the parameter pins have been hard-coded. + For example, + para_map['DOA_REG'] = '1' + para_map['WRITE_WIDTH_A'] = '1' + para_map['WRITE_WIDTH_B'] = '72' + para_map['DOB_REG'] = '1' + """ + def __init__(self, schema_dir): + """ Read and compile logical netlist for FPGA02-12 benchmarks + + """ + import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] + # add import path from the rapidwright java.capnp + # import_path.append(os.path.join(schema_dir, '../../schema')) + import_path.append('IFsupport') + self.logical_netlist_capnp = capnp.load(os.path.join(schema_dir, 'LogicalNetlist.capnp'), imports=import_path) + + with open('FPGA02_rapidwright.netlist', 'rb') as in_f: + f_comp = gzip.GzipFile(fileobj = in_f, mode='rb') + + with self.logical_netlist_capnp.Netlist.from_bytes(f_comp.read(), traversal_limit_in_words=NO_TRAVERSAL_LIMIT, nesting_limit=NESTING_LIMIT) as message: + self.logical_netlist = message + + self.strs = [s for s in self.logical_netlist.strList] + + self.string_index = {} + for idx, s in enumerate(self.strs): + self.string_index[s] = idx + + self.cell_list = self.logical_netlist.instList + + # for parameterPin mapping + self.cell_prop_map = {} + + for cell_instance in self.cell_list: + entries = cell_instance.propMap.entries + cell_name = self.strs[cell_instance.name] + if cell_name not in self.cell_prop_map: + self.cell_prop_map[cell_name] = [] + + for parameter in entries: + key = self.strs[parameter.key] + parameter_which = parameter.which() + if parameter_which == 'textValue': + value = self.strs[parameter.textValue] + elif parameter_which == 'intValue': + value = str(parameter.intValue) + elif parameter_which == 'boolValue': + value = str(parameter.boolValue) + + self.cell_prop_map[cell_name].append((key,value)) + + + +class DeviceResources: + """DeviceResources class to parse the part's placement resources. + yield_cell_bel_mappings(self) + get_library(self) + get_macro_instance(self) + get_site_type(self) + get_packages + """ + def __init__(self, schema_dir, part_name): + """ Read and compile device resources for part assigned by part_name + + """ + import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] + # add import path from the rapidwright java.capnp + # import_path.append(os.path.join(schema_dir, '../../schema')) + import_path.append('IFsupport') + self.device_resources_capnp = capnp.load(os.path.join(schema_dir, 'DeviceResources.capnp'), imports=import_path) + + device_file = os.path.join('IFsupport', part_name) + with open(device_file + '.device', 'rb') as in_f: + f_comp = gzip.GzipFile(fileobj = in_f, mode='rb') + + with self.device_resources_capnp.Device.from_bytes(f_comp.read(), traversal_limit_in_words=NO_TRAVERSAL_LIMIT, nesting_limit=NESTING_LIMIT) as message: + self.device_resources = message + + self.strs = [s for s in self.device_resources.strList] + + self.string_index = {} + for idx, s in enumerate(self.strs): + self.string_index[s] = idx + + self.site_type_names = [] + self.site_type_name_to_index = {} + self.site_type_bels = {} + # generate site_type object + for site_type_index, site_type in enumerate(self.device_resources.siteTypeList): + site_obj = SiteType(self.strs, site_type, site_type_index) + site_type_name = self.strs[site_type.name] + self.site_type_names.append(site_type_name) + self.site_type_name_to_index[site_type_name] = site_type_index + self.site_type_bels[site_type_name] = [] + site_bels = site_obj.bels + for site_bel in site_bels: + site_bel_name = site_bel.name + self.site_type_bels[site_type_name].append(site_bel_name) + + tiletype_list = self.device_resources.tileTypeList + sitetype_list = self.device_resources.siteTypeList + + self.site_types = {} + self.site_type_map = {} + self.alt_site_type_map = {} + self.site_name_to_site = {} + self.altPinsToPrimPins = {} + + + for tile_idx, tile in enumerate(self.device_resources.tileList): + tile_name = self.strs[tile.name] + tile_name_index = self.string_index[tile_name] + + for site_idx, site in enumerate(tile.sites): + site_in_tile = self.strs[site.name] + self.site_name_to_site[site_in_tile] = {} + + tile_type_site_type_index = site.type + site_types = tiletype_list[tile.type].siteTypes + site_type_index = site_types[site.type].primaryType + site_type_name = self.strs[sitetype_list[site_type_index].name] + + self.site_type_map[site_in_tile] = site_type_name + + self.site_name_to_site[site_in_tile][site_type_name] = Site( + tile_index=tile_idx, + tile_name_index=tile_name_index, + site_index=site_idx, + tile_type_site_type_index=tile_type_site_type_index, + site_type_index=site_type_index, + alt_index=None, + site_type_name=site_type_name) + + for alt_index, alt_site_type_index in enumerate(sitetype_list[site_type_index].altSiteTypes): + site_type_name = self.site_type_names[alt_site_type_index] + self.alt_site_type_map[site_in_tile] = site_type_name + self.site_name_to_site[site_in_tile][site_type_name] = Site( + tile_index=tile_idx, + tile_name_index=tile_name_index, + site_index=site_idx, + tile_type_site_type_index=tile_type_site_type_index, + site_type_index=alt_site_type_index, + alt_index=alt_index, + site_type_name=site_type_name) + + # self.tile_types = {} + self.tile_wire_index_to_node_index = None + + + def get_site_type_index(self, site_type_name): + return self.site_type_name_to_index[site_type_name] + + def get_site_type(self, site_type_index): + """ Get SiteType object for specified site type index. """ + if site_type_index not in self.site_types: + self.site_types[site_type_index] = SiteType( + self.strs, + self.device_resources.siteTypeList[site_type_index], + site_type_index) + + return self.site_types[site_type_index] + + + def get_tile_name_at_site_name(self, site_name): + """ Get Tile name at site name. """ + + sites_dict = self.site_name_to_site[site_name] + + # Get the first site in the dict. Assume all alternative sites are at + # the same tile + site = list(sites_dict.values())[0] + return self.strs[site.tile_name_index] + + def bel_pin(self, site_name, site_type, bel, pin): + """ Return BelPin device resource for BEL pin in site. + site_name (str) - Name of site + site_type (str) - Name of specific site type being queried. + bel (str) - BEL name containing site PIP. + pin (str) - BEL pin name for specific edge. + """ + site = self.site_name_to_site[site_name][site_type] + return self.get_site_type(site.site_type_index).bel_pin(site, bel, pin) + + def site_pin(self, site_name, site_type, pin): + """ Return SitePin device resource for site pin in site. + site_name (str) - Name of site + site_type (str) - Name of specific site type being queried. + pin (str) - Site pin name + """ + site = self.site_name_to_site[site_name][site_type] + return self.get_site_type(site.site_type_index).site_pin( + site, self, pin) + + def site_pip(self, site_name, site_type, bel, pin): + """ Return SitePip device resource for site PIP in site. + site_name (str) - Name of site + site_type (str) - Name of specific site type being queried. + bel (str) - BEL name containing site PIP. + pin (str) - BEL pin name for specific edge. + """ + site = self.site_name_to_site[site_name][site_type] + return self.get_site_type(site.site_type_index).site_pip( + site, bel, pin) + + + def get_site_pin(self, site, site_pin_index): + """ Get SitePinNames for specified site pin. + site (Site) - Site tuple + site_pin_index (int) - Index into SiteType.pins list. + Site pin to tile relationships are estabilished through the site type + in tile type data. + If the site tuple indicates this is a primary site type, then the + tile wire can be returned directly. + If the site tuple indicates this is an alternate site type, then the + tile wire is found by first mapping the site pin from the alternate + site type to the primary site type. At that point, the tile wire can + be found. + """ + tile = self.device_resources.tileList[site.tile_index] + tile_type_index = tile.type + tile_type = self.device_resources.tileTypeList[tile_type_index] + site_type_in_tile_type = tile_type.siteTypes[site. + tile_type_site_type_index] + if site.alt_index is None: + # This site type is the primary site type, return the tile wire + # directly. + site_type = self.device_resources.siteTypeList[ + site_type_in_tile_type.primaryType] + site_type_name = self.strs[site_type.name] + pin_name = self.strs[site_type.pins[site_pin_index].name] + wire_name = self.strs[site_type_in_tile_type. + primaryPinsToTileWires[site_pin_index]] + else: + # This site type is an alternate site type. + prim_site_type = self.device_resources.siteTypeList[ + site_type_in_tile_type.primaryType] + site_type = self.device_resources.siteTypeList[ + prim_site_type.altSiteTypes[site.alt_index]] + site_type_name = self.strs[site_type.name] + pin_name = self.strs[site_type.pins[site_pin_index].name] + + # First translate the site_pin_index from the alternate site type + # To the primary site type pin index. + prim_site_pin_index = site_type_in_tile_type.altPinsToPrimaryPins[ + site.alt_index].pins[site_pin_index] + prim_site_pin_name = self.strs[site_type.pins[prim_site_pin_index].name] + self.altPinsToPrimPins[site_type_name][pin_name] = prim_site_pin_name + # Then lookup the tile wire using the primary site pin index. + wire_name = self.strs[site_type_in_tile_type. + primaryPinsToTileWires[prim_site_pin_index]] + + return SitePinNames( + tile_name=self.strs[tile.name], + site_name=self.strs[tile.sites[site.site_index].name], + site_type_name=site_type_name, + pin_name=pin_name, + wire_name=wire_name) + + + def yield_cell_bel_mappings(self): + """ yield cell bel mapping """ + for cell_bel_mapping in self.device_resources.cellBelMap: + yield CellBel(self.strs, cell_bel_mapping) + + def get_library(self): + """Build library for primitives and macros from device resources + Didn't fully parse the property map. + """ + netlist = self.device_resources.primLibs + + libraries = {} + for cell_capnp in netlist.cellList: + cell_decl = netlist.cellDecls[cell_capnp.index] + prop_map = {} + for prop in cell_decl.propMap.entries: + key = self.strs[prop.key] + if prop.which() == 'textValue': + value = self.strs[prop.textValue] + elif prop.which() == 'intValue': + value = prop.intValue + else: + assert prop.which() == 'boolValue' + value = prop.boolValue + prop_map[key] = value + cell = Cell( + name=self.strs[cell_decl.name], + capnp_index=cell_capnp.index, + property_map=prop_map, + ) + cell.view = self.strs[cell_decl.view] + for inst in cell_capnp.insts: + # struct CellInstance { + # name @0 : StringIdx $stringRef(); + # propMap @1 : PropertyMap; + # view @2 : StringIdx $stringRef(); + # cell @3 : CellIdx $cellRef(); + # } + cell_instance_name = self.strs[netlist.instList[inst].name] + cell_name = self.strs[netlist.cellDecls[netlist.instList[inst].cell].name] + cell.cell_instances[cell_instance_name] = (cell_instance_name, cell_name) + + library = self.strs[cell_decl.lib] + if library not in libraries: + libraries[library] = Library(name=library) + libraries[library].add_cell(cell) + return libraries + + def get_macro_instance(self): + """ Get macros from device resources + macro_inst - build a map for macros and their instances. + One macro consists of more than one primitives. + """ + macro_lib = self.get_library()['macros'] + macro_inst = {} + + for cell_name, cell in sorted( + macro_lib.cells.items(), key=lambda x: x[0]): + macro_name = cell_name + macro_inst[macro_name] = {} + + for inst_name, inst in sorted( + cell.cell_instances.items(), key=lambda x: x[0]): + macro_inst[macro_name][inst_name] = inst[1] + + return macro_inst + + def get_packages(self): + """ Get the device package for debugging. """ + package_list = self.device_resources.packages + + for package in package_list: + package_name = self.strs[package.name] + + +class db_to_physicalnetlist(): + def __init__(self, placedb, schema_dir, part_name): + self.part = part_name + self.schema_dir = schema_dir + self.sitemap = {} + # self.SLICE = {} + self.DSP = [] + self.BRAM = [] + self.IO = [] + bram_y_num = placedb. num_sites_y / 5 + dsp_y_num = placedb. num_sites_y / 2.5 + + + slice_index_x = 0 + # initialize sitemap + for i in range(placedb.num_sites_x): + slice_flag = False + # slice_index_y = 0 + for j in range(placedb.num_sites_y): + # LUT/FF + if placedb.site_type_map[i, j] == 1: + slice_flag = True + for k in range(0, 16): + self.sitemap[i, j, k] = "SLICE_X" + str(slice_index_x) + "Y" + str(j) + # slice_index_y += 1 + # DSP + elif placedb.site_type_map[i, j] == 2: + self.DSP.append((i, j)) + idx = len(self.DSP) - 1 + index_x = int(idx / dsp_y_num) + index_y = int(idx - index_x * dsp_y_num) + self.sitemap[i, j, 0] = "DSP48E2_X" + str(index_x) + "Y" + str(index_y) + # BRAM + elif placedb.site_type_map[i, j] == 3: + self.BRAM.append((i, j)) + idx = len(self.BRAM) - 1 + index_x = int(idx / bram_y_num) + index_y = int(idx - index_x * bram_y_num) + self.sitemap[i, j, 0] = "RAMB36_X" + str(index_x) + "Y" + str(index_y) + # IO + elif placedb.site_type_map[i, j] == 4: + self.IO.append((i, j)) + + if slice_flag: + slice_index_x += 1 + + # if slice_index_x == 90: + # print(i) + + IOB_col = [] + BUFGCE_col = [] + for loc in self.IO: + x, y = loc + if x != 0 and x != placedb.num_sites_x - 1: + if x not in IOB_col and x not in BUFGCE_col: + IOB_col.append(x) + BUFGCE_col.append(x+1) + + + self.Site_LUTs = {} + self.shared_LUT = [] + + # map from node name to cellplacement obj + self.node_placement = {} + # map from port name to cellplacement obj + self.port_placement = {} + + # map from site name to SiteInst object + self.site_instances = {} + # map from node name to site name + self.node_site_map = {} + + self.device_resource = DeviceResources(self.schema_dir, part_name) + + + + sitetypes_prefix = ['SLICEM', 'SLICEL', 'HPIOB', 'HRIO', 'BUFGCE', 'RAMB36', 'DSP48E2'] + sitetypes = [] + for site_type_name in self.device_resource.site_type_names: + for prefix in sitetypes_prefix: + if prefix in site_type_name: + sitetypes.append(site_type_name) + + # print(sitetypes) + + self.routing_graphs = {} + self.site_in = {} + self.site_out = {} + self.dsp_bel_pins = {} + + + for sitetype in sitetypes: + site_index = self.device_resource.get_site_type_index(sitetype) + sitetype_obj = self.device_resource.get_site_type(site_index) + routing_graph, site_in, site_out = sitetype_obj.site_routing_graph() + self.routing_graphs[sitetype] = routing_graph + self.site_in[sitetype] = site_in + self.site_out[sitetype] = site_out + if sitetype == 'DSP48E2': + for key in sitetype_obj.bel_pins: + bel_name, bel_pin_name = key + if bel_name not in self.dsp_bel_pins: + self.dsp_bel_pins[bel_name] = [] + self.dsp_bel_pins[bel_name].append(bel_pin_name) + + + def Map_bel(self, node_z, node_type): + if node_type[:3] == "LUT": + switcher = { + 0: "A5LUT", + 1: "A6LUT", + 2: "B5LUT", + 3: "B6LUT", + 4: "C5LUT", + 5: "C6LUT", + 6: "D5LUT", + 7: "D6LUT", + 8: "E5LUT", + 9: "E6LUT", + 10:"F5LUT", + 11:"F6LUT", + 12:"G5LUT", + 13:"G6LUT", + 14:"H5LUT", + 15:"H6LUT", + } + return switcher[node_z] + elif node_type[:4] == "FDRE": + switcher = { + 0: "AFF", + 1: "AFF2", + 2: "BFF", + 3: "BFF2", + 4: "CFF", + 5: "CFF2", + 6: "DFF", + 7: "DFF2", + 8: "EFF", + 9: "EFF2", + 10:"FFF", + 11:"FFF2", + 12:"GFF", + 13:"GFF2", + 14:"HFF", + 15:"HFF2", + } + return switcher[node_z] + elif node_type[:4] == "BUFG": + return "BUFCE" + elif node_type[:4] == "OBUF": + return "OUTBUF" + elif node_type[:3] == "RAM": + return "RAMB36E2" + else: + return "None" + + + def prevent_pin_overlap(self, placedb, phys_netlist): + """ For the cell bel pin mapping, especially for LUT packing + + The shared inputs of 5LUT and 6LUT must be connected to the same nets. + In addition, when 5LUT and 6LUT are both used, A6 pin must be connected to VCC. + The higher the pin number is, the less the delay. + e.g: A5 has higher pin number than A4, etc + + My pin mapping scheme: I map the shared inputs first and start from higher pin number, + and then move to the not shared ones. + + This is probably different from how vivado copes with pin mapping! + + """ + + bel_pins = ['A5', 'A4', 'A3', 'A2', 'A1'] + + for site in self.Site_LUTs: + LUT_map = { + 'A': [], + 'B': [], + 'C': [], + 'D': [], + 'E': [], + 'F': [], + 'G': [], + 'H': [], + } + for lut_placement in self.Site_LUTs[site]: + if lut_placement.bel_name[:1] == 'A': + LUT_map['A'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'B': + LUT_map['B'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'C': + LUT_map['C'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'D': + LUT_map['D'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'E': + LUT_map['E'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'F': + LUT_map['F'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'G': + LUT_map['G'].append(lut_placement) + elif lut_placement.bel_name[:1] == 'H': + LUT_map['H'].append(lut_placement) + else: + continue + + sharedluts_insite = [] + + for key in LUT_map: + if len(LUT_map[key]) == 2: + self.shared_LUT.append((LUT_map[key][0], LUT_map[key][1])) + sharedluts_insite.append((LUT_map[key][0], LUT_map[key][1])) + + #single 5LUT detection + elif len(LUT_map[key]) == 1 and LUT_map[key][0].bel_name.endswith('5LUT'): + LUT_map[key][0].bel_name = LUT_map[key][0].bel_name[:1] + '6LUT' + + # build new pinmap + new_pinmap = [] + for pin in LUT_map[key][0].pins: + if pin[0][:1] == 'O': + new_pin = (pin[0], 'O6') + elif pin[0][:1] == 'I': + new_pin = (pin[0], pin[2]) + new_pinmap.append(new_pin) + + LUT_map[key][0].pins.clear() + + for new_pin in new_pinmap: + LUT_map[key][0].add_pins(new_pin[0], new_pin[1]) + + else: + continue + + for lut_pair in sharedluts_insite: + lut_name_0 = lut_pair[0].cell_name + node_id_0 = placedb.node_name2id_map[lut_name_0] + + lut_name_1 = lut_pair[1].cell_name + node_id_1 = placedb.node_name2id_map[lut_name_1] + + pin2net_0 = {} + + for pin_id in placedb.node2pin_map[node_id_0]: + pin2net_0[placedb.pin_names[pin_id]] = placedb.pin2net_map[pin_id] + + pin2net_1 = {} + for pin_id in placedb.node2pin_map[node_id_1]: + pin2net_1[placedb.pin_names[pin_id]] = placedb.pin2net_map[pin_id] + + error_cnt = 0 + for pin_0 in lut_pair[0].pins: + cellpin_0 = pin_0[0] + belpin_0 = pin_0[2] + if belpin_0 == 'A6': + error_cnt += 1 + break + + for pin_1 in lut_pair[1].pins: + cellpin_1 = pin_1[0] + belpin_1 = pin_1[2] + if belpin_1 == 'A6': + error_cnt += 1 + break + + if belpin_0 == belpin_1: + if cellpin_0 in pin2net_0 and cellpin_1 in pin2net_1: + if pin2net_0[cellpin_0] == pin2net_1[cellpin_1]: + pass + else: + error_cnt += 1 + break + + + # fix the wrong mapping + + if error_cnt != 0: + + shared_pins_0 = [] + shared_pins_1 = [] + un_shared_pins_0 = [] + un_shared_pins_1 = [] + input_pins_0 = [] + output_pin_0 = None + input_pins_1 = [] + output_pin_1 = None + + for pin_0 in lut_pair[0].pins: + cellpin_0 = pin_0[0] + if cellpin_0[:1] == 'I': + input_pins_0.append(cellpin_0) + else: + output_pin_0 = pin_0 + + for pin_1 in lut_pair[1].pins: + cellpin_1 = pin_1[0] + if cellpin_1[:1] == 'I': + input_pins_1.append(cellpin_1) + else: + output_pin_1 = pin_1 + + lut_pair[0].pins.clear() + lut_pair[1].pins.clear() + + + for cellpin_0 in input_pins_0: + if cellpin_0 not in shared_pins_0: + for cellpin_1 in input_pins_1: + if cellpin_1 not in shared_pins_1: + if cellpin_0 in pin2net_0 and cellpin_1 in pin2net_1: + if pin2net_0[cellpin_0] == pin2net_1[cellpin_1]: + net_name = placedb.net_names[pin2net_0[cellpin_0]] + shared_pins_0.append(cellpin_0) + shared_pins_1.append(cellpin_1) + break + + + for cellpin_0 in input_pins_0: + if cellpin_0 not in shared_pins_0: + for cellpin_1 in input_pins_1: + if cellpin_1 not in shared_pins_1: + if cellpin_0 in pin2net_0 and cellpin_1 not in pin2net_1: + shared_pins_0.append(cellpin_0) + shared_pins_1.append(cellpin_1) + + elif cellpin_0 not in pin2net_0 and cellpin_1 in pin2net_1: + shared_pins_0.append(cellpin_0) + shared_pins_1.append(cellpin_1) + + + for cellpin_0 in input_pins_0: + if cellpin_0 not in shared_pins_0: + for cellpin_1 in input_pins_1: + if cellpin_1 not in shared_pins_1: + if cellpin_0 not in pin2net_0 and cellpin_1 not in pin2net_1: + shared_pins_0.append(cellpin_0) + shared_pins_1.append(cellpin_1) + + + # self.pins.append((cellpin, self.bel_name, belpin)) + for idx, pin in enumerate(shared_pins_0): + lut_pair[0].add_pins(shared_pins_0[idx], bel_pins[idx]) + lut_pair[1].add_pins(shared_pins_1[idx], bel_pins[idx]) + + + for pin_0 in input_pins_0: + if pin_0 not in shared_pins_0: + un_shared_pins_0.append(pin_0) + for pin_1 in input_pins_1: + if pin_1 not in shared_pins_1: + un_shared_pins_1.append(pin_1) + + for idx, un_shared_pin in enumerate(un_shared_pins_0): + lut_pair[0].add_pins(un_shared_pin, bel_pins[idx + len(shared_pins_0)]) + + for idx, un_shared_pin in enumerate(un_shared_pins_1): + lut_pair[1].add_pins(un_shared_pin, bel_pins[idx + len(un_shared_pins_0) + len(shared_pins_1)]) + + + lut_pair[0].add_pins(output_pin_0[0], output_pin_0[2]) + lut_pair[1].add_pins(output_pin_1[0], output_pin_1[2]) + + + def stitch_routing(self, placedb, phys_netlist): + """ + Do intra-site routing through site_routing graph. + call function site_router(), this is the clean way to do intra-site routing for each site. + """ + + nets = {} + net_source = {} + io_nets = {} + vcc_nets = [] + gnd_nets = [] + constant_nets = { + 0 : 'GLOBAL_LOGIC0', + 1 : 'GLOBAL_LOGIC1' + } + + ram_pin_pair = { + 'ADDRENAL' : 'ADDRENAU', + 'ADDRENBL' : 'ADDRENBU', + 'CLKARDCLKL' : 'CLKARDCLKU', + 'CLKBWRCLKL' : 'CLKBWRCLKU', + 'ECCPIPECEL': 'ECCPIPECEU', + 'ENARDENL' : 'ENARDENU', + 'ENBWRENL' : 'ENBWRENU', + 'REGCEAREGCEL' : 'REGCEAREGCEU', + 'REGCEBL' : 'REGCEBU', + 'REGCLKARDRCLKL' : 'REGCLKARDRCLKU', + 'REGCLKBL' : 'REGCLKBU', + 'RSTRAMARSTRAML' : 'RSTRAMARSTRAMU', + 'RSTRAMBL' : 'RSTRAMBU', + 'RSTREGARSTREGL' : 'RSTREGARSTREGU', + 'RSTREGBL' : 'RSTREGBU', + 'SLEEPL' : 'SLEEPU' + } + + + # sort nets + for net_id in range(len(placedb.net2pin_map)): + net_name = placedb.net_names[net_id] + sources = [] + stubs = [] + + for pin_id in placedb.net2pin_map[net_id]: + pin_name = placedb.pin_names[pin_id] + node_name = placedb.node_names[placedb.pin2node_map[pin_id]] + node_id = placedb.node_name2id_map[node_name] + node_type = placedb.node_types[node_id] + + # ignore the pseudo VCC, GND nodes + if node_type == 'LUT0': + if node_name == 'VCC': + vcc_nets.append(net_name) + else: + gnd_nets.append(net_name) + + continue + + site_name = self.node_site_map[node_name] + + + # net_roots + site_obj = self.site_instances[site_name] + cells = site_obj.cells + + # This pin is somehow not to be mapped + if pin_name == 'RSTREGB': + continue + + if node_type[:3] == 'DSP' and pin_name[0] == 'D': + pin_name = pin_name.replace('D', 'DIN') + + # primary cells + if len(cells[node_name]) == 1: + cell_placement = cells[node_name][0] + bel_name = cell_placement.bel_name + + for pin in cell_placement.pins: + if pin[0] == pin_name: + bel_pin = pin[2] + belpin_tup = 'bel_pin', bel_name, bel_pin + site_obj.add_belpins(net_name, belpin_tup) + if bel_pin in ram_pin_pair: + belup_tup = 'bel_pin', bel_name, ram_pin_pair[bel_pin] + site_obj.add_belpins(net_name, belup_tup) + + break + + else: + for cell in cells[node_name]: + cell_placement = cell + bel_name = cell_placement.bel_name + + belpin_tup = None + for pin in cell_placement.pins: + if pin[0] == pin_name: + bel_pin = pin[2] + belpin_tup = 'bel_pin', bel_name, bel_pin + break + elif bel_name.endswith('LUT') and pin_name.startswith('O'): + belpin_tup = 'bel_pin', bel_name, pin_name + + if bel_name == 'INBUF': + ctrl_tup = 'bel_pin', 'IBUFCTRL', 'I' + out_net = node_name + '/OUT' + site_obj.add_belpins(out_net, ctrl_tup) + site_obj.add_belpins(out_net, belpin_tup) + + elif bel_name == 'IBUFCTRL': + o_net = node_name + '/O' + io_nets[net_name] = o_net + site_obj.add_belpins(o_net, belpin_tup) + + + elif belpin_tup != None: + site_obj.add_belpins(net_name, belpin_tup) + + + # site router + for site_name, site_obj in self.site_instances.items(): + site_type = phys_netlist.siteInsts[site_name] + site_nets, site_net_source = site_obj.site_router(self.routing_graphs[site_type], self.site_in[site_type], self.site_out[site_type]) + + for net_name, root_list in site_nets.items(): + if net_name in io_nets: + net_name = io_nets[net_name] + if net_name not in nets: + nets[net_name] = root_list + else: + for root in root_list: + nets[net_name].append(root) + + for net_name, source in site_net_source.items(): + if net_name not in net_source: + net_source[net_name] = source + + + vcc_stubs = [] + gnd_stubs = [] + # Build physical nets for normal nets + # Extend the stub lists for vcc and gnd nets + for net_name, root_list in nets.items(): + sources = [] + stubs = [] + + for root in root_list: + if net_name in net_source and root.bel_name == net_source[net_name][1]: + sources.append(root) + else: + stubs.append(root) + + if net_name == constant_nets[0] or net_name in gnd_nets: + gnd_stubs.extend(stubs) + + elif net_name == constant_nets[1] or net_name in vcc_nets: + vcc_stubs.extend(stubs) + + else: + phys_netlist.add_physical_net(net_name=net_name, + sources=sources, + stubs=stubs, + stubNodes=[], + net_type=PhysicalNetType.Signal) + + # Finally build physical nets for vcc and gnd + # For gnd and vcc nets, they don't have sources + phys_netlist.add_physical_net(net_name=constant_nets[0], + sources=[], + stubs=gnd_stubs, + stubNodes=[], + net_type=PhysicalNetType.Gnd) + + phys_netlist.add_physical_net(net_name=constant_nets[1], + sources=[], + stubs=vcc_stubs, + stubNodes=[], + net_type=PhysicalNetType.Vcc) + + + + def build_physicalnetlist(self, placedb, out_file): + phys_netlist = PhysicalNetlist(self.part) + + # cell_prop_map = self.logical_netlist.cell_prop_map + + mappings = self.device_resource.yield_cell_bel_mappings() + site_type_map = self.device_resource.site_type_map + alt_site_type_map = self.device_resource.alt_site_type_map + site_type_bels = self.device_resource.site_type_bels + macro_inst = self.device_resource.get_macro_instance() + + pinmap = {} + parameter_pinmap = {} + for mapping in mappings: + cell = mapping.cell + pinmap[cell] = mapping.common_pins + parameter_pinmap[cell] = mapping.parameter_pins + + with open(out_file, 'r') as fin: + for line in fin: + node_name, x, y, z = line.split() + x = int(x) + y = int(y) + z = int(z) + node_id = placedb.node_name2id_map[node_name] + node_type = placedb.node_types[node_id] + + # ignore the pseudo VCC, GND nodes + if node_type == 'LUT0': + continue + + self.node_placement[node_name] = [] + + if node_type in macro_inst: + LUT6_2_flag = False + for inst in macro_inst[node_type]: + cell_type = macro_inst[node_type][inst] + cell_name = node_name + "/" + inst + site_name = self.sitemap[x, y, z] + + if site_type_map[site_name][:5] == 'SLICE' and LUT6_2_flag == False: + bel_name = self.Map_bel(z-1, node_type) + LUT6_2_flag = True + elif LUT6_2_flag == True: + bel_name = self.Map_bel(z, node_type) + else: + bel_name = cell_type + + self.node_site_map[node_name] = site_name + + # build siteinst obj + if site_name not in self.site_instances: + site_instance = SiteInst(site_name) + self.site_instances[site_name] = site_instance + + + # For site instances + if site_name not in phys_netlist.siteInsts: + site_type = site_type_map[site_name] + phys_netlist.add_site_instance(site_name, site_type) + + self.site_instances[site_name] = SiteInst(site_name) + + else: + site_type = phys_netlist.siteInsts[site_name] + + # add cell instance + cellplacement = Cellplacement(cell_name, cell_type, site_name, bel_name) + self.node_placement[node_name].append(cellplacement) + + # add pins for cell instance + for key, value in pinmap[cell_type][site_type, bel_name].items(): + belpin = key + cellpin = value + if cellpin == "GND": + continue + elif node_type == 'DSP48E2': + if belpin in self.dsp_bel_pins[bel_name]: + cellplacement.add_pins(cellpin, belpin) + else: + break + else: + cellplacement.add_pins(cellpin, belpin) + phys_netlist.add_cellplacement(cellplacement) + + self.site_instances[site_name].add_cells(node_name, cellplacement) + + # primary insts + else: + cell_type = node_type + cell_name = node_name + site_name = self.sitemap[x, y, z] + bel_name = self.Map_bel(z, node_type) + self.node_site_map[node_name] = site_name + + if site_name not in self.site_instances: + site_instance = SiteInst(site_name) + self.site_instances[site_name] = site_instance + + + # For site instances + if site_name not in phys_netlist.siteInsts: + if cell_type == "RAMB36E2": + site_type = alt_site_type_map[site_name] + else: + site_type = site_type_map[site_name] + phys_netlist.add_site_instance(site_name, site_type) + else: + site_type = phys_netlist.siteInsts[site_name] + + + # add cell instance + cellplacement = Cellplacement(cell_name, cell_type, site_name, bel_name) + self.node_placement[node_name].append(cellplacement) + self.site_instances[site_name].add_cells(node_name, cellplacement) + + # add pins for cell instance + for key, value in pinmap[cell_type][site_type, bel_name].items(): + belpin = key + cellpin = value + if cellpin == "GND": + continue + else: + cellplacement.add_pins(cellpin, belpin) + + + if cell_type == 'RAMB36E2': + para_belcell = {} + para_map = {} + para_map['DOA_REG'] = '1' + para_map['WRITE_WIDTH_A'] = '1' + para_map['WRITE_WIDTH_B'] = '72' + para_map['DOB_REG'] = '1' + + + for prop_key, prop_value in para_map.items(): + for key, value in parameter_pinmap[cell_type][site_type, bel_name, prop_key, prop_value].items(): + belpin = key + cellpin = value + + # hardcode this need to find a clean way + if belpin == 'DINBDIN1': + cellpin = 'DINBDIN[1]' + + if cellpin == "GND" or cellpin == "VCC": + continue + elif belpin not in para_belcell: + para_belcell[belpin] = cellpin + cellplacement.add_pins(cellpin, belpin) + + + phys_netlist.add_cellplacement(cellplacement) + + if site_name[:5] == 'SLICE': + if site_name not in self.Site_LUTs: + in_site_luts = [] + if cell_type[:3] == 'LUT': + in_site_luts.append(cellplacement) + self.Site_LUTs[site_name] = in_site_luts + else: + if cell_type[:3] == 'LUT': + self.Site_LUTs[site_name].append(cellplacement) + + + self.prevent_pin_overlap(placedb, phys_netlist) + + self.stitch_routing(placedb, phys_netlist) + + return phys_netlist + + +class tcl_generator(): + """ generate a tcl script for the golden reference IF file + + file_name - "place_cells.tcl" + place cells one by one by using vivado tcl command place_cell; + place each cell to its corresponding site. + + """ + def __init__(self): + self.file_name = 'place_cells.tcl' + + def write_tcl(self, phys_netlist): + """ write out the tcl script """ + with open(self.file_name, 'w') as tcl_file: + for placement in phys_netlist.placements: + if placement.cell_name.endswith('/LUT5'): + continue + elif placement.cell_name.endswith('/LUT6'): + placement.cell_name = placement.cell_name[:-5] + line = 'place_cell ' + placement.cell_name + ' ' + placement.site_name + '/' + placement.bel_name + tcl_file.write(line + os.linesep) + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py new file mode 100644 index 00000000..a5c9c353 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py @@ -0,0 +1,152 @@ +## +# @file NesterovAcceleratedGradientOptimizer.py +# @author Yibo Lin (DREAMPlace) +# @date Aug 2018 +# @brief Nesterov's accelerated gradient method proposed by e-place. +# + +import os +import sys +import time +import pickle +import numpy as np +import torch +from torch.optim.optimizer import Optimizer, required +import torch.nn as nn +import pdb + +class NesterovAcceleratedGradientOptimizer(Optimizer): + """ + @brief Follow the Nesterov's implementation of e-place algorithm 2 + http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf + """ + def __init__(self, params, lr=required, obj_and_grad_fn=required, constraint_fn=None): + """ + @brief initialization + @param params variable to optimize + @param lr learning rate + @param obj_and_grad_fn a callable function to get objective and gradient + @param constraint_fn a callable function to force variables to satisfy all the constraints + """ + if lr is not required and lr < 0.0: + raise ValueError("Invalid learning rate: {}".format(lr)) + + # u_k is major solution + # v_k is reference solution + # obj_k is the objective at v_k + # a_k is optimization parameter + # alpha_k is the step size + # v_k_1 is previous reference solution + # g_k_1 is gradient to v_k_1 + # obj_k_1 is the objective at v_k_1 + defaults = dict(lr=lr, + u_k=[], v_k=[], g_k=[], obj_k=[], a_k=[], alpha_k=[], + v_k_1=[], g_k_1=[], obj_k_1=[], + v_kp1 = [None], + obj_eval_count=0) + super(NesterovAcceleratedGradientOptimizer, self).__init__(params, defaults) + self.obj_and_grad_fn = obj_and_grad_fn + self.constraint_fn = constraint_fn + + # I do not know how to get generator's length + if len(self.param_groups) != 1: + raise ValueError("Only parameters with single tensor is supported") + + def __setstate__(self, state): + super(NesterovAcceleratedGradientOptimizer, self).__setstate__(state) + + def step(self, closure=None): + """ + @brief Performs a single optimization step. + @param closure A callable closure function that reevaluates the model and returns the loss. + """ + loss = None + if closure is not None: + loss = closure() + + for group in self.param_groups: + obj_and_grad_fn = self.obj_and_grad_fn + constraint_fn = self.constraint_fn + for i, prm in enumerate(group['params']): + if prm.grad is None: + continue + if not group['u_k']: + group['u_k'].append(prm.data.clone()) + # directly use prm as v_k to save memory + #group['v_k'].append(torch.autograd.Variable(prm.data, requires_grad=True)) + group['v_k'].append(prm) + obj, grad = obj_and_grad_fn(group['v_k'][i]) + group['g_k'].append(grad.data.clone()) # must clone + group['obj_k'].append(obj.data.clone()) + u_k = group['u_k'][i] + v_k = group['v_k'][i] + g_k = group['g_k'][i] + obj_k = group['obj_k'][i] + if not group['a_k']: + group['a_k'].append(torch.ones(1, dtype=g_k.dtype, device=g_k.device)) + group['v_k_1'].append(torch.autograd.Variable(torch.zeros_like(v_k), requires_grad=True)) + group['v_k_1'][i].data.copy_(group['v_k'][i]-group['lr']*g_k) + obj, grad = obj_and_grad_fn(group['v_k_1'][i]) + group['g_k_1'].append(grad.data) + group['obj_k_1'].append(obj.data.clone()) + a_k = group['a_k'][i] + v_k_1 = group['v_k_1'][i] + g_k_1 = group['g_k_1'][i] + obj_k_1 = group['obj_k_1'][i] + if not group['alpha_k']: + group['alpha_k'].append((v_k-v_k_1).norm(p=2) / (g_k-g_k_1).norm(p=2)) + alpha_k = group['alpha_k'][i] + + if group['v_kp1'][i] is None: + group['v_kp1'][i] = torch.autograd.Variable(torch.zeros_like(v_k), requires_grad=True) + v_kp1 = group['v_kp1'][i] + + # line search with alpha_k as hint + a_kp1 = (1 + (4*a_k.pow(2)+1).sqrt()) / 2 + coef = (a_k-1) / a_kp1 + alpha_kp1 = 0 + backtrack_cnt = 0 + max_backtrack_cnt = 10 + + while True: + #with torch.autograd.profiler.profile(use_cuda=True) as prof: + u_kp1 = v_k - alpha_k*g_k + #constraint_fn(u_kp1) + v_kp1.data.copy_(u_kp1 + coef*(u_kp1-u_k)) + # make sure v_kp1 subjects to constraints + # g_kp1 must correspond to v_kp1 + constraint_fn(v_kp1) + + f_kp1, g_kp1 = obj_and_grad_fn(v_kp1) + + #tt = time.time() + alpha_kp1 = torch.sqrt(torch.sum((v_kp1.data-v_k.data)**2) / torch.sum((g_kp1.data-g_k.data)**2)) + #alpha_kp1 = torch.dist(v_kp1.data, v_k.data, p=2) / torch.dist(g_kp1.data, g_k.data, p=2) + backtrack_cnt += 1 + group['obj_eval_count'] += 1 + + #logging.debug("|g_k| = %.6E, |g_kp1| = %.6E" % (g_k.norm(p=2), g_kp1.norm(p=2))) + if alpha_kp1 > 0.95*alpha_k or backtrack_cnt >= max_backtrack_cnt: + alpha_k.data.copy_(alpha_kp1.data) + break + else: + alpha_k.data.copy_(alpha_kp1.data) + + v_k_1.data.copy_(v_k.data) + g_k_1.data.copy_(g_k.data) + obj_k_1.data.copy_(obj_k.data) + + u_k.data.copy_(u_kp1.data) + v_k.data.copy_(v_kp1.data) + g_k.data.copy_(g_kp1.data) + obj_k.data.copy_(f_kp1.data) + a_k.data.copy_(a_kp1.data) + + # although the solution should be u_k + # we need the gradient of v_k + # the update of density weight also requires v_k + # I do not know how to copy u_k back to prm when exit yet + #prm.data.copy_(v_k.data) + + return loss + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py new file mode 100644 index 00000000..afeb6f8a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py @@ -0,0 +1,1044 @@ +## +# @file NonLinearPlace.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief Nonlinear placement engine to be called with parameters and placement database +# + +import os +import sys +import time +import pickle +import numpy as np +import logging +import torch +import gzip +import copy +import matplotlib.pyplot as plt +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle +from BasicPlace import * +from PlaceObj import * +from EvalMetrics import * +import NesterovAcceleratedGradientOptimizer +import pdb +import dreamplacefpga.ops.dsp_ram_legalization.dsp_ram_legalization as dsp_ram_legalization + +class NonLinearPlaceFPGA (BasicPlaceFPGA): + """ + @brief Nonlinear placement engine. + It takes parameters and placement database and runs placement flow. + """ + def __init__(self, params, placedb): + """ + @brief initialization. + @param params parameters + @param placedb placement database + """ + super(NonLinearPlaceFPGA, self).__init__(params, placedb) + + def __call__(self, params, placedb): + """ + @brief Top API to solve placement. + @param params parameters + @param placedb placement database + """ + iteration = 0 + blockLegalIter = 0 + all_metrics = [] + + # global placement + if params.global_place_flag: + # global placement may run in multiple stages according to user specification + for global_place_params in params.global_place_stages: + + # we formulate each stage as a 3-nested optimization problem + # f_gamma(g_density(h(x) ; density weight) ; gamma) + # Lgamma Llambda Lsub + # When optimizing an inner problem, the outer parameters are fixed. + # This is a generalization to the eplace/RePlAce approach + + # As global placement may easily diverge, we record the position of best overflow + best_metric = [None] + best_pos = [None] + + if params.gpu: + torch.cuda.synchronize() + tt = time.time() + # construct model and optimizer + density_weight = 0.0 + # construct placement model + model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) + #print("Model constructed in %g ms"%((time.time()-tt)*1000)) + + optimizer_name = global_place_params["optimizer"] + + # determine optimizer + if optimizer_name.lower() == "adam": + optimizer = torch.optim.Adam(self.parameters(), lr=0) + elif optimizer_name.lower() == "sgd": + optimizer = torch.optim.SGD(self.parameters(), lr=0) + elif optimizer_name.lower() == "sgd_momentum": + optimizer = torch.optim.SGD(self.parameters(), lr=0, momentum=0.9, nesterov=False) + elif optimizer_name.lower() == "sgd_nesterov": + optimizer = torch.optim.SGD(self.parameters(), lr=0, momentum=0.9, nesterov=True) + elif optimizer_name.lower() == "nesterov": + optimizer = NesterovAcceleratedGradientOptimizer.NesterovAcceleratedGradientOptimizer(self.parameters(), + lr=0, + obj_and_grad_fn=model.obj_and_grad_fn, + constraint_fn=self.op_collections.move_boundary_op, + ) + else: + assert 0, "unknown optimizer %s" % (optimizer_name) + + logging.info("use %s optimizer" % (optimizer_name)) + + model.train() + # defining evaluation ops + eval_ops = { + "hpwl" : self.op_collections.hpwl_op, + "overflow" : self.op_collections.density_overflow_op + } + if params.routability_opt_flag: + eval_ops.update({ + 'clustering_compatibility_lut': + self.op_collections.clustering_compatibility_lut_area_op, + 'clustering_compatibility_ff': + self.op_collections.clustering_compatibility_ff_area_op, + 'route_utilization': + self.op_collections.route_utilization_map_op, + 'pin_utilization': + self.op_collections.pin_utilization_map_op + }) + #For fence regions + eval_ops.update({ + 'density': + self.op_collections.fence_region_density_merged_op, + "overflow": + self.op_collections.fence_region_density_overflow_merged_op, + }) + + #Initialization moved before printing metrics + if torch.eq(torch.mean(model.density_weight), 0.0): + model.initialize_density_weight(params, placedb) + #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) + + # a function to initialize learning rate + def initialize_learning_rate(pos): + learning_rate = model.estimate_initial_learning_rate(pos) + # update learning rate + for param_group in optimizer.param_groups: + param_group['lr'] = learning_rate.data + + if iteration == 0: + if params.gp_noise_ratio > 0.0: + #logging.info("add %g%% noise" % (params.gp_noise_ratio*100)) + model.op_collections.noise_op(model.data_collections.pos[0], params.gp_noise_ratio) + initialize_learning_rate(model.data_collections.pos[0]) + # the state must be saved after setting learning rate + initial_state = copy.deepcopy(optimizer.state_dict()) + + if params.gpu: + torch.cuda.synchronize() + #logging.info("%s initialization takes %g seconds" % (optimizer_name, (time.time()-tt))) + + # as nesterov requires line search, we cannot follow the convention of other solvers + if optimizer_name.lower() in {"sgd", "adam", "sgd_momentum", "sgd_nesterov"}: + model.obj_and_grad_fn(model.data_collections.pos[0]) + elif optimizer_name.lower() != "nesterov": + assert 0, "unsupported optimizer %s" % (optimizer_name) + + # stopping criteria + def Lgamma_stop_criterion(placedb, Lgamma_step, metrics, stop_mask=None): + with torch.no_grad(): + if len(metrics) > 1: + cur_metric = metrics[-1][-1][-1] + prev_metric = metrics[-2][-1][-1] + + if Lgamma_step > 100 and (((cur_metric.overflow.cpu().numpy() < placedb.targetOverflow).sum() == len(placedb.targetOverflow) and cur_metric.hpwl > prev_metric.hpwl) or + cur_metric.max_density.max() < 1.0) and (placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].sum() == 0 or blockLegalIter >= 5): + logInfo = "Lgamma stopping criteria: " + str(Lgamma_step) + " > 100 and (( OVFL: " + for el in range(placedb.targetOverflow.size): + logInfo += str(round(cur_metric.overflow[el].item(),4)) + " < " + str(placedb.targetOverflow[el]) + "; " + logInfo += " and HPWL " + '{:.4e}'.format(cur_metric.hpwl.item()) + " > " + '{:.4e}'.format(prev_metric.hpwl.item()) + " ) or " + logInfo += str(round(cur_metric.max_density.max().item(),4)) + " < 1.0) and DSP/RAM block legal iter " + str(blockLegalIter) + " >= 5" + logging.info(logInfo) + return True + return False + + def Llambda_stop_criterion(placedb, Lgamma_step, Llambda_density_weight_step, metrics): + with torch.no_grad(): + if len(metrics) > 1: + cur_metric = metrics[-1][-1] + prev_metric = metrics[-2][-1] + if ((cur_metric.overflow.cpu().numpy() < placedb.targetOverflow).sum() == len(placedb.targetOverflow) and + cur_metric.hpwl > prev_metric.hpwl and (placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].sum() == 0 or blockLegalIter >= 5)) or cur_metric.max_density[-1] < 1.0: + logInfo = "Llambda stopping criteria: " + str(Llambda_density_weight_step) + " and (( OVFL: " + for el in range(placedb.targetOverflow.size): + logInfo += str(round(cur_metric.overflow[el].item(),4)) + " < " + str(placedb.targetOverflow[el]) + "; " + logInfo += " and HPWL " + '{:.4e}'.format(cur_metric.hpwl.item()) + " > " + '{:.4e}'.format(prev_metric.hpwl.item()) + " ) or " + logInfo += str(round(cur_metric.max_density.max().item(),4)) + " < 1.0)" + return True + return False + + # use a moving average window for stopping criteria, for an example window of 3 + # 0, 1, 2, 3, 4, 5, 6 + # window2 + # window1 + moving_avg_window = max(min(model.Lsub_iteration // 2, 3), 1) + def Lsub_stop_criterion(Lgamma_step, Llambda_density_weight_step, Lsub_step, metrics): + with torch.no_grad(): + if len(metrics) >= moving_avg_window * 2: + cur_avg_obj = 0 + prev_avg_obj = 0 + for i in range(moving_avg_window): + cur_avg_obj += metrics[-1 - i].objective + prev_avg_obj += metrics[-1 - moving_avg_window - i].objective + cur_avg_obj /= moving_avg_window + prev_avg_obj /= moving_avg_window + threshold = 0.999 + if cur_avg_obj >= prev_avg_obj * threshold: + logging.info("Lsub stopping criteria: %d and %g > %g * %g" % (Lsub_step, cur_avg_obj, prev_avg_obj, threshold)) + return True + return False + + def one_descent_step(Lgamma_step, Llambda_density_weight_step, Lsub_step, iteration, metrics, stop_mask=None): + + # metric for this iteration + cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) + cur_metric.gamma = model.gamma.data + cur_metric.density_weight = model.density_weight.data + metrics.append(cur_metric) + pos = model.data_collections.pos[0] + + # move any out-of-bound cell back to placement region + self.op_collections.move_boundary_op(pos) + optimizer.zero_grad() + cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) + model.overflow = cur_metric.overflow.data.clone() + #logging.debug("evaluation %.3f ms" % ((time.time()-t1)*1000)) + #t2 = time.time() + + # as nesterov requires line search, we cannot follow the convention of other solvers + if optimizer_name.lower() in ["sgd", "adam", "sgd_momentum", "sgd_nesterov"]: + obj, grad = model.obj_and_grad_fn(pos) + cur_metric.objective = obj.data.clone() + elif optimizer_name.lower() != "nesterov": + assert 0, "unsupported optimizer %s" % (optimizer_name) + + # plot placement + if params.plot_flag and (iteration % 100 == 0): + cur_pos = self.pos[0].data.clone().cpu().numpy() + self.plot(params, placedb, iteration, cur_pos) + + logging.info(cur_metric) + + t3 = time.time() + if(model.update_mask is not None): + pos_bk = pos.data.clone() + optimizer.step() + # print(model.update_mask) + for region_id, fence_region_update_flag in enumerate(model.update_mask): + #If there are no elements of resource type, skip + if(fence_region_update_flag == 0 and placedb.num_movable_nodes_fence_region[region_id] > 0): + ### don't update cell location in that region + mask = self.op_collections.fence_region_density_ops[region_id].pos_mask + pos.data.masked_scatter_(mask, pos_bk[mask]) + else: + optimizer.step() + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + #Update locations of pseudo filler nodes + if placedb.num_mlab_nodes > 0: + mlab_locations_x = pos[:placedb.num_physical_nodes][placedb.is_mlab_node].data + mlab_locations_y = pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes][placedb.is_mlab_node].data + pos[:placedb.num_nodes][placedb.is_mlab_filler_node == 1].data.copy_(mlab_locations_x) + pos[placedb.num_nodes:][placedb.is_mlab_filler_node == 1].data.copy_(mlab_locations_y) + + # nesterov has already computed the objective of the next step + if optimizer_name.lower() == "nesterov": + cur_metric.objective = optimizer.param_groups[0]['obj_k_1'][0].data.clone() + #print("Nesterov objective %f \n"%(tobj.data.clone())) + #print("HPWL is %g; Obj is %g \n" %(cur_metric.hpwl, cur_metric.objective)) + + # actually reports the metric before step + #logging.info(cur_metric) + # record the best outer cell overflow + if best_metric[0] is None or (best_metric[0].overflow > cur_metric.overflow).sum().item() == cur_metric.overflow.size()[0]: + best_metric[0] = cur_metric + if best_pos[0] is None: + best_pos[0] = self.pos[0].data.clone() + else: + best_pos[0].data.copy_(self.pos[0].data) + + #logging.info("full step %.3f ms" % ((time.time()-t0)*1000)) + + def check_plateau(x, window=10, threshold=0.001): + if(len(x) < window): + return False + x = x[-window:] + return (np.max(x) - np.min(x)) / np.mean(x) < threshold + + def check_divergence(x, window=50, threshold=0.05): + if(len(x) < window): + return False + x = np.array(x[-window:]) + smooth = max(1,int(0.1*window)) + wl_beg, wl_end = np.mean(x[0:smooth,0]), np.mean(x[-smooth:,0]) + overflow_beg, overflow_end = np.mean(x[0:smooth,1]), np.mean(x[-smooth:,1]) + # wl_ratio, overflow_ratio = (wl_end - wl_beg)/wl_beg, (overflow_end - max(placedb.targetOverflow.max(), best_metric[0].overflow))/best_metric[0].overflow + overflow_mean = np.mean(x[:,1]) + overflow_diff = np.maximum(0,np.sign(x[1:,1] - x[:-1,1])).astype(np.float32) + overflow_diff = np.sum(overflow_diff) / overflow_diff.shape[0] + overflow_range = np.max(x[:,1]) - np.min(x[:,1]) + wl_mean = np.mean(x[:,0]) + wl_ratio, overflow_ratio = (wl_mean - best_metric[0].hpwl.item())/best_metric[0].hpwl.item(), (overflow_mean - max(placedb.targetOverflow.max(), best_metric[0].overflow.max().item()))/best_metric[0].overflow.max().item() + if(wl_ratio > threshold*1.2): + if(overflow_ratio > threshold): + print(f"[Warning] Divergence detected: overflow increases too much than best overflow ({overflow_ratio:.4f} > {threshold:.4f})") + return True + elif(overflow_range/overflow_mean < threshold): + print(f"[Warning] Divergence detected: overflow plateau ({overflow_range/overflow_mean:.4f} < {threshold:.4f})") + return True + elif(overflow_diff > 0.6): + print(f"[Warning] Divergence detected: overflow fluctuate too frequently ({overflow_diff:.2f} > 0.6)") + return True + else: + return False + else: + return False + + Lgamma_metrics = all_metrics + + if params.routability_opt_flag: + adjust_area_flag = True + adjust_resource_area_flag = params.adjust_resource_area_flag + adjust_route_area_flag = params.adjust_route_area_flag + adjust_pin_area_flag = params.adjust_pin_area_flag + num_area_adjust = 0 + + Llambda_flat_iteration = 0 + + ### self-adaptive divergence check + overflow_list = np.ones((len(placedb.region_boxes)), dtype=placedb.dtype) + divergence_list = [] + min_perturb_interval = 50 + stop_placement = 0 + last_perturb_iter = -min_perturb_interval + noise_injected_flag = 0 + perturb_counter = 0 + allow_update = 1 + + # Start to compute time for optimization without parsing and initialization + optimization_timer = time.time() + for Lgamma_step in range(model.Lgamma_iteration): + Lgamma_metrics.append([]) + Llambda_metrics = Lgamma_metrics[-1] + for Llambda_density_weight_step in range(model.Llambda_density_weight_iteration): + Llambda_metrics.append([]) + Lsub_metrics = Llambda_metrics[-1] + for Lsub_step in range(model.Lsub_iteration): + ## Divergence threshold should decrease as overflow decreases + ## Only detect divergence when overflow is relatively low but not too low + if(((placedb.targetOverflow * 1.1 < overflow_list).sum() == len(placedb.targetOverflow) and (overflow_list < placedb.targetOverflow).sum() == len(placedb.targetOverflow)) and check_divergence(divergence_list, window=3, threshold=0.01 * overflow_list)): + self.pos[0].data.copy_(best_pos[0].data) + stop_placement = 1 + allow_update = 0 + logging.error( + "possible DIVERGENCE detected, roll back to the best position recorded and switch to ZerothOrderSearch of overflow and hpwl" + ) + + ct0 = time.time() + one_descent_step(Lgamma_step, Llambda_density_weight_step, Lsub_step, iteration, Lsub_metrics) + #print("Time for one step: %g ms" %((time.time()-ct0)*1000)) + iteration += 1 + if model.lock_mask is not None and model.lock_mask[placedb.dsp_ram_compIds].sum() == len(placedb.dsp_ram_compIds): + blockLegalIter += 1 + # stopping criteria + if Lsub_stop_criterion(Lgamma_step, Llambda_density_weight_step, Lsub_step, Lsub_metrics): + break + Llambda_flat_iteration += 1 + # update density weight + if Llambda_flat_iteration > 1: + model.op_collections.update_density_weight_op(Llambda_metrics[-1][-1], Llambda_metrics[-2][-1] if len(Llambda_metrics) > 1 else Lgamma_metrics[-2][-1][-1], Llambda_flat_iteration) + #logging.debug("update density weight %.3f ms" % ((time.time()-t2)*1000)) + if Llambda_stop_criterion(placedb, Lgamma_step, Llambda_density_weight_step, Llambda_metrics): + break + + if (params.routability_opt_flag and num_area_adjust < params.max_num_area_adjust and + (Llambda_metrics[-1][-1].overflow[placedb.slice_compIds] < self.data_collections.node_area_adjust_overflow[placedb.slice_compIds]).sum().item() == len(placedb.slice_compIds)): + pos = model.data_collections.pos[0] + + route_utilization_map = None + pin_utilization_map = None + resource_areas = None + if adjust_route_area_flag: + #Use RUDY for FPGA + route_utilization_map = model.op_collections.route_utilization_map_op(pos) + if params.plot_flag: + path = "%s/%s" % (params.result_dir, params.design_name()) + figname = "%s/plot/rudy%d.png" % (path, num_area_adjust) + os.system("mkdir -p %s" % (os.path.dirname(figname))) + plt.imsave(figname, route_utilization_map.data.cpu().numpy().T, origin='lower') + if adjust_pin_area_flag: + pin_utilization_map = model.op_collections.pin_utilization_map_op(pos) + if params.plot_flag: + path = "%s/%s" % (params.result_dir, params.design_name()) + figname = "%s/plot/pin%d.png" % (path, num_area_adjust) + os.system("mkdir -p %s" % (os.path.dirname(figname))) + plt.imsave(figname, pin_utilization_map.data.cpu().numpy().T, origin='lower') + # Compute LUT/FF clustering compatibility optimized resource areas + if adjust_resource_area_flag: + lut_resource_areas = model.op_collections.clustering_compatibility_lut_area_op(pos) + ff_resource_areas = model.op_collections.clustering_compatibility_ff_area_op(pos) + resource_areas = lut_resource_areas + ff_resource_areas + + adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag = model.op_collections.adjust_node_area_op( + pos, + resource_areas, + route_utilization_map, + pin_utilization_map + ) + content = "routability optimization round %d: adjust area flags = (%d, %d, %d, %d)" % (num_area_adjust, adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag) + logging.info(content) + if adjust_area_flag: + num_area_adjust += 1 + + #Record position before instance area update + best_metric[0] = Llambda_metrics[-1][-1] + if best_pos[0] is None: + best_pos[0] = model.data_collections.pos[0].data.clone() + else: + best_pos[0].data.copy_(model.data_collections.pos[0].data) + + #Compute new node areas + for el in range(len(placedb.slice_compIds)): + rsrcId = placedb.comp2rsrcId_map[el] + mask = model.data_collections.node2fence_region_map == rsrcId + model.data_collections.total_movable_node_area_fence_region[el] = (model.data_collections.node_size_x[:model.data_collections.num_physical_nodes] * model.data_collections.node_size_y[:model.data_collections.num_physical_nodes] * mask).sum() + + #Update node areas + model.data_collections.node_areas = model.data_collections.node_size_x * model.data_collections.node_size_y + + # restart Llambda + model.op_collections.density_op.reset(model.data_collections) + model.op_collections.density_overflow_op.reset() + model.op_collections.pin_utilization_map_op.reset() + for fence_reg in model.op_collections.fence_region_density_ops: + fence_reg.reset(model.data_collections) + + cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) + cur_metric.gamma = model.gamma.data + cur_metric.density_weight = model.density_weight.data + cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) + model.overflow = cur_metric.overflow.data.clone() + + model.op_collections.update_gamma_op(Lgamma_step, model.overflow) + model.reset_density_weight(params, placedb, 0.1) + #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) + + # load state to restart the optimizer + optimizer.load_state_dict(initial_state) + # must after loading the state + initialize_learning_rate(pos) + # increase iterations of the sub problem to slow down the search + model.Lsub_iteration = model.routability_Lsub_iteration + + break + + ##DSP/RAM legalization condition check + if len(placedb.dsp_ram_compIds) > 0 and placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].max() > 0 and (Llambda_metrics[-1][-1].overflow < model.data_collections.targetOverflow).sum().item() == model.data_collections.targetOverflow.size()[0]: + pos = model.data_collections.pos[0] + if model.lock_mask is not None and model.lock_mask[placedb.dsp_ram_compIds].sum() == len(placedb.dsp_ram_compIds): + break + + ## plot placement + #if params.plot_flag: + # cur_pos = pos.data.clone().cpu().numpy() + # self.plot(params, placedb, iteration, cur_pos) + # iteration += 1 + + #Legalize DSP/RAM at the end of Global placement + for lgId in placedb.dsp_ram_rsrcIds: + if placedb.node_count[lgId] > 0: + movVal = dsp_ram_legalization.LegalizeDSPRAMFunction.legalize(pos, placedb, lgId, model) + logging.info("Legalized %d %s instances with maxMov = %g and avgMov = %g" % + (placedb.node_count[lgId], placedb.rsrcTypes[lgId], movVal[0], movVal[1])) + + ## plot placement + #if params.plot_flag: + # cur_pos = pos.data.clone().cpu().numpy() + # self.plot(params, placedb, iteration, cur_pos) + # iteration += 1 + + #Lock DSP/RAM locations + model.lock_mask[placedb.dsp_ram_compIds] = True + model.update_mask = ~model.lock_mask + pos.grad[0:placedb.num_physical_nodes].data.masked_fill_(model.data_collections.dsp_ram_mask, 0.0) + pos.grad[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes].data.masked_fill_(model.data_collections.dsp_ram_mask, 0.0) + + # restart Llambda + for fence_reg in model.op_collections.fence_region_density_ops: + fence_reg.setLockDSPRAM() + + #Restart place params + cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) + cur_metric.gamma = model.gamma.data + cur_metric.density_weight = model.density_weight.data + cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) + model.overflow = cur_metric.overflow.data.clone() + + model.op_collections.update_gamma_op(Lgamma_step, model.overflow) + + model.reset_density_weight(params, placedb, 1.0) + #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) + + # load state to restart the optimizer + optimizer.load_state_dict(initial_state) + # must after loading the state + initialize_learning_rate(pos) + # increase iterations of the sub problem to slow down the search + model.Lsub_iteration = model.routability_Lsub_iteration + + break + + # gradually reduce gamma to tradeoff smoothness and accuracy + model.op_collections.update_gamma_op(Lgamma_step, Llambda_metrics[-1][-1].overflow) + + if Lgamma_stop_criterion(placedb, Lgamma_step, Lgamma_metrics) or stop_placement == 1: + break + + # update learning rate + if optimizer_name.lower() in ["sgd", "adam", "sgd_momentum", "sgd_nesterov", "cg"]: + if 'learning_rate_decay' in global_place_params: + for param_group in optimizer.param_groups: + param_group['lr'] *= global_place_params['learning_rate_decay'] + +######################################## +######################################## + + def solve_problem_2(pos_w, admm_multiplier, non_fence_regions_ex, non_fence_regions, iteration): + def check_valid(regions, pos_x, pos_y, pos_xh, pos_yh, valid_margin_x=0, valid_margin_y=0): + if(type(regions) == list): + regions = np.concatenate(regions,0) + valid_mask = torch.ones_like(pos_x, dtype=torch.bool) + for sub_region in regions: + xll, yll, xhh, yhh = sub_region + valid_margin_x = min((xhh-xll)/2, valid_margin_x) + valid_margin_y = min((yhh-yll)/2, valid_margin_y) + valid_mask.masked_fill_((pos_x < xhh-valid_margin_x) & (pos_xh > xll+valid_margin_x) & (pos_y < yhh-valid_margin_y) & (pos_yh > yll+valid_margin_y), 0) + return valid_mask + + num_nodes = placedb.num_nodes + num_movable_nodes = placedb.num_movable_nodes + + pos_g = pos_w + admm_multiplier # minimize the L2 norm + # node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map[:num_movable_nodes]).to(pos_g.device) + node2fence_region_map = self.data_collections.node2fence_region_map + + pos_x, pos_y = pos_g[:num_movable_nodes], pos_g[num_nodes:num_nodes + num_movable_nodes] + node_size_x, node_size_y = model.data_collections.node_size_x[:num_movable_nodes], model.data_collections.node_size_y[:num_movable_nodes] + num_regions = len(placedb.region_boxes) + + regions = placedb.region_boxes + # margin = 20 * 0.997**iteration + margin_x = placedb.bin_size_x * min(1,4*0.997**iteration) + margin_y = placedb.bin_size_y * min(1,4*0.997**iteration) + + # valid_margin = 1000 * 0.995**iteration + valid_margin_x = placedb.bin_size_x * 200*0.996**iteration + valid_margin_y = placedb.bin_size_y * 200*0.996**iteration + # valid_margin = 0 if valid_margin < 5 else valid_margin + ### move cells into fence regions + for i in range(num_regions): + if i in placedb.fixed_rsrcIds: + continue + mask = (node2fence_region_map == i) + pos_x_i, pos_y_i = pos_x[mask], pos_y[mask] + num_movable_nodes_i = pos_x_i.numel() + node_size_x_i, node_size_y_i = node_size_x[mask], node_size_y[mask] + pos_xh_i = pos_x_i + node_size_x_i + pos_yh_i = pos_y_i + node_size_y_i + regions_i = regions[i] # [n_regions, 4] + delta_min = torch.empty(num_movable_nodes_i, device=pos_x.device).fill_(((placedb.xh-placedb.xl)**2+(placedb.yh-placedb.yl)**2)) + delta_x_min = torch.zeros_like(delta_min) + delta_y_min = torch.zeros_like(delta_min) + + valid_mask = check_valid(non_fence_regions[i], pos_x_i, pos_y_i, pos_xh_i, pos_yh_i, valid_margin_x, valid_margin_y) + + for sub_region in regions_i: + delta_x = torch.zeros_like(delta_min) + delta_y = torch.zeros_like(delta_min) + xl, yl, xh, yh = sub_region + + # on the left + mask_l = (pos_x_i < xl + margin_x).masked_fill_(valid_mask, 0) + # on the right + mask_r = (pos_xh_i > xh - margin_x).masked_fill_(valid_mask, 0) + # on the top + mask_t = (pos_yh_i > yh - margin_y).masked_fill_(valid_mask, 0) + # on the bottom + mask_b = (pos_y_i < yl + margin_y).masked_fill_(valid_mask, 0) + + # x replacement for left cell + delta_x.masked_scatter_(mask_l, xl + margin_x - pos_x_i[mask_l]) + # x replacement for right cell + delta_x.masked_scatter_(mask_r, xh - margin_x - pos_xh_i[mask_r]) + # delta_x.masked_fill_(~(mask_l | mask_r), 0) + # y replacement for top cell + delta_y.masked_scatter_(mask_t, yh - margin_y - pos_yh_i[mask_t]) + # y replacement for bottom cell + delta_y.masked_scatter_(mask_b, yl + margin_y - pos_y_i[mask_b]) + # delta_y.masked_fill_(~(mask_t | mask_b), 0) + # update minimum replacement + delta_i = (delta_x ** 2 + delta_y ** 2) + update_mask = delta_i < delta_min + + delta_x_min.masked_scatter_(update_mask, delta_x[update_mask]) + delta_y_min.masked_scatter_(update_mask, delta_y[update_mask]) + delta_min.masked_scatter_(update_mask, delta_i[update_mask]) + + # update the minimum replacement for subregions + pos_x.masked_scatter_(mask, pos_x_i + delta_x_min) + pos_y.masked_scatter_(mask, pos_y_i + delta_y_min) + + ### move cells out of fence regions + # margin = 0 + # valid_margin = 100 * 0.99**iteration + exclude_mask = (node2fence_region_map == placedb.rIOIdx) | (node2fence_region_map == placedb.rPLLIdx) + pos_x_ex, pos_y_ex = pos_x[exclude_mask], pos_y[exclude_mask] + node_size_x_ex, node_size_y_ex = node_size_x[exclude_mask], node_size_y[exclude_mask] + pos_xh_ex = pos_x_ex + node_size_x_ex + pos_yh_ex = pos_y_ex + node_size_y_ex + + delta_min = torch.empty(pos_x_ex.numel(), device=pos_x.device).fill_(((placedb.xh-placedb.xl)**2+(placedb.yh-placedb.yl)**2)) + delta_x_min = torch.zeros_like(delta_min) + delta_y_min = torch.zeros_like(delta_min) + ### don't move valid cells + valid_mask = check_valid(regions, pos_x_ex, pos_y_ex, pos_xh_ex, pos_yh_ex, valid_margin_x, valid_margin_y) + + for sub_region in non_fence_regions_ex: + delta_x = torch.zeros_like(delta_min) + delta_y = torch.zeros_like(delta_min) + xl, yl, xh, yh = sub_region + + # on the left + mask_l = (pos_x_ex < xl).masked_fill_(valid_mask, 0) + # on the right + mask_r = (pos_xh_ex > xh).masked_fill_(valid_mask, 0) + # on the top + mask_t = (pos_yh_ex > yh).masked_fill_(valid_mask, 0) + # on the bottom + mask_b = (pos_y_ex < yl).masked_fill_(valid_mask, 0) + + # x replacement for left cell + delta_x.masked_scatter_(mask_l, xl + margin_x - pos_x_ex[mask_l]) + # x replacement for right cell + delta_x.masked_scatter_(mask_r, xh - margin_x - pos_xh_ex[mask_r]) + # delta_x.masked_fill_(~(mask_l | mask_r), 0) + # y replacement for top cell + delta_y.masked_scatter_(mask_t, yh - margin_y - pos_yh_ex[mask_t]) + # y replacement for bottom cell + delta_y.masked_scatter_(mask_b, yl + margin_y - pos_y_ex[mask_b]) + # delta_y.masked_fill_(~(mask_t | mask_b), 0) + # update minimum replacement + delta_i = (delta_x ** 2 + delta_y ** 2) + update_mask = delta_i < delta_min + + delta_x_min.masked_scatter_(update_mask, delta_x[update_mask]) + delta_y_min.masked_scatter_(update_mask, delta_y[update_mask]) + delta_min.masked_scatter_(update_mask, delta_i[update_mask]) + + # update the minimum replacement for subregions + pos_x.masked_scatter_(exclude_mask, pos_x_ex + delta_x_min) + pos_y.masked_scatter_(exclude_mask, pos_y_ex + delta_y_min) + + ### write back solution + fillers = np.zeros(self.num_filler_nodes, dtype=placedb.dtype) + fmask = placedb.io_mask + fillMask = np.concatenate((fmask,fillers.astype(bool),fmask,fillers.astype(bool)),axis=0) + allLoc = np.concatenate((placedb.node_x, fillers, placedb.node_y, fillers),axis=0) + omask = ~fmask + allMask = ~fillMask + res = pos_g.data.clone() + res.data[:num_movable_nodes].copy_(pos_x) + res.data[num_nodes:num_nodes + num_movable_nodes].copy_(pos_y) + return res + + # in case of divergence, use the best metric + ### always rollback to best outer cell overflow + last_metric = all_metrics[-1][-1][-1] + self.targetOverflow = torch.tensor(placedb.targetOverflow, dtype=torch.float, device=self.device) + if last_metric.overflow.max() > max(self.targetOverflow.max(), best_metric[0].overflow.max()) and last_metric.hpwl > best_metric[0].hpwl: + self.pos[0].data.copy_(best_pos[0].data) + logging.error("possible DIVERGENCE detected, roll back to the best position recorded") + all_metrics.append([best_metric]) + logging.info(best_metric[0]) + + #Legalize DSP/RAMs if any + if ((len(placedb.dsp_ram_compIds) > 0 and + placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].max() > 0) and + (model.lock_mask is not None and + model.lock_mask[placedb.dsp_ram_compIds].sum() != len(placedb.dsp_ram_compIds))): + + for lgId in placedb.dsp_ram_rsrcIds: + if placedb.node_count[lgId] > 0: + movVal = dsp_ram_legalization.LegalizeDSPRAMFunction.legalize(pos, placedb, lgId, model) + logging.info("Legalized %s with maxMov = %g and avgMov = %g" % (placedb.rsrcTypes[lgId], movVal[0], movVal[1])) + model.lock_mask[placedb.dsp_ram_compIds] = True + model.update_mask = ~model.lock_mask + + #logging.info("optimizer %s takes %.3f seconds" % (optimizer_name, time.time()-tt)) + # recover node size and pin offset for legalization, since node size is adjusted in global placement + if params.routability_opt_flag: + with torch.no_grad(): + # convert lower left to centers + # convert lower left to centers + #self.pos[0][:placedb.num_movable_nodes].add_( + # self.data_collections. + # node_size_x[:placedb.num_movable_nodes] / 2) + #self.pos[0][placedb.num_nodes:placedb.num_nodes + + # placedb.num_movable_nodes].add_( + # self.data_collections. + # node_size_y[:placedb.num_movable_nodes] / + # 2) + self.data_collections.node_size_x.copy_( + self.data_collections.original_node_size_x) + self.data_collections.node_size_y.copy_( + self.data_collections.original_node_size_y) + ## use fixed centers as the anchor + #self.pos[0][:placedb.num_movable_nodes].sub_( + # self.data_collections. + # node_size_x[:placedb.num_movable_nodes] / 2) + #self.pos[0][placedb.num_nodes:placedb.num_nodes + + # placedb.num_movable_nodes].sub_( + # self.data_collections. + # node_size_y[:placedb.num_movable_nodes] / + # 2) + self.data_collections.pin_offset_x.copy_( + self.data_collections.original_pin_offset_x) + self.data_collections.pin_offset_y.copy_( + self.data_collections.original_pin_offset_y) + #else: + # cur_metric = EvalMetricsFPGA(iteration) + # all_metrics.append(cur_metric) + # cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) + # logging.info(cur_metric) + + # dump global placement solution for legalization + if params.dump_global_place_solution_flag: + self.dump(params, placedb, self.pos[0].cpu(), "%s.lg.pklz" %(params.design_name())) + + half_pos = self.pos[0].shape[0]//2 + + ## plot placement + #if params.plot_flag: + # cur_pos = self.pos[0].data.clone().cpu().numpy() + # self.plot(params, placedb, 12345, cur_pos) + + if params.global_place_flag == 1 and placedb.num_ccNodes > 0: + #Update sizes & GP location of cc nodes + model.data_collections.org_node_size_x[placedb.new2org_node_map[:placedb.num_movable_nodes]] = model.data_collections.node_size_x[:placedb.num_movable_nodes] + model.data_collections.org_node_size_y[placedb.new2org_node_map[:placedb.num_movable_nodes]] = model.data_collections.node_size_y[:placedb.num_movable_nodes] + model.data_collections.org_node_size_x[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = model.data_collections.node_size_x[placedb.num_movable_nodes:placedb.num_physical_nodes] + model.data_collections.org_node_size_y[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = model.data_collections.node_size_y[placedb.num_movable_nodes:placedb.num_physical_nodes] + + model.data_collections.org_node_x[placedb.new2org_node_map[:placedb.num_movable_nodes]] = self.pos[0][:placedb.num_movable_nodes].data + model.data_collections.org_node_y[placedb.new2org_node_map[:placedb.num_movable_nodes]] = self.pos[0][half_pos:half_pos+placedb.num_movable_nodes].data + model.data_collections.org_node_x[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = self.pos[0][placedb.num_movable_nodes:placedb.num_physical_nodes].data + model.data_collections.org_node_size_y[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = self.pos[0][half_pos+placedb.num_movable_nodes:half_pos+placedb.num_physical_nodes] + + ccYLocIncr = 1/placedb.SLICE_CAPACITY + for ccId in range(placedb.num_carry_chains): + org_cc_indices = np.where(placedb.org_node2ccId_map == ccId)[0] + curr_cc_index = placedb.cc2nodeId_map[ccId] + elCount = placedb.cc_element_count[ccId] + model.data_collections.org_node_size_x[org_cc_indices] = model.data_collections.node_size_x[curr_cc_index] + model.data_collections.org_node_size_y[org_cc_indices] = model.data_collections.node_size_y[curr_cc_index]/elCount + model.data_collections.org_node_x[org_cc_indices] = self.pos[0][curr_cc_index].data + yoffset = torch.arange(elCount-1, -1, -1, dtype=self.data_collections.dtype, device=self.device)*ccYLocIncr + model.data_collections.org_node_y[org_cc_indices] = self.pos[0][half_pos:][curr_cc_index].data + yoffset + #print("Updated Carry chain %d at (%.2f, %.2f) => org (%.2f, %.2f)"% + # (ccId, self.pos[0][curr_cc_index].data, self.pos[0][half_pos:][curr_cc_index].data, + # model.data_collections.org_node_x[org_cc_indices[0]], + # model.data_collections.org_node_y[org_cc_indices[0]])) + + model.data_collections.org_node_areas = model.data_collections.org_node_size_x * model.data_collections.org_node_size_y + placedb.num_movable_nodes = placedb.org_num_movable_nodes + placedb.num_physical_nodes = placedb.org_num_physical_nodes + self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_x) + self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_y) + + ## plot placement + #if params.plot_flag: + # cur_pos = self.pos[0].data.clone().cpu().numpy() + # self.plot(params, placedb, 5678, cur_pos) + + # legalization + if params.legalize_flag: + if params.global_place_flag == 0: + #Load from GP results + for global_place_params in params.global_place_stages: + + if params.gpu: + torch.cuda.synchronize() + tt = time.time() + # construct model and optimizer + density_weight = 0.0 + # construct placement model + model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) + print("Model constructed in %g ms"%((time.time()-tt)*1000)) + + place_file=params.global_place_sol + #logging.info("Reading %s" % (place_file)) + with open (place_file, "r") as f: + for line in f: + tokens = line.split() + if len(tokens) > 0: + if tokens[0] in placedb.node_name2id_map: + nodeId = placedb.node_name2id_map[tokens[0]] + self.data_collections.node_x[nodeId].data.fill_(placedb.dtype(tokens[1])) + self.data_collections.node_y[nodeId].data.fill_(placedb.dtype(tokens[2])) + self.data_collections.node_z[nodeId].data.fill_(int(tokens[3])) + if placedb.num_ccNodes: + nodeId = placedb.org_node_name2id_map[tokens[0]] + self.data_collections.org_node_x[nodeId].data.fill_(placedb.dtype(tokens[1])) + self.data_collections.org_node_y[nodeId].data.fill_(placedb.dtype(tokens[2])) + self.data_collections.org_node_z[nodeId].data.fill_(int(tokens[3])) + self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.node_x) + self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.node_y) + logging.info("Read Global Placement solution from %s" % (place_file)) + cur_metric = EvalMetricsFPGA(iteration) + all_metrics.append(cur_metric) + cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) + logging.info(cur_metric) + iteration += 1 + + #Break carry chain nodes as single entity + if placedb.num_ccNodes > 0: + placedb.num_movable_nodes = placedb.org_num_movable_nodes + placedb.num_physical_nodes = placedb.org_num_physical_nodes + self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_x) + self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_y) + node_areas = self.data_collections.org_node_areas + lut_mask = placedb.org_lut_mask + flop_mask = placedb.org_flop_mask + lut_flop_mask = placedb.org_lut_flop_mask + node_z = self.data_collections.org_node_z + else: + node_areas = self.data_collections.node_areas + lut_mask = placedb.lut_mask + flop_mask = placedb.flop_mask + lut_flop_mask = placedb.lut_flop_mask + node_z = self.data_collections.node_z + + #Perform sorting of pin, net, node + _, sortedNetIdx = torch.sort(self.data_collections.net2pincount_map) + sortedNetIdx = sortedNetIdx.to(torch.int32) + _, sortedNetMap = torch.sort(sortedNetIdx) + sortedNetMap = sortedNetMap.to(torch.int32) + + _, sortedPinIdx = torch.sort(sortedNetMap[self.data_collections.pin2net_map.to(torch.long)]) + sortedPinIdx = sortedPinIdx.to(torch.int32) + _, sortedPinMap = torch.sort(sortedPinIdx) + sortedPinMap = sortedPinMap.to(torch.int32) + + node2pinId0 = self.op_collections.sort_node2pin_op(sortedPinMap) + #node2pinId0 = torch.zeros(placedb.num_physical_nodes, dtype=torch.int32) + #for el in range(placedb.num_physical_nodes): + # startId = data_collections.flat_node2pin_start_map[el] + # endId = data_collections.flat_node2pin_start_map[el+1] + # _, sorted_node2pin_idx = torch.sort(sorted_pin_map[data_collections.flat_node2pin_map.to(torch.long)[startId:endId]]) + # data_collections.flat_node2pin_map[startId:endId].data.copy_(data_collections.flat_node2pin_map[startId:endId][sorted_node2pin_idx].data) + # node2pinId0[el] = sorted_pin_map[data_collections.flat_node2pin_map[startId]] + + _, sortedNodeIdx = torch.sort(node2pinId0) + sortedNodeIdx = sortedNodeIdx.to(torch.int32) + + _, sortedNodeMap = torch.sort(sortedNodeIdx) + sortedNodeMap = sortedNodeMap.to(torch.int32) + + tt = time.time() + + if placedb.num_ccNodes == 0: + preconditioner = model.precondWL[:placedb.num_physical_nodes] + else: + preconditioner = model.lg_precondWL[:placedb.num_physical_nodes] + + self.op_collections.lut_ff_legalization_op.initialize(self.pos[0], preconditioner, sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap) + + DLStatus = 1 + dlIter = 0 + + #For runDLIter stopping criteria + MAX_DL_ITERS=100 + MIN_DL_ITERS=50 + ITERS_INCREASE=50 + STOP_ITERS=150 + STABLE_ITER_COUNT=5 + REM_INSTANCE_RATIO=0.09 + activeStatus = torch.zeros(placedb.num_sites_x*placedb.num_sites_y, dtype=torch.int, device=self.device) + illegalStatus = torch.zeros(placedb.num_physical_nodes, dtype=torch.int, device=self.device) + + iter_stable = 0 + prevAct = 0 + + while (DLStatus == 1): + self.op_collections.lut_ff_legalization_op.runDLIter(self.pos[0], preconditioner, sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap, activeStatus, illegalStatus, dlIter) + + if prevAct == illegalStatus.sum().item() + activeStatus.sum().item(): + iter_stable = iter_stable + 1 + else: + iter_stable = 0 + + dlIter = dlIter+1 + if activeStatus.sum().item() > 0: + DLStatus = 1 + elif illegalStatus.sum().item() > 0: + DLStatus = -1 + else: + DLStatus = 0 + + prevAct=illegalStatus.sum().item() + activeStatus.sum().item() + + if dlIter > STOP_ITERS or (dlIter > MIN_DL_ITERS and iter_stable > STABLE_ITER_COUNT): + DLStatus = 0 + + if dlIter > MAX_DL_ITERS and iter_stable < STABLE_ITER_COUNT: + if illegalStatus.sum().item() < REM_INSTANCE_RATIO*placedb.num_physical_nodes: + DLStatus = 0 + else: + MAX_DL_ITERS += ITERS_INCREASE + + #Use inflated instance areas for ripUP & greedy LG + avgLUTArea = node_areas[:placedb.num_physical_nodes][lut_mask].sum() + avgLUTArea /= placedb.node_count[placedb.rLUTIdx] + avgFFArea = node_areas[:placedb.num_physical_nodes][flop_mask].sum() + avgFFArea /= placedb.node_count[placedb.rFFIdx] + #Inst Areas + inst_areas = node_areas[:placedb.num_physical_nodes].detach().clone() + inst_areas[~lut_flop_mask] = 0.0 #Area of non SLICE nodes set to 0.0 + inst_areas[lut_mask] /= avgLUTArea + inst_areas[flop_mask] /= avgFFArea + + self.pos[0].data.copy_(self.op_collections.lut_ff_legalization_op.ripUP_Greedy_slotAssign(self.pos[0], preconditioner, node_z[:placedb.num_movable_nodes], sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap, inst_areas)) + + #Terminate if legalization has errors + if (self.pos[0] == -1).sum().item() > 0: + sys.exit("[ERROR] " + str((self.pos[0] == -1).sum().item()) + " instances were not legalized - Please ensure there is sufficient space in sitemap and/or revisit LG algorithm") + + logging.info("legalization takes %.3f seconds" % (time.time()-tt)) + cur_metric = EvalMetricsFPGA(iteration) + all_metrics.append(cur_metric) + cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) + logging.info(cur_metric) + iteration += 1 + + # recover node size and pin offset for plot, since node size is adjusted in global placement + if params.routability_opt_flag: + with torch.no_grad(): + # convert lower left to centers + # convert lower left to centers + #self.pos[0][:placedb.num_movable_nodes].add_( + # self.data_collections. + # node_size_x[:placedb.num_movable_nodes] / 2) + #self.pos[0][placedb.num_nodes:placedb.num_nodes + + # placedb.num_movable_nodes].add_( + # self.data_collections. + # node_size_y[:placedb.num_movable_nodes] / + # 2) + self.data_collections.node_size_x.copy_( + self.data_collections.original_node_size_x) + self.data_collections.node_size_y.copy_( + self.data_collections.original_node_size_y) + ## use fixed centers as the anchor + #self.pos[0][:placedb.num_movable_nodes].sub_( + # self.data_collections. + # node_size_x[:placedb.num_movable_nodes] / 2) + #self.pos[0][placedb.num_nodes:placedb.num_nodes + + # placedb.num_movable_nodes].sub_( + # self.data_collections. + # node_size_y[:placedb.num_movable_nodes] / + # 2) + self.data_collections.pin_offset_x.copy_( + self.data_collections.original_pin_offset_x) + self.data_collections.pin_offset_y.copy_( + self.data_collections.original_pin_offset_y) + + # plot placement + #if params.plot_flag: + # self.plot(params, placedb, iteration, self.pos[0].data.clone().cpu().numpy()) + + # dump legalization solution for detailed placement + if params.dump_legalize_solution_flag: + self.dump(params, placedb, self.pos[0].cpu(), "%s.dp.pklz" %(params.design_name())) + + # detailed placement + if params.detailed_place_flag: + place_file=params.lg_place_sol + if params.global_place_flag == 0 and params.legalize_flag == 0 and place_file != "": + #Load legal placement results from file + for global_place_params in params.global_place_stages: + + if params.gpu: + torch.cuda.synchronize() + tt = time.time() + # construct model and optimizer + density_weight = 0.0 + # construct placement model + model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) + print("Model constructed in %g ms"%((time.time()-tt)*1000)) + + with open (place_file, "r") as f: + for line in f: + tokens = line.split() + if len(tokens) > 0: + nodeId = placedb.node_name2id_map[tokens[0]] + self.data_collections.node_x[nodeId].data.fill_(float(tokens[1])) + self.data_collections.node_y[nodeId].data.fill_(float(tokens[2])) + self.data_collections.node_z[nodeId].data.fill_(int(tokens[3])) + self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.node_x) + self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.node_y) + + ##Update locations for all instances from placement solution + logging.info("Read Legalized Placement solution from %s" % (place_file)) + cur_metric = EvalMetricsFPGA(iteration) + all_metrics.append(cur_metric) + cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) + logging.info(cur_metric) + iteration += 1 + + ## detailed placement + #if params.detailed_place_flag: + # tt = time.time() + # self.pos[0].data.copy_(self.op_collections.detailed_place_op(self.pos[0])) + # logging.info("detailed placement takes %.3f seconds" % (time.time()-tt)) + # cur_metric = EvalMetricsFPGA(iteration) + # all_metrics.append(cur_metric) + # cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) + # logging.info(cur_metric) + # iteration += 1 + + if placedb.num_ccNodes: + node_z = self.data_collections.org_node_z + else: + node_z = self.data_collections.node_z + half_pos = self.pos[0].shape[0]//2 + + # save results + cur_pos = self.pos[0].data.clone().cpu().numpy() + node_z = node_z[:placedb.num_movable_nodes].data.clone().cpu().numpy() + # apply solution + placedb.apply( + cur_pos[0:placedb.num_movable_nodes], + cur_pos[half_pos:half_pos + placedb.num_movable_nodes], + node_z) + + # plot placement + if params.plot_flag: + self.plot(params, placedb, iteration, cur_pos) + + return all_metrics + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py new file mode 100644 index 00000000..7750a1a0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py @@ -0,0 +1,179 @@ +## +# @file Params.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief User parameters +# + +import os +import sys +import json +import math +from collections import OrderedDict +import pdb + +class Params: + """ + @brief Parameter class + """ + def __init__(self): + """ + @brief initialization + """ + filename = os.path.join(os.path.dirname(__file__), 'params.json') + self.__dict__ = {} + params_dict = {} + with open(filename, "r") as f: + params_dict = json.load(f, object_pairs_hook=OrderedDict) + for key, value in params_dict.items(): + if 'default' in value: + self.__dict__[key] = value['default'] + else: + self.__dict__[key] = None + self.__dict__['params_dict'] = params_dict + + def printWelcome(self): + """ + @brief print welcome message + """ + content = """\ +======================================================== + DREAMPlaceFPGA +========================================================""" + print(content) + + def printHelp(self): + """ + @brief print help message for JSON parameters + """ + content = self.toMarkdownTable() + print(content) + + def toMarkdownTable(self): + """ + @brief convert to markdown table + """ + key_length = len('JSON Parameter') + key_length_map = [] + default_length = len('Default') + default_length_map = [] + description_length = len('Description') + description_length_map = [] + + def getDefaultColumn(key, value): + if sys.version_info.major < 3: # python 2 + flag = isinstance(value['default'], unicode) + else: #python 3 + flag = isinstance(value['default'], str) + if flag and not value['default'] and 'required' in value: + return value['required'] + else: + return value['default'] + + for key, value in self.params_dict.items(): + key_length_map.append(len(key)) + default_length_map.append(len(str(getDefaultColumn(key, value)))) + description_length_map.append(len(value['descripton'])) + key_length = max(key_length, key_length_map[-1]) + default_length = max(default_length, default_length_map[-1]) + description_length = max(description_length, description_length_map[-1]) + + content = "| %s %s| %s %s| %s %s|\n" % ( + 'JSON Parameter', + " " * (key_length - len('JSON Parameter') + 1), + 'Default', + " " * (default_length - len('Default') + 1), + 'Description', + " " * (description_length - len('Description') + 1) + ) + content += "| %s | %s | %s |\n" % ( + "-" * (key_length + 1), + "-" * (default_length + 1), + "-" * (description_length + 1) + ) + count = 0 + for key, value in self.params_dict.items(): + content += "| %s %s| %s %s| %s %s|\n" % ( + key, + " " * (key_length - key_length_map[count] + 1), + str(getDefaultColumn(key, value)), + " " * (default_length - default_length_map[count] + 1), + value['descripton'], + " " * (description_length - description_length_map[count] + 1) + ) + count += 1 + return content + + def toJson(self): + """ + @brief convert to json + """ + data = {} + for key, value in self.__dict__.items(): + if key != 'params_dict': + data[key] = value + return data + + def fromJson(self, data): + """ + @brief load form json + """ + for key, value in data.items(): + self.__dict__[key] = value + + def dump(self, filename): + """ + @brief dump to json file + """ + with open(filename, 'w') as f: + json.dump(self.toJson(), f) + + def load(self, filename): + """ + @brief load from json file + """ + with open(filename, 'r') as f: + self.fromJson(json.load(f)) + + def __str__(self): + """ + @brief string + """ + return str(self.toJson()) + + def __repr__(self): + """ + @brief print + """ + return self.__str__() + + def design_name(self): + """ + @brief speculate the design name for dumping out intermediate solutions + """ + design_name = os.path.basename(self.aux_input).replace(".aux", "").replace(".AUX", "") + return design_name + + def solution_file_suffix(self): + """ + @brief speculate placement solution file suffix + """ + return "pl" + +#Rac: Create child class for FPGA +class ParamsFPGA(Params): + """ + initialization + """ + def __init__(self): + #Same as Params to read in all the specified variables + filename = os.path.join(os.path.dirname(__file__), 'paramsFPGA.json') + self.__dict__ = {} + params_dict = {} + with open(filename, "r") as f: + params_dict = json.load(f, object_pairs_hook=OrderedDict) + for key, value in params_dict.items(): + if 'default' in value: + self.__dict__[key] = value['default'] + else: + self.__dict__[key] = None diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py new file mode 100644 index 00000000..a10bf33b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py @@ -0,0 +1,1156 @@ +## +# @file PlaceDB.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Oct 2020 +# @brief FPGA placement database +# + +import sys +import os +import re +import math +import time +import numpy as np +import logging +import Params +import dreamplacefpga +import dreamplacefpga.ops.place_io.place_io as place_io +import pdb +from enum import IntEnum + +datatypes = { + 'float32' : np.float32, + 'float64' : np.float64 + } + +class PlaceDBFPGA (object): + """ + initialization + To avoid the usage of list, flatten everything. + """ + def __init__(self): + self.rawdb = None # raw placement database, a C++ object + self.num_physical_nodes = 0 # number of real nodes, including movable nodes, terminals, and terminal_NIs + self.node_names = [] # name of instances + self.node_name2id_map = {} # map instance name to instance id + self.node_types = [] # instance types + self.node_x = [] # site location + self.node_y = [] # site location + self.node_z = [] # site specific location + self.ctrlSets = [] #Used for Flops + self.flat_ctrlSets = [] #Used for Flops + self.flop2ctrlSetId_map = [] #Used for Flop to ctrlset Id map + self.node_size_x = []# 1D array, cell width + self.node_size_y = []# 1D array, cell height + self.resource_size_x = None# 1D array, resource type-based cell width + self.resource_size_y = None# 1D array, resource type-based cell height + #Legalization + self.spiral_accessor = [] + + self.pin_names = [] # pin names + self.pin_types = [] # pin types + self.pin_offset_x = []# 1D array, pin offset x to its node + self.pin_offset_y = []# 1D array, pin offset y to its node + self.lg_pin_offset_x = []# 1D array, pin offset x to its node + self.lg_pin_offset_y = []# 1D array, pin offset y to its node + self.pin2nodeType_map = [] # 1D array, pin to node type map + self.node2pin_map = [] # nested array of array to record pins in each instance + self.flat_node2pin_map = [] #Flattened array of node2pin_map + self.flat_node2pin_start_map = [] #Contains start index for flat_node2pin_map + self.pin2node_map = [] # map pin to node + + self.net_names = [] # net names + self.net2pin_map = [] # nested array of array to record pins in each net + self.flat_net2pin_map = [] # flattend version of net2pin_map + self.flat_net2pin_start_map = [] # starting point for flat_net2pin_map + self.pin2net_map = None # map pin to net + + self.num_bins_x = None# number of bins in horizontal direction + self.num_bins_y = None# number of bins in vertical direction + self.bin_size_x = None# bin width, currently 1 site + self.bin_size_y = None# bin height, currently 1 site + + self.num_sites_x = None # number of sites in horizontal direction + self.num_sites_y = None # number of sites in vertical direction + self.site_type_map = None # site type of each site + self.lg_siteXYs = None # site type of each site + self.dspSiteXYs = [] #Sites for DSP instances + self.ramSite0XYs = [] #Sites for RAM instances + self.ramSite1XYs = [] #Sites for RAM instances + + self.xWirelenWt = None #X-directed wirelength weight + self.yWirelenWt = None #Y-directed wirelength weight + self.baseWirelenGammaBinRatio = None # The base wirelenGamma is * average bin size + self.instDemStddevTrunc = None # We truncate Gaussian distribution outside the instDemStddevTrunc * instDemStddev + # Resource Area Parameters + self.gpInstStddev = None + self.gpInstStddevTrunc = None + self.instDemStddevX = None + self.instDemStddevY = None + # Routability and pin density optimization parameters + self.unitHoriRouteCap = 0 + self.unitVertRouteCap = 0 + self.unitPinCap = 0 + + #Area type parameters + self.filler_size_x = [] #Filler size X for each resourceType + self.filler_size_y = [] #Filler size Y for each resourceType + self.targetOverflow = [] #Target overflow + self.overflowInstDensityStretchRatio = [] #OVFL density stretch ratio + + self.rawdb = None # raw placement database, a C++ object + + self.num_movable_nodes = 0# number of movable nodes + self.num_terminals = 0# number of IOs, essentially fixed instances + self.num_ccNodes= 0# number of carry chains + self.net_weights = None # weights for each net + + self.xl = None + self.yl = None + self.xh = None + self.yh = None + + self.num_movable_pins = None + + self.total_movable_node_area = None # total movable cell area + self.total_fixed_node_area = None # total fixed cell area + self.total_space_area = None # total placeable space area excluding fixed cells + + # enable filler cells + # the Idea from e-place and RePlace + self.total_filler_node_area = None + self.num_filler_nodes = 0 + + self.routing_grid_xl = None + self.routing_grid_yl = None + self.routing_grid_xh = None + self.routing_grid_yh = None + self.num_routing_grids_x = None + self.num_routing_grids_y = None + self.num_routing_layers = None + self.unit_horizontal_capacity = None # per unit distance, projected to one layer + self.unit_vertical_capacity = None # per unit distance, projected to one layer + self.unit_horizontal_capacities = None # per unit distance, layer by layer + self.unit_vertical_capacities = None # per unit distance, layer by layer + self.initial_horizontal_demand_map = None # routing demand map from fixed cells, indexed by (grid x, grid y), projected to one layer + self.initial_vertical_demand_map = None # routing demand map from fixed cells, indexed by (grid x, grid y), projected to one layer + self.dtype = None + #Use Fence region structure for different resource type placement + self.regions = 0 #FF, LUT, DSP, RAM & IO + self.flat_region_boxes = []# flat version of regionsLimits + self.flat_region_boxes_start = []# start indices of regionsLimits, length of num regions + 1 + self.node2fence_region_map = []# map cell to a region, maximum integer if no fence region + self.node_count = [] #Count of nodes based on resource type + #Introduce masks + self.flop_mask = None + self.lut_mask = None + self.lut_type = None + self.cluster_lut_type = None + self.ram0_mask = None + self.ram1_mask = None + self.dsp_mask = None + + self.fixed_rsrcIds = [] + self.slice_rsrcIds = [] + self.slice_compIds = [] + self.dsp_ram_rsrcIds = [] + self.dsp_ram_compIds= [] + + """ + @return number of nodes + """ + @property + def num_nodes_nofiller(self): + return self.num_physical_nodes + """ + @return number of nodes + """ + @property + def num_nodes(self): + return self.num_physical_nodes + self.num_filler_nodes + """ + @return number of nets + """ + @property + def num_nets(self): + return len(self.net2pin_map) + """ + @return number of pins + """ + @property + def num_pins(self): + return len(self.pin2node_map) + + @property + def width(self): + """ + @return width of layout + """ + return int(self.xh-self.xl) + + @property + def height(self): + """ + @return height of layout + """ + return int(self.yh-self.yl) + + @property + def area(self): + """ + @return area of layout + """ + return self.width*self.height + + @property + def routing_grid_size_x(self): + return (self.routing_grid_xh - self.routing_grid_xl) / self.num_routing_grids_x + + @property + def routing_grid_size_y(self): + return (self.routing_grid_yh - self.routing_grid_yl) / self.num_routing_grids_y + + def num_bins(self, l, h, bin_size): + """ + @brief compute number of bins + @param l lower bound + @param h upper bound + @param bin_size bin size + @return number of bins + """ + return int(np.ceil((h-l)/bin_size)) + + """ + read all files including .inst, .pin, .net, .routingUtil files + """ + def read(self, params): + self.dtype = datatypes[params.dtype] + + self.rawdb = place_io.PlaceIOFunction.read(params) + + self.initialize_from_rawdb(params) + + self.lut_mask = self.node2fence_region_map == self.rLUTIdx + self.flop_mask = self.node2fence_region_map == self.rFFIdx + self.lut_flop_mask = self.lut_mask | self.flop_mask + + if self.num_ccNodes > 0: + self.org_lut_mask = self.org_node2fence_region_map == self.rLUTIdx + self.org_flop_mask = self.org_node2fence_region_map == self.rFFIdx + self.org_lut_flop_mask = self.org_lut_mask | self.org_flop_mask + + if self.rLUTIdx != -1: + self.slice_rsrcIds.append(self.rLUTIdx) + if self.rFFIdx != -1: + self.slice_rsrcIds.append(self.rFFIdx) + + self.dsp_ram_mask = self.node2fence_region_map == -1 + if self.rDSPIdx != -1: + self.dsp_mask = self.node2fence_region_map == self.rDSPIdx + self.dsp_ram_mask |= self.dsp_mask + self.dsp_ram_rsrcIds.append(self.rDSPIdx) + if self.rBRAMIdx != -1: + self.ram0_mask = self.node2fence_region_map == self.rBRAMIdx + self.dsp_ram_mask |= self.ram0_mask + self.dsp_ram_rsrcIds.append(self.rBRAMIdx) + if self.rM9KIdx != -1: + self.ram0_mask = self.node2fence_region_map == self.rM9KIdx + self.dsp_ram_mask |= self.ram0_mask + self.dsp_ram_rsrcIds.append(self.rM9KIdx) + if self.rM144KIdx != -1: + self.ram1_mask = self.node2fence_region_map == self.rM144KIdx + self.dsp_ram_mask |= self.ram1_mask + self.dsp_ram_rsrcIds.append(self.rM144KIdx) + self.dsp_ram_rsrcIds = np.array(self.dsp_ram_rsrcIds, dtype=np.int32) + + self.io_mask = self.node2fence_region_map == -1 + if self.rIOIdx!= -1: + self.fixed_rsrcIds.append(self.rIOIdx) + self.io_mask |= self.node2fence_region_map == self.rIOIdx + if self.rPLLIdx != -1: + self.fixed_rsrcIds.append(self.rPLLIdx) + self.io_mask |= self.node2fence_region_map == self.rPLLIdx + self.fixed_rsrcIds = np.array(self.fixed_rsrcIds, dtype=np.int32) + + def initialize_from_rawdb(self, params): + """ + @brief initialize data members from raw database + @param params parameters + """ + pydb = place_io.PlaceIOFunction.pydb(self.rawdb) + + self.num_terminals = pydb.num_terminals + self.num_movable_nodes = pydb.num_movable_nodes + self.num_physical_nodes = pydb.num_physical_nodes + self.node_count = np.array(pydb.node_count, dtype=np.int32) + + #Do not use numpy array for names as it could result in + # large memory usage for large designs with long names + self.node_names = pydb.node_names + self.node_name2id_map = pydb.node_name2id_map + self.node_size_x = np.array(pydb.node_size_x, dtype=self.dtype) + self.node_size_y = np.array(pydb.node_size_y, dtype=self.dtype) + self.node_types = np.array(pydb.node_types, dtype=np.str_) + self.node2fence_region_map = np.array(pydb.node2fence_region_map, dtype=np.int32) + self.node_x = np.array(pydb.node_x, dtype=self.dtype) + self.node_y = np.array(pydb.node_y, dtype=self.dtype) + self.node_z = np.array(pydb.node_z, dtype=np.int32) + + self.node2pin_map = pydb.node2pin_map + self.flat_node2pin_map = np.array(pydb.flat_node2pin_map, dtype=np.int32) + self.flat_node2pin_start_map = np.array(pydb.flat_node2pin_start_map, dtype=np.int32) + self.node2pincount_map = np.array(pydb.node2pincount_map, dtype=np.int32) + self.net2pincount_map = np.array(pydb.net2pincount_map, dtype=np.int32) + self.node2outpinIdx_map = np.array(pydb.node2outpinIdx_map, dtype=np.int32) + self.flop_indices = np.array(pydb.flop_indices) + self.lut_type = np.array(pydb.lut_type).astype(np.int32) + #Use for clustering aware instance area update. LUT0 is ignored and other types have N-1 for type LUTN + self.cluster_lut_type = np.array(pydb.cluster_lut_type).astype(np.int32) + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + self.is_mlab_node = self.lut_type > 9 + self.num_mlab_nodes = self.is_mlab_node.sum() + + self.pin_offset_x = np.array(pydb.pin_offset_x, dtype=self.dtype) + self.pin_offset_y = np.array(pydb.pin_offset_y, dtype=self.dtype) + self.pin2nodeType_map = np.array(pydb.pin2nodeType_map, dtype=np.int32) + + self.pin_names = pydb.pin_names + self.pin_types = np.array(pydb.pin_types, dtype=np.str_) + self.pin_typeIds = np.array(pydb.pin_typeIds, dtype=np.int32) + self.pin2node_map = np.array(pydb.pin2node_map, dtype=np.int32) + self.pin2net_map = np.array(pydb.pin2net_map, dtype=np.int32) + self.spiral_accessor = np.array(pydb.spiral_accessor, dtype=np.int32) + self.spiral_maxVal = pydb.spiral_maxVal + + self.net_names = pydb.net_names + self.net2pin_map = pydb.net2pin_map + + self.flat_net2pin_map = np.array(pydb.flat_net2pin_map, dtype=np.int32) + self.flat_net2pin_start_map = np.array(pydb.flat_net2pin_start_map, dtype=np.int32) + self.net_name2id_map = pydb.net_name2id_map + self.net_weights = np.array(np.ones(len(self.net_names)), dtype=self.dtype) + + self.num_sites_x = pydb.num_sites_x + self.num_sites_y = pydb.num_sites_y + self.siteTypes = np.array(pydb.siteTypes, dtype=np.str_) + self.siteWidths = np.array(pydb.siteWidths, dtype=self.dtype) + self.siteHeights = np.array(pydb.siteHeights, dtype=self.dtype) + self.rsrcTypes = np.array(pydb.rsrcTypes, dtype=np.str_) + self.rsrcInstWidths = np.array(pydb.rsrcInstWidths, dtype=self.dtype) + self.rsrcInstHeights = np.array(pydb.rsrcInstHeights, dtype=self.dtype) + #TODO: Cell dimensions read in for FF/LUT are square values + self.rsrcInstWidths[self.rsrcInstWidths < 1.0] = np.sqrt(self.rsrcInstWidths[self.rsrcInstWidths < 1.0]) + self.rsrcInstHeights[self.rsrcInstHeights < 1.0] = np.sqrt(self.rsrcInstHeights[self.rsrcInstHeights < 1.0]) + self.siteResources = pydb.siteResources + self.rsrcInsts = pydb.rsrcInsts + self.rsrcInstTypes = np.array(pydb.rsrcInstTypes, dtype=np.str_) + self.rsrc2siteMap = pydb.rsrc2siteMap + self.inst2rsrcMap = pydb.inst2rsrcMap + self.siteRsrc2CountMap = pydb.siteRsrc2CountMap + self.siteType2indexMap = pydb.siteType2indexMap + self.rsrcType2indexMap = pydb.rsrcType2indexMap + self.rsrcInstType2indexMap = pydb.rsrcInstType2indexMap + self.sliceElements = pydb.sliceElements + self.lut_maxShared = pydb.lut_maxShared + self.lutTypeInSliceUnit = pydb.lut_type_in_sliceUnit + self.lutFracturesMap = pydb.lutFracturesMap + self.sliceFF_ctrl_mode = pydb.sliceFF_ctrl_mode + self.sliceFFCtrls = pydb.sliceFFCtrls + self.sliceUnitFFCtrls = pydb.sliceUnitFFCtrls + self.siteOutCoordinates = np.array(pydb.siteOutCoordinates, dtype=np.str_) + self.siteOutValues = np.array(pydb.siteOutValues, dtype=np.int32) + self.site_type_map = np.array(pydb.site_type_map, dtype=np.int32) + self.lg_siteXYs = np.array(pydb.lg_siteXYs, dtype=self.dtype) + + #Compute Indices for rsrcTypes + self.rFFIdx = -1 + self.rLUTIdx = -1 + self.rMlabIdx = -1 + self.rADDIdx = -1 + self.rIOIdx = -1 + self.rPLLIdx = -1 + self.rBRAMIdx = -1 + self.rM9KIdx = -1 + self.rM144KIdx = -1 + self.rDSPIdx = -1 + self.rEMPTYIdx = -1 + + if 'FF' in self.rsrcType2indexMap: + self.rFFIdx = self.rsrcType2indexMap['FF'] + elif 'dffeas' in self.rsrcType2indexMap: + self.rFFIdx = self.rsrcType2indexMap['dffeas'] + if 'LUT' in self.rsrcType2indexMap: + self.rLUTIdx = self.rsrcType2indexMap['LUT'] + elif 'lcell_comb' in self.rsrcType2indexMap: + self.rLUTIdx = self.rsrcType2indexMap['lcell_comb'] + elif 'stratixiv_lcell_comb' in self.rsrcType2indexMap: + self.rLUTIdx = self.rsrcType2indexMap['stratixiv_lcell_comb'] + + fVal = [value for key, value in self.rsrcType2indexMap.items() if 'mlab' in key.lower()] + if len(fVal) > 0: + self.rMlabIdx = fVal[0] + + if 'ADD' in self.rsrcType2indexMap: + self.rADDIdx = self.rsrcType2indexMap['ADD'] + if 'IO' in self.rsrcType2indexMap: + self.rIOIdx = self.rsrcType2indexMap['IO'] + elif 'io' in self.rsrcType2indexMap: + self.rIOIdx = self.rsrcType2indexMap['io'] + if 'PLL' in self.rsrcType2indexMap: + self.rPLLIdx= self.rsrcType2indexMap['PLL'] + if 'DSP' in self.rsrcType2indexMap: + self.rDSPIdx = self.rsrcType2indexMap['DSP'] + elif 'DSP48E2' in self.rsrcType2indexMap: + self.rDSPIdx = self.rsrcType2indexMap['DSP48E2'] + if 'BRAM' in self.rsrcType2indexMap: + self.rBRAMIdx = self.rsrcType2indexMap['BRAM'] + elif 'RAMB36E2' in self.rsrcType2indexMap: + self.rBRAMIdx = self.rsrcType2indexMap['RAMB36E2'] + if 'M9K' in self.rsrcType2indexMap: + self.rM9KIdx = self.rsrcType2indexMap['M9K'] + if 'M144K' in self.rsrcType2indexMap: + self.rM144KIdx = self.rsrcType2indexMap['M144K'] + if 'EMPTY' in self.rsrcType2indexMap: + self.rEMPTYIdx = self.rsrcType2indexMap['EMPTY'] + elif 'empty' in self.rsrcType2indexMap: + self.rEMPTYIdx = self.rsrcType2indexMap['empty'] + + self.lg_pin_offset_x = self.pin_offset_x.copy() + self.lg_pin_offset_y = self.pin_offset_y.copy() + #Initialize pin offsets for LUT/FF/IO to 0.0 during legalization + dsp_ram_pin_mask = (self.pin2nodeType_map == self.rDSPIdx) | (self.pin2nodeType_map == self.rBRAMIdx) | (self.pin2nodeType_map == self.rM9KIdx) | (self.pin2nodeType_map == self.rM144KIdx) + self.lg_pin_offset_x[~dsp_ram_pin_mask] = 0.0 + self.lg_pin_offset_y[~dsp_ram_pin_mask] = 0.0 + + #Indices for siteTypes + self.sSLICEIdx = pydb.sliceIdx + self.sDSPIdx = pydb.dspIdx + self.sBRAMIdx = pydb.bramIdx + self.sM9KIdx = pydb.m9kIdx + self.sM144KIdx = pydb.m144kIdx + self.sIOIdx = pydb.ioIdx + self.sPLLIdx = pydb.pllIdx + + self.sliceSiteXYs = np.array(pydb.sliceSiteXYs, dtype=self.dtype) + self.slice_x_min = np.min(self.sliceSiteXYs[:,0]) + self.slice_y_min = np.min(self.sliceSiteXYs[:,1]) + self.slice_x_max = np.max(self.sliceSiteXYs[:,0]) + self.slice_y_max = np.max(self.sliceSiteXYs[:,1]) + + self.dspSiteXYs = np.array(pydb.dspSiteXYs, dtype=self.dtype) + if self.sBRAMIdx != -1 or self.sM9KIdx != -1: + self.ramSite0XYs = np.array(pydb.ramSite0XYs, dtype=self.dtype) + if self.sM144KIdx != -1: + self.ramSite1XYs = np.array(pydb.ramSite1XYs, dtype=self.dtype) + + self.lutName = [key for key, val in self.rsrcType2indexMap.items() if val==self.rLUTIdx][0] + self.slice_lut_capacity=self.siteRsrc2CountMap[self.lutName] + self.SLICE_CAPACITY = self.slice_lut_capacity + self.HALF_SLICE_CAPACITY = self.SLICE_CAPACITY//2 + + self.regions = self.rsrcTypes.shape[0] + self.flat_region_boxes = np.array(pydb.flat_region_boxes, dtype=self.dtype) + self.flat_region_boxes_start = np.array(pydb.flat_region_boxes_start, dtype=np.int32) + self.ctrlSets = np.array(pydb.ctrlSets, dtype=np.int32) + self.flat_ctrlSets = self.ctrlSets.flatten() + self.flop2ctrlSetId_map = np.zeros(self.num_physical_nodes, dtype=np.int32) + self.flop2ctrlSetId_map[self.node2fence_region_map == self.rFFIdx] = np.arange(self.node_count[self.rFFIdx]) + #For 'SHARED' flop ctrls + self.extended_ctrlSets = np.array(pydb.extended_ctrlSets, dtype=np.int32) + self.ext_ctrlSet_start_map = np.array(pydb.ext_ctrlSet_start_map, dtype=np.int32) + + ##Carry chains + self.num_ccNodes = pydb.num_ccNodes + self.num_carry_chains = 0 + #self.flat_cc2node_map = np.array(pydb.flat_cc2node_map, dtype=np.int32) + #self.flat_cc2node_start_map = np.array(pydb.flat_cc2node_start_map, dtype=np.int32) + + if self.num_ccNodes > 0: + self.node2ccId_map = np.array(pydb.node2ccId_map, dtype=np.int32) + self.cc2nodeId_map = np.array(pydb.cc2nodeId_map, dtype=np.int32) + self.cc_element_count= np.array(pydb.cc_element_count, dtype=np.int32) + self.is_cc_node = np.array(pydb.is_cc_node, dtype=np.int32) + self.cc_site_height = np.ceil(self.node_size_y[self.is_cc_node == 1]).astype(np.int32) + + self.org_num_movable_nodes = pydb.org_num_movable_nodes + self.org_num_physical_nodes = pydb.org_num_movable_nodes + pydb.num_terminals + self.org_node_name2id_map = pydb.org_node_name2id_map + self.org_node_names = pydb.org_node_names + self.org_node_types = np.array(pydb.org_node_types, dtype=np.str_) + self.org_node_size_x = np.array(pydb.org_node_size_x, dtype=self.dtype) + self.org_node_size_y = np.array(pydb.org_node_size_y, dtype=self.dtype) + self.org_node_x = np.array(pydb.org_node_x, dtype=self.dtype) + self.org_node_y = np.array(pydb.org_node_y, dtype=self.dtype) + self.org_node_z = np.array(pydb.org_node_z, dtype=np.int32) + self.org_node2fence_region_map = np.array(pydb.org_node2fence_region_map, dtype=np.int32) + self.org_node_count = np.array(pydb.org_node_count, dtype=np.int32) + self.org_is_cc_node = np.array(pydb.org_is_cc_node, dtype=np.int32) + self.org_flop_indices = np.array(pydb.org_flop_indices) + self.org_lut_type = np.array(pydb.org_lut_type).astype(np.int32) + self.org_node2ccId_map = np.array(pydb.org_node2ccId_map, dtype=np.int32) + self.org_pin_offset_x = np.array(pydb.org_pin_offset_x, dtype=self.dtype) + self.org_pin_offset_y = np.array(pydb.org_pin_offset_y, dtype=self.dtype) + self.org_pin2nodeType_map = np.array(pydb.org_pin2nodeType_map, dtype=np.int32) + self.org_node2pincount_map = np.array(pydb.org_node2pincount_map, dtype=np.int32) + self.org_pin2node_map = np.array(pydb.org_pin2node_map, dtype=np.int32) + self.org_node2outpinIdx_map = np.array(pydb.org_node2outpinIdx_map, dtype=np.int32) + self.org_flat_node2pin_map = np.array(pydb.org_flat_node2pin_map, dtype=np.int32) + self.org_flat_node2pin_start_map = np.array(pydb.org_flat_node2pin_start_map, dtype=np.int32) + self.org_flat_cc2node_map = np.array(pydb.org_flat_cc2node_map, dtype=np.int32) + self.org_flat_cc2node_start_map = np.array(pydb.org_flat_cc2node_start_map, dtype=np.int32) + self.org_node2ccId_map = np.array(pydb.org_node2ccId_map, dtype=np.int32) + self.new2org_node_map = np.array(pydb.new2org_node_map, dtype=np.int32) + ## FF Ctrl + self.org_ctrlSets = np.array(pydb.org_ctrlSets, dtype=np.int32) + self.flat_org_ctrlSets = self.org_ctrlSets.flatten() + self.org_flop2ctrlSetId_map = np.zeros(self.org_num_physical_nodes, dtype=np.int32) + self.org_flop2ctrlSetId_map[self.org_node2fence_region_map == self.rFFIdx] = np.arange(self.org_node_count[self.rFFIdx]) + #For 'SHARED' flop ctrls + self.org_extended_ctrlSets = np.array(pydb.org_extended_ctrlSets, dtype=np.int32) + self.org_ext_ctrlSet_start_map = np.array(pydb.org_ext_ctrlSet_start_map, dtype=np.int32) + self.org_is_mlab_node = self.org_lut_type > 9 + + self.org_lg_pin_offset_x = self.org_pin_offset_x.copy() + self.org_lg_pin_offset_y = self.org_pin_offset_y.copy() + #Initialize pin offsets for LUT/FF/IO to 0.0 during legalization + dsp_ram_pin_mask = (self.org_pin2nodeType_map == self.rDSPIdx) | (self.org_pin2nodeType_map == self.rBRAMIdx) | (self.org_pin2nodeType_map == self.rM9KIdx) | (self.org_pin2nodeType_map == self.rM144KIdx) + self.org_lg_pin_offset_x[~dsp_ram_pin_mask] = 0.0 + self.org_lg_pin_offset_y[~dsp_ram_pin_mask] = 0.0 + + + else: + self.is_cc_node = np.zeros(self.num_nodes, dtype=np.int32) + self.org_is_cc_node = np.zeros_like(self.is_cc_node) + + self.num_routing_grids_x = pydb.xh + self.num_routing_grids_y = pydb.yh + self.routing_grid_xl = self.dtype(pydb.routing_grid_xl) + self.routing_grid_yl = self.dtype(pydb.routing_grid_yl) + self.routing_grid_xh = self.dtype(pydb.routing_grid_xh) + self.routing_grid_yh = self.dtype(pydb.routing_grid_yh) + + self.xl = self.dtype(pydb.xl) + self.yl = self.dtype(pydb.yl) + self.xh = self.dtype(pydb.xh) + self.yh = self.dtype(pydb.yh) + + self.ff_ctrl_type = pydb.ff_ctrl_type + self.num_routing_layers = 1 + self.xWirelenWt = pydb.wl_weightX + self.yWirelenWt = pydb.wl_weightY + self.unitPinCap = pydb.pinRouteCap + self.unit_horizontal_capacity = 0.95 * pydb.routeCapH + self.unit_vertical_capacity = 0.95 * pydb.routeCapV + + #Use for debug when node and net names are long + if params.name_map_file_dump == 1: + tt = time.time() + #Dump out design.nodes + content="" + + for nodeId in range(len(self.node_names)): + upd_node_name = "inst_"+str(nodeId) + content += "%s %s\n" % (upd_node_name, self.node_types[nodeId]) + + mNames_file = "mapped_design.nodes" + with open(mNames_file, "w") as f: + f.write(content) + logging.info("write out node name mapping to %s took %.3f seconds" % (mNames_file, time.time()-tt)) + + #Dump out design.nets + content="" + + for netId in range(len(self.net_names)): + upd_net_name = "net_"+str(netId) + content += "net %s %d\n" % (upd_net_name, self.net2pincount_map[netId]) + + n2pStart = self.flat_net2pin_start_map[netId] + n2pEnd = self.flat_net2pin_start_map[netId+1] + + for pId in range(n2pStart, n2pEnd): + pinId = self.flat_net2pin_map[pId] + nodeId = self.pin2node_map[pinId] + upd_node_name = "inst_"+str(nodeId) + content += "\t%s %s\n" % (upd_node_name, self.pin_names[pinId]) + + content += "endnet\n" + + mNets_file = "mapped_design.nets" + with open(mNets_file, "w") as f: + f.write(content) + logging.info("write out node net mapping to %s took %.3f seconds" % (mNets_file, time.time()-tt)) + + + def print_node(self, node_id): + """ + @brief print node information + @param node_id cell index + """ + logging.debug("node %s(%d), size (%g, %g), pos (%g, %g)" % (self.node_names[node_id], node_id, self.node_size_x[node_id], self.node_size_y[node_id], self.node_x[node_id], self.node_y[node_id])) + pins = "pins " + for pin_id in self.node2pin_map[node_id]: + pins += "%s(%s, %d) " % (self.node_names[self.pin2node_map[pin_id]], self.net_names[self.pin2net_map[pin_id]], pin_id) + logging.debug(pins) + + def print_net(self, net_id): + """ + @brief print net information + @param net_id net index + """ + logging.debug("net %s(%d)" % (self.net_names[net_id], net_id)) + pins = "pins " + for pin_id in self.net2pin_map[net_id]: + pins += "%s(%s, %d) " % (self.node_names[self.pin2node_map[pin_id]], self.net_names[self.pin2net_map[pin_id]], pin_id) + logging.debug(pins) + + def flatten_nested_map(self, net2pin_map): + """ + @brief flatten an array of array to two arrays like CSV format + @param net2pin_map array of array + @return a pair of (elements, cumulative column indices of the beginning element of each row) + """ + # flat netpin map, length of #pins + flat_net2pin_map = np.zeros(len(pin2net_map), dtype=np.int32) + # starting index in netpin map for each net, length of #nets+1, the last entry is #pins + flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(net2pin_map)): + flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] + flat_net2pin_start_map[i] = count + count += len(net2pin_map[i]) + assert flat_net2pin_map[-1] != 0 + flat_net2pin_start_map[len(net2pin_map)] = len(pin2net_map) + + return flat_net2pin_map, flat_net2pin_start_map + + def __call__(self, params): + """ + @brief top API to read placement files + @param params parameters + """ + tt = time.time() + + self.read(params) + self.initialize(params) + + logging.info("reading benchmark takes %g seconds" % (time.time()-tt)) + + def calc_num_filler_for_fence_region(self, region_id, node2fence_region_map, filler_size_x, filler_size_y): + ''' + @description: calculate number of fillers for each fence region + @param fence_regions{type} + @return: + ''' + fence_region_mask = (node2fence_region_map == region_id) + + if region_id in self.fixed_rsrcIds: + return 0, 0, self.num_terminals + + #If no cells of particular resourceType + if np.sum(fence_region_mask) == 0: + return 0, 0, 0.0 + + movable_node_size_x = self.node_size_x[fence_region_mask] + movable_node_size_y = self.node_size_y[fence_region_mask] + + #Calcuation based on region size + region = self.flat_region_boxes[self.flat_region_boxes_start[region_id]:self.flat_region_boxes_start[region_id+1]] + placeable_area = np.sum((region[:, 2]-region[:, 0])*(region[:, 3]-region[:, 1])) + + total_movable_node_area = np.sum(movable_node_size_x*movable_node_size_y) + + total_filler_node_area = max(placeable_area-total_movable_node_area, 0.0) + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + #Add pseudo filler nodes for FFs + if region_id == self.rFFIdx and self.num_mlab_nodes > 0: + total_filler_node_area -= self.num_mlab_nodes + num_filler = int(math.floor(total_filler_node_area/(filler_size_x*filler_size_y))) + self.num_mlab_nodes + else: + num_filler = int(math.floor(total_filler_node_area/(filler_size_x*filler_size_y))) + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + if region_id == self.rLUTIdx and self.num_mlab_nodes > 0: + logging.info("Region: %d [%s] #movable_nodes = %d (%s) + %d (mlab) = %d, movable_node_area = %.1f, placeable_area = %.1f, filler_node_area = %.1f, #fillers = %d, filler size = %.4g x %g\n" + % (region_id, self.rsrcTypes[region_id], fence_region_mask.sum()-self.num_mlab_nodes, self.rsrcTypes[region_id], self.num_mlab_nodes, fence_region_mask.sum(), total_movable_node_area, placeable_area, total_filler_node_area, num_filler, filler_size_x, filler_size_y)) + else: + logging.info("Region: %d [%s] #movable_nodes = %d movable_node_area = %.1f, placeable_area = %.1f, filler_node_area = %.1f, #fillers = %d, filler size = %.4g x %g\n" + % (region_id, self.rsrcTypes[region_id], fence_region_mask.sum(), total_movable_node_area, placeable_area, total_filler_node_area, num_filler, filler_size_x, filler_size_y)) + + #Ensure there is sufficient space available for placement + if total_movable_node_area > placeable_area: + logging.error("Provided %d x %d site_map is not large enough to accomodate all %s instances. Use a larger site_map.\n" % (self.num_sites_x, self.num_sites_y, self.rsrcTypes[region_id])) + sys.exit(0) + + return num_filler, total_movable_node_area, np.sum(fence_region_mask) + + + def initialize(self, params): + """ + @brief initialize data members after reading + @param params parameters + """ + self.resource_size_x = self.siteWidths + self.resource_size_y = self.siteHeights + + #Parameter initialization - Can be changed later through params + if self.xWirelenWt == None or self.xWirelenWt == 0: + self.xWirelenWt = 1.0 + if self.yWirelenWt == None or self.yWirelenWt == 0: + self.yWirelenWt = 1.0 + self.instDemStddevTrunc = 2.5 + + #Resource area parameter + self.gpInstStddev = math.sqrt(2.5e-4 * self.num_nodes) / (2.0 * self.instDemStddevTrunc) + self.gpInstStddevTrunc = self.instDemStddevTrunc + + self.instDemStddevX = self.gpInstStddev + self.instDemStddevY = self.gpInstStddev + + #Parameter for Direct Legalization + self.nbrDistEnd = 1.2 * self.gpInstStddev * self.gpInstStddevTrunc + + # Routability and pin density optimization parameters + self.unitPinCap = 0 + + #Area type parameters - Consider default fillerstrategy of FIXED_SHAPE + self.filler_size_x = np.zeros(self.regions - self.fixed_rsrcIds.size) + self.filler_size_y = np.zeros(self.regions - self.fixed_rsrcIds.size) + self.targetOverflow = np.zeros(self.regions - self.fixed_rsrcIds.size) + self.overflowInstDensityStretchRatio = np.zeros(self.regions - self.fixed_rsrcIds.size) + self.node_area_adjust_overflow = np.ones_like(self.targetOverflow) + self.node_area_adjust_overflow *= params.node_area_adjust_overflow + + self.rsrc2compId_map = np.ones(self.regions, dtype=np.int32) + self.rsrc2compId_map *= -1 + self.comp2rsrcId_map = np.ones_like(self.rsrc2compId_map) + self.comp2rsrcId_map *= -1 + + tId = 0 + for rId in range(self.regions): + if rId not in self.fixed_rsrcIds: + if self.node_count[rId] > 0: + #Do not consider large instances such as carry chains while determining filler sizes + largeNodes = self.is_cc_node[self.node2fence_region_map == rId] + if rId == self.rLUTIdx: + largeNodes |= self.is_mlab_node[self.node2fence_region_map == rId] + max_x = np.max(self.node_size_x[self.node2fence_region_map == rId][largeNodes == 0]) + max_y = np.max(self.node_size_y[self.node2fence_region_map == rId][largeNodes == 0]) + self.filler_size_x[tId] = math.sqrt(round(max_x*max_x,4)) + self.filler_size_y[tId] = math.sqrt(round(max_y*max_y,4)) + if max_x < 1.0 and max_y < 1.0: + self.targetOverflow[tId] = 0.1 + self.overflowInstDensityStretchRatio[tId] = math.sqrt(2.0) + else: + self.targetOverflow[tId] = 0.2 + self.node_area_adjust_overflow[tId] = 0.25 + self.rsrc2compId_map[rId] = tId + self.comp2rsrcId_map[tId] = rId + tId = tId + 1 + + ##Set FF filler size to be same as LUT + self.filler_size_x[self.rFFIdx] = self.filler_size_x[self.rLUTIdx] + self.filler_size_y[self.rFFIdx] = self.filler_size_y[self.rLUTIdx] + + #Resize based on available resources in the design + maxVal = self.rsrc2compId_map.max()+1 + self.filler_size_x = self.filler_size_x[:maxVal] + self.filler_size_y = self.filler_size_y[:maxVal] + self.targetOverflow = self.targetOverflow[:maxVal] + self.overflowInstDensityStretchRatio = self.overflowInstDensityStretchRatio[:maxVal] + + if self.rLUTIdx != -1 and self.rsrc2compId_map[self.rLUTIdx] != -1: + self.slice_compIds.append(self.rsrc2compId_map[self.rLUTIdx]) + if self.rFFIdx != -1 and self.rsrc2compId_map[self.rFFIdx] != -1: + self.slice_compIds.append(self.rsrc2compId_map[self.rFFIdx]) + + if self.rDSPIdx != -1 and self.rsrc2compId_map[self.rDSPIdx] != -1: + self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rDSPIdx]) + if self.rBRAMIdx != -1 and self.rsrc2compId_map[self.rBRAMIdx] != -1: + self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rBRAMIdx]) + if self.rM9KIdx != -1 and self.rsrc2compId_map[self.rM9KIdx] != -1: + self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rM9KIdx]) + if self.rM144KIdx != -1 and self.rsrc2compId_map[self.rM144KIdx] != -1: + self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rM144KIdx]) + + #set number of bins + self.num_bins_x = params.num_bins_x + self.num_bins_y = params.num_bins_y + self.bin_size_x = self.width/self.num_bins_x + self.bin_size_y = self.height/self.num_bins_y + + # set total cell area + self.total_movable_node_area = self.dtype(np.sum(self.lut_flop_mask)*self.filler_size_x[self.rLUTIdx]*self.filler_size_y[self.rLUTIdx]) + if self.dsp_ram_mask.sum() > 0: + self.total_movable_node_area += self.dtype(np.sum(self.node_size_x[self.dsp_ram_mask]*self.node_size_y[self.dsp_ram_mask])) + + # total fixed node area should exclude the area outside the layout and the area of terminal_NIs + self.total_fixed_node_area = self.dtype(self.num_terminals) + self.total_space_area = self.width * self.height + + self.region_boxes = [] + + #For FPGA, the regions are fixed for each resourceType + for region_id in range(self.regions): + idx = self.rsrc2compId_map[region_id] + if idx != -1: + region = self.flat_region_boxes[self.flat_region_boxes_start[region_id]:self.flat_region_boxes_start[region_id+1]] + self.region_boxes.append(region) + + # insert filler nodes + ### calculate fillers for different resourceTypes + self.filler_size_x_fence_region = [] + self.filler_size_y_fence_region = [] + self.num_filler_nodes = 0 + self.num_filler_nodes_fence_region = [] + self.num_movable_nodes_fence_region = [] + self.total_movable_node_area_fence_region = [] + self.target_density_fence_region = [] + self.filler_start_map = None + filler_node_size_x_list = [] + filler_node_size_y_list = [] + self.total_filler_node_area = 0 + + for idx in range(self.regions): + i = self.rsrc2compId_map[idx] + if i != -1: + num_filler_i, total_movable_node_area_i, num_movable_nodes_i = self.calc_num_filler_for_fence_region(idx, self.node2fence_region_map, + self.filler_size_x[i], self.filler_size_y[i]) + self.num_movable_nodes_fence_region.append(num_movable_nodes_i) + self.num_filler_nodes_fence_region.append(num_filler_i) + self.total_movable_node_area_fence_region.append(total_movable_node_area_i) + self.target_density_fence_region.append(self.targetOverflow[i]) + self.filler_size_x_fence_region.append(self.filler_size_x[i]) + self.filler_size_y_fence_region.append(self.filler_size_y[i]) + self.num_filler_nodes += num_filler_i + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + #Add pseudo filler nodes for FFs + if i == self.rFFIdx and self.num_mlab_nodes > 0: + filler_count = num_filler_i - self.num_mlab_nodes + tmp_filler_size_x = np.full(self.num_mlab_nodes, fill_value=self.rsrcInstWidths[self.rMlabIdx], dtype=self.node_size_x.dtype) + tmp_filler_size_x = np.concatenate((tmp_filler_size_x, np.full(filler_count, fill_value=self.filler_size_x[i], dtype=self.node_size_x.dtype))) + filler_node_size_x_list.append(tmp_filler_size_x) + tmp_filler_size_y = np.full(self.num_mlab_nodes, fill_value=self.rsrcInstHeights[self.rMlabIdx], dtype=self.node_size_y.dtype) + tmp_filler_size_y = np.concatenate((tmp_filler_size_y, np.full(filler_count, fill_value=self.filler_size_y[i], dtype=self.node_size_y.dtype))) + filler_node_size_y_list.append(tmp_filler_size_y) + filler_node_area_i = filler_count * (self.filler_size_x[i]*self.filler_size_y[i]) + self.num_mlab_nodes + else: + filler_node_size_x_list.append(np.full(num_filler_i, fill_value=self.filler_size_x[i], dtype=self.node_size_x.dtype)) + filler_node_size_y_list.append(np.full(num_filler_i, fill_value=self.filler_size_y[i], dtype=self.node_size_y.dtype)) + filler_node_area_i = num_filler_i * (self.filler_size_x[i]*self.filler_size_y[i]) + self.total_filler_node_area += filler_node_area_i + + for rId in self.fixed_rsrcIds: + if self.node_count[rId] > 0: + logging.info("Region: %d [%s] #fixed_nodes = %d \n" + % (rId, self.rsrcTypes[rId], self.node_count[rId])) + + self.total_movable_node_area_fence_region = np.array(self.total_movable_node_area_fence_region, dtype=self.dtype) + self.num_movable_nodes_fence_region = np.array(self.num_movable_nodes_fence_region, dtype=np.int32) + + if params.enable_fillers: + # the way to compute this is still tricky; we need to consider place_io together on how to + # summarize the area of fixed cells, which may overlap with each other. + self.filler_start_map = np.cumsum([0]+self.num_filler_nodes_fence_region) + self.num_filler_nodes_fence_region = np.array(self.num_filler_nodes_fence_region, dtype=np.int32) + self.node_size_x = np.concatenate([self.node_size_x] + filler_node_size_x_list) + self.node_size_y = np.concatenate([self.node_size_y] + filler_node_size_y_list) + else: + self.total_filler_node_area = 0 + self.num_filler_nodes = 0 + filler_size_x, filler_size_y = 0, 0 + if(len(self.region_boxes) > 0): + self.filler_start_map = np.zeros(len(self.region_boxes)+1, dtype=np.int32) + self.num_filler_nodes_fence_region = np.zeros(len(self.num_filler_nodes_fence_region), dtype=np.int32) + + #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished + if self.num_mlab_nodes > 0: + self.is_mlab_filler_node = np.zeros(self.num_nodes, dtype=np.int32) + self.is_mlab_filler_node[self.filler_start_map[self.rFFIdx]:self.filler_start_map[self.rFFIdx]+self.num_mlab_nodes] = 1 + + def write(self, pl_file): + """ + @brief write placement solution as .pl file + @Use as intermediate - does not contain VPR output format + @param pl_file .pl file + """ + tt = time.time() + #logging.info("writing to %s" % (pl_file)) + + if self.num_ccNodes == 0: + node_x = self.node_x + node_y = self.node_y + node_z = self.node_z + str_node_names = self.node_names + node_area = self.node_size_x*self.node_size_y + else: + node_x = self.org_node_x + node_y = self.org_node_y + node_z = self.org_node_z + str_node_names = self.org_node_names + node_area = self.org_node_size_x*self.org_node_size_y + + #cc_length = self.cc_site_height + #cc_node_x = node_x[self.is_cc_node == 1].astype(np.int32) + #cc_node_y = node_y[self.is_cc_node == 1].astype(np.int32) + + content = "" + + for i in range(self.num_physical_nodes): + #if self.is_cc_node[i] == 1: + # ccId = self.node2ccId_map[i] + # ccXloc = cc_node_x[ccId] + # #solution is the starting Slice + # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 + # ccZloc = 0 + # ccElArea = round(self.node_size_y[i]/self.cc_element_count[ccId], 4) + # rStart = self.org_flat_cc2node_start_map[ccId] + # rEnd = self.org_flat_cc2node_start_map[ccId+1] + # for mId in range(rStart, rEnd): + # orgNodeId = self.org_flat_cc2node_map[mId] + # content += "%s %.6E %.6E %g %.6E" % ( + # self.org_node_names[orgNodeId], + # ccXloc, + # currY, + # ccZloc, + # ccElArea + # ) + # if mId < rEnd-1: + # content += "\n" + # ccZloc = ccZloc+1 + # if ccZloc == self.slice_lut_capacity: + # ccZloc = 0; + # currY = currY-1 + #else: + content += "%s %.6E %.6E %g %.6E" % ( + str_node_names[i], + node_x[i], + node_y[i], + node_z[i], + node_area[i] + ) + if i < self.num_physical_nodes-1: + content += "\n" + + with open(pl_file, "w") as f: + f.write(content) + logging.info("write placement solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) + + def writeFinalSolution(self, pl_file): + """ + @brief write placement solution as .pl file + @param pl_file .pl file + """ + tt = time.time() + #logging.info("writing to %s" % (pl_file)) + + if self.num_ccNodes == 0: + node_x = self.node_x + node_y = self.node_y + node_z = self.node_z + node_types = self.node_types + str_node_names = self.node_names + node2fence_region_map = self.node2fence_region_map + else: + node_x = self.org_node_x + node_y = self.org_node_y + node_z = self.org_node_z + node_types = self.org_node_types + str_node_names = self.org_node_names + node2fence_region_map = self.org_node2fence_region_map + + #cc_length = self.cc_site_height + #cc_node_x = node_x[self.is_cc_node == 1].astype(np.int32) + #cc_node_y = node_y[self.is_cc_node == 1].astype(np.int32) + + content = "" + #node_area = self.node_size_x*self.node_size_y + + if self.siteOutValues.shape[0] == 0: + for i in range(self.num_physical_nodes): + #if self.is_cc_node[i] == 1: + # ccId = self.node2ccId_map[i] + # ccXloc = cc_node_x[ccId] + # #solution is the starting Slice + # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 + # ccZloc = 0 + # rStart = self.org_flat_cc2node_start_map[ccId] + # rEnd = self.org_flat_cc2node_start_map[ccId+1] + # for mId in range(rStart, rEnd): + # orgNodeId = self.org_flat_cc2node_map[mId] + # content += "%s %d %d %g" % ( + # self.org_node_names[orgNodeId], + # ccXloc, + # currY, + # ccZloc + # ) + # if mId < rEnd-1: + # content += "\n" + # ccZloc = ccZloc+1 + # if ccZloc == self.slice_lut_capacity: + # ccZloc = 0; + # currY = currY-1 + #else: + content += "%s %d %d %g" % ( + str_node_names[i], + node_x[i], + node_y[i], + node_z[i] + ) + if i < self.num_physical_nodes-1: + content += "\n" + else: + for i in range(self.num_physical_nodes): + #if self.is_cc_node[i] == 1: + # siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[node2fence_region_map[i]]]] + # zVal = 0 + # sVal = 0 + # ccId = self.node2ccId_map[i] + # ccXloc = cc_node_x[ccId] + # #solution is the starting Slice + # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 + # ccZloc = 0 + # ccElArea = round(self.node_size_y[i]/self.cc_element_count[ccId], 4) + # rStart = self.org_flat_cc2node_start_map[ccId] + # rEnd = self.org_flat_cc2node_start_map[ccId+1] + # for mId in range(rStart, rEnd): + # orgNodeId = self.org_flat_cc2node_map[mId] + # if self.siteOutCoordinates[siteId] == 'z': + # sVal = ccZloc + # zVal = self.siteOutValues[siteId] + # elif self.siteOutCoordinates[siteId] == 's': + # zVal = ccZloc + # sVal = self.siteOutValues[siteId] + # content += "%s %d %d %g %g %s" % ( + # self.org_node_names[orgNodeId], + # ccXloc, + # currY, + # zVal, + # sVal, + # self.org_node_types[orgNodeId] + # ) + # if mId < rEnd-1: + # content += "\n" + # ccZloc = ccZloc+1 + # if ccZloc == self.slice_lut_capacity: + # ccZloc = 0; + # currY = currY-1 + #else: + siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[node2fence_region_map[i]]]] + zVal = 0 + sVal = 0 + if self.siteOutCoordinates[siteId] == 'z': + sVal = node_z[i] + zVal = self.siteOutValues[siteId] + elif self.siteOutCoordinates[siteId] == 's': + zVal = node_z[i] + sVal = self.siteOutValues[siteId] + content += "%s %d %d %g %g %s" % ( + str_node_names[i], + node_x[i], + node_y[i], + zVal, + sVal, + node_types[i] + ) + if i < self.num_physical_nodes-1: + content += "\n" + + with open(pl_file, "w") as f: + f.write(content) + logging.info("write placement solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) + + #Use for debug - Does not support macro mode + def writeMapSolution(self): + """ + @brief write mapped placement solution as .pl file + """ + tt = time.time() + #logging.info("writing to %s" % (pl_file)) + + node_x = self.node_x + node_y = self.node_y + node_z = self.node_z + + pl_file = "mapped_design_final.pl" + + content = "" + + if self.siteOutValues.shape[0] == 0: + for i in range(self.num_physical_nodes): + content += "%s %d %d %g" % ( + "inst_"+str(i), + node_x[i], + node_y[i], + node_z[i] + ) + if i < self.num_physical_nodes-1: + content += "\n" + else: + for i in range(self.num_physical_nodes): + siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[self.node2fence_region_map[i]]]] + zVal = 0 + sVal = 0 + if self.siteOutCoordinates[siteId] == 'z': + sVal = node_z[i] + zVal = self.siteOutValues[siteId] + elif self.siteOutCoordinates[siteId] == 's': + zVal = node_z[i] + sVal = self.siteOutValues[siteId] + content += "%s %d %d %g %g %s" % ( + "inst_"+str(i), + node_x[i], + node_y[i], + zVal, + sVal, + self.node_types[i] + ) + if i < self.num_physical_nodes-1: + content += "\n" + + with open(pl_file, "w") as f: + f.write(content) + logging.info("write out mapped solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) + + def apply(self, node_x, node_y, node_z): + """ + @brief apply placement solution and update database + """ + + if self.num_ccNodes == 0: + # assign solution + self.node_x[:self.num_movable_nodes] = node_x[:self.num_movable_nodes] + self.node_y[:self.num_movable_nodes] = node_y[:self.num_movable_nodes] + self.node_z[:self.num_movable_nodes] = node_z[:self.num_movable_nodes] + node_x = self.node_x + node_y = self.node_y + node_z = self.node_z + else: + # assign solution + self.org_node_x[:self.num_movable_nodes] = node_x[:self.num_movable_nodes] + self.org_node_y[:self.num_movable_nodes] = node_y[:self.num_movable_nodes] + self.org_node_z[:self.num_movable_nodes] = node_z[:self.num_movable_nodes] + node_x = self.org_node_x + node_y = self.org_node_y + node_z = self.org_node_z + + # update raw database + place_io.PlaceIOFunction.apply(self.rawdb, node_x.astype(self.dtype), node_y.astype(self.dtype), node_z.astype(np.int32)) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + logging.error("One input parameters in json format in required") + + params = Params.Params() + params.load(sys.argv[sys.argv[1]]) + logging.info("parameters = %s" % (params)) + + db = PlaceDB() + db(params) + + db.print_node(1) + db.print_net(1) + db.print_row(1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py new file mode 100644 index 00000000..acd13916 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py @@ -0,0 +1,876 @@ +## +# @file PlaceObj.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief Placement model class defining the placement objective. +# + +import os +import sys +import time +import numpy as np +import itertools +import logging +import torch +import torch.autograd as autograd +import torch.nn as nn +import torch.nn.functional as F +import pdb +import gzip +import math +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle +import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength as weighted_average_wirelength +#import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength as logsumexp_wirelength +import dreamplacefpga.ops.electric_potential.electric_potential as electric_potential +import dreamplacefpga.ops.rudy.rudy as rudy +import dreamplacefpga.ops.pin_utilization.pin_utilization as pin_utilization +#FPGA clustering compatibility resource area computation +import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility as clustering_compatibility +import dreamplacefpga.ops.adjust_node_area.adjust_node_area as adjust_node_area + +# For FPGA +class PreconditionOpFPGA: + """Preconditioning engine is critical for convergence. + Need to be carefully designed. + """ + def __init__(self, placedb, data_collections): + self.placedb = placedb + self.data_collections = data_collections + self.iteration = 0 + self.filler2fence_region_map = torch.zeros(placedb.num_filler_nodes, device=data_collections.pos[0].device, dtype=torch.long) + self.movablenode2fence_region_map_clamp = data_collections.node2fence_region_map[:placedb.num_movable_nodes].long() + for idx in range(placedb.regions): + i = placedb.rsrc2compId_map[idx] + if i != -1: + filler_beg, filler_end = placedb.filler_start_map[i:i+2] + self.filler2fence_region_map[filler_beg:filler_end] = i + #Adjust movablenode2fence_region_map from rsrcId to compId + if idx != i: + self.movablenode2fence_region_map_clamp[data_collections.node2fence_region_map[:placedb.num_movable_nodes] == idx] = i + + def __call__(self, grad, density_weight, precondWL, update_mask=None): + """Introduce alpha parameter to avoid divergence. + It is tricky for this parameter to increase. + """ + with torch.no_grad(): + #FPGA Preconditioning + node_areas = self.data_collections.node_areas.clone() + + for imk in range(self.placedb.regions): + mk = self.placedb.rsrc2compId_map[imk] + if mk != -1: + mask = self.data_collections.node2fence_region_map[:self.placedb.num_movable_nodes] == imk + node_areas[:self.placedb.num_movable_nodes].masked_scatter_(mask, node_areas[:self.placedb.num_movable_nodes][mask]*density_weight[mk]) + filler_beg, filler_end = self.placedb.filler_start_map[mk:mk+2] + node_areas[self.placedb.num_nodes-self.placedb.num_filler_nodes+filler_beg:self.placedb.num_nodes-self.placedb.num_filler_nodes+filler_end] *= density_weight[mk] + + precond = precondWL + node_areas + #Use alpha to avoid divergence + #precond = precondWL + self.alpha * node_areas + + precond.clamp_(min=1.0) + grad[0:self.placedb.num_nodes].div_(precond) + grad[self.placedb.num_nodes:self.placedb.num_nodes * + 2].div_(precond) + + #print("Overall preconditioned grad norm1: %g" %(grad.norm(p=1))) + ### stop gradients for terminated electric field + if(update_mask is not None): + grad = grad.view(2, -1) + update_mask = ~update_mask + if update_mask.sum() < len(update_mask): + movable_mask = update_mask[self.movablenode2fence_region_map_clamp] + filler_mask = update_mask[self.filler2fence_region_map] + grad[0, :self.placedb.num_movable_nodes].masked_fill_(movable_mask, 0) + grad[1, :self.placedb.num_movable_nodes].masked_fill_(movable_mask, 0) + grad[0, self.placedb.num_nodes-self.placedb.num_filler_nodes:].masked_fill_(filler_mask, 0) + grad[1, self.placedb.num_nodes-self.placedb.num_filler_nodes:].masked_fill_(filler_mask, 0) + grad = grad.view(-1) + self.iteration += 1 + + return grad + +class PlaceObjFPGA(nn.Module): + """ + @brief Define placement objective: + wirelength + density_weight * density penalty + It includes various ops related to global placement as well. + """ + def __init__(self, density_weight, params, placedb, data_collections, op_collections, global_place_params): + """ + @brief initialize ops for placement + @param density_weight density weight in the objective + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + @param op_collections a collection of all ops + @param global_place_params global placement parameters for current global placement stage + """ + super(PlaceObjFPGA, self).__init__() + + ### quadratic penalty + self.density_quad_coeff = 1000 #corresponds to beta/2 in obj function + self.quad_penalty_coeff = None + self.init_density = None + ### increase density penalty if slow convergence + self.density_factor = 1 + + ### fence region will enable quadratic penalty by default + self.quad_penalty = True + + ### fence region + ### update mask controls whether stop gradient/updating, 1 represents allow grad/update + self.update_mask = None + self.lock_mask = None + ### for subregion rough legalization, once stop updating, perform immediate greedy legalization once + ### this is to avoid repeated legalization + ### 1 represents already legal + self.legal_mask = torch.zeros(placedb.regions) + self.legal_mask[placedb.fixed_rsrcIds] = 1 #IOs are fixed + + self.params = params + self.placedb = placedb + self.data_collections = data_collections + self.op_collections = op_collections + self.global_place_params = global_place_params + + self.fixedDemMaps = [] + + self.gpu = params.gpu + self.precondWL = self.op_collections.precondwl_op() + if placedb.num_ccNodes > 0: + self.lg_precondWL = self.op_collections.lg_precondition_op() + self.fixedDemMaps = self.op_collections.demandMap_op() + + ### different fence region needs different density weights in multi-electric field algorithm + self.density_weight = torch.tensor( + [density_weight]*(placedb.targetOverflow.size), + dtype=self.data_collections.dtype, + device=self.data_collections.device) + ### Note: even for multi-electric fields, they use the same gamma + self.gamma = torch.tensor(self.base_gamma(params, placedb)[0], + dtype=self.data_collections.dtype, + device=self.data_collections.device) + initOverflow = torch.ones(placedb.targetOverflow.size, dtype=self.gamma.dtype, device=self.gamma.device) + self.update_gamma(0, initOverflow, self.base_gamma(params, placedb)) + + # compute weighted average wirelength from position + self.num_bins_x = placedb.num_bins_x + self.num_bins_y = placedb.num_bins_y + + self.name = "%dx%d bins" % (self.num_bins_x, self.num_bins_y) + + self.op_collections.wirelength_op, self.op_collections.update_gamma_op = self.build_weighted_average_wl( + params, placedb, self.data_collections, self.op_collections.pin_pos_op) + + self.op_collections.density_op = self.build_electric_potential( + params, + placedb, + self.data_collections, + self.num_bins_x, + self.num_bins_y, + name=self.name) + + ### build multiple density op for multi-electric field + self.op_collections.fence_region_density_ops, self.op_collections.fence_region_density_merged_op, self.op_collections.fence_region_density_overflow_merged_op = self.build_multi_fence_region_density_op() + + self.op_collections.update_density_weight_op = self.build_update_density_weight(params, placedb) + + self.op_collections.precondition_op = self.build_precondition(params, placedb, self.data_collections) + + self.op_collections.noise_op = self.build_noise(params, placedb, self.data_collections) + + if params.routability_opt_flag: + # compute congestion map, RISA/RUDY congestion map + self.op_collections.route_utilization_map_op = self.build_route_utilization_map(params, placedb, self.data_collections) + self.op_collections.pin_utilization_map_op = self.build_pin_utilization_map(params, placedb, self.data_collections) + #FPGA clustering compatibility resource area computation + self.op_collections.clustering_compatibility_lut_area_op = self.build_clustering_compatibility_lut_map(params, placedb, self.data_collections) + self.op_collections.clustering_compatibility_ff_area_op = self.build_clustering_compatibility_ff_map(params, placedb, self.data_collections) + # adjust instance area with congestion map + self.op_collections.adjust_node_area_op = self.build_adjust_node_area(params, placedb, self.data_collections) + + self.Lgamma_iteration = global_place_params["iteration"] + if 'Llambda_density_weight_iteration' in global_place_params: + self.Llambda_density_weight_iteration = global_place_params['Llambda_density_weight_iteration'] + else: + self.Llambda_density_weight_iteration = 1 + if 'Lsub_iteration' in global_place_params: + self.Lsub_iteration = global_place_params['Lsub_iteration'] + else: + self.Lsub_iteration = 1 + if 'routability_Lsub_iteration' in global_place_params: + self.routability_Lsub_iteration = global_place_params['routability_Lsub_iteration'] + else: + self.routability_Lsub_iteration = self.Lsub_iteration + self.start_fence_region_density = False + + def obj_fn(self, pos): + """ + @brief Compute objective. + wirelength + density_weight * density penalty + @param pos locations of cells + @return objective value + """ + wirelength = self.op_collections.wirelength_op(pos) + + density = self.op_collections.fence_region_density_merged_op(pos) + + if self.init_density is None: + ### record initial density + self.init_density = density.data.clone() + ### density weight subgradient preconditioner + self.density_weight_grad_precond = self.init_density.masked_scatter(self.init_density != 0, 1/self.init_density[self.init_density != 0]) + ### quadratic density penalty + if self.quad_penalty_coeff is None: + self.quad_penalty_coeff = self.density_quad_coeff/2 * self.density_weight_grad_precond + + density = density*(1+self.quad_penalty_coeff * density) + + result = wirelength + self.density_weight_u.dot(density) + #logging.info("result: %g" %(result)) + + return result + + def obj_and_grad_fn(self, pos): + """ + @brief compute objective and gradient. + wirelength + density_weight * density penalty + @param pos locations of cells + @return objective value + """ + #self.check_gradient(pos) + if pos.grad is not None: + pos.grad.zero_() + + obj = self.obj_fn(pos) + obj.backward() + + self.op_collections.precondition_op(pos.grad, self.density_weight, self.precondWL, self.update_mask) + + return obj, pos.grad + + def forward(self): + """ + @brief Compute objective with current locations of cells. + """ + return self.obj_fn(self.data_collections.pos[0]) + + def check_gradient(self, pos): + """ + @brief check gradient for debug + @param pos locations of cells + """ + wirelength = self.op_collections.wirelength_op(pos) + + if pos.grad is not None: + pos.grad.zero_() + wirelength.backward() + wirelength_grad = pos.grad.clone() + + pos.grad.zero_() + density = self.density_weight * self.op_collections.density_op(pos) + density.backward() + density_grad = pos.grad.clone() + + wirelength_grad_norm = wirelength_grad.norm(p=1) + density_grad_norm = density_grad.norm(p=1) + + pos.grad.zero_() + + def estimate_initial_learning_rate(self, x_k): + """ + @brief Estimate initial learning rate by moving a small step. + Computed as | x_k - x_k_1 |_2 / | g_k - g_k_1 |_2. + @param x_k current solution + """ + obj_k, g_k = self.obj_and_grad_fn(x_k) + lr = 0.001 * min((self.placedb.xh - self.placedb.xl), (self.placedb.yh-self.placedb.yl)) * (self.placedb.num_nodes - self.placedb.num_terminals) + lr /= g_k.norm(p=1) + x_k_1 = torch.autograd.Variable(x_k - lr * g_k, requires_grad=True) + obj_k_1, g_k_1 = self.obj_and_grad_fn(x_k_1) + #print("alpha = %g"%(lr)) + #print("learning rate = %g"%((x_k - x_k_1).norm(p=2) / (g_k - g_k_1).norm(p=2))) + + return (x_k - x_k_1).norm(p=2) / (g_k - g_k_1).norm(p=2) + + def build_weighted_average_wl(self, params, placedb, data_collections, pin_pos_op): + """ + @brief build the op to compute weighted average wirelength + @param params parameters + @param placedb placement database + @param data_collections a collection of data and variables required for constructing ops + @param pin_pos_op the op to compute pin locations according to cell locations + """ + # use WeightedAverageWirelength atomic + wirelength_for_pin_op = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=data_collections.flat_net2pin_map, + netpin_start=data_collections.flat_net2pin_start_map, + pin2net_map=data_collections.pin2net_map, + net_weights=data_collections.net_weights, + num_carry_chains=placedb.num_carry_chains, + cc_net_weight=placedb.carry_chain_net_weight, + dir_net_weight=params.dir_net_weight, + net_mask=data_collections.net_mask_ignore_large_degrees, + pin_mask=data_collections.pin_mask_ignore_fixed_macros, + gamma=self.gamma, + net_bounding_box_min=data_collections.net_bounding_box_min, + net_bounding_box_max=data_collections.net_bounding_box_max, + num_threads=params.num_threads, + algorithm='merged') + #algorithm='net-by-net') + + # wirelength for position + def build_wirelength_op(pos): + return wirelength_for_pin_op(pin_pos_op(pos)) + + # update gamma + base_gamma = self.base_gamma(params, placedb) + + def build_update_gamma_op(iteration, overflow): + self.update_gamma(iteration, overflow, base_gamma) + #logging.debug("update gamma to %g" % (wirelength_for_pin_op.gamma.data)) + + return build_wirelength_op, build_update_gamma_op + + def build_electric_potential(self, params, placedb, data_collections, + num_bins_x, num_bins_y, name, region_id=None, fence_regions=None): + """ + @brief e-place electrostatic potential + @param params parameters + @param placedb placement database + @param data_collections a collection of data and variables required for constructing ops + @param num_bins_x number of bins in horizontal direction + @param num_bins_y number of bins in vertical direction + @param padding number of padding bins to left, right, bottom, top of the placement region + @param name string for printing + @param fence_regions a [n_subregions, 4] tensor for fence regions potential penalty + """ + bin_size_x = (placedb.xh - placedb.xl) / num_bins_x + bin_size_y = (placedb.yh - placedb.yl) / num_bins_y + + return electric_potential.ElectricPotential( + node_size_x=data_collections.node_size_x, + node_size_y=data_collections.node_size_y, + xl=placedb.xl, + yl=placedb.yl, + xh=placedb.xh, + yh=placedb.yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=placedb.num_filler_nodes, + deterministic_flag=params.deterministic_flag, + sorted_node_map=data_collections.sorted_node_map, + region_id=region_id, + fence_regions=fence_regions, + node2fence_region_map=data_collections.node2fence_region_map, + placedb=placedb) + + def initialize_density_weight(self, params, placedb): + """ + @brief compute initial density weight + @param params parameters + @param placedb placement database + """ + #Updated to elfPlace + wirelength = self.op_collections.wirelength_op(self.data_collections.pos[0]) + + if self.data_collections.pos[0].grad is not None: + self.data_collections.pos[0].grad.zero_() + wirelength.backward() + wirelength_grad_norm = self.data_collections.pos[0].grad.norm(p=1) + #content = "Initial WL grad norm = %.3E" % (wirelength_grad_norm) + + self.data_collections.pos[0].grad.zero_() + density_weight = [] + density_list = [] + density_grad_list = [] + for density_op in self.op_collections.fence_region_density_ops: + density_i = density_op(self.data_collections.pos[0]) + density_list.append(density_i.data.clone()) + density_i.backward() + density_grad_list.append(self.data_collections.pos[0].grad.data.clone()) + self.data_collections.pos[0].grad.zero_() + + ## density = self.op_collections.fence_region_density_merged_op(self.data_collections.pos[0]) + #### record initial density + self.init_density = torch.stack(density_list) + + #### density weight subgradient preconditioner + self.density_weight_grad_precond = self.init_density.masked_scatter(self.init_density != 0, 1/self.init_density[self.init_density != 0]) + #content += ", Density weight gradient preconditioner = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight_grad_precond.cpu().numpy().tolist()]) + #### compute u + self.density_weight_u = self.init_density * self.density_weight_grad_precond + self.density_weight_u += 0.5 * self.density_quad_coeff * self.density_weight_u**2 + #### compute s + density_weight_s = 1 + self.density_quad_coeff * self.init_density * self.density_weight_grad_precond + + #### compute density grad L1 norm + density_grad_norm = sum(self.density_weight_u[i]*density_weight_s[i]*density_grad_list[i].norm(p=1) for i in range(density_weight_s.size(0))) + #content += ", Initial Density grad Norm = %.3E" % (density_grad_norm) + + self.density_weight_u *= params.density_weight * wirelength_grad_norm / density_grad_norm + + #### set initial step size for density weight update + self.density_weight_step_size_inc_low = 1.05 + self.density_weight_step_size_inc_high = 1.06 + + self.density_weight_step_size = (self.density_weight_step_size_inc_low - 1) * self.density_weight_u.norm(p=2) + ### commit initial density weight + self.density_weight = self.density_weight_u * density_weight_s + + return self.density_weight + + def reset_density_weight(self, params, placedb, ratio): + """ + @brief compute initial density weight + @param params parameters + @param placedb placement database + @param ratio to weight density weight + """ + wirelength = self.op_collections.wirelength_op(self.data_collections.pos[0]) + + if self.data_collections.pos[0].grad is not None: + self.data_collections.pos[0].grad.zero_() + wirelength.backward() + wirelength_grad_norm = self.data_collections.pos[0].grad.norm(p=1) + + self.data_collections.pos[0].grad.zero_() + density_list = [] + density_grad_list = [] + for density_op in self.op_collections.fence_region_density_ops: + density_i = density_op(self.data_collections.pos[0]) + density_list.append(density_i.data.clone()) + # if(self.quad_penalty): + # density_i = density_i + self.density_quad_coeff / 2 / density_i * density_i **2 + density_i.backward() + density_grad_list.append(self.data_collections.pos[0].grad.data.clone()) + self.data_collections.pos[0].grad.zero_() + + ### record updated density + self.upd_density = torch.stack(density_list) + + #### Reset lambda - density_weight_u + self.density_weight_u = self.upd_density * self.density_weight_grad_precond + self.density_weight_u += 0.5 * self.density_quad_coeff * self.density_weight_u**2 + #### compute s + density_weight_s = 1 + self.density_quad_coeff * self.upd_density * self.density_weight_grad_precond + + #### compute density grad L1 norm + density_grad_norm = sum(self.density_weight_u[i]*density_weight_s[i]*density_grad_list[i].norm(p=1) for i in range(density_weight_s.size(0))) + + self.density_weight_u *= ratio * wirelength_grad_norm / density_grad_norm + + self.density_weight_step_size = (self.density_weight_step_size_inc_low - 1) * self.density_weight_u.norm(p=2) + # ### commit the density weight + self.density_weight = self.density_weight_u * density_weight_s + + return self.density_weight + + + def build_update_density_weight(self, params, placedb, algo="overflow"): + """ + @brief update density weight + @param params parameters + @param placedb placement database + """ + #Updated to elfPlace + ### params for hpwl mode from RePlAce + ref_hpwl = params.RePlAce_ref_hpwl + LOWER_PCOF = params.RePlAce_LOWER_PCOF + UPPER_PCOF = params.RePlAce_UPPER_PCOF + ### params for overflow mode from elfPlace + # alpha_h = 1.06 + # alpha_l = 1.05 + # self.density_step_size = alpha_h-1 + assert algo in {"hpwl", "overflow"}, logging.error("density weight update not supports hpwl mode or overflow mode") + + def update_density_weight_op_hpwl(cur_metric, prev_metric, iteration): + ### based on hpwl + with torch.no_grad(): + delta_hpwl = cur_metric.hpwl - prev_metric.hpwl + if delta_hpwl < 0: + mu = UPPER_PCOF * np.maximum( + np.power(0.9999, float(iteration)), 0.98) + #mu = UPPER_PCOF*np.maximum(np.power(0.9999, float(iteration)), 1.03) + else: + mu = UPPER_PCOF * torch.pow( + UPPER_PCOF, -delta_hpwl / ref_hpwl).clamp( + min=LOWER_PCOF, max=UPPER_PCOF) + self.density_weight *= mu + + def update_density_weight_op_overflow(cur_metric, prev_metric, iteration): + assert self.quad_penalty == True, "[Error] density weight update based on overflow only works for quadratic density penalty" + ### based on overflow + ### stop updating if a region has lower overflow than stop overflow + with torch.no_grad(): + density_norm = cur_metric.density * self.density_weight_grad_precond + density_weight_grad = density_norm + self.density_quad_coeff/2*density_norm**2 + + #content = "Density Norm = [%s]" % ", ".join(["%.3E" % i for i in density_norm.cpu().numpy().tolist()]) + ##Rachel: Possibility of zero in density_norm for some resource types could result in INF in density weight grad computation + if density_weight_grad.isinf().any(): + density_weight_grad[density_weight_grad == float("Inf")] = 0 + + density_weight_grad /= density_weight_grad.norm(p=2) + + #content += ", Density Weight Grad = [%s]" % ", ".join(["%.3E" % i for i in density_weight_grad.cpu().numpy().tolist()]) + + ### self.density_weight += self.density_weight_step_size * density_weight_grad# * 1e-7 + self.density_weight_u += self.density_weight_step_size * density_weight_grad + density_weight_s = 1 + self.density_quad_coeff * density_norm + + #content += ", Density Weight Step Size = %.3E, " % (self.density_weight_step_size) + #content += "density_weight_s = [%s]" % ", ".join(["%.3E" % i for i in density_weight_s.cpu().numpy().tolist()]) + #content += ", density_weight_u = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight_u.cpu().numpy().tolist()]) + + #### update density weight step size + rate = torch.log(self.density_quad_coeff * density_norm.norm(p=2)).clamp(min=0) + rate = rate / (1 + rate) + rate = rate * (self.density_weight_step_size_inc_high - self.density_weight_step_size_inc_low) + self.density_weight_step_size_inc_low + self.density_weight_step_size *= rate + + #content += ", Rate = %g" % (rate) + #### conditional update if this region's overflow is higher than stop overflow + + density_weight_new = self.density_weight_u * density_weight_s + + self.targetOverflow = torch.tensor(self.placedb.targetOverflow, dtype=torch.float, device=self.data_collections.pos[0].device) + if(self.update_mask is None): + self.update_mask = cur_metric.overflow >= self.targetOverflow + self.lock_mask = cur_metric.overflow < self.targetOverflow + + self.density_weight.masked_scatter_(self.update_mask, density_weight_new[self.update_mask]) + #content += ", density_weight = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight.cpu().numpy().tolist()]) + #logging.info(content) + + + if(not self.quad_penalty and algo == "overflow"): + logging.warn("quadratic density penalty is disabled, density weight update is forced to be based on HPWL") + algo = "hpwl" + if(len(self.placedb.region_boxes) == 0 and algo == "overflow"): + logging.warn("for benchmark without fence region, density weight update is forced to be based on HPWL") + algo = "hpwl" + + update_density_weight_op = {"hpwl":update_density_weight_op_hpwl, "overflow": update_density_weight_op_overflow}[algo] + + return update_density_weight_op + + def base_gamma(self, params, placedb): + """ + @brief compute base gamma + @param params parameters + @param placedb placement database + """ + ## Updated to elfPlace + self.baseWLGamma = [] + self.WLGammaK = [] + self.WLGammaB = [] + self.WLGammaWt = [] + + for idx in range(placedb.regions): + i = placedb.rsrc2compId_map[idx] + if i != -1: + self.baseWLGamma.append(0.5 * params.gamma * (placedb.bin_size_x + placedb.bin_size_y)) + # Compute coeffcient for wirelength gamma updating + # The basic idea is that we want to achieve + # gamma = 10 * base_gamma, if overflow = 1.0 + # gamma = 0.1 * base_gamma, if overflow = target_overflow + # We use function f(ovfl) = 10^(k * ovfl + b) to achieve the two above two points + # So we want + # k + b = 1 + # k * target_overflow + b = -1 + # Then we have + # k = 2.0 / (1 - target_overflow) + # b = 1.0 - k + self.WLGammaK.append(2.0/(1.0 - placedb.targetOverflow[i])) + self.WLGammaB.append(1.0 - self.WLGammaK[i]) + # Compare the wirelength gamma weight to balance gamma updating for different area types + self.WLGammaWt.append(self.precondWL[:placedb.num_physical_nodes][self.data_collections.node2fence_region_map == idx].sum()) + + return self.baseWLGamma + + def update_gamma(self, iteration, overflow, base_gamma): + """ + @brief update gamma in wirelength model + @param iteration optimization step + @param overflow evaluated in current step + @param base_gamma base gamma + """ + ## Updated to elfPlace + # Compute the gamma for each area type and use the pin count-averaged value as the final gamma + totalGamma = 0.0 + totalWt = 0.0 + for idx in range(self.placedb.regions): + i = self.placedb.rsrc2compId_map[idx] + if i != -1: + gma = base_gamma[i] * pow(10.0, overflow[i] * self.WLGammaK[i] + self.WLGammaB[i]) + totalGamma += gma * self.WLGammaWt[i] + totalWt += self.WLGammaWt[i] + + self.gamma.data.fill_(totalGamma / totalWt) + return True + + def build_noise(self, params, placedb, data_collections): + """ + @brief add noise to cell locations + @param params parameters + @param placedb placement database + @param data_collections a collection of data and variables required for constructing ops + """ + node_size = torch.cat([data_collections.node_size_x, data_collections.node_size_y], + dim=0).to(data_collections.device) + + def noise_op(pos, noise_ratio): + with torch.no_grad(): + noise = torch.rand_like(pos) + noise.sub_(0.5).mul_(node_size).mul_(noise_ratio) + # no noise to fixed cells + noise[placedb.num_movable_nodes:placedb.num_nodes - + placedb.num_filler_nodes].zero_() + noise[placedb.num_nodes + + placedb.num_movable_nodes:2 * placedb.num_nodes - + placedb.num_filler_nodes].zero_() + return pos.add_(noise) + + return noise_op + + def build_precondition(self, params, placedb, data_collections): + """ + @brief preconditioning to gradient + @param params parameters + @param placedb placement database + @param data_collections a collection of data and variables required for constructing ops + """ + + #def precondition_op(grad): + # with torch.no_grad(): + # # preconditioning + # node_areas = data_collections.node_size_x * data_collections.node_size_y + # precond = self.density_weight * node_areas + # precond[:placedb.num_physical_nodes].add_(data_collections.pin_weights) + # precond.clamp_(min=1.0) + # grad[0:placedb.num_nodes].div_(precond) + # grad[placedb.num_nodes:placedb.num_nodes*2].div_(precond) + # #for p in pos: + # # grad_norm = p.grad.norm(p=2) + # # logging.debug("grad_norm = %g" % (grad_norm.data)) + # # p.grad.div_(grad_norm.data) + # # logging.debug("grad_norm = %g" % (p.grad.norm(p=2).data)) + # #grad.data[0:placedb.num_movable_nodes].div_(grad[0:placedb.num_movable_nodes].norm(p=2)) + # #grad.data[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes].div_(grad[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes].norm(p=2)) + # return grad + + #return precondition_op + + return PreconditionOpFPGA(placedb, data_collections) + + def build_route_utilization_map(self, params, placedb, data_collections): + """ + @brief routing congestion map based on current cell locations + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + """ + congestion_op = rudy.Rudy( + netpin_start=data_collections.flat_net2pin_start_map, + flat_netpin=data_collections.flat_net2pin_map, + net_weights=data_collections.net_weights, + xl=placedb.routing_grid_xl, + yl=placedb.routing_grid_yl, + xh=placedb.routing_grid_xh, + yh=placedb.routing_grid_yh, + num_bins_x=placedb.num_routing_grids_x, + num_bins_y=placedb.num_routing_grids_y, + unit_horizontal_capacity=placedb.unit_horizontal_capacity, + unit_vertical_capacity=placedb.unit_vertical_capacity, + deterministic_flag=params.deterministic_flag, + initial_horizontal_utilization_map=data_collections. + initial_horizontal_utilization_map, + initial_vertical_utilization_map=data_collections. + initial_vertical_utilization_map, + num_threads=params.num_threads) + + def route_utilization_map_op(pos): + pin_pos = self.op_collections.pin_pos_op(pos) + return congestion_op(pin_pos) + + return route_utilization_map_op + + def build_pin_utilization_map(self, params, placedb, data_collections): + """ + @brief pin density map based on current cell locations + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + """ + return pin_utilization.PinUtilization( + pin_weights=data_collections.pin_weights, + flat_node2pin_start_map=data_collections.flat_node2pin_start_map, + node_size_x=data_collections.node_size_x, + node_size_y=data_collections.node_size_y, + xl=placedb.routing_grid_xl, + yl=placedb.routing_grid_yl, + xh=placedb.routing_grid_xh, + yh=placedb.routing_grid_yh, + num_movable_nodes=placedb.num_movable_nodes, + num_filler_nodes=placedb.num_filler_nodes, + num_bins_x=placedb.num_routing_grids_x, + num_bins_y=placedb.num_routing_grids_y, + unit_pin_capacity=data_collections.unit_pin_capacity, + pin_stretch_ratio=params.pin_stretch_ratio, + deterministic_flag=params.deterministic_flag, + num_threads=params.num_threads) + + #Update for LUT + def build_clustering_compatibility_lut_map(self, params, placedb, data_collections): + """ + @brief clustering compatibility lut map based on current cell locations to ensure maximum input pin constraint is met + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + """ + bins_x = math.ceil((placedb.xh - placedb.xl)/placedb.instDemStddevX) + bins_y = math.ceil((placedb.yh - placedb.yl)/placedb.instDemStddevY) + + return clustering_compatibility.LUTCompatibility( + lut_indices=data_collections.lut_indices, + lut_type=data_collections.cluster_lut_type, + node_size_x=data_collections.node_size_x, + node_size_y=data_collections.node_size_y, + num_bins_x=bins_x, + num_bins_y=bins_y, + num_bins_l=placedb.lutTypeInSliceUnit, + placedb=placedb, + deterministic_flag=params.deterministic_flag, + num_threads=params.num_threads) + + #Update for FF + def build_clustering_compatibility_ff_map(self, params, placedb, data_collections): + """ + @brief clustering compatibility flop map based on current cell locations to ensure control set constraint is met + @param params parameters + @param placedb placement database + @param data_collections a collection of all data and variables required for constructing the ops + """ + bins_x = math.ceil((placedb.xh - placedb.xl)/placedb.instDemStddevX) + bins_y = math.ceil((placedb.yh - placedb.yl)/placedb.instDemStddevY) + return clustering_compatibility.FFCompatibility( + flop_indices=data_collections.flop_indices, + flop_ctrlSets=data_collections.flop_ctrlSets, + node_size_x=data_collections.node_size_x, + node_size_y=data_collections.node_size_y, + num_bins_x=bins_x, + num_bins_y=bins_y, + num_bins_ck=placedb.ctrlSets[:,1].max()+1, + num_bins_ce=placedb.ctrlSets[:,2].max()+1, + placedb=placedb, + deterministic_flag=params.deterministic_flag, + num_threads=params.num_threads) + + def build_adjust_node_area(self, params, placedb, data_collections): + """ + @brief adjust cell area according to routing congestion and pin utilization map + """ + #Include total area only for LUT/FF + total_movable_area = ( + data_collections.node_size_x[:placedb.num_movable_nodes] * + data_collections.node_size_y[:placedb.num_movable_nodes] * + data_collections.flop_lut_mask[:placedb.num_movable_nodes]).sum().item() + lut_compId = placedb.rsrc2compId_map[placedb.rLUTIdx] + lut_filler_start = placedb.filler_start_map[lut_compId] + lut_filler_end = placedb.filler_start_map[lut_compId+1] + lut_fillers = lut_filler_start + lut_filler_end + lut_filler_area = ( + data_collections.node_size_x[placedb.num_physical_nodes + lut_filler_start:placedb.num_physical_nodes + lut_filler_end] * + data_collections.node_size_y[placedb.num_physical_nodes + lut_filler_start:placedb.num_physical_nodes + lut_filler_end]).sum().item() + + flop_compId = placedb.rsrc2compId_map[placedb.rFFIdx] + flop_filler_start = placedb.filler_start_map[flop_compId] + flop_filler_end = placedb.filler_start_map[flop_compId+1] + flop_fillers = flop_filler_start + flop_filler_end + flop_filler_area = ( + data_collections.node_size_x[placedb.num_physical_nodes + flop_filler_start:placedb.num_physical_nodes + flop_filler_end] * + data_collections.node_size_y[placedb.num_physical_nodes + flop_filler_start:placedb.num_physical_nodes + flop_filler_end]).sum().item() + + flop_lut_fillers = lut_fillers + flop_fillers + total_filler_area = lut_filler_area + flop_filler_area + total_place_area = total_movable_area + total_filler_area + + adjust_node_area_op = adjust_node_area.AdjustNodeArea( + placedb=placedb, + flat_node2pin_map=data_collections.flat_node2pin_map, + flat_node2pin_start_map=data_collections.flat_node2pin_start_map, + pin_weights=data_collections.pin_weights, + flop_lut_indices=data_collections.flop_lut_indices, + flop_lut_mask=data_collections.flop_lut_mask, + flop_mask=data_collections.flop_mask, + lut_mask=data_collections.lut_mask, + total_place_area=total_place_area, + total_whitespace_area=total_place_area - total_movable_area, + max_route_opt_adjust_rate=params.max_route_opt_adjust_rate, + route_opt_adjust_exponent=params.route_opt_adjust_exponent, + max_pin_opt_adjust_rate=params.max_pin_opt_adjust_rate, + area_adjust_stop_ratio=params.area_adjust_stop_ratio, + route_area_adjust_stop_ratio=params.route_area_adjust_stop_ratio, + pin_area_adjust_stop_ratio=params.pin_area_adjust_stop_ratio, + unit_pin_capacity=data_collections.unit_pin_capacity) + + def build_adjust_node_area_op(pos, resource_areas, route_utilization_map, pin_utilization_map): + return adjust_node_area_op( + pos, data_collections.node_size_x, + data_collections.node_size_y, data_collections.pin_offset_x, + data_collections.pin_offset_y, 1.0, + resource_areas, route_utilization_map, pin_utilization_map) + + return build_adjust_node_area_op + + def build_multi_fence_region_density_op(self): + # region 0, ..., region n, non_fence_region + self.op_collections.fence_region_density_ops = [] + + for idx in range(self.placedb.rsrc2compId_map.size): + i = self.placedb.rsrc2compId_map[idx] + if i != -1: + #Create for all resource types but do not update fixed types + self.op_collections.fence_region_density_ops.append(self.build_electric_potential( + self.params, + self.placedb, + self.data_collections, + self.num_bins_x, + self.num_bins_y, + name=self.name, + region_id=idx, + fence_regions=self.fixedDemMaps[i]) + ) + + def merged_density_op(pos): + #### stop mask is to stop forward of density + #### 1 represents stop flag + + resdb = torch.stack([densityOp(pos, mode="density") for densityOp in self.op_collections.fence_region_density_ops]) + + return resdb + + def merged_density_overflow_op(pos): + ### stop mask is to stop forward of density + ### 1 represents stop flag + overflow_list, max_density_list = [], [] + for density_op in self.op_collections.fence_region_density_ops: + overflow, max_density = density_op(pos, mode="overflow") + overflow_list.append(overflow) + max_density_list.append(max_density) + overflow_list, max_density_list = torch.stack(overflow_list), torch.stack(max_density_list) + return overflow_list, max_density_list + + self.op_collections.fence_region_density_merged_op = merged_density_op + + self.op_collections.fence_region_density_overflow_merged_op = merged_density_overflow_op + return self.op_collections.fence_region_density_ops, self.op_collections.fence_region_density_merged_op, self.op_collections.fence_region_density_overflow_merged_op + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py new file mode 100644 index 00000000..e334e200 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py @@ -0,0 +1,130 @@ +## +# @file Placer.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Sep 2020 +# @brief Main file to run the entire placement flow. +# + +import matplotlib +matplotlib.use('Agg') +import os +import sys +import time +import numpy as np +import logging +# for consistency between python2 and python3 +root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if root_dir not in sys.path: + sys.path.append(root_dir) +import dreamplacefpga.configure as configure +from Params import * +from PlaceDB import * +from NonLinearPlace import * +from IFWriter import * +import pdb + +def placeFPGA(params): + """ + @brief Top API to run the entire placement flow. + @param params parameters + """ + assert (not params.gpu) or configure.compile_configurations["CUDA_FOUND"] == 'TRUE', \ + "CANNOT enable GPU without CUDA compiled" + + np.random.seed(params.random_seed) + # Read Database + start = time.time() + placedb = PlaceDBFPGA() + placedb(params) #Call function + + # Placement + placer = NonLinearPlaceFPGA(params, placedb) + metrics = placer(params, placedb) + logging.info("Non-Linear Placement completed in %.2f seconds" % (time.time()-start)) + + # write placement solution + path = "%s/%s" % (params.result_dir, params.design_name()) + if not os.path.exists(path): + os.system("mkdir -p %s" % (path)) + if params.global_place_flag and params.legalize_flag == 0 and placedb.sliceFF_ctrl_mode == "HALF": ##Only global placement is run + gp_out_file = os.path.join(path, "%s.gp.pl" % (params.design_name())) + placedb.write(gp_out_file) + + ##Use elfPlace binary to run legalization and detatiled placement + #elfPlace binary picks file named gp.pl in the current directory + if os.path.exists("thirdparty/elfPlace_LG_DP"): + cp_cmd = "cp %s gp.pl" %(gp_out_file) + os.system(cp_cmd) + out_file = os.path.join(path, "%s_final.%s" % (params.design_name(), params.solution_file_suffix())) + cmd = "./thirdparty/elfPlace_LG_DP --aux %s --numThreads %s --pl %s" % (params.aux_input, params.num_threads, out_file) + logging.info("Legalization and Detailed Placement run using elfPlace (CPU): %s" % (cmd)) + tt = time.time() + os.system(cmd) + logging.info("Legalization and detailed placement completed in %.3f seconds" % (time.time()-tt)) + else: + logging.warning("External legalization & detailed placement engine NOT found at thirdparty/elfPlace_LG_DP") + + elif params.global_place_flag: + if params.legalize_flag: ## Run both global placement and legalization + logging.info("Detailed Placement not run") + else: + gp_out_file = os.path.join(path, "%s.gp.pl" % (params.design_name())) + placedb.write(gp_out_file) + logging.info("Legalization & detailed placement not run") + + final_out_file = os.path.join(path, "%s.final.%s" % (params.design_name(), params.solution_file_suffix())) + placedb.writeFinalSolution(final_out_file) + + logging.info("Total Runtime: %.3f seconds" % (time.time()-start)) + + ##For debug + if params.name_map_file_dump == 1: + placedb.writeMapSolution() + + if params.enable_if == 1: + tt = time.time() + logging.info("Start writing solution to Interchange Format(IF)") + part_name = params.part_name + schema_dir = os.path.join(os.path.dirname(__file__), '../thirdparty/fpga-interchange-schema/interchange') + db2phys = db_to_physicalnetlist(placedb, schema_dir, part_name) + phys_netlist = db2phys.build_physicalnetlist(placedb, final_out_file) + # tcl_generator().write_tcl(phys_netlist) + if_writer = IFWriter(schema_dir) + physical_netlist = if_writer.build_IF(phys_netlist) + if_file = os.path.join(path, "%s.phys" % (params.design_name())) + if_writer.write_IF(physical_netlist, if_file) + logging.info("Interchange Format(IF) Writer completed in %.3f seconds" % (time.time()-tt)) + + +if __name__ == "__main__": + """ + @brief main function to invoke the entire placement flow. + """ + logging.root.name = 'DREAMPlaceFPGA' + logging.basicConfig(level=logging.INFO, format='[%(levelname)-7s] %(name)s - %(message)s', stream=sys.stdout) + + if len(sys.argv) < 2: + logging.error("Input parameters required in json format") + paramsArray = [] + for i in range(1, len(sys.argv)): + params = ParamsFPGA() + params.load(sys.argv[i]) + paramsArray.append(params) + logging.info("Parameters[%d] = %s" % (len(paramsArray), paramsArray)) + + #Settings to minimze non-determinism + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + torch.backends.cudnn.enabled = False + torch.manual_seed(params.random_seed) + np.random.seed(params.random_seed) + #random.seed(params.random_seed) + if params.gpu: + torch.cuda.manual_seed_all(params.random_seed) + torch.cuda.manual_seed(params.random_seed) + + # tt = time.time() + for params in paramsArray: + placeFPGA(params) + # logging.info("Completed Placement in %.3f seconds" % (time.time()-tt)) + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py new file mode 100644 index 00000000..5882ca67 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jul 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py new file mode 100644 index 00000000..e216a291 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py @@ -0,0 +1,26 @@ +## +# @file configure.py +# @author Yibo Lin (DREAMPlace) +# @date Feb 2020 +# @brief Record all configurations including compilation +# + +compile_configurations = { + "CMAKE_CXX_COMPILER" : "/usr/bin/g++-9", + "CMAKE_CC_COMPILER" : "", + "CMAKE_BUILD_TYPE" : "Release", + "CMAKE_CXX_ABI" : "0", + "CMAKE_CXX_STANDARD" : "14", + "PYTHON" : "", + "Boost_DIR" : "", + "Boost_INCLUDE_DIRS" : "/usr/include", + "ZLIB_INCLUDE_DIRS" : "/usr/include", + "ZLIB_LIBRARIES" : "/usr/lib/x86_64-linux-gnu/libz.so", + "CUDA_FOUND" : "", + "CUDA_TOOLKIT_ROOT_DIR" : "", + "CMAKE_CUDA_FLAGS" : "", + "CAIRO_FOUND" : "TRUE", + "CAIRO_INCLUDE_DIRS" : "/usr/include/cairo", + "CAIRO_LIBRARIES" : "/usr/lib/x86_64-linux-gnu/libcairo.so", + } + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in new file mode 100644 index 00000000..cf999a86 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in @@ -0,0 +1,26 @@ +## +# @file configure.py +# @author Yibo Lin (DREAMPlace) +# @date Feb 2020 +# @brief Record all configurations including compilation +# + +compile_configurations = { + "CMAKE_CXX_COMPILER" : "${CMAKE_CXX_COMPILER}", + "CMAKE_CC_COMPILER" : "${CMAKE_CC_COMPILER}", + "CMAKE_BUILD_TYPE" : "${CMAKE_BUILD_TYPE}", + "CMAKE_CXX_ABI" : "${CMAKE_CXX_ABI}", + "CMAKE_CXX_STANDARD" : "${CMAKE_CXX_STANDARD}", + "PYTHON" : "${PYTHON}", + "Boost_DIR" : "${Boost_DIR}", + "Boost_INCLUDE_DIRS" : "${Boost_INCLUDE_DIRS}", + "ZLIB_INCLUDE_DIRS" : "${ZLIB_INCLUDE_DIRS}", + "ZLIB_LIBRARIES" : "${ZLIB_LIBRARIES}", + "CUDA_FOUND" : "${CUDA_FOUND}", + "CUDA_TOOLKIT_ROOT_DIR" : "${CUDA_TOOLKIT_ROOT_DIR}", + "CMAKE_CUDA_FLAGS" : "${CMAKE_CUDA_FLAGS}", + "CAIRO_FOUND" : "${CAIRO_FOUND}", + "CAIRO_INCLUDE_DIRS" : "${CAIRO_INCLUDE_DIRS}", + "CAIRO_LIBRARIES" : "${CAIRO_LIBRARIES}", + } + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt new file mode 100644 index 00000000..5196183c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt @@ -0,0 +1,30 @@ +add_subdirectory(utility) +add_subdirectory(dct) +add_subdirectory(pin_pos) +add_subdirectory(density_map) +#add_subdirectory(density_overflow) +add_subdirectory(density_potential) +add_subdirectory(logsumexp_wirelength) +add_subdirectory(draw_place) +add_subdirectory(electric_potential) +add_subdirectory(hpwl) +add_subdirectory(move_boundary) +add_subdirectory(weighted_average_wirelength) +#add_subdirectory(rmst_wl) +add_subdirectory(place_io) +add_subdirectory(precondWL) +add_subdirectory(sortNode2Pin) +add_subdirectory(demandMap) +# legalization operators +add_subdirectory(dsp_ram_legalization) +add_subdirectory(lut_ff_legalization) +# routing congestion operator +add_subdirectory(pin_utilization) +add_subdirectory(rudy) +add_subdirectory(adjust_node_area) +add_subdirectory(clustering_compatibility) + +file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") +install( + FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py new file mode 100644 index 00000000..2fe6334d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Yibo Lin +# @date Jun 2018 +# @brief ops +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt new file mode 100644 index 00000000..907eb82a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt @@ -0,0 +1,46 @@ +set(OP_NAME adjust_node_area) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(adjust_node_area_cpp + src/adjust_node_area.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(update_pin_offset_cpp + src/update_pin_offset.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + adjust_node_area_cpp + update_pin_offset_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(adjust_node_area_cuda + src/adjust_node_area_cuda.cpp + src/adjust_node_area_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(update_pin_offset_cuda + src/update_pin_offset_cuda.cpp + src/update_pin_offset_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + adjust_node_area_cuda + update_pin_offset_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py new file mode 100644 index 00000000..a28e9763 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py @@ -0,0 +1,407 @@ +## +# @file adjust_node_area.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Nov 2020 +# @brief Nonlinear placement engine to be called with parameters and placement database +# +import math +import torch +from torch import nn +import torch.nn.functional as F +import logging +import pdb + +import dreamplacefpga.ops.adjust_node_area.adjust_node_area_cpp as adjust_node_area_cpp +import dreamplacefpga.ops.adjust_node_area.update_pin_offset_cpp as update_pin_offset_cpp + +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.adjust_node_area.adjust_node_area_cuda as adjust_node_area_cuda + import dreamplacefpga.ops.adjust_node_area.update_pin_offset_cuda as update_pin_offset_cuda + +logger = logging.getLogger(__name__) + +class ComputeNodeAreaFromRouteMap(nn.Module): + def __init__(self, xl, yl, xh, yh, flop_lut_indices, num_movable_nodes, num_bins_x, + num_bins_y): + super(ComputeNodeAreaFromRouteMap, self).__init__() + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.flop_lut_indices = flop_lut_indices + self.num_movable_nodes = num_movable_nodes + self.num_bins_x = num_bins_x + self.num_bins_y = num_bins_y + self.bin_size_x = (xh - xl) / num_bins_x + self.bin_size_y = (yh - yl) / num_bins_y + + def forward(self, pos, node_size_x, node_size_y, utilization_map): + if pos.is_cuda: + func = adjust_node_area_cuda.forward + else: + func = adjust_node_area_cpp.forward + output = func(pos, node_size_x, node_size_y, utilization_map, + self.bin_size_x, self.bin_size_y, self.xl, self.yl, + self.xh, self.yh, self.flop_lut_indices, self.num_movable_nodes, + self.num_bins_x, self.num_bins_y) + return output + + +class ComputeNodeAreaFromPinMap(ComputeNodeAreaFromRouteMap): + def __init__(self, pin_weights, flat_node2pin_start_map, xl, yl, xh, yh, + flop_lut_indices, num_movable_nodes, num_bins_x, num_bins_y, unit_pin_capacity): + super(ComputeNodeAreaFromPinMap, + self).__init__(xl, yl, xh, yh, flop_lut_indices, num_movable_nodes, num_bins_x, + num_bins_y) + bin_area = (xh - xl) / num_bins_x * (yh - yl) / num_bins_y + self.unit_pin_capacity = unit_pin_capacity + # for each physical node, we use the pin counts as the weights + if pin_weights is not None: + self.pin_weights = pin_weights + elif flat_node2pin_start_map is not None: + self.pin_weights = flat_node2pin_start_map[ + 1:self.num_movable_nodes + + 1] - flat_node2pin_start_map[:self.num_movable_nodes] + else: + assert "either pin_weights or flat_node2pin_start_map is required" + + def forward(self, pos, node_size_x, node_size_y, utilization_map): + output = super(ComputeNodeAreaFromPinMap, + self).forward(pos, node_size_x, node_size_y, + utilization_map) + output.mul_(self.pin_weights[:self.num_movable_nodes].to(node_size_x.dtype) / (node_size_x[:self.num_movable_nodes] * node_size_y[:self.num_movable_nodes] * self.unit_pin_capacity)) + return output + + +class AdjustNodeArea(nn.Module): + def __init__( + self, + placedb, + flat_node2pin_map, + flat_node2pin_start_map, + pin_weights, # only one of them needed + flop_lut_indices, + flop_lut_mask, + flop_mask, + lut_mask, + total_place_area, # total placement area excluding fixed cells + total_whitespace_area, # total white space area excluding movable and fixed cells + max_route_opt_adjust_rate, + route_opt_adjust_exponent=2.5, + max_pin_opt_adjust_rate=2.5, + area_adjust_stop_ratio=0.01, + route_area_adjust_stop_ratio=0.01, + pin_area_adjust_stop_ratio=0.05, + unit_pin_capacity=0.0): + + super(AdjustNodeArea, self).__init__() + self.flat_node2pin_start_map = flat_node2pin_start_map + self.flat_node2pin_map = flat_node2pin_map + self.pin_weights = pin_weights + + self.flop_lut_indices = flop_lut_indices + self.flop_lut_mask = flop_lut_mask + self.flop_mask = flop_mask + self.lut_mask = lut_mask + + self.filler_start_map = placedb.filler_start_map + self.xl = placedb.xl + self.xh = placedb.xh + self.yl = placedb.yl + self.yh = placedb.yh + self.num_movable_nodes = placedb.num_movable_nodes + self.num_filler_nodes = placedb.num_filler_nodes + + #mlab + self.num_mlab_nodes = placedb.num_mlab_nodes + if self.num_mlab_nodes > 0: + self.is_mlab_node = placedb.is_mlab_node + self.is_mlab_filler_node = placedb.is_mlab_filler_node + + #large carry chain nodes + self.num_ccNodes = placedb.num_ccNodes + if self.num_ccNodes > 0: + self.is_cc_node = torch.from_numpy(placedb.is_cc_node).to(self.lut_mask.device) + # maximum and minimum instance area adjustment rate for routability optimization + self.max_route_opt_adjust_rate = max_route_opt_adjust_rate + self.min_route_opt_adjust_rate = 1.0 / max_route_opt_adjust_rate + # exponent for adjusting the utilization map + self.route_opt_adjust_exponent = route_opt_adjust_exponent + # maximum and minimum instance area adjustment rate for routability optimization + self.max_pin_opt_adjust_rate = max_pin_opt_adjust_rate + self.min_pin_opt_adjust_rate = 1.0 / max_pin_opt_adjust_rate + + # stop ratio + self.area_adjust_stop_ratio = area_adjust_stop_ratio + self.route_area_adjust_stop_ratio = route_area_adjust_stop_ratio + self.pin_area_adjust_stop_ratio = pin_area_adjust_stop_ratio + + self.compute_node_area_route = ComputeNodeAreaFromRouteMap( + xl=self.xl, + yl=self.yl, + xh=self.xh, + yh=self.yh, + flop_lut_indices=self.flop_lut_indices, + num_movable_nodes=self.num_movable_nodes, + num_bins_x=placedb.num_routing_grids_x, + num_bins_y=placedb.num_routing_grids_y) + self.compute_node_area_pin = ComputeNodeAreaFromPinMap( + pin_weights=self.pin_weights, + flat_node2pin_start_map=self.flat_node2pin_start_map, + xl=self.xl, + yl=self.yl, + xh=self.xh, + yh=self.yh, + flop_lut_indices=self.flop_lut_indices, + num_movable_nodes=self.num_movable_nodes, + num_bins_x=placedb.num_routing_grids_x, + num_bins_y=placedb.num_routing_grids_y, + unit_pin_capacity=unit_pin_capacity) + + # placement area excluding fixed cells + self.total_place_area = total_place_area + # placement area excluding movable and fixed cells + self.total_whitespace_area = total_whitespace_area + + def forward(self, pos, node_size_x, node_size_y, pin_offset_x, + pin_offset_y, target_density, resource_areas, + route_utilization_map, pin_utilization_map): + + with torch.no_grad(): + adjust_area_flag = True + adjust_resource_area_flag = resource_areas is not None + adjust_route_area_flag = route_utilization_map is not None + adjust_pin_area_flag = pin_utilization_map is not None + + if not (adjust_resource_area_flag or adjust_pin_area_flag or adjust_route_area_flag): + return False, False, False, False + + num_physical_nodes = node_size_x.numel() - self.num_filler_nodes + num_flop_lut_fillers = self.filler_start_map[2] + # compute old areas of movable nodes - Ignore DSP/RAM instances + node_size_x_movable = node_size_x[:self.num_movable_nodes] + node_size_y_movable = node_size_y[:self.num_movable_nodes] + + node_size_x_filler = node_size_x[num_physical_nodes:num_physical_nodes+num_flop_lut_fillers] + node_size_y_filler = node_size_y[num_physical_nodes:num_physical_nodes+num_flop_lut_fillers] + old_movable_area = node_size_x_movable * node_size_y_movable + old_filler_area = node_size_x_filler * node_size_y_filler + + #Update for LUT + old_movable_area_lut_sum = (node_size_x_movable * node_size_y_movable * self.lut_mask[:self.num_movable_nodes]).sum() + num_lut_fillers = self.filler_start_map[1] + old_filler_area_lut_sum = (node_size_x_filler[:num_lut_fillers] * node_size_y_filler[:num_lut_fillers]).sum() + + #Update for FF + old_movable_area_flop_sum = (node_size_x_movable * node_size_y_movable * self.flop_mask[:self.num_movable_nodes]).sum() + num_flop_fillers = self.filler_start_map[2] - self.filler_start_map[1] + old_filler_area_flop_sum = (node_size_x_filler[num_lut_fillers:] * node_size_y_filler[num_lut_fillers:]).sum() + old_filler_area_sum = old_filler_area_lut_sum + old_filler_area_flop_sum + + #No filler space available for nodes to inflate + if old_filler_area_sum == 0: + return False, False, False, False + + # compute routability optimized area + if adjust_route_area_flag: + # clamp the routing square of routing utilization map + route_utilization_map_clamp = route_utilization_map.pow(self.route_opt_adjust_exponent).clamp_( + min=self.min_route_opt_adjust_rate, + max=self.max_route_opt_adjust_rate) + route_opt_area = self.compute_node_area_route(pos, node_size_x, node_size_y, route_utilization_map_clamp) + # compute pin density optimized area + if adjust_pin_area_flag: + pin_opt_area = self.compute_node_area_pin(pos, node_size_x, node_size_y, + # clamp the pin utilization map + pin_utilization_map.clamp(min=self.min_pin_opt_adjust_rate, max=self.max_pin_opt_adjust_rate)) + + # compute the extra area max(route_opt_area, pin_opt_area) over the base area for each movable node + # Include all possible conditions + if adjust_resource_area_flag and adjust_route_area_flag and adjust_pin_area_flag: + area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], torch.max(route_opt_area, pin_opt_area)) - old_movable_area) + elif adjust_resource_area_flag and adjust_route_area_flag: + area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], route_opt_area) - old_movable_area) + elif adjust_resource_area_flag and adjust_pin_area_flag: + area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], pin_opt_area) - old_movable_area) + elif adjust_route_area_flag and adjust_pin_area_flag: + area_increment = F.relu(torch.max(route_opt_area, pin_opt_area) - old_movable_area) + elif adjust_resource_area_flag: + area_increment = F.relu(resource_areas[:self.num_movable_nodes] - old_movable_area) + elif adjust_route_area_flag: + area_increment = F.relu(route_opt_area - old_movable_area) + elif adjust_pin_area_flag: + area_increment = F.relu(pin_opt_area - old_movable_area) + else: + area_increment = torch.zeros(old_movable_area.numel(), dtype=old_movable_area.dtype, device=old_movable_area.device) + + #Restrict inflation of large carry chain nodes + if self.num_ccNodes > 0: + cc_mask = self.is_cc_node[:self.num_movable_nodes] == 1 + area_increment[cc_mask] = area_increment[cc_mask].clamp_(max=3.0) + + area_increment_lut_sum = (area_increment * self.lut_mask[:self.num_movable_nodes]).sum() + area_increment_flop_sum = (area_increment * self.flop_mask[:self.num_movable_nodes]).sum() + ## check whether the total area is larger than the max area requirement + scale_factor_lut = max(((self.total_place_area/2.0 - old_movable_area_lut_sum) / area_increment_lut_sum).clamp_(max=1.0), 0.0) + scale_factor_flop = max(((self.total_place_area/2.0 - old_movable_area_flop_sum) / area_increment_flop_sum).clamp_(max=1.0), 0.0) + #Incorporate area of placeholder MLAB fillers for FF + if self.num_mlab_nodes > 0: + mlab_size_x = node_size_x_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] + mlab_size_y = node_size_y_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] + mlab_area = mlab_size_x * mlab_size_y + scale_factor_flop = max(((self.total_place_area/2.0 - old_movable_area_flop_sum - mlab_area.sum()) / area_increment_flop_sum).clamp_(max=1.0), 0.0) + + # set the new_movable_area as base_area + scaled area increment + new_movable_area = old_movable_area + (area_increment * scale_factor_lut * self.lut_mask[:self.num_movable_nodes]) + (area_increment * scale_factor_flop * self.flop_mask[:self.num_movable_nodes]) + + area_increment_sum = area_increment_lut_sum * scale_factor_lut + area_increment_flop_sum * scale_factor_flop + old_movable_area_sum = old_movable_area_lut_sum + old_movable_area_flop_sum + new_movable_area_sum = old_movable_area_sum + area_increment_sum + area_increment_ratio = area_increment_sum / old_movable_area_sum + if area_increment_sum > 0: + logger.info( + "area_increment = %E, area_increment / movable = %g, area_adjust_stop_ratio = %g" + % (area_increment_sum, area_increment_ratio, + self.area_adjust_stop_ratio)) + logger.info( + "area_increment / total_place_area = %g, area_increment / filler = %g, area_increment / total_whitespace_area = %g" + % (area_increment_sum / self.total_place_area, + area_increment_sum / old_filler_area_sum, + area_increment_sum / self.total_whitespace_area)) + + # compute the adjusted area increase ratio + # disable some of the area adjustment if the condition holds + if adjust_resource_area_flag: + resource_area_increment_ratio = F.relu((resource_areas[:self.num_movable_nodes] - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum + adjust_resource_area_flag = resource_area_increment_ratio.item() > self.route_area_adjust_stop_ratio + logger.info( + "resource_area_increment_ratio = %g, resource_area_adjust_stop_ratio = %g" + % (resource_area_increment_ratio, self.route_area_adjust_stop_ratio)) + if adjust_route_area_flag: + route_area_increment_ratio = F.relu((route_opt_area - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum + adjust_route_area_flag = route_area_increment_ratio.data.item() > self.route_area_adjust_stop_ratio + logger.info( + "route_area_increment_ratio = %g, route_area_adjust_stop_ratio = %g" + % (route_area_increment_ratio, self.route_area_adjust_stop_ratio)) + if adjust_pin_area_flag: + pin_area_increment_ratio = F.relu((pin_opt_area - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum + adjust_pin_area_flag = pin_area_increment_ratio.data.item() > self.pin_area_adjust_stop_ratio + logger.info( + "pin_area_increment_ratio = %g, pin_area_adjust_stop_ratio = %g" + % (pin_area_increment_ratio, self.pin_area_adjust_stop_ratio)) + adjust_area_flag = ( + area_increment_ratio.data.item() > self.area_adjust_stop_ratio + ) and (adjust_resource_area_flag or adjust_route_area_flag or adjust_pin_area_flag) + + if not adjust_area_flag: + return adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag + + num_nodes = pos.numel() // 2 + # adjust the size and positions of movable nodes + # each movable node have its own inflation ratio, the shape of movable_nodes_ratio is (num_movable_nodes) + # we keep the centers the same + movable_nodes_ratio = new_movable_area / old_movable_area + logger.info( + "inflation ratio for movable nodes: avg/max %g/%g" % + (movable_nodes_ratio.mean(), movable_nodes_ratio.max())) + movable_nodes_ratio.sqrt_() + ## convert positions to centers + # scale size + node_size_x_movable *= movable_nodes_ratio + node_size_y_movable *= movable_nodes_ratio + ## convert back to lower left corners + + # finally scale the filler instance areas to let the total area be self.total_place_area + # all the filler nodes share the same deflation ratio, filler_nodes_ratio is a scalar + if new_movable_area_sum + old_filler_area_sum > self.total_place_area: + ##Use common filler size for both LUT/FF + + #Update for LUT fillers + new_movable_area_lut_sum = old_movable_area_lut_sum + area_increment_lut_sum * scale_factor_lut + new_lut_filler_area = F.relu(self.total_place_area/2 - new_movable_area_lut_sum)/num_lut_fillers + new_lut_filler_length = new_lut_filler_area.sqrt() + + node_size_x_filler[:num_lut_fillers] = new_lut_filler_length + node_size_y_filler[:num_lut_fillers] = new_lut_filler_length + + #Update for Flop fillers + new_movable_area_flop_sum = old_movable_area_flop_sum + area_increment_flop_sum * scale_factor_flop + new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum)/num_flop_fillers + new_flop_filler_length = new_flop_filler_area.sqrt() + + node_size_x_filler[num_lut_fillers:] = new_flop_filler_length + node_size_y_filler[num_lut_fillers:] = new_flop_filler_length + + #Old movable area for flops includes mlab placeholder filler area + if self.num_mlab_nodes > 0: + mlab_ff_mask = self.is_mlab_filler_node[num_lut_fillers:num_lut_fillers+num_flop_fillers] == 1 + mlab_size_x = node_size_x_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] + mlab_size_y = node_size_y_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] + mlab_area = mlab_size_x * mlab_size_y + #assign mlab area to placeholder fillers in FF + node_size_x_filler[num_lut_fillers:][mlab_ff_mask] = mlab_size_x + node_size_y_filler[num_lut_fillers:][mlab_ff_mask] = mlab_size_y + new_movable_area_flop_sum = (node_size_x_movable * node_size_y_movable * self.flop_mask[:self.num_movable_nodes]).sum() + if new_movable_area_flop_sum + mlab_area.sum() > self.total_place_area/2: + logger.info( + "FLOP: new movable area %.3E + mlab placeholder filler area %.3E > total_place_area %.3E - CHECK!" + % (new_movable_area_flop_sum, mlab_area.sum(),self.total_place_area/2)) + new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum - mlab_area.sum()) + new_flop_filler_area /= (num_flop_fillers - self.num_mlab_nodes) + new_flop_filler_length = new_flop_filler_area.sqrt() + node_size_x_filler[num_lut_fillers:] = new_flop_filler_length + node_size_y_filler[num_lut_fillers:] = new_flop_filler_length + #Update new_flop_filler_area to correct value + new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum) + new_flop_filler_area /= num_flop_fillers + + new_filler_area_sum = F.relu(self.total_place_area - new_movable_area_sum) + else: + new_filler_area_sum = old_filler_area_sum + new_movable_area_lut_sum = old_movable_area_lut_sum + new_lut_filler_area = old_filler_area_lut_sum/num_lut_fillers + new_movable_area_flop_sum = old_movable_area_flop_sum + new_flop_filler_area = old_filler_area_flop_sum/num_flop_fillers + + logger.info( + "old total movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (old_movable_area_sum, old_filler_area_sum, + old_movable_area_sum + old_filler_area_sum, + self.total_place_area)) + logger.info( + "LUT old movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (old_movable_area_lut_sum, old_filler_area_lut_sum, + old_movable_area_lut_sum+old_filler_area_lut_sum, + self.total_place_area/2)) + logger.info( + "FLOP old movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (old_movable_area_flop_sum, old_filler_area_flop_sum, + old_movable_area_flop_sum+old_filler_area_flop_sum, + self.total_place_area/2)) + + + logger.info( + "new total movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (new_movable_area_sum, new_filler_area_sum, + new_movable_area_sum + new_filler_area_sum, + self.total_place_area)) + logger.info( + "LUT new movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (new_movable_area_lut_sum, new_lut_filler_area * num_lut_fillers, + new_movable_area_lut_sum + new_lut_filler_area * num_lut_fillers, + self.total_place_area/2)) + logger.info( + "FLOP new movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" + % (new_movable_area_flop_sum, new_flop_filler_area * num_flop_fillers, + new_movable_area_flop_sum + new_flop_filler_area * num_flop_fillers, + self.total_place_area/2)) + + if pos.is_cuda: + func = update_pin_offset_cuda.forward + else: + func = update_pin_offset_cpp.forward + func(node_size_x, node_size_y, self.flat_node2pin_start_map, + self.flat_node2pin_map, movable_nodes_ratio, + self.num_movable_nodes, pin_offset_x, pin_offset_y) + return adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp new file mode 100644 index 00000000..fafdad39 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp @@ -0,0 +1,114 @@ +/** + * @file adjust_node_area.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Adjust cell area according to congestion map. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "adjust_node_area/src/scaling_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +DEFINE_AVERAGE_SCALING_FUNCTION(T); + +template +DEFINE_MAX_SCALING_FUNCTION(T); + +#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +int computeInstanceRoutabilityOptimizationMapLauncher( + const T *pos_x, const T *pos_y, const int *indices, const T *node_size_x, const T *node_size_y, + const T *routing_utilization_map, T xl, T yl, T bin_size_x, T bin_size_y, + int num_bins_x, int num_bins_y, int num_movable_nodes, int num_threads, + T *instance_route_area) { + const T inv_bin_size_x = 1.0 / bin_size_x; + const T inv_bin_size_y = 1.0 / bin_size_y; + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max( + int(num_movable_nodes / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int idx = 0; idx < num_movable_nodes; ++idx) { + const int i = indices[idx]; + const T x_min = pos_x[i]; + const T x_max = x_min + node_size_x[i]; + const T y_min = pos_y[i]; + const T y_max = y_min + node_size_y[i]; + + // compute the bin box that this net will affect + // We do NOT follow Wuxi's implementation. Instead, we clamp the bounding + // box. + int bin_index_xl = int((x_min - xl) * inv_bin_size_x); + int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + int bin_index_yl = int((y_min - yl) * inv_bin_size_y); + int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + instance_route_area[i] = + SCALING_OP(routing_utilization_map, xl, yl, bin_size_x, bin_size_y, + num_bins_x, num_bins_y, bin_index_xl, bin_index_yl, + bin_index_xh, bin_index_yh, x_min, y_min, x_max, y_max); + } + + return 0; +} + +at::Tensor adjust_node_area_forward(at::Tensor pos, at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor routing_utilization_map, + double bin_size_x, double bin_size_y, + double xl, double yl, double xh, double yh, + at::Tensor flop_lut_indices, + int num_movable_nodes, int num_bins_x, + int num_bins_y) { + CHECK_FLAT_CPU(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT_CPU(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT_CPU(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + CHECK_FLAT_CPU(flop_lut_indices); + CHECK_CONTIGUOUS(flop_lut_indices); + + int num_nodes = pos.numel() / 2; + at::Tensor instance_route_area = + at::zeros({num_movable_nodes}, pos.options()); + + // compute routability and density optimziation instance area + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeInstanceRoutabilityOptimizationMapLauncher", [&] { + computeInstanceRoutabilityOptimizationMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(flop_lut_indices, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(routing_utilization_map, scalar_t), xl, + yl, bin_size_x, bin_size_y, num_bins_x, num_bins_y, + flop_lut_indices.numel(), at::get_num_threads(), + DREAMPLACE_TENSOR_DATA_PTR(instance_route_area, scalar_t)); + }); + + return instance_route_area; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::adjust_node_area_forward, + "Compute adjusted area for routability optimization"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp new file mode 100644 index 00000000..274c6e4b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp @@ -0,0 +1,72 @@ +/** + * @file adjust_node_area_cuda.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Adjust cell area according to congestion map. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +int computeInstanceRoutabilityOptimizationMapCudaLauncher( + const T *pos_x, const T *pos_y, const int *indices, const T *node_size_x, const T *node_size_y, + const T *routing_utilization_map, T xl, T yl, T bin_size_x, T bin_size_y, + int num_bins_x, int num_bins_y, int num_movable_nodes, + T *instance_route_area); + +at::Tensor adjust_node_area_forward(at::Tensor pos, at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor routing_utilization_map, + double bin_size_x, double bin_size_y, + double xl, double yl, double xh, double yh, + at::Tensor flop_lut_indices, + int num_movable_nodes, int num_bins_x, + int num_bins_y) { + CHECK_FLAT_CUDA(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT_CUDA(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT_CUDA(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + CHECK_FLAT_CUDA(flop_lut_indices); + CHECK_CONTIGUOUS(flop_lut_indices); + + int num_nodes = pos.numel() / 2; + at::Tensor instance_route_area = + at::zeros({num_movable_nodes}, pos.options()); + + // compute routability and density optimziation instance area + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeInstanceRoutabilityOptimizationMapCudaLauncher", [&] { + computeInstanceRoutabilityOptimizationMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(flop_lut_indices, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(routing_utilization_map, scalar_t), xl, + yl, bin_size_x, bin_size_y, num_bins_x, num_bins_y, + flop_lut_indices.numel(), + DREAMPLACE_TENSOR_DATA_PTR(instance_route_area, scalar_t)); + }); + + return instance_route_area; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::adjust_node_area_forward, + "Compute adjusted area for routability optimization (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu new file mode 100644 index 00000000..b041e551 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu @@ -0,0 +1,105 @@ +/** + * @file adjust_node_area_cuda_kernel.cu + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Adjust cell area according to congestion map. + */ + +#include "utility/src/utils.cuh" +// local dependency +#include "adjust_node_area/src/scaling_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__device__ DEFINE_AVERAGE_SCALING_FUNCTION(T); + +template +__device__ DEFINE_MAX_SCALING_FUNCTION(T); + +template +__global__ void computeInstanceRoutabilityOptimizationMap( + const T *pos_x, const T *pos_y, const int *indices, + const T *node_size_x, const T *node_size_y, + const T *routing_utilization_map, + T xl, T yl, + T bin_size_x, T bin_size_y, + int num_bins_x, int num_bins_y, + int num_movable_nodes, + T *instance_route_area) +{ + const int idx = threadIdx.x + blockDim.x * blockIdx.x; + if (idx < num_movable_nodes) + { + const int i = indices[idx]; + const T x_min = pos_x[i]; + const T x_max = x_min + node_size_x[i]; + const T y_min = pos_y[i]; + const T y_max = y_min + node_size_y[i]; + + // compute the bin box that this net will affect + // We do NOT follow Wuxi's implementation. Instead, we clamp the bounding box. + int bin_index_xl = int((x_min - xl) / bin_size_x); + int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + int bin_index_yl = int((y_min - yl) / bin_size_y); + int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + instance_route_area[i] = SCALING_OP( + routing_utilization_map, + xl, yl, + bin_size_x, bin_size_y, + num_bins_x, num_bins_y, + bin_index_xl, + bin_index_yl, + bin_index_xh, + bin_index_yh, + x_min, y_min, x_max, y_max + ); + } +} + +template +int computeInstanceRoutabilityOptimizationMapCudaLauncher( + const T *pos_x, const T *pos_y, const int *indices, + const T *node_size_x, const T *node_size_y, + const T *routing_utilization_map, + T xl, T yl, + T bin_size_x, T bin_size_y, + int num_bins_x, int num_bins_y, + int num_movable_nodes, + T *instance_route_area) +{ + int thread_count = 512; + int block_count = ceilDiv(num_movable_nodes, thread_count); + computeInstanceRoutabilityOptimizationMap<<>>( + pos_x, pos_y, indices, + node_size_x, node_size_y, + routing_utilization_map, + xl, yl, + bin_size_x, bin_size_y, + num_bins_x, num_bins_y, + num_movable_nodes, + instance_route_area); + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeInstanceRoutabilityOptimizationMapCudaLauncher( \ + const T *pos_x, const T *pos_y, const int *indices, \ + const T *node_size_x, const T *node_size_y, \ + const T *routing_utilization_map, \ + T xl, T yl, \ + T bin_size_x, T bin_size_y, \ + int num_bins_x, int num_bins_y, \ + int num_movable_nodes, \ + T *instance_route_area); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h new file mode 100644 index 00000000..9c36e856 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h @@ -0,0 +1,67 @@ +/** + * @file scaling_function.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2020 + */ + +DREAMPLACE_BEGIN_NAMESPACE + +//#define SCALING_OP maxScaling +#define SCALING_OP averageScaling + +#define DEFINE_AVERAGE_SCALING_FUNCTION(T) \ + T averageScaling( \ + const T* routing_utilization_map, \ + T xl, T yl, \ + T bin_size_x, T bin_size_y, \ + int num_bins_x, int num_bins_y, \ + int bin_index_xl, \ + int bin_index_yl, \ + int bin_index_xh, \ + int bin_index_yh, \ + T x_min, T y_min, T x_max, T y_max \ + ) \ + { \ + T area = 0; \ + for (int x = bin_index_xl; x < bin_index_xh; ++x) \ + { \ + for (int y = bin_index_yl; y < bin_index_yh; ++y) \ + { \ + T bin_xl = xl + x * bin_size_x; \ + T bin_yl = yl + y * bin_size_y; \ + T bin_xh = bin_xl + bin_size_x; \ + T bin_yh = bin_yl + bin_size_y; \ + T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * \ + DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); \ + area += overlap * routing_utilization_map[x * num_bins_y + y]; \ + } \ + } \ + return area; \ + } + +#define DEFINE_MAX_SCALING_FUNCTION(T) \ + T maxScaling( \ + const T* routing_utilization_map, \ + T xl, T yl, \ + T bin_size_x, T bin_size_y, \ + int num_bins_x, int num_bins_y, \ + int bin_index_xl, \ + int bin_index_yl, \ + int bin_index_xh, \ + int bin_index_yh, \ + T x_min, T y_min, T x_max, T y_max \ + ) \ + { \ + T util = 0; \ + for (int x = bin_index_xl; x < bin_index_xh; ++x) \ + { \ + for (int y = bin_index_yl; y < bin_index_yh; ++y) \ + { \ + util = DREAMPLACE_STD_NAMESPACE::max(util, routing_utilization_map[x * num_bins_y + y]); \ + } \ + } \ + T area = (x_max - x_min) * (y_max - y_min); \ + return area * util; \ + } + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp new file mode 100644 index 00000000..d97fa038 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp @@ -0,0 +1,72 @@ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +void updatePinOffset(const T *node_size_x, const T *node_size_y, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, const T *node_ratios, + const int num_nodes, T *pin_offset_x, T *pin_offset_y, + const int num_threads) { +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) { + T ratio = (node_ratios[i] - 1) / 2; + T sx = node_size_x[i]; + T sy = node_size_y[i]; + + int start = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i + 1]; + for (int j = start; j < end; ++j) { + int pin_id = flat_node2pin_map[j]; + pin_offset_x[pin_id] += ratio * sx; + pin_offset_y[pin_id] += ratio * sy; + } + } +} + +void update_pin_offset_forward(at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor node_ratios, int num_movable_nodes, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y) { + CHECK_FLAT_CPU(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + + CHECK_FLAT_CPU(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + + CHECK_FLAT_CPU(node_ratios); + CHECK_CONTIGUOUS(node_ratios); + + CHECK_FLAT_CPU(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + + CHECK_FLAT_CPU(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pin_offset_x, "updatePinOffset", [&] { + updatePinOffset( + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node_ratios, scalar_t), + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + at::get_num_threads()); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::update_pin_offset_forward, + "Update pin offset with cell scaling"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp new file mode 100644 index 00000000..8ad4692f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp @@ -0,0 +1,55 @@ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +void updatePinOffsetCudaLauncher(const T *node_size_x, const T *node_size_y, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const T *node_ratios, const int num_nodes, + T *pin_offset_x, T *pin_offset_y); + +void update_pin_offset_forward(at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor node_ratios, int num_movable_nodes, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y) { + CHECK_FLAT_CUDA(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + + CHECK_FLAT_CUDA(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + + CHECK_FLAT_CUDA(node_ratios); + CHECK_CONTIGUOUS(node_ratios); + + CHECK_FLAT_CUDA(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + + CHECK_FLAT_CUDA(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pin_offset_x, "updatePinOffsetCudaLauncher", [&] { + updatePinOffsetCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node_ratios, scalar_t), + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t)); + }); +} + +DREAMPLACE_END_NAMESPACE +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::update_pin_offset_forward, + "Update pin offset with cell scaling"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu new file mode 100644 index 00000000..c3b1e9d8 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu @@ -0,0 +1,67 @@ +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void updatePinOffset( + const T* node_size_x, const T* node_size_y, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const T *node_ratios, + const int num_nodes, + T *pin_offset_x, T *pin_offset_y + ) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nodes) + { + T ratio = (node_ratios[i] - 1) / 2; + T sx = node_size_x[i]; + T sy = node_size_y[i]; + + int start = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i + 1]; + for (int j = start; j < end; ++j) + { + int pin_id = flat_node2pin_map[j]; + pin_offset_x[pin_id] += ratio * sx; + pin_offset_y[pin_id] += ratio * sy; + } + } +} + +template +void updatePinOffsetCudaLauncher( + const T* node_size_x, const T* node_size_y, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const T *node_ratios, + const int num_nodes, + T *pin_offset_x, T *pin_offset_y + ) +{ + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + updatePinOffset<<>>( + node_size_x, node_size_y, + flat_node2pin_start_map, + flat_node2pin_map, + node_ratios, + num_nodes, + pin_offset_x, pin_offset_y + ); +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template void updatePinOffsetCudaLauncher( \ + const T* node_size_x, const T* node_size_y, \ + const int *flat_node2pin_start_map, \ + const int *flat_node2pin_map, \ + const T *node_ratios, \ + const int num_nodes, \ + T *pin_offset_x, T *pin_offset_y); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt new file mode 100644 index 00000000..b9ba8e5d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt @@ -0,0 +1,36 @@ +set(OP_NAME clustering_compatibility) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(clustering_compatibility_cpp + src/lut_compatibility.cpp + src/flop_compatibility.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + clustering_compatibility_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(clustering_compatibility_cuda + src/lut_compatibility_cuda.cpp + src/lut_compatibility_cuda_kernel.cu + src/flop_compatibility_cuda.cpp + src/flop_compatibility_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + clustering_compatibility_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py new file mode 100644 index 00000000..da346be1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Oct 2020 +# + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py new file mode 100644 index 00000000..37f7a0c8 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py @@ -0,0 +1,237 @@ +''' +@File: clustering_compatibility.py +@Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) +@Date: June 2023 +''' +import math +import torch +from torch import nn +from torch.autograd import Function +import matplotlib.pyplot as plt +import pdb + +import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility_cpp as clustering_compatibility_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility_cuda as clustering_compatibility_cuda + +class LUTCompatibility(nn.Module): + def __init__(self, + lut_indices, lut_type, node_size_x, node_size_y, + num_bins_x, num_bins_y, num_bins_l, + placedb, deterministic_flag, + num_threads + ): + super(LUTCompatibility, self).__init__() + self.lut_indices = lut_indices + self.lut_type = lut_type + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.num_threads = num_threads + self.num_bins_x = num_bins_x + self.num_bins_y = num_bins_y + self.num_bins_l = num_bins_l + self.xl = placedb.xl + self.yl = placedb.yl + self.xh = placedb.xh + self.yh = placedb.yh + self.inst_stddev_x = placedb.instDemStddevX + self.inst_stddev_y = placedb.instDemStddevY + self.inst_stddev_trunc = placedb.instDemStddevTrunc + self.deterministic_flag = deterministic_flag + self.lutFracturesMap = placedb.lutFracturesMap + self.SLICE_CAPACITY = placedb.SLICE_CAPACITY + self.maxLUTSize = placedb.lutTypeInSliceUnit + self.subSlice_area = 1/placedb.HALF_SLICE_CAPACITY + + self.half_ctrl_mode = 0 + if placedb.sliceFF_ctrl_mode == "HALF": + self.half_ctrl_mode = 1 + + + def forward(self, pos): + lutType_DemMap = torch.zeros((self.num_bins_x, self.num_bins_y, self.num_bins_l), dtype=pos.dtype, device=pos.device) + resource_areas = torch.zeros(len(self.node_size_x), dtype=pos.dtype, device=pos.device) + + ext_bin = max(round(self.inst_stddev_trunc - 0.5), 0) + demandX = torch.zeros((2 * ext_bin + 1), dtype=pos.dtype, device=pos.device) + demandY = torch.zeros_like(demandX) + + lut_fracture = torch.zeros((self.num_bins_l, self.num_bins_l), dtype=torch.int, device=pos.device) + + if self.half_ctrl_mode == 0: + resource_areas[self.lut_indices.long()] = (self.node_size_x*self.node_size_y)[self.lut_indices.long()] + #Set the LUTs one sizes smaller than maxLUTSize to also be subSlice_area + val = self.maxLUTSize - 2 + large_lut_indices = torch.where(self.lut_type == val)[0] + if (large_lut_indices.shape[0] > 0): + resource_areas[large_lut_indices] = self.subSlice_area + #Set the LUTs two sizes smaller than maxLUTSize to be 0.75xsubSlice_area + val = self.maxLUTSize - 3 + large_lut_indices = torch.where(self.lut_type == val)[0] + if (large_lut_indices.shape[0] > 0): + resource_areas[large_lut_indices] = 0.75*self.subSlice_area + return resource_areas + + for i in range(self.num_bins_l): + lut_fracture[i][self.lutFracturesMap[i]] = 1 + + if pos.is_cuda: + areaMap = clustering_compatibility_cuda.lut_compatibility( + pos.view(pos.numel()), + self.lut_indices, + self.lut_type, + self.node_size_x, + self.node_size_y, + lut_fracture, + self.num_bins_x, + self.num_bins_y, + self.num_bins_l, + self.xl, + self.yl, + self.xh, + self.yh, + self.inst_stddev_x, + self.inst_stddev_y, + 1.0/self.inst_stddev_x, + 1.0/self.inst_stddev_y, + ext_bin, + self.inst_stddev_x * self.inst_stddev_y, + 1/math.sqrt(2.0), + self.deterministic_flag, + lutType_DemMap, + resource_areas + ) + else: + areaMap = clustering_compatibility_cpp.lut_compatibility( + pos.view(pos.numel()), + self.lut_indices, + self.lut_type, + self.node_size_x, + self.node_size_y, + lut_fracture, + self.num_bins_x, + self.num_bins_y, + self.num_bins_l, + self.num_threads, + self.inst_stddev_x, + self.inst_stddev_y, + 1.0/self.inst_stddev_x, + 1.0/self.inst_stddev_y, + ext_bin, + self.inst_stddev_x * self.inst_stddev_y, + 1/math.sqrt(2.0), + demandX, + demandY, + lutType_DemMap, + resource_areas + ) + + # Include post-processing if any here + resource_areas /= self.SLICE_CAPACITY + + return resource_areas + + +class FFCompatibility(nn.Module): + def __init__(self, + flop_indices, flop_ctrlSets, node_size_x, node_size_y, + num_bins_x, num_bins_y, num_bins_ck, num_bins_ce, + placedb, deterministic_flag, + num_threads + ): + super(FFCompatibility, self).__init__() + self.flop_indices = flop_indices + self.flop_ctrlSets = flop_ctrlSets + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.num_bins_x = num_bins_x + self.num_bins_y = num_bins_y + self.num_bins_ck = num_bins_ck + self.num_bins_ce = num_bins_ce + self.num_threads = num_threads + self.deterministic_flag = deterministic_flag + self.xl = placedb.xl + self.yl = placedb.yl + self.xh = placedb.xh + self.yh = placedb.yh + self.inst_stddev_x = placedb.instDemStddevX + self.inst_stddev_y = placedb.instDemStddevY + self.inst_stddev_trunc = placedb.instDemStddevTrunc + self.SLICE_CAPACITY = placedb.SLICE_CAPACITY + + self.half_ctrl_mode = 0 + if placedb.sliceFF_ctrl_mode == "HALF": + self.half_ctrl_mode = 1 + + def forward(self, pos): + resource_areas = torch.zeros(len(self.node_size_x), dtype=pos.dtype, device=pos.device) + + if self.half_ctrl_mode == 0 and self.num_bins_ck == 1: + resource_areas[self.flop_indices.long()] = (self.node_size_x*self.node_size_y)[self.flop_indices.long()] + return resource_areas + + flopType_DemMap = torch.zeros((self.num_bins_x, self.num_bins_y, self.num_bins_ck, self.num_bins_ce), dtype=pos.dtype, device=pos.device) + + ext_bin = max(round(self.inst_stddev_trunc - 0.5), 0) + demandX = torch.zeros((2 * ext_bin + 1), dtype=pos.dtype, device=pos.device) + demandY = torch.zeros_like(demandX) + + if pos.is_cuda: + areaMap = clustering_compatibility_cuda.flop_compatibility( + pos.view(pos.numel()), + self.flop_indices, + self.flop_ctrlSets, + self.node_size_x, + self.node_size_y, + self.num_bins_x, + self.num_bins_y, + self.num_bins_ck, + self.num_bins_ce, + self.xl, + self.yl, + self.xh, + self.yh, + self.inst_stddev_x, + self.inst_stddev_y, + 1.0/self.inst_stddev_x, + 1.0/self.inst_stddev_y, + ext_bin, + self.inst_stddev_x * self.inst_stddev_y, + 1/math.sqrt(2.0), + self.SLICE_CAPACITY, + self.deterministic_flag, + flopType_DemMap, + resource_areas + ) + else: + areaMap = clustering_compatibility_cpp.flop_compatibility( + pos.view(pos.numel()), + self.flop_indices, + self.flop_ctrlSets, + self.node_size_x, + self.node_size_y, + self.num_bins_x, + self.num_bins_y, + self.num_bins_ck, + self.num_bins_ce, + self.num_threads, + self.inst_stddev_x, + self.inst_stddev_y, + 1.0/self.inst_stddev_x, + 1.0/self.inst_stddev_y, + ext_bin, + self.inst_stddev_x * self.inst_stddev_y, + 1/math.sqrt(2.0), + self.SLICE_CAPACITY, + demandX, + demandY, + flopType_DemMap, + resource_areas + ) + + # Include post-processing if any here + resource_areas /= self.SLICE_CAPACITY + + return resource_areas + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp new file mode 100644 index 00000000..3941a2e4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp @@ -0,0 +1,328 @@ +/** + * @file flop_compatibility.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for FLOP based on elfPlace. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "clustering_compatibility/src/functions.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define gaussian_auc_function +template +DEFINE_GAUSSIAN_AUC_FUNCTION(T); +/// define smooth_ceil_function +template +DEFINE_SMOOTH_CEIL_FUNCTION(T); +/// define flop_aggregate_demand_function +template +DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(T); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map node by node +template +int fillDemandMapFF(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + int num_threads, int num_nodes, + T stddev_x, T stddev_y, + T inv_stddev_x, T inv_stddev_y, + int ext_bin, T inv_sqrt, + T *demandX, T *demandY, T *demMap) +{ + + //unsigned int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + unsigned int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + unsigned int o_p = num_bins_ck * num_bins_ce; + for (unsigned int i = 0; i < num_nodes; ++i) + { + const unsigned int nIdx = indices[i]; + T node_x = pos_x[nIdx] + 0.5 * node_size_x[nIdx]; + T node_y = pos_y[nIdx] + 0.5 * node_size_y[nIdx]; + //Ctrl set values + int cksr = ctrlSets[i*3 + 1]; + int ce = ctrlSets[i*3 + 2]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + // compute the bin box that this net will affect + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); + T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); + + T sf = 1.0 / (gaussianX * gaussianY); + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + demandX[x - bin_index_xl] = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); + } + + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + demandY[y - bin_index_yl] = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); + } + + ////DBG + //std::cout << "x limit: " << bin_index_xl << " to " << bin_index_xh << std::endl; + //std::cout << "y limit: " << bin_index_yl << " to " << bin_index_yh << std::endl; + ////DBG + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + //#pragma omp atomic update + unsigned int idx = x * n_o_p + y * o_p + cksr * num_bins_ce + ce; + T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; + ////DBG + //std::cout << "idx = " << x << "(x) * " << n_o_p << "(n_o_p) + " + // << y << "(y) * " << o_p << "(o_p) + " + // << cksr << "(cksr) * " << num_bins_ce << "(num_bins_ce) + " + // << ce << "(ce)" << std::endl; + //std::cout << "FillDemandMapFF: Add " << dem << " to demMap at " << idx << std::endl; + ////DBG + demMap[idx] += dem; + } + } + } + return 0; +} + + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaFF(const T *demMap, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + int num_threads, T stddev_x, T stddev_y, + int ext_bin, T bin_area, T half_slice, T *areaMap) +{ + + int total_bins = num_bins_x*num_bins_y; + int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + int o_p = num_bins_ck * num_bins_ce; + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(total_bins / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < total_bins; ++i) + { + ////DBG + //std::cout << "Start for Bin " << i << " out of " << total_bins << std::endl; + ////DBG + + int binX = int(i/num_bins_y); + int binY = int(i%num_bins_y); + + // compute the bin box + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + int index = binX * n_o_p + binY * o_p; + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + if (x != binX && y != binY) + { + unsigned int idx = x * n_o_p + y * o_p; + flop_aggregate_demand_function(demMap, idx, areaMap, index, num_bins_ck, num_bins_ce); + } + } + } + ////DBG + //std::cout << "For Bin " << i << " completed flop_aggregate_demand_function for x: " << bin_index_xl << " -> " << bin_index_xh + // << " and y : " << bin_index_yl << " -> " << bin_index_yh << std::endl; + ////DBG + + //Flop compute areas + for (int ck = 0; ck < num_bins_ck; ++ck) + { + T totalQ = 0.0; + for (int ce = 0; ce < num_bins_ce; ++ce) + { + unsigned int updIdx = index + ck*num_bins_ce + ce; + if (areaMap[updIdx] > 0.0) + { + totalQ += smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); + } + } + + T sf = half_slice * smooth_ceil_function(totalQ*0.5, 0.5) / totalQ; + + for (int cE = 0; cE < num_bins_ce; ++cE) + { + unsigned int updIdx = index + ck*num_bins_ce + cE; + if (areaMap[updIdx] > 0.0) + { + T qrt = smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); + areaMap[updIdx] = sf * qrt / areaMap[updIdx]; + } + } + } + ////DBG + //std::cout << "For Bin " << i << " completed flop area compute for num_bins_ck: " << num_bins_ck + // << " and num_bins_ce: " << num_bins_ce << std::endl; + ////DBG + + } + return 0; +} + + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasFF(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T *areaMap, + int num_threads, int num_nodes, + T inv_stddev_x, T inv_stddev_y, + T *instAreas) +{ + + int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + int o_p = num_bins_ck * num_bins_ce; + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (unsigned int i = 0; i < num_nodes; ++i) + { + const unsigned int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + //Ctrl set values + int cksr = ctrlSets[i*3 + 1]; + int ce = ctrlSets[i*3 + 2]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + unsigned int index = binX * n_o_p + binY * o_p + cksr * num_bins_ce + ce; + instAreas[idx] = areaMap[index]; + } + return 0; +} + +at::Tensor flop_compatibility( + at::Tensor pos, + at::Tensor indices, + at::Tensor ctrlSets, + at::Tensor node_size_x, + at::Tensor node_size_y, + int num_bins_x, + int num_bins_y, + int num_bins_ck, + int num_bins_ce, + int num_threads, + double stddev_x, + double stddev_y, + double inv_stddev_x, + double inv_stddev_y, + int ext_bin, + double bin_area, + double inv_sqrt, + int slice_capacity, + at::Tensor demandX, + at::Tensor demandY, + at::Tensor demMap, + at::Tensor rsrcAreas + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(indices); + CHECK_CONTIGUOUS(indices); + + CHECK_FLAT(ctrlSets); + CHECK_CONTIGUOUS(ctrlSets); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + int num_nodes = pos.numel() / 2; + double half_slice = slice_capacity / 2.0; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapFF", [&] { + fillDemandMapFF( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + num_threads, indices.numel(), + stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt, + DREAMPLACE_TENSOR_DATA_PTR(demandX, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(demandY, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); + }); + + //std::cout << "Completed fillDemandMapFF" << std::endl; + + at::Tensor areaMap = demMap.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaFF", [&] { + computeInstanceAreaFF( + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), + num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + num_threads, + stddev_x, stddev_y, ext_bin, bin_area, half_slice, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); + }); + //std::cout << "Completed computeInstanceAreaFF" << std::endl; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasFF", [&] { + collectInstanceAreasFF( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_y, num_bins_ck, num_bins_ce, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), + num_threads, indices.numel(), + inv_stddev_x, inv_stddev_y, + DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); + }); + //std::cout << "Completed collectInstanceAreasFF" << std::endl; + + return areaMap; +} + +DREAMPLACE_END_NAMESPACE + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp new file mode 100644 index 00000000..bdbcdd4a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp @@ -0,0 +1,151 @@ +/** + * @file flop_compatibility_cuda.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for Flop based on elfPlace. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map node by node +template +int fillDemandMapFFCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T xl, const T yl, + const T xh, const T yh, + const int num_nodes, + const T stddev_x, const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt, + const int deterministic_flag, + T *demMap); + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaFFCuda(const T *demMap, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T stddev_x, const T stddev_y, + const int ext_bin, const T bin_area, + const T half_slice, T *areaMap); + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasFFCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T *areaMap, + const int num_nodes, + const T inv_stddev_x, + const T inv_stddev_y, + T *instAreas); + +at::Tensor flop_compatibility( + at::Tensor pos, + at::Tensor indices, + at::Tensor ctrlSets, + at::Tensor node_size_x, + at::Tensor node_size_y, + int num_bins_x, + int num_bins_y, + int num_bins_ck, + int num_bins_ce, + double xl, + double yl, + double xh, + double yh, + double stddev_x, + double stddev_y, + double inv_stddev_x, + double inv_stddev_y, + int ext_bin, + double bin_area, + double inv_sqrt, + int slice_capacity, + int deterministic_flag, + at::Tensor demMap, + at::Tensor rsrcAreas + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(indices); + CHECK_CONTIGUOUS(indices); + + CHECK_FLAT(ctrlSets); + CHECK_CONTIGUOUS(ctrlSets); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + int num_nodes = pos.numel() / 2; + double half_slice = slice_capacity / 2.0; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapFFCuda", [&] { + fillDemandMapFFCuda( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + xl, yl, xh, yh, indices.numel(), + stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt, + deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); + }); + + at::Tensor areaMap = demMap.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaFFCuda", [&] { + computeInstanceAreaFFCuda( + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), + num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + stddev_x, stddev_y, ext_bin, bin_area, half_slice, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); + }); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasFFCuda", [&] { + collectInstanceAreasFFCuda( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_y, num_bins_ck, num_bins_ce, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), + indices.numel(), inv_stddev_x, inv_stddev_y, + DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); + }); + + return areaMap; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu new file mode 100644 index 00000000..cdaf70a1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu @@ -0,0 +1,330 @@ +/** + * @file lut_compatibility_cuda_kernel.cu + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for LUT based on elfPlace. + */ + +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" +// local dependency +#include "clustering_compatibility/src/functions.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define gaussian_auc_function +template +inline __device__ DEFINE_GAUSSIAN_AUC_FUNCTION(T); +/// define smooth_ceil_function +template +inline __device__ DEFINE_SMOOTH_CEIL_FUNCTION(T); +/// define flop_aggregate_demand_function +template +inline __device__ DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(T); + +template +__global__ void fillDemandMapFF(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const int num_nodes, + const T stddev_x, const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt, + AtomicOp atomic_add_op, + typename AtomicOp::type* demMap) +{ + const unsigned int i = threadIdx.x + blockDim.x * blockIdx.x; + int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + int o_p = num_bins_ck * num_bins_ce; + if (i < num_nodes) + { + const unsigned int nIdx = indices[i]; + T node_x = pos_x[nIdx] + 0.5 * node_size_x[nIdx]; + T node_y = pos_y[nIdx] + 0.5 * node_size_y[nIdx]; + //Ctrl set values + int cksr = ctrlSets[i*3 + 1]; + int ce = ctrlSets[i*3 + 2]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + // compute the bin box that this net will affect + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); + T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); + + T sf = 1.0 / (gaussianX * gaussianY); + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + T dem_xmbin_index_xl = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + unsigned int idx = x * n_o_p + y * o_p + cksr * num_bins_ce + ce; + T dem_ymbin_index_yl = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); + //T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; + T dem = sf * dem_xmbin_index_xl * dem_ymbin_index_yl; + //demMap[idx] += dem; + atomic_add_op(&demMap[idx], dem); + } + } + } +} + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +__global__ void computeInstanceAreaFF(const T *demMap, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T stddev_x, const T stddev_y, const int ext_bin, + const T bin_area, const T half_slice, T *areaMap) +{ + const unsigned int i = threadIdx.x + blockDim.x * blockIdx.x; + int total_bins = num_bins_x*num_bins_y; + int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + int o_p = num_bins_ck * num_bins_ce; + if (i < total_bins) + { + int binX = int(i/num_bins_y); + int binY = int(i%num_bins_y); + + // compute the bin box + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + int index = binX * n_o_p + binY * o_p; + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + if (x != binX && y != binY) + { + unsigned int idx = x * n_o_p + y * o_p; + flop_aggregate_demand_function(demMap, idx, areaMap, index, num_bins_ck, num_bins_ce); + } + } + } + + //Flop compute areas + for (int ck = 0; ck < num_bins_ck; ++ck) + { + T totalQ = 0.0; + for (int ce = 0; ce < num_bins_ce; ++ce) + { + unsigned int updIdx = index + ck*num_bins_ce + ce; + if (areaMap[updIdx] > 0.0) + { + totalQ += smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); + } + } + + T sf = half_slice * smooth_ceil_function(totalQ*0.5, 0.5) / totalQ; + + for (int cE = 0; cE < num_bins_ce; ++cE) + { + unsigned int updIdx = index + ck*num_bins_ce + cE; + if (areaMap[updIdx] > 0.0) + { + T qrt = smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); + areaMap[updIdx] = sf * qrt / areaMap[updIdx]; + } + } + } + } +} + + +// Set a set of instance area in a area vector based on the given area map +template +__global__ void collectInstanceAreasFF(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T *areaMap, + const int num_nodes, const T inv_stddev_x, + const T inv_stddev_y, + T *instAreas) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; + int o_p = num_bins_ck * num_bins_ce; + if (i < num_nodes) + { + const unsigned int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + //Ctrl set values + int cksr = ctrlSets[i*3 + 1]; + int ce = ctrlSets[i*3 + 2]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + unsigned int index = binX * n_o_p + binY * o_p + cksr * num_bins_ce + ce; + instAreas[idx] = areaMap[index]; + } +} + +// fill the demand map net by net +template +int fillDemandMapFFCuda(const T *pos_x, const T *pos_y, + const int *indices, const int *ctrlSets, + const T *node_size_x, const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const T stddev_x, const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt, + const int deterministic_flag, + T *demMap) +{ + if (deterministic_flag == 1) + { + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_bins_x * num_bins_y * num_bins_ck * num_bins_ce; + unsigned long long int *buf_map = NULL; + allocateCUDA(buf_map, num_bins, unsigned long long int); + + AtomicAddCUDA atomic_add_op(scale_factor); + + int thread_count = 512; + int block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + buf_map, demMap, scale_factor, num_bins); + + block_count = ceilDiv(num_nodes, thread_count); + fillDemandMapFF<<>>( + pos_x, pos_y, indices, ctrlSets, node_size_x, + node_size_y, num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + num_nodes, stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, + ext_bin, inv_sqrt, + atomic_add_op, buf_map + ); + + block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + demMap, buf_map, T(1.0 / scale_factor), num_bins); + + destroyCUDA(buf_map); + + } else + { + AtomicAddCUDA atomic_add_op; + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + fillDemandMapFF<<>>( + pos_x, pos_y, indices, ctrlSets, node_size_x, + node_size_y, num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + num_nodes, stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, + ext_bin, inv_sqrt, + atomic_add_op, demMap + ); + } + return 0; +} + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaFFCuda(const T *demMap, + const int num_bins_x, const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T stddev_x, const T stddev_y, const int ext_bin, + const T bin_area, const T half_slice, T *areaMap) +{ + int thread_count = 512; + int block_count = ceilDiv(num_bins_x*num_bins_y, thread_count); + computeInstanceAreaFF<<>>( + demMap, + num_bins_x, num_bins_y, + num_bins_ck, num_bins_ce, + stddev_x, stddev_y, + ext_bin, bin_area, + half_slice, areaMap + ); + return 0; +} + + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasFFCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *ctrlSets, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_ck, const int num_bins_ce, + const T *areaMap, + const int num_nodes, + const T inv_stddev_x, const T inv_stddev_y, + T *instAreas) +{ + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + collectInstanceAreasFF<<>>( + pos_x, pos_y, + indices, ctrlSets, + node_size_x, node_size_y, + num_bins_y, num_bins_ck, + num_bins_ce, + areaMap, num_nodes, + inv_stddev_x, inv_stddev_y, + instAreas + ); + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int fillDemandMapFFCuda( \ + const T *pos_x, const T *pos_y, const int *indices, const int *ctrlSets, \ + const T *node_size_x, const T *node_size_y, const int num_bins_x, \ + const int num_bins_y, const int num_bins_ck, const int num_bins_ce, \ + const T xl, const T yl, const T xh, const T yh, const int num_nodes, \ + const T stddev_x, const T stddev_y, const T inv_stddev_x, const T inv_stddev_y, \ + const int ext_bin, const T inv_sqrt, const int deterministic_flag, T *demMap); \ + \ + template int computeInstanceAreaFFCuda( \ + const T *demMap, const int num_bins_x, const int num_bins_y, \ + const int num_bins_ck, const int num_bins_ce, const T stddev_x, \ + const T stddev_y, const int ext_bin, const T bin_area, const T half_slice, \ + T *areaMap); \ + \ + template int collectInstanceAreasFFCuda( \ + const T *pos_x, const T *pos_y, const int *indices, const int *ctrlSets, \ + const T *node_size_x, const T *node_size_y, const int num_bins_y, \ + const int num_bins_ck, const int num_bins_ce, const T *areaMap, \ + const int num_nodes, const T inv_stddev_x, const T inv_stddev_y, T *instAreas); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h new file mode 100644 index 00000000..e6c8fede --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h @@ -0,0 +1,131 @@ +/** + * @file functions.h + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + */ + +#ifndef DREAMPLACE_CLUSTERING_COMPATIBILITY_FUNCTIONS_H +#define DREAMPLACE_CLUSTERING_COMPATIBILITY_FUNCTIONS_H + +DREAMPLACE_BEGIN_NAMESPACE + +#define DEFINE_GAUSSIAN_AUC_FUNCTION(type) \ + T gaussian_auc_function(T mu, T sigma, T xLo, T xHi, T invSqrt) \ + { \ + T a = invSqrt / sigma; \ + T hiVal = DREAMPLACE_STD_NAMESPACE::erfc((mu - xHi) * a); \ + T loVal = DREAMPLACE_STD_NAMESPACE::erfc((mu - xLo) * a); \ + return T(0.5) * (hiVal - loVal); \ + } + +#define DEFINE_SMOOTH_CEIL_FUNCTION(type) \ + T smooth_ceil_function(T val, T threshold) \ + { \ + T r = DREAMPLACE_STD_NAMESPACE::fmod(val, T(1.0)); \ + T interm = DREAMPLACE_STD_NAMESPACE::min(r/threshold, T(1.0)); \ + return val - r + interm; \ + } + +//LUT +// Reuse version +//// Note the following mergings are feasible +//// LUT1 and {LUT1, LUT2, LUT3, LUT4} +//// LUT2 and {LUT2, LUT3} +// +//// Compute white space area in the window +//RealType totalDem = std::accumulate(dem.begin(), dem.end(), 0.0); +//RealType space = std::max(winArea - totalDem, 0.0); +//RealType totalArea = totalDem + space; +// +//area[0] = (dem[0] + dem[1] + dem[2] + dem[3] + 2.0 * (dem[4] + dem[5] + space)) / totalArea; +//area[1] = (dem[0] + dem[1] + dem[2] + 2.0 * (dem[3] + dem[4] + dem[5] + space)) / totalArea; +//area[2] = (dem[0] + dem[1] + 2.0 * (dem[2] + dem[3] + dem[4] + dem[5] + space)) / totalArea; +//area[3] = (dem[0] + 2.0 * (dem[1] + dem[2] + dem[3] + dem[4] + dem[5] + space)) / totalArea; +//area[4] = 2.0; +//area[5] = 2.0; + +//Hard-coded version for Ultrascale +#define DEFINE_LUT_COMPUTE_AREAS_FUNCTION(type) \ + void lut_compute_areas_function(const T winArea, T* area, const int idx, const int lBins) \ + { \ + T totalDem = T(0.0); \ + for (int x = 0; x < lBins; ++x) \ + { \ + totalDem += area[idx + x]; \ + } \ + T space = DREAMPLACE_STD_NAMESPACE::max(winArea - totalDem, T(0.0)); \ + T totalArea = totalDem + space; \ + space += space; \ + \ + T sum23 = area[idx+2] + area[idx+3]; \ + T sum45 = area[idx+4] + area[idx+5]; \ + T sum3 = area[idx+3]; \ + T sum0 = area[idx]; \ + area[idx] = (totalDem + sum45 + space) / totalArea; \ + area[idx+1] = (totalDem + sum3 + sum45 + space) / totalArea; \ + area[idx+2] = (totalDem + sum23 + sum45 + space) / totalArea; \ + area[idx+3] = (T(2.0) * totalDem - sum0 + space) / totalArea; \ + area[idx+4] = T(2.0); \ + area[idx+5] = area[idx+4]; \ + } + +//Generic version based on lut_fracture +#define DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(type) \ + void lut_compute_areas_function_generic(const int* lut_fracture, const T winArea, T* area, const int idx, const int lBins) \ + { \ + T totalDem = T(0.0); \ + T dem[16]; \ + for (int x = 0; x < lBins; ++x) \ + { \ + totalDem += area[idx + x]; \ + dem[x] = area[idx + x]; \ + } \ + T space = DREAMPLACE_STD_NAMESPACE::max(winArea - totalDem, T(0.0)); \ + T totalArea = totalDem + space; \ + space += space; \ + \ + for (int lt = 0; lt < lBins; ++lt) \ + {\ + char fracture(0);\ + T val = T(0.0);\ + for (int lf = 0; lf < lBins; ++lf)\ + {\ + int lfId = lt*lBins + lf;\ + if (lut_fracture[lfId] == 1)\ + {\ + val += dem[lf];\ + fracture = 1;\ + } else\ + {\ + val += T(2.0)*dem[lf]; \ + }\ + }\ + if (fracture == 1)\ + {\ + area[idx+lt] = (val + space)/totalArea;\ + } else\ + {\ + area[idx+lt] = T(2.0);\ + }\ + }\ + } + +//Flop + +#define DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(type) \ + void flop_aggregate_demand_function(const T* demMap, unsigned int dIdx, T* resMap, int rIdx, const int ckSize, const int ceSize) \ + { \ + for (int ck = 0; ck < ckSize; ++ck) \ + { \ + for (int ce = 0; ce < ceSize; ++ce) \ + { \ + int cIdx = ck*ceSize + ce; \ + resMap[rIdx+cIdx] += demMap[dIdx+cIdx]; \ + } \ + } \ + } + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp new file mode 100644 index 00000000..ca5fdb35 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp @@ -0,0 +1,283 @@ +/** + * @file lut_compatibility.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for LUT based on elfPlace. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "clustering_compatibility/src/functions.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define gaussian_auc_function +template +DEFINE_GAUSSIAN_AUC_FUNCTION(T); +/// define lut_compute_areas_function +template +DEFINE_LUT_COMPUTE_AREAS_FUNCTION(T); +template +DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(T); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + + +// fill the demand map node by node +template +int fillDemandMapLUT(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_bins_l, + int num_threads, int num_nodes, + T stddev_x, T stddev_y, + T inv_stddev_x, T inv_stddev_y, + int ext_bin, T inv_sqrt2, + T *demandX, T *demandY, T *demMap) +{ + + //int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + const int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + int lutType = type[idx]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + // compute the bin box that this net will affect + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt2); + T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt2); + + T sf = 1.0 / (gaussianX * gaussianY); + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + demandX[x - bin_index_xl] = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt2); + } + + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + demandY[y - bin_index_yl] = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt2); + } + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + //#pragma omp atomic update + int index = x * (num_bins_y * num_bins_l) + y * num_bins_l + lutType; + T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; + demMap[index] += dem; + } + } + } + return 0; +} + + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaLUT(const T *demMap, const int* lut_fracture, + const int num_bins_x, const int num_bins_y, + const int num_bins_l, + int num_threads, T stddev_x, T stddev_y, + int ext_bin, T bin_area, T *areaMap) +{ + + int total_bins = num_bins_x*num_bins_y; + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(total_bins / num_threads / 16), 1); + //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < total_bins; ++i) + { + int binX = int(i/num_bins_y); + int binY = int(i%num_bins_y); + + // compute the bin box + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + int idx = binX * (num_bins_y * num_bins_l) + binY * num_bins_l; + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + int index = x * (num_bins_y * num_bins_l) + y * num_bins_l; + if (x != binX && y != binY) + { + for (int l = 0; l < num_bins_l; ++l) + { + areaMap[idx + l] += demMap[index + l]; + //tempMap[idx + l] += demMap[index + l]; + } + } + } + } + + // Compute instance areas based on the window demand distribution + T winArea = (bin_index_xh - bin_index_xl) * (bin_index_yh - bin_index_yl) * bin_area; + + //lut_compute_areas_function(winArea, tempMap, idx, num_bins_l); + lut_compute_areas_function_generic(lut_fracture, winArea, areaMap, idx, num_bins_l); + } + return 0; +} + + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasLUT(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_l, + const T *areaMap, + int num_threads, int num_nodes, + T inv_stddev_x, T inv_stddev_y, + T *instAreas) +{ + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + const int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + int index = binX * (num_bins_y * num_bins_l) + binY * num_bins_l + type[idx]; + instAreas[idx] = areaMap[index]; + } + return 0; +} + +at::Tensor lut_compatibility( + at::Tensor pos, + at::Tensor indices, + at::Tensor type, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor lut_fracture, + int num_bins_x, + int num_bins_y, + int num_bins_l, + int num_threads, + double stddev_x, + double stddev_y, + double inv_stddev_x, + double inv_stddev_y, + int ext_bin, + double bin_area, + double inv_sqrt2, + at::Tensor demandX, + at::Tensor demandY, + at::Tensor demMap, + at::Tensor rsrcAreas + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(indices); + CHECK_CONTIGUOUS(indices); + + CHECK_FLAT(type); + CHECK_CONTIGUOUS(type); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + int num_nodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapLUT", [&] { + fillDemandMapLUT( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(type, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_x, num_bins_y, num_bins_l, + num_threads, indices.numel(), + stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt2, + DREAMPLACE_TENSOR_DATA_PTR(demandX, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(demandY, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); + }); + + at::Tensor areaMap = demMap.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaLUT", [&] { + computeInstanceAreaLUT( + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(lut_fracture, int), + num_bins_x, num_bins_y, num_bins_l, num_threads, + stddev_x, stddev_y, ext_bin, bin_area, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); + }); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasLUT", [&] { + collectInstanceAreasLUT( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(type, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_y, num_bins_l, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), + num_threads, indices.numel(), + inv_stddev_x, inv_stddev_y, + DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); + }); + + return areaMap; +} + +at::Tensor flop_compatibility( + at::Tensor pos, at::Tensor indices, at::Tensor ctrlSets, + at::Tensor node_size_x, at::Tensor node_size_y, int num_bins_x, + int num_bins_y, int num_bins_ck, int num_bins_ce, + int num_threads, double stddev_x, double stddev_y, + double inv_stddev_x, double inv_stddev_y, int ext_bin, + double bin_area, double inv_sqrt, int slice_capacity, + at::Tensor demandX, at::Tensor demandY, at::Tensor demMap, + at::Tensor rsrcAreas); + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("lut_compatibility", &DREAMPLACE_NAMESPACE::lut_compatibility, "compute LUT compatibility instance areas"); + m.def("flop_compatibility", &DREAMPLACE_NAMESPACE::flop_compatibility, "compute Flop compatibility instance areas"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp new file mode 100644 index 00000000..1c9405ea --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp @@ -0,0 +1,165 @@ +/** + * @file lut_compatibility_cuda.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for LUT based on elfPlace. + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map node by node +template +int fillDemandMapLUTCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_l, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const T stddev_x, const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt2, + const int deterministic_flag, + T *demMap); + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaLUTCuda(const T *demMap, + const int *lut_fracture, + const int num_bins_x, + const int num_bins_y, + const int num_bins_l, + const T stddev_x, const T stddev_y, + const int ext_bin, const T bin_area, + T *areaMap); + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasLUTCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_l, + const T *areaMap, + const int num_nodes, + const T inv_stddev_x, + const T inv_stddev_y, + T *instAreas); + +at::Tensor lut_compatibility( + at::Tensor pos, + at::Tensor indices, + at::Tensor type, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor lut_fracture, + int num_bins_x, + int num_bins_y, + int num_bins_l, + double xl, + double yl, + double xh, + double yh, + double stddev_x, + double stddev_y, + double inv_stddev_x, + double inv_stddev_y, + int ext_bin, + double bin_area, + double inv_sqrt2, + int deterministic_flag, + at::Tensor demMap, + at::Tensor rsrcAreas + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(indices); + CHECK_CONTIGUOUS(indices); + + CHECK_FLAT(type); + CHECK_CONTIGUOUS(type); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + int num_nodes = pos.numel() / 2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapLUTCuda", [&] { + fillDemandMapLUTCuda( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(type, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_x, num_bins_y, num_bins_l, + xl, yl, xh, yh, indices.numel(), + stddev_x, stddev_y, + inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt2, + deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); + }); + + at::Tensor areaMap = demMap.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaLUTCuda", [&] { + computeInstanceAreaLUTCuda( + DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(lut_fracture, int), + num_bins_x, num_bins_y, num_bins_l, + stddev_x, stddev_y, ext_bin, bin_area, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); + }); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasLUTCuda", [&] { + collectInstanceAreasLUTCuda( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(indices, int), + DREAMPLACE_TENSOR_DATA_PTR(type, int), + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + num_bins_y, num_bins_l, + DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), + indices.numel(), inv_stddev_x, inv_stddev_y, + DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); + }); + + return areaMap; +} + +at::Tensor flop_compatibility( + at::Tensor pos, at::Tensor indices, at::Tensor ctrlSets, + at::Tensor node_size_x, at::Tensor node_size_y, + int num_bins_x, int num_bins_y, int num_bins_ck, + int num_bins_ce, double xl, double yl, double xh, + double yh, double stddev_x, double stddev_y, + double inv_stddev_x, double inv_stddev_y, int ext_bin, + double bin_area, double inv_sqrt, int slice_capacity, + int deterministic_flag, at::Tensor demMap, at::Tensor rsrcAreas); + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("lut_compatibility", &DREAMPLACE_NAMESPACE::lut_compatibility, "compute LUT compatibility instance areas (CUDA)"); + m.def("flop_compatibility", &DREAMPLACE_NAMESPACE::flop_compatibility, "compute Flop compatibility instance areas (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu new file mode 100644 index 00000000..f35274bd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu @@ -0,0 +1,309 @@ +/** + * @file lut_compatibility_cuda_kernel.cu + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute the Clustering compatibility map for LUT based on elfPlace. + */ + +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" +// local dependency +#include "clustering_compatibility/src/functions.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define gaussian_auc_function +template +inline __device__ DEFINE_GAUSSIAN_AUC_FUNCTION(T); +/// define lut_compute_areas_function +template +inline __device__ DEFINE_LUT_COMPUTE_AREAS_FUNCTION(T); +template +inline __device__ DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(T); + +template +__global__ void fillDemandMapLUT(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_bins_l, + const int num_nodes, const T stddev_x, + const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt, + AtomicOp atomic_add_op, + typename AtomicOp::type* demMap) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nodes) + { + const int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + int lutType = type[idx]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + // compute the bin box that this net will affect + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); + T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); + + T sf = 1.0 / (gaussianX * gaussianY); + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + T dem_xmbin_index_xl = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + //#pragma omp atomic update + int index = x * (num_bins_y * num_bins_l) + y * num_bins_l + lutType; + T dem_ymbin_index_yl = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); + //T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; + T dem = sf * dem_xmbin_index_xl * dem_ymbin_index_yl; + //demMap[index] += dem; + atomic_add_op(&demMap[index], dem); + } + } + } +} + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +__global__ void computeInstanceAreaLUT(const T *demMap, + const int* lut_fracture, + const int num_bins_x, + const int num_bins_y, + const int num_bins_l, + const T stddev_x, const T stddev_y, + const int ext_bin, const T bin_area, + T *areaMap) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + int total_bins = num_bins_x*num_bins_y; + if (i < total_bins) + { + int binX = int(i/num_bins_y); + int binY = int(i%num_bins_y); + + // compute the bin box + int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); + int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); + + int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); + int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); + + int idx = binX * (num_bins_y * num_bins_l) + binY * num_bins_l; + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + int index = x * (num_bins_y * num_bins_l) + y * num_bins_l; + if (x != binX && y != binY) + { + for (int l = 0; l < num_bins_l; ++l) + { + areaMap[idx + l] += demMap[index + l]; + } + } + } + } + + // Compute instance areas based on the window demand distribution + T winArea = (bin_index_xh - bin_index_xl) * (bin_index_yh - bin_index_yl) * bin_area; + //lut_compute_areas_function(winArea, areaMap, idx, num_bins_l); + lut_compute_areas_function_generic(lut_fracture, winArea, areaMap, idx, num_bins_l); + } +} + + +// Set a set of instance area in a area vector based on the given area map +template +__global__ void collectInstanceAreasLUT(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_l, + const T *areaMap, + const int num_nodes, + const T inv_stddev_x, + const T inv_stddev_y, + T *instAreas) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nodes) + { + const int idx = indices[i]; + T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; + T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; + + int binX = int(node_x * inv_stddev_x); + int binY = int(node_y * inv_stddev_y); + + int index = binX * (num_bins_y * num_bins_l) + binY * num_bins_l + type[idx]; + instAreas[idx] = areaMap[index]; + } +} + +// fill the demand map net by net +template +int fillDemandMapLUTCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_x, const int num_bins_y, + const int num_bins_l, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const T stddev_x, const T stddev_y, + const T inv_stddev_x, const T inv_stddev_y, + const int ext_bin, const T inv_sqrt, + const int deterministic_flag, + T *demMap) +{ + if (deterministic_flag == 1) + { + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_bins_x * num_bins_y * num_bins_l; + unsigned long long int *buf_map = NULL; + allocateCUDA(buf_map, num_bins, unsigned long long int); + + AtomicAddCUDA atomic_add_op(scale_factor); + + int thread_count = 512; + int block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + buf_map, demMap, scale_factor, num_bins); + + block_count = ceilDiv(num_nodes, thread_count); + fillDemandMapLUT<<>>( + pos_x, pos_y, indices, type, node_size_x, + node_size_y, num_bins_x, num_bins_y, num_bins_l, + num_nodes, stddev_x, stddev_y, inv_stddev_x, + inv_stddev_y, ext_bin, inv_sqrt, + atomic_add_op, buf_map + ); + + block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + demMap, buf_map, T(1.0 / scale_factor), num_bins); + + destroyCUDA(buf_map); + + } else + { + AtomicAddCUDA atomic_add_op; + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + fillDemandMapLUT<<>>( + pos_x, pos_y, indices, type, node_size_x, + node_size_y, num_bins_x, num_bins_y, num_bins_l, + num_nodes, stddev_x, stddev_y, inv_stddev_x, + inv_stddev_y, ext_bin, inv_sqrt, + atomic_add_op, demMap + ); + } + return 0; +} + +// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution +template +int computeInstanceAreaLUTCuda(const T *demMap, + const int *lut_fracture, + const int num_bins_x, + const int num_bins_y, + const int num_bins_l, + const T stddev_x, const T stddev_y, + const int ext_bin, const T bin_area, + T *areaMap) +{ + int thread_count = 512; + int block_count = ceilDiv(num_bins_x*num_bins_y, thread_count); + computeInstanceAreaLUT<<>>( + demMap, lut_fracture, + num_bins_x, num_bins_y, + num_bins_l, + stddev_x, stddev_y, + ext_bin, bin_area, + areaMap + ); + return 0; +} + + +// Set a set of instance area in a area vector based on the given area map +template +int collectInstanceAreasLUTCuda(const T *pos_x, + const T *pos_y, + const int *indices, + const int *type, + const T *node_size_x, + const T *node_size_y, + const int num_bins_y, + const int num_bins_l, + const T *areaMap, + const int num_nodes, + const T inv_stddev_x, + const T inv_stddev_y, + T *instAreas) +{ + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + collectInstanceAreasLUT<<>>( + pos_x, pos_y, + indices, type, + node_size_x, node_size_y, + num_bins_y, num_bins_l, + areaMap, num_nodes, + inv_stddev_x, inv_stddev_y, + instAreas + ); + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int fillDemandMapLUTCuda( \ + const T *pos_x, const T *pos_y, const int *indices, const int *type, \ + const T *node_size_x, const T *node_size_y, const int num_bins_x, \ + const int num_bins_y, const int num_bins_l, const T xl, const T yl, \ + const T xh, const T yh, const int num_nodes, \ + const T stddev_x, const T stddev_y, \ + const T inv_stddev_x, const T inv_stddev_y, const int ext_bin, \ + const T inv_sqrt, const int deterministic_flag, \ + T *demMap); \ + \ + template int computeInstanceAreaLUTCuda( \ + const T *demMap, const int* lut_fracture, const int num_bins_x, const int num_bins_y, \ + const int num_bins_l, const T stddev_x, const T stddev_y, \ + const int ext_bin, const T bin_area, T *areaMap); \ + \ + template int collectInstanceAreasLUTCuda( \ + const T *pos_x, const T *pos_y, const int *indices, const int *type, \ + const T *node_size_x, const T *node_size_y, const int num_bins_y, \ + const int num_bins_l, const T *areaMap, const int num_nodes, \ + const T inv_stddev_x, const T inv_stddev_y, T *instAreas); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt new file mode 100644 index 00000000..275789bb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt @@ -0,0 +1,68 @@ +set(OP_NAME dct) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(dct_cpp + src/dct.cpp + src/dst.cpp + src/dxt.cpp + src/dct_2N.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(dct_lee_cpp + src/dct_lee.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(dct2_fft2_cpp + src/dct2_fft2.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + dct_cpp dct_lee_cpp dct2_fft2_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) + +# shared by dct_cuda and dct_lee_cuda +add_torch_extension(dct_dst_kernel_cuda + src/dct_cuda_kernel.cu + src/dst_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(dct_cuda + src/dct_cuda.cpp + src/dst_cuda.cpp + src/dxt_cuda.cpp + src/dct_2N_cuda.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES dct_dst_kernel_cuda ${LINK_LIBS}) + +add_pytorch_extension(dct_lee_cuda + src/dct_lee_cuda.cpp + src/dct_lee_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES dct_dst_kernel_cuda ${LINK_LIBS}) + +add_pytorch_extension(dct2_fft2_cuda + src/dct2_fft2_cuda.cpp + src/dct2_fft2_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + dct_cuda dct_lee_cuda dct2_fft2_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py new file mode 100644 index 00000000..58eefdd2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Mar 2019 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py new file mode 100644 index 00000000..3fc374e5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py @@ -0,0 +1,399 @@ +## +# @file dct.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# + +import os +import sys +import numpy as np +import torch +from torch.autograd import Function +from torch import nn + +import dreamplacefpga.ops.dct.dct_cpp as dct_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.dct.dct_cuda as dct_cuda + +import dreamplacefpga.ops.dct.discrete_spectral_transform as discrete_spectral_transform + +def dct(x, expk, algorithm): + """compute discrete cosine transformation, DCT II, using N-FFT or 2N-FFT + yk = \sum_{n=0}^{N-1} x_n cos(pi/N*n*(k+1/2)) + + @param x sequence + @param expk coefficients for post-processing + @param algorithm algorithm type N | 2N + """ + if x.is_cuda: + if algorithm == 'N': + output = dct_cuda.dct(x.view([-1, x.size(-1)]), expk) + elif algorithm == '2N': + output = dct_cuda.dct_2N(x.view([-1, x.size(-1)]), expk) + else: + if algorithm == 'N': + output = dct_cpp.dct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + elif algorithm == '2N': + output = dct_cpp.dct_2N(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + return output.view(x.size()) + +class DCTFunction(Function): + @staticmethod + def forward(ctx, x, expk, algorithm): + return dct(x, expk, algorithm) + +class DCT(nn.Module): + def __init__(self, expk=None, algorithm='N'): + super(DCT, self).__init__() + self.expk = expk + self.algorithm = algorithm + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return DCTFunction.apply(x, self.expk, self.algorithm) + +def idct(x, expk, algorithm): + """Compute inverse discrete cosine transformation, which is also the DCT III, using N-FFT or 2N-FFT + yk = Re { 1/2*x0 + \sum_{n=1}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + The actual yk will be scaled by 2 to match other python implementation + + @param x sequence + @param expk coefficients for pre-processing + @param algorithm algorithm type N | 2N + """ + if x.is_cuda: + if algorithm == 'N': + output = dct_cuda.idct(x.view([-1, x.size(-1)]), expk) + elif algorithm == '2N': + output = dct_cuda.idct_2N(x.view([-1, x.size(-1)]), expk) + else: + if algorithm == 'N': + output = dct_cpp.idct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + elif algorithm == '2N': + output = dct_cpp.idct_2N(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + return output.view(x.size()) + +class IDCTFunction(Function): + @staticmethod + def forward(ctx, x, expk, algorithm): + return idct(x, expk, algorithm) + +class IDCT(nn.Module): + def __init__(self, expk=None, algorithm='N'): + super(IDCT, self).__init__() + self.expk = expk + self.algorithm = algorithm + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDCTFunction.apply(x, self.expk, self.algorithm) + +def dct2(x, expk0, expk1, algorithm='N'): + """compute 2D discrete cosine transformation, using N-FFT or 2N-FFT + """ + if x.is_cuda: + if algorithm == 'N': + output = dct_cuda.dct2(x, expk0, expk1) + #output = dct_cuda.dct(dct_cuda.dct(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() + elif algorithm == '2N': + output = dct_cuda.dct2_2N(x, expk0, expk1) + #output = dct_cuda.dct_2N(dct_cuda.dct_2N(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() + else: + if algorithm == 'N': + output = dct_cpp.dct2(x, expk0, expk1, torch.get_num_threads()) + #output = dct_cpp.dct(dct_cpp.dct(x, expk1, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous(), expk0, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous() + elif algorithm == '2N': + output = dct_cpp.dct2_2N(x, expk0, expk1, torch.get_num_threads()) + return output + +class DCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, algorithm): + return dct2(x, expk0, expk1, algorithm) + +class DCT2(nn.Module): + def __init__(self, expk0=None, expk1=None, algorithm='N'): + super(DCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.algorithm = algorithm + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return DCT2Function.apply(x, self.expk0, self.expk1, self.algorithm) + +def idct2(x, expk0, expk1, algorithm='N'): + """compute 2D inverse discrete cosine transformation, using N-FFT or 2N-FFT + """ + if x.is_cuda: + if algorithm == 'N': + output = dct_cuda.idct2(x, expk0, expk1) + #output = dct_cuda.idct(dct_cuda.idct(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() + elif algorithm == '2N': + output = dct_cuda.idct2_2N(x, expk0, expk1) + else: + if algorithm == 'N': + output = dct_cpp.idct2(x, expk0, expk1, torch.get_num_threads()) + #output = dct_cpp.idct(dct_cpp.idct(x, expk1, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous(), expk0, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous() + elif algorithm == '2N': + output = dct_cpp.idct2_2N(x, expk0, expk1, torch.get_num_threads()) + return output + +class IDCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, algorithm): + return idct2(x, expk0, expk1, algorithm) + +class IDCT2(nn.Module): + def __init__(self, expk0=None, expk1=None, algorithm='N'): + super(IDCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.algorithm = algorithm + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDCT2Function.apply(x, self.expk0, self.expk1, self.algorithm) + +def dst(x, expk): + """compute discrete sine transformation + yk = \sum_{n=0}^{N-1} x_n cos(pi/N*(n+1/2)*(k+1)) + """ + if x.is_cuda: + output = dct_cuda.dst(x.view([-1, x.size(-1)]), expk) + else: + output = dct_cpp.dst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + return output.view(x.size()) + +class DSTFunction(Function): + @staticmethod + def forward(ctx, x, expk): + return dst(x, expk) + +class DST(nn.Module): + def __init__(self, expk=None): + super(DST, self).__init__() + self.expk = expk + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return DSTFunction.apply(x, self.expk) + +def idst(x, expk): + """Compute inverse discrete sine transformation, which is also the DST III + yk = Im { (-1)^k*x_{N-1}/2 + \sum_{n=0}^{N-2} xn exp(j*pi/(2N)*(n+1)*(2k+1)) } + The actual yk will be scaled by 2 to match other python implementation + """ + if x.is_cuda: + output = dct_cuda.idst(x.view([-1, x.size(-1)]), expk) + else: + output = dct_cpp.idst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + return output.view(x.size()) + +class IDSTFunction(Function): + @staticmethod + def forward(ctx, x, expk): + return idst(x, expk) + +class IDST(nn.Module): + def __init__(self, expk=None): + super(IDST, self).__init__() + self.expk = expk + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDSTFunction.apply(x, self.expk) + +def idxct(x, expk): + """compute inverse discrete cosine transformation + This is different from ordinary formulation for IDCT III + yk = Re { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + """ + if x.is_cuda: + output = dct_cuda.idxct(x.view([-1, x.size(-1)]), expk) + else: + output = dct_cpp.idxct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + #output = IDCTFunction.forward(ctx, x, expk) + #output.add_(x[..., 0].unsqueeze(-1)).mul_(0.5) + ##output.mul_(0.5).add_(x[..., 0].unsqueeze(-1).mul(0.5)) + return output.view(x.size()) + +class IDXCTFunction(Function): + @staticmethod + def forward(ctx, x, expk): + return idxct(x, expk) + +class IDXCT(nn.Module): + def __init__(self, expk=None): + super(IDXCT, self).__init__() + self.expk = expk + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDXCTFunction.apply(x, self.expk) + +def idxst(x, expk): + """compute inverse discrete sine transformation + This is different from ordinary formulation for IDCT III + yk = Im { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + """ + if x.is_cuda: + output = dct_cuda.idxst(x.view([-1, x.size(-1)]), expk) + else: + output = dct_cpp.idxst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) + return output.view(x.size()) + +class IDXSTFunction(Function): + @staticmethod + def forward(ctx, x, expk): + return idxst(x, expk) + +class IDXST(nn.Module): + def __init__(self, expk=None): + super(IDXST, self).__init__() + self.expk = expk + def forward(self, x): + if self.expk is None or self.expk.size(-2) != x.size(-1): + self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDXSTFunction.apply(x, self.expk) + +def idcct2(x, expk0, expk1): + """compute inverse discrete cosine-sine transformation + This is equivalent to idcct(idcct(x)^T)^T + """ + if x.is_cuda: + output = dct_cuda.idcct2(x.view([-1, x.size(-1)]), expk0, expk1) + else: + output = dct_cpp.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) + return output.view(x.size()) + +class IDCCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1): + return idcct2(x, expk0, expk1) + +class IDCCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDCCT2Function.apply(x, self.expk0, self.expk1) + +def idcst2(x, expk0, expk1): + """compute inverse discrete cosine-sine transformation + This is equivalent to idxct(idxst(x)^T)^T + """ + if x.is_cuda: + output = dct_cuda.idcst2(x.view([-1, x.size(-1)]), expk0, expk1) + else: + output = dct_cpp.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) + return output.view(x.size()) + +class IDCST2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1): + return idcst2(x, expk0, expk1) + +class IDCST2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCST2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDCST2Function.apply(x, self.expk0, self.expk1) + +def idsct2(x, expk0, expk1): + """compute inverse discrete cosine-sine transformation + This is equivalent to idxst(idxct(x)^T)^T + """ + if x.is_cuda: + output = dct_cuda.idsct2(x.view([-1, x.size(-1)]), expk0, expk1) + else: + output = dct_cpp.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) + return output.view(x.size()) + +class IDSCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1): + return idsct2(x, expk0, expk1) + +class IDSCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDSCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDSCT2Function.apply(x, self.expk0, self.expk1) + +def idct_idxst(x, expk0, expk1): + """compute inverse discrete cosine-sine transformation + This is equivalent to idct(idxst(x)^T)^T + """ + if x.is_cuda: + output = dct_cuda.idct_idxst(x.view([-1, x.size(-1)]), expk0, expk1) + else: + output = dct_cpp.idct_idxst(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) + return output.view(x.size()) + +class IDCT_IDXSTFunction(Function): + @staticmethod + def forward(ctx, x, expk0, expk1): + return idct_idxst(x, expk0, expk1) + +class IDCT_IDXST(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCT_IDXST, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDCT_IDXSTFunction.apply(x, self.expk0, self.expk1) + +def idxst_idct(x, expk0, expk1): + """compute inverse discrete cosine-sine transformation + This is equivalent to idxst(idct(x)^T)^T + """ + if x.is_cuda: + output = dct_cuda.idxst_idct(x.view([-1, x.size(-1)]), expk0, expk1) + else: + output = dct_cpp.idxst_idct(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) + return output.view(x.size()) + +class IDXST_IDCTFunction(Function): + @staticmethod + def forward(ctx, x, expk0, expk1): + return idxst_idct(x, expk0, expk1) + + +class IDXST_IDCT(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDXST_IDCT, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + def forward(self, x): + if self.expk0 is None or self.expk0.size(-2) != x.size(-2): + self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) + if self.expk1 is None or self.expk1.size(-2) != x.size(-1): + self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) + return IDXST_IDCTFunction.apply(x, self.expk0, self.expk1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py new file mode 100644 index 00000000..9b2dfc4b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py @@ -0,0 +1,150 @@ +## +# @file dct2_fft2.py +# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) +# @date Jun 2018 +# @brief Implement 2d dct, 2d idct, idxst(idct(x)), idct(idxst(x)) based on 2d fft +# + +import numpy as np +import torch +from torch.autograd import Function +from torch import nn + +from dreamplacefpga.ops.dct.discrete_spectral_transform import get_exact_expk as precompute_expk + +import dreamplacefpga.ops.dct.dct2_fft2_cpp as dct2_fft2_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.dct.dct2_fft2_cuda as dct2_fft2_cuda + + +class DCT2Function(Function): + @staticmethod + def forward(ctx, x, expkM, expkN, out, buf): + if x.is_cuda: + dct2_fft2_cuda.dct2_fft2(x, expkM, expkN, out, buf) + else: + dct2_fft2_cpp.dct2_fft2(x, expkM, expkN, out, buf, torch.get_num_threads()) + return out + + +class DCT2(nn.Module): + def __init__(self, expkM=None, expkN=None): + super(DCT2, self).__init__() + + self.expkM = expkM + self.expkN = expkN + self.out = None + self.buf = None + + def forward(self, x): + M = x.size(-2) + N = x.size(-1) + if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: + self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) + if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: + self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) + if self.out is None: + self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) + self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) + + return DCT2Function.apply(x, self.expkM, self.expkN, self.out, self.buf) + + +class IDCT2Function(Function): + @staticmethod + def forward(ctx, x, expkM, expkN, out, buf): + if x.is_cuda: + dct2_fft2_cuda.idct2_fft2(x, expkM, expkN, out, buf) + else: + dct2_fft2_cpp.idct2_fft2(x, expkM, expkN, out, buf, torch.get_num_threads()) + return out + + +class IDCT2(nn.Module): + def __init__(self, expkM=None, expkN=None): + super(IDCT2, self).__init__() + + self.expkM = expkM + self.expkN = expkN + self.out = None + self.buf = None + + def forward(self, x): + M = x.size(-2) + N = x.size(-1) + if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: + self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) + if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: + self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) + if self.out is None: + self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) + self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) + + return IDCT2Function.apply(x, self.expkM, self.expkN, self.out, self.buf) + + +class IDCT_IDXSTFunction(Function): + @staticmethod + def forward(ctx, x, expkM, expkN, out, buf): + if x.is_cuda: + dct2_fft2_cuda.idct_idxst(x, expkM, expkN, out, buf) + else: + dct2_fft2_cpp.idct_idxst(x, expkM, expkN, out, buf, torch.get_num_threads()) + return out + + +class IDCT_IDXST(nn.Module): + def __init__(self, expkM=None, expkN=None): + super(IDCT_IDXST, self).__init__() + + self.expkM = expkM + self.expkN = expkN + self.out = None + self.buf = None + + def forward(self, x): + M = x.size(-2) + N = x.size(-1) + if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: + self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) + if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: + self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) + if self.out is None: + self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) + self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) + + return IDCT_IDXSTFunction.apply(x, self.expkM, self.expkN, self.out, self.buf) + + +class IDXST_IDCTFunction(Function): + @staticmethod + def forward(ctx, x, expkM, expkN, out, buf): + if x.is_cuda: + dct2_fft2_cuda.idxst_idct(x, expkM, expkN, out, buf) + else: + dct2_fft2_cpp.idxst_idct(x, expkM, expkN, out, buf, torch.get_num_threads()) + return out + + +class IDXST_IDCT(nn.Module): + def __init__(self, expkM=None, expkN=None): + super(IDXST_IDCT, self).__init__() + + self.expkM = expkM + self.expkN = expkN + self.out = None + self.buf = None + + def forward(self, x): + M = x.size(-2) + N = x.size(-1) + if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: + self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) + if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: + self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) + if self.out is None: + self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) + self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) + + return IDXST_IDCTFunction.apply(x, self.expkM, self.expkN, self.out, self.buf) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py new file mode 100644 index 00000000..bec5ce63 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py @@ -0,0 +1,426 @@ +## +# @file dct_lee.py +# @author Yibo Lin (DREAMPlace) +# @date Oct 2018 +# + +import numpy as np +import torch +from torch.autograd import Function +from torch import nn +import pdb + +import dreamplacefpga.ops.dct.dct_lee_cpp as dct_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.dct.dct_lee_cuda as dct_cuda + +def dct(x, expk, buf, out): + """compute discrete cosine transformation, DCT II + yk = \sum_{n=0}^{N-1} x_n cos(pi/N*n*(k+1/2)) + """ + if x.is_cuda: + dct_cuda.dct(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.dct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + return out.view(x.size()) + +class DCTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return dct(x, expk, buf, out) + +class DCT(nn.Module): + def __init__(self, expk=None): + super(DCT, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_dct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_dct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return DCTFunction.apply(x, self.expk, self.buf, self.out) + +def idct(x, expk, buf, out): + """Compute inverse discrete cosine transformation, which is also the DCT III + yk = Re { 1/2*x0 + \sum_{n=1}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + The actual yk will be scaled by 2 to match other python implementation + """ + if x.is_cuda: + dct_cuda.idct(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.idct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDCTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return idct(x, expk, buf, out) + +class IDCT(nn.Module): + def __init__(self, expk=None): + super(IDCT, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDCTFunction.apply(x, self.expk, self.buf, self.out) + +def dct2(x, expk0, expk1, buf, out): + """compute 2D discrete cosine transformation + """ + if x.is_cuda: + dct_cuda.dct2(x, expk0, expk1, buf, out) + else: + dct_cpp.dct2(x, expk0, expk1, buf, out, torch.get_num_threads()) + return out + +class DCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, buf, out): + return dct2(x, expk0, expk1, buf, out) + +class DCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(DCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.buf = None + self.out = None + def forward(self, x): + if self.expk0 is None or self.expk0.size(-1) != x.size(-2): + self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_dct_cos(x.size(-2), self.expk0) + else: + dct_cpp.precompute_dct_cos(x.size(-2), self.expk0) + if self.expk1 is None or self.expk1.size(-1) != x.size(-1): + self.expk1 = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_dct_cos(x.size(-1), self.expk1) + else: + dct_cpp.precompute_dct_cos(x.size(-1), self.expk1) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return DCT2Function.apply(x, self.expk0, self.expk1, self.buf, self.out) + +def idct2(x, expk0, expk1, buf, out): + """compute 2D inverse discrete cosine transformation + """ + if x.is_cuda: + dct_cuda.idct2(x, expk0, expk1, buf, out) + else: + dct_cpp.idct2(x, expk0, expk1, buf, out, torch.get_num_threads()) + return out + +class IDCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, buf, out): + return idct2(x, expk0, expk1, buf, out) + +class IDCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.buf = None + self.out = None + def forward(self, x): + if self.expk0 is None or self.expk0.size(-1) != x.size(-2): + self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) + else: + dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) + if self.expk1 is None or self.expk1.size(-1) != x.size(-1): + self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDCT2Function.apply(x, self.expk0, self.expk1, self.buf, self.out) + +def dst(x, expk, buf, out): + """compute discrete sine transformation + yk = \sum_{n=0}^{N-1} x_n cos(pi/N*(n+1/2)*(k+1)) + """ + if x.is_cuda: + dct_cuda.dst(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.dst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + return out.view(x.size()) + +class DSTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return dst(x, expk, buf, out) + +class DST(nn.Module): + def __init__(self, expk=None): + super(DST, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_dct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_dct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return DSTFunction.apply(x, self.expk, self.buf, self.out) + +def idst(x, expk, buf, out): + """Compute inverse discrete sine transformation, which is also the DST III + yk = Im { (-1)^k*x_{N-1}/2 + \sum_{n=0}^{N-2} xn exp(j*pi/(2N)*(n+1)*(2k+1)) } + The actual yk will be scaled by 2 to match other python implementation + """ + if x.is_cuda: + dct_cuda.idst(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.idst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDSTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return idst(x, expk, buf, out) + +class IDST(nn.Module): + def __init__(self, expk=None): + super(IDST, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDSTFunction.apply(x, self.expk, self.buf, self.out) + +def idxct(x, expk, buf, out): + """compute inverse discrete cosine transformation + This is different from ordinary formulation for IDCT III + yk = Re { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + """ + if x.is_cuda: + dct_cuda.idxct(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.idxct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + #output = IDCTFunction.forward(ctx, x, expk) + #output.add_(x[..., 0].unsqueeze(-1)).mul_(0.5) + ##output.mul_(0.5).add_(x[..., 0].unsqueeze(-1).mul(0.5)) + return out.view(x.size()) + +class IDXCTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return idxct(x, expk, buf, out) + +class IDXCT(nn.Module): + def __init__(self, expk=None): + super(IDXCT, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDXCTFunction.apply(x, self.expk, self.buf, self.out) + +def idxst(x, expk, buf, out): + """compute inverse discrete sine transformation + This is different from ordinary formulation for IDCT III + yk = Im { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } + """ + if x.is_cuda: + dct_cuda.idxst(x.view([-1, x.size(-1)]), expk, buf, out) + else: + dct_cpp.idxst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDXSTFunction(Function): + @staticmethod + def forward(ctx, x, expk, buf, out): + return idxst(x, expk, buf, out) + +class IDXST(nn.Module): + def __init__(self, expk=None): + super(IDXST, self).__init__() + self.expk = expk + self.buf = None + self.out = None + def forward(self, x): + if self.expk is None or self.expk.size(-1) != x.size(-1): + self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk) + if self.out is None or self.out.size() != x.size(): + self.buf = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDXSTFunction.apply(x, self.expk, self.buf, self.out) + +def idcct2(x, expk0, expk1, buf0, buf1, out): + """compute inverse discrete cosine-sine transformation + This is equivalent to idcct(idcct(x)^T)^T + """ + if x.is_cuda: + dct_cuda.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) + else: + dct_cpp.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDCCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, buf0, buf1, out): + return idcct2(x, expk0, expk1, buf0, buf1, out) + +class IDCCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.buf0 = None + self.buf1 = None + self.out = None + def forward(self, x): + if self.expk0 is None or self.expk0.size(-1) != x.size(-2): + self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) + else: + dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) + if self.expk1 is None or self.expk1.size(-1) != x.size(-1): + self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) + if self.out is None or self.out.size() != x.size(): + self.buf0 = torch.empty_like(x) + self.buf1 = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDCCT2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) + +def idcst2(x, expk0, expk1, buf0, buf1, out): + """compute inverse discrete cosine-sine transformation + This is equivalent to idxct(idxst(x)^T)^T + """ + if x.is_cuda: + dct_cuda.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) + else: + dct_cpp.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDCST2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, buf0, buf1, out): + return idcst2(x, expk0, expk1, buf0, buf1, out) + +class IDCST2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDCST2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.buf0 = None + self.buf1 = None + self.out = None + def forward(self, x): + if self.expk0 is None or self.expk0.size(-1) != x.size(-2): + self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) + else: + dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) + if self.expk1 is None or self.expk1.size(-1) != x.size(-1): + self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) + if self.out is None or self.out.size() != x.size(): + self.buf0 = torch.empty_like(x) + self.buf1 = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDCST2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) + +def idsct2(x, expk0, expk1, buf0, buf1, out): + """compute inverse discrete cosine-sine transformation + This is equivalent to idxst(idxct(x)^T)^T + """ + if x.is_cuda: + dct_cuda.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) + else: + dct_cpp.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) + return out.view(x.size()) + +class IDSCT2Function(Function): + @staticmethod + def forward(ctx, x, expk0, expk1, buf0, buf1, out): + return idsct2(x, expk0, expk1, buf0, buf1, out) + +class IDSCT2(nn.Module): + def __init__(self, expk0=None, expk1=None): + super(IDSCT2, self).__init__() + self.expk0 = expk0 + self.expk1 = expk1 + self.buf0 = None + self.buf1 = None + self.out = None + def forward(self, x): + if self.expk0 is None or self.expk0.size(-1) != x.size(-2): + self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) + else: + dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) + if self.expk1 is None or self.expk1.size(-1) != x.size(-1): + self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) + if x.is_cuda: + dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) + else: + dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) + if self.out is None or self.out.size() != x.size(): + self.buf0 = torch.empty_like(x) + self.buf1 = torch.empty_like(x) + self.out = torch.empty_like(x) + return IDSCT2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py new file mode 100644 index 00000000..5351ecfb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py @@ -0,0 +1,430 @@ +## +# @file discrete_spectral_transform.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# + +import os +import sys +import numpy as np +import torch +import torch.nn.functional as F +import pdb +import dreamplacefpga.ops.dct.torch_fft_api as torch_fft_api + +""" Discrete spectral transformation leveraging fast fourier transform engine. +The math here mainly uses Prosthaphaeresis properties. +The trigonometric identities exploited by prosthaphaeresis relate products of trigonometric functions to sums. +sin(a) sin(b) = 1/2 * (cos(a-b) - cos(a+b)) +cos(a) cos(b) = 1/2 * (cos(a-b) + cos(a+b)) +sin(a) cos(b) = 1/2 * (sin(a+b) + sin(a-b)) +cos(a) sin(b) = 1/2 * (sin(a-b) - sin(a+b)) + +A 2D FFT performs +y_{u, v} = \sum_i \sum_j x_{i, j} exp(-j*2*pi*u*i/M) exp(-j*2*pi*v*j/N) + = \sum_i \sum_j x_{i, j} exp(-j*2*pi*(u*i/M + v*j/N)) + = \sum_i \sum_j x_{i, j} (cos(-2*pi*(u*i/M + v*j/N)) + j sin(-2*pi*(u*i/M + v*j/N))). + +By mapping the original image from (i, j) to (i, N-j), we can have (u*i/M - v*j/N) inside exp. +This will enable us to derive various cos/sin transformation by computing FFT twice. +""" + +def get_expk(N, dtype, device): + """ Compute 2*exp(-1j*pi*u/(2N)), but not exactly the same. + The actual return is 2*cos(pi*u/(2N)), 2*sin(pi*u/(2N)). + This will make later multiplication easier. + """ + pik_by_2N = torch.arange(N, dtype=dtype, device=device) + pik_by_2N.mul_(np.pi/(2*N)) + # cos, sin + # I use sin because the real part requires subtraction + # this will be easier for multiplication + expk = torch.stack([pik_by_2N.cos(), pik_by_2N.sin()], dim=-1) + expk.mul_(2) + + return expk.contiguous() + + +def get_expkp1(N, dtype, device): + """ Compute 2*exp(-1j*pi*(u+1)/(2N)), but not exactly the same. + The actual return is 2*cos(pi*(u+1)/(2N)), 2*sin(pi*(u+1)/(2N)) + """ + neg_pik_by_2N = torch.arange(1, N+1, dtype=dtype, device=device) + neg_pik_by_2N.mul_(np.pi/(2*N)) + # sin, -cos + # I swap -cos and sin because we need the imag part + # this will be easier for multiplication + expk = torch.stack([neg_pik_by_2N.cos(), neg_pik_by_2N.sin()], dim=-1) + expk.mul_(2) + + return expk.contiguous() + + +def get_exact_expk(N, dtype, device): + # Compute exp(-j*pi*u/(2N)) = cos(pi*u/(2N)) - j * sin(pi*u/(2N)) + pik_by_2N = torch.arange(N, dtype=dtype, device=device) + pik_by_2N.mul_(np.pi/(2*N)) + # cos, -sin + expk = torch.stack([pik_by_2N.cos(), -pik_by_2N.sin()], dim=-1) + return expk.contiguous() + + +def get_perm(N, dtype, device): + """ Compute permutation to generate following array + 0, 2, 4, ..., 2*(N//2)-2, 2*(N//2)-1, 2*(N//2)-3, ..., 3, 1 + """ + perm = torch.zeros(N, dtype=dtype, device=device) + perm[0:(N-1)//2+1] = torch.arange(0, N, 2, dtype=dtype, device=device) + perm[(N-1)//2+1:] = torch.arange(2*(N//2)-1, 0, -2, dtype=dtype, device=device) + + return perm + +def dct_2N(x, expk=None): + """ Batch Discrete Cosine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i cos(pi*(2i+1)*u/(2N)), + Impelements the 2N padding trick to solve DCT with FFT in the following link, + https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft + + 1. Pad x by zeros + 2. Perform FFT + 3. Multiply by 2*exp(-1j*pi*u/(2N)) + 4. Extract the real part + """ + # last dimension + N = x.size(-1) + # pad last dimension + x_pad = F.pad(x, (0, N), 'constant', 0) + + # the last dimension here becomes -2 because complex numbers introduce a new dimension + y = torch_fft_api.rfft(x_pad, signal_ndim=1, normalized=False, onesided=True)[..., 0:N, :] + y.mul_(1.0/N) + + if expk is None: + expk = get_expk(N, dtype=x.dtype, device=x.device) + + # get real part + y.mul_(expk) + + # I found add is much faster than sum + #y = y.sum(dim=-1) + return y[..., 0]+y[..., 1] + + +def dct_N(x, perm=None, expk=None): + """ Batch Discrete Cosine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i cos(pi*(2i+1)*u/(2N)), + Impelements the N permuting trick to solve DCT with FFT in the following link, + https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft + + 1. permute x such that [a, b, c, d, e, f] becomes [a, c, e, f, d, b] + 2. Perform FFT + 3. Multiply by 2*exp(-1j*pi*u/(2N)) + 4. Extract the real part + """ + # last dimension + N = x.size(-1) + + if perm is None: + perm = get_perm(N, dtype=torch.int64, device=x.device) + if x.ndimension() <= 1: + x_reorder = x.view([1, N]) + else: + x_reorder = x.clone() + # switch from row-major to column-major for speedup + x_reorder.transpose_(dim0=-2, dim1=-1) + #x_reorder = x_reorder[..., perm, :] + x_reorder = x_reorder.index_select(dim=-2, index=perm) + # switch back + x_reorder.transpose_(dim0=-2, dim1=-1) + + y = torch_fft_api.rfft(x_reorder, signal_ndim=1, normalized=False, onesided=False)[..., 0:N, :] + y.mul_(1.0/N) + + if expk is None: + expk = get_expk(N, dtype=x.dtype, device=x.device) + + # get real part + y.mul_(expk) + # I found add is much faster than sum + #y = y.sum(dim=-1) + return y[..., 0]+y[..., 1] + + +def idct_2N(x, expk=None): + """ Batch Inverse Discrete Cosine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), + Impelements the 2N padding trick to solve IDCT with IFFT in the following link, + https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py + + 1. Multiply by 2*exp(1j*pi*u/(2N)) + 2. Pad x by zeros + 3. Perform IFFT + 4. Extract the real part + """ + # last dimension + N = x.size(-1) + + if expk is None: + expk = get_expk(N, dtype=x.dtype, device=x.device) + + # multiply by 2*exp(1j*pi*u/(2N)) + x_pad = x.unsqueeze(-1).mul(expk) + # pad second last dimension, excluding the complex number dimension + x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) + + if len(x.size()) == 1: + x_pad.unsqueeze_(0) + + # the last dimension here becomes -2 because complex numbers introduce a new dimension + y = torch_fft_api.irfft(x_pad, signal_ndim=1, normalized=False, onesided=False, signal_sizes=[2*N])[..., 0:N] + y.mul_(N) + + if len(x.size()) == 1: + y.squeeze_(0) + + return y + + +def idct_N(x, expk=None): + N = x.size(-1) + + if expk is None: + expk = get_expk(N, dtype=x.dtype, device=x.device) + + size = list(x.size()) + size.append(2) + x_reorder = torch.zeros(size, dtype=x.dtype, device=x.device) + x_reorder[..., 0] = x + x_reorder[..., 1:, 1] = x.flip([x.ndimension()-1])[..., :N-1].mul_(-1) + + x_reorder[..., 0] = x.mul(expk[..., 0]).sub_(x_reorder[..., 1].mul(expk[..., 1])) + x_reorder[..., 1].mul_(expk[..., 0]) + x_reorder[..., 1].add_(x.mul(expk[..., 1])) + # this is to match idct_2N + # normal way should multiply 0.25 + x_reorder.mul_(0.5) + + y = torch_fft_api.ifft(x_reorder, signal_ndim=1, normalized=False) + y.mul_(N) + + z = torch.empty_like(x) + z[..., 0:N:2] = y[..., :(N+1)//2, 0] + z[..., 1:N:2] = y[..., (N+1)//2:, 0].flip([x.ndimension()-1]) + + return z + + +def dst(x, expkp1=None): + """ Batch Discrete Sine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i sin(pi*(2i+1)*(u+1)/(2N)), + Impelements the 2N padding trick to solve DCT with FFT in the following link, + https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft + + 1. Pad x by zeros + 2. Perform FFT + 3. Multiply by 2*exp(-1j*pi*u/(2N)) + 4. Extract the real part + """ + # last dimension + N = x.size(-1) + # pad last dimension + x_pad = F.pad(x, (0, N), 'constant', 0) + + # the last dimension here becomes -2 because complex numbers introduce a new dimension + y = torch_fft_api.rfft(x_pad, signal_ndim=1, normalized=False, onesided=True)[..., 1:N+1, :] + + if expkp1 is None: + expkp1 = get_expkp1(N, dtype=x.dtype, device=x.device) + + # get imag part + y = y[..., 1].mul(expkp1[:, 0]) - y[..., 0].mul(expkp1[:, 1]) + + return y + + +def idst(x, expkp1=None): + """ Batch Inverse Discrete Sine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), + Impelements the 2N padding trick to solve IDCT with IFFT in the following link, + https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py + + 1. Multiply by 2*exp(1j*pi*u/(2N)) + 2. Pad x by zeros + 3. Perform IFFT + 4. Extract the real part + """ + # last dimension + N = x.size(-1) + + if expkp1 is None: + expkp1 = get_expkp1(N, dtype=x.dtype, device=x.device) + + # multiply by 2*exp(1j*pi*u/(2N)) + x_pad = x.unsqueeze(-1).mul(expkp1) + # pad second last dimension, excluding the complex number dimension + x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) + + if len(x.size()) == 1: + x_pad.unsqueeze_(0) + + # the last dimension here becomes -2 because complex numbers introduce a new dimension + y = torch_fft_api.irfft(x_pad, signal_ndim=1, normalized=False, onesided=False, signal_sizes=[2*N])[..., 1:N+1] + y.mul_(N) + + if len(x.size()) == 1: + y.squeeze_(0) + + return y + + +def idxt(x, cos_or_sin_flag, expk=None): + """ Batch Inverse Discrete Cosine Transformation without normalization to coefficients. + Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), + Impelements the 2N padding trick to solve IDCT with IFFT in the following link, + https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py + + 1. Multiply by 2*exp(1j*pi*u/(2N)) + 2. Pad x by zeros + 3. Perform IFFT + 4. Extract the real part + + @param x batch 1D tensor for conversion + @param cos_or_sin_flag 0 for cosine tranformation and 1 or sine transformation + @param expk 2*exp(j*pi*k/(2N)) + """ + # last dimension + N = x.size(-1) + + if expk is None: + expk = get_expk(N, dtype=x.dtype, device=x.device) + + # multiply by 2*exp(1j*pi*u/(2N)) + x_pad = x.unsqueeze(-1).mul(expk) + # pad second last dimension, excluding the complex number dimension + x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) + + if len(x.size()) == 1: + x_pad.unsqueeze_(0) + + # the last dimension here becomes -2 because complex numbers introduce a new dimension + # Must use IFFT here + y = torch_fft_api.ifft(x_pad, signal_ndim=1, normalized=False)[..., 0:N, cos_or_sin_flag] + y.mul_(N) + + if len(x.size()) == 1: + y.squeeze_(0) + + return y + + +def dct2_2N(x, expk0=None, expk1=None): + """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. + Compute 1D DCT twice. + @param x batch tensor, the 2D part is MxN + @param expk0 with length M + @param expk1 with length N + """ + return dct_2N(dct_2N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) + + +def dct2_N(x, perm0=None, expk0=None, perm1=None, expk1=None): + """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. + Compute 1D DCT twice. + @param x batch tensor, the 2D part is MxN + @param perm0 with length M + @param expk0 with length M + @param perm1 with length N + @param expk1 with length N + """ + return dct_N(dct_N(x.transpose(dim0=-2, dim1=-1), perm=perm0, expk=expk0).transpose_(dim0=-2, dim1=-1), perm=perm1, expk=expk1) + + +def idct2_2N(x, expk0=None, expk1=None): + """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. + Compute 1D DCT twice. + @param x batch tensor, the 2D part is MxN + @param expk0 with length M + @param expk1 with length N + """ + return idct_2N(idct_2N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) + + +def idct2_N(x, expk0=None, expk1=None): + """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. + Compute 1D DCT twice. + @param x batch tensor, the 2D part is MxN + @param expk0 with length M + @param expk1 with length N + """ + return idct_N(idct_N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) + + +def dst2(x, expkp1_0=None, expkp1_1=None): + """ Batch 2D Discrete Sine Transformation without normalization to coefficients. + Compute 1D DST twice. + @param x batch tensor, the 2D part is MxN + @param expkp1_0 with length M + @param expkp1_1 with length N + """ + return dst(dst(x.transpose(dim0=-2, dim1=-1), expkp1_0).transpose_(dim0=-2, dim1=-1), expkp1_1) + + +def idcct2(x, expk_0=None, expk_1=None): + """ Batch 2D Inverse Discrete Cosine-Cosine Transformation without normalization to coefficients. + It computes following equation, which is slightly different from standard DCT formulation. + y_{u, v} = \sum_p \sum_q x_{p, q} cos(pi/M*p*(u+0.5)) cos(pi/N*q*(v+0.5)) + Compute 1D DCT twice. + @param x batch tensor, the 2D part is MxN + @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) + @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) + """ + return idxt(idxt(x, 0, expk_1).transpose_(dim0=-2, dim1=-1), 0, expk_0).transpose(dim0=-2, dim1=-1) + # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1), 0, expk_1) + + +def idsct2(x, expk_0=None, expk_1=None): + """ Batch 2D Inverse Discrete Sine-Cosine Transformation without normalization to coefficients. + It computes following equation, which is slightly different from standard DCT formulation. + y_{u, v} = \sum_p \sum_q x_{p, q} sin(pi/M*p*(u+0.5)) cos(pi/N*q*(v+0.5)) + Compute 1D DST and then 1D DCT. + @param x batch tensor, the 2D part is MxN + @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) + @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) + """ + return idxt(idxt(x, 0, expk_1).transpose_(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1) + # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1), 0, expk_1) + + +def idcst2(x, expk_0=None, expk_1=None): + """ Batch 2D Inverse Discrete Cosine-Sine Transformation without normalization to coefficients. + It computes following equation, which is slightly different from standard DCT formulation. + y_{u, v} = \sum_p \sum_q x_{p, q} cos(pi/M*p*(u+0.5)) sin(pi/N*q*(v+0.5)) + Compute 1D DCT and then 1D DST. + @param x batch tensor, the 2D part is MxN + @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) + @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) + """ + return idxt(idxt(x, 1, expk_1).transpose_(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1) + # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1), 1, expk_1) + + +def idxst_idct(x, expk_0=None, expk_1=None): + ''' + Batch 2D Inverse Discrete Sine-Cosine Transformation without normalization to coefficients. + Compute idxst(idct(x)) + @param x batch tensor, the 2D part is MxN + @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) + @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) + ''' + return idxt(idct_N(x, expk_1).transpose_(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1) + + +def idct_idxst(x, expk_0=None, expk_1=None): + ''' + Batch 2D Inverse Discrete Cosine-Sine Transformation without normalization to coefficients. + Compute idct(idxst(x)). + @param x batch tensor, the 2D part is MxN + @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) + @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) + ''' + return idct_N(idxt(x, 1, expk_1).transpose_(dim0=-2, dim1=-1), expk_0).transpose_(dim0=-2, dim1=-1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py new file mode 100644 index 00000000..1ba9440e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py @@ -0,0 +1,106 @@ +## +# @file naive.py +# @author Yibo Lin (DREAMPlace) +# @date Sep 2018 +# + +import numpy as np +import scipy +from scipy import fftpack +import torch +import pdb + +def myidst(x): + N = x.shape[-1] + z = np.zeros_like(x).astype(np.complex128) + for k in range(len(z)): + for i in range(len(x)): + #content = str(x[i]) + " " + str(i) + #print("[%d] %s" % (k, content)) + z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0))) + #for k in range(len(z)): + # for i in range(len(x)): + # if i == N-1: + # z[k] -= x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0)))/2.0 + return z + +def myidst_ext(x): + N = x.shape[-1] + z = np.zeros_like(x).astype(np.complex128) + for k in range(len(z)): + for i in range(len(x)): + #content = str(x[i]) + " " + str(i+1) + #print("[%d] %s" % (k, content)) + z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1))) + #for k in range(len(z)): + # for i in range(len(x)): + # if i == N-1: + # z[k] -= x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0)))/2.0 + return z + +def dst_type2(x): + N = x.shape[-1] + z = np.zeros_like(x).astype(np.complex128) + for k in range(len(z)): + for i in range(len(x)): + z[k] += x[i]*np.sin(np.pi/N*((k+1)*(i+0.5))) + #z[k] += x[i]*np.exp(1j*np.pi/N*((k+1)*(i+0.5))) + return z + +def dst_type3(x): + N = x.shape[-1] + z = np.zeros_like(x).astype(np.complex128) + for k in range(len(z)): + for i in range(len(x)): + if i == N-1: + #z[k] += ((-1)**k)/2.0*x[i] + z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1)))/2.0 + else: + #z[k] += x[i]*np.sin(np.pi/N*((k+0.5)*(i+1))) + z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1))) + return z*2 + +if __name__ == "__main__": + x = np.array([1, 23, 5, 6, 7, 4]).astype(np.float64) + xflip = np.flip(x, 0) + N = len(x) + print("scipy dst") + print(fftpack.dst(x, 2)/2) + + ydst2 = dst_type2(x) + print("dst_type2") + print(ydst2) + + pdb.set_trace() + + print("scipy idst") + print(fftpack.idst(ydst2, 2)/len(x)) + + print("scipy dst III") + print(fftpack.dst(ydst2, 3)/len(x)) + + zidst2 = dst_type3(ydst2)/len(x) + print("idst_type2") + print(zidst2) + + zmyidst = myidst(ydst2) + print("myidst") + print(zmyidst) + + ydst2_ext = np.concatenate([ydst2[1:], [0]]) + zmyidst_ext = myidst_ext(ydst2_ext) + print("myidst_ext") + print(zmyidst_ext) + + print(fftpack.idct(np.flip(ydst2_ext, 0), 2)/2) + + #expk = 0.5*np.exp(np.arange(N)*1j*np.pi*2/(4*N)) + #v = np.zeros_like(expk) + #for k in range(N): + # if k == 0: + # v[k] = expk[k] * (-0 + 1j*ydst2_ext[k]) + # else: + # v[k] = expk[k] * (-ydst2_ext[N-k] + 1j*ydst2_ext[k]) + #print(np.fft.ifft(v)) + + pdb.set_trace() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp new file mode 100644 index 00000000..9079eb0b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp @@ -0,0 +1,255 @@ +/** + * @file dct.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + */ +#include "dct.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dct_forward(at::Tensor x, at::Tensor expk, int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // auto x_reorder = at::empty_like(x); + auto x_reorder = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_forward", [&] { + computeReorder(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + auto y = at::rfft(x_reorder, 1, false, true); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + // std::cout << "x_reorder\n" << x_reorder << "\n"; + // std::cout << "expk\n" << expk << "\n"; + computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + // std::cout << "z\n" << x_reorder << "\n"; + x_reorder.mul_(1.0 / N); + }); + + return x_reorder; +} + +at::Tensor idct_forward(at::Tensor x, at::Tensor expk, int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_forward", [&] { + computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << __func__ << " v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << __func__ << " y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // reuse v + v.resize_({M, N}); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + // this is to match python implementation + // normal way should be multiply by 0.25*N + v.mul_(0.5 * N); + }); + + return v; +} + +at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + auto x_reorder = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_forward", [&] { + computeReorder(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + auto y = at::rfft(x_reorder, 1, false, true); + // y.mul_(1.0/N); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + // std::cout << "expk1\n" << expk1 << "\n"; + computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + // std::cout << "z\n" << x_reorder << "\n"; + + // 1D DCT to rows + auto xt = x_reorder.transpose(-2, -1).contiguous(); + // std::cout << "xt\n" << xt << "\n"; + // I do not want to allocate memory another time + // x_reorder = at::empty_like(xt); + x_reorder = x_reorder.view_as(xt); + computeReorder(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + y = at::rfft(x_reorder, 1, false, true); + // y.mul_(1.0/M); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + // std::cout << "expk0\n" << expk0 << "\n"; + computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads); + + x_reorder.mul_(1.0 / (M * N)); + x_reorder.transpose_(-2, -1); + }); + + return x_reorder.contiguous(); +} + +at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_forward", [&] { + computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "expk1\n" << expk1 << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + // y.mul_(0.25*N); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + // auto z = at::empty(x.options(), {M, N}); + /// reuse v + v.resize_({M, N}); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + // 1D DCT to rows + auto xt = v.transpose(-2, -1).contiguous(); + // std::cout << "xt\n" << xt << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "expk0\n" << expk0 << "\n"; + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + // y.mul_(0.25*M); + + // std::cout << "y\n" << y << "\n"; + + // I do not want to allocate memory another time + // reuse v + v.resize_({N, M}); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + // this is to match python implementation + // normal way should be multiply by 0.25*0.25*M*N + v.mul_(0.25 * M * N); + v.transpose_(-2, -1); + }); + + return v.contiguous(); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("dct", &DREAMPLACE_NAMESPACE::dct_forward, "DCT forward"); + m.def("idct", &DREAMPLACE_NAMESPACE::idct_forward, "IDCT forward"); + m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_forward, "IDXCT forward"); + m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_forward, "DCT2 forward"); + + m.def("dst", &DREAMPLACE_NAMESPACE::dst_forward, "DST forward"); + m.def("idst", &DREAMPLACE_NAMESPACE::idst_forward, "IDST forward"); + + m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_forward, "IDCT2 forward"); + m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_forward, "IDXST forward"); + + // use idxst and idxct as kernels + m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_forward, "IDCCT2 forward"); + m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_forward, "IDCST2 forward"); + m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_forward, "IDSCT2 forward"); + + // use idxst and idct as kernels + m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST(IDCT(x)) forward"); + m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT(IDXST(x)) forward"); + + m.def("dct_2N", &DREAMPLACE_NAMESPACE::dct_2N_forward, "DCT forward"); + m.def("idct_2N", &DREAMPLACE_NAMESPACE::idct_2N_forward, "IDCT forward"); + m.def("dct2_2N", &DREAMPLACE_NAMESPACE::dct2_2N_forward, "DCT2 forward"); + m.def("idct2_2N", &DREAMPLACE_NAMESPACE::idct2_2N_forward, "IDCT2 forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h new file mode 100644 index 00000000..64911e9d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h @@ -0,0 +1,427 @@ +/** + * @file dct.h + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#ifndef DREAMPLACE_DCT_H +#define DREAMPLACE_DCT_H + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_CPU(x) AT_ASSERTM(!x.is_cuda(), #x "must be a tensor on CPU") +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +at::Tensor dct_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idct_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor dct2_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idct2_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor dst_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idst_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idxct_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idxst_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idcct2_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idcst2_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idsct2_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idxst_idct_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idct_idxst_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +template +void computeReorder( + const T* x, + const int M, + const int N, + T* y, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int ii = i%N; + + if (ii < (N>>1)) + { + // i*2 + //printf("x[%d] = y[%d]\n", i+ii, i); + y[i] = x[i+ii]; + } + else + { + // (N-i)*2-1 + //printf("x[%d] = y[%d]\n", i+N*2-ii*3-1, i); + y[i] = x[i+N*2-ii*3-1]; + } + } +} + +template +void computeMulExpk( + const T* x, + const T* expk, + const int M, + const int N, + T* z, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int fft_onesided_size = (N>>1)+1; + int fft_onesided_size_2x = fft_onesided_size<<1; + + if (col_2x <= N) + { + int j = row*fft_onesided_size_2x + col_2x; + //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); + z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; + } + else + { + int j = row*fft_onesided_size_2x + (N<<1) - col_2x; + //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); + z[i] = x[j]*expk[col_2x] - x[j+1]*expk[col_2x+1]; + } + } +} + +template +void computeVk( + const T* x, + const T* expk, + const int M, + const int N, + T* v, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*(N/2+1); ++i) + { + int ncol = N/2+1; + int row = i/ncol; // row + int col = i-row*ncol; // column + int col_2x = (col<<1); + + // real + T real = x[row*N+col]; + T imag = (col == 0)? 0 : -x[row*N+N-col]; + + v[2*i] = real*expk[col_2x] - imag*expk[col_2x+1]; + // imag, x[N-i] + v[2*i+1] = real*expk[col_2x+1] + imag*expk[col_2x]; + } +} + +template +void computeReorderReverse( + const T* y, + const int M, + const int N, + T* z, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + + //assert((i-col*2+N-1)*2 < M*N*2); + //printf("z[%d] = y[%d]\n", i, (col&1)? (i-col*3/2+N-1) : (i-col/2)); + //z[i] = (col&1)? y[(i-col*3/2+N-1)] : y[(i-col/2)]; + // according to the paper, it should be N - (col+1)/2 for col is odd + // but it seems previous implementation accidentally matches this as well + z[i] = (col&1)? y[(i-col) + N - (col+1)/2] : y[(i-col/2)]; + } +} + +template +void addX0AndScale( + const T* x, + const int M, + const int N, + T* y, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int i0 = int(i/N)*N; + y[i] = (y[i]+x[i0])*0.5; + } +} + +/// extends from addX0AndScale to merge scaling +template +void addX0AndScaleN( + const T* x, + const int M, + const int N, + T* y, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int i0 = int(i/N)*N; + // this is to match python implementation + // normal way should be multiply by 0.25*N + y[i] = y[i]*0.25*N+x[i0]*0.5; + } +} + +/// given an array +/// x_0, x_1, ..., x_{N-1} +/// convert to +/// 0, x_{N-1}, ..., x_2, x_1 +/// drop x_0 +template +void computeFlipAndShift( + const T* x, + const int M, + const int N, + T* y, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int ii = i%N; + y[i] = (ii)? x[i+N-ii*2] : 0; + } +} + +/// flip sign of odd entries +/// index starts from 0 +template +void negateOddEntries( + T* x, + const int M, + const int N, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*(N/2); ++i) + { + x[i*2+1] = -x[i*2+1]; + } +} + +/// given an array +/// x_0, x_1, ..., x_{N-1} +/// convert to +/// x_{N-1}, ..., x_2, x_1, x_0 +template +void computeFlip( + const T* x, + const int M, + const int N, + T* y, + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int ii = i%N; + y[i] = x[i+N-ii*2-1]; + } +} + +at::Tensor dct_2N_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor idct_2N_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ); + +at::Tensor dct2_2N_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +at::Tensor idct2_2N_forward( + at::Tensor x, + at::Tensor expk0, + at::Tensor expk1, + int num_threads + ); + +template +void computePad( + const T* x, // M*N + const int M, + const int N, + T* z, // M*2N + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + int j = row*(N<<1) + col; + z[j] = x[i]; + } +} + +template +void computeMulExpk_2N( + const T* x, // M*(N+1)*2 + const T* expk, + const int M, + const int N, + T* z, // M*N + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int j = row*((N+1)<<1) + col_2x; + z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; + } +} + +template +void computeMulExpkAndPad_2N( + const T* x, // M*N + const T* expk, + const int M, + const int N, + T* z, // M*2N*2 + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int j = row*(N<<2) + col_2x; + z[j] = x[i]*expk[col_2x]; + z[j+1] = x[i]*expk[col_2x+1]; + } +} + + +/// remove last N entries in each column +template +void computeTruncation( + const T* x, // M*2N + const int M, + const int N, + T* z, // M*N + int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < M*N; ++i) + { + int row = i/N; // row + int col = i-row*N; // column + int j = row*(N<<1) + col; + z[i] = x[j]; + } +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp new file mode 100644 index 00000000..b33a139d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp @@ -0,0 +1,148 @@ +/** + * @file dct2_fft2.cpp + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Aug 2019 + * @brief All the transforms in this file are implemented based on 2D FFT. + * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. + */ + +#include "dct/src/dct2_fft2.h" + +DREAMPLACE_BEGIN_NAMESPACE + +void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads) { + CHECK_CPU(x); + CHECK_CPU(expkM); + CHECK_CPU(expkN); + CHECK_CPU(out); + CHECK_CPU(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_fft2_forward", [&] { + dct2dPreprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); + + buf = at::rfft(out, 2, false, true); + + dct2dPostprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); + }); +} + +void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads) { + CHECK_CPU(x); + CHECK_CPU(expkM); + CHECK_CPU(expkN); + CHECK_CPU(out); + CHECK_CPU(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_fft2_forward", [&] { + idct2_fft2PreprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idct2_fft2PostprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); + }); +} + +void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads) { + CHECK_CPU(x); + CHECK_CPU(expkM); + CHECK_CPU(expkN); + CHECK_CPU(out); + CHECK_CPU(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_idxst_forward", [&] { + idct_idxstPreprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idct_idxstPostprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); + }); +} + +void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads) { + CHECK_CPU(x); + CHECK_CPU(expkM); + CHECK_CPU(expkN); + CHECK_CPU(out); + CHECK_CPU(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_idct_forward", [&] { + idxst_idctPreprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idxst_idctPostprocessCpuLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("dct2_fft2", &DREAMPLACE_NAMESPACE::dct2_fft2_forward, "DCT2 FFT2D (CPU)"); + m.def("idct2_fft2", &DREAMPLACE_NAMESPACE::idct2_fft2_forward, "IDCT2 FFT2D (CPU)"); + m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT IDXST FFT2D (CPU)"); + m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST IDCT FFT2D (CPU)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h new file mode 100644 index 00000000..d0e51bcf --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h @@ -0,0 +1,613 @@ +/** + * @file dct2_fft2.h + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Aug 2019 + * @brief All the transforms in this file are implemented based on 2D FFT. + * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. + */ +#ifndef DREAMPLACE_DCT2_FFT2_H +#define DREAMPLACE_DCT2_FFT2_H + +#include +#include +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads); + +void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads); + +void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads); + +void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf, int num_threads); + +inline int INDEX(const int hid, const int wid, const int N) { + return (hid * N + wid); +} + +template +void dct2dPreprocessCpu(const T* x, T* y, const int M, const int N, + int num_threads) { + int halfN = N / 2; +#pragma omp parallel for num_threads(num_threads) + for(int hid = 0; hid < M; ++hid) { + for(int wid = 0; wid < N; ++wid) { + int index; + int cond = (((hid & 1) == 0) << 1) | ((wid & 1) == 0); + switch (cond) { + case 0: + index = INDEX(2 * M - (hid + 1), N - (wid + 1) / 2, halfN); + break; + case 1: + index = INDEX(2 * M - (hid + 1), wid / 2, halfN); + break; + case 2: + index = INDEX(hid, N - (wid + 1) / 2, halfN); + break; + case 3: + index = INDEX(hid, wid / 2, halfN); + break; + default: + break; + } + y[index] = x[INDEX(hid, wid, N)]; + } + } +} + +template +void dct2dPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, + int num_threads) { + dct2dPreprocessCpu(x, y, M, N, num_threads); +} + +template +void dct2dPostprocessCpu(const TComplex* V, T* y, const int M, const int N, + const TComplex* expkM, const TComplex* expkN, + int num_threads) { + int halfM = M / 2; + int halfN = N / 2; + T four_over_MN =(T)(4. / (M * N)); + T two_over_MN =(T)(2. / (M * N)); + +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < halfM; ++hid) { + for (int wid = 0; wid < halfN; ++wid) { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) { + case 0: + { + y[0] = V[0].x * four_over_MN; + y[halfN] = RealPartOfMul(expkN[halfN], V[halfN]) * four_over_MN; + y[INDEX(halfM, 0, N)] = expkM[halfM].x * V[INDEX(halfM, 0, halfN + 1)].x * four_over_MN; + y[INDEX(halfM, halfN, N)] = expkM[halfM].x * RealPartOfMul(expkN[halfN], V[INDEX(halfM, halfN, halfN + 1)]) * four_over_MN; + break; + } + + case 1: + { + ComplexType tmp; + + tmp = V[wid]; + y[wid] = RealPartOfMul(expkN[wid], tmp) * four_over_MN; + y[N - wid] = -ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; + + tmp = V[INDEX(halfM, wid, halfN + 1)]; + y[INDEX(halfM, wid, N)] = expkM[halfM].x * RealPartOfMul(expkN[wid], tmp) * four_over_MN; + y[INDEX(halfM, N - wid, N)] = -expkM[halfM].x * ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; + break; + } + + case 2: + { + ComplexType tmp1, tmp2, tmp_up, tmp_down; + tmp1 = V[INDEX(hid, 0, halfN + 1)]; + tmp2 = V[INDEX(M - hid, 0, halfN + 1)]; + tmp_up.x = expkM[hid].x * (tmp1.x + tmp2.x) + expkM[hid].y * (tmp2.y - tmp1.y); + tmp_down.x = -expkM[hid].y * (tmp1.x + tmp2.x) + expkM[hid].x * (tmp2.y - tmp1.y); + y[INDEX(hid, 0, N)] = tmp_up.x * two_over_MN; + y[INDEX(M - hid, 0, N)] = tmp_down.x * two_over_MN; + + tmp1 = complexAdd(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); + tmp2 = complexSubtract(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); + tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; + tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; + tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; + tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; + y[INDEX(hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_up) * two_over_MN; + y[INDEX(M - hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_down) * two_over_MN; + break; + } + + case 3: + { + ComplexType tmp1, tmp2, tmp_up, tmp_down; + tmp1 = complexAdd(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); + tmp2 = complexSubtract(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); + tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; + tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; + tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; + tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; + y[INDEX(hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_up) * two_over_MN; + y[INDEX(M - hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_down) * two_over_MN; + y[INDEX(hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_up) * two_over_MN; + y[INDEX(M - hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_down) * two_over_MN; + break; + } + + default: + assert(0); + break; + } + } + } +} + +template +void dct2dPostprocessCpuLauncher(const T* x, T* y, const int M, const int N, + const T* expkM, const T* expkN, + int num_threads) { + dct2dPostprocessCpu>((ComplexType *)x, y, M, N, (ComplexType *)expkM, (ComplexType *)expkN, num_threads); +} + +template +void idct2_fft2PreprocessCpu(const T* input, TComplex* output, const int M, + const int N, const TComplex* expkM, + const TComplex* expkN, int num_threads) { + const int halfM = M / 2; + const int halfN = N / 2; +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < halfM; ++hid) { + for (int wid = 0; wid < halfN; ++wid) { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = input[0]; + output[0].y = 0; + + tmp1 = input[halfN]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); + + tmp1 = input[INDEX(halfM, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + TComplex tmp_up; + tmp_up.x = input[wid]; + tmp_up.y = input[N - wid]; + output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); + + T tmp1 = input[INDEX(halfM, wid, N)]; + T tmp2 = input[INDEX(halfM, N - wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + tmp1 = input[INDEX(hid, 0, N)]; + tmp3 = input[INDEX(M - hid, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp3; + tmp_down.x = tmp3; + tmp_down.y = tmp1; + + output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); + output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); + + tmp1 = input[INDEX(hid, halfN, N)]; + tmp3 = input[INDEX(M - hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(hid, wid, N)]; + T tmp2 = input[INDEX(hid, N - wid, N)]; + T tmp3 = input[INDEX(M - hid, wid, N)]; + T tmp4 = input[INDEX(M - hid, N - wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } + } +} + +template +void idct2_fft2PreprocessCpuLauncher(const T* x, T* y, const int M, const int N, + const T* expkM, const T* expkN, + int num_threads) { + idct2_fft2PreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); +} + +template +void idct2_fft2PostprocessCpu(const T* x, T* y, const int M, const int N, + int num_threads) { + int MN = M * N; + #pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < M; ++hid) { + for (int wid = 0; wid < N; ++wid) { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + break; + default: + assert(0); + break; + } + y[index] = x[INDEX(hid, wid, N)] * MN; + } + } +} + +template +void idct2_fft2PostprocessCpuLauncher(const T* x, T* y, const int M, + const int N, int num_threads) { + idct2_fft2PostprocessCpu(x, y, M, N, num_threads); +} + +template +void idct_idxstPreprocessCpu(const T* input, TComplex* output, const int M, + const int N, const TComplex* expkM, + const TComplex* expkN, int num_threads) +{ + int halfM = M / 2; + int halfN = N / 2; +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < halfM; ++hid) + { + for (int wid = 0; wid < halfN; ++wid) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = 0; + output[0].y = 0; + + tmp1 = input[halfN]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); + + output[INDEX(halfM, 0, halfN + 1)].x = 0; + output[INDEX(halfM, 0, halfN + 1)].y = 0; + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + TComplex tmp_up; + tmp_up.x = input[N - wid]; + tmp_up.y = input[wid]; + output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); + + T tmp1 = input[INDEX(halfM, N - wid, N)]; + T tmp2 = input[INDEX(halfM, wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + output[INDEX(hid, 0, halfN + 1)].x = 0; + output[INDEX(hid, 0, halfN + 1)].y = 0; + output[INDEX(M - hid, 0, halfN + 1)].x = 0; + output[INDEX(M - hid, 0, halfN + 1)].y = 0; + + tmp1 = input[INDEX(hid, halfN, N)]; + tmp3 = input[INDEX(M - hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(hid, N - wid, N)]; + T tmp2 = input[INDEX(hid, wid, N)]; + T tmp3 = input[INDEX(M - hid, N - wid, N)]; + T tmp4 = input[INDEX(M - hid, wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } + } +} + +template +void idct_idxstPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, + const T* expkM, const T* expkN, + int num_threads) { + idct_idxstPreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); +} + +template +void idct_idxstPostprocessCpu(const T* x, T* y, const int M, const int N, + int num_threads) { + //const int halfN = N / 2; + const int MN = M * N; +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < M; ++hid) + { + for (int wid = 0; wid < N; ++wid) + { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) + { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + default: + assert(0); + break; + } + } + } +} + +template +void idct_idxstPostprocessCpuLauncher(const T* x, T* y, const int M, + const int N, int num_threads) { + idct_idxstPostprocessCpu(x, y, M, N, num_threads); +} + +template +void idxst_idctPreprocessCpu(const T* input, TComplex* output, const int M, + const int N, const TComplex* expkM, + const TComplex* expkN, int num_threads) { + const int halfM = M / 2; + const int halfN = N / 2; +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < halfM; ++hid) + { + for (int wid = 0; wid < halfN; ++wid) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = 0; + output[0].y = 0; + + output[halfN].x = 0; + output[halfN].y = 0; + + tmp1 = input[INDEX(halfM, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + output[wid].x = 0; + output[wid].y = 0; + + TComplex tmp_up; + T tmp1 = input[INDEX(halfM, wid, N)]; + T tmp2 = input[INDEX(halfM, N - wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + tmp1 = input[INDEX(M - hid, 0, N)]; + tmp3 = input[INDEX(hid, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp3; + tmp_down.x = tmp3; + tmp_down.y = tmp1; + + output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); + output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); + + tmp1 = input[INDEX(M - hid, halfN, N)]; + tmp3 = input[INDEX(hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(M - hid, wid, N)]; + T tmp2 = input[INDEX(M - hid, N - wid, N)]; + T tmp3 = input[INDEX(hid, wid, N)]; + T tmp4 = input[INDEX(hid, N - wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } + } +} + +template +void idxst_idctPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, + const T* expkM, const T* expkN, + int num_threads) { + idxst_idctPreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); +} + +template +void idxst_idctPostprocessCpu(const T* x, T* y, const int M, const int N, + int num_threads) { + //const int halfN = N / 2; + const int MN = M * N; +#pragma omp parallel for num_threads(num_threads) + for (int hid = 0; hid < M; ++hid) + { + for (int wid = 0; wid < N; ++wid) + { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) + { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + default: + assert(0); + break; + } + } + } +} + +template +void idxst_idctPostprocessCpuLauncher(const T* x, T* y, const int M, + const int N, int num_threads) { + idxst_idctPostprocessCpu(x, y, M, N, num_threads); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp new file mode 100644 index 00000000..8ab45a9c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp @@ -0,0 +1,148 @@ +/** + * @file dct2_fft2_cuda.cpp + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Apr 2019 + * @brief All the transforms in this file are implemented based on 2D FFT. + * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. + */ + +#include "dct2_fft2_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf) { + CHECK_CUDA(x); + CHECK_CUDA(expkM); + CHECK_CUDA(expkN); + CHECK_CUDA(out); + CHECK_CUDA(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_fft2_forward", [&] { + dct2dPreprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + + buf = at::rfft(out, 2, false, true); + + dct2dPostprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); + }); +} + +void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf) { + CHECK_CUDA(x); + CHECK_CUDA(expkM); + CHECK_CUDA(expkN); + CHECK_CUDA(out); + CHECK_CUDA(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_fft2_forward", [&] { + idct2_fft2PreprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idct2_fft2PostprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + }); +} + +void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf) { + CHECK_CUDA(x); + CHECK_CUDA(expkM); + CHECK_CUDA(expkN); + CHECK_CUDA(out); + CHECK_CUDA(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_idxst_forward", [&] { + idct_idxstPreprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idct_idxstPostprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + }); +} + +void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf) { + CHECK_CUDA(x); + CHECK_CUDA(expkM); + CHECK_CUDA(expkN); + CHECK_CUDA(out); + CHECK_CUDA(buf); + + CHECK_CONTIGUOUS(x); + CHECK_CONTIGUOUS(expkM); + CHECK_CONTIGUOUS(expkN); + CHECK_CONTIGUOUS(out); + CHECK_CONTIGUOUS(buf); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_idct_forward", [&] { + idxst_idctPreprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); + + auto y = at::irfft(buf, 2, false, true, {{M, N}}); + + idxst_idctPostprocessCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("dct2_fft2", &DREAMPLACE_NAMESPACE::dct2_fft2_forward, "DCT2 FFT2D (CUDA)"); + m.def("idct2_fft2", &DREAMPLACE_NAMESPACE::idct2_fft2_forward, "IDCT2 FFT2D (CUDA)"); + m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT IDXST FFT2D (CUDA)"); + m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST IDCT FFT2D (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h new file mode 100644 index 00000000..c8a82c33 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h @@ -0,0 +1,70 @@ +/** + * @file dct2_fft2_cuda.h + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Apr 2019 + * @brief All the transforms in this file are implemented based on 2D FFT. + * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. + */ + +#ifndef DREAMPLACE_DCT2_FFT2_CUDA_H +#define DREAMPLACE_DCT2_FFT2_CUDA_H + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +// dct2_fft2 +void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf); + +template +void dct2dPreprocessCudaLauncher(const T *x, T *y, const int M, const int N); + +template +void dct2dPostprocessCudaLauncher(const T *x, T *y, const int M, const int N, + const T *__restrict__ expkM, + const T *__restrict__ expkN); + +// idct2_fft2 +void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf); + +template +void idct2_fft2PreprocessCudaLauncher(const T *x, T *y, const int M, + const int N, const T *__restrict__ expkM, + const T *__restrict__ expkN); + +template +void idct2_fft2PostprocessCudaLauncher(const T *x, T *y, const int M, + const int N); + +// idct_idxst +void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf); + +template +void idct_idxstPreprocessCudaLauncher(const T *x, T *y, const int M, + const int N, const T *__restrict__ expkM, + const T *__restrict__ expkN); + +template +void idct_idxstPostprocessCudaLauncher(const T *x, T *y, const int M, + const int N); + +// idxst_idct +void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, + at::Tensor out, at::Tensor buf); + +template +void idxst_idctPreprocessCudaLauncher(const T *x, T *y, const int M, + const int N, const T *__restrict__ expkM, + const T *__restrict__ expkN); + +template +void idxst_idctPostprocessCudaLauncher(const T *x, T *y, const int M, + const int N); + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu new file mode 100644 index 00000000..21a1dedc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu @@ -0,0 +1,728 @@ +/** + * @file dct2_fft2_cuda_kernel.cu + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Apr 2019 + * @brief Refernece: Byeong Lee, "A new algorithm to compute the discrete cosine Transform," + * in IEEE Transactions on Acoustics, Speech, and Signal Processing, vol. 32, no. 6, pp. 1243-1245, December 1984. + * The preprocess and postprocess of 2d dct and 2d idct are discussed in the original paper. + * idct(idxst(x)) and idxst(idct(x)) are similar to the idct2d(x), + * except tiny modifications on preprocessing and postprocessing + */ + +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +#define TPB (16) + +DREAMPLACE_BEGIN_NAMESPACE + +inline __device__ int INDEX(const int hid, const int wid, const int N) +{ + return (hid * N + wid); +} + +// dct2_fft2 +template +__global__ void dct2dPreprocess(const T *x, T *y, const int M, const int N, const int halfN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < M && wid < N) + { + int index; + int cond = (((hid & 1) == 0) << 1) | ((wid & 1) == 0); + switch (cond) + { + case 0: + index = INDEX(2 * M - (hid + 1), N - (wid + 1) / 2, halfN); + break; + case 1: + index = INDEX(2 * M - (hid + 1), wid / 2, halfN); + break; + case 2: + index = INDEX(hid, N - (wid + 1) / 2, halfN); + break; + case 3: + index = INDEX(hid, wid / 2, halfN); + break; + default: + break; + } + y[index] = x[INDEX(hid, wid, N)]; + } +} + +template +void dct2dPreprocessCudaLauncher(const T *x, T *y, const int M, const int N) +{ + dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + dct2dPreprocess<<>>(x, y, M, N, N / 2); +} + +template +__global__ void __launch_bounds__(TPB * TPB, 8) dct2dPostprocess(const TComplex *V, T *y, const int M, const int N, + const int halfM, const int halfN, const T two_over_MN, const T four_over_MN, + const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) +{ + + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < halfM && wid < halfN) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + y[0] = V[0].x * four_over_MN; + y[halfN] = RealPartOfMul(expkN[halfN], V[halfN]) * four_over_MN; + y[INDEX(halfM, 0, N)] = expkM[halfM].x * V[INDEX(halfM, 0, halfN + 1)].x * four_over_MN; + y[INDEX(halfM, halfN, N)] = expkM[halfM].x * RealPartOfMul(expkN[halfN], V[INDEX(halfM, halfN, halfN + 1)]) * four_over_MN; + break; + } + + case 1: + { + ComplexType tmp; + + tmp = V[wid]; + y[wid] = RealPartOfMul(expkN[wid], tmp) * four_over_MN; + y[N - wid] = -ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; + + tmp = V[INDEX(halfM, wid, halfN + 1)]; + y[INDEX(halfM, wid, N)] = expkM[halfM].x * RealPartOfMul(expkN[wid], tmp) * four_over_MN; + y[INDEX(halfM, N - wid, N)] = -expkM[halfM].x * ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; + break; + } + + case 2: + { + ComplexType tmp1, tmp2, tmp_up, tmp_down; + tmp1 = V[INDEX(hid, 0, halfN + 1)]; + tmp2 = V[INDEX(M - hid, 0, halfN + 1)]; + tmp_up.x = expkM[hid].x * (tmp1.x + tmp2.x) + expkM[hid].y * (tmp2.y - tmp1.y); + tmp_down.x = -expkM[hid].y * (tmp1.x + tmp2.x) + expkM[hid].x * (tmp2.y - tmp1.y); + y[INDEX(hid, 0, N)] = tmp_up.x * two_over_MN; + y[INDEX(M - hid, 0, N)] = tmp_down.x * two_over_MN; + + tmp1 = complexAdd(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); + tmp2 = complexSubtract(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); + tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; + tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; + tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; + tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; + y[INDEX(hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_up) * two_over_MN; + y[INDEX(M - hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_down) * two_over_MN; + break; + } + + case 3: + { + ComplexType tmp1, tmp2, tmp_up, tmp_down; + tmp1 = complexAdd(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); + tmp2 = complexSubtract(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); + tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; + tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; + tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; + tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; + y[INDEX(hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_up) * two_over_MN; + y[INDEX(M - hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_down) * two_over_MN; + y[INDEX(hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_up) * two_over_MN; + y[INDEX(M - hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_down) * two_over_MN; + break; + } + + default: + assert(0); + break; + } + } +} + +template +void dct2dPostprocessCudaLauncher(const T *x, T *y, const int M, const int N, + const T *__restrict__ expkM, const T *__restrict__ expkN) +{ + dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + dct2dPostprocess><<>>((ComplexType *)x, y, M, N, M / 2, N / 2, (T)(2. / (M * N)), (T)(4. / (M * N)), (ComplexType *)expkM, (ComplexType *)expkN); +} + +// idct2_fft2 +template +__global__ void __launch_bounds__(TPB * TPB, 8) idct2_fft2Preprocess(const T *input, TComplex *output, const int M, const int N, + const int halfM, const int halfN, + const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < halfM && wid < halfN) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = input[0]; + output[0].y = 0; + + tmp1 = input[halfN]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); + + tmp1 = input[INDEX(halfM, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + TComplex tmp_up; + tmp_up.x = input[wid]; + tmp_up.y = input[N - wid]; + output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); + + T tmp1 = input[INDEX(halfM, wid, N)]; + T tmp2 = input[INDEX(halfM, N - wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + tmp1 = input[INDEX(hid, 0, N)]; + tmp3 = input[INDEX(M - hid, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp3; + tmp_down.x = tmp3; + tmp_down.y = tmp1; + + output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); + output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); + + tmp1 = input[INDEX(hid, halfN, N)]; + tmp3 = input[INDEX(M - hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(hid, wid, N)]; + T tmp2 = input[INDEX(hid, N - wid, N)]; + T tmp3 = input[INDEX(M - hid, wid, N)]; + T tmp4 = input[INDEX(M - hid, N - wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } +} + +template +void idct2_fft2PreprocessCudaLauncher( + const T *x, + T *y, + const int M, + const int N, + const T *__restrict__ expkM, + const T *__restrict__ expkN) +{ + dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idct2_fft2Preprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); +} + +template +__global__ void idct2_fft2Postprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < M && wid < N) + { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) + { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + break; + default: + assert(0); + break; + } + y[index] = x[INDEX(hid, wid, N)] * MN; + } +} + +template +void idct2_fft2PostprocessCudaLauncher(const T *x, T *y, const int M, const int N) +{ + dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idct2_fft2Postprocess<<>>(x, y, M, N, N / 2, M * N); +} + +// idct_idxst +// Adpated from idct2d_preprocess(). The only change is the reordered input +// if (wid != 0) +// new_input[hid][wid] = input[hid][N - wid]; +// else +// new_input[hid][0] = 0 +template +__global__ void __launch_bounds__(TPB * TPB, 8) idct_idxstPreprocess(const T *input, TComplex *output, const int M, const int N, + const int halfM, const int halfN, + const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < halfM && wid < halfN) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = 0; + output[0].y = 0; + + tmp1 = input[halfN]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); + + output[INDEX(halfM, 0, halfN + 1)].x = 0; + output[INDEX(halfM, 0, halfN + 1)].y = 0; + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + TComplex tmp_up; + tmp_up.x = input[N - wid]; + tmp_up.y = input[wid]; + output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); + + T tmp1 = input[INDEX(halfM, N - wid, N)]; + T tmp2 = input[INDEX(halfM, wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + output[INDEX(hid, 0, halfN + 1)].x = 0; + output[INDEX(hid, 0, halfN + 1)].y = 0; + output[INDEX(M - hid, 0, halfN + 1)].x = 0; + output[INDEX(M - hid, 0, halfN + 1)].y = 0; + + tmp1 = input[INDEX(hid, halfN, N)]; + tmp3 = input[INDEX(M - hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(hid, N - wid, N)]; + T tmp2 = input[INDEX(hid, wid, N)]; + T tmp3 = input[INDEX(M - hid, N - wid, N)]; + T tmp4 = input[INDEX(M - hid, wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } +} + +template +void idct_idxstPreprocessCudaLauncher(const T *x, T *y, const int M, const int N, + const T *__restrict__ expkM, const T *__restrict__ expkN) +{ + dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idct_idxstPreprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); +} + +// Adpated from idct2d_postprocess() with changes on sign and scale +// if (wid % 2 == 1) +// new_output[hid][wid] = -output[hid][wid]; +// else +// new_output[hid][wid] = output[hid][wid]; +template +__global__ void idct_idxstPostprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < M && wid < N) + { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) + { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + default: + assert(0); + break; + } + } +} + +template +void idct_idxstPostprocessCudaLauncher(const T *x, T *y, const int M, const int N) +{ + dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idct_idxstPostprocess<<>>(x, y, M, N, N / 2, M * N); +} + +// idxst_idct +// Adpated from idct2d_preprocess(). The only change is the reordered input +// if (hid != 0) +// new_input[hid][wid] = input[M - hid][wid]; +// else +// new_input[0][wid] = 0 +template +__global__ void __launch_bounds__(TPB * TPB, 8) idxst_idctPreprocess(const T *input, TComplex *output, const int M, const int N, + const int halfM, const int halfN, + const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < halfM && wid < halfN) + { + int cond = ((hid != 0) << 1) | (wid != 0); + switch (cond) + { + case 0: + { + T tmp1; + TComplex tmp_up; + + output[0].x = 0; + output[0].y = 0; + + output[halfN].x = 0; + output[halfN].y = 0; + + tmp1 = input[INDEX(halfM, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp1; + output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); + + tmp1 = input[INDEX(halfM, halfN, N)]; + tmp_up.x = 0; + tmp_up.y = 2 * tmp1; + output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); + break; + } + + case 1: + { + output[wid].x = 0; + output[wid].y = 0; + + TComplex tmp_up; + T tmp1 = input[INDEX(halfM, wid, N)]; + T tmp2 = input[INDEX(halfM, N - wid, N)]; + tmp_up.x = tmp1 - tmp2; + tmp_up.y = tmp1 + tmp2; + output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); + break; + } + + case 2: + { + T tmp1, tmp3; + TComplex tmp_up, tmp_down; + + tmp1 = input[INDEX(M - hid, 0, N)]; + tmp3 = input[INDEX(hid, 0, N)]; + tmp_up.x = tmp1; + tmp_up.y = tmp3; + tmp_down.x = tmp3; + tmp_down.y = tmp1; + + output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); + output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); + + tmp1 = input[INDEX(M - hid, halfN, N)]; + tmp3 = input[INDEX(hid, halfN, N)]; + tmp_up.x = tmp1 - tmp3; + tmp_up.y = tmp3 + tmp1; + tmp_down.x = tmp3 - tmp1; + tmp_down.y = tmp1 + tmp3; + + output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); + output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); + break; + } + + case 3: + { + T tmp1 = input[INDEX(M - hid, wid, N)]; + T tmp2 = input[INDEX(M - hid, N - wid, N)]; + T tmp3 = input[INDEX(hid, wid, N)]; + T tmp4 = input[INDEX(hid, N - wid, N)]; + TComplex tmp_up, tmp_down; + tmp_up.x = tmp1 - tmp4; + tmp_up.y = tmp3 + tmp2; + tmp_down.x = tmp3 - tmp2; + tmp_down.y = tmp1 + tmp4; + + output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); + output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); + break; + } + + default: + assert(0); + break; + } + } +} + +template +void idxst_idctPreprocessCudaLauncher( + const T *x, + T *y, + const int M, + const int N, + const T *__restrict__ expkM, + const T *__restrict__ expkN) +{ + dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idxst_idctPreprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); +} + +// Adpated from idct2d_postprocess() with changes on sign and scale +// if (hid % 2 == 1) +// new_output[hid][wid] = -output[hid][wid]; +// else +// new_output[hid][wid] = output[hid][wid]; +template +__global__ void idxst_idctPostprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) +{ + const int wid = blockDim.x * blockIdx.x + threadIdx.x; + const int hid = blockDim.y * blockIdx.y + threadIdx.y; + if (hid < M && wid < N) + { + int cond = ((hid < M / 2) << 1) | (wid < N / 2); + int index; + switch (cond) + { + case 0: + index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 1: + index = INDEX(((M - hid) << 1) - 1, wid << 1, N); + y[index] = -x[INDEX(hid, wid, N)] * MN; + break; + case 2: + index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + case 3: + index = INDEX(hid << 1, wid << 1, N); + y[index] = x[INDEX(hid, wid, N)] * MN; + break; + default: + assert(0); + break; + } + } +} + +template +void idxst_idctPostprocessCudaLauncher(const T *x, T *y, const int M, const int N) +{ + dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); + dim3 blockSize(TPB, TPB, 1); + idxst_idctPostprocess<<>>(x, y, M, N, N / 2, M * N); +} + +// dct2_fft2 +#define REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(type) \ + template void dct2dPreprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N); + +REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(float); +REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(double); + +#define REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(type) \ + template void dct2dPostprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N, \ + const type *__restrict__ expkM, \ + const type *__restrict__ expkN); + +REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(float); +REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(double); + +//idct_idxst +#define REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(type) \ + template void idct_idxstPreprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N, \ + const type *__restrict__ expkM, \ + const type *__restrict__ expkN); + +REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(double); + +#define REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(type) \ + template void idct_idxstPostprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N); + +REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(double); + +//idxst_idct +#define REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(type) \ + template void idxst_idctPreprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N, \ + const type *__restrict__ expkM, \ + const type *__restrict__ expkN); + +REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(double); + +#define REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(type) \ + template void idxst_idctPostprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N); + +REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(double); + +//idct2_fft2 +#define REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(type) \ + template void idct2_fft2PreprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N, \ + const type *__restrict__ expkM, \ + const type *__restrict__ expkN); + +REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(double); + +#define REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(type) \ + template void idct2_fft2PostprocessCudaLauncher( \ + const type *x, \ + type *y, \ + const int M, \ + const int N); + +REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(float); +REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp new file mode 100644 index 00000000..df9a9697 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp @@ -0,0 +1,187 @@ +/** + * @file dct_2N.cpp + * @author Yibo Lin (DREAMPlace) + * @date Nov 2018 + */ +#include "dct.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk, int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel()/N; + + auto x_pad = at::zeros({M, 2*N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_2N_forward", [&] { + computePad(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), + num_threads); + + auto y = at::rfft(x_pad, 1, false, true); + + // re-use x_pad as output + x_pad.resize_({{M, N}}); + computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + x_pad.mul_(1.0/N); + }); + + return x_pad; +} + +at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk, int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel()/N; + + auto x_pad = at::zeros({M, 2*N, 2}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_2N_forward", [&] { + computeMulExpkAndPad_2N( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + // y is real now + auto y = at::irfft(x_pad, 1, false, false, {2*N}); + + // reuse x_pad + x_pad.resize_({{M, N}}); + computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + //std::cout << "z\n" << z << "\n"; + + // this is to match python implementation + // normal way should be multiply by 0.25*N + x_pad.mul_(N); + }); + + return x_pad; +} + +at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + // 1D DCT to columns + + //std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel()/N; + auto x_pad = at::zeros({M, 2*N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_2N_forward", [&] { + computePad(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), + num_threads); + + auto y = at::rfft(x_pad, 1, false, true); + + // re-use x_pad as output + x_pad.resize_({{M, N}}); + computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + //x_pad.mul_(1.0/N); + + // 1D DCT to rows + auto xt = x_pad.transpose(-2, -1).contiguous(); + // I do not want to allocate memory another time + // must zero-out x_pad + x_pad.resize_({N, 2*M}).zero_(); + computePad(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), + num_threads); + + y = at::rfft(x_pad, 1, false, true); + //y.mul_(1.0/M); + + // re-use x_reorder as output + x_pad.resize_({N, M}); + computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + x_pad.mul_(1.0/(M*N)); + x_pad.transpose_(-2, -1); + }); + + return x_pad.contiguous(); +} + +at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // 1D DCT to columns + + auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_2N_forward", [&] { + computeMulExpkAndPad_2N( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + // y is real now + auto y = at::irfft(x_pad, 1, false, false, {2 * N}); + + // reuse x_pad + x_pad.resize_({M, N}); + computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + // this is to match python implementation + // normal way should be multiply by 0.25*N + // x_pad.mul_(N); + + // 1D DCT to rows + auto xt = x_pad.transpose(-2, -1).contiguous(); + x_pad.resize_({N, 2 * M, 2}).zero_(); + computeMulExpkAndPad_2N( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + // y is real now + y = at::irfft(x_pad, 1, false, false, {2 * M}); + + // reuse x_pad + x_pad.resize_({N, M}); + computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); + + // this is to match python implementation + // normal way should be multiply by 0.25*0.25*M*N + x_pad.mul_(M * N); + x_pad.transpose_(-2, -1); + }); + + return x_pad.contiguous(); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp new file mode 100644 index 00000000..b8e10e44 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp @@ -0,0 +1,188 @@ +/** + * @file dct_2N_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Nov 2018 + */ +#include "dct_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + auto x_pad = at::zeros({M, 2 * N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_2N_forward", [&] { + computePadCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + auto y = at::rfft(x_pad, 1, false, true); + + // re-use x_pad as output + x_pad.resize_({M, N}); + computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + x_pad.mul_(1.0 / N); + }); + + return x_pad; +} + +at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_2N_forward", [&] { + computeMulExpkAndPad_2N_CudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + // y is real now + auto y = at::irfft(x_pad, 1, false, false, {2 * N}); + + // reuse x_pad + x_pad.resize_({M, N}); + computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + // this is to match python implementation + // normal way should be multiply by 0.25*N + x_pad.mul_(N); + }); + + return x_pad; +} + +at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + auto x_pad = at::zeros({M, 2 * N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_2N_forward", [&] { + computePadCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + auto y = at::rfft(x_pad, 1, false, true); + + // re-use x_pad as output + x_pad.resize_({M, N}); + computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + // x_pad.mul_(1.0/N); + + // 1D DCT to rows + auto xt = x_pad.transpose(-2, -1).contiguous(); + // I do not want to allocate memory another time + // must zero-out x_pad + x_pad.resize_({N, 2 * M}).zero_(); + computePadCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + y = at::rfft(x_pad, 1, false, true); + // y.mul_(1.0/M); + + // re-use x_reorder as output + x_pad.resize_({N, M}); + computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + x_pad.mul_(1.0 / (M * N)); + x_pad.transpose_(-2, -1); + }); + + return x_pad.contiguous(); +} + +at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // 1D DCT to columns + + auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_2N_forward", [&] { + computeMulExpkAndPad_2N_CudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + // y is real now + auto y = at::irfft(x_pad, 1, false, false, {2 * N}); + + // reuse x_pad + x_pad.resize_({M, N}); + computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + // this is to match python implementation + // normal way should be multiply by 0.25*N + // x_pad.mul_(N); + + // 1D DCT to rows + auto xt = x_pad.transpose(-2, -1).contiguous(); + x_pad.resize_({N, 2 * M, 2}).zero_(); + computeMulExpkAndPad_2N_CudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + // y is real now + y = at::irfft(x_pad, 1, false, false, {2 * M}); + + // reuse x_pad + x_pad.resize_({N, M}); + computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); + + // this is to match python implementation + // normal way should be multiply by 0.25*0.25*M*N + x_pad.mul_(M * N); + x_pad.transpose_(-2, -1); + }); + + return x_pad.contiguous(); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp new file mode 100644 index 00000000..531df58a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp @@ -0,0 +1,265 @@ +/** + * @file dct_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + */ +#include "dct_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dct_forward(at::Tensor x, at::Tensor expk) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // auto x_reorder = at::empty_like(x); + auto x_reorder = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_forward", [&] { + computeReorderCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + auto y = at::rfft(x_reorder, 1, false, true); + y.mul_(1.0 / N); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + computeMulExpkCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + // std::cout << "z\n" << z << "\n"; + }); + + return x_reorder; +} + +at::Tensor idct_forward(at::Tensor x, at::Tensor expk) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk); + CHECK_CONTIGUOUS(expk); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_forward", [&] { + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + // auto z = at::empty({M, N}, x.options()); + // reuse v + v.resize_({M, N}); + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + // std::cout << "z\n" << z << "\n"; + // this is to match python implementation + // normal way should be multiply by 0.25*N + v.mul_(0.5 * N); + }); + + return v; +} + +at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + auto x_reorder = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_forward", [&] { + computeReorderCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + auto y = at::rfft(x_reorder, 1, false, true); + // y.mul_(1.0/N); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + // std::cout << "expk1\n" << expk1 << "\n"; + computeMulExpkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, + N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + // std::cout << "z\n" << x_reorder << "\n"; + + // 1D DCT to rows + auto xt = x_reorder.transpose(-2, -1).contiguous(); + // std::cout << "xt\n" << xt << "\n"; + // I do not want to allocate memory another time + // x_reorder = at::empty_like(xt); + x_reorder = x_reorder.view_as(xt); + computeReorderCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + + // std::cout << "x_reorder\n" << x_reorder << "\n"; + + y = at::rfft(x_reorder, 1, false, true); + // y.mul_(1.0/M); + // std::cout << "y\n" << y << "\n"; + + // re-use x_reorder as output + // std::cout << "expk0\n" << expk0 << "\n"; + computeMulExpkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, + M, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + + x_reorder.mul_(1.0 / (M * N)); + x_reorder.transpose_(-2, -1); + }); + + return x_reorder.contiguous(); +} + +at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_forward", [&] { + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "expk1\n" << expk1 << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + // y.mul_(0.25*N); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + // auto z = at::empty({M, N}, x.options()); + // reuse v + v.resize_({M, N}); + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + // 1D DCT to rows + auto xt = v.transpose(-2, -1).contiguous(); + // std::cout << "xt\n" << xt << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // reuse v + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "expk0\n" << expk0 << "\n"; + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + // y.mul_(0.25*M); + + // std::cout << "y\n" << y << "\n"; + + // I do not want to allocate memory another time + v.resize_({N, M}); + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + // this is to match python implementation + // normal way should be multiply by 0.25*0.25*M*N + v.mul_(0.25 * M * N); + v.transpose_(-2, -1); + }); + + return v.contiguous(); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("dct", &DREAMPLACE_NAMESPACE::dct_forward, "DCT forward (CUDA)"); + m.def("idct", &DREAMPLACE_NAMESPACE::idct_forward, "IDCT forward (CUDA)"); + m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_forward, "DCT2 forward (CUDA)"); + m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_forward, "IDCT2 forward (CUDA)"); + + m.def("dst", &DREAMPLACE_NAMESPACE::dst_forward, "DST forward (CUDA)"); + m.def("idst", &DREAMPLACE_NAMESPACE::idst_forward, "IDST forward (CUDA)"); + + m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_forward, "IDXCT forward (CUDA)"); + m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_forward, "IDXST forward (CUDA)"); + + // use idxst and idxct as kernels + m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_forward, + "IDCCT2 forward (CUDA)"); + m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_forward, + "IDCST2 forward (CUDA)"); + m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_forward, + "IDSCT2 forward (CUDA)"); + + // use idxst and idct as kernels + m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, + "IDXST(IDCT(x)) forward (CUDA)"); + m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, + "IDCT(IDXST(x)) forward (CUDA)"); + + m.def("dct_2N", &DREAMPLACE_NAMESPACE::dct_2N_forward, "DCT forward (CUDA)"); + m.def("idct_2N", &DREAMPLACE_NAMESPACE::idct_2N_forward, + "IDCT forward (CUDA)"); + m.def("dct2_2N", &DREAMPLACE_NAMESPACE::dct2_2N_forward, + "DCT2 forward (CUDA)"); + m.def("idct2_2N", &DREAMPLACE_NAMESPACE::idct2_2N_forward, + "IDCT2 forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h new file mode 100644 index 00000000..990e3d2f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h @@ -0,0 +1,119 @@ +/** + * @file dct_cuda.h + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#ifndef DREAMPLACE_DCT_CUDA_H +#define DREAMPLACE_DCT_CUDA_H + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dct_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idct_forward(at::Tensor x, at::Tensor expk); + +at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor dst_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idst_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idxct_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idxst_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +template +void computeReorderCudaLauncher(const T* x, const int M, const int N, T* y); + +template +void computeMulExpkCudaLauncher(const T* x, const T* expk, const int M, + const int N, T* z); + +template +void computeVkCudaLauncher(const T* x, const T* expk, const int M, const int N, + T* v); + +template +void computeReorderReverseCudaLauncher(const T* y, const int M, const int N, + T* z); + +template +void addX0AndScaleCudaLauncher(const T* x, const int M, const int N, T* y); + +/// extends from addX0AndScale to merge scaling +template +void addX0AndScaleNCudaLauncher(const T* x, const int M, const int N, T* y); + +/// given an array +/// x_0, x_1, ..., x_{N-1} +/// convert to +/// x_{N-1}, ..., x_2, x_1, x_0 +template +void computeFlipCudaLauncher(const T* x, const int M, const int N, T* y); + +/// given an array +/// x_0, x_1, ..., x_{N-1} +/// convert to +/// 0, x_{N-1}, ..., x_2, x_1 +/// drop x_0 +template +void computeFlipAndShiftCudaLauncher(const T* x, const int M, const int N, + T* y); + +/// flip sign of odd entries +/// index starts from 0 +template +void negateOddEntriesCudaLauncher(T* x, const int M, const int N); + +at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk); + +at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk); + +at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); + +template +void computePadCudaLauncher(const T* x, // M*N + const int M, const int N, + T* z // M*2N +); + +template +void computeMulExpk_2N_CudaLauncher(const T* x, // M*(N+1)*2 + const T* expk, const int M, const int N, + T* z // M*N +); + +template +void computeMulExpkAndPad_2N_CudaLauncher(const T* x, // M*N + const T* expk, const int M, + const int N, + T* z // M*2N*2 +); + +/// remove last N entries in each column +template +void computeTruncationCudaLauncher(const T* x, // M*2N + const int M, const int N, + T* z // M*N +); + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu new file mode 100644 index 00000000..c8dc2df1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu @@ -0,0 +1,523 @@ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeMulExpk( + const T* x, + const T* expk, + const int M, + const int N, + T* z + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < M*N) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int fft_onesided_size = (N>>1)+1; + int fft_onesided_size_2x = fft_onesided_size<<1; + + if (col_2x <= N) + { + int j = row*fft_onesided_size_2x + col_2x; + //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); + z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; + } + else + { + int j = row*fft_onesided_size_2x + (N<<1) - col_2x; + //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); + z[i] = x[j]*expk[col_2x] - x[j+1]*expk[col_2x+1]; + } + } +} + +template +void computeMulExpkCudaLauncher( + const T* x, + const T* expk, + const int M, + const int N, + T* z + ) +{ + const int thread_count = 1024; + const int block_count = (M * N - 1 + thread_count) / thread_count; + + computeMulExpk<<>>( + x, + expk, + M, + N, + z + ); +} + +template +__global__ void computeReorder( + const T* x, + const int M, + const int N, + T* y + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < M*N) + { + int ii = i%N; + + if (ii < (N>>1)) + { + // i*2 + //printf("x[%d] = y[%d]\n", i+ii, i); + y[i] = x[i+ii]; + } + else + { + // (N-i)*2-1 + //printf("x[%d] = y[%d]\n", i+N*2-ii*3-1, i); + y[i] = x[i+N*2-ii*3-1]; + } + } +} + +template +void computeReorderCudaLauncher( + const T* x, + const int M, + const int N, + T* y + ) +{ + const int thread_count = 1024; + const int block_count = (M * N - 1 + thread_count) / thread_count; + + computeReorder<<>>( + x, + M, + N, + y + ); +} + +template +__global__ void computeVk( + const T* x, + const T* expk, + const int M, + const int N, + T* v + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < M*(N/2+1)) + { + int ncol = N/2+1; + int row = i/ncol; // row + int col = i-row*ncol; // column + int col_2x = (col<<1); + + // real + T real = x[row*N+col]; + T imag = (col == 0)? 0 : -x[row*N+N-col]; + + v[2*i] = real*expk[col_2x] - imag*expk[col_2x+1]; + // imag, x[N-i] + v[2*i+1] = real*expk[col_2x+1] + imag*expk[col_2x]; + } + +} + +template +void computeVkCudaLauncher( + const T* x, + const T* expk, + const int M, + const int N, + T* v + ) +{ + const int thread_count = 512; + const int block_count = (M*(N/2+1) - 1 + thread_count) / thread_count; + + computeVk<<>>( + x, + expk, + M, + N, + v + ); +} + + +template +__global__ void computeReorderReverse( + const T* y, + const int M, + const int N, + T* z + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < M*N) + { + int row = i/N; // row + int col = i-row*N; // column + + //printf("z[%d] = y[%d]\n", i, (col&1)? (i-col*3/2+N-1) : (i-col/2)); + //z[i] = (col&1)? y[(i-col*3/2+N-1)] : y[(i-col/2)]; + // according to the paper, it should be N - (col+1)/2 for col is odd + // but it seems previous implementation accidentally matches this as well + z[i] = (col&1)? y[(i-col) + N - (col+1)/2] : y[(i-col/2)]; + } +} + +template +void computeReorderReverseCudaLauncher( + const T* y, + const int M, + const int N, + T* z + ) +{ + const int thread_count = 512; + const int block_count = (M * N - 1 + thread_count) / thread_count; + + computeReorderReverse<<>>( + y, + M, + N, + z + ); +} + +template +__global__ void addX0AndScale( + const T* x, + const int M, + const int N, + T* y + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int i0 = int(i/N)*N; + y[i] = (y[i]+x[i0])*0.5; + } +} + +template +void addX0AndScaleCudaLauncher( + const T* x, + const int M, + const int N, + T* y + ) +{ + addX0AndScale<<<32, 1024>>>( + x, + M, + N, + y + ); +} + +/// extends from addX0AndScale to merge scaling +template +__global__ void addX0AndScaleN( + const T* x, + const int M, + const int N, + T* y + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int i0 = int(i/N)*N; + // this is to match python implementation + // normal way should be multiply by 0.25*N + y[i] = y[i]*0.25*N+x[i0]*0.5; + } +} + +template +void addX0AndScaleNCudaLauncher( + const T* x, + const int M, + const int N, + T* y + ) +{ + addX0AndScaleN<<<32, 1024>>>( + x, + M, + N, + y + ); +} + +template +__global__ void computePad( + const T* x, // M*N + const int M, + const int N, + T* z // M*2N + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int row = i/N; // row + int col = i-row*N; // column + int j = row*(N<<1) + col; + z[j] = x[i]; + } +} + +template +void computePadCudaLauncher( + const T* x, // M*N + const int M, + const int N, + T* z // M*2N + ) +{ + computePad<<<32, 1024>>>( + x, + M, + N, + z + ); +} + +template +__global__ void computeMulExpk_2N( + const T* x, // M*(N+1)*2 + const T* expk, + const int M, + const int N, + T* z // M*N + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int j = row*((N+1)<<1) + col_2x; + z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; + } +} + +template +void computeMulExpk_2N_CudaLauncher( + const T* x, // M*(N+1)*2 + const T* expk, + const int M, + const int N, + T* z // M*N + ) +{ + computeMulExpk_2N<<<32, 1024>>>( + x, + expk, + M, + N, + z + ); +} + +template +__global__ void computeMulExpkAndPad_2N( + const T* x, // M*N + const T* expk, + const int M, + const int N, + T* z // M*2N*2 + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int row = i/N; // row + int col = i-row*N; // column + int col_2x = (col<<1); + int j = row*(N<<2) + col_2x; + z[j] = x[i]*expk[col_2x]; + z[j+1] = x[i]*expk[col_2x+1]; + } +} + +template +void computeMulExpkAndPad_2N_CudaLauncher( + const T* x, // M*N + const T* expk, + const int M, + const int N, + T* z // M*2N*2 + ) +{ + computeMulExpkAndPad_2N<<<32, 1024>>>( + x, + expk, + M, + N, + z + ); +} + +/// remove last N entries in each column +template +__global__ void computeTruncation( + const T* x, // M*2N + const int M, + const int N, + T* z // M*N + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int row = i/N; // row + int col = i-row*N; // column + int j = row*(N<<1) + col; + z[i] = x[j]; + } +} + +template +void computeTruncationCudaLauncher( + const T* x, // M*2N + const int M, + const int N, + T* z // M*N + ) +{ + computeTruncation<<<32, 1024>>>( + x, + M, + N, + z + ); +} + +// manually instantiate the template function +#define REGISTER_MULPEXPK_KERNEL_LAUNCHER(type) \ + template void computeMulExpkCudaLauncher(\ + const type* x, \ + const type* expk, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_MULPEXPK_KERNEL_LAUNCHER(float); +REGISTER_MULPEXPK_KERNEL_LAUNCHER(double); + +#define REGISTER_REORDER_KERNEL_LAUNCHER(type) \ + template void computeReorderCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* y \ + ); + +REGISTER_REORDER_KERNEL_LAUNCHER(float); +REGISTER_REORDER_KERNEL_LAUNCHER(double); + +#define REGISTER_VK_KERNEL_LAUNCHER(type) \ + template void computeVkCudaLauncher(\ + const type* x, \ + const type* expk, \ + const int M, \ + const int N, \ + type* v \ + ); + +REGISTER_VK_KERNEL_LAUNCHER(float); +REGISTER_VK_KERNEL_LAUNCHER(double); + +#define REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(type) \ + template void computeReorderReverseCudaLauncher(\ + const type* y, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(float); +REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(double); + +#define REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(type) \ + template void addX0AndScaleCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* y \ + ); + +REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(float); +REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(double); + +#define REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(type) \ + template void addX0AndScaleNCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* y \ + ); + +REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(float); +REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(double); + +#define REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(type) \ + template void computePadCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(float); +REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(double); + +#define REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(type) \ + template void computeMulExpk_2N_CudaLauncher(\ + const type* x, \ + const type* expk, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(float); +REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(double); + +#define REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(type) \ + template void computeMulExpkAndPad_2N_CudaLauncher(\ + const type* x, \ + const type* expk, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(float); +REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(double); + +#define REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(type) \ + template void computeTruncationCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* z \ + ); + +REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(float); +REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp new file mode 100644 index 00000000..b9def3ef --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp @@ -0,0 +1,337 @@ +/** + * @file dct_lee.cpp + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +#include "dct.h" +#include "dct_lee_cpu.h" + +DREAMPLACE_BEGIN_NAMESPACE + +void dct_lee_precompute_dct_cos(int N, at::Tensor out) { + out.resize_(N); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + out, "dct_lee_precompute_dct_cos", [&] { + lee::precompute_dct_cos( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); + }); +} + +void dct_lee_precompute_idct_cos(int N, at::Tensor out) { + out.resize_(N); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + out, "dct_lee_precompute_idct_cos", [&] { + lee::precompute_idct_cos( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); + }); +} + +void dct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out, int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos); + CHECK_CONTIGUOUS(cos); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_lee_forward", [&] { + lee::dct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N, num_threads); + }); + + out.mul_(2.0 / N); +} + +void idct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out, int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos); + CHECK_CONTIGUOUS(cos); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_lee_forward", [&] { + lee::idct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N, num_threads); + }); + + out.mul_(2); +} + +void dst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, + at::Tensor out, int num_threads = at::get_num_threads()) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + buf.copy_(x); + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, + num_threads); + + dct_lee_forward(buf, expk, buf, out, num_threads); + // std::cout << "y\n" << y << "\n"; + + computeFlip(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + num_threads); + }); + + out.copy_(buf); +} + +void idst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, + at::Tensor out, int num_threads = at::get_num_threads()) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + computeFlip(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + num_threads); + + idct_lee_forward(buf, expk, buf, out, num_threads); + // std::cout << "y\n" << y << "\n"; + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + num_threads); + // std::cout << "z\n" << y << "\n"; + }); +} + +void dct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf, at::Tensor out, + int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CPU(cos1); + CHECK_CONTIGUOUS(cos1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + + out.resize_({M, N}); + buf.resize_({M, N}); + + dct_lee_forward(x, cos1, out, buf, num_threads); + + // 1D DCT to rows + out.resize_({N, M}); + out.copy_(buf.transpose(-2, -1)); + buf.resize_({N, M}); + + dct_lee_forward(out, cos0, out, buf, num_threads); + + out.resize_({M, N}); + out.copy_(buf.transpose_(-2, -1)); +} + +void idct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf, at::Tensor out, + int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CPU(cos1); + CHECK_CONTIGUOUS(cos1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + + out.resize_({M, N}); + buf.resize_({M, N}); + + idct_lee_forward(x, cos1, out, buf, num_threads); + + // 1D DCT to rows + out.resize_({N, M}); + out.copy_(buf.transpose(-2, -1)); + buf.resize_({N, M}); + + idct_lee_forward(out, cos0, out, buf, num_threads); + + out.resize_({M, N}); + out.copy_(buf.transpose(-2, -1)); +} + +void idxct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out, + int num_threads = at::get_num_threads()) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_lee_forward", [&] { + idct_lee_forward(x, cos, buf, out, num_threads); + + // std::cout << __func__ << " z\n" << z << "\n"; + + addX0AndScale(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), + num_threads); + }); +} + +void idxst_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out, + int num_threads = at::get_num_threads()) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + num_threads); + + idct_lee_forward(buf, cos, buf, out, num_threads); + out.mul_(0.5); + // std::cout << "y\n" << y << "\n"; + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + num_threads); + // std::cout << "z\n" << y << "\n"; + }); +} + +void idcct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out, + int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CPU(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxct for rows + + idxct_lee_forward(x, cos1, buf0, out, num_threads); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxct_lee_forward(buf0, cos0, out, buf1, num_threads); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +void idcst2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out, + int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CPU(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxst for rows + + idxst_lee_forward(x, cos1, buf0, out, num_threads); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxct_lee_forward(buf0, cos0, out, buf1, num_threads); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +void idsct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out, + int num_threads = at::get_num_threads()) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CPU(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxst for rows + + idxct_lee_forward(x, cos1, buf0, out, num_threads); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxst_lee_forward(buf0, cos0, out, buf1, num_threads); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("precompute_dct_cos", &DREAMPLACE_NAMESPACE::dct_lee_precompute_dct_cos, + "Precompute DCT cosine"); + m.def("precompute_idct_cos", + &DREAMPLACE_NAMESPACE::dct_lee_precompute_idct_cos, + "Precompute IDCT cosine"); + m.def("dct", &DREAMPLACE_NAMESPACE::dct_lee_forward, "DCT forward"); + m.def("idct", &DREAMPLACE_NAMESPACE::idct_lee_forward, "IDCT forward"); + m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_lee_forward, "IDXCT forward"); + m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_lee_forward, "IDXST forward"); + + m.def("dst", &DREAMPLACE_NAMESPACE::dst_lee_forward, "DST forward"); + m.def("idst", &DREAMPLACE_NAMESPACE::idst_lee_forward, "IDST forward"); + + m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_lee_forward, "DCT2 forward"); + m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_lee_forward, "IDCT2 forward"); + m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_lee_forward, "IDCCT2 forward"); + m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_lee_forward, "IDCST2 forward"); + m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_lee_forward, "IDSCT2 forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h new file mode 100644 index 00000000..b5545da1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h @@ -0,0 +1,309 @@ +/** + * @file dct_lee_cpu.h + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +#ifndef DREAMPLACE_DCT_LEE_CPU_H +#define DREAMPLACE_DCT_LEE_CPU_H + +#include +#include +#include +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +namespace lee { + +constexpr double PI = 3.14159265358979323846; + +/// Return true if a number is power of 2 +template +inline bool isPowerOf2(T val) { + return val && (val & (val - 1)) == 0; +} + +/// Transpose a row-major matrix with M rows and N columns using block transpose +/// method +template +inline void transpose(const TValue *in, TValue *out, TIndex M, TIndex N, + TIndex blockSize = 16) { + //#pragma omp parallel for collapse(2) schedule(static) + for (TIndex j = 0; j < N; j += blockSize) { + for (TIndex i = 0; i < M; i += blockSize) { + // Transpose the block beginning at [i, j] + TIndex xend = std::min(M, i + blockSize); + TIndex yend = std::min(N, j + blockSize); + for (TIndex y = j; y < yend; ++y) { + for (TIndex x = i; x < xend; ++x) { + out[x + y * M] = in[y + x * N]; + } + } + } + } +} + +/// Negate values in odd position of a vector +template +inline void negateOddEntries(TValue *vec, TIndex N, int num_threads) { +#pragma omp parallel for num_threads(num_threads) + for (TIndex i = 1; i < N; i += 2) { + vec[i] = -vec[i]; + } +} + +/// Precompute cosine values needed for N-point dct +/// @param cos size N - 1 buffer, contains the result after function call +/// @param N the length of target dct, must be power of 2 +template +void precompute_dct_cos(TValue *cos, TIndex N) { + // The input length must be power of 2 + if (!isPowerOf2(N)) { + throw std::domain_error("Input length is not power of 2."); + } + + TIndex offset = 0; + TIndex halfLen = N / 2; + while (halfLen) { + TValue phaseStep = 0.5 * PI / halfLen; + TValue phase = 0.5 * phaseStep; + for (TIndex i = 0; i < halfLen; ++i) { + cos[offset + i] = 0.5 / std::cos(phase); + phase += phaseStep; + } + offset += halfLen; + halfLen /= 2; + } +} + +/// Precompute cosine values needed for N-point idct +/// @param cos size N - 1 buffer, contains the result after function call +/// @param N the length of target idct, must be power of 2 +template +void precompute_idct_cos(TValue *cos, TIndex N) { + // The input length must be power of 2 + if (!isPowerOf2(N)) { + throw std::domain_error("Input length is not power of 2."); + } + + TIndex offset = 0; + TIndex halfLen = 1; + while (halfLen < N) { + TValue phaseStep = 0.5 * PI / halfLen; + TValue phase = 0.5 * phaseStep; + for (TIndex i = 0; i < halfLen; ++i) { + cos[offset + i] = 0.5 / std::cos(phase); + phase += phaseStep; + } + offset += halfLen; + halfLen *= 2; + } +} + +/// The implementation of fast Discrete Cosine Transform (DCT) algorithm and its +/// inverse (IDCT) are Lee's algorithms Algorithm reference: A New Algorithm to +/// Compute the Discrete Cosine Transform, by Byeong Gi Lee, 1984 +/// +/// Lee's algorithm has a recursive structure in nature. +/// Here is a sample recursive implementation: +/// https://www.nayuki.io/page/fast-discrete-cosine-transform-algorithms +/// +/// My implementation here is iterative, which is more efficient than the +/// recursive version. Here is a sample iterative implementation: +/// https://www.codeproject.com/Articles/151043/Iterative-Fast-1D-Forvard-DCT + +/// Compute y[k] = sum_n=0..N-1 (x[n] * cos((n + 0.5) * k * PI / N)), for k = +/// 0..N-1 +/// +/// @param vec length N sequence to be transformed +/// @param temp length 2 * N helping buffer +/// @param cos length N - 1, stores cosine values precomputed by function +/// 'precompute_dct_cos' +/// @param N length of vec, must be power of 2 +template +inline void dct(TValue *vec, TValue *out, TValue *buf, const TValue *cos, + TIndex N) { + // The input length must be power of 2 + if (!isPowerOf2(N)) { + throw std::domain_error("Input length is not power of 2."); + } + + // Pointers point to the beginning indices of two adjacent iterations + TValue *curr = out; + TValue *next = buf; + + // 'temp' is used to store data of two adjacent iterations + // Copy 'vec' to the first N element in 'temp' + std::copy(vec, vec + N, curr); + + // Current bufferfly length and half length + TIndex len = N; + TIndex halfLen = len / 2; + + // Iteratively bi-partition sequences into sub-sequences + TIndex cosOffset = 0; + while (halfLen) { + TIndex offset = 0; + TIndex steps = N / len; + for (TIndex k = 0; k < steps; ++k) { + for (TIndex i = 0; i < halfLen; ++i) { + next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; + next[offset + halfLen + i] = + (curr[offset + i] - curr[offset + len - i - 1]) * + cos[cosOffset + i]; + } + offset += len; + } + std::swap(curr, next); + cosOffset += halfLen; + len = halfLen; + halfLen /= 2; + } + + // Bottom-up form the final DCT solution + // Note that the case len = 2 will do nothing, so we start from len = 4 + len = 4; + halfLen = 2; + while (halfLen < N) { + TIndex offset = 0; + TIndex steps = N / len; + for (TIndex k = 0; k < steps; ++k) { + for (TIndex i = 0; i < halfLen - 1; ++i) { + next[offset + i * 2] = curr[offset + i]; + next[offset + i * 2 + 1] = + curr[offset + halfLen + i] + curr[offset + halfLen + i + 1]; + } + next[offset + len - 2] = curr[offset + halfLen - 1]; + next[offset + len - 1] = curr[offset + len - 1]; + offset += len; + } + std::swap(curr, next); + halfLen = len; + len *= 2; + } + + // Populate the final results into 'out' + if (curr != out) { + std::copy(curr, curr + N, out); + } +} + +/// Compute y[k] = 0.5 * x[0] + sum_n=1..N-1 (x[n] * cos(n * (k + 0.5) * PI / +/// N)), for k = 0..N-1 +/// @param vec length N sequence to be transformed +/// @param temp length 2 * N helping buffer +/// @param cos length N - 1, stores cosine values precomputed by function +/// 'precompute_idct_cos' +/// @param N length of vec, must be power of 2 +template +inline void idct(TValue *vec, TValue *out, TValue *buf, const TValue *cos, + TIndex N) { + // The input length must be power of 2 + if (!isPowerOf2(N)) { + throw std::domain_error("Input length is not power of 2."); + } + + // Pointers point to the beginning indices of two adjacent iterations + TValue *curr = out; + TValue *next = buf; + + // This array is used to store date of two adjacent iterations + // Copy 'vec' to the first N element in 'temp' + std::copy(vec, vec + N, curr); + curr[0] /= 2; + + // Current bufferfly length and half length + TIndex len = N; + TIndex halfLen = len / 2; + + // Iteratively bi-partition sequences into sub-sequences + while (halfLen) { + TIndex offset = 0; + TIndex steps = N / len; + for (TIndex k = 0; k < steps; ++k) { + next[offset] = curr[offset]; + next[offset + halfLen] = curr[offset + 1]; + for (TIndex i = 1; i < halfLen; ++i) { + next[offset + i] = curr[offset + i * 2]; + next[offset + halfLen + i] = + curr[offset + i * 2 - 1] + curr[offset + i * 2 + 1]; + } + offset += len; + } + std::swap(curr, next); + len = halfLen; + halfLen /= 2; + } + + // Bottom-up form the final IDCT solution + len = 2; + halfLen = 1; + TIndex cosOffset = 0; + while (halfLen < N) { + TIndex offset = 0; + TIndex steps = N / len; + for (TIndex k = 0; k < steps; ++k) { + for (TIndex i = 0; i < halfLen; ++i) { + TValue g = curr[offset + i]; + TValue h = curr[offset + halfLen + i] * cos[cosOffset + i]; + next[offset + i] = g + h; + next[offset + len - 1 - i] = g - h; + } + offset += len; + } + std::swap(curr, next); + cosOffset += halfLen; + halfLen = len; + len *= 2; + } + + // Populate the final results into 'out' + if (curr != out) { + std::copy(curr, curr + N, out); + } +} + +/// Compute batch dct +/// @param mtx size M * N row-major matrix to be transformed +/// @param temp length 3 * M * N helping buffer, first 2 * M * N is for dct, +/// the last M * N is for matrix transpose +/// @param cosM length M - 1, stores cosine values precomputed by function +/// 'precompute_dct_cos' for M-point dct +/// @param cosN length N - 1, stores cosine values precomputed by function +/// 'precompute_dct_cos' for N-point dct +/// @param M number of rows +/// @param N number of columns +template +inline void dct(TValue *mtx, TValue *out, TValue *buf, const TValue *cos, + TIndex M, TIndex N, int num_threads) { +#pragma omp parallel for num_threads(num_threads) schedule(static) + for (TIndex i = 0; i < M; ++i) { + dct(mtx + i * N, out + i * N, buf + i * N, cos, N); + } +} + +/// Compute batch idct +/// @param mtx size M * N row-major matrix to be transformed +/// @param temp length 3 * M * N helping buffer, first 2 * M * N is for dct, +/// the last M * N is for matrix transpose +/// @param cosM length M - 1, stores cosine values precomputed by function +/// 'precompute_dct_cos' for M-point dct +/// @param cosN length N - 1, stores cosine values precomputed by function +/// 'precompute_dct_cos' for N-point dct +/// @param M number of rows +/// @param N number of columns +template +inline void idct(TValue *mtx, TValue *out, TValue *buf, const TValue *cos, + TIndex M, TIndex N, int num_threads) { +#pragma omp parallel for num_threads(num_threads) schedule(static) + for (TIndex i = 0; i < M; ++i) { + idct(mtx + i * N, out + i * N, buf + i * N, cos, N); + } +} + +} // End of namespace lee + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp new file mode 100644 index 00000000..4081115b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp @@ -0,0 +1,330 @@ +/** + * @file dct_lee_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +#include "dct_cuda.h" +#include "dct_lee_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +void dct_lee_precompute_dct_cos(int N, at::Tensor out) { + out.resize_(N); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + out, "dct_lee_precompute_dct_cos", [&] { + lee::precompute_dct_cos( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); + }); +} + +void dct_lee_precompute_idct_cos(int N, at::Tensor out) { + out.resize_(N); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + out, "dct_lee_precompute_idct_cos", [&] { + lee::precompute_idct_cos( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); + }); +} + +inline void dct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos); + CHECK_CONTIGUOUS(cos); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_lee_forward", [&] { + lee::dct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N); + }); + + out.mul_(2.0 / N); +} + +inline void idct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos); + CHECK_CONTIGUOUS(cos); + + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_lee_forward", [&] { + lee::idct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N); + }); + + out.mul_(2); +} + +void dst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, + at::Tensor out) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + buf.copy_(x); + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N); + + dct_lee_forward(buf, expk, buf, out); + // std::cout << "y\n" << y << "\n"; + + computeFlipCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); + }); + + out.copy_(buf); +} + +void idst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, + at::Tensor out) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + computeFlipCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); + + idct_lee_forward(buf, expk, buf, out); + // std::cout << "y\n" << y << "\n"; + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + // std::cout << "z\n" << y << "\n"; + }); +} + +void dct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf, at::Tensor out) { + // CHECK_CUDA(x); + // CHECK_CONTIGUOUS(x); + // CHECK_CUDA(cos0); + // CHECK_CONTIGUOUS(cos0); + // CHECK_CUDA(cos1); + // CHECK_CONTIGUOUS(cos1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + + out.resize_({M, N}); + buf.resize_({M, N}); + + dct_lee_forward(x, cos1, out, buf); + + // 1D DCT to rows + out.resize_({N, M}); + out.copy_(buf.transpose(-2, -1)); + buf.resize_({N, M}); + + dct_lee_forward(out, cos0, out, buf); + + out.resize_({M, N}); + out.copy_(buf.transpose(-2, -1)); +} + +void idct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf, at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CUDA(cos1); + CHECK_CONTIGUOUS(cos1); + + // 1D DCT to columns + + // std::cout << "x\n" << x << "\n"; + auto N = x.size(-1); + auto M = x.numel() / N; + + out.resize_({M, N}); + buf.resize_({M, N}); + + idct_lee_forward(x, cos1, out, buf); + + // 1D DCT to rows + out.resize_({N, M}); + out.copy_(buf.transpose(-2, -1)); + buf.resize_({N, M}); + + idct_lee_forward(out, cos0, out, buf); + + out.resize_({M, N}); + out.copy_(buf.transpose(-2, -1)); +} + +void idxct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_lee_forward", [&] { + idct_lee_forward(x, cos, buf, out); + + // std::cout << __func__ << " z\n" << z << "\n"; + + addX0AndScaleCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)); + }); +} + +void idxst_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, + at::Tensor out) { + auto N = x.size(-1); + auto M = x.numel() / N; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_lee_forward", [&] { + // std::cout << "x\n" << x << "\n"; + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); + + idct_lee_forward(buf, cos, buf, out); + out.mul_(0.5); + // std::cout << "y\n" << y << "\n"; + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); + // std::cout << "z\n" << y << "\n"; + }); +} + +void idcct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CUDA(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxct for rows + + idxct_lee_forward(x, cos1, buf0, out); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxct_lee_forward(buf0, cos0, out, buf1); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +void idcst2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CUDA(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxst for rows + + idxst_lee_forward(x, cos1, buf0, out); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxct_lee_forward(buf0, cos0, out, buf1); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +void idsct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, + at::Tensor buf0, at::Tensor buf1, at::Tensor out) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(cos0); + CHECK_CONTIGUOUS(cos0); + CHECK_CUDA(cos1); + CHECK_CONTIGUOUS(cos1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // two buffers are required to keep make sure no additional allocation of + // memory + + // idxst for rows + + idxct_lee_forward(x, cos1, buf0, out); + + // idxct for columns + + buf0.resize_({N, M}); + buf0.copy_(out.transpose(-2, -1)); + buf1.resize_({N, M}); + out.resize_({N, M}); + + idxst_lee_forward(buf0, cos0, out, buf1); + + out.resize_({M, N}); + out.copy_(buf1.transpose(-2, -1)); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("precompute_dct_cos", &DREAMPLACE_NAMESPACE::dct_lee_precompute_dct_cos, + "Precompute DCT cosine"); + m.def("precompute_idct_cos", + &DREAMPLACE_NAMESPACE::dct_lee_precompute_idct_cos, + "Precompute IDCT cosine"); + m.def("dct", &DREAMPLACE_NAMESPACE::dct_lee_forward, "DCT forward"); + m.def("idct", &DREAMPLACE_NAMESPACE::idct_lee_forward, "IDCT forward"); + m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_lee_forward, "IDXCT forward"); + m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_lee_forward, "IDXST forward"); + + m.def("dst", &DREAMPLACE_NAMESPACE::dst_lee_forward, "DST forward"); + m.def("idst", &DREAMPLACE_NAMESPACE::idst_lee_forward, "IDST forward"); + + m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_lee_forward, "DCT2 forward"); + m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_lee_forward, "IDCT2 forward"); + m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_lee_forward, "IDCCT2 forward"); + m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_lee_forward, "IDCST2 forward"); + m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_lee_forward, "IDSCT2 forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h new file mode 100644 index 00000000..c3f80d25 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h @@ -0,0 +1,32 @@ +/** + * @file dct_lee_cuda.h + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +#ifndef DREAMPLACE_DCT_LEE_CUDA_H +#define DREAMPLACE_DCT_LEE_CUDA_H + +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +namespace lee { + +template +void precompute_dct_cos(TValue *cos, int N); + +template +void precompute_idct_cos(TValue *cos, int N); + +template +void dct(const TValue *vec, TValue *curr, TValue* next, const TValue *cos, int M, int N); + +template +void idct(const TValue *vec, TValue *curr, TValue *next, const TValue *cos, int M, int N); + +} // End of namespace lee + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu new file mode 100644 index 00000000..4996e9de --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu @@ -0,0 +1,294 @@ +/** + * @file dct_lee_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +//#include +//#include +#include +#include +#include +#include +#include "cuda_runtime.h" + +//#include "dct_lee_cuda.h" +#include "dct_lee_cuda_kernel.h" + +DREAMPLACE_BEGIN_NAMESPACE + +namespace lee +{ + +constexpr double PI = 3.14159265358979323846; + +/// Return true if a number is power of 2 +template +inline bool isPowerOf2(T val) +{ + return val && (val & (val - 1)) == 0; +} + +template +inline void swap(T& x, T& y) +{ + T tmp = x; + x = y; + y = tmp; +} + +/// Precompute cosine values needed for N-point dct +/// @param cos size N - 1 buffer on GPU, contains the result after function call +/// @param N the length of target dct, must be power of 2 +template +void precompute_dct_cos(TValue *cos, int N) +{ + // The input length must be power of 2 + if (! isPowerOf2(N)) + { + printf("Input length is not power of 2.\n"); + assert(0); + } + + // create the array on host + TValue* cos_host = new TValue [N]; + + int offset = 0; + int halfLen = N / 2; + while (halfLen) + { + TValue phaseStep = 0.5 * PI / halfLen; + TValue phase = 0.5 * phaseStep; + for (int i = 0; i < halfLen; ++i) + { + cos_host[offset + i] = 0.5 / std::cos(phase); + phase += phaseStep; + } + offset += halfLen; + halfLen /= 2; + } + + // copy to GPU + cudaMemcpy(cos, cos_host, N*sizeof(TValue), cudaMemcpyHostToDevice); + + delete [] cos_host; +} + +/// Precompute cosine values needed for N-point idct +/// @param cos size N - 1 buffer on GPU, contains the result after function call +/// @param N the length of target idct, must be power of 2 +template +void precompute_idct_cos(TValue *cos, int N) +{ + // The input length must be power of 2 + if (! isPowerOf2(N)) + { + printf("Input length is not power of 2.\n"); + assert(0); + } + + // create the array on host + TValue* cos_host = new TValue [N]; + + int offset = 0; + int halfLen = 1; + while(halfLen < N) + { + TValue phaseStep = 0.5 * PI / halfLen; + TValue phase = 0.5 * phaseStep; + for (int i = 0; i < halfLen; ++i) + { + cos_host[offset + i] = 0.5 / std::cos(phase); + phase += phaseStep; + } + offset += halfLen; + halfLen *= 2; + } + + // copy to GPU + cudaMemcpy(cos, cos_host, N*sizeof(TValue), cudaMemcpyHostToDevice); + + delete [] cos_host; +} + +/// The implementation of fast Discrete Cosine Transform (DCT) algorithm and its inverse (IDCT) are Lee's algorithms +/// Algorithm reference: A New Algorithm to Compute the Discrete Cosine Transform, by Byeong Gi Lee, 1984 +/// +/// Lee's algorithm has a recursive structure in nature. +/// Here is a sample recursive implementation: https://www.nayuki.io/page/fast-discrete-cosine-transform-algorithms +/// +/// My implementation here is iterative, which is more efficient than the recursive version. +/// Here is a sample iterative implementation: https://www.codeproject.com/Articles/151043/Iterative-Fast-1D-Forvard-DCT + +/// Compute y[k] = sum_n=0..N-1 (x[n] * cos((n + 0.5) * k * PI / N)), for k = 0..N-1 +/// +/// @param vec length M * N sequence to be transformed in last dimension +/// @param out length M * N helping buffer, which is also the output +/// @param buf length M * N helping buffer +/// @param cos length N - 1, stores cosine values precomputed by function 'precompute_dct_cos' +/// @param M length of dimension 0 of vec +/// @param N length of dimension 1 of vec, must be power of 2 +template +void dct(const TValue *vec, TValue *out, TValue* buf, const TValue *cos, int M, int N) +{ + int block_count = 2048; + int thread_count = 512; + + // The input length must be power of 2 + if (! isPowerOf2(N)) + { + printf("Input length is not power of 2.\n"); + assert(0); + } + + // Pointers point to the beginning indices of two adjacent iterations + TValue *curr = buf; + TValue *next = out; + + // 'temp' used to store date of two adjacent iterations + // Copy 'vec' to the first N element in 'temp' + cudaMemcpy(curr, vec, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); + + // Current bufferfly length and half length + int len = N; + int halfLen = len / 2; + + // Iteratively bi-partition sequences into sub-sequences + int cosOffset = 0; + while (halfLen) + { + computeDctForward<<>>(curr, next, cos, M, N, len, halfLen, cosOffset); + swap(curr, next); + cosOffset += halfLen; + len = halfLen; + halfLen /= 2; + } + + // Bottom-up form the final DCT solution + // Note that the case len = 2 will do nothing, so we start from len = 4 + len = 4; + halfLen = 2; + while (halfLen < N) + { + computeDctBackward<<>>(curr, next, M, N, len, halfLen); + swap(curr, next); + halfLen = len; + len *= 2; + } + + // Populate the final results into 'out' + if (curr != out) + { + cudaMemcpy(out, curr, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); + } +} + +/// Compute y[k] = 0.5 * x[0] + sum_n=1..N-1 (x[n] * cos(n * (k + 0.5) * PI / N)), for k = 0..N-1 +/// @param vec length M * N sequence to be transformed +/// @param out length M * N helping buffer, which is also the output +/// @param buf length M * N helping buffer +/// @param cos length N - 1, stores cosine values precomputed by function 'precompute_idct_cos' +/// @param M length of dimension 0 of vec +/// @param N length of dimension 1 of vec, must be power of 2 +template +void idct(const TValue *vec, TValue *out, TValue *buf, const TValue *cos, int M, int N) +{ + int block_count = 32; + int thread_count = 1024; + + // The input length must be power of 2 + if (! isPowerOf2(N)) + { + printf("Input length is not power of 2.\n"); + assert(0); + } + + // Pointers point to the beginning indices of two adjacent iterations + TValue *curr = buf; + TValue *next = out; + + // This array is used to store date of two adjacent iterations + // Copy 'vec' to the first N element in 'temp' + cudaMemcpy(curr, vec, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); + computeIdctScale0<<>>(curr, M, N); + + // Current bufferfly length and half length + int len = N; + int halfLen = len / 2; + + // Iteratively bi-partition sequences into sub-sequences + while (halfLen) + { + computeIdctForward<<>>(curr, next, M, N, len, halfLen); + swap(curr, next); + len = halfLen; + halfLen /= 2; + } + + // Bottom-up form the final IDCT solution + len = 2; + halfLen = 1; + int cosOffset = 0; + while(halfLen < N) + { + ComputeIdctBackward<<>>(curr, next, cos, M, N, len, halfLen, cosOffset); + swap(curr, next); + cosOffset += halfLen; + halfLen = len; + len *= 2; + } + + // Populate the final results into 'out' + if (curr != out) + { + cudaMemcpy(out, curr, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); + } +} + +} // End of namespace lee + +#define REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(type) \ + template void lee::precompute_dct_cos(\ + type* cos, \ + int N \ + ); + +REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(float); +REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(double); + +#define REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(type) \ + template void lee::precompute_idct_cos(\ + type* cos, \ + int N \ + ); + +REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(float); +REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(double); + +#define REGISTER_DCT_KERNEL_LAUNCHER(type) \ + template void lee::dct(\ + const type* vec, \ + type* curr, \ + type* next, \ + const type* cos, \ + int M, \ + int N \ + ); + +REGISTER_DCT_KERNEL_LAUNCHER(float); +REGISTER_DCT_KERNEL_LAUNCHER(double); + +#define REGISTER_IDCT_KERNEL_LAUNCHER(type) \ + template void lee::idct(\ + const type* vec, \ + type* curr, \ + type* next, \ + const type* cos, \ + int M, \ + int N \ + ); + +REGISTER_IDCT_KERNEL_LAUNCHER(float); +REGISTER_IDCT_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h new file mode 100644 index 00000000..f218d5b9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h @@ -0,0 +1,169 @@ +/** + * @file dct_lee_cuda_kernel.h + * @author Yibo Lin (DREAMPlace) + * @date Oct 2018 + */ + +#ifndef DREAMPLACE_DCT_LEE_CUDA_KERNEL_H +#define DREAMPLACE_DCT_LEE_CUDA_KERNEL_H + +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +namespace lee { + +template +__global__ void computeDctForward(const TValue* curr, TValue* next, + const TValue* cos, TIndex M, TIndex N, + TIndex len, TIndex halfLen, + TIndex cosOffset) { + // for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < + // M*N/2; thread_id += blockDim.x * gridDim.x) + //{ + // TIndex halfN = N/2; + // TIndex batch_id = thread_id / halfN; + // TIndex rest = thread_id - batch_id*halfN; + // TIndex k = rest / halfLen; + // TIndex i = rest - k*halfLen; + // TIndex batch_offset = batch_id*N; + // TIndex offset = batch_offset + k*len; + + // next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; + // next[offset + halfLen + i] = (curr[offset + i] - curr[offset + len -i - + // 1]) * cos[cosOffset + i]; + //} + TIndex halfN = (N >> 1); + TIndex halfMN = M * halfN; + // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; + // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; + thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { + TIndex rest = thread_id & (halfN - 1); + TIndex i = rest & (halfLen - 1); + TIndex offset = (thread_id - i) * 2; + + next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; + // next[offset + i + halfLen] = (curr[offset + i] - curr[offset + len - i - + // 1]) * cos[cosOffset + i]; + } + // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; + // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; + thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { + TIndex rest = thread_id & (halfN - 1); + TIndex i = rest & (halfLen - 1); + TIndex offset = (thread_id - i) * 2; + + // next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; + next[offset + i + halfLen] = + (curr[offset + i] - curr[offset + len - i - 1]) * cos[cosOffset + i]; + } +} + +template +__global__ void computeDctBackward(const TValue* curr, TValue* next, TIndex M, + TIndex N, TIndex len, TIndex halfLen) { + // for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < + // M*N/2; thread_id += blockDim.x * gridDim.x) + //{ + // TIndex halfN = N/2; + // TIndex batch_id = thread_id / halfN; + // TIndex rest = thread_id - batch_id*halfN; + // TIndex k = rest / halfLen; + // TIndex i = rest - k*halfLen; + // TIndex batch_offset = batch_id*N; + // TIndex offset = batch_offset + k*len; + + // if (i+1 == halfLen) + // { + // next[offset + len - 2] = curr[offset + halfLen - 1]; + // next[offset + len - 1] = curr[offset + len - 1]; + // } + // else + // { + // next[offset + i * 2] = curr[offset + i]; + // next[offset + i * 2 + 1] = curr[offset + halfLen + i] + curr[offset + // + halfLen + i + 1]; + // } + // //next[offset + i] = (i&1)? curr[offset + halfLen + i/2] + curr[offset + + // halfLen + i/2 + 1*(i+1 < len)]*(i+1 < len) : curr[offset + i/2]; + //} + TIndex halfN = (N >> 1); + TIndex halfMN = M * halfN; + // TIndex halfMN_by_gridDim = halfMN/gridDim.x; + // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; + // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; + thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { + TIndex rest = thread_id & (halfN - 1); + TIndex i = rest & (halfLen - 1); + TIndex offset = (thread_id - i) * 2; + + next[offset + i * 2] = curr[offset + i]; + next[offset + i * 2 + 1] = + (i + 1 == halfLen) + ? curr[offset + len - 1] + : curr[offset + halfLen + i] + curr[offset + halfLen + i + 1]; + } +} + +template +__global__ void computeIdctScale0(TValue* curr, TIndex M, TIndex N) { + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < M; + thread_id += blockDim.x * gridDim.x) { + curr[thread_id * N] *= 0.5; + } +} + +template +__global__ void computeIdctForward(const TValue* curr, TValue* next, TIndex M, + TIndex N, TIndex len, TIndex halfLen) { + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; + thread_id < M * N / 2; thread_id += blockDim.x * gridDim.x) { + TIndex halfN = N / 2; + TIndex batch_id = thread_id / halfN; + TIndex rest = thread_id - batch_id * halfN; + TIndex k = rest / halfLen; + TIndex i = rest - k * halfLen; + TIndex batch_offset = batch_id * N; + TIndex offset = batch_offset + k * len; + + if (i == 0) { + next[offset] = curr[offset]; + next[offset + halfLen] = curr[offset + 1]; + } else { + next[offset + i] = curr[offset + i * 2]; + next[offset + halfLen + i] = + curr[offset + i * 2 - 1] + curr[offset + i * 2 + 1]; + } + } +} + +template +__global__ void ComputeIdctBackward(const TValue* curr, TValue* next, + const TValue* cos, TIndex M, TIndex N, + TIndex len, TIndex halfLen, + TIndex cosOffset) { + for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; + thread_id < M * N / 2; thread_id += blockDim.x * gridDim.x) { + TIndex halfN = N / 2; + TIndex batch_id = thread_id / halfN; + TIndex rest = thread_id - batch_id * halfN; + TIndex k = rest / halfLen; + TIndex i = rest - k * halfLen; + TIndex batch_offset = batch_id * N; + TIndex offset = batch_offset + k * len; + + TValue g = curr[offset + i]; + TValue h = curr[offset + halfLen + i] * cos[cosOffset + i]; + next[offset + i] = g + h; + next[offset + len - 1 - i] = g - h; + } +} + +} // End of namespace lee + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp new file mode 100644 index 00000000..2ed7a0a1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp @@ -0,0 +1,83 @@ +/** + * @file dst.cpp + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#include "dct.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dst_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ) +{ + auto N = x.size(-1); + auto M = x.numel()/N; + + //std::cout << "x\n" << x << "\n"; + auto x_reorder = x.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_forward", [&] { + negateOddEntries( + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + M, + N, + num_threads + ); + + auto y = dct_forward(x_reorder, expk, num_threads); + //std::cout << "y\n" << y << "\n"; + + computeFlip( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, + N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads + ); + //std::cout << "z\n" << y << "\n"; + }); + + return x_reorder; +} + +at::Tensor idst_forward( + at::Tensor x, + at::Tensor expk, + int num_threads + ) +{ + auto N = x.size(-1); + auto M = x.numel()/N; + + //std::cout << "x\n" << x << "\n"; + auto x_reorder = at::empty_like(x); + auto y = at::empty_like(x); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_forward", [&] { + computeFlip( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + M, + N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + num_threads + ); + + y = idct_forward(x_reorder, expk, num_threads); + //std::cout << "y\n" << y << "\n"; + + negateOddEntries( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, + N, + num_threads + ); + //std::cout << "z\n" << y << "\n"; + }); + + return y; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp new file mode 100644 index 00000000..80a3c195 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp @@ -0,0 +1,75 @@ +/** + * @file dst_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#include "dct_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor dst_forward( + at::Tensor x, + at::Tensor expk) +{ + auto N = x.size(-1); + auto M = x.numel()/N; + + //std::cout << "x\n" << x << "\n"; + auto x_reorder = x.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_forward", [&] { + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), + M, + N + ); + + auto y = dct_forward(x_reorder, expk); + //std::cout << "y\n" << y << "\n"; + + computeFlipCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, + N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t) + ); + //std::cout << "z\n" << y << "\n"; + }); + + return x_reorder; +} + +at::Tensor idst_forward( + at::Tensor x, + at::Tensor expk) +{ + auto N = x.size(-1); + auto M = x.numel()/N; + + //std::cout << "x\n" << x << "\n"; + auto x_reorder = at::empty_like(x); + auto y = at::empty_like(x); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_forward", [&] { + computeFlipCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + M, + N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t) + ); + + y = idct_forward(x_reorder, expk); + //std::cout << "y\n" << y << "\n"; + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, + N + ); + //std::cout << "z\n" << y << "\n"; + }); + + return y; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu new file mode 100644 index 00000000..b6ef72ee --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu @@ -0,0 +1,131 @@ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + + +template +__global__ void computeFlip( + const T* x, + const int M, + const int N, + T* y + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int ii = i%N; + y[i] = x[i+N-ii*2-1]; + } +} + +template +void computeFlipCudaLauncher( + const T* x, + const int M, + const int N, + T* y + ) +{ + computeFlip<<<32, 1024>>>( + x, + M, + N, + y + ); +} + +template +__global__ void computeFlipAndShift( + const T* x, + const int M, + const int N, + T* y + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) + { + int ii = i%N; + y[i] = (ii)? x[i+N-ii*2] : 0; + } +} + +template +void computeFlipAndShiftCudaLauncher( + const T* x, + const int M, + const int N, + T* y + ) +{ + computeFlipAndShift<<<32, 1024>>>( + x, + M, + N, + y + ); +} + +template +__global__ void negateOddEntries( + T* x, + const int M, + const int N + ) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*(N>>1); i += blockDim.x * gridDim.x) + { + x[i*2+1] = -x[i*2+1]; + } +} + +template +void negateOddEntriesCudaLauncher( + T* x, + const int M, + const int N + ) +{ + negateOddEntries<<<32, 1024>>>( + x, + M, + N + ); +} + +#define REGISTER_FLIP_KERNEL_LAUNCHER(type) \ + template void computeFlipCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* y \ + ); + +REGISTER_FLIP_KERNEL_LAUNCHER(float); +REGISTER_FLIP_KERNEL_LAUNCHER(double); + +#define REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(type) \ + template void computeFlipAndShiftCudaLauncher(\ + const type* x, \ + const int M, \ + const int N, \ + type* y \ + ); + +REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(float); +REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(double); + +#define REGISTER_NEGATE_KERNEL_LAUNCHER(type) \ + template void negateOddEntriesCudaLauncher(\ + type* x, \ + const int M, \ + const int N \ + ); + +REGISTER_NEGATE_KERNEL_LAUNCHER(float); +REGISTER_NEGATE_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp new file mode 100644 index 00000000..413bbef4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp @@ -0,0 +1,448 @@ +/** + * @file dxt.cpp + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#include "dct.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor idxct_forward(at::Tensor x, at::Tensor expk, int num_threads) { + auto N = x.size(-1); + auto M = x.numel() / N; + + auto z = idct_forward(x, expk, num_threads); + + // std::cout << __func__ << " z\n" << z << "\n"; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_forward", [&] { + addX0AndScale(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + }); + + return z; +} + +at::Tensor idxst_forward(at::Tensor x, at::Tensor expk, int num_threads) { + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // auto x_reorder = at::empty_like(x); + auto x_reorder = at::empty({M, N}, x.options()); + auto y = at::empty_like(x); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_forward", [&] { + computeFlipAndShift( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), num_threads); + + y = idct_forward(x_reorder, expk, num_threads); + y.mul_(0.5); + // std::cout << "y\n" << y << "\n"; + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + num_threads); + // std::cout << "z\n" << y << "\n"; + }); + + return y; +} + +at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcct2_forward", [&] { + computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + v.resize_({M, N}); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), + num_threads); + + // std::cout << "z\n" << z << "\n"; + // idxct for columns + + auto xt = v.transpose(-2, -1).contiguous(); + + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << __func__ << " v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << __func__ << " y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + v.resize_({N, M}); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + // std::cout << __func__ << " z\n" << z << "\n"; + + addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), + num_threads); + + v.transpose_(-2, -1); + }); + + return v.contiguous(); +} + +at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = + at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { + computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxst for columns + + auto xt = z.transpose(-2, -1).contiguous(); + // std::cout << "x\n" << x << "\n"; + z = z.view_as(xt); + computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, + M, DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + // this is to match python implementation + // normal way should be multiply by 0.25*N + z.mul_(0.25 * M); + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M, + num_threads); + // std::cout << "z\n" << y << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxst for rows + // std::cout << "x\n" << x << "\n"; + // auto z = at::empty_like(x); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { + computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + // this is to match python implementation + // normal way should be multiply by 0.25*N + z.mul_(0.25 * N); + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N, + num_threads); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxct for columns + + auto xt = z.transpose(-2, -1).contiguous(); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + z = z.view_as(xt); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = + at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { + computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + // idxst for columns + + auto xt = z.transpose(-2, -1).contiguous(); + // std::cout << "x\n" << x << "\n"; + z = z.view_as(xt); + computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, + M, DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + // normalized to match dct2_fft2 implementation + z.mul_(0.25 * M * N); + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M, + num_threads); + // std::cout << "z\n" << y << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, + int num_threads) { + CHECK_CPU(x); + CHECK_CONTIGUOUS(x); + CHECK_CPU(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CPU(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxst for rows + // std::cout << "x\n" << x << "\n"; + // auto z = at::empty_like(x); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { + computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + num_threads); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + // normalized to match dct2_fft2 implementation + z.mul_(0.25 * N * M); + + negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N, + num_threads); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxct for columns + + auto xt = z.transpose(-2, -1).contiguous(); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + z = z.view_as(xt); + computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); + // std::cout << "z\n" << z << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp new file mode 100644 index 00000000..08736c7e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp @@ -0,0 +1,465 @@ +/** + * @file dxt_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Sep 2018 + */ +#include "dct_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +at::Tensor idxct_forward(at::Tensor x, at::Tensor expk) { + auto N = x.size(-1); + auto M = x.numel() / N; + + auto z = idct_forward(x, expk); + + // std::cout << "z\n" << z << "\n"; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_forward", [&] { + addX0AndScaleCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + }); + + return z; +} + +at::Tensor idxst_forward(at::Tensor x, at::Tensor expk) { + auto N = x.size(-1); + auto M = x.numel() / N; + + // std::cout << "x\n" << x << "\n"; + // auto x_reorder = at::empty_like(x); + auto x_reorder = at::empty({M, N}, x.options()); + auto y = at::empty_like(x); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_forward", [&] { + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); + + y = idct_forward(x_reorder, expk); + y.mul_(0.5); + // std::cout << "y\n" << y << "\n"; + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N); + // std::cout << "z\n" << y << "\n"; + }); + + return y; +} + +at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcct2_forward", [&] { + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + v.resize_({M, N}); + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + addX0AndScaleNCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "z\n" << z << "\n"; + // idxct for columns + + auto xt = v.transpose(-2, -1).contiguous(); + + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << __func__ << " v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << __func__ << " y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + v.resize_({N, M}); + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + // std::cout << __func__ << " z\n" << z << "\n"; + + addX0AndScaleNCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + v.transpose_(-2, -1); + }); + + return v.contiguous(); +} + +at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + addX0AndScaleNCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxst for columns + + auto xt = z.transpose(-2, -1).contiguous(); + // std::cout << "x\n" << x << "\n"; + z = z.view_as(xt); + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + // this is to match python implementation + // normal way should be multiply by 0.25*N + z.mul_(0.25 * M); + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M); + // std::cout << "z\n" << y << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxst for rows + // std::cout << "x\n" << x << "\n"; + // auto z = at::empty_like(x); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + // this is to match python implementation + // normal way should be multiply by 0.25*N + z.mul_(0.25 * N); + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxct for columns + + auto xt = z.transpose(-2, -1).contiguous(); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + z = z.view_as(xt); + computeReorderReverseCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + addX0AndScaleNCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, + at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxct for rows + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk is hermitian symmetric, only fill in half + auto v = + at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + // y is real now + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + // auto z = at::empty_like(x); + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + // idxst for columns + + auto xt = z.transpose(-2, -1).contiguous(); + // std::cout << "x\n" << x << "\n"; + z = z.view_as(xt); + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + // normalized to match dct2_fft2 implementation + z.mul_(0.25 * M * N); + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M); + // std::cout << "z\n" << y << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, + at::Tensor expk1) { + CHECK_CUDA(x); + CHECK_CONTIGUOUS(x); + CHECK_CUDA(expk0); + CHECK_CONTIGUOUS(expk0); + CHECK_CUDA(expk1); + CHECK_CONTIGUOUS(expk1); + + auto N = x.size(-1); + auto M = x.numel() / N; + + // idxst for rows + // std::cout << "x\n" << x << "\n"; + // auto z = at::empty_like(x); + auto z = at::empty({M, N}, x.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { + computeFlipAndShiftCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + auto v = at::empty({M * N + std::max(M, N)}, x.options()) + .resize_({M, N / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + auto y = at::irfft(v, 1, false, true, {N}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + M, N, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + // normalized to match dct2_fft2 implementation + z.mul_(0.25 * N * M); + + negateOddEntriesCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N); + // std::cout << __func__ << " z\n" << z << "\n"; + + // idxct for columns + + auto xt = z.transpose(-2, -1).contiguous(); + + // std::cout << "x\n" << x << "\n"; + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) + v.resize_({N, M / 2 + 1, 2}); + computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); + + // std::cout << "v\n" << v << "\n"; + + y = at::irfft(v, 1, false, true, {M}); + + // std::cout << "y\n" << y << "\n"; + + // std::cout << "expk\n" << expk << "\n"; + z = z.view_as(xt); + computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), + N, M, + DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); + // std::cout << "z\n" << z << "\n"; + + z.transpose_(-2, -1); + }); + + return z.contiguous(); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py new file mode 100644 index 00000000..4d4bf5a0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py @@ -0,0 +1,148 @@ +#* """ +#* Author: Yibo Lin (DREAMPlace) +#* Date: June 15, 2021 +#* """ + +import torch +from distutils.version import LooseVersion, StrictVersion + +def rfft(input, signal_ndim, normalized=False, onesided=True): + if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): + return torch.rfft(input, signal_ndim, normalized, onesided) + else: + if onesided: + if normalized: + if signal_ndim == 1: + y = torch.fft.rfft(input, None, -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.rfft2(input, None, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.rfftn(input, None, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized rfft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.rfft(input, None, -1, "backward") + elif signal_ndim == 2: + y = torch.fft.rfft2(input, None, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.rfftn(input, None, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized rfft() has illegal number of dimensions %s" % (signal_ndim) + else: + if normalized: + if signal_ndim == 1: + y = torch.fft.fft(input, None, -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.fft2(input, None, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.fftn(input, None, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized fft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.fft(input, None, -1, "backward") + elif signal_ndim == 2: + y = torch.fft.fft2(input, None, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.fftn(input, None, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized fft() has illegal number of dimensions %s" % (signal_ndim) + + return torch.view_as_real(y).contiguous() + +def irfft(input, signal_ndim, normalized=False, onesided=True, signal_sizes=None): + if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): + return torch.irfft(input, signal_ndim, normalized, onesided, signal_sizes) + else: + assert signal_sizes, "Parameter signal_sizes is required" + if onesided: + if normalized: + if signal_ndim == 1: + y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized irfft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "backward") + elif signal_ndim == 2: + y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized irfft() has illegal number of dimensions %s" % (signal_ndim) + else: + if normalized: + if signal_ndim == 1: + y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "backward") + elif signal_ndim == 2: + y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) + assert not y.is_complex() + return y.contiguous() + +def fft(input, signal_ndim, normalized=False): + if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): + return torch.fft(input, signal_ndim, normalized) + else: + if normalized: + if signal_ndim == 1: + y = torch.fft.fft(torch.view_as_complex(input), None, -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.fft2(torch.view_as_complex(input), None, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.fftn(torch.view_as_complex(input), None, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized fft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.fft(torch.view_as_complex(input), None, -1, "backward") + elif signal_ndim == 2: + y = torch.fft.fft2(torch.view_as_complex(input), None, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.fftn(torch.view_as_complex(input), None, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized fft() has illegal number of dimensions %s" % (signal_ndim) + + return torch.view_as_real(y).contiguous() + +def ifft(input, signal_ndim, normalized=False): + if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): + return torch.ifft(input, signal_ndim, normalized) + else: + if normalized: + if signal_ndim == 1: + y = torch.fft.ifft(torch.view_as_complex(input), None, -1, "ortho") + elif signal_ndim == 2: + y = torch.fft.ifft2(torch.view_as_complex(input), None, (-2, -1), "ortho") + elif signal_ndim == 3: + y = torch.fft.ifftn(torch.view_as_complex(input), None, (-3, -2, -1), "ortho") + else: + assert False, "Ortho-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) + else: + if signal_ndim == 1: + y = torch.fft.ifft(torch.view_as_complex(input), None, -1, "backward") + elif signal_ndim == 2: + y = torch.fft.ifft2(torch.view_as_complex(input), None, (-2, -1), "backward") + elif signal_ndim == 3: + y = torch.fft.ifftn(torch.view_as_complex(input), None, (-3, -2, -1), "backward") + else: + assert False, "Backward-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) + + return torch.view_as_real(y).contiguous() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt new file mode 100644 index 00000000..be394aa9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt @@ -0,0 +1,34 @@ +set(OP_NAME demandMap) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(demandMap_cpp + src/demandMap.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + demandMap_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) + +add_pytorch_extension(demandMap_cuda + src/demandMap_cuda.cpp + src/demandMap_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + demandMap_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py new file mode 100644 index 00000000..384de405 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Nov 2020 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py new file mode 100644 index 00000000..e3d1d0a5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py @@ -0,0 +1,105 @@ +## +# @file demandMap.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Nov 2020 +# + +import torch +from torch.autograd import Function +from torch import nn +import numpy as np +import pdb +import time + +import dreamplacefpga.ops.demandMap.demandMap_cpp as demandMap_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.demandMap.demandMap_cuda as demandMap_cuda + +class DemandMap(nn.Module): + """ + @brief Build binCapMap and fixedDemandMap + """ + def __init__(self, placedb, site_type_map, site_size_x, site_size_y, + deterministic_flag, device, num_threads): + """ + @brief initialization + @param placedb + @param site_type_map + @param site_size_x + @param site_size_y + @param deterministic_flag + @param device + @param num_threads + """ + super(DemandMap, self).__init__() + self.num_bins_x=placedb.num_bins_x + self.num_bins_y=placedb.num_bins_y + self.width=placedb.xh - placedb.xl + self.height=placedb.yh - placedb.yl + self.rsrc2compId_map=placedb.rsrc2compId_map + self.comp2rsrcId_map=placedb.comp2rsrcId_map + self.rsrc2siteMap = placedb.rsrc2siteMap + self.rsrcType2IndexMap = placedb.rsrcType2indexMap + self.siteType2IndexMap = placedb.siteType2indexMap + self.node_count=placedb.node_count + self.site_type_map=site_type_map + self.site_size_x=site_size_x + self.site_size_y=site_size_y + self.deterministic_flag = deterministic_flag + self.device=device + self.num_threads = num_threads + + def forward(self): + numSiteTypes = len(self.siteType2IndexMap)+1 + binCapMap = torch.zeros((numSiteTypes, self.num_bins_x, self.num_bins_y), dtype=self.site_size_x.dtype, device=self.device) + + binW = self.width/self.num_bins_x + binH = self.height/self.num_bins_y + + if binCapMap.is_cuda: + demandMap_cuda.forward( + self.site_type_map.flatten(), + self.site_size_x, + self.site_size_y, + self.num_bins_x, + self.num_bins_y, + self.width, + self.height, + binW, binH, + numSiteTypes, + self.num_bins_x*self.num_bins_y, + binCapMap, + self.deterministic_flag) + else: + demandMap_cpp.forward( + self.site_type_map.flatten(), + self.site_size_x, + self.site_size_y, + self.num_bins_x, + self.num_bins_y, + self.width, + self.height, + numSiteTypes, + binCapMap, + self.num_threads, + self.deterministic_flag) + + binArea = binW * binH + binCapMap = binArea - binCapMap + + rsrcDemMap = torch.zeros((len(self.rsrcType2IndexMap),self.num_bins_x,self.num_bins_y), dtype=self.site_size_x.dtype, device=self.device) + + for rsrc, rsrcId in self.rsrcType2IndexMap.items(): + compId = self.rsrc2compId_map[rsrcId] + if compId != -1: + sId = self.siteType2IndexMap[self.rsrc2siteMap[rsrc]] + rsrcDemMap[compId] = binCapMap[sId] + + out = [] + + for idx in self.rsrc2compId_map: + if idx != -1: + out.append(rsrcDemMap[idx]) + + return out diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp new file mode 100644 index 00000000..d98088d4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp @@ -0,0 +1,149 @@ +/** + * @file demandMap.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Nov 2020 + * @brief Compute binCapMap + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "demandMap/src/demand_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define compute_demand_function +template +DEFINE_COMPUTE_DEMAND_FUNCTION(T); + +template +int computeDemandMapLauncher( + const int *site_type_map, + const T *site_size_x, + const T *site_size_y, + const int num_bins_x, + const int num_bins_y, + const int width, + const int height, + const int num_threads, + AtomicOp atomic_add_op, + typename AtomicOp::type* buf_map + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +#define CALL_FPGA_LAUNCHER(atomic_add_op, map_ptr) \ + computeDemandMapLauncher( \ + DREAMPLACE_TENSOR_DATA_PTR(site_type_map, int), \ + DREAMPLACE_TENSOR_DATA_PTR(site_size_x, scalar_t), \ + DREAMPLACE_TENSOR_DATA_PTR(site_size_y, scalar_t), \ + num_bins_x, num_bins_y, width, height, \ + num_threads, atomic_add_op, map_ptr) + +/// @brief Compute wirelength preconditioner +int forward( + at::Tensor site_type_map, + at::Tensor site_size_x, + at::Tensor site_size_y, + int num_bins_x, + int num_bins_y, + int width, + int height, + int num_site_types, + at::Tensor binCapMap, + int num_threads, + int deterministic_flag) +{ + CHECK_FLAT(site_type_map); + CHECK_CONTIGUOUS(site_type_map); + + CHECK_FLAT(site_size_x); + CHECK_CONTIGUOUS(site_size_x); + CHECK_FLAT(site_size_y); + CHECK_CONTIGUOUS(site_size_y); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(site_size_x, "computeDemandMapLauncher", [&] { + if (deterministic_flag == 1) + { + double diearea = width * height; + int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); + long scale_factor = (1L << fraction_bits); + int num_bins = num_site_types * num_bins_x * num_bins_y; + + std::vector buf(num_bins, 0); + AtomicAdd atomic_add_op(scale_factor); + + CALL_FPGA_LAUNCHER(atomic_add_op, buf.data()); + + scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t), + buf.data(), 1.0 / scale_factor, num_bins, num_threads); + } else + { + auto buf = DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t); + AtomicAdd atomic_add_op; + CALL_FPGA_LAUNCHER(atomic_add_op, buf); + } + + }); + return 0; +} + +template +int computeDemandMapLauncher( + const int *site_type_map, + const T *site_size_x, + const T *site_size_y, + const int num_bins_x, + const int num_bins_y, + const int width, + const int height, + const int num_threads, + AtomicOp atomic_add_op, + typename AtomicOp::type* buf_map + ) +{ + int bins_xy = num_bins_x * num_bins_y; + int num_sites = width * height; + T binW = T(width)/T(num_bins_x); + T binH = T(height)/T(num_bins_y); +#pragma omp parallel for num_threads(num_threads) + for (int s = 0; s < num_sites; ++s) + { + int site_type = site_type_map[s]; + int site_typeId = site_type*bins_xy; + int rw = int(s/height); + int cl = int(s%height); + + if (site_type > 0) + { + T nodeX = site_size_x[site_type]; + T nodeY = site_size_y[site_type]; + T col = DREAMPLACE_STD_NAMESPACE::round(cl/nodeY)*nodeY; + int iLo = int(rw/binW); + int jLo = int(col/binH); + int iHi = DREAMPLACE_STD_NAMESPACE::min(int((rw + nodeX)/binW), num_bins_x-1); + int jHi = DREAMPLACE_STD_NAMESPACE::min(int((col + nodeY)/binH), num_bins_y-1); + for (int i = iLo; i <= iHi; ++i) + { + T w = compute_demand_function(i, binW, T(rw), nodeX); + for (int j = jLo; j <= jHi; ++j) + { + T h = compute_demand_function(j, binH, col, nodeY); + T area = w * h; + int index = site_typeId + i*num_bins_y + j; + atomic_add_op(&buf_map[index], area); + } + } + } + } + return 0; +} + +#undef CALL_FPGA_LAUNCHER + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "DemandMap forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp new file mode 100644 index 00000000..c73198f5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp @@ -0,0 +1,71 @@ +/** + * @file demandMap_cuda.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Nov 2020 + * @brief Compute binCapMap + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" +using namespace torch::indexing; + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeDemandMapCudaLauncher( + const int *site_type_map, + const T *site_size_x, + const T *site_size_y, + const T binW, + const T binH, + const int num_site_types, + const int num_bins_x, + const int num_bins_y, + const int width, + const int height, + const int bins_xy, + const int deterministic_flag, + T *binCapMap); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute bin capacity map +void forward( + at::Tensor site_type_map, + at::Tensor site_size_x, + at::Tensor site_size_y, + int num_bins_x, + int num_bins_y, + int width, int height, + double binW, double binH, + int num_site_types, + int bins_xy, + at::Tensor binCapMap, + int deterministic_flag) +{ + CHECK_FLAT(site_type_map); + CHECK_CONTIGUOUS(site_type_map); + + CHECK_FLAT(site_size_x); + CHECK_CONTIGUOUS(site_size_x); + CHECK_FLAT(site_size_y); + CHECK_CONTIGUOUS(site_size_y); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(site_size_x, "computeDemandMapCudaLauncher", [&] { + computeDemandMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(site_type_map, int), + DREAMPLACE_TENSOR_DATA_PTR(site_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_size_y, scalar_t), + binW, binH, num_site_types, num_bins_x, num_bins_y, + width, height, bins_xy, + deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t) + ); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "DemandMap forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu new file mode 100644 index 00000000..2fefe592 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu @@ -0,0 +1,148 @@ +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" +// local dependency +#include "demandMap/src/demand_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define compute_demand_function +template +inline __device__ DEFINE_COMPUTE_DEMAND_FUNCTION(T); + +template +__global__ void __launch_bounds__(1024, 8) computeDemandMap( + const int *site_type_map, const T *site_size_x, const T *site_size_y, + const T binW, const T binH, const int num_bins_x, const int num_bins_y, + const int width, const int height, const int bins_xy, + AtomicOp atomicAddOp, typename AtomicOp::type *binCapMap) +{ + __shared__ int num_sites; + num_sites = width*height; + + int idx = blockIdx.x * blockDim.z + threadIdx.z; + if (idx < num_sites) + { + int site_type = site_type_map[idx]; + int site_typeId = site_type*bins_xy; + int rw = int(idx/height); + int cl = int(idx%height); + + if (site_type > 0) + { + T nodeX = site_size_x[site_type]; + T nodeY = site_size_y[site_type]; + T col = DREAMPLACE_STD_NAMESPACE::round(cl/nodeY)*nodeY; + int iLo = int(rw/binW); + int jLo = int(col/binH); + int iHi = DREAMPLACE_STD_NAMESPACE::min(int((rw + nodeX)/binW), num_bins_x-1); + int jHi = DREAMPLACE_STD_NAMESPACE::min(int((col + nodeY)/binH), num_bins_y-1); + + for (int i = iLo + threadIdx.y; i <= iHi; i += blockDim.y) + { + T w = compute_demand_function(i, binW, T(rw), nodeX); + for (int j = jLo + threadIdx.x; j <= jHi; j += blockDim.x) + { + T h = compute_demand_function(j, binH, col, nodeY); + T area = w * h; + int index = site_typeId + i*num_bins_y + j; + atomicAddOp(&binCapMap[index], area); + } + } + } + } +} + +template +int computeDemandMapCallKernel( + const int *site_type_map, const T *site_size_x, + const T *site_size_y, const T binW, const T binH, + const int num_bins_x, const int num_bins_y, + const int width, const int height, + const int bins_xy, + AtomicOp atomicAddOp, + typename AtomicOp::type *binCapMap) +{ + int thread_count = 64; + dim3 blockSize(2, 2, thread_count); + + int block_count = (width*height - 1 + thread_count) / thread_count; + + computeDemandMap<<>>( + site_type_map, site_size_x, site_size_y, binW, binH, + num_bins_x, num_bins_y, width, height, bins_xy, + atomicAddOp, binCapMap); + + return 0; +} + + +template +int computeDemandMapCudaLauncher( + const int *site_type_map, + const T *site_size_x, + const T *site_size_y, + const T binW, + const T binH, + const int num_site_types, + const int num_bins_x, + const int num_bins_y, + const int width, + const int height, + const int bins_xy, + const int deterministic_flag, + T *binCapMap) +{ + if (deterministic_flag == 1) + { + // total die area + double diearea = width * height; + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_site_types * num_bins_x * num_bins_y; + + unsigned long long int *bin_cap_map = NULL; + allocateCUDA(bin_cap_map, num_bins, unsigned long long int); + + AtomicAddCUDA atomicAddOp(scale_factor); + int thread_count = 512; + + copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, + thread_count>>>( + bin_cap_map, binCapMap, scale_factor, num_bins); + + computeDemandMapCallKernel( + site_type_map, site_size_x, site_size_y, binW, binH, + num_bins_x, num_bins_y, width, height, bins_xy, + atomicAddOp, bin_cap_map); + + copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, + thread_count>>>(binCapMap, + bin_cap_map, T(1.0 / scale_factor), num_bins); + + destroyCUDA(bin_cap_map); + } else + { + AtomicAddCUDA atomicAddOp; + + computeDemandMapCallKernel( + site_type_map, site_size_x, site_size_y, binW, binH, + num_bins_x, num_bins_y, width, height, bins_xy, + atomicAddOp, binCapMap); + } + return 0; +} + +// manually instantiate the template function +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeDemandMapCudaLauncher( \ + const int *site_type_map, const T *site_size_x, \ + const T *site_size_y, const T binW, const T binH, \ + const int num_site_types, const int num_bins_x, \ + const int num_bins_y, const int width, const int height, \ + const int bins_xy, const int deterministic_flag, T *binCapMap); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h new file mode 100644 index 00000000..c97cdaa2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h @@ -0,0 +1,23 @@ +/** + * @file demand_function.h + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Nov 2020 + */ + +#ifndef DREAMPLACE_DEMANDMAP_DEMAND_FUNCTION_H +#define DREAMPLACE_DEMANDMAP_DEMAND_FUNCTION_H + +DREAMPLACE_BEGIN_NAMESPACE + +// return non-negative value +#define DEFINE_COMPUTE_DEMAND_FUNCTION(type) \ + T compute_demand_function(int k, T bin_size, T siteL, T siteW) \ + { \ + T bin_k = k * bin_size; \ + T bin_kp1 = bin_k + bin_size; \ + return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(siteL + siteW, bin_kp1) - DREAMPLACE_STD_NAMESPACE::max(siteL, bin_k)); \ + } + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt new file mode 100644 index 00000000..b87c0a0d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt @@ -0,0 +1,35 @@ +set(OP_NAME density_map) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/density_map.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/density_map_cuda.cpp + src/density_map_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py new file mode 100644 index 00000000..1441687d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py @@ -0,0 +1,211 @@ +## +# @file density_map.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# @brief Compute density map +# + +import math +import torch +from torch import nn +from torch.autograd import Function + +import dreamplacefpga.ops.density_map.density_map_cpp as density_map_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.density_map.density_map_cuda as density_map_cuda + +import numpy as np +import matplotlib +matplotlib.use('Agg') +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +import pdb + +class DensityMapFunction(Function): + """ + @brief compute density map. + """ + @staticmethod + def forward( + pos, + node_size_x, + node_size_y, + bin_center_x, + bin_center_y, + initial_density_map, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_filler_nodes, + num_threads + ): + if pos.is_cuda: + output = density_map_cuda.forward( + pos.view(pos.numel()), + node_size_x, + node_size_y, + bin_center_x, + bin_center_y, + initial_density_map, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_filler_nodes) + else: + output = density_map_cpp.forward( + pos.view(pos.numel()), + node_size_x, + node_size_y, + bin_center_x, + bin_center_y, + initial_density_map, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_filler_nodes, + num_threads + ) + return output + +class DensityMap(object): + """ + @brief Compute density map for both movable and fixed cells. + The density map for fixed cells is pre-computed. + Each call will only compute the density map for movable cells. + """ + def __init__(self, node_size_x, node_size_y, bin_center_x, bin_center_y, xl, yl, xh, yh, bin_size_x, bin_size_y, num_movable_nodes, num_terminals, num_filler_nodes, num_threads=8): + """ + @brief initialization + @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order + @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order + @param bin_center_x bin center x locations + @param bin_center_y bin center y locations + @param xl left boundary + @param yl bottom boundary + @param xh right boundary + @param yh top boundary + @param bin_size_x bin width + @param bin_size_y bin height + @param num_movable_nodes number of movable cells + @param num_terminals number of fixed cells + @param num_filler_nodes number of filler cells + """ + super(DensityMap, self).__init__() + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.bin_center_x = bin_center_x + self.bin_center_y = bin_center_y + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.bin_size_x = bin_size_x + self.bin_size_y = bin_size_y + self.num_movable_nodes = num_movable_nodes + self.num_terminals = num_terminals + self.num_filler_nodes = num_filler_nodes + self.initial_density_map = None + self.num_threads = num_threads + def forward(self, pos): + """ + @brief API + @param pos cell locations. The array consists of x locations of movable cells, fixed cells, and filler cells, then y locations of them + """ + if self.initial_density_map is None: + if pos.is_cuda: + self.initial_density_map = density_map_cuda.fixed_density_map( + pos, + self.node_size_x, + self.node_size_y, + self.bin_center_x, + self.bin_center_y, + self.xl, + self.yl, + self.xh, + self.yh, + self.bin_size_x, + self.bin_size_y, + self.num_movable_nodes, + self.num_terminals + ) + else: + self.initial_density_map = density_map_cpp.fixed_density_map( + pos, + self.node_size_x, + self.node_size_y, + self.bin_center_x, + self.bin_center_y, + self.xl, + self.yl, + self.xh, + self.yh, + self.bin_size_x, + self.bin_size_y, + self.num_movable_nodes, + self.num_terminals, + self.num_threads + ) + + density_map = DensityMapFunction.forward( + pos=pos, + node_size_x=self.node_size_x, + node_size_y=self.node_size_y, + bin_center_x=self.bin_center_x, + bin_center_y=self.bin_center_y, + initial_density_map=self.initial_density_map, + xl=self.xl, + yl=self.yl, + xh=self.xh, + yh=self.yh, + bin_size_x=self.bin_size_x, + bin_size_y=self.bin_size_y, + num_movable_nodes=self.num_movable_nodes, + num_filler_nodes=self.num_filler_nodes, + num_threads=self.num_threads + ) + + return density_map + +def plot(density_map, name): + """ + @brief density map contour and heat map + """ + print(np.amax(density_map)) + print(np.mean(density_map)) + fig = plt.figure(figsize=(4, 3)) + ax = fig.gca(projection='3d') + + x = np.arange(density_map.shape[0]) + y = np.arange(density_map.shape[1]) + + x, y = np.meshgrid(x, y) + ax.plot_surface(x, y, density_map, alpha=0.8) + + ax.set_xlabel('x') + ax.set_ylabel('y') + ax.set_zlabel('density') + + plt.savefig(name+".3d.png") + + plt.clf() + + fig, ax = plt.subplots() + + ax.pcolor(density_map) + + fig.tight_layout() + plt.savefig(name+".2d.png") diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp new file mode 100644 index 00000000..afb1e6a5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp @@ -0,0 +1,246 @@ +/** + * @file density_map.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density map on CPU + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +void distributeBox2Bin( + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T bxl, T byl, T bxh, T byh, + T* buf_map + ) +{ + // density overflow function + auto computeDensityFunc = [](T x, T node_size, T bin_center, T bin_size){ + return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(x+node_size, bin_center+bin_size/2) - DREAMPLACE_STD_NAMESPACE::max(x, bin_center-bin_size/2)); + }; + // x direction + int bin_index_xl = int((bxl-xl)/bin_size_x); + int bin_index_xh = int(ceil((bxh-xl)/bin_size_x))+1; // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + // y direction + int bin_index_yl = int((byl-yl-2*bin_size_y)/bin_size_y); + int bin_index_yh = int(ceil((byh-yl+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityFunc(bxl, bxh - bxl, bin_center_x_tensor[k], bin_size_x); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityFunc(byl, byh - byl, bin_center_y_tensor[h], bin_size_y); + + // still area + T& density = buf_map[k*num_bins_y+h]; +#pragma omp atomic + density += px*py; + } + } +}; + +/// @brief compute density map +/// @param x_tensor cell x locations +/// @param y_tensor cell y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param num_nodes number of cells +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_threads number of threads +/// @param density_map_tensor 2D density map in column-major to write +template +int computeDensityMapLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + int num_threads, + T* density_map_tensor + ) +{ + // density_map_tensor should be initialized outside + +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + T bxl = x_tensor[i]; + T byl = y_tensor[i]; + T bxh = bxl + node_size_x_tensor[i]; + T byh = byl + node_size_y_tensor[i]; + distributeBox2Bin( + bin_center_x_tensor, bin_center_y_tensor, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + bxl, byl, bxh, byh, + density_map_tensor + ); + } + + return 0; +} + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute density map. +/// @param pos cell locations, array of x locations and then y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param initial_density_map initial density map +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @return density map +at::Tensor density_map_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + at::Tensor initial_density_map, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + int num_bins_x = int(ceil((xh-xl)/bin_size_x)); + int num_bins_y = int(ceil((yh-yl)/bin_size_y)); + at::Tensor density_map = initial_density_map.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { + computeDensityMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_movable_nodes, // only compute that for movable cells + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { + computeDensityMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_filler_nodes, // only compute that for movable cells + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + } + + return density_map; +} + +/// @brief Compute the density overflow for fixed cells. +/// This map can be used as the initial density map since it only needs to be computed once. +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_terminals number of fixed cells +/// @return a density map for fixed cells +at::Tensor fixed_density_map( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_terminals, + int num_threads + ) +{ + int num_bins_x = int(ceil((xh-xl)/bin_size_x)); + int num_bins_y = int(ceil((yh-yl)/bin_size_y)); + int num_nodes = pos.numel() / 2; + at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); + + if (num_terminals) + { + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { + computeDensityMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t) + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_terminals, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + } + + return density_map; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::density_map_forward, "DensityMap forward"); + m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_map, "DensityMap Map for Fixed Cells"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp new file mode 100644 index 00000000..65d0398f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp @@ -0,0 +1,175 @@ +/** + * @file density_map.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density map on GPU + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief compute density map +/// @param x_tensor cell x locations +/// @param y_tensor cell y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param num_nodes number of cells +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param density_map_tensor 2D density map in column-major to write +template +int computeDensityMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T* density_map_tensor + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute density map. +/// @param pos cell locations, array of x locations and then y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param initial_density_map initial density map +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @return density map +at::Tensor density_map_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + at::Tensor initial_density_map, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + int num_bins_x = int(ceil((xh-xl)/bin_size_x)); + int num_bins_y = int(ceil((yh-yl)/bin_size_y)); + at::Tensor density_map = initial_density_map.clone(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { + computeDensityMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_movable_nodes, // only compute that for movable cells + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { + computeDensityMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_filler_nodes, // only compute that for movable cells + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + } + + return density_map; +} + +/// @brief Compute the density overflow for fixed cells. +/// This map can be used as the initial density map since it only needs to be computed once. +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_terminals number of fixed cells +/// @return a density map for fixed cells +at::Tensor fixed_density_map( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_terminals + ) +{ + int num_bins_x = int(ceil((xh-xl)/bin_size_x)); + int num_bins_y = int(ceil((yh-yl)/bin_size_y)); + at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); + + if (num_terminals) + { + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { + computeDensityMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel()/2 + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t) + num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_terminals, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + } + + return density_map; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::density_map_forward, "DensityMap forward (CUDA)"); + m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_map, "DensityMap Map for Fixed Cells (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu new file mode 100644 index 00000000..523309d9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu @@ -0,0 +1,146 @@ +/** + * @file density_map_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Dec 2019 + */ + +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/print.cuh" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +inline __device__ void distributeBox2Bin( + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T bxl, T byl, T bxh, T byh, + T* buf_map + ) +{ + // density overflow function + auto computeDensityFunc = [](T x, T node_size, T bin_center, T bin_size){ + return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(x+node_size, bin_center+bin_size/2) - DREAMPLACE_STD_NAMESPACE::max(x, bin_center-bin_size/2)); + }; + // x direction + int bin_index_xl = int((bxl-xl)/bin_size_x); + int bin_index_xh = int(ceil((bxh-xl)/bin_size_x))+1; // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + // y direction + int bin_index_yl = int((byl-yl-2*bin_size_y)/bin_size_y); + int bin_index_yh = int(ceil((byh-yl+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityFunc(bxl, bxh - bxl, bin_center_x_tensor[k], bin_size_x); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityFunc(byl, byh - byl, bin_center_y_tensor[h], bin_size_y); + + // still area + atomicAdd(&buf_map[k*num_bins_y+h], px * py); + } + } +}; + +template +__global__ void computeDensityMap( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T* density_map_tensor + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nodes) + { + T bxl = x_tensor[i]; + T byl = y_tensor[i]; + T bxh = bxl + node_size_x_tensor[i]; + T byh = byl + node_size_y_tensor[i]; + distributeBox2Bin( + bin_center_x_tensor, bin_center_y_tensor, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + bxl, byl, bxh, byh, + density_map_tensor + ); + } +} + +/// @brief compute density map +/// @param x_tensor cell x locations +/// @param y_tensor cell y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param num_nodes number of cells +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param density_map_tensor 2D density map in column-major to write +template +int computeDensityMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T* density_map_tensor + ) +{ + int thread_count = 256; + int block_count = ceilDiv(num_nodes, thread_count); + + computeDensityMap<<>>( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + bin_center_x_tensor, bin_center_y_tensor, + num_nodes, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + density_map_tensor + ); + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeDensityMapCudaLauncher(\ + const T* x_tensor, const T* y_tensor, \ + const T* node_size_x_tensor, const T* node_size_y_tensor, \ + const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ + const int num_nodes, \ + const int num_bins_x, const int num_bins_y, \ + const T xl, const T yl, const T xh, const T yh, \ + const T bin_size_x, const T bin_size_y, \ + T* density_map_tensor\ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt new file mode 100644 index 00000000..f7e8f60d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt @@ -0,0 +1,36 @@ +set(OP_NAME density_potential) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/density_potential.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/density_potential_cuda.cpp + src/density_potential_cuda_kernel.cu + src/density_overflow_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py new file mode 100644 index 00000000..efdada13 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py @@ -0,0 +1,408 @@ +## +# @file density_potential.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# @brief Compute density potential according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) +# + +import math +import numpy as np +import torch +from torch import nn +from torch.autograd import Function +from torch.nn import functional as F + +import dreamplacefpga.ops.density_potential.density_potential_cpp as density_potential_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.density_potential.density_potential_cuda as density_potential_cuda + +import pdb +import matplotlib +matplotlib.use('Agg') +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +# global variable for plot +#plot_count = 0 + +class DensityPotentialFunction(Function): + """ + @brief compute density potential. + """ + + @staticmethod + def forward( + ctx, + pos, + node_size_x, node_size_y, + ax, bx, cx, + ay, by, cy, + bin_center_x, bin_center_y, + initial_density_map, + target_density, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_movable_nodes, + num_filler_nodes, + padding, + padding_mask, # same dimensions as density map, with padding regions to be 1 + num_bins_x, + num_bins_y, + num_impacted_bins_x, + num_impacted_bins_y, + num_threads + ): + if pos.is_cuda: + output = density_potential_cuda.forward( + pos.view(pos.numel()), + node_size_x, node_size_y, + ax, bx, cx, + ay, by, cy, + bin_center_x, bin_center_y, + initial_density_map, + target_density, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_movable_nodes, + num_filler_nodes, + padding, + num_bins_x, + num_bins_y, + num_impacted_bins_x, + num_impacted_bins_y + ) + else: + output = density_potential_cpp.forward( + pos.view(pos.numel()), + node_size_x, node_size_y, + ax, bx, cx, + ay, by, cy, + bin_center_x, bin_center_y, + initial_density_map, + target_density, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_movable_nodes, + num_filler_nodes, + padding, + num_bins_x, + num_bins_y, + num_impacted_bins_x, + num_impacted_bins_y, + num_threads + ) + + # output consists of (density_cost, density_map, max_density) + ctx.node_size_x = node_size_x + ctx.node_size_y = node_size_y + ctx.ax = ax + ctx.bx = bx + ctx.cx = cx + ctx.ay = ay + ctx.by = by + ctx.cy = cy + ctx.bin_center_x = bin_center_x + ctx.bin_center_y = bin_center_y + ctx.target_density = target_density + ctx.xl = xl + ctx.yl = yl + ctx.xh = xh + ctx.yh = yh + ctx.bin_size_x = bin_size_x + ctx.bin_size_y = bin_size_y + ctx.num_movable_nodes = num_movable_nodes + ctx.num_filler_nodes = num_filler_nodes + ctx.padding = padding + ctx.num_bins_x = num_bins_x + ctx.num_bins_y = num_bins_y + ctx.num_impacted_bins_x = num_impacted_bins_x + ctx.num_impacted_bins_y = num_impacted_bins_y + ctx.pos = pos + ctx.num_threads = num_threads + ctx.density_map = output[1] + + # set padding density + if padding > 0: + ctx.density_map.masked_fill_(padding_mask, target_density * bin_size_x * bin_size_y) + + #global plot_count + #if plot_count % 100 == 0: + # plot(plot_count, output[1].clone().div(bin_size_x*bin_size_y).cpu().numpy(), padding, 'summary/potential_map') + #plot_count += 1 + + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + if grad_pos.is_cuda: + output = density_potential_cuda.backward( + grad_pos, + ctx.num_bins_x, ctx.num_bins_y, + ctx.num_impacted_bins_x, ctx.num_impacted_bins_y, + ctx.density_map, + ctx.pos, + ctx.node_size_x, ctx.node_size_y, + ctx.ax, ctx.bx, ctx.cx, + ctx.ay, ctx.by, ctx.cy, + ctx.bin_center_x, ctx.bin_center_y, + ctx.target_density, + ctx.xl, ctx.yl, ctx.xh, ctx.yh, + ctx.bin_size_x, ctx.bin_size_y, + ctx.num_movable_nodes, + ctx.num_filler_nodes, + ctx.padding + ) + else: + output = density_potential_cpp.backward( + grad_pos, + ctx.num_bins_x, ctx.num_bins_y, + ctx.num_impacted_bins_x, ctx.num_impacted_bins_y, + ctx.density_map, + ctx.pos, + ctx.node_size_x, ctx.node_size_y, + ctx.ax, ctx.bx, ctx.cx, + ctx.ay, ctx.by, ctx.cy, + ctx.bin_center_x, ctx.bin_center_y, + ctx.target_density, + ctx.xl, ctx.yl, ctx.xh, ctx.yh, + ctx.bin_size_x, ctx.bin_size_y, + ctx.num_movable_nodes, + ctx.num_filler_nodes, + ctx.padding, + ctx.num_threads + ) + return output, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None + +class DensityPotential(nn.Module): + """ + @brief Compute density potential according to NTUPlace3 + """ + def __init__(self, + node_size_x, node_size_y, + ax, bx, cx, + ay, by, cy, + bin_center_x, bin_center_y, + target_density, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_movable_nodes, + num_terminals, + num_filler_nodes, + padding, + sigma, + delta, + num_threads + ): + """ + @brief initialization + @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order + @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order + @param ax + @param bx + @param cx + @param ay + @param by + @param cy see the a, b, c defined in NTUPlace3 + @param bin_center_x bin center x locations + @param bin_center_y bin center y locations + @param target_density target density + @param xl left boundary + @param yl bottom boundary + @param xh right boundary + @param yh top boundary + @param bin_size_x bin width + @param bin_size_y bin height + @param num_movable_nodes number of movable cells + @param num_terminals number of fixed cells + @param num_filler_nodes number of filler cells + @param padding bin padding to boundary of placement region + @param sigma parameter for density map of fixed cells according to NTUPlace3 + @param delta parameter for density map of fixed cells according to NTUPlace3 + """ + super(DensityPotential, self).__init__() + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.ax = ax + self.bx = bx + self.cx = cx + self.ay = ay + self.by = by + self.cy = cy + self.bin_center_x = bin_center_x + self.bin_center_y = bin_center_y + self.target_density = target_density + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.bin_size_x = bin_size_x + self.bin_size_y = bin_size_y + self.num_movable_nodes = num_movable_nodes + self.num_terminals = num_terminals + self.num_filler_nodes = num_filler_nodes + self.padding = padding + # compute maximum impacted bins + self.num_bins_x = int(math.ceil((xh-xl)/bin_size_x)) + self.num_bins_y = int(math.ceil((yh-yl)/bin_size_y)) + self.num_impacted_bins_x = ((node_size_x[:num_movable_nodes].max()+4*self.bin_size_x)/self.bin_size_x).ceil().clamp(max=self.num_bins_x); + self.num_impacted_bins_y = ((node_size_y[:num_movable_nodes].max()+4*self.bin_size_y)/self.bin_size_y).ceil().clamp(max=self.num_bins_y); + if self.padding > 0: + self.padding_mask = torch.ones(self.num_bins_x, self.num_bins_y, dtype=torch.uint8, device=node_size_x.device) + self.padding_mask[self.padding:self.num_bins_x-self.padding, self.padding:self.num_bins_y-self.padding].fill_(0) + else: + self.padding_mask = torch.zeros(self.num_bins_x, self.num_bins_y, dtype=torch.uint8, device=node_size_x.device) + + # parameters for initial density map + self.sigma = sigma + self.delta = delta + self.num_threads = num_threads + # initial density_map due to fixed cells + self.initial_density_map = None + + def forward(self, pos): + if self.initial_density_map is None: + if self.num_terminals == 0: + num_impacted_bins_x = 0 + num_impacted_bins_y = 0 + else: + num_impacted_bins_x = ((self.node_size_x[self.num_movable_nodes:self.num_movable_nodes+self.num_terminals].max()+self.bin_size_x)/self.bin_size_x).ceil().clamp(max=self.num_bins_x) + num_impacted_bins_y = ((self.node_size_y[self.num_movable_nodes:self.num_movable_nodes+self.num_terminals].max()+self.bin_size_y)/self.bin_size_y).ceil().clamp(max=self.num_bins_y) + if pos.is_cuda: + self.initial_density_map = density_potential_cuda.fixed_density_map( + pos.view(pos.numel()), + self.node_size_x, self.node_size_y, + self.ax, self.bx, self.cx, + self.ay, self.by, self.cy, + self.bin_center_x, self.bin_center_y, + self.xl, self.yl, self.xh, self.yh, + self.bin_size_x, self.bin_size_y, + self.num_movable_nodes, + self.num_terminals, + self.num_bins_x, + self.num_bins_y, + num_impacted_bins_x, + num_impacted_bins_y, + self.sigma, self.delta + ) + else: + self.initial_density_map = density_potential_cpp.fixed_density_map( + pos.view(pos.numel()), + self.node_size_x, self.node_size_y, + self.ax, self.bx, self.cx, + self.ay, self.by, self.cy, + self.bin_center_x, self.bin_center_y, + self.xl, self.yl, self.xh, self.yh, + self.bin_size_x, self.bin_size_y, + self.num_movable_nodes, + self.num_terminals, + self.num_bins_x, + self.num_bins_y, + num_impacted_bins_x, + num_impacted_bins_y, + self.sigma, self.delta, + self.num_threads + ) + # there exist fixed cells + if (self.num_movable_nodes+self.num_filler_nodes) < pos.numel()/2: + # convert area to density + bin_area = self.bin_size_x*self.bin_size_y + self.initial_density_map.div_(bin_area) + # gaussian filter + gaussian_weights = torch.tensor(gaussian_kernel(self.sigma)).to(pos.device) + self.initial_density_map = F.conv2d( + self.initial_density_map.view([1, 1, self.num_bins_x, self.num_bins_y]), + gaussian_weights.view([1, 1, gaussian_weights.size(0), gaussian_weights.size(1)]), + padding=[gaussian_weights.size(0)/2, gaussian_weights.size(1)/2] + ).view([self.num_bins_x, self.num_bins_y]) + ## level smoothing + #self.initial_density_map.div_(self.initial_density_map.max()) + #density_mean = self.initial_density_map.mean() + #delta_map = self.initial_density_map - density_mean + #self.initial_density_map = density_mean + delta_map.sign().mul_(delta_map.abs().pow_(self.delta)) + # convert density to area + self.initial_density_map.mul_(bin_area) + + #plot(self.initial_density_map.clone().div(self.bin_size_x*self.bin_size_y).cpu().numpy(), self.padding, 'initial_potential_map') + + return DensityPotentialFunction.apply( + pos, + self.node_size_x, self.node_size_y, + self.ax, self.bx, self.cx, + self.ay, self.by, self.cy, + self.bin_center_x, + self.bin_center_y, + self.initial_density_map, + self.target_density, + self.xl, + self.yl, + self.xh, + self.yh, + self.bin_size_x, + self.bin_size_y, + self.num_movable_nodes, + self.num_filler_nodes, + self.padding, + self.padding_mask, + self.num_bins_x, + self.num_bins_y, + self.num_impacted_bins_x, + self.num_impacted_bins_y, + self.num_threads + ) + +def gaussian_kernel(sigma, truncate=4.0): + """ + Return Gaussian that truncates at the given number of standard deviations. + """ + + sigma = float(sigma) + radius = int(truncate * sigma + 0.5) + + x, y = np.mgrid[-radius:radius+1, -radius:radius+1] + sigma = sigma**2 + + k = 2*np.exp(-0.5 * (x**2 + y**2) / sigma) + k = k / np.sum(k) + + return k + +def plot(plot_count, density_map, padding, name): + """ + density map contour and heat map + """ + density_map = density_map[padding:-1-padding, padding:-1-padding] + print("max density = %g" % (np.amax(density_map))) + print("mean density = %g" % (np.mean(density_map))) + + fig = plt.figure() + ax = fig.gca(projection='3d') + + x = np.arange(density_map.shape[0]) + y = np.arange(density_map.shape[1]) + + x, y = np.meshgrid(x, y) + ax.plot_surface(x, y, density_map, alpha=0.8) + + ax.set_xlabel('x') + ax.set_ylabel('y') + ax.set_zlabel('density') + + #plt.tight_layout() + plt.savefig(name+".3d.%d.png" % (plot_count)) + plt.close() + + #plt.clf() + + #fig, ax = plt.subplots() + + #ax.pcolor(density_map) + + ## Loop over data dimensions and create text annotations. + ##for i in range(density_map.shape[0]): + ## for j in range(density_map.shape[1]): + ## text = ax.text(j, i, density_map[i, j], + ## ha="center", va="center", color="w") + #fig.tight_layout() + #plt.savefig(name+".2d.%d.png" % (plot_count)) + #plt.close() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu new file mode 100644 index 00000000..3624f1f1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu @@ -0,0 +1,159 @@ +/** + * @file density_overflow_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density overflow according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063). + * This is for fixed cells. + */ +#include +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeDensityMap( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const int num_impacted_bins_x, const int num_impacted_bins_y, + T* density_map_tensor) +{ + int64_t i = blockIdx.x * blockDim.x + threadIdx.x; + int64_t bound = int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y; + // rank-one update density map + if (i < bound) + { + // density overflow function + auto computeDensityOverflowFunc = [](T x, T node_size, T bin_center, T bin_size){ + return max(T(0.0), min(x+node_size, bin_center+bin_size/2) - max(x, bin_center-bin_size/2)); + }; + int node_id = i/(num_impacted_bins_x*num_impacted_bins_y); + int residual_index = i-node_id*num_impacted_bins_x*num_impacted_bins_y; + // x direction + int bin_index_xl = int((x_tensor[node_id]-xl)/bin_size_x); + bin_index_xl = max(bin_index_xl, 0); + int k = bin_index_xl+int(residual_index / num_impacted_bins_y); + if (k+1 > num_bins_x) + { + return; + } + // y direction + int bin_index_yl = int((y_tensor[node_id]-yl)/bin_size_y); + bin_index_yl = max(bin_index_yl, 0); + int h = bin_index_yl+(residual_index % num_impacted_bins_y); + if (h+1 > num_bins_y) + { + return; + } + + T px = computeDensityOverflowFunc(x_tensor[node_id], node_size_x_tensor[node_id], bin_center_x_tensor[k], bin_size_x); + T py = computeDensityOverflowFunc(y_tensor[node_id], node_size_y_tensor[node_id], bin_center_y_tensor[h], bin_size_y); + // still area + atomicAdd(&density_map_tensor[k*num_bins_y+h], px*py); + } +} + +template +int computeDensityOverflowMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const int num_impacted_bins_x, const int num_impacted_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T* density_map_tensor + ) +{ + int64_t thread_count = 512; + int64_t block_count = (int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y - 1 + thread_count) /thread_count; + dreamplaceAssert(block_count >= 0); // avoid numerical overflow + + computeDensityMap<<>>( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + bin_center_x_tensor, bin_center_y_tensor, + num_nodes, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_impacted_bins_x, num_impacted_bins_y, + density_map_tensor); + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeDensityOverflowMapCudaLauncher(\ + const T* x_tensor, const T* y_tensor, \ + const T* node_size_x_tensor, const T* node_size_y_tensor, \ + const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ + const int num_nodes, \ + const int num_bins_x, const int num_bins_y, \ + const int num_impacted_bins_x, const int num_impacted_bins_y, \ + const T xl, const T yl, const T xh, const T yh, \ + const T bin_size_x, const T bin_size_y, \ + T* density_map_tensor\ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +template +__global__ void computeGaussianFilterWeights( + const int num_bins_x, const int num_bins_y, + const T sigma, + T* gaussian_filter_tensor + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_bins_x*num_bins_y) + { + int x = i/num_bins_y; + int y = i-x*num_bins_y; + T sigma_square = sigma*sigma; + T x2_y2 = (x-num_bins_x/2)*(x-num_bins_x) + (y-num_bins_y/2)*(y-num_bins_y); + //gaussian_filter_tensor[i] = 1.0/(2*M_PI*sigma_square) * exp(-x2_y2/(2*sigma_square)); + gaussian_filter_tensor[i] = 2 * exp(-x2_y2/(2*sigma_square)); + } +} + +template +int computeGaussianFilterLauncher( + const int num_bins_x, const int num_bins_y, + const T sigma, + T* gaussian_filter_tensor + ) +{ + int thread_count = 512; + int block_count = (num_bins_x*num_bins_y - 1 + thread_count) / thread_count; + + computeGaussianFilterWeights<<>>( + num_bins_x, num_bins_y, + sigma, + gaussian_filter_tensor + ); + + return 0; +} + +#define REGISTER_GAUSSIAN_FILTER_LAUNCHER(T) \ + template int computeGaussianFilterLauncher(\ + const int num_bins_x, const int num_bins_y, \ + const T sigma, \ + T* gaussian_filter_tensor\ + ); + +REGISTER_GAUSSIAN_FILTER_LAUNCHER(float); +REGISTER_GAUSSIAN_FILTER_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp new file mode 100644 index 00000000..c5876155 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp @@ -0,0 +1,615 @@ +/** + * @file density_potential.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density potential on CPU according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief compute density map, density cost, and gradient +/// @param x_tensor cell x locations +/// @param y_tensor cell y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param ax_tensor ax tensor according to NTUPlace3 paper, for x direction +/// @param bx_tensor bx tensor according to NTUPlace3 paper, for x direction +/// @param cx_tensor cx tensor according to NTUPlace3 paper, for x direction +/// @param ay_tensor ay tensor according to NTUPlace3 paper, for y direction +/// @param by_tensor by tensor according to NTUPlace3 paper, for y direction +/// @param cy_tensor cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +/// @param num_nodes number of cells +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param target_area target area computed from target density +/// @param density_map_tensor 2D density map in column-major to write +/// @param grad_tensor input gradient from backward propagation +/// @param grad_x_tensor density gradient of cell in x direction +/// @param grad_y_tensor density gradient of cell in y direction +template +int computeDensityPotentialMapLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + int num_impacted_bins_x, int num_impacted_bins_y, + const int num_nodes, + const int num_bins_x, const int num_bins_y, const int padding, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_area, + T* density_map_tensor, + const T* grad_tensor, + int num_threads, + T* grad_x_tensor, T* grad_y_tensor + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief compute density map, density cost, and gradient +/// @param pos cell locations. The array consists of all x locations and then y locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param ax ax tensor according to NTUPlace3 paper, for x direction +/// @param bx bx tensor according to NTUPlace3 paper, for x direction +/// @param cx cx tensor according to NTUPlace3 paper, for x direction +/// @param ay ay tensor according to NTUPlace3 paper, for y direction +/// @param by by tensor according to NTUPlace3 paper, for y direction +/// @param cy cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param initial_density_map initial density map for fixed cells +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +std::vector density_potential_forward( + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + at::Tensor initial_density_map, // initial density map from fixed cells + double target_density, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + int padding, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = initial_density_map.clone(); + double target_area = target_density*bin_size_x*bin_size_y; + + //int num_nodes = pos.numel()/2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { + computeDensityPotentialMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + num_movable_nodes, + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + nullptr, + num_threads, + nullptr, nullptr + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { + computeDensityPotentialMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + num_filler_nodes, + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + nullptr, + num_threads, + nullptr, nullptr + ); + }); + } + + auto max_density = density_map.max(); + // (max(0, density-target_area))^2 + //auto delta = (density_map-target_area).clamp_min(0).pow(2); + auto delta = (density_map-target_area).pow(2); + auto density_cost = at::sum(delta); + + return {density_cost, density_map, max_density}; +} + +/// @brief Compute density potential gradient +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +/// @param density_map current density map +/// @param pos cell locations. The array consists of all x locations and then y locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param ax ax tensor according to NTUPlace3 paper, for x direction +/// @param bx bx tensor according to NTUPlace3 paper, for x direction +/// @param cx cx tensor according to NTUPlace3 paper, for x direction +/// @param ay ay tensor according to NTUPlace3 paper, for y direction +/// @param by by tensor according to NTUPlace3 paper, for y direction +/// @param cy cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +at::Tensor density_potential_backward( + at::Tensor grad_pos, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y, + at::Tensor density_map, + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double target_density, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + int padding, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + AT_ASSERTM(!density_map.is_cuda() && density_map.ndimension() == 2 && density_map.size(0) == num_bins_x && density_map.size(1) == num_bins_y, "density_map must be a 2D tensor on CPU"); + double target_area = target_density*bin_size_x*bin_size_y; + at::Tensor grad_out = at::zeros_like(pos); + + //int num_nodes = pos.numel()/2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { + computeDensityPotentialMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + num_movable_nodes, + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()/2 + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { + computeDensityPotentialMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+pos.numel()/2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + num_filler_nodes, + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()-num_filler_nodes + ); + }); + } + + return grad_out; +} + +template +int computeDensityOverflowMapLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_density, + const int num_threads, + T* density_map_tensor + ); + +template +int computeGaussianFilterLauncher( + const int num_bins_x, const int num_bins_y, + const T sigma, + const int num_threads, + T* gaussian_filter_tensor + ); + +/// @brief compute density map for fixed cells +at::Tensor fixed_density_potential_map( + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_terminals, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y, + double sigma, double delta, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.type()); + + int num_nodes = pos.numel()/2; + + // Call the cuda kernel launcher + if (num_terminals && num_impacted_bins_x && num_impacted_bins_y) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityOverflowMapLauncher", [&] { + computeDensityOverflowMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_terminals, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + 0, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + }); + } + + return density_map; +} + +template +int computeDensityPotentialMapLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + int num_impacted_bins_x, int num_impacted_bins_y, + const int num_nodes, + const int num_bins_x, const int num_bins_y, const int padding, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_area, + T* density_map_tensor, + const T* grad_tensor, + int num_threads, + T* grad_x_tensor, T* grad_y_tensor + ) +{ + // density_map_tensor should be initialized outside + + // density potential function + auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ + // from origin to center + x += node_size/2; + //printf("x = %g, bin_center = %g\n", x, bin_center); + T dist = fabs(x-bin_center); + //printf("dist = %g\n", dist); + T partition1 = node_size/2+bin_size; + //printf("partition1 = %g\n", partition1); + T partition2 = partition1+bin_size; + //printf("partition2 = %g\n", partition2); + //printf("a = %g, b = %g, c = %g\n", a, b, c); + if (dist < partition1) + { + return c*(1-a*dist*dist); + } + else if (dist < partition2) + { + return c*(b*(dist-partition2)*(dist-partition2)); + } + else + { + return T(0.0); + } + }; + // density potential gradient function + auto computeDensityPotentialGradFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ + // from origin to center + x += node_size/2; + T dist = fabs(x-bin_center); + T partition1 = node_size/2+bin_size; + T partition2 = partition1+bin_size; + if (dist < partition1) + { + return -2*c*a*(x-bin_center); + } + else if (dist < partition2) + { + T sign = (x < bin_center)? -1.0 : 1.0; + return 2*c*b*(dist-partition2)*sign; + } + else + { + return T(0.0); + } + }; + +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + // x direction + int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); + int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive + bin_index_xl = std::max(bin_index_xl, 0); + bin_index_xh = std::min(bin_index_xh, num_bins_x); + + // y direction + int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); + int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = std::max(bin_index_yl, 0); + bin_index_yh = std::min(bin_index_yh, num_bins_y); + + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); + //printf("px[%d, %d] = %g\n", i, k, px); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); + //printf("py[%d, %d] = %g\n", i, h, py); + //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); + + // still area + T& density = density_map_tensor[k*num_bins_y+h]; +#pragma omp atomic + density += px*py; + } + } + } + + if (grad_tensor) // compute density gradient + { +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); + //int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive + bin_index_xl = std::max(bin_index_xl, 0); + // be careful about the bin_index_xl and bin_index_xh here + // the assumption is that num_bins_x >= num_impacted_bins_x + // each row of the px matrix should be filled with num_impacted_bins_x columns + bin_index_xl = std::min(bin_index_xl, num_bins_x-num_impacted_bins_x); + //bin_index_xh = std::min(bin_index_xh, num_bins_x); + int bin_index_xh = bin_index_xl+num_impacted_bins_x; + + int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); + //int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = std::max(bin_index_yl, 0); + // be careful about the bin_index_yl and bin_index_yh here + // the assumption is that num_bins_y >= num_impacted_bins_y + // each row of the py matrix should be filled with num_impacted_bins_y columns + bin_index_yl = std::min(bin_index_yl, num_bins_y-num_impacted_bins_y); + //bin_index_yh = std::min(bin_index_yh, num_bins_y); + int bin_index_yh = bin_index_yl+num_impacted_bins_y; + + grad_x_tensor[i] = 0; + grad_y_tensor[i] = 0; + // update density potential map + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); + T gradx = computeDensityPotentialGradFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); + T grady = computeDensityPotentialGradFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); + + T delta = density_map_tensor[k*num_bins_y+h]-target_area; + //delta = std::max(delta, (T)0); + + grad_x_tensor[i] += 2*delta*py*gradx; + grad_y_tensor[i] += 2*delta*px*grady; + + } + } + + grad_x_tensor[i] *= *grad_tensor; + grad_y_tensor[i] *= *grad_tensor; + } + } + else // compute density cost + { + // handle padding +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_bins_x; ++i) + { + for (int j = 0; j < num_bins_y; ++j) + { + if (!(i >= padding && i+padding < num_bins_x && j >= padding && j+padding < num_bins_y)) + { + density_map_tensor[i*num_bins_y+j] = target_area; + } + } + } + } + + return 0; +} + +template +int computeDensityOverflowMapLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_area, + const int num_threads, + T* density_map_tensor + ) +{ + // initialize + int num_bins = num_bins_x*num_bins_y; +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_bins; ++i) + { + density_map_tensor[i] = 0; + } + + // density overflow function + auto computeDensityOverflowFunc = [](T x, T node_size, T bin_center, T bin_size){ + return std::max(T(0.0), std::min(x+node_size, bin_center+bin_size/2) - std::max(x, bin_center-bin_size/2)); + }; +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + // x direction + int bin_index_xl = int((x_tensor[i]-xl)/bin_size_x); + int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i])/bin_size_x))+1; // exclusive + bin_index_xl = std::max(bin_index_xl, 0); + bin_index_xh = std::min(bin_index_xh, num_bins_x); + + // y direction + int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); + int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = std::max(bin_index_yl, 0); + bin_index_yh = std::min(bin_index_yh, num_bins_y); + + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityOverflowFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityOverflowFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y); + //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); + + // still area + T& density = density_map_tensor[k*num_bins_y+h]; +#pragma omp atomic + density += px*py; + } + } + } + + return 0; +} + +template +int computeGaussianFilterLauncher( + const int num_bins_x, const int num_bins_y, + const T sigma, + const int num_threads, + T* gaussian_filter_tensor + ) +{ + T sigma_square = sigma*sigma; +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_bins_x; ++i) + { + for (int j = 0; j < num_bins_y; ++j) + { + T x2_y2 = (i-num_bins_x/2)*(i-num_bins_x) + (j-num_bins_y/2)*(j-num_bins_y); + gaussian_filter_tensor[i*num_bins_y+j] = 1.0/(2*M_PI*sigma_square) * exp(-x2_y2/(2*sigma_square)); + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::density_potential_forward, "DensityPotential forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::density_potential_backward, "DensityPotential backward"); + m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_potential_map, "DensityPotential Map for Fixed Cells"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp new file mode 100644 index 00000000..183b3f53 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp @@ -0,0 +1,414 @@ +/** + * @file hpwl_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density potential on CUDA according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief compute density map, density cost, and gradient +/// @param x_tensor cell x locations +/// @param y_tensor cell y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param ax_tensor ax tensor according to NTUPlace3 paper, for x direction +/// @param bx_tensor bx tensor according to NTUPlace3 paper, for x direction +/// @param cx_tensor cx tensor according to NTUPlace3 paper, for x direction +/// @param ay_tensor ay tensor according to NTUPlace3 paper, for y direction +/// @param by_tensor by tensor according to NTUPlace3 paper, for y direction +/// @param cy_tensor cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x_tensor bin center x locations +/// @param bin_center_y_tensor bin center y locations +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +/// @param num_nodes number of cells +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param target_area target area computed from target density +/// @param density_map_tensor 2D density map in column-major to write +/// @param density_cost_tensor overall density overflow +/// @param grad_tensor input gradient from backward propagation +/// @param grad_x_tensor density gradient of cell in x direction +/// @param grad_y_tensor density gradient of cell in y direction +template +int computeDensityPotentialMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_impacted_bins_x, const int num_impacted_bins_y, + const int mat_size_x, const int mat_size_y, + const int num_nodes, + const int num_bins_x, const int num_bins_y, const int padding, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_area, + T* density_map_tensor, + const T* grad_tensor, + T* grad_x_tensor, T* grad_y_tensor + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +typedef double T; + +/// @brief compute density map, density cost, and gradient +/// @param pos cell locations. The array consists of all x locations and then y locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param ax ax tensor according to NTUPlace3 paper, for x direction +/// @param bx bx tensor according to NTUPlace3 paper, for x direction +/// @param cx cx tensor according to NTUPlace3 paper, for x direction +/// @param ay ay tensor according to NTUPlace3 paper, for y direction +/// @param by by tensor according to NTUPlace3 paper, for y direction +/// @param cy cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +std::vector density_potential_forward( + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + at::Tensor initial_density_map, // initial density map from fixed cells + double target_density, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + int padding, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = initial_density_map.clone(); + double target_area = target_density*bin_size_x*bin_size_y; + + int num_nodes = pos.numel()/2; + int mat_size_x = (num_movable_nodes*num_impacted_bins_x); // only need to compute for movable nodes + int mat_size_y = (num_movable_nodes*num_impacted_bins_y); + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { + computeDensityPotentialMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + mat_size_x, mat_size_y, + num_movable_nodes, // only need to compute for movable nodes + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + nullptr, + nullptr, nullptr + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { + computeDensityPotentialMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes*2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + mat_size_x, mat_size_y, + num_filler_nodes, // only need to compute for movable nodes + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + nullptr, + nullptr, nullptr + ); + }); + } + + auto max_density = density_map.max(); + + // (max(0, density-target_area))^2 + //auto delta = (density_map-target_area).clamp_min(0).pow(2); + auto delta = (density_map-target_area).pow(2); + auto density_cost = at::sum(delta); + + return {density_cost, + density_map, + max_density + }; +} + +/// @brief Compute density potential gradient +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_impacted_bins_x number of impacted bins for any cell in x direction +/// @param num_impacted_bins_y number of impacted bins for any cell in y direction +/// @param density_map current density map +/// @param pos cell locations. The array consists of all x locations and then y locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param ax ax tensor according to NTUPlace3 paper, for x direction +/// @param bx bx tensor according to NTUPlace3 paper, for x direction +/// @param cx cx tensor according to NTUPlace3 paper, for x direction +/// @param ay ay tensor according to NTUPlace3 paper, for y direction +/// @param by by tensor according to NTUPlace3 paper, for y direction +/// @param cy cy tensor according to NTUPlace3 paper, for y direction +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +at::Tensor density_potential_backward( + at::Tensor grad_pos, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y, + at::Tensor density_map, + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double target_density, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + int padding) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + AT_ASSERTM(density_map.is_cuda() && density_map.ndimension() == 2 && density_map.size(0) == num_bins_x && density_map.size(1) == num_bins_y, "density_map must be a 2D tensor on GPU"); + double target_area = target_density*bin_size_x*bin_size_y; + at::Tensor grad_out = at::zeros_like(pos); + + int num_nodes = pos.numel()/2; + int mat_size_x = (num_movable_nodes*num_impacted_bins_x); + int mat_size_y = (num_movable_nodes*num_impacted_bins_y); + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { + computeDensityPotentialMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + mat_size_x, mat_size_y, + num_movable_nodes, // only need to compute for movable nodes + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes + ); + }); + if (num_filler_nodes) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { + computeDensityPotentialMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes*2-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+num_nodes-num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_impacted_bins_x, num_impacted_bins_y, + mat_size_x, mat_size_y, + num_filler_nodes, // only need to compute for movable nodes + num_bins_x, num_bins_y, padding, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + target_area, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes*2-num_filler_nodes + ); + }); + } + + return grad_out; +} + +template +int computeDensityOverflowMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const int num_impacted_bins_x, const int num_impacted_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + T* density_map_tensor + ); + +template +int computeGaussianFilterLauncher( + const int num_bins_x, const int num_bins_y, + const T sigma, + T* gaussian_filter_tensor + ); + +/// @brief compute density map for fixed cells +at::Tensor fixed_density_potential_map( + at::Tensor pos, + at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor ax, at::Tensor bx, at::Tensor cx, + at::Tensor ay, at::Tensor by, at::Tensor cy, + at::Tensor bin_center_x, + at::Tensor bin_center_y, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_terminals, + int num_bins_x, int num_bins_y, + int num_impacted_bins_x, int num_impacted_bins_y, + double sigma, double delta + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.type()); + + int num_nodes = pos.numel()/2; + + // Call the cuda kernel launcher + if (num_terminals && num_impacted_bins_x && num_impacted_bins_y) + { + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityOverflowMapCudaLauncher", [&] { + //int mat_size_x = ((num_nodes-num_movable_nodes)*num_impacted_bins_x); + //int mat_size_y = ((num_nodes-num_movable_nodes)*num_impacted_bins_y); + //computeDensityPotentialMapCudaLauncher( + // DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, + // DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, + // DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), + // DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), + // DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + // num_impacted_bins_x, num_impacted_bins_y, + // mat_size_x, mat_size_y, + // num_nodes-num_movable_nodes, + // num_bins_x, num_bins_y, 0, + // xl, yl, xh, yh, + // bin_size_x, bin_size_y, + // 0, + // DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + // nullptr, + // nullptr, nullptr + // ); + computeDensityOverflowMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), + num_terminals, + num_bins_x, num_bins_y, + num_impacted_bins_x, num_impacted_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) + ); + +#if 0 + density_map.div_(bin_size_x*bin_size_y); + + // smooth with Gaussian filter + T truncate = 4.0; + int radius = std::round(truncate*sigma); + int kernel_size = 2*radius+1; + at::Tensor gaussian_filter = at::zeros({kernel_size, kernel_size}, density_map.type()); + computeGaussianFilterLauncher( + gaussian_filter.size(0), gaussian_filter.size(1), + sigma, + DREAMPLACE_TENSOR_DATA_PTR(gaussian_filter, T) + ); + gaussian_filter.div_(gaussian_filter.sum()); + //std::cout << "density_map = " << density_map << "\n"; + //std::cout << "gaussian_filter = " << gaussian_filter << "\n"; + density_map = at::conv2d(density_map.view({1, 1, num_bins_x, num_bins_y}), gaussian_filter.view({1, 1, gaussian_filter.size(0), gaussian_filter.size(1)}), {}, 1, {{radius, radius}}).view({num_bins_x, num_bins_y}); + //std::cout << "density_map = " << density_map << "\n"; + // normalize to [0, 1] + //density_map.div_(density_map.max()); + + // level smoothing + at::Tensor density_mean = density_map.mean(); + at::Tensor delta_map = density_map-density_mean; + density_map = density_mean + delta_map.sign().mul_(delta_map.abs().pow_(delta)); + + density_map.mul_(bin_size_x*bin_size_y); +#endif + }); + } + + return density_map; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::density_potential_forward, "DensityPotential forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::density_potential_backward, "DensityPotential backward (CUDA)"); + m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_potential_map, "DensityPotential Map for Fixed Cells (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu new file mode 100644 index 00000000..d5b2bc40 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu @@ -0,0 +1,317 @@ +/** + * @file density_potential_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute density potential according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063). + * This is for movable and filler cells. + */ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +#if 0 +template +__global__ void computePaddingDensityMap( + const int num_bins_x, const int num_bins_y, + const int padding, + T* density_map_tensor) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < num_nodes*num_impacted_bins_x*num_impacted_bins_y; i += blockDim.x * gridDim.x) + { + int ix = i/num_bins_y; + int iy = i-ix*num_bins_y; + + if (ix < padding) + { + density_map_tensor[i] = density_map_tensor[padding*num_bins_y+iy]; + } + } +} +#endif + +template +__global__ void computeDensityMap( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const int num_impacted_bins_x, const int num_impacted_bins_y, + T* density_map_tensor) +{ + int64_t i = blockIdx.x * blockDim.x + threadIdx.x; + int64_t bound = int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y; + // rank-one update density map + if (i < bound) + { + // density potential function + auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ + // from origin to center + x += node_size/2; + //printf("x = %g, bin_center = %g\n", x, bin_center); + T dist = fabs(x-bin_center); + //printf("dist = %g\n", dist); + T partition1 = node_size/2+bin_size; + //printf("partition1 = %g\n", partition1); + T partition2 = partition1+bin_size; + //printf("partition2 = %g\n", partition2); + //printf("a = %g, b = %g, c = %g\n", a, b, c); + if (dist < partition1) + { + return c*(1-a*dist*dist); + } + else if (dist < partition2) + { + return c*(b*(dist-partition2)*(dist-partition2)); + } + else + { + return T(0.0); + } + }; + int node_id = i/(num_impacted_bins_x*num_impacted_bins_y); + int residual_index = i-node_id*num_impacted_bins_x*num_impacted_bins_y; + // x direction + int bin_index_xl = int((x_tensor[node_id]-xl-2*bin_size_x)/bin_size_x); + bin_index_xl = max(bin_index_xl, 0); + int k = bin_index_xl+int(residual_index / num_impacted_bins_y); + if (k+1 > num_bins_x) + { + return; + } + // y direction + int bin_index_yl = int((y_tensor[node_id]-yl-2*bin_size_y)/bin_size_y); + bin_index_yl = max(bin_index_yl, 0); + int h = bin_index_yl+(residual_index % num_impacted_bins_y); + if (h+1 > num_bins_y) + { + return; + } + + T px = computeDensityPotentialFunc(x_tensor[node_id], node_size_x_tensor[node_id], bin_center_x_tensor[k], bin_size_x, ax_tensor[node_id], bx_tensor[node_id], cx_tensor[node_id]); + T py = computeDensityPotentialFunc(y_tensor[node_id], node_size_y_tensor[node_id], bin_center_y_tensor[h], bin_size_y, ay_tensor[node_id], by_tensor[node_id], cy_tensor[node_id]); + //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); + // still area + atomicAdd(&density_map_tensor[k*num_bins_y+h], px*py); + } +} + +template +__global__ void computeDensityGradient( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const int num_impacted_bins_x, const int num_impacted_bins_y, + const T* grad_tensor, const T target_area, + const T* density_map_tensor, + T* grad_x_tensor, T* grad_y_tensor + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + // rank-one update density map + if (i < num_nodes) + { + // density potential function + auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ + // from origin to center + x += node_size/2; + //printf("x = %g, bin_center = %g\n", x, bin_center); + T dist = fabs(x-bin_center); + //printf("dist = %g\n", dist); + T partition1 = node_size/2+bin_size; + //printf("partition1 = %g\n", partition1); + T partition2 = partition1+bin_size; + //printf("partition2 = %g\n", partition2); + //printf("a = %g, b = %g, c = %g\n", a, b, c); + if (dist < partition1) + { + return c*(1-a*dist*dist); + } + else if (dist < partition2) + { + return c*(b*(dist-partition2)*(dist-partition2)); + } + else + { + return T(0.0); + } + }; + // density potential gradient function + auto computeDensityPotentialGradFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ + // from origin to center + x += node_size/2; + T dist = fabs(x-bin_center); + T partition1 = node_size/2+bin_size; + T partition2 = partition1+bin_size; + if (dist < partition1) + { + return -2*c*a*(x-bin_center); + } + else if (dist < partition2) + { + T sign = (x < bin_center)? -1.0 : 1.0; + return 2*c*b*(dist-partition2)*sign; + } + else + { + return T(0.0); + } + }; + int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); + int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive + bin_index_xl = max(bin_index_xl, 0); + // be careful about the bin_index_xl and bin_index_xh here + // the assumption is that num_bins_x >= num_impacted_bins_x + // each row of the px matrix should be filled with num_impacted_bins_x columns + bin_index_xl = min(bin_index_xl, num_bins_x-num_impacted_bins_x); + bin_index_xh = min(bin_index_xh, num_bins_x); + //int bin_index_xh = bin_index_xl+num_impacted_bins_x; + + int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); + int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive + bin_index_yl = max(bin_index_yl, 0); + // be careful about the bin_index_yl and bin_index_yh here + // the assumption is that num_bins_y >= num_impacted_bins_y + // each row of the py matrix should be filled with num_impacted_bins_y columns + bin_index_yl = min(bin_index_yl, num_bins_y-num_impacted_bins_y); + bin_index_yh = min(bin_index_yh, num_bins_y); + //int bin_index_yh = bin_index_yl+num_impacted_bins_y; + + grad_x_tensor[i] = 0; + grad_y_tensor[i] = 0; + // update density potential map + for (int k = bin_index_xl; k < bin_index_xh; ++k) + { + T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); + T gradx = computeDensityPotentialGradFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); + for (int h = bin_index_yl; h < bin_index_yh; ++h) + { + T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); + T grady = computeDensityPotentialGradFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); + + T delta = density_map_tensor[k*num_bins_y+h]-target_area; + //delta = max(delta, (T)0); + + grad_x_tensor[i] += 2*delta*py*gradx; + grad_y_tensor[i] += 2*delta*px*grady; + + } + } + + grad_x_tensor[i] *= *grad_tensor; + grad_y_tensor[i] *= *grad_tensor; + } +} + +template +int computeDensityPotentialMapCudaLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, + const T* bin_center_x_tensor, const T* bin_center_y_tensor, + const int num_impacted_bins_x, const int num_impacted_bins_y, + const int mat_size_x, const int mat_size_y, + const int num_nodes, + const int num_bins_x, const int num_bins_y, const int padding, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const T target_area, + T* density_map_tensor, + const T* grad_tensor, + T* grad_x_tensor, T* grad_y_tensor + ) +{ + int64_t block_count; + int64_t thread_count = 512; + + // compute gradient + if (grad_tensor) + { + block_count = (num_nodes - 1 + thread_count) / thread_count; + + computeDensityGradient<<>>( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + ax_tensor, bx_tensor, cx_tensor, + ay_tensor, by_tensor, cy_tensor, + bin_center_x_tensor, bin_center_y_tensor, + num_nodes, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_impacted_bins_x, num_impacted_bins_y, + grad_tensor, target_area, + density_map_tensor, + grad_x_tensor, grad_y_tensor + ); + + // print gradient + //printArray(grad_x_tensor, 10, "grad_x_tensor"); + //printArray(grad_y_tensor, 10, "grad_y_tensor"); + } + else + { + block_count = (int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y - 1 + thread_count) / thread_count; + + computeDensityMap<<>>( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + ax_tensor, bx_tensor, cx_tensor, + ay_tensor, by_tensor, cy_tensor, + bin_center_x_tensor, bin_center_y_tensor, + num_nodes, + num_bins_x, num_bins_y, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_impacted_bins_x, num_impacted_bins_y, + density_map_tensor); + + // print density map + //print2DArray(density_map_tensor, num_bins_x, num_bins_y, "potential density_map_tensor"); + //printScalar(bin_size_x, "bin_size_x"); + //printScalar(bin_size_y, "bin_size_y"); + //printScalar(target_area, "target_area"); + + } + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeDensityPotentialMapCudaLauncher(\ + const T* x_tensor, const T* y_tensor, \ + const T* node_size_x_tensor, const T* node_size_y_tensor, \ + const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, \ + const T* ay_tensor, const T* by_tensor, const T* cy_tensor, \ + const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ + const int num_impacted_bins_x, const int num_impacted_bins_y, \ + const int mat_size_x, const int mat_size_y, \ + const int num_nodes, \ + const int num_bins_x, const int num_bins_y, const int padding, \ + const T xl, const T yl, const T xh, const T yh, \ + const T bin_size_x, const T bin_size_y, \ + const T target_area, \ + T* density_map_tensor, \ + const T* grad_tensor, \ + T* grad_x_tensor, T* grad_y_tensor \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore new file mode 100644 index 00000000..5b6f5911 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore @@ -0,0 +1,6 @@ +*.pkl +*.dat +*.png +*.pklz +*.gds +__pycache__ diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt new file mode 100644 index 00000000..c5192ce1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt @@ -0,0 +1,33 @@ +set(OP_NAME draw_place) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${LIMBO_SOURCE_DIR} + ) + +set(LINK_LIBS utility_cxx + ${LIMBO_BINARY_DIR}/limbo/parsers/gdsii/stream/libgdsparser.a + ${LIMBO_BINARY_DIR}/limbo/thirdparty/gzstream/libgzstream.a + ${ZLIB_LIBRARIES} + ) + +if(CAIRO_FOUND) + set(INCLUDE_DIRS ${INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}) + set(LINK_LIBS ${LINK_LIBS} ${CAIRO_LIBRARIES}) + set(DRAWPLACE 1) +else() + set(DRAWPLACE 0) +endif() + +add_pytorch_extension(${TARGET_NAME}_cpp + src/draw_place.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS} + EXTRA_DEFINITIONS DRAWPLACE=${DRAWPLACE} + ) + +install(TARGETS ${TARGET_NAME}_cpp DESTINATION dreamplacefpga/ops/${OP_NAME}) +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py new file mode 100644 index 00000000..93dd6e6b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py @@ -0,0 +1,218 @@ +## +# @file PlaceDrawer.py +# @author Yibo Lin (DREAMPlace) +# @date Mar 2019 +# @brief A python implementation of placement drawer as an alternative when cairo C/C++ API is not available. +# + +import sys +import os +import time +import math +import cairocffi as cairo +import numpy as np + +class PlaceDrawer (object): + """ + @brief A python implementation of placement drawer as an alternative when cairo C/C++ API is not available. + """ + @staticmethod + def forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + filename, + iteration=None): + """ + @brief python implementation of placement drawer. + @param pos locations of cells + @param node_size_x array of cell width + @param node_size_y array of cell height + @param pin_offset_x pin offset to cell origin + @param pin_offset_y pin offset to cell origin + @param pin2node_map map pin to cell + @param xl left boundary + @param yl bottom boundary + @param xh right boundary + @param yh top boundary + @param site_width width of placement site + @param row_height height of placement row, equivalent to height of placement site + @param bin_size_x bin width + @param bin_size_y bin height + @param num_movable_nodes number of movable cells + @param num_filler_nodes number of filler cells + @param filename output filename + @param iteration current optimization step + """ + num_nodes = len(pos)//2 + num_movable_nodes = num_movable_nodes + num_filler_nodes = num_filler_nodes + num_physical_nodes = num_nodes-num_filler_nodes + num_bins_x = int(math.ceil((xh-xl)/bin_size_x)) + num_bins_y = int(math.ceil((yh-yl)/bin_size_y)) + x = np.array(pos[:num_nodes]) + y = np.array(pos[num_nodes:]) + node_size_x = np.array(node_size_x) + node_size_y = np.array(node_size_y) + pin_offset_x = np.array(pin_offset_x) + pin_offset_y = np.array(pin_offset_y) + pin2node_map = np.array(pin2node_map) + try: + tt = time.time() + width = 800 + height = 800 + line_width = 0.1 + padding = 0 + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + ctx = cairo.Context(surface) + # Do not use scale function. + # This is not compatible with show_text + + if num_movable_nodes < num_physical_nodes: + layout_xl = min(np.amin(x[num_movable_nodes:num_physical_nodes]), xl) + layout_yl = min(np.amin(y[num_movable_nodes:num_physical_nodes]), yl) + layout_xh = max(np.amax(x[num_movable_nodes:num_physical_nodes]+node_size_x[num_movable_nodes:num_physical_nodes]), xh) + layout_yh = max(np.amax(y[num_movable_nodes:num_physical_nodes]+node_size_y[num_movable_nodes:num_physical_nodes]), yh) + else: + layout_xl = xl + layout_yl = yl + layout_xh = xh + layout_yh = yh + + def bin_xl(id_x): + """ + @param id_x horizontal index + @return bin xl + """ + return xl+id_x*bin_size_x + + def bin_xh(id_x): + """ + @param id_x horizontal index + @return bin xh + """ + return min(bin_xl(id_x)+bin_size_x, xh) + + def bin_yl(id_y): + """ + @param id_y vertical index + @return bin yl + """ + return yl+id_y*bin_size_y + + def bin_yh(id_y): + """ + @param id_y vertical index + @return bin yh + """ + return min(bin_yl(id_y)+bin_size_y, yh) + + def normalize_x(xx): + return (xx - (layout_xl-padding*bin_size_x))/(layout_xh-layout_xl+padding*2*bin_size_x)*width + def normalize_y(xx): + return (xx - (layout_yl-padding*bin_size_y))/(layout_yh-layout_yl+padding*2*bin_size_y)*height + def draw_rect(x1, y1, x2, y2): + ctx.move_to(x1, y1) + ctx.line_to(x1, y2) + ctx.line_to(x2, y2) + ctx.line_to(x2, y1) + ctx.close_path() + ctx.stroke() + + # draw layout region + ctx.set_source_rgb(1, 1, 1) + draw_layout_xl = normalize_x(layout_xl-padding*bin_size_x) + draw_layout_yl = normalize_y(layout_yl-padding*bin_size_y) + draw_layout_xh = normalize_x(layout_xh+padding*bin_size_x) + draw_layout_yh = normalize_y(layout_yh+padding*bin_size_y) + ctx.rectangle(draw_layout_xl, draw_layout_yl, draw_layout_xh, draw_layout_yh) + ctx.fill() + ctx.set_line_width(line_width) + ctx.set_source_rgba(0.1, 0.1, 0.1, alpha=0.8) + ctx.move_to(normalize_x(xl), normalize_y(yl)) + ctx.line_to(normalize_x(xl), normalize_y(yh)) + ctx.line_to(normalize_x(xh), normalize_y(yh)) + ctx.line_to(normalize_x(xh), normalize_y(yl)) + ctx.close_path() + ctx.stroke() + ## draw bins + #for i in range(1, num_bins_x): + # ctx.move_to(normalize_x(bin_xl(i)), normalize_y(yl)) + # ctx.line_to(normalize_x(bin_xl(i)), normalize_y(yh)) + # ctx.close_path() + # ctx.stroke() + #for i in range(1, num_bins_y): + # ctx.move_to(normalize_x(xl), normalize_y(bin_yl(i))) + # ctx.line_to(normalize_x(xh), normalize_y(bin_yl(i))) + # ctx.close_path() + # ctx.stroke() + + # draw cells + ctx.set_font_size(16) + ctx.select_font_face("monospace", cairo.FONT_SLANT_NORMAL, + cairo.FONT_WEIGHT_NORMAL) + node_xl = x + node_yl = layout_yl+layout_yh-(y+node_size_y[0:len(y)]) # flip y + node_xh = node_xl+node_size_x[0:len(x)] + node_yh = layout_yl+layout_yh-y # flip y + node_xl = normalize_x(node_xl) + node_yl = normalize_y(node_yl) + node_xh = normalize_x(node_xh) + node_yh = normalize_y(node_yh) + ctx.set_line_width(line_width) + #print("plot layout") + # draw fixed macros + ctx.set_source_rgba(1, 0, 0, alpha=0.5) + for i in range(num_movable_nodes, num_physical_nodes): + ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) + ctx.fill() + ctx.set_source_rgba(0, 0, 0, alpha=1.0) # Solid color + for i in range(num_movable_nodes, num_physical_nodes): + draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) + # draw fillers + if len(node_xl) > num_physical_nodes: # filler is included + ctx.set_line_width(line_width) + ctx.set_source_rgba(115/255.0, 115/255.0, 125/255.0, alpha=0.5) # Solid color + for i in range(num_physical_nodes, num_nodes): + ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) + ctx.fill() + ctx.set_source_rgba(230/255.0, 230/255.0, 250/255.0, alpha=0.3) # Solid color + for i in range(num_physical_nodes, num_nodes): + draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) + # draw cells + ctx.set_line_width(line_width*2) + ctx.set_source_rgba(0, 0, 1, alpha=0.5) # Solid color + for i in range(num_movable_nodes): + ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) + ctx.fill() + ctx.set_source_rgba(0, 0, 0.8, alpha=0.8) # Solid color + for i in range(num_movable_nodes): + draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) + ## draw cell indices + #for i in range(num_nodes): + # ctx.move_to((node_xl[i]+node_xh[i])/2, (node_yl[i]+node_yh[i])/2) + # ctx.show_text("%d" % (i)) + + # show iteration + if iteration: + ctx.set_source_rgb(0, 0, 0) + ctx.set_line_width(line_width*10) + ctx.select_font_face("monospace", cairo.FONT_SLANT_NORMAL, + cairo.FONT_WEIGHT_NORMAL) + ctx.set_font_size(32) + ctx.move_to(normalize_x((xl+xh)/2), normalize_y((yl+yh)/2)) + ctx.show_text('{:04}'.format(iteration)) + + surface.write_to_png(filename) # Output to PNG + print("[I] plotting to %s takes %.3f seconds" % (filename, time.time()-tt)) + except Exception as e: + print("[E] failed to plot") + print(str(e)) + return 0 + + return 1 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py new file mode 100644 index 00000000..2ca80a39 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py @@ -0,0 +1,238 @@ +## +# @file draw_place.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Jan 2021 +# @brief Plot placement to an image +# + +import os +import sys +import torch +from torch.autograd import Function + +import dreamplacefpga.ops.draw_place.draw_place_cpp as draw_place_cpp +import dreamplacefpga.ops.draw_place.PlaceDrawer as PlaceDrawer +import pdb +import numpy as np + +class DrawPlaceFunction(Function): + @staticmethod + def forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + filename + ): + ret = draw_place_cpp.forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + filename + ) + # if C/C++ API failed, try with python implementation + if not filename.endswith(".gds") and not ret: + ret = PlaceDrawer.PlaceDrawer.forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + filename + ) + return ret + +class DrawPlace(object): + """ + @brief Draw placement + """ + def __init__(self, placedb): + """ + @brief initialization + """ + self.node_size_x = torch.from_numpy(placedb.node_size_x).float() + self.node_size_y = torch.from_numpy(placedb.node_size_y).float() + self.pin_offset_x = torch.FloatTensor(placedb.pin_offset_x).float() + self.pin_offset_y = torch.FloatTensor(placedb.pin_offset_y).float() + self.pin2node_map = torch.from_numpy(placedb.pin2node_map) + self.xl = placedb.xl + self.yl = placedb.yl + self.xh = placedb.xh + self.yh = placedb.yh + self.site_width = placedb.width + self.row_height = placedb.height + self.bin_size_x = placedb.bin_size_x + self.bin_size_y = placedb.bin_size_y + self.num_movable_nodes = placedb.num_movable_nodes + self.num_filler_nodes = placedb.num_filler_nodes + + def forward(self, pos, filename): + """ + @param pos cell locations, array of x locations and then y locations + @param filename suffix specifies the format + """ + return DrawPlaceFunction.forward( + pos, + self.node_size_x, + self.node_size_y, + self.pin_offset_x, + self.pin_offset_y, + self.pin2node_map, + self.xl, + self.yl, + self.xh, + self.yh, + self.site_width, + self.row_height, + self.bin_size_x, + self.bin_size_y, + self.num_movable_nodes, + self.num_filler_nodes, + filename + ) + + def __call__(self, pos, filename): + """ + @brief top API + @param pos cell locations, array of x locations and then y locations + @param filename suffix specifies the format + """ + return self.forward(pos, filename) + +# FPGA version - Added by Rachel +class DrawPlaceFunctionFPGA(Function): + @staticmethod + def forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_physical_nodes, num_filler_nodes, + node2fence_region_map, + is_cc_node, + ffIdx, lutIdx, addIdx, + bramIdx, m9kIdx, m144kIdx, + dspIdx, + ioIdx,pllIdx, + filename + ): + ret = draw_place_cpp.fpga( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_physical_nodes, num_filler_nodes, + node2fence_region_map, + is_cc_node, + ffIdx, lutIdx, addIdx, + bramIdx, m9kIdx, m144kIdx, + dspIdx, + ioIdx,pllIdx, + filename + ) + # if C/C++ API failed, try with python implementation + if not filename.endswith(".gds") and not ret: + ret = PlaceDrawer.PlaceDrawer.forward( + pos, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + filename + ) + return ret + +class DrawPlaceFPGA(object): + """ + @brief Draw placement + """ + def __init__(self, placedb): + """ + @brief initialization + """ + if placedb.num_ccNodes > 0: + self.is_cc_node = torch.from_numpy(placedb.is_cc_node) + nodeSizeX = placedb.node_size_x + nodeSizeX[:placedb.num_physical_nodes][placedb.is_cc_node == 1] = 0.5 + self.node_size_x = torch.from_numpy(nodeSizeX) + else: + self.is_cc_node = torch.zeros(placedb.num_physical_nodes, dtype=torch.int32) + self.node_size_x = torch.from_numpy(placedb.node_size_x) + self.node_size_y = torch.from_numpy(placedb.node_size_y) + self.pin_offset_x = torch.from_numpy(placedb.pin_offset_x) + self.pin_offset_y = torch.from_numpy(placedb.pin_offset_y) + self.pin2node_map = torch.from_numpy(placedb.pin2node_map) + self.xl = placedb.xl + self.yl = placedb.yl + self.xh = placedb.xh + self.yh = placedb.yh + self.bin_size_x = placedb.bin_size_x + self.bin_size_y = placedb.bin_size_y + self.num_physical_nodes = placedb.num_physical_nodes + self.num_filler_nodes = placedb.num_filler_nodes + self.node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map) + self.fmask = torch.from_numpy(placedb.io_mask) + self.node_x = torch.from_numpy(placedb.node_x) + self.node_y = torch.from_numpy(placedb.node_y) + self.num_ccNodes = placedb.num_ccNodes + + ##Use resource type identifier for color coding + self.ffIdx = placedb.rFFIdx + self.lutIdx = placedb.rLUTIdx + self.addIdx = placedb.rADDIdx + self.bramIdx = placedb.rBRAMIdx + self.m9kIdx = placedb.rM9KIdx + self.m144kIdx = placedb.rM144KIdx + self.dspIdx = placedb.rDSPIdx + self.ioIdx = placedb.rIOIdx + self.pllIdx = placedb.rPLLIdx + + def forward(self, pos, filename): + """ + @param pos cell locations, array of x locations and then y locations + @param filename suffix specifies the format + """ + fillers = torch.tensor(np.zeros(self.num_filler_nodes)).bool() + fmask = torch.cat((self.fmask,fillers,self.fmask, fillers),0) + allLoc = torch.cat((self.node_x, fillers.to(dtype=pos.dtype), self.node_y, fillers.to(dtype=pos.dtype)),0) + omask = ~fmask + newpos = pos*omask.to(dtype=pos.dtype) + allLoc*fmask.to(dtype=pos.dtype) + + return DrawPlaceFunctionFPGA.forward( + newpos, self.node_size_x, self.node_size_y, + self.pin_offset_x, self.pin_offset_y, + self.pin2node_map, self.xl, self.yl, + self.xh, self.yh, self.bin_size_x, + self.bin_size_y, self.num_physical_nodes, + self.num_filler_nodes, self.node2fence_region_map, + self.is_cc_node, self.ffIdx, self.lutIdx, + self.addIdx, self.bramIdx, self.m9kIdx, + self.m144kIdx, self.dspIdx, self.ioIdx, + self.pllIdx, filename) + + def __call__(self, pos, filename): + """ + @brief top API + @param pos cell locations, array of x locations and then y locations + @param filename suffix specifies the format + """ + return self.forward(pos, filename) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h new file mode 100644 index 00000000..df22c208 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h @@ -0,0 +1,437 @@ +/** + * @file PlaceDrawFPGA.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef DREAMPLACE_PLACEDRAWFPGA_H +#define DREAMPLACE_PLACEDRAWFPGA_H + +#include +#include +#include +#include + +#if DRAWPLACE == 1 +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "utility/src/utils.h" + +typedef struct _cairo_surface cairo_surface_t; + +DREAMPLACE_BEGIN_NAMESPACE + +/// PlaceDrawFPGA write files in various formats +template +class PlaceDrawFPGA +{ + public: + typedef T coordinate_type; + typedef I index_type; + + enum FileFormat { + EPS = 0, // handle by cairo + PDF = 1, // handle by cairo + SVG = 2, // handle by cairo + PNG = 3 // handle by cairo + }; + enum DrawContent { + NONE = 0, + NODE = 1, + NODETEXT = 2, + PIN = 4, + NET = 8, + ALL = NODE|NODETEXT|PIN|NET + }; + /// constructor + PlaceDrawFPGA(const coordinate_type* x, const coordinate_type* y, + const coordinate_type* node_size_x, const coordinate_type* node_size_y, + const coordinate_type* pin_offset_x, const coordinate_type* pin_offset_y, + const index_type* pin2node_map, + const index_type num_nodes, + const index_type num_physical_nodes, + const index_type num_filler_nodes, + const index_type num_pins, + const coordinate_type xl, const coordinate_type yl, const coordinate_type xh, const coordinate_type yh, + const coordinate_type bin_size_x, const coordinate_type bin_size_y, + const index_type* node2fence_region_map, + const index_type* is_cc_node, + const int ffIdx, + const int lutIdx, + const int addIdx, + const int bramIdx, + const int m9kIdx, + const int m144kIdx, + const int dspIdx, + const int ioIdx, + const int pllIdx, + int content = ALL) + : m_x(x) + , m_y(y) + , m_node_size_x(node_size_x) + , m_node_size_y(node_size_y) + , m_pin_offset_x(pin_offset_x) + , m_pin_offset_y(pin_offset_y) + , m_pin2node_map(pin2node_map) + , m_num_nodes(num_nodes) + , m_num_physical_nodes(num_physical_nodes) + , m_num_filler_nodes(num_filler_nodes) + , m_num_pins(num_pins) + , m_xl(xl) + , m_yl(yl) + , m_xh(xh) + , m_yh(yh) + , m_bin_size_x(bin_size_x) + , m_bin_size_y(bin_size_y) + , m_node2fence_region_map(node2fence_region_map) + , m_is_cc_node(is_cc_node) + , m_ffId(ffIdx) + , m_lutId(lutIdx) + , m_addId(addIdx) + , m_bramId(bramIdx) + , m_m9kId(m9kIdx) + , m_m144kId(m144kIdx) + , m_dspId(dspIdx) + , m_ioId(ioIdx) + , m_pllId(pllIdx) + , m_content(content) + { + } + + bool run(std::string const& filename, FileFormat ff) const + { + dreamplacePrint(kINFO, "writing placement to %s\n", filename.c_str()); + bool flag = false; + + //PlaceDB const& placeDB = m_db.placeDB(); + + switch (ff) + { + case EPS: + case PDF: + case SVG: + case PNG: + flag = writeFig(filename.c_str(), m_xh*10, m_yh*10, ff); + break; + default: + dreamplacePrint(kERROR, "unknown writing format at line %u\n", __LINE__); + break; + } + + return flag; + } + + /// \param first and last mark nodes whose nets will be drawn + template + bool run(std::string const& filename, FileFormat ff, Iterator first, Iterator last) + { + m_sMarkNode.insert(first, last); + bool flag = run(filename, ff); + m_sMarkNode.clear(); + return flag; + } + protected: + /// write formats supported by cairo + /// \param width of screen + /// \param height of screen + void paintCairo(cairo_surface_t* cs, double width, double height) const + { +#if DRAWPLACE == 1 + double ratio[2] = { + width/(m_xh-m_xl), + height/(m_yh-m_yl) + }; + char buf[16]; + cairo_t *c; + cairo_text_extents_t extents; + + c=cairo_create(cs); + cairo_save(c); // save status + cairo_translate(c, 0-m_xl*ratio[0], height+m_yl*ratio[1]); // translate is additive + cairo_scale(c, ratio[0], -ratio[1]); // scale is additive + + // background + cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); + cairo_set_source_rgb(c, 1.0, 1.0, 1.0); + cairo_fill(c); + cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); + cairo_set_line_width(c, 0.001); + cairo_set_source_rgb(c, 0.1, 0.1, 0.1); + cairo_stroke(c); + + // bins + cairo_set_line_width(c, 0.001); + cairo_set_source_rgba(c, 0.1, 0.1, 0.1, 0.8); + for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) + { + cairo_move_to(c, bx, m_yl); + cairo_line_to(c, bx, m_yh); + cairo_stroke(c); + } + for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) + { + cairo_move_to(c, m_xl, by); + cairo_line_to(c, m_xh, by); + cairo_stroke(c); + } + + // nodes + cairo_set_line_width(c, 0.001); + cairo_select_font_face (c, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + if (m_content&NODE) + { + // filler + for (int i = m_num_nodes-m_num_filler_nodes; i < m_num_nodes; ++i) + { + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 200/255.0, 200/255.0, 200/255.0, 0.5); + cairo_fill(c); + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 175/255.0, 175/255.0, 175/255.0, 0.8); + cairo_stroke(c); + if (m_content&NODETEXT) + { + sprintf(buf, "%u", i); + cairo_set_font_size (c, m_node_size_y[i]/20); + cairo_text_extents (c, buf, &extents); + cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); + cairo_show_text(c, buf); + } + } + // movable & fixed nodes + for (int i = 0; i < m_num_physical_nodes; ++i) + { + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + + if (m_is_cc_node[i] == 1) + { + cairo_set_source_rgba(c, 1, 50/255, 1, 0.7); + } + else if (m_node2fence_region_map[i] == m_lutId) + { + cairo_set_source_rgba(c, 0, 1, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_ffId) + { + cairo_set_source_rgba(c, 0, 0, 1, 0.7); + } + else if (m_node2fence_region_map[i] == m_dspId) + { + cairo_set_source_rgba(c, 1, 0.5, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_bramId || + m_node2fence_region_map[i] == m_m9kId) + { + cairo_set_source_rgba(c, 175/255.0, 0, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_m144kId) + { + cairo_set_source_rgba(c, 1, 107/255, 107/255, 0.7); + } + else if (m_node2fence_region_map[i] == m_ioId) + { + cairo_set_source_rgba(c, 0, 0, 100/255, 0.7); + } + else if (m_node2fence_region_map[i] == m_pllId) + { + cairo_set_source_rgba(c, 0, 100/255.0, 100/255.0, 0.7); + } + else if (m_node2fence_region_map[i] == m_addId) + { + cairo_set_source_rgba(c, 100/255.0, 0, 100/255.0, 0.7); + } else + { + cairo_set_source_rgba(c, 0, 0, 0, 1); + } + + cairo_fill(c); + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + + if (m_is_cc_node[i] == 1) + { + cairo_set_source_rgba(c, 1, 50/255, 1, 0.7); + } + else if (m_node2fence_region_map[i] == m_lutId) + { + cairo_set_source_rgba(c, 0, 1, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_ffId) + { + cairo_set_source_rgba(c, 0, 0, 1, 0.7); + } + else if (m_node2fence_region_map[i] == m_dspId) + { + cairo_set_source_rgba(c, 1, 0.5, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_bramId || + m_node2fence_region_map[i] == m_m9kId) + { + cairo_set_source_rgba(c, 175/255.0, 0, 0, 0.7); + } + else if (m_node2fence_region_map[i] == m_m144kId) + { + cairo_set_source_rgba(c, 1, 107/255, 107/255, 0.7); + } + else if (m_node2fence_region_map[i] == m_ioId) + { + cairo_set_source_rgba(c, 0, 0, 100/255, 0.7); + } + else if (m_node2fence_region_map[i] == m_pllId) + { + cairo_set_source_rgba(c, 0, 100/255.0, 100/255.0, 0.7); + } + else if (m_node2fence_region_map[i] == m_addId) + { + cairo_set_source_rgba(c, 100/255.0, 0, 100/255.0, 0.7); + } else + { + cairo_set_source_rgba(c, 0, 0, 0, 1); + } + + cairo_stroke(c); + + if (m_content&NODETEXT) + { + sprintf(buf, "%u", i); + cairo_set_font_size (c, m_node_size_y[i]/20); + cairo_text_extents (c, buf, &extents); + cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); + cairo_show_text(c, buf); + } + } + } + cairo_restore(c); + + cairo_show_page(c); + + cairo_destroy(c); +#else + dreamplacePrint(kWARN, "cs = %p, width = %g, height = %g are not used, as DRAWPLACE not enabled\n", cs, width, height); +#endif + } + bool writeFig(const char* fname, double width, double height, FileFormat ff) const + { +#if DRAWPLACE == 1 + cairo_surface_t *cs; + + switch (ff) + { + case PNG: + cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + break; + case PDF: + cs=cairo_pdf_surface_create(fname, width, height); + break; + case EPS: + cs=cairo_ps_surface_create(fname, width, height); + break; + case SVG: + cs=cairo_svg_surface_create(fname, width, height); + break; + default: + dreamplacePrint(kERROR, "unknown file format in %s\n", __func__); + return false; + } + + paintCairo(cs, width, height); + + cairo_surface_flush(cs); + // need additional writing call for PNG + if (ff == PNG) + cairo_surface_write_to_png(cs, fname); + cairo_surface_destroy(cs); + return true; +#else + dreamplacePrint(kWARN, "filename = %s, width = %g, height = %g, file format = %d not used, as DRAWPLACE not enabled\n", fname, width, height, (int)ff); + return false; +#endif + } + /// scale source coordinate to target screen + double scaleToScreen(double coord, double srcOffset, double srcSize, double tgtOffset, double tgtSize) const + { + double ratio = tgtSize/srcSize; + return tgtOffset + (coord-srcOffset)*ratio; + } + + /// automatically increment by 2 + /// \param reset controls whehter restart from 1 + unsigned getLayer(bool reset = false) const + { + static unsigned count = 0; + if (reset) + count = 0; + return (++count)<<1; + } + /// \param i node id + /// \return text to be shown on cell + std::string getTextOnNode(index_type i) const + { + return ""; + } + /// \param i pin id + /// \return text to be shown on pin + std::string getTextOnPin(index_type i) const + { + return "NA"; + } + /// \brief set pin bounding box + /// \param i pin id + void getPinBbox(index_type i, double scale_rato, coordinate_type& xl, coordinate_type& yl, coordinate_type& xh, coordinate_type& yh) const + { + index_type node_id = m_pin2node_map[i]; + coordinate_type x = m_x[node_id]; + coordinate_type y = m_y[node_id]; + coordinate_type offset_x = m_pin_offset_x[i]; + coordinate_type offset_y = m_pin_offset_y[i]; + coordinate_type pin_size = std::max(std::min(x, y)/10, (coordinate_type)(1.0/scale_rato)); + xl = x+offset_x-pin_size; + yl = y+offset_y-pin_size; + xh = x+offset_x+pin_size; + yh = y+offset_y+pin_size; + } + + const coordinate_type* m_x; + const coordinate_type* m_y; + const coordinate_type* m_node_size_x; + const coordinate_type* m_node_size_y; + const coordinate_type* m_pin_offset_x; + const coordinate_type* m_pin_offset_y; + const index_type* m_pin2node_map; + index_type m_num_nodes; + index_type m_num_physical_nodes; + index_type m_num_filler_nodes; + index_type m_num_pins; + coordinate_type m_xl; + coordinate_type m_yl; + coordinate_type m_xh; + coordinate_type m_yh; + coordinate_type m_bin_size_x; + coordinate_type m_bin_size_y; + const index_type* m_node2fence_region_map; + const index_type* m_is_cc_node; + const int m_ffId; + const int m_lutId; + const int m_addId; + const int m_bramId; + const int m_m9kId; + const int m_m144kId; + const int m_dspId; + const int m_ioId; + const int m_pllId; + std::set m_sMarkNode; ///< marked nodes whose net will be drawn + int m_content; ///< content for DrawContent +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h new file mode 100644 index 00000000..73b0bd02 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h @@ -0,0 +1,462 @@ +/** + * @file PlaceDrawer.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef DREAMPLACE_PLACEDRAWER_H +#define DREAMPLACE_PLACEDRAWER_H + +#include +#include +#include +#include + +#if DRAWPLACE == 1 +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "utility/src/utils.h" + +typedef struct _cairo_surface cairo_surface_t; + +DREAMPLACE_BEGIN_NAMESPACE + +/// PlaceDrawer write files in various formats +template +class PlaceDrawer +{ + public: + typedef T coordinate_type; + typedef I index_type; + + enum FileFormat { + EPS = 0, // handle by cairo + PDF = 1, // handle by cairo + SVG = 2, // handle by cairo + PNG = 3, // handle by cairo + GDSII = 4 + }; + enum DrawContent { + NONE = 0, + NODE = 1, + NODETEXT = 2, + PIN = 4, + NET = 8, + ALL = NODE|NODETEXT|PIN|NET + }; + /// constructor + PlaceDrawer(const coordinate_type* x, const coordinate_type* y, + const coordinate_type* node_size_x, const coordinate_type* node_size_y, + const coordinate_type* pin_offset_x, const coordinate_type* pin_offset_y, + const index_type* pin2node_map, + const index_type num_nodes, + const index_type num_movable_nodes, + const index_type num_filler_nodes, + const index_type num_pins, + const coordinate_type xl, const coordinate_type yl, const coordinate_type xh, const coordinate_type yh, + const coordinate_type site_width, const coordinate_type row_height, + const coordinate_type bin_size_x, const coordinate_type bin_size_y, + int content = ALL) + : m_x(x) + , m_y(y) + , m_node_size_x(node_size_x) + , m_node_size_y(node_size_y) + , m_pin_offset_x(pin_offset_x) + , m_pin_offset_y(pin_offset_y) + , m_pin2node_map(pin2node_map) + , m_num_nodes(num_nodes) + , m_num_movable_nodes(num_movable_nodes) + , m_num_filler_nodes(num_filler_nodes) + , m_num_pins(num_pins) + , m_xl(xl) + , m_yl(yl) + , m_xh(xh) + , m_yh(yh) + , m_site_width(site_width) + , m_row_height(row_height) + , m_bin_size_x(bin_size_x) + , m_bin_size_y(bin_size_y) + , m_content(content) + { + } + + bool run(std::string const& filename, FileFormat ff) const + { + dreamplacePrint(kINFO, "writing placement to %s\n", filename.c_str()); + bool flag = false; + + //PlaceDB const& placeDB = m_db.placeDB(); + + switch (ff) + { + case EPS: + case PDF: + case SVG: + case PNG: + flag = writeFig(filename.c_str(), 800, 800, ff); + break; + case GDSII: + flag = writeGdsii(filename); + break; + default: + dreamplacePrint(kERROR, "unknown writing format at line %u\n", __LINE__); + break; + } + + return flag; + } + + /// \param first and last mark nodes whose nets will be drawn + template + bool run(std::string const& filename, FileFormat ff, Iterator first, Iterator last) + { + m_sMarkNode.insert(first, last); + bool flag = run(filename, ff); + m_sMarkNode.clear(); + return flag; + } + protected: + /// write formats supported by cairo + /// \param width of screen + /// \param height of screen + void paintCairo(cairo_surface_t* cs, double width, double height) const + { +#if DRAWPLACE == 1 + double ratio[2] = { + width/(m_xh-m_xl), + height/(m_yh-m_yl) + }; + char buf[16]; + cairo_t *c; + cairo_text_extents_t extents; + + c=cairo_create(cs); + cairo_save(c); // save status + cairo_translate(c, 0-m_xl*ratio[0], height+m_yl*ratio[1]); // translate is additive + cairo_scale(c, ratio[0], -ratio[1]); // scale is additive + + // background + cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); + cairo_set_source_rgb(c, 1.0, 1.0, 1.0); + cairo_fill(c); + cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); + cairo_set_line_width(c, 0.001); + cairo_set_source_rgb(c, 0.1, 0.1, 0.1); + cairo_stroke(c); + + // bins + cairo_set_line_width(c, 0.001); + cairo_set_source_rgba(c, 0.1, 0.1, 0.1, 0.8); + for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) + { + cairo_move_to(c, bx, m_yl); + cairo_line_to(c, bx, m_yh); + cairo_stroke(c); + } + for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) + { + cairo_move_to(c, m_xl, by); + cairo_line_to(c, m_xh, by); + cairo_stroke(c); + } + + // nodes + cairo_set_line_width(c, 0.001); + cairo_select_font_face (c, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + if (m_content&NODE) + { + // fixed macro + for (int i = m_num_movable_nodes; i < m_num_nodes-m_num_filler_nodes; ++i) + { + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 1.0, 0.0, 0.0, 0.5); + cairo_fill(c); + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgb(c, 0.0, 0.0, 0.0); + cairo_stroke(c); + if (m_content&NODETEXT) + { + sprintf(buf, "%u", i); + cairo_set_font_size (c, m_node_size_y[i]/20); + cairo_text_extents (c, buf, &extents); + cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); + cairo_show_text(c, buf); + } + } + // filler + for (int i = m_num_nodes-m_num_filler_nodes; i < m_num_nodes; ++i) + { + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 115/255.0, 115/255.0, 125/255.0, 0.5); + cairo_fill(c); + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 100/255.0, 100/255.0, 100/255.0, 0.8); + cairo_stroke(c); + if (m_content&NODETEXT) + { + sprintf(buf, "%u", i); + cairo_set_font_size (c, m_node_size_y[i]/20); + cairo_text_extents (c, buf, &extents); + cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); + cairo_show_text(c, buf); + } + } + // movable + for (int i = 0; i < m_num_movable_nodes; ++i) + { + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 0, 0, 1, 0.5); + cairo_fill(c); + cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); + cairo_set_source_rgba(c, 0, 0, 0.8, 0.8); + cairo_stroke(c); + if (m_content&NODETEXT) + { + sprintf(buf, "%u", i); + cairo_set_font_size (c, m_node_size_y[i]/20); + cairo_text_extents (c, buf, &extents); + cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); + cairo_show_text(c, buf); + } + } + } + cairo_restore(c); + + cairo_show_page(c); + + cairo_destroy(c); +#else + dreamplacePrint(kWARN, "cs = %p, width = %g, height = %g are not used, as DRAWPLACE not enabled\n", cs, width, height); +#endif + } + bool writeFig(const char* fname, double width, double height, FileFormat ff) const + { +#if DRAWPLACE == 1 + cairo_surface_t *cs; + + switch (ff) + { + case PNG: + cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + break; + case PDF: + cs=cairo_pdf_surface_create(fname, width, height); + break; + case EPS: + cs=cairo_ps_surface_create(fname, width, height); + break; + case SVG: + cs=cairo_svg_surface_create(fname, width, height); + break; + default: + dreamplacePrint(kERROR, "unknown file format in %s\n", __func__); + return false; + } + + paintCairo(cs, width, height); + + cairo_surface_flush(cs); + // need additional writing call for PNG + if (ff == PNG) + cairo_surface_write_to_png(cs, fname); + cairo_surface_destroy(cs); + return true; +#else + dreamplacePrint(kWARN, "filename = %s, width = %g, height = %g, file format = %d not used, as DRAWPLACE not enabled\n", fname, width, height, (int)ff); + return false; +#endif + } + /// scale source coordinate to target screen + double scaleToScreen(double coord, double srcOffset, double srcSize, double tgtOffset, double tgtSize) const + { + double ratio = tgtSize/srcSize; + return tgtOffset + (coord-srcOffset)*ratio; + } + + /// write gdsii format + virtual bool writeGdsii(std::string const& filename) const + { + double scale_rato = 1000; + GdsParser::GdsWriter gw (filename.c_str()); + gw.create_lib("TOP", 0.001, 1e-6/scale_rato); + gw.gds_write_bgnstr(); + gw.gds_write_strname("TOP"); + + // kernel function to fill in contents + writeGdsiiContent(gw, scale_rato); + + gw.gds_write_endstr(); + gw.gds_write_endlib(); + + return true; + } + /// write contents to GDSII + virtual void writeGdsiiContent(GdsParser::GdsWriter& gw, double scale_rato) const + { + // layer specification + // it is better to use even layers, because text appears on odd layers + const unsigned dieAreaLayer = getLayer(true); + const unsigned rowLayer = getLayer(false); + const unsigned subRowLayer = getLayer(false); + const unsigned binRowLayer = getLayer(false); + const unsigned binLayer = getLayer(false); + const unsigned sbinLayer = getLayer(false); + const unsigned movableCellBboxLayer = getLayer(false); + const unsigned fixedCellBboxLayer = getLayer(false); + const unsigned blockageBboxLayer = getLayer(false); + const unsigned fillerCellBboxLayer = getLayer(false); + const unsigned pinLayer = getLayer(false); + const unsigned multiRowCellBboxLayer = getLayer(false); + const unsigned movePathLayer = getLayer(false); + const unsigned markedNodeLayer = getLayer(false); // together with netLayer + const unsigned netLayer = getLayer(false); + + dreamplacePrint(kINFO, "Layer: dieArea:%u, row:%u, subRow:%u, binRow:%u, bin:%u, sbin:%u, movableCellBbox:%u, fixedCellBbox:%u, blockageBbox:%u, fillerCellBboxLayer:%u, pin:%u, multiRowCellBbox:%u, movePathLayer:%u, markedNodeLayer:%u, net:from %u\n", + dieAreaLayer, rowLayer, subRowLayer, binRowLayer, binLayer, sbinLayer, movableCellBboxLayer, fixedCellBboxLayer, blockageBboxLayer, fillerCellBboxLayer, pinLayer, multiRowCellBboxLayer, movePathLayer, markedNodeLayer, netLayer); + + char buf[1024]; + + // write dieArea + gw.write_box(dieAreaLayer, 0, m_xl*scale_rato, m_yl*scale_rato, m_xh*scale_rato, m_yh*scale_rato); + // write bins + for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) + { + for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) + { + coordinate_type bxl = bx; + coordinate_type byl = by; + coordinate_type bxh = std::min(bxl+m_bin_size_x, m_xh); + coordinate_type byh = std::min(byl+m_bin_size_y, m_yh); + gw.write_box(binLayer, 0, bxl*scale_rato, byl*scale_rato, bxh*scale_rato, byh*scale_rato); + dreamplaceSPrint(kNONE, buf, "%u,%u", (unsigned int)round((bx-m_xl)/m_bin_size_x), (unsigned int)round((by-m_yl)/m_bin_size_y)); + gw.gds_create_text(buf, (bxl+bxh)/2*scale_rato, (byl+byh)/2*scale_rato, binLayer+1, 5); + } + } + // write cells + for (index_type i = 0; i < m_num_nodes; ++i) + { + // bounding box of cells and its name + coordinate_type node_xl = m_x[i]; + coordinate_type node_yl = m_y[i]; + coordinate_type node_xh = node_xl+m_node_size_x[i]; + coordinate_type node_yh = node_yl+m_node_size_y[i]; + unsigned layer; + if (i < m_num_movable_nodes) // movable cell + { + layer = movableCellBboxLayer; + } + else if (i >= m_num_nodes-m_num_filler_nodes) // filler cell + { + layer = fillerCellBboxLayer; + } + else // fixed cells + { + layer = fixedCellBboxLayer; + } + + if (layer == fixedCellBboxLayer || m_sMarkNode.empty()) // do not write cells if there are marked cells + { + gw.write_box(layer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); + dreamplaceSPrint(kNONE, buf, "(%u)%s", i, getTextOnNode(i).c_str()); + gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, layer+1, 5); + + if (i < m_num_movable_nodes && m_node_size_y[i] > m_row_height) // multi-row cell + { + gw.write_box(multiRowCellBboxLayer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); + gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, multiRowCellBboxLayer+1, 5); + } + } + if (m_sMarkNode.count(i)) // highlight marked nodes + { + gw.write_box(markedNodeLayer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); + dreamplaceSPrint(kNONE, buf, "(%u)%s", i, getTextOnNode(i).c_str()); + gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, markedNodeLayer+1, 5); + } + } + // write pins + for (index_type i = 0; i < m_num_pins; ++i) + { + coordinate_type pin_xl; + coordinate_type pin_yl; + coordinate_type pin_xh; + coordinate_type pin_yh; + getPinBbox(i, scale_rato, pin_xl, pin_yl, pin_xh, pin_yh); + // bounding box of pins and its macropin name + gw.write_box(pinLayer, 0, pin_xl*scale_rato, pin_yl*scale_rato, pin_xh*scale_rato, pin_yh*scale_rato); + gw.gds_create_text(getTextOnPin(i).c_str(), (pin_xl+pin_xh)/2*scale_rato, (pin_yl+pin_yh)/2*scale_rato, pinLayer+1, 5); + } + + } + /// automatically increment by 2 + /// \param reset controls whehter restart from 1 + unsigned getLayer(bool reset = false) const + { + static unsigned count = 0; + if (reset) + count = 0; + return (++count)<<1; + } + /// \param i node id + /// \return text to be shown on cell + std::string getTextOnNode(index_type i) const + { + return ""; + } + /// \param i pin id + /// \return text to be shown on pin + std::string getTextOnPin(index_type i) const + { + return "NA"; + } + /// \brief set pin bounding box + /// \param i pin id + void getPinBbox(index_type i, double scale_rato, coordinate_type& xl, coordinate_type& yl, coordinate_type& xh, coordinate_type& yh) const + { + index_type node_id = m_pin2node_map[i]; + coordinate_type x = m_x[node_id]; + coordinate_type y = m_y[node_id]; + coordinate_type offset_x = m_pin_offset_x[i]; + coordinate_type offset_y = m_pin_offset_y[i]; + coordinate_type pin_size = std::max(std::min(m_site_width, m_row_height)/10, (coordinate_type)(1.0/scale_rato)); + xl = x+offset_x-pin_size; + yl = y+offset_y-pin_size; + xh = x+offset_x+pin_size; + yh = y+offset_y+pin_size; + } + + const coordinate_type* m_x; + const coordinate_type* m_y; + const coordinate_type* m_node_size_x; + const coordinate_type* m_node_size_y; + const coordinate_type* m_pin_offset_x; + const coordinate_type* m_pin_offset_y; + const index_type* m_pin2node_map; + index_type m_num_nodes; + index_type m_num_movable_nodes; + index_type m_num_filler_nodes; + index_type m_num_pins; + coordinate_type m_xl; + coordinate_type m_yl; + coordinate_type m_xh; + coordinate_type m_yh; + coordinate_type m_site_width; + coordinate_type m_row_height; + coordinate_type m_bin_size_x; + coordinate_type m_bin_size_y; + std::set m_sMarkNode; ///< marked nodes whose net will be drawn + int m_content; ///< content for DrawContent +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp new file mode 100644 index 00000000..a0a0d76d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp @@ -0,0 +1,150 @@ +/** + * @file src/draw_place.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Plot placement to an image + */ +#include +#include "utility/src/torch.h" +#include "draw_place/src/draw_place.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief plot placement solution to an image +/// @param pos cell locations, array of x locations and then y locations +/// @param node_size_x_tensor cell width array +/// @param node_size_y_tensor cell height array +/// @param pin_offset_x pin offset to its cell origin +/// @param pin_offset_y pin offset to its cell origin +/// @param pin2node_map map pin to cell +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param site_width width of a placement site +/// @param row_height height of a placement row, same as height of a placement site +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param filename output image file name +int draw_place_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + double xl, + double yl, + double xh, + double yh, + double site_width, + double row_height, + double bin_size_x, + double bin_size_y, + int num_movable_nodes, + int num_filler_nodes, + const std::string& filename + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + int num_nodes = pos.numel()/2; + + // Call the kernel launcher + int ret = 0; + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "drawPlaceLauncher", [&] { + ret = drawPlaceLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + num_nodes, + num_movable_nodes, + num_filler_nodes, + pin2node_map.numel(), + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + filename + ); + }); + + return ret; +} + +int draw_place_fpga( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_physical_nodes, + int num_filler_nodes, + at::Tensor node2fence_region_map, + at::Tensor is_cc_node, + int ffIdx, + int lutIdx, + int addIdx, + int bramIdx, + int m9kIdx, + int m144kIdx, + int dspIdx, + int ioIdx, + int pllIdx, + const std::string& filename + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + int num_nodes = pos.numel()/2; + + // Call the kernel launcher + int ret = 0; + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "drawPlaceLauncherFPGA", [&] { + ret = drawPlaceLauncherFPGA( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + num_nodes, + num_physical_nodes, + num_filler_nodes, + pin2node_map.numel(), + xl, yl, xh, yh, + bin_size_x, bin_size_y, + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(is_cc_node, int), + ffIdx, lutIdx, addIdx, + bramIdx, m9kIdx, m144kIdx, + dspIdx, + ioIdx, pllIdx, + filename + ); + }); + + return ret; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::draw_place_forward, "Draw place forward"); + m.def("fpga", &DREAMPLACE_NAMESPACE::draw_place_fpga, "Draw place FPGA"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h new file mode 100644 index 00000000..32c99929 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h @@ -0,0 +1,132 @@ +/** + * @file draw_place.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ +#ifndef DREAMPLACE_DRAW_PLACE_H +#define DREAMPLACE_DRAW_PLACE_H + +#include "draw_place/src/PlaceDrawer.h" +#include "draw_place/src/PlaceDrawFPGA.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int drawPlaceLauncher( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* pin_offset_x_tensor, const T* pin_offset_y_tensor, + const int* pin2node_map_tensor, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes, + const int num_pins, + const T xl, const T yl, const T xh, const T yh, + const T site_width, const T row_height, + const T bin_size_x, const T bin_size_y, + const std::string& filename + ) +{ + PlaceDrawer drawer ( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + pin_offset_x_tensor, pin_offset_y_tensor, + pin2node_map_tensor, + num_nodes, + num_movable_nodes, + num_filler_nodes, + num_pins, + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y + ); + typename PlaceDrawer::FileFormat ff; + if (filename.substr(filename.size()-4) == ".eps") + { + ff = PlaceDrawer::EPS; + } + else if (filename.substr(filename.size()-4) == ".pdf") + { + ff = PlaceDrawer::PDF; + } + else if (filename.substr(filename.size()-4) == ".svg") + { + ff = PlaceDrawer::SVG; + } + else if (filename.substr(filename.size()-4) == ".png") + { + ff = PlaceDrawer::PNG; + } + else + { + ff = PlaceDrawer::GDSII; + } + return drawer.run(filename, ff); +} + +template +int drawPlaceLauncherFPGA( + const T* x_tensor, const T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T* pin_offset_x_tensor, const T* pin_offset_y_tensor, + const int* pin2node_map_tensor, + const int num_nodes, + const int num_physical_nodes, + const int num_filler_nodes, + const int num_pins, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, + const int* node2fence_region_map, + const int* is_cc_node, + const int ffIdx, + const int lutIdx, + const int addIdx, + const int bramIdx, + const int m9kIdx, + const int m144kIdx, + const int dspIdx, + const int ioIdx, + const int pllIdx, + const std::string& filename + ) +{ + PlaceDrawFPGA drawer ( + x_tensor, y_tensor, + node_size_x_tensor, node_size_y_tensor, + pin_offset_x_tensor, pin_offset_y_tensor, + pin2node_map_tensor, + num_nodes, + num_physical_nodes, + num_filler_nodes, + num_pins, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + node2fence_region_map, + is_cc_node, + ffIdx, lutIdx, addIdx, + bramIdx, m9kIdx, m144kIdx, + dspIdx, + ioIdx, pllIdx + ); + typename PlaceDrawFPGA::FileFormat ff; + if (filename.substr(filename.size()-4) == ".eps") + { + ff = PlaceDrawFPGA::EPS; + } + else if (filename.substr(filename.size()-4) == ".pdf") + { + ff = PlaceDrawFPGA::PDF; + } + else if (filename.substr(filename.size()-4) == ".svg") + { + ff = PlaceDrawFPGA::SVG; + } + else { + ff = PlaceDrawFPGA::PNG; + } + return drawer.run(filename, ff); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt new file mode 100644 index 00000000..5643fb81 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt @@ -0,0 +1,22 @@ +set(OP_NAME dsp_ram_legalization) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${LIMBO_SOURCE_DIR} + ${LEMON_INCLUDE_DIRS} + ) + +set(LINK_LIBS utility_cxx + lemon + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(legalize_cpp + src/legalize.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS} + ) + +install(TARGETS legalize_cpp DESTINATION dreamplacefpga/ops/${OP_NAME}) +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py new file mode 100644 index 00000000..e3e3fd7a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @Date: Oct 2020 +# + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py new file mode 100644 index 00000000..2741f487 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py @@ -0,0 +1,109 @@ +''' +@File: dsp_ram_legalization.py +@Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) +@Date: May 2023 +''' +import math +import torch +from torch import nn +from torch.autograd import Function +import numpy as np +import pdb + +import dreamplacefpga.ops.dsp_ram_legalization.legalize_cpp as legalize_cpp +import dreamplacefpga.configure as configure + +import logging +logger = logging.getLogger(__name__) + +class LegalizeDSPRAMFunction(Function): + @staticmethod + def legalize(pos, placedb, region_id, model): + """ + @brief legalize DSP/RAM at the end of Global Placement + @param pos X/Y locations of all instances locX ndarray + @param placedb Placement Database + @param region_id Instance type identifier + @param model Use for region mask and wirelength preconditioner + @param num_nodes Instance count + @param num_sites Instance site count + @param sites Instance site ndarray + @param precondWL Instance wirelength preconditioner ndarray + @param dInit lg_max_dist_init + @param dIncr lg_max_dist_incr + @param fScale lg_flow_cost_scale + @param movVal Maximum & Average Instance movement (list) + @param outLoc Legalized Instance locations list - {x0, x1, ... xn, y0, y1, ... yn} + """ + lg_max_dist_init=10.0 + lg_max_dist_incr=10.0 + lg_flow_cost_scale=100.0 + numNodes = pos.numel()//2 + comp_id = placedb.rsrc2compId_map[region_id] + num_inst = placedb.num_movable_nodes_fence_region[comp_id] + outLoc = np.zeros(2*num_inst, dtype=np.float32).tolist() + + if region_id == placedb.rDSPIdx: + mask = model.data_collections.dsp_mask + sites = placedb.dspSiteXYs + else: + if region_id == placedb.rBRAMIdx or region_id == placedb.rM9KIdx: + mask = model.data_collections.ram0_mask + sites = placedb.ramSite0XYs + elif region_id == placedb.rM144KIdx: + mask = model.data_collections.ram1_mask + sites = placedb.ramSite1XYs + + locX = pos[:placedb.num_physical_nodes][mask].cpu().detach().numpy() + locY = pos[numNodes:numNodes+placedb.num_physical_nodes][mask].cpu().detach().numpy() + + num_sites = len(sites) + precondWL = model.precondWL[:placedb.num_physical_nodes][mask].cpu().detach().numpy() + movVal = np.zeros(2, dtype=np.float32).tolist() + + #Use auction algorithm + if placedb.sliceFF_ctrl_mode != "HALF" or num_inst > 0.8*num_sites: + # Assign num_sites as N for auction algorithm that employs an N->N mapping + cost = torch.ones(num_sites*num_sites, dtype=pos.dtype, device=pos.device) + cost *= -10000.0 + locations = torch.ones(num_sites, dtype=torch.int, device=pos.device) + locations *= -1 + displacement = torch.zeros(num_inst, dtype=pos.dtype, device=pos.device) + lg_sites = torch.from_numpy(sites.flatten()).to(pos.device) + posX = pos[:placedb.num_physical_nodes][mask].data + posY = pos[numNodes:numNodes+placedb.num_physical_nodes][mask].data + precond = model.precondWL[:placedb.num_physical_nodes][mask] + diff = num_sites - num_inst + if diff > 0: + tmp_diff = torch.zeros(diff, dtype=pos.dtype, device=pos.device) + posX = torch.cat((posX, tmp_diff), 0) + posY = torch.cat((posY, tmp_diff), 0) + tmp_diff += 1.0 + precond = torch.cat((precond, tmp_diff), 0) + + if pos.is_cuda: + cpu_locations = locations.cpu() + cpu_displacement = displacement.cpu() + legalize_cpp.legalize_auction(posX.cpu(), posY.cpu(), lg_sites.cpu(), + precond.cpu(), num_inst, num_sites, cost.cpu(), cpu_displacement, + cpu_locations) + locations.data.copy_(cpu_locations.data) + displacement.data.copy_(cpu_displacement.data) + else: + legalize_cpp.legalize_auction(posX, posY, lg_sites, precond, + num_inst, num_sites, cost, displacement, locations) + + outLoc[:num_inst] = sites[locations[:num_inst].cpu().detach().numpy()][:,0] + outLoc[num_inst:] = sites[locations[:num_inst].cpu().detach().numpy()][:,1] + outLoc = np.array(outLoc) + movVal[0] = displacement.max().item() + movVal[1] = displacement.mean().item() + else: + legalize_cpp.legalize(locX, locY, num_inst, num_sites, sites.flatten(), precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) + outLoc = np.array(outLoc) + + updLoc = torch.from_numpy(outLoc).to(dtype=pos.dtype, device=pos.device) + pos.data[:placedb.num_physical_nodes].masked_scatter_(mask, updLoc[:num_inst]) + pos.data[numNodes:numNodes+placedb.num_physical_nodes].masked_scatter_(mask, updLoc[num_inst:]) + + return movVal diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp new file mode 100644 index 00000000..d75daab8 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp @@ -0,0 +1,271 @@ +/** + * @file dsp_ram_legalization.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Legalize DSP/RAM instances at the end of Global Placement. + */ +#include +#include +#include +#include + +#include +#include +#include +#include "utility/src/utils.h" +#include "utility/src/torch.h" +// Lemon for min cost flow +#include "lemon/list_graph.h" +#include "lemon/network_simplex.h" +#include "lemon/cost_scaling.h" +// local dependency +#include "dsp_ram_legalization/src/legalize_auction.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define INVALID -1 +#define AUCTION_MAX_EPS 5.0 // Larger values mean solution is more approximate +#define AUCTION_MIN_EPS 1.0 +#define AUCTION_FACTOR 0.1 +#define AUCTION_MAX_ITERS 9999 + +//Min-cost flow to legalize DSPs/RAMs +void legalize( + pybind11::array_t const& locX, + pybind11::array_t const& locY, + int const num_nodes, int const num_sites, + pybind11::array_t const& sites, + pybind11::array_t const& precond, + double const &lg_max_dist_init, double const &lg_max_dist_incr, + double const &lg_flow_cost_scale, pybind11::list &movVal, pybind11::list &out) +{ + typedef lemon::ListDigraph graphType; + graphType graph; + graphType::ArcMap capLo(graph); + graphType::ArcMap capHi(graph); + graphType::ArcMap cost(graph); + std::vector lNodes, rNodes; + std::vector lArcs, rArcs, mArcs; + std::vector> mArcPairs; + + //Source and target Nodes + graphType::Node s = graph.addNode(), t = graph.addNode(); + + //Add left nodes (blocks) and arcs between source node and left nodes + for (int i = 0; i < num_nodes; ++i) + { + lNodes.emplace_back(graph.addNode()); + lArcs.emplace_back(graph.addArc(s, lNodes.back())); + cost[lArcs.back()] = 0.0; + capLo[lArcs.back()] = 0.0; + capHi[lArcs.back()] = 1.0; + } + + //Add right nodes (sites) and arc between right nodes and target node + for (int j=0; j < num_sites; ++j) + { + rNodes.emplace_back(graph.addNode()); + rArcs.emplace_back(graph.addArc(rNodes.back(), t)); + cost[rArcs.back()] = 0.0; + capLo[rArcs.back()] = 0.0; + capHi[rArcs.back()] = 1.0; + } + + //To improve efficiency, we do not run matching for complete bipartite graph but incrementally add arcs when needed + double distMin = 0.0; + double distMax = lg_max_dist_init; + + while (true) + { + //Generate arcs between left (blocks) and right (sites) nodes, pruning based on distance + for (int blk = 0; blk < num_nodes; ++blk) + { + for (int st = 0; st < num_sites; ++st) + { + double dist = std::abs(locX.at(blk) - sites.at(st*2)) + std::abs(locY.at(blk) - sites.at(st*2+1)); + if (dist >= distMin && dist < distMax) + { + mArcs.emplace_back(graph.addArc(lNodes[blk], rNodes[st])); + mArcPairs.emplace_back(blk, st); + double mArcCost = dist * precond.at(blk) * lg_flow_cost_scale; + cost[mArcs.back()] = mArcCost; + capLo[mArcs.back()] = 0.0; + capHi[mArcs.back()] = 1.0; + } + } + } + + //Run min-cost flow + lemon::NetworkSimplex mcf(graph); + mcf.stSupply(s, t, num_nodes); + mcf.lowerMap(capLo).upperMap(capHi).costMap(cost); + mcf.run(); + + //A feasible solution must have flow size equal to the no of blocks + //If not, we need to increase the max distance constraint + double flowSize = 0.0; + for (const auto &arc : rArcs) + { + flowSize += mcf.flow(arc); + } + if (flowSize != num_nodes) + { + //Increase searching range + distMin = distMax; + distMax += lg_max_dist_incr; + continue; + } + ////DBG + //std::cout << "INFO: Search range min: 0 and max: " << distMax + // << " for " << num_nodes << " instances and " << num_sites + // << " sites with " << mArcs.size() << " arcs" < +int auctionAlgorithmLauncher( + const T* locX, + const T* locY, + const T* sites, + const T* precond, + const int num_nodes, + const int num_sites, + T* cost, + T* displacements, + int* outLoc) +{ + //Populate cost array + for (int blk = 0; blk < num_nodes; ++blk) + { + for (int st = 0; st < num_sites; ++st) + { + T dist = std::abs(locX[blk] - sites[st*2]) + std::abs(locY[blk] - sites[st*2+1]); + T mArcCost = dist * precond[blk]; + unsigned index = blk*num_sites+ st; + cost[index] = mArcCost; + } + } + + std::vector m_matrix; + std::vector m_item2person; + std::vector m_bids; + std::vector m_prices; + std::vector m_sbids; + + unsigned nn = num_sites*num_sites; + + m_matrix.resize(nn); + m_item2person.resize(num_sites); + m_bids.resize(nn); + m_prices.resize(num_sites); + m_sbids.resize(num_sites); + + std::copy(cost, cost+nn, m_matrix.data()); + + int ret = run_auction( + num_nodes, + num_sites, + m_matrix.data(), + outLoc, + AUCTION_MAX_EPS, + AUCTION_MIN_EPS, + AUCTION_FACTOR, + AUCTION_MAX_ITERS, + m_item2person.data(), + m_bids.data(), + m_prices.data(), + m_sbids.data() + ); + + ////Collect node displacements + for (int idx = 0; idx < num_nodes; ++idx) + { + int sId = outLoc[idx]; + if (sId != INVALID) + { + displacements[idx] = std::abs(locX[idx] - sites[sId*2]) + std::abs(locY[idx] - sites[sId*2+1]); + } + } + + return ret; +} + + +void legalize_auction( + at::Tensor posX, at::Tensor posY, at::Tensor sites, + at::Tensor precond, int num_nodes, int num_sites, + at::Tensor cost, at::Tensor displacements, + at::Tensor locations) +{ + CHECK_FLAT_CPU(posX); + CHECK_CONTIGUOUS(posX); + + CHECK_FLAT_CPU(posY); + CHECK_CONTIGUOUS(posY); + + CHECK_FLAT_CPU(sites); + CHECK_EVEN(sites); + CHECK_CONTIGUOUS(sites); + + CHECK_FLAT_CPU(precond); + CHECK_CONTIGUOUS(precond); + + CHECK_FLAT_CPU(cost); + CHECK_CONTIGUOUS(cost); + + ////DBG + //CPUTimer::hr_clock_rep timer_start, timer_stop; + //timer_start = CPUTimer::getGlobaltime(); + ////DBG + + DREAMPLACE_DISPATCH_FLOATING_TYPES(posX, "auctionAlgorithmLauncher", [&] { + auctionAlgorithmLauncher( + DREAMPLACE_TENSOR_DATA_PTR(posX, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(posY, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(sites, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(precond, scalar_t), + num_nodes, num_sites, + DREAMPLACE_TENSOR_DATA_PTR(cost, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(displacements, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(locations, int)); + }); + + ////DBG + //timer_stop = CPUTimer::getGlobaltime(); + //dreamplacePrint(kINFO, "Legalize using Auction Algorithm takes %g ms\n", + // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); + ////DBG +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("legalize", &DREAMPLACE_NAMESPACE::legalize, "Legalize DSP & RAM instances using Min-Cost Flow"); + m.def("legalize_auction", &DREAMPLACE_NAMESPACE::legalize_auction, "Legalize using Auction Algorithm"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h new file mode 100644 index 00000000..f8541e8f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h @@ -0,0 +1,164 @@ +/** + * @file legalize_auction.h + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Jul 2023 + */ +#ifndef _DREAMPLACE_LEGALIZE_AUCTION_H +#define _DREAMPLACE_LEGALIZE_AUCTION_H + +#include +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +#define INVALID -1 +#define BIG_POSITIVE 9999999 + +/// Auction Algorithm +template +int run_auction( + int num_nodes, + int num_sites, + T* data_ptr, // data, num_sites*num_sites in row-major + int* person2item_ptr, // results + float auction_max_eps, + float auction_min_eps, + float auction_factor, + int auction_max_iters, + int* item2person_ptr=nullptr, + T* bids_ptr=nullptr, + T* prices_ptr=nullptr, + int* sbids_ptr=nullptr +) +{ + // Declare variables + bool allocate_flag = false; + if (!item2person_ptr) + { + item2person_ptr = (int*)malloc(num_sites * sizeof(int)); + bids_ptr = (T*)malloc(num_sites * num_sites * sizeof(T)); + prices_ptr = (T*)malloc(num_sites * sizeof(T)); + sbids_ptr = (int*)malloc(num_sites * sizeof(int)); + allocate_flag = true; + } + + T *data = data_ptr; + int *person2item = person2item_ptr; + int *item2person = item2person_ptr; + T *prices = prices_ptr; + int *sbids = sbids_ptr; + T *bids = bids_ptr; + int num_assigned = 0; + + for(int i = 0; i < num_sites; i++) { + prices[i] = 0.0; + person2item[i] = INVALID; + } + + float auction_eps = auction_max_eps; + int counter = 0; + while(auction_eps >= auction_min_eps && counter < auction_max_iters) { + for(int i = 0; i < num_sites; i++) { + person2item[i] = INVALID; + item2person[i] = INVALID; + } + num_assigned = 0; + + while(num_assigned < num_nodes && counter < auction_max_iters){ + counter += 1; + + std::memset(bids, BIG_POSITIVE, num_sites * num_sites * sizeof(T)); + std::memset(sbids, 0, num_sites * sizeof(int)); + + for(int i = 0; i < num_nodes; i++) { + if(person2item[i] == INVALID) { + T top1_val = BIG_POSITIVE; + T top2_val = BIG_POSITIVE; + int top1_col = BIG_POSITIVE; + T tmp_val = BIG_POSITIVE; + + for (int col = 0; col < num_sites; col++) + { + tmp_val = data[i * num_sites+ col]; + if (tmp_val < 0) + { + continue; + } + tmp_val = tmp_val + prices[col]; + if (tmp_val < top1_val) + { + top2_val = top1_val; + top1_col = col; + top1_val = tmp_val; + } + else if (tmp_val <= top2_val) + { + top2_val = tmp_val; + } + } + if (top2_val == BIG_POSITIVE) + { + top2_val = top1_val; + } + T bid = top1_val + auction_eps; + bids[i*num_sites + top1_col] = bid; + sbids[top1_col] = 1; + } + } + + for(int j = 0; j < num_sites; j++) { + if(sbids[j] != 0) { + T low_bid = BIG_POSITIVE; + int low_bidder = INVALID; + + T tmp_bid = BIG_POSITIVE; + for(int i = 0; i < num_nodes; i++){ + tmp_bid = bids[i*num_sites + j]; + if(tmp_bid < low_bid){ + low_bid = tmp_bid; + low_bidder = i; + } + } + + int current_person = item2person[j]; + if(current_person >= 0){ + person2item[current_person] = INVALID; + } else { + num_assigned++; + } + + prices[j] += low_bid; + person2item[low_bidder] = j; + item2person[j] = low_bidder; + } + } + } + + auction_eps *= auction_factor; + } + + ////DBG + ////Print results + //int score = 0; + //for (int i = 0; i < num_nodes; i++) { + // std::cout << i << " " << person2item[i] << std::endl; + // score += data[i * num_sites + person2item[i]]; + //} + //std::cout << "score=" <= num_nodes); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore new file mode 100644 index 00000000..80a85ad9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore @@ -0,0 +1,2 @@ +*.csv +*.png diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt new file mode 100644 index 00000000..0ea7524f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt @@ -0,0 +1,38 @@ +set(OP_NAME electric_potential) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/electric_density_map.cpp + src/electric_force.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/electric_density_map_cuda.cpp + src/electric_density_map_cuda_kernel.cu + src/electric_force_cuda.cpp + src/electric_force_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py new file mode 100644 index 00000000..28c21fad --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py @@ -0,0 +1,210 @@ +## +# @file electric_overflow.py +# @Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @Date: Oct 2020 +# + +import math +import numpy as np +import torch +from torch import nn +from torch.autograd import Function +from torch.nn import functional as F + +import dreamplacefpga.ops.electric_potential.electric_potential_cpp as electric_potential_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.electric_potential.electric_potential_cuda as electric_potential_cuda + +import pdb +import matplotlib +matplotlib.use('Agg') +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + + +class ElectricDensityMapFunction(Function): + """ + @brief compute density overflow. + @param ctx pytorch API to store data for backward proporgation + @param pos location of cells, x and then y + @param node_size_x_clamped stretched size, max(bin_size*sqrt2, node_size) + @param node_size_y_clamped stretched size, max(bin_size*sqrt2, node_size) + @param offset_x (stretched size - node_size) / 2 + @param offset_y (stretched size - node_size) / 2 + @param ratio node_size_x * node_size_y for FPGA + @param initial_density_map density_map for fixed cells + @param target_density target density + @param xl left boundary + @param yl lower boundary + @param xh right boundary + @param yh upper boundary + @param bin_size_x bin width + @param bin_size_x bin height + @param num_movable_nodes number of movable cells + @param num_filler_nodes number of filler cells + @param padding bin padding to boundary of placement region + @param padding_mask padding mask with 0 and 1 to indicate padding bins with padding regions to be 1 + @param num_bins_x number of bins in horizontal direction + @param num_bins_y number of bins in vertical direction + @param num_movable_impacted_bins_x number of impacted bins for any movable cell in x direction + @param num_movable_impacted_bins_y number of impacted bins for any movable cell in y direction + @param num_filler_impacted_bins_x number of impacted bins for any filler cell in x direction + @param num_filler_impacted_bins_y number of impacted bins for any filler cell in y direction + @param sorted_node_map the indices of the movable node map + """ + @staticmethod + def forward( + pos, + node_size_x_clamped, + node_size_y_clamped, + offset_x, + offset_y, + ratio, + initial_density_map, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_filler_nodes, + num_bins_x, + num_bins_y, + deterministic_flag, + sorted_node_map, + stretchRatio): + + ##Rachel: Incorporate stretch ratio as a variable as it differs for Density/OVFL computation in FPGA + targetHalfSizeX = 0.5 * stretchRatio * bin_size_x + targetHalfSizeY = 0.5 * stretchRatio * bin_size_y + + if pos.is_cuda: + output = electric_potential_cuda.density_map_fpga( + pos.view(pos.numel()), node_size_x_clamped, + node_size_y_clamped, offset_x, offset_y, ratio.mul(0.25), + initial_density_map, xl, yl, xh, + yh, bin_size_x, bin_size_y, num_movable_nodes, + num_filler_nodes, num_bins_x, num_bins_y, + deterministic_flag, sorted_node_map, targetHalfSizeX, targetHalfSizeY) + else: + output = electric_potential_cpp.density_map_fpga( + pos.view(pos.numel()), node_size_x_clamped, + node_size_y_clamped, offset_x, offset_y, ratio.mul(0.25), + initial_density_map, xl, yl, xh, yh, bin_size_x, bin_size_y, + targetHalfSizeX, targetHalfSizeY, num_movable_nodes, + num_filler_nodes, num_bins_x, num_bins_y, + deterministic_flag) + + density_map = output.view([num_bins_x, num_bins_y]) + + return density_map + + +class ElectricOverflow(nn.Module): + def __init__( + self, + node_size_x, + node_size_y, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_terminals, + num_filler_nodes, + deterministic_flag, # control whether to use deterministic routine + sorted_node_map, + fence_regions=None, + stretchRatio=None): + super(ElectricOverflow, self).__init__() + self.node_size_x = node_size_x + self.node_size_y = node_size_y + + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.bin_size_x = bin_size_x + self.bin_size_y = bin_size_y + self.num_movable_nodes = num_movable_nodes + self.num_terminals = 0 + self.num_filler_nodes = num_filler_nodes + self.sorted_node_map = sorted_node_map + + self.deterministic_flag = deterministic_flag + #Rachel: Include explicit stretchRatio as it varies for DSP/RAM for Density and OVFL computation + self.stretchRatio = stretchRatio + + self.reset() + # initial density_map due to fixed cells + if fence_regions is not None: + self.initial_density_map = fence_regions + + def reset(self, data_collections=None): + sqrt2 = math.sqrt(2) + # clamped means stretch a cell to bin size + # clamped = max(bin_size*sqrt2, node_size) + # offset means half of the stretch size + # ratio means the original area over the stretched area + self.node_size_x_clamped = self.node_size_x + self.offset_x = self.node_size_x_clamped.mul(0.5) + self.node_size_y_clamped = self.node_size_y + self.offset_y = self.node_size_y_clamped.mul(0.5) + self.ratio = self.node_size_x_clamped * self.node_size_y_clamped + + # compute maximum impacted bins + self.num_bins_x = int(math.ceil((self.xh - self.xl) / self.bin_size_x)) + self.num_bins_y = int(math.ceil((self.yh - self.yl) / self.bin_size_y)) + + def forward(self, pos): + if self.initial_density_map is None: + pdb.set_trace() #Rachel: Should not reach here for FPGA + + density_map = ElectricDensityMapFunction.forward( + pos, self.node_size_x_clamped, self.node_size_y_clamped, + self.offset_x, self.offset_y, self.ratio, + self.initial_density_map, + self.xl, self.yl, self.xh, self.yh, self.bin_size_x, + self.bin_size_y, self.num_movable_nodes, self.num_filler_nodes, + self.num_bins_x, self.num_bins_y, + self.deterministic_flag, self.sorted_node_map, self.stretchRatio) + + bin_area = self.bin_size_x * self.bin_size_y + density_cost = (density_map - + self.target_density * bin_area).clamp_(min=0.0).sum().unsqueeze(0) + + return density_cost, density_map.max().unsqueeze(0) / bin_area + + +def plot(plot_count, density_map, padding, name): + """ + density map contour and heat map + """ + density_map = density_map[padding:density_map.shape[0] - padding, + padding:density_map.shape[1] - padding] + print("max density = %g @ %s" % + (np.amax(density_map), + np.unravel_index(np.argmax(density_map), density_map.shape))) + print("mean density = %g" % (np.mean(density_map))) + + fig = plt.figure() + ax = fig.gca(projection='3d') + + x = np.arange(density_map.shape[0]) + y = np.arange(density_map.shape[1]) + + x, y = np.meshgrid(x, y) + # looks like x and y should be swapped + ax.plot_surface(y, x, density_map, alpha=0.8) + + ax.set_xlabel('x') + ax.set_ylabel('y') + ax.set_zlabel('density') + + # plt.tight_layout() + plt.savefig(name + ".3d.png") + plt.close() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py new file mode 100644 index 00000000..5269cefe --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py @@ -0,0 +1,430 @@ +## +# @file electric_potential.py +# @Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @Date: Oct 2020 +# @brief electric potential according to e-place (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) +# + +import os +import sys +import math +import numpy as np +import time +import torch +from torch import nn +from torch.autograd import Function +from torch.nn import functional as F +import logging + +import dreamplacefpga.ops.dct.discrete_spectral_transform as discrete_spectral_transform + +import dreamplacefpga.ops.dct.dct2_fft2 as dct +from dreamplacefpga.ops.dct.discrete_spectral_transform import get_exact_expk as precompute_expk + +from dreamplacefpga.ops.electric_potential.electric_overflow import ElectricDensityMapFunction as ElectricDensityMapFunction +from dreamplacefpga.ops.electric_potential.electric_overflow import ElectricOverflow as ElectricOverflow + +import dreamplacefpga.ops.electric_potential.electric_potential_cpp as electric_potential_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.electric_potential.electric_potential_cuda as electric_potential_cuda + +import pdb +import matplotlib +matplotlib.use('Agg') + +logger = logging.getLogger(__name__) + +# global variable for plot +plot_count = 0 + + +class ElectricPotentialFunction(Function): + """ + @brief compute electric potential according to e-place. + """ + @staticmethod + def forward( + ctx, + pos, + node_size_x_clamped, + node_size_y_clamped, + offset_x, + offset_y, + ratio, + initial_density_map, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_filler_nodes, + num_bins_x, + num_bins_y, + deterministic_flag, + sorted_node_map, + exact_expkM=None, # exp(-j*pi*k/M) + exact_expkN=None, # exp(-j*pi*k/N) + inv_wu2_plus_wv2=None, # 1.0/(wu^2 + wv^2) + wu_by_wu2_plus_wv2_half=None, # wu/(wu^2 + wv^2)/2 + wv_by_wu2_plus_wv2_half=None, # wv/(wu^2 + wv^2)/2 + dct2=None, + idct2=None, + idct_idxst=None, + idxst_idct=None, + stretchRatio=None, + lock_flag=None + ): + + tt = time.time() + + # output consists of (density_cost, density_map, max_density) + ctx.node_size_x_clamped = node_size_x_clamped + ctx.node_size_y_clamped = node_size_y_clamped + ctx.offset_x = offset_x + ctx.offset_y = offset_y + ctx.ratio = ratio + ctx.xl = xl + ctx.yl = yl + ctx.xh = xh + ctx.yh = yh + ctx.bin_size_x = bin_size_x + ctx.bin_size_y = bin_size_y + ctx.num_movable_nodes = num_movable_nodes + ctx.num_filler_nodes = num_filler_nodes + ctx.num_bins_x = num_bins_x + ctx.num_bins_y = num_bins_y + ctx.pos = pos + ctx.sorted_node_map = sorted_node_map + ctx.stretchRatio = stretchRatio + + if lock_flag is None: + ctx.lock_flag = False + else: + ctx.lock_flag = lock_flag + #Return zero if there are no elements in this resourceType + if (num_movable_nodes == 0 and num_filler_nodes == 0) or ctx.lock_flag: + return torch.tensor(0, dtype=pos.dtype, device=pos.device) + + #If filler sizes become zero due to instance adjust area + if node_size_x_clamped[-1] == 0 or node_size_y_clamped[-1] == 0: + density_map = ElectricDensityMapFunction.forward( + pos, node_size_x_clamped[:num_movable_nodes], node_size_y_clamped[:num_movable_nodes], + offset_x[:num_movable_nodes], offset_y[:num_movable_nodes], + ratio[:num_movable_nodes], + initial_density_map, + xl, yl, xh, yh, bin_size_x, bin_size_y, + num_movable_nodes, 0, + num_bins_x, num_bins_y, + deterministic_flag, sorted_node_map, stretchRatio) + else: + density_map = ElectricDensityMapFunction.forward( + pos, node_size_x_clamped, node_size_y_clamped, offset_x, offset_y, + ratio, + initial_density_map, + xl, yl, xh, yh, bin_size_x, bin_size_y, + num_movable_nodes, num_filler_nodes, + num_bins_x, num_bins_y, + deterministic_flag, sorted_node_map, stretchRatio) + + # for DCT + M = num_bins_x + N = num_bins_y + + # wu and wv + if inv_wu2_plus_wv2 is None: + wu = torch.arange(M, + dtype=density_map.dtype, + device=density_map.device).mul(2 * np.pi / + M).view([M, 1]) + wv = torch.arange(N, + dtype=density_map.dtype, + device=density_map.device).mul(2 * np.pi / + N).view([1, N]) + wu2_plus_wv2 = wu.pow(2) + wv.pow(2) + wu2_plus_wv2[0, + 0] = 1.0 # avoid zero-division, it will be zeroed out + inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 + inv_wu2_plus_wv2[0, 0] = 0.0 + wu_by_wu2_plus_wv2_half = wu.mul(inv_wu2_plus_wv2).mul_(1. / 2) + wv_by_wu2_plus_wv2_half = wv.mul(inv_wu2_plus_wv2).mul_(1. / 2) + + # compute auv + density_map.mul_(1.0 / (ctx.bin_size_x * ctx.bin_size_y)) + + #auv = discrete_spectral_transform.dct2_2N(density_map, expk0=exact_expkM, expk1=exact_expkN) + auv = dct2.forward(density_map) + + # compute field xi + auv_by_wu2_plus_wv2_wu = auv.mul(wu_by_wu2_plus_wv2_half) + auv_by_wu2_plus_wv2_wv = auv.mul(wv_by_wu2_plus_wv2_half) + + ctx.field_map_x = idxst_idct.forward(auv_by_wu2_plus_wv2_wu) + + ctx.field_map_y = idct_idxst.forward(auv_by_wu2_plus_wv2_wv) + + auv_by_wu2_plus_wv2 = auv.mul(inv_wu2_plus_wv2) + + potential_map = idct2.forward(auv_by_wu2_plus_wv2) + + # compute energy + energy = potential_map.mul(density_map).sum() + return energy + + @staticmethod + def backward(ctx, grad_pos): + tt = time.time() + + #Return zero if there are no elements in this resourceType + if (ctx.num_movable_nodes == 0 and ctx.num_filler_nodes == 0) or ctx.lock_flag: + return None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None + + if grad_pos.is_cuda: + output = -electric_potential_cuda.electric_force_fpga( + grad_pos, ctx.num_bins_x, ctx.num_bins_y, + ctx.field_map_x.view([-1]), ctx.field_map_y.view([-1]), + ctx.pos, ctx.node_size_x_clamped, + ctx.node_size_y_clamped, + ctx.bin_size_x, ctx.bin_size_y, ctx.num_movable_nodes, + ctx.num_filler_nodes, ctx.sorted_node_map) + else: + output = -electric_potential_cpp.electric_force_fpga( + grad_pos, ctx.num_bins_x, ctx.num_bins_y, + ctx.field_map_x.view([-1]), ctx.field_map_y.view( + [-1]), ctx.pos, ctx.node_size_x_clamped, + ctx.node_size_y_clamped, + ctx.ratio, + ctx.bin_size_x, ctx.bin_size_y, ctx.num_movable_nodes, + ctx.num_filler_nodes) + return output, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None, None, \ + None, None, None + + +class ElectricPotential(ElectricOverflow): + """ + @brief Compute electric potential according to e-place + """ + def __init__( + self, + node_size_x, + node_size_y, + xl, + yl, + xh, + yh, + bin_size_x, + bin_size_y, + num_movable_nodes, + num_terminals, + num_filler_nodes, + deterministic_flag, # control whether to use deterministic routine + sorted_node_map, + region_id=None, + fence_regions=None, # [n_subregion, 4] as dummy macros added to initial density. (xl,yl,xh,yh) rectangles + node2fence_region_map=None, + placedb=None, + stretchRatio=None + ): + """ + @brief initialization + Be aware that all scalars must be python type instead of tensors. + Otherwise, GPU version can be weirdly slow. + @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order + @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order + @param movable_macro_mask some large movable macros need to be scaled to avoid halos + @param bin_center_x bin center x locations + @param bin_center_y bin center y locations + @param target_density target density + @param xl left boundary + @param yl bottom boundary + @param xh right boundary + @param yh top boundary + @param bin_size_x bin width + @param bin_size_y bin height + @param num_movable_nodes number of movable cells + @param num_terminals number of fixed cells + @param num_filler_nodes number of filler cells + @param padding bin padding to boundary of placement region + @param deterministic_flag control whether to use deterministic routine + @param fast_mode if true, only gradient is computed, while objective computation is skipped + @param region_id id for fence region, from 0 to N if there are N fence regions + @param fence_regions # [n_subregion, 4] as dummy macros added to initial density. (xl,yl,xh,yh) rectangles + @param node2fence_region_map node to region id map, non fence region is set to INT_MAX + @param placedb + """ + + if(region_id is not None): + ### reconstruct data structure + self.region_id = region_id + self.comp_id = placedb.rsrc2compId_map[region_id] + num_nodes = placedb.num_nodes + self.fence_region_mask = node2fence_region_map[:num_movable_nodes] == region_id + + node_size_x = torch.cat([node_size_x[:num_movable_nodes][self.fence_region_mask], + node_size_x[num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id]:num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id+1]]], 0) + node_size_y = torch.cat([node_size_y[:num_movable_nodes][self.fence_region_mask], + node_size_y[num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id]:num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id+1]]], 0) + + num_movable_nodes = (self.fence_region_mask).long().sum().item() + num_filler_nodes = placedb.filler_start_map[self.comp_id+1]-placedb.filler_start_map[self.comp_id] + ## sorted cell is recomputed + sorted_node_map = torch.sort(node_size_x[:num_movable_nodes])[1].to(torch.int32) + ## make pos mask for fast forward + self.pos_mask = torch.zeros(2, placedb.num_nodes, dtype=torch.bool, device=node_size_x.device) + self.pos_mask[0,:placedb.num_movable_nodes].masked_fill_(self.fence_region_mask, 1) + self.pos_mask[1,:placedb.num_movable_nodes].masked_fill_(self.fence_region_mask, 1) + self.pos_mask[:,placedb.num_nodes-placedb.num_filler_nodes+placedb.filler_start_map[self.comp_id]:placedb.num_nodes-placedb.num_filler_nodes+placedb.filler_start_map[self.comp_id+1]] = 1 + self.pos_mask = self.pos_mask.view(-1) + + super(ElectricPotential, + self).__init__(node_size_x=node_size_x, + node_size_y=node_size_y, + xl=xl, + yl=yl, + xh=xh, + yh=yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=num_movable_nodes, + num_terminals=0, + num_filler_nodes=num_filler_nodes, + deterministic_flag=deterministic_flag, + sorted_node_map=sorted_node_map) + self.fence_regions = fence_regions + self.node2fence_region_map = node2fence_region_map + self.placedb = placedb + self.region_id = region_id + self.fence_region_mask = node2fence_region_map == region_id + ## set by build_density_op func + self.filler_start_map = None + self.filler_beg = None + self.filler_end = None + self.initial_density_map = None + self.lock_flag = False + + + def reset(self, data_collections=None): + """ Compute members derived from input + """ + if data_collections is not None and self.region_id is not None: + self.node_size_x = torch.cat([data_collections.node_size_x[:data_collections.num_movable_nodes][self.fence_region_mask[:data_collections.num_movable_nodes]], + data_collections.node_size_x[data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id]:data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id+1]]], 0) + self.node_size_y = torch.cat([data_collections.node_size_y[:data_collections.num_movable_nodes][self.fence_region_mask[:data_collections.num_movable_nodes]], + data_collections.node_size_y[data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id]:data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id+1]]], 0) + self.sorted_node_map = torch.sort(self.node_size_x[:(self.fence_region_mask).long().sum().item()])[1].to(torch.int32) + + super(ElectricPotential, self).reset() + + def setLockDSPRAM(self): + """ Set computation for DSP/RAM to zero after legalization + """ + if self.region_id is not None and self.region_id in self.placedb.dsp_ram_rsrcIds: + self.lock_flag = True + + def forward(self, pos, mode="density"): + assert mode in {"density", "overflow"}, "Only support density mode or overflow mode" + if(self.region_id is not None): + ### reconstruct pos, only extract cells in this electric field + pos = pos[self.pos_mask] + + if self.initial_density_map is None: + num_nodes = pos.size(0)//2 + if(self.fence_regions is not None): + self.initial_density_map = self.fence_regions + else: + pdb.set_trace() #Rachel: Should not reach here for FPGA + #logger.info("fixed density map: average %g, max %g, bin area %g" % + # (self.initial_density_map.mean(), + # self.initial_density_map.max(), + # self.bin_size_x * self.bin_size_y)) + + # expk + M = self.num_bins_x + N = self.num_bins_y + self.exact_expkM = precompute_expk(M, + dtype=pos.dtype, + device=pos.device) + self.exact_expkN = precompute_expk(N, + dtype=pos.dtype, + device=pos.device) + + # init dct2, idct2, idct_idxst, idxst_idct with expkM and expkN + self.dct2 = dct.DCT2(self.exact_expkM, self.exact_expkN) + self.idct2 = dct.IDCT2(self.exact_expkM, self.exact_expkN) + self.idct_idxst = dct.IDCT_IDXST(self.exact_expkM, + self.exact_expkN) + self.idxst_idct = dct.IDXST_IDCT(self.exact_expkM, + self.exact_expkN) + + ar = self.bin_size_x / self.bin_size_y * self.placedb.xWirelenWt / self.placedb.yWirelenWt + # wu and wv + wu = torch.arange(M, dtype=pos.dtype, device=pos.device).mul( + 2 * np.pi / M).view([M, 1]) + # scale wv because the aspect ratio of a bin may not be 1 + wv = torch.arange(N, dtype=pos.dtype, device=pos.device).mul(2*np.pi / N).view( + [1, N]).mul_(ar) + wu2_plus_wv2 = wu.pow(2) + wv.pow(2) + wu2_plus_wv2[0, + 0] = 1.0 # avoid zero-division, it will be zeroed out + self.inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 + self.inv_wu2_plus_wv2[0, 0] = 0.0 + self.wu_by_wu2_plus_wv2_half = wu.mul(self.inv_wu2_plus_wv2).mul_( + 1. / 2) + self.wv_by_wu2_plus_wv2_half = wv.mul(self.inv_wu2_plus_wv2).mul_( + 1. / 2) + + if(mode == "density"): + #print("Density computation for region: %d" %(self.region_id)) + return ElectricPotentialFunction.apply( + pos, self.node_size_x_clamped, self.node_size_y_clamped, + self.offset_x, self.offset_y, self.ratio, + self.initial_density_map, + self.xl, self.yl, self.xh, self.yh, self.bin_size_x, + self.bin_size_y, self.num_movable_nodes, self.num_filler_nodes, + self.num_bins_x, self.num_bins_y, + self.deterministic_flag, self.sorted_node_map, self.exact_expkM, + self.exact_expkN, self.inv_wu2_plus_wv2, + self.wu_by_wu2_plus_wv2_half, self.wv_by_wu2_plus_wv2_half, + self.dct2, self.idct2, self.idct_idxst, self.idxst_idct, + math.sqrt(2.0), self.lock_flag) + elif(mode == "overflow"): + ### num_filler_nodes is set 0 + #Return zero if there are no elements in this resourceType + if (self.num_movable_nodes == 0 and self.num_filler_nodes == 0) or self.lock_flag: + return torch.tensor(0, dtype=pos.dtype, device=pos.device), torch.tensor(0, dtype=pos.dtype, device=pos.device) + + density_map = ElectricDensityMapFunction.forward( + pos, self.node_size_x_clamped, self.node_size_y_clamped, + self.offset_x, self.offset_y, self.ratio, + self.initial_density_map, + self.xl, self.yl, self.xh, self.yh, self.bin_size_x, + self.bin_size_y, self.num_movable_nodes, 0, + self.num_bins_x, self.num_bins_y, + self.deterministic_flag, self.sorted_node_map, + self.placedb.overflowInstDensityStretchRatio[self.comp_id]) + + bin_area = self.bin_size_x * self.bin_size_y + density_cost = (density_map - bin_area).clamp_(min=0.0).sum() + + return density_cost, density_map.max() / bin_area + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h new file mode 100644 index 00000000..03977a3f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h @@ -0,0 +1,24 @@ +/** + * @file density_function.h + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + */ + +#ifndef DREAMPLACE_ELECTRIC_POTENTIAL_DENSITY_FUNCTION_H +#define DREAMPLACE_ELECTRIC_POTENTIAL_DENSITY_FUNCTION_H + +DREAMPLACE_BEGIN_NAMESPACE + +//Added by Rachel +// return non-negative value +#define DEFINE_FPGA_DENSITY_FUNCTION(type) \ + T fpga_density_function(T xh, T xl, int k, T bin_size) \ + { \ + T bin_k = k * bin_size; \ + T bin_kp1 = bin_k + bin_size; \ + return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(xh, bin_kp1) - DREAMPLACE_STD_NAMESPACE::max(xl, bin_k)); \ + } + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp new file mode 100644 index 00000000..af7a4b4f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp @@ -0,0 +1,247 @@ +/** + * @file density_map.cpp + * @author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute density map according to e-place + * (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "electric_potential/src/density_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define fpga_density_function +template +DEFINE_FPGA_DENSITY_FUNCTION(T); + +/// @brief The fpga density model from elfPlace. +template +int computeFPGADensityMapLauncher( + const T* x_tensor, const T* y_tensor, const T* node_size_x_tensor, + const T* node_size_y_tensor, const T* offset_x_tensor, + const T* offset_y_tensor, const T* ratio_tensor, + const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, + const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, + const T targetHalfSizeX, const T targetHalfSizeY, + const int num_threads, AtomicOp atomic_add_op, + typename AtomicOp::type* buf_map); + + +#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + + +#define CALL_FPGA_LAUNCHER(begin, end, atomic_add_op, map_ptr) \ + computeFPGADensityMapLauncher( \ + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + begin, end - (begin),\ + num_bins_x, num_bins_y, xl, yl, xh, yh, bin_size_x, bin_size_y, \ + targetHalfSizeX, targetHalfSizeY, \ + at::get_num_threads(), atomic_add_op, map_ptr) + +/// @brief compute density map for movable and filler cells for FPGA +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param initial_density_map initial density map for fixed cells +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +at::Tensor density_map_fpga( + at::Tensor pos, at::Tensor node_size_x_clamped, + at::Tensor node_size_y_clamped, at::Tensor offset_x, at::Tensor offset_y, + at::Tensor ratio, at::Tensor initial_density_map, double xl, double yl, + double xh, double yh, double bin_size_x, double bin_size_y, + double targetHalfSizeX, double targetHalfSizeY, + int num_movable_nodes, int num_filler_nodes, int num_bins_x, + int num_bins_y, int deterministic_flag) { + + CHECK_FLAT_CPU(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = initial_density_map.clone(); + int num_nodes = pos.numel() / 2; + + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = + DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); + long scale_factor = (1L << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeFPGADensityMapLauncher", [&] { + if (deterministic_flag == 1) { + std::vector buf(num_bins, 0); + AtomicAdd atomic_add_op(scale_factor); + CALL_FPGA_LAUNCHER(0, num_movable_nodes, atomic_add_op, + buf.data()); + if (num_filler_nodes) { + CALL_FPGA_LAUNCHER(num_nodes - num_filler_nodes, num_nodes, + atomic_add_op, buf.data()); + } + scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + buf.data(), 1.0 / scale_factor, num_bins, + at::get_num_threads()); + } else { + auto buf = DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t); + AtomicAdd atomic_add_op; + CALL_FPGA_LAUNCHER(0, num_movable_nodes, atomic_add_op, buf); + if (num_filler_nodes) { + CALL_FPGA_LAUNCHER(num_nodes - num_filler_nodes, num_nodes, + atomic_add_op, buf); + } + } + }); + + return density_map; +} + +/// @brief Compute electric force for movable and filler cells +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +/// @param field_map_x electric field map in x direction +/// @param field_map_y electric field map in y direction +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +at::Tensor electric_force_fpga( + at::Tensor grad_pos, int num_bins_x, int num_bins_y, + at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, + at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, at::Tensor ratio, + double bin_size_x, double bin_size_y, + int num_movable_nodes, int num_filler_nodes); + +template +int computeFPGADensityMapLauncher( + const T* x_tensor, const T* y_tensor, const T* node_size_x_clamped_tensor, + const T* node_size_y_clamped_tensor, const T* offset_x_tensor, + const T* offset_y_tensor, const T* ratio_tensor, + const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, + const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, + const T targetHalfSizeX, const T targetHalfSizeY, + const int num_threads, AtomicOp atomic_add_op, + typename AtomicOp::type* buf_map) { + // density_map_tensor should be initialized outside + + T inv_bin_size_x = 1.0 / bin_size_x; + T inv_bin_size_y = 1.0 / bin_size_y; + // do not use dynamic scheduling for determinism + // int chunk_size = + // DREAMPLACE_STD_NAMESPACE::max(int(num_nodes/num_threads/16), 1); +#pragma omp parallel for num_threads( \ + num_threads) // schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) { + // use stretched node size + T node_size_x = node_size_x_clamped_tensor[i]; + T node_size_y = node_size_y_clamped_tensor[i]; + T node_x = x_tensor[i] + offset_x_tensor[i]; + T node_y = y_tensor[i] + offset_y_tensor[i]; + T offset_x = offset_x_tensor[i]; + T offset_y = offset_y_tensor[i]; + T ratio = ratio_tensor[i]; + + T regValX = DREAMPLACE_STD_NAMESPACE::min(node_x - xl, xh - node_x); + T halfSizeX = DREAMPLACE_STD_NAMESPACE::max(offset_x, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeX, regValX)); + + T bXLo = node_x - halfSizeX; + T bXHi = node_x + halfSizeX; + + int bin_index_xl = int(bXLo * inv_bin_size_x); + int bin_index_xh = + int((bXHi * inv_bin_size_x)) + 1; // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + T regValY = DREAMPLACE_STD_NAMESPACE::min(node_y - yl, yh - node_y); + T halfSizeY = DREAMPLACE_STD_NAMESPACE::max(offset_y, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeY, regValY)); + + T bYLo = node_y - halfSizeY; + T bYHi = node_y + halfSizeY; + + int bin_index_yl = int(bYLo * inv_bin_size_y); + int bin_index_yh = + int((bYHi * inv_bin_size_y)) + 1; // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + T inv_halfSizes = 1.0 / (halfSizeX * halfSizeY); + T instDensity = ratio * inv_halfSizes; + + // update density potential map + for (int k = bin_index_xl; k < bin_index_xh; ++k) { + T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); + T px_by_ratio = px * instDensity; + + for (int h = bin_index_yl; h < bin_index_yh; ++h) { + T py = + fpga_density_function(bYHi, bYLo, h, bin_size_y); + T area = px_by_ratio * py; + + atomic_add_op(&buf_map[k * num_bins_y + h], area); + } + } + } + + return 0; +} + +#undef CALL_FPGA_LAUNCHER + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("density_map_fpga", &DREAMPLACE_NAMESPACE::density_map_fpga, "ElectricPotential Density Map"); + m.def("electric_force_fpga", &DREAMPLACE_NAMESPACE::electric_force_fpga, "ElectricPotential Electric Force"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp new file mode 100644 index 00000000..38954f58 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp @@ -0,0 +1,169 @@ +/** + * @file density_map_cuda.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute density map according to e-place + * (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + + +// The FPGA density model from elfPlace +template +int computeFPGADensityMapCudaLauncher( + const T* x_tensor, const T* y_tensor, const T* node_size_x_clamped_tensor, + const T* node_size_y_clamped_tensor, const T* offset_x_tensor, + const T* offset_y_tensor, const T* ratio_tensor, int num_nodes, + const int num_bins_x, const int num_bins_y, const T xl, const T yl, + const T xh, const T yh, const T bin_size_x, const T bin_size_y, + bool deterministic_flag, T* density_map_tensor, const int* sorted_node_map, + const T targetHalfSizeX, const T targetHalfSizeY); + +#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + + +/// @brief compute density map for movable and filler cells for FPGA +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) +/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) +/// @param offset_x (node_size_x - node_size_x_clamped)/2 +/// @param offset_y (node_size_y - node_size_y_clamped)/2 +/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * +/// node_size_y_clamped) +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param initial_density_map initial density map for fixed cells +/// @param target_density target density +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param padding bin padding to boundary of placement region +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +/// @param sorted_node_map the indices of the movable node map +at::Tensor density_map_fpga( + at::Tensor pos, at::Tensor node_size_x_clamped, + at::Tensor node_size_y_clamped, at::Tensor offset_x, at::Tensor offset_y, + at::Tensor ratio, at::Tensor initial_density_map, + double xl, double yl, double xh, double yh, double bin_size_x, double bin_size_y, + int num_movable_nodes, int num_filler_nodes, int num_bins_x, + int num_bins_y, int deterministic_flag, at::Tensor sorted_node_map, + double targetHalfSizeX, double targetHalfSizeY) { + CHECK_FLAT_CUDA(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor density_map = initial_density_map.clone(); + int num_nodes = pos.numel() / 2; + + // Added by Rachel + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeFPGADensityMapCudaLauncher", [&] { + computeFPGADensityMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t), + num_movable_nodes, num_bins_x, num_bins_y, xl, yl, + xh, yh, bin_size_x, bin_size_y, (bool)deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + targetHalfSizeX, targetHalfSizeY); + }); + + if (num_filler_nodes) { + int num_physical_nodes = num_nodes - num_filler_nodes; + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeFPGADensityMapCudaLauncher", [&] { + computeFPGADensityMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + num_physical_nodes, + num_filler_nodes, num_bins_x, num_bins_y, xl, yl, + xh, yh, bin_size_x, bin_size_y, (bool)deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), NULL, + targetHalfSizeX, targetHalfSizeY); + }); + } + + return density_map; +} + +/// @brief Compute electric force for movable and filler cells for FPGA +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +/// @param field_map_x electric field map in x direction +/// @param field_map_y electric field map in y direction +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) +/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) +/// @param offset_x (node_size_x - node_size_x_clamped)/2 +/// @param offset_y (node_size_y - node_size_y_clamped)/2 +/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * +/// node_size_y_clamped) +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +at::Tensor electric_force_fpga( + at::Tensor grad_pos, int num_bins_x, int num_bins_y, + at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, + at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, + double bin_size_x, double bin_size_y, + int num_movable_nodes, int num_filler_nodes, at::Tensor sorted_node_map); + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("density_map_fpga", &DREAMPLACE_NAMESPACE::density_map_fpga, + "ElectricPotential Density Map (CUDA)"); + m.def("electric_force_fpga", &DREAMPLACE_NAMESPACE::electric_force_fpga, + "ElectricPotential Electric Force (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu new file mode 100644 index 00000000..d7fd543e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu @@ -0,0 +1,182 @@ +/** + * @file electric_density_map_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + */ +#include +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +// local dependency +#include "electric_potential/src/density_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define fpga_density_function +template +inline __device__ DEFINE_FPGA_DENSITY_FUNCTION(T); + +//Added by Rachel +template +__global__ void __launch_bounds__(1024, 8) computeFPGADensityMap( + const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, + const T *node_size_y_clamped_tensor, const T *offset_x_tensor, + const T *offset_y_tensor, const T *ratio_tensor, + const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, + const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, + const T inv_bin_size_x, const T inv_bin_size_y, AtomicOp atomicAddOp, + typename AtomicOp::type *density_map_tensor, + const int *sorted_node_map, ///< can be NULL if not sorted + const T targetHalfSizeX, const T targetHalfSizeY +) { + int index = blockIdx.x * blockDim.z + threadIdx.z; + if (index < num_nodes) { + int i = (sorted_node_map) ? sorted_node_map[index] : index; + + // use stretched node size + T node_size_x = node_size_x_clamped_tensor[i]; + T node_size_y = node_size_y_clamped_tensor[i]; + T node_x = x_tensor[i] + offset_x_tensor[i]; + T node_y = y_tensor[i] + offset_y_tensor[i]; + T offset_x = offset_x_tensor[i]; + T offset_y = offset_y_tensor[i]; + T ratio = ratio_tensor[i]; + + T regValX = DREAMPLACE_STD_NAMESPACE::min(node_x - xl, xh - node_x); + T halfSizeX = DREAMPLACE_STD_NAMESPACE::max(offset_x, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeX, regValX)); + + T bXLo = node_x - halfSizeX; + T bXHi = node_x + halfSizeX; + + int bin_index_xl = int(bXLo * inv_bin_size_x); + int bin_index_xh = int((bXHi * inv_bin_size_x)) + 1; // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + T regValY = DREAMPLACE_STD_NAMESPACE::min(node_y - yl, yh - node_y); + T halfSizeY = DREAMPLACE_STD_NAMESPACE::max(offset_y, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeY, regValY)); + + T bYLo = node_y - halfSizeY; + T bYHi = node_y + halfSizeY; + + int bin_index_yl = int(bYLo * inv_bin_size_y); + int bin_index_yh = int((bYHi * inv_bin_size_y)) + 1; // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh + 1, num_bins_y); + + T inv_halfSizes = 1.0 / (halfSizeX * halfSizeY); + T instDensity = ratio * inv_halfSizes; + + // update density potential map + for (int k = bin_index_xl + threadIdx.y; k < bin_index_xh; + k += blockDim.y) { + T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); + T px_by_ratio = px * instDensity; + + for (int h = bin_index_yl + threadIdx.x; h < bin_index_yh; + h += blockDim.x) { + T py = fpga_density_function(bYHi, bYLo, h, bin_size_y); + T area = px_by_ratio * py; + atomicAddOp(&density_map_tensor[k * num_bins_y + h], area); + + } + } + } +} + + +template +int computeFPGADensityMapCallKernel( + const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, + const T *node_size_y_clamped_tensor, const T *offset_x_tensor, + const T *offset_y_tensor, const T *ratio_tensor, int num_nodes, + const int num_bins_x, const int num_bins_y, + const T xl, const T yl, const T xh, const T yh, + const T bin_size_x, const T bin_size_y, AtomicOp atomicAddOp, + typename AtomicOp::type *density_map_tensor, const int *sorted_node_map, + const T targetHalfSizeX, const T targetHalfSizeY) { + int thread_count = 64; + dim3 blockSize(2, 2, thread_count); + + int block_count = (num_nodes - 1 + thread_count) / thread_count; + computeFPGADensityMap<<>>( + x_tensor, y_tensor, node_size_x_clamped_tensor, + node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, + ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, yl, xh, yh, + bin_size_x, bin_size_y, 1 / bin_size_x, 1 / bin_size_y, atomicAddOp, + density_map_tensor, sorted_node_map, targetHalfSizeX, targetHalfSizeY); + + return 0; +} + +template +int computeFPGADensityMapCudaLauncher( + const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, + const T *node_size_y_clamped_tensor, const T *offset_x_tensor, + const T *offset_y_tensor, const T *ratio_tensor, int num_nodes, + const int num_bins_x, const int num_bins_y, const T xl, const T yl, + const T xh, const T yh, const T bin_size_x, const T bin_size_y, + bool deterministic_flag, T *density_map_tensor, const int *sorted_node_map, + const T targetHalfSizeX, const T targetHalfSizeY) { + if (deterministic_flag) // deterministic implementation using unsigned long + // as fixed point number + { + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + unsigned long long int *scaled_density_map_tensor = NULL; + allocateCUDA(scaled_density_map_tensor, num_bins, unsigned long long int); + + AtomicAddCUDA atomicAddOp(scale_factor); + + int thread_count = 512; + copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, + thread_count>>>( + scaled_density_map_tensor, density_map_tensor, scale_factor, num_bins); + computeFPGADensityMapCallKernel( + x_tensor, y_tensor, node_size_x_clamped_tensor, + node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, + ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, + yl, xh, yh, bin_size_x, bin_size_y, atomicAddOp, + scaled_density_map_tensor, sorted_node_map, targetHalfSizeX, targetHalfSizeY); + copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, + thread_count>>>(density_map_tensor, + scaled_density_map_tensor, + T(1.0 / scale_factor), num_bins); + + destroyCUDA(scaled_density_map_tensor); + } else { + AtomicAddCUDA atomicAddOp; + + computeFPGADensityMapCallKernel( + x_tensor, y_tensor, node_size_x_clamped_tensor, + node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, + ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, + yl, xh, yh, bin_size_x, bin_size_y, atomicAddOp, density_map_tensor, + sorted_node_map, targetHalfSizeX, targetHalfSizeY); + } + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeFPGADensityMapCudaLauncher( \ + const T *x_tensor, const T *y_tensor, \ + const T *node_size_x_clamped_tensor, \ + const T *node_size_y_clamped_tensor, const T *offset_x_tensor, \ + const T *offset_y_tensor, const T *ratio_tensor, \ + const int num_nodes, const int num_bins_x, const int num_bins_y, \ + const T xl, const T yl, const T xh, const T yh, const T bin_size_x, \ + const T bin_size_y, bool deterministic_flag, T *density_map_tensor, \ + const int *sorted_node_map, const T targetHalfSizeX, \ + const T targetHalfSizeY); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp new file mode 100644 index 00000000..63b1dedb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp @@ -0,0 +1,168 @@ +/** + * @file electric_force.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute electric force according to e-place + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" +// local dependency +#include "electric_potential/src/density_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define fpga_density_function +template +DEFINE_FPGA_DENSITY_FUNCTION(T); + +#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +int computeElectricForceFPGALauncher( + int num_bins_x, int num_bins_y, + const T* field_map_x_tensor, const T* field_map_y_tensor, + const T* x_tensor, const T* y_tensor, + const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, + const T* ratio_tensor, + T bin_size_x, T bin_size_y, int num_nodes, int num_threads, + T* grad_x_tensor, T* grad_y_tensor); + +#define CALL_FPGA_LAUNCHER(begin, end) \ + computeElectricForceFPGALauncher( \ + num_bins_x, num_bins_y, \ + DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), \ + DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), \ + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + begin, \ + bin_size_x, bin_size_y, end - (begin), at::get_num_threads(), \ + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + begin, \ + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes + begin) + + +/// @brief compute electric force for movable and filler cells +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +/// @param field_map_x electric field map in x direction +/// @param field_map_y electric field map in y direction +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x cell width array +/// @param node_size_y cell height array +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +at::Tensor electric_force_fpga( + at::Tensor grad_pos, int num_bins_x, int num_bins_y, + at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, + at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, at::Tensor ratio, + double bin_size_x, double bin_size_y, + int num_movable_nodes, int num_filler_nodes) { + CHECK_FLAT_CPU(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + at::Tensor grad_out = at::zeros_like(pos); + int num_nodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeElectricForceFPGALauncher", [&] { + CALL_FPGA_LAUNCHER(0, num_movable_nodes); + if (num_filler_nodes) { + int num_physical_nodes = num_nodes - num_filler_nodes; + CALL_FPGA_LAUNCHER(num_physical_nodes, num_nodes); + } + }); + + return grad_out.mul_(grad_pos); +} + +template +int computeElectricForceFPGALauncher( + int num_bins_x, int num_bins_y, + const T* field_map_x_tensor, const T* field_map_y_tensor, + const T* x_tensor, const T* y_tensor, + const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, + const T* ratio_tensor, + T bin_size_x, T bin_size_y, int num_nodes, int num_threads, + T* grad_x_tensor, T* grad_y_tensor) { + // density_map_tensor should be initialized outside + + T inv_bin_size_x = 1.0 / bin_size_x; + T inv_bin_size_y = 1.0 / bin_size_y; + int chunk_size = + DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) { + // use stretched node size + T node_size_x = node_size_x_clamped_tensor[i]; + T node_size_y = node_size_y_clamped_tensor[i]; + T node_x = x_tensor[i]; + T node_y = y_tensor[i]; + T ratio = ratio_tensor[i]; + + T bXLo = node_x; + T bXHi = node_x + node_size_x; + + // Yibo: looks very weird implementation, but this is how RePlAce implements + // it the common practice should be floor Zixuan and Jiaqi: use the common + // practice of floor + int bin_index_xl = int(bXLo * inv_bin_size_x); + int bin_index_xh = int(bXHi * inv_bin_size_x); // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x - 1); + + T bYLo = node_y; + T bYHi = node_y + node_size_y; + + // Yibo: looks very weird implementation, but this is how RePlAce implements + // it the common practice should be floor Zixuan and Jiaqi: use the common + // practice of floor + int bin_index_yl = int(bYLo * inv_bin_size_y); + int bin_index_yh = int(bYHi * inv_bin_size_y); // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y - 1); + + T& gx = grad_x_tensor[i]; + T& gy = grad_y_tensor[i]; + gx = 0.0; + gy = 0.0; + // update density potential map + for (int k = bin_index_xl; k <= bin_index_xh; ++k) { + T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); + for (int h = bin_index_yl; h <= bin_index_yh; ++h) { + T py = + fpga_density_function(bYHi, bYLo, h, bin_size_y); + T area = px * py; + + int idx = k * num_bins_y + h; + gx += area * field_map_x_tensor[idx]; + gy += area * field_map_y_tensor[idx]; + } + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp new file mode 100644 index 00000000..3ae9e4c6 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp @@ -0,0 +1,119 @@ +/** + * @file electric_force_cuda.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + * @brief Compute electric force according to e-place + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +int computeElectricForceFPGACudaLauncher( + int num_bins_x, int num_bins_y, + const T* field_map_x_tensor, + const T* field_map_y_tensor, const T* x_tensor, const T* y_tensor, + const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, + T bin_size_x, T bin_size_y, int num_nodes, T* grad_x_tensor, + T* grad_y_tensor, const int* sorted_node_map); + + +/// @brief compute electric force for movable and filler cells for FPGA +/// @param grad_pos input gradient from backward propagation +/// @param num_bins_x number of bins in horizontal bins +/// @param num_bins_y number of bins in vertical bins +/// @param num_movable_impacted_bins_x number of impacted bins for any movable +/// cell in x direction +/// @param num_movable_impacted_bins_y number of impacted bins for any movable +/// cell in y direction +/// @param num_filler_impacted_bins_x number of impacted bins for any filler +/// cell in x direction +/// @param num_filler_impacted_bins_y number of impacted bins for any filler +/// cell in y direction +/// @param field_map_x electric field map in x direction +/// @param field_map_y electric field map in y direction +/// @param pos cell locations. The array consists of all x locations and then y +/// locations. +/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) +/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) +/// @param offset_x (node_size_x - node_size_x_clamped)/2 +/// @param offset_y (node_size_y - node_size_y_clamped)/2 +/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * +/// node_size_y_clamped) +/// @param bin_center_x bin center x locations +/// @param bin_center_y bin center y locations +/// @param xl left boundary +/// @param yl bottom boundary +/// @param xh right boundary +/// @param yh top boundary +/// @param bin_size_x bin width +/// @param bin_size_y bin height +/// @param num_movable_nodes number of movable cells +/// @param num_filler_nodes number of filler cells +/// @param sorted_node_map the indices of the movable node map +at::Tensor electric_force_fpga( + at::Tensor grad_pos, int num_bins_x, int num_bins_y, + at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, + at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, + double bin_size_x, double bin_size_y, + int num_movable_nodes, int num_filler_nodes, at::Tensor sorted_node_map) { + CHECK_FLAT_CUDA(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT_CUDA(field_map_x); + CHECK_CONTIGUOUS(field_map_x); + CHECK_FLAT_CUDA(field_map_y); + CHECK_CONTIGUOUS(field_map_y); + + at::Tensor grad_out = at::zeros_like(pos); + int num_nodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeElectricForceFPGACudaLauncher", [&] { + computeElectricForceFPGACudaLauncher( + num_bins_x, num_bins_y, + DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t), + bin_size_x, bin_size_y, num_movable_nodes, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int)); + }); + + if (num_filler_nodes) { + int num_physical_nodes = num_nodes - num_filler_nodes; + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeElectricForceFPGACudaLauncher", [&] { + computeElectricForceFPGACudaLauncher( + num_bins_x, num_bins_y, + DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + + num_physical_nodes, + bin_size_x, bin_size_y, num_filler_nodes, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + + num_physical_nodes, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes + + num_physical_nodes, + NULL); + }); + } + + return grad_out.mul_(grad_pos); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu new file mode 100644 index 00000000..2fffa566 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu @@ -0,0 +1,131 @@ +/** + * @file electric_force_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Oct 2020 + */ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +// local dependency +#include "electric_potential/src/density_function.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// define fpga_density_function +template +inline __device__ DEFINE_FPGA_DENSITY_FUNCTION(T); + +template +__global__ void __launch_bounds__(1024, 8) computeElectricForceFPGA( + int num_bins_x, int num_bins_y, const T *field_map_x_tensor, + const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, + const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, + const T bin_size_x, const T bin_size_y, const T inv_bin_size_x, + const T inv_bin_size_y, int num_nodes, T *grad_x_tensor, T *grad_y_tensor, + const int *sorted_node_map ///< can be NULL if not sorted +) { + int index = blockIdx.x * blockDim.z + threadIdx.z; + if (index < num_nodes) { + int i = (sorted_node_map) ? sorted_node_map[index] : index; + + // use stretched node size + T node_size_x = node_size_x_clamped_tensor[i]; + T node_size_y = node_size_y_clamped_tensor[i]; + T node_x = x_tensor[i]; + T node_y = y_tensor[i]; + + T bXLo = node_x; + T bXHi = node_x + node_size_x; + + int bin_index_xl = int(bXLo * inv_bin_size_x); + int bin_index_xh = int((bXHi * inv_bin_size_x)); // exclusive + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x -1); + + T bYLo = node_y; + T bYHi = node_y + node_size_y; + + int bin_index_yl = int(bYLo * inv_bin_size_y); + int bin_index_yh = int((bYHi * inv_bin_size_y)); // exclusive + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y-1); + + // blockDim.x * blockDim.y threads will be used to update one node + // shared memory is used to privatize the atomic memory access to thread + // block + extern __shared__ unsigned char s_xy[]; + T *s_x = (T *)s_xy; + T *s_y = s_x + blockDim.z; + if (threadIdx.x == 0 && threadIdx.y == 0) { + s_x[threadIdx.z] = s_y[threadIdx.z] = 0; + } + __syncthreads(); + + T tmp_x, tmp_y; + tmp_x = 0; + tmp_y = 0; + + // update density potential map + for (int k = bin_index_xl + threadIdx.y; k <= bin_index_xh; + k += blockDim.y) { + T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); + + for (int h = bin_index_yl + threadIdx.x; h <= bin_index_yh; + h += blockDim.x) { + T py = + fpga_density_function(bYHi, bYLo, h, bin_size_y); + T area = px * py; + + int idx = k * num_bins_y + h; + tmp_x += area * field_map_x_tensor[idx]; + tmp_y += area * field_map_y_tensor[idx]; + } + } + + atomicAdd(&s_x[threadIdx.z], tmp_x); + atomicAdd(&s_y[threadIdx.z], tmp_y); + __syncthreads(); + + if (threadIdx.x == 0 && threadIdx.y == 0) { + grad_x_tensor[i] = s_x[threadIdx.z]; + grad_y_tensor[i] = s_y[threadIdx.z]; + } + } +} + +template +int computeElectricForceFPGACudaLauncher( + int num_bins_x, int num_bins_y, + const T *field_map_x_tensor, + const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, + const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, + T bin_size_x, T bin_size_y, int num_nodes, T *grad_x_tensor, + T *grad_y_tensor, const int *sorted_node_map) { + int thread_count = 64; + dim3 blockSize(2, 2, thread_count); + size_t shared_mem_size = sizeof(T) * thread_count * 2; + + int block_count_nodes = (num_nodes + thread_count - 1) / thread_count; + computeElectricForceFPGA<<>>( + num_bins_x, num_bins_y, field_map_x_tensor, field_map_y_tensor, x_tensor, + y_tensor, node_size_x_clamped_tensor, node_size_y_clamped_tensor, + bin_size_x, bin_size_y, 1 / bin_size_x, 1 / bin_size_y, num_nodes, + grad_x_tensor, grad_y_tensor, sorted_node_map); + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeElectricForceFPGACudaLauncher( \ + int num_bins_x, int num_bins_y, const T *field_map_x_tensor, \ + const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, \ + const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, \ + T bin_size_x, T bin_size_y, int num_nodes, T *grad_x_tensor, \ + T *grad_y_tensor, const int *sorted_node_map); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt new file mode 100644 index 00000000..cd142a0e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt @@ -0,0 +1,48 @@ +set(OP_NAME hpwl) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/hpwl.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cpp_atomic + src/hpwl_atomic.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + ${TARGET_NAME}_cpp_atomic + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/hpwl_cuda.cpp + src/hpwl_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda_atomic + src/hpwl_cuda_atomic.cpp + src/hpwl_cuda_atomic_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + ${TARGET_NAME}_cuda_atomic + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py new file mode 100644 index 00000000..e372a75e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py @@ -0,0 +1,128 @@ +## +# @file hpwl.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date May 2023 +# + +import torch +from torch.autograd import Function +from torch import nn +import numpy as np +import pdb +import time + +import dreamplacefpga.ops.hpwl.hpwl_cpp as hpwl_cpp +import dreamplacefpga.ops.hpwl.hpwl_cpp_atomic as hpwl_cpp_atomic +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.hpwl.hpwl_cuda as hpwl_cuda + import dreamplacefpga.ops.hpwl.hpwl_cuda_atomic as hpwl_cuda_atomic + +class HPWLFunction(Function): + """compute half-perimeter wirelength. + @param pos pin location (x array, y array), not cell location + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param net_weights weight of nets + @param net_mask a boolean mask containing whether a net should be computed + @param pin2net_map pin2net map, second set of options + """ + @staticmethod + def forward(ctx, pos, flat_netpin, netpin_start, net_weights, + net_weights_x, net_mask, net_bounding_box_min, net_bounding_box_max, + xWeight, yWeight, num_threads): + + output = pos.new_empty(1) + if pos.is_cuda: + output = hpwl_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, net_mask) + return (net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight + else: + output = hpwl_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, + net_weights_x, net_mask, xWeight, yWeight, num_threads) + return output + +class HPWLAtomicFunction(Function): + """compute half-perimeter wirelength using atomic max/min. + @param pos pin location (x array, y array), not cell location + @param pin2net_map pin2net map, second set of options + @param net_weights weight of nets + @param net_mask a boolean mask containing whether a net should be computed + """ + @staticmethod + def forward(ctx, pos, pin2net_map, net_weights, net_weights_x, net_mask, xWeight, yWeight): + + output = pos.new_empty(1) + if pos.is_cuda: + output = hpwl_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask) + return ((net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight).sum().mul_(1.0/1000) + else: + output = hpwl_cpp_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask) + return ((net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight).sum() + +class HPWL(nn.Module): + """ + @brief Compute half-perimeter wirelength. + Support two algoriths: net-by-net and atomic. + Different parameters are required for different algorithms. + """ + def __init__(self, placedb=None, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, + num_carry_chains=None, cc_net_weight=None, dir_net_weight=None, net_mask=None, net_bounding_box_min=None, + net_bounding_box_max=None, num_threads=None, algorithm='atomic'): + """ + @brief initialization + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore + @param algorithm must be net-by-net | atomic + """ + super(HPWL, self).__init__() + assert net_mask is not None, "net_mask is a requried parameter" + if algorithm == 'net-by-net': + assert flat_netpin is not None and netpin_start is not None, "flat_netpin, netpin_start are requried parameters for algorithm net-by-net" + elif algorithm == 'atomic': + assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" + self.xWeight = placedb.xWirelenWt + self.yWeight = placedb.yWirelenWt + self.flat_netpin = flat_netpin + self.netpin_start = netpin_start + self.pin2net_map = pin2net_map + self.net_weights = net_weights + self.net_weights_x = net_weights.detach().clone() + self.num_carry_chains = num_carry_chains + self.cc_net_weight = cc_net_weight + self.dir_net_weight = dir_net_weight + self.net_mask = net_mask + self.algorithm = algorithm + self.num_threads = num_threads + self.net_bounding_box_min = net_bounding_box_min + self.net_bounding_box_max = net_bounding_box_max + + #Weight x direction 50% more than y for carry chains + if self.num_carry_chains > 0 and self.cc_net_weight > 1.0 and self.dir_net_weight: + self.net_weights_x[self.net_weights_x == cc_net_weight] = 1.5*cc_net_weight + + def forward(self, pos): + if self.algorithm == 'net-by-net': + return HPWLFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.net_weights, + self.net_weights_x, + self.net_mask, + self.net_bounding_box_min, + self.net_bounding_box_max, + self.xWeight, + self.yWeight, + self.num_threads + ) + elif self.algorithm == 'atomic': + return HPWLAtomicFunction.apply(pos, + self.pin2net_map, + self.net_weights, + self.net_weights_x, + self.net_mask, + self.xWeight, + self.yWeight + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp new file mode 100644 index 00000000..25d26354 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp @@ -0,0 +1,130 @@ +/** + * @file hpwl.cpp + * @author Yibo Lin (DREAMPlace) Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date May 2023 + * @brief Compute half-perimeter wirelength + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeHPWLLauncher( + const T* x, const T* y, + const T* net_weights, + const T* net_weights_x, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T xWeight, + T yWeight, + int num_threads, + T* hpwl + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute half-perimeter wirelength +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +at::Tensor hpwl_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + double xWeight, + double yWeight, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_weights_x); + CHECK_CONTIGUOUS(net_weights_x); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = netpin_start.numel()-1; + at::Tensor hpwl = at::zeros(num_nets, pos.options()); + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLLauncher", [&] { + computeHPWLLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, xWeight, yWeight, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(hpwl, scalar_t) + ); + }); + //if (net_weights.numel()) + //{ + // hpwl.mul_(net_weights); + //} + return hpwl.sum(); +} + +template +int computeHPWLLauncher( + const T* x, const T* y, + const T* net_weights, + const T* net_weights_x, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T xWeight, + T yWeight, + int num_threads, + T* hpwl + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nets; ++i) + { + T max_x = -std::numeric_limits::max(); + T min_x = std::numeric_limits::max(); + T max_y = -std::numeric_limits::max(); + T min_y = std::numeric_limits::max(); + + // ignore large degree nets + if (net_mask[i]) + { + for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) + { + min_x = std::min(min_x, x[flat_netpin[j]]); + max_x = std::max(max_x, x[flat_netpin[j]]); + min_y = std::min(min_y, y[flat_netpin[j]]); + max_y = std::max(max_y, y[flat_netpin[j]]); + } + hpwl[i] = ((max_x-min_x)*net_weights_x[i]*xWeight) + ((max_y-min_y)*net_weights[i]*yWeight); + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_forward, "HPWL forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp new file mode 100644 index 00000000..c11c3670 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp @@ -0,0 +1,122 @@ +/** + * @file hpwl_atomic.cpp + * @author Yibo Lin (DREAMPlace) + * @date Mar 2019 + * @brief Compute half-perimeter wirelength to mimic a parallel atomic implementation + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeHPWLAtomicLauncher( + const T* x, const T* y, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + T* partial_hpwl_max, + T* partial_hpwl_min + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute half-perimeter wirelength +/// @param pos cell locations, array of x locations and then y locations +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +at::Tensor hpwl_atomic_forward( + at::Tensor pos, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = net_mask.numel(); + // x then y + at::Tensor partial_hpwl_max = at::zeros({2, num_nets}, pos.type()); + at::Tensor partial_hpwl_min = at::zeros({2, num_nets}, pos.type()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLAtomicLauncher", [&] { + partial_hpwl_max[0].fill_(std::numeric_limits::min()); + partial_hpwl_max[1].fill_(std::numeric_limits::min()); + partial_hpwl_min[0].fill_(std::numeric_limits::max()); + partial_hpwl_min[1].fill_(std::numeric_limits::max()); + computeHPWLAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + pin2net_map.numel(), + DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_max, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_min, scalar_t) + ); + }); + + auto hpwl = (partial_hpwl_max-partial_hpwl_min); + + //if (net_weights.numel()) + //{ + // hpwl.mul_(net_weights.view({1, num_nets})); + //} + + //return hpwl.sum(); + return hpwl; +} + +template +int computeHPWLAtomicLauncher( + const T* x, const T* y, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + T* partial_hpwl_max, + T* partial_hpwl_min + ) +{ + T* partial_hpwl_x_max = partial_hpwl_max; + T* partial_hpwl_x_min = partial_hpwl_min; + T* partial_hpwl_y_max = partial_hpwl_max+num_nets; + T* partial_hpwl_y_min = partial_hpwl_min+num_nets; + for (int i = 0; i < num_pins; ++i) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + partial_hpwl_x_max[net_id] = std::max(partial_hpwl_x_max[net_id], x[i]); + partial_hpwl_x_min[net_id] = std::min(partial_hpwl_x_min[net_id], x[i]); + partial_hpwl_y_max[net_id] = std::max(partial_hpwl_y_max[net_id], y[i]); + partial_hpwl_y_min[net_id] = std::min(partial_hpwl_y_min[net_id], y[i]); + } + else + { + partial_hpwl_x_max[net_id] = 0; + partial_hpwl_x_min[net_id] = 0; + partial_hpwl_y_max[net_id] = 0; + partial_hpwl_y_min[net_id] = 0; + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_atomic_forward, "HPWL forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp new file mode 100644 index 00000000..9372b8b5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp @@ -0,0 +1,148 @@ +/** + * @file hpwl_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute half-perimeter wirelength + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" +using namespace torch::indexing; + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeHPWLCudaLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* partial_wl + ); + +template +int computeHPWLCudaLauncherFPGA( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* bbox_min_x, + T* bbox_max_x, + T* bbox_min_y, + T* bbox_max_y, + T* partial_wl + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute half-perimeter wirelength +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +at::Tensor hpwl_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_mask + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + // x then y + int num_nets = net_mask.numel(); + at::Tensor partial_wl = at::zeros({2, num_nets}, pos.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLCudaLauncher", [&] { + computeHPWLCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t) + ); + }); + //std::cout << "partial_hpwl = \n" << partial_wl << "\n"; + + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({1, num_nets})); + } + return partial_wl; +} + +/// @brief Compute half-perimeter wirelength along with net bbox +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +at::Tensor hpwl_forward_fpga( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor net_bounding_box_min, + at::Tensor net_bounding_box_max + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + // x then y + int num_nets = net_mask.numel(); + at::Tensor partial_wl = at::zeros({2, num_nets}, pos.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLCudaLauncherFPGA", [&] { + computeHPWLCudaLauncherFPGA( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t)+num_nets, DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t)+num_nets, + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t) + ); + }); + //std::cout << "partial_hpwl = \n" << partial_wl << "\n"; + + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({1, num_nets})); + } + return partial_wl; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_forward, "HPWL forward (CUDA)"); + m.def("forward_fpga", &DREAMPLACE_NAMESPACE::hpwl_forward_fpga, "HPWL forward to generate net bbox(CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp new file mode 100644 index 00000000..7b7e58f2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp @@ -0,0 +1,98 @@ +/** + * @file hpwl_cuda_atomic.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jul 2018 + * @brief Compute half-perimeter wirelength to mimic a parallel atomic implementation + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeHPWLCudaAtomicLauncher( + const T* x, const T* y, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + T* partial_hpwl_max, + T* partial_hpwl_min + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute half-perimeter wirelength +/// @param pos cell locations, array of x locations and then y locations +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +at::Tensor hpwl_atomic_forward( + at::Tensor pos, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask + ) +{ + typedef int T; + + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = net_mask.numel(); + // x then y + at::Tensor scaled_pos = at::_cast_Int(pos.mul(1000), false); + at::Tensor partial_hpwl_max = at::zeros({2, num_nets}, scaled_pos.type()); + at::Tensor partial_hpwl_min = at::zeros({2, num_nets}, scaled_pos.type()); + partial_hpwl_max[0].fill_(std::numeric_limits::min()); + partial_hpwl_max[1].fill_(std::numeric_limits::min()); + partial_hpwl_min[0].fill_(std::numeric_limits::max()); + partial_hpwl_min[1].fill_(std::numeric_limits::max()); + + computeHPWLCudaAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(scaled_pos, T), DREAMPLACE_TENSOR_DATA_PTR(scaled_pos, T)+scaled_pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + pin2net_map.numel(), + DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_max, T), + DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_min, T) + ); + + auto delta = partial_hpwl_max-partial_hpwl_min; + + at::Tensor hpwl; + switch (pos.scalar_type()) + { + case at::ScalarType::Double: + hpwl = at::_cast_Double(delta, false); + break; + case at::ScalarType::Float: + hpwl = at::_cast_Float(delta, false); + break; + default: + AT_ERROR("hpwl_atomic_forward", " not implemented for '", at::toString(pos.scalar_type()), "'"); + } + + //if (net_weights.numel()) + //{ + // hpwl.mul_(net_weights.view({1, num_nets})); + //} + //return hpwl.sum().mul_(1.0/1000); + return hpwl; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_atomic_forward, "HPWL forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu new file mode 100644 index 00000000..648feed0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu @@ -0,0 +1,103 @@ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeHPWLMax( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + T *partial_hpwl_x_max) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + atomicMax(&partial_hpwl_x_max[net_id], (T)net_mask[net_id] * x[i]); + } +} + +template +__global__ void computeHPWLMin( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + T *partial_hpwl_x_min) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + atomicMin(&partial_hpwl_x_min[net_id], (T)net_mask[net_id] * x[i]); + } +} + +template +__global__ void computeHPWLMaxMin( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + T *partial_hpwl_x_max, T *partial_hpwl_x_min, + T *partial_hpwl_y_max, T *partial_hpwl_y_min) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + + T xx = (T)net_mask[net_id] * x[i]; + atomicMax(&partial_hpwl_x_max[net_id], xx); + atomicMin(&partial_hpwl_x_min[net_id], xx); + + T yy = (T)net_mask[net_id] * y[i]; + atomicMax(&partial_hpwl_y_max[net_id], yy); + atomicMin(&partial_hpwl_y_min[net_id], yy); + } +} + +template +int computeHPWLCudaAtomicLauncher( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + T *partial_hpwl_max, + T *partial_hpwl_min) +{ + const int thread_count = 64; + const int block_count_pins = (num_pins + thread_count - 1) / thread_count; + + computeHPWLMaxMin<<>>( + x, y, + pin2net_map, + net_mask, + num_pins, + partial_hpwl_max, partial_hpwl_min, + partial_hpwl_max + num_nets, partial_hpwl_min + num_nets); + + return 0; +} + +// manually instantiate the template function +#define REGISTER_KERNEL_LAUNCHER(type) \ + template int computeHPWLCudaAtomicLauncher( \ + const type *x, const type *y, \ + const int *pin2net_map, \ + const unsigned char *net_mask, \ + int num_nets, \ + int num_pins, \ + type *partial_hpwl_max, \ + type *partial_hpwl_min); + +REGISTER_KERNEL_LAUNCHER(int); +REGISTER_KERNEL_LAUNCHER(long long int); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu new file mode 100644 index 00000000..18c5a4c7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu @@ -0,0 +1,238 @@ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +//template +//__global__ void fillArray(T* x, const int n, const T v) +//{ +// //for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) +// int i = blockIdx.x * blockDim.x + threadIdx.x; +// if (i < n) +// { +// x[i] = v; +// } +//} + +template +__global__ void computeHPWL( + const T* x, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* partial_hpwl + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + T max_x = -FLT_MAX; + T min_x = FLT_MAX; + + if (net_mask[i]) + { + for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) + { + min_x = min(min_x, x[flat_netpin[j]]); + max_x = max(max_x, x[flat_netpin[j]]); + } + partial_hpwl[i] = max_x-min_x; + } + else + { + partial_hpwl[i] = 0; + } + } +} + +template +__global__ void computeHPWLFPGA( + const T* x, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* bbox_min_x, + T* bbox_max_x, + T* partial_hpwl + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + //T max_x = -FLT_MAX; + //T min_x = FLT_MAX; + bbox_max_x[i] = -FLT_MAX; + bbox_min_x[i] = FLT_MAX; + + if (net_mask[i]) + { + for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) + { + //min_x = min(min_x, x[flat_netpin[j]]); + //max_x = max(max_x, x[flat_netpin[j]]); + bbox_min_x[i] = min(bbox_min_x[i], x[flat_netpin[j]]); + bbox_max_x[i] = max(bbox_max_x[i], x[flat_netpin[j]]); + } + partial_hpwl[i] = bbox_max_x[i]-bbox_min_x[i]; + } + else + { + partial_hpwl[i] = 0; + } + } +} + +template +int computeHPWLCudaLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* partial_hpwl + ) +{ + const int thread_count = 512; + const int block_count_nets = (num_nets + thread_count - 1) / thread_count; + + cudaError_t status; + cudaStream_t stream_y; + status = cudaStreamCreate(&stream_y); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_y\n"); + fflush(stdout); + return 1; + } + + computeHPWL<<>>( + x, + flat_netpin, + netpin_start, + net_mask, + num_nets, + partial_hpwl + ); + + computeHPWL<<>>( + y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + partial_hpwl+num_nets + ); + + /* destroy stream */ + status = cudaStreamDestroy(stream_y); + if (status != cudaSuccess) + { + printf("stream_y destroy failed\n"); + fflush(stdout); + return 1; + } + + //printArray(partial_hpwl, num_nets, "partial_hpwl"); + + // I move out the summation to use ATen + // significant speedup is observed + //sumArray<<<1, 1>>>(partial_hpwl, num_nets, hpwl); + + return 0; +} + +template +int computeHPWLCudaLauncherFPGA( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + T* bbox_min_x, T* bbox_min_y, + T* bbox_max_x, T* bbox_max_y, + T* partial_hpwl + ) +{ + const int thread_count = 512; + const int block_count_nets = (num_nets + thread_count - 1) / thread_count; + + cudaError_t status; + cudaStream_t stream_y; + status = cudaStreamCreate(&stream_y); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_y\n"); + fflush(stdout); + return 1; + } + + computeHPWLFPGA<<>>( + x, + flat_netpin, + netpin_start, + net_mask, + num_nets, + bbox_min_x, + bbox_max_x, + partial_hpwl + ); + + computeHPWLFPGA<<>>( + y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + bbox_min_y, + bbox_max_y, + partial_hpwl+num_nets + ); + + /* destroy stream */ + status = cudaStreamDestroy(stream_y); + if (status != cudaSuccess) + { + printf("stream_y destroy failed\n"); + fflush(stdout); + return 1; + } + + //printArray(partial_hpwl, num_nets, "partial_hpwl"); + + // I move out the summation to use ATen + // significant speedup is observed + //sumArray<<<1, 1>>>(partial_hpwl, num_nets, hpwl); + + return 0; +} + +// manually instantiate the template function +#define REGISTER_KERNEL_LAUNCHER(type) \ + template int computeHPWLCudaLauncher(\ + const type* x, const type* y, \ + const int* flat_netpin, \ + const int* netpin_start, \ + const unsigned char* net_mask, \ + int num_nets, \ + type* partial_hpwl \ + ); \ + template int computeHPWLCudaLauncherFPGA( \ + const type* x, const type* y, \ + const int* flat_netpin, \ + const int* netpin_start, \ + const unsigned char* net_mask, \ + int num_nets, \ + type* bbox_min_x, type* bbox_min_y, \ + type* bbox_max_x, type* bbox_max_y, \ + type* partial_hpwl \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt new file mode 100644 index 00000000..60026fbb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt @@ -0,0 +1,45 @@ +set(OP_NAME logsumexp_wirelength) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp_merged + src/logsumexp_wirelength_merged.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp_merged + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) + +add_pytorch_extension(${TARGET_NAME}_cuda_merged + src/logsumexp_wirelength_cuda_merged.cpp + src/logsumexp_wirelength_cuda_merged_kernel.cu + src/integrate_net_weights_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda_atomic + src/logsumexp_wirelength_cuda_atomic.cpp + src/logsumexp_wirelength_cuda_atomic_kernel.cu + src/integrate_net_weights_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda_merged + ${TARGET_NAME}_cuda_atomic + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py new file mode 100644 index 00000000..cd043ea5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py @@ -0,0 +1,200 @@ +## +# @file logsumexp_wirelength.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 +# + +import time +import torch +from torch import nn +from torch.autograd import Function +import logging + +import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cpp_merged as logsumexp_wirelength_cpp_merged +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cuda_merged as logsumexp_wirelength_cuda_merged + import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cuda_atomic as logsumexp_wirelength_cuda_atomic +import pdb + +logger = logging.getLogger(__name__) + + +class LogSumExpWirelengthAtomicFunction(Function): + """compute weighted average wirelength. + @param pos pin location (x array, y array), not cell location + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength + @param gamma the smaller, the closer to HPWL + """ + @staticmethod + def forward(ctx, pos, pin2net_map, net_weights, net_mask, gamma): + if pos.is_cuda: + output = logsumexp_wirelength_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask, gamma) + else: + assert 0, "CPU version NOT IMPLEMENTED" + ctx.pin2net_map = pin2net_map + ctx.net_weights = net_weights + ctx.net_mask = net_mask + ctx.gamma = gamma + ctx.exp_xy = output[1] + ctx.exp_nxy = output[2] + ctx.exp_xy_sum = output[3] + ctx.exp_nxy_sum = output[4] + ctx.pos = pos + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + if grad_pos.is_cuda: + output = logsumexp_wirelength_cuda_atomic.backward( + grad_pos, + ctx.pos, + ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), + ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), + ctx.pin2net_map, + ctx.net_weights, + ctx.net_mask, + ctx.gamma + ) + else: + assert 0, "CPU version NOT IMPLEMENTED" + return output, None, None, None, None + +class LogSumExpWirelengthMergedFunction(Function): + """ + @brief compute weighted average wirelength. + """ + @staticmethod + def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, pin_mask, gamma, num_threads): + """ + @param pos pin location (x array, y array), not cell location + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength + @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute + @param gamma the larger, the closer to HPWL + """ + tt = time.time() + if pos.is_cuda: + output = logsumexp_wirelength_cuda_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, gamma) + else: + output = logsumexp_wirelength_cpp_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, gamma, num_threads) + ctx.num_threads = num_threads + ctx.pin2net_map = pin2net_map + ctx.flat_netpin = flat_netpin + ctx.netpin_start = netpin_start + ctx.net_weights = net_weights + ctx.net_mask = net_mask + ctx.pin_mask = pin_mask + ctx.gamma = gamma + ctx.grad_intermediate = output[1] + ctx.pos = pos + if pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + tt = time.time() + if grad_pos.is_cuda: + output = logsumexp_wirelength_cuda_merged.backward( + grad_pos, + ctx.pos, + ctx.grad_intermediate, + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_mask, + ctx.gamma + ) + else: + output = logsumexp_wirelength_cpp_merged.backward( + grad_pos, + ctx.pos, + ctx.grad_intermediate, + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_mask, + ctx.gamma, + ctx.num_threads + ) + output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) + output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) + if grad_pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) + return output, None, None, None, None, None, None, None, None + + +class LogSumExpWirelength(nn.Module): + """ Compute log-sum-exp wirelength. + CPU only supports net-by-net algorithm. + GPU supports two algorithms: atomic, sparse. + Different parameters are required for different algorithms. + + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore + @param gamma the smaller, the closer to HPWL + @param algorithm must be merged | atomic + """ + def __init__(self, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, net_mask=None, pin_mask=None, gamma=None, algorithm='merged', num_threads=8): + super(LogSumExpWirelength, self).__init__() + assert net_weights is not None \ + and net_mask is not None \ + and pin_mask is not None \ + and gamma is not None, "net_weights, net_mask, pin_mask, gamma are requried parameters" + if algorithm == 'merged': + assert flat_netpin is not None and netpin_start is not None and pin2net_map is not None, "flat_netpin, netpin_start, pin2net_map are requried parameters for algorithm %s" % (algorithm) + elif algorithm == 'atomic': + assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" + self.flat_netpin = flat_netpin + self.netpin_start = netpin_start + self.netpin_values = None + self.pin2net_map = pin2net_map + self.net_weights = net_weights + self.net_mask = net_mask + self.pin_mask = pin_mask + self.gamma = gamma + self.algorithm = algorithm + self.num_threads = num_threads + def forward(self, pos): + if pos.is_cuda: + if self.algorithm == 'atomic': + return LogSumExpWirelengthAtomicFunction.apply(pos, + self.pin2net_map, + self.net_weights, + self.net_mask, + self.gamma + ) + elif self.algorithm == 'merged': + return LogSumExpWirelengthMergedFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.pin2net_map, + self.net_weights, + self.net_mask, + self.pin_mask, + self.gamma, + self.num_threads + ) + else: # only merged for CPU + return LogSumExpWirelengthMergedFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.pin2net_map, + self.net_weights, + self.net_mask, + self.pin_mask, + self.gamma, + self.num_threads + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu new file mode 100644 index 00000000..b7f0e077 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu @@ -0,0 +1,103 @@ +/** + * @file integrate_net_weights_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Jul 2019 + */ + +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "weighted_average_wirelength/src/functional_cuda.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void integrateNetWeights( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + T weight = net_weights[net_id]; + if (net_id >= 0 && net_mask[net_id]) + { + grad_x_tensor[i] *= weight; + grad_y_tensor[i] *= weight; + } + } +} + +template +void integrateNetWeightsCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ) +{ + integrateNetWeights<<>>(pin2net_map, net_mask, net_weights, grad_x_tensor, grad_y_tensor, num_pins); +} + +template +__global__ void integrateNetWeightsforWL( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* partial_wl, ///< x,y interleave + int num_pins + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + T weight = net_weights[net_id]; + if (net_id >= 0 && net_mask[net_id]) + { + partial_wl[2*i] *= weight; + partial_wl[2*i+1] *= weight; + } + } +} + +template +void integrateNetWeightsforWLCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* partial_wl, + int num_pins + ) +{ + integrateNetWeightsforWL<<>>(pin2net_map, net_mask, net_weights, partial_wl, num_pins); +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template void integrateNetWeightsCudaLauncher(\ + const int* pin2net_map, \ + const unsigned char* net_mask, \ + const T* net_weights, \ + T* grad_x_tensor, T* grad_y_tensor, \ + int num_pins \ + ); \ + template void integrateNetWeightsforWLCudaLauncher(\ + const int* pin2net_map, \ + const unsigned char* net_mask, \ + const T* net_weights, \ + T* partial_wl, \ + int num_pins \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp new file mode 100644 index 00000000..f63e0687 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp @@ -0,0 +1,194 @@ +/** + * @file logsumexp_wirelength_cuda_atomic.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jul 2018 + * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeLogSumExpWirelengthCudaAtomicLauncher( + const T* x, const T* y, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + const T* gamma, + T* exp_xy, T* exp_nxy, + T* exp_xy_sum, T* exp_nxy_sum, + V* xy_max, V* xy_min, + T* partial_wl, // wirelength of each net + const T* grad_tensor, + T* grad_x_tensor, T* grad_y_tensor // the gradient is partial total wirelength to partial pin position + ); + +/// @brief add net weights to gradient +template +void integrateNetWeightsCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +typedef int V; + +/// @brief Compute log-sum-exp wirelength according to NTUPlace3 +/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) +/// @param pos cell locations, array of x locations and then y locations +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +std::vector logsumexp_wirelength_atomic_forward( + at::Tensor pos, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma // a scalar tensor + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for -y + at::Tensor partial_wl = at::zeros({4, num_nets}, pos.type()); + at::Tensor exp_xy = at::zeros_like(pos); + at::Tensor exp_nxy = at::zeros_like(pos); + at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.type()); + at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.type()); + + // it is ok for xy_max and xy_min to be integer + // we do not really need accurate max/min, just some values to scale x/y + // therefore, there is no need to scale xy_max and xy_min to improve accuracy + at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); + at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaAtomicLauncher", [&] { + computeLogSumExpWirelengthCudaAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + nullptr, + nullptr, nullptr + ); + }); + + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({1, num_nets})); + } + // significant speedup is achieved by using summation in ATen + auto wl = partial_wl.sum(); + return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from back-propagation +/// @param pos locations of pins +/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) +/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) +/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) +/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +at::Tensor logsumexp_wirelength_atomic_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor exp_xy, at::Tensor exp_nxy, + at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma // a scalar tensor + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(exp_xy); + CHECK_EVEN(exp_xy); + CHECK_CONTIGUOUS(exp_xy); + CHECK_FLAT(exp_nxy); + CHECK_EVEN(exp_nxy); + CHECK_CONTIGUOUS(exp_nxy); + CHECK_FLAT(exp_xy_sum); + CHECK_EVEN(exp_xy_sum); + CHECK_CONTIGUOUS(exp_xy_sum); + CHECK_FLAT(exp_nxy_sum); + CHECK_EVEN(exp_nxy_sum); + CHECK_CONTIGUOUS(exp_nxy_sum); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + at::Tensor grad_out = at::zeros_like(pos); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaAtomicLauncher", [&] { + computeLogSumExpWirelengthCudaAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + nullptr, nullptr, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins + ); + if (net_weights.numel()) + { + integrateNetWeightsCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, + num_pins + ); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_atomic_forward, "LogSumExpWirelength forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_atomic_backward, "LogSumExpWirelength backward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu new file mode 100644 index 00000000..81c50e40 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu @@ -0,0 +1,461 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +// V has to be int, or long long int +template +__global__ void computeMax( + const T* x, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + V* x_max + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicMax(&x_max[net_id], (V)(x[i])); + } + } +} + +// V has to be int, or long long int +template +__global__ void computeMin( + const T* x, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + V* x_min + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicMin(&x_min[net_id], (V)(x[i])); + } + } +} + +template +__global__ void computeExp( + const T* x, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + const T* gamma, + V* x_max, + T* exp_x + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_x[i] = exp((x[i]-x_max[net_id])/(*gamma)); + } + } +} + +template +__global__ void computeNegExp( + const T* x, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + const T* gamma, + V* x_min, + T* exp_nx + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_nx[i] = exp(-(x[i]-x_min[net_id])/(*gamma)); + } + } +} + +template +__global__ void computeExpSum( + const T* exp_x, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + T* exp_x_sum + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicAdd(&exp_x_sum[net_id], exp_x[i]); + } + } +} + +template +__global__ void computeLogSumExp( + const T* exp_x_sum, + const V* x_max, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + const T* gamma, + T* partial_wl + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + if (net_mask[i]) + { + partial_wl[i] = (*gamma)*log(exp_x_sum[i]) + (T)x_max[i]; + } + } +} + +template +__global__ void computeLogSumNegExp( + const T* exp_nx_sum, + const V* x_min, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + const T* gamma, + T* partial_wl + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + if (net_mask[i]) + { + partial_wl[i] = (*gamma)*log(exp_nx_sum[i]) - (T)x_min[i]; + } + } +} + +template +__global__ void computeLogSumExpWirelengthGrad( + const T* exp_x, const T* exp_nx, + const T* exp_x_sum, const T* exp_nx_sum, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + const T* gamma, + const T* grad_tensor, + T* grad_x_tensor + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + grad_x_tensor[i] = (exp_x[i]/exp_x_sum[net_id] - exp_nx[i]/exp_nx_sum[net_id])*(*grad_tensor); + } + } +} + +template +int computeLogSumExpWirelengthCudaAtomicLauncher( + const T* x, const T* y, + const int* pin2net_map, + const unsigned char* net_mask, + int num_nets, + int num_pins, + const T* gamma, + T* exp_xy, T* exp_nxy, + T* exp_xy_sum, T* exp_nxy_sum, + V* xy_max, V* xy_min, + T* partial_wl, // wirelength of each net + const T* grad_tensor, + T* grad_x_tensor, T* grad_y_tensor // the gradient is partial total wirelength to partial pin position + ) +{ + int thread_count = 512; + int block_count_pins = (num_pins + thread_count - 1) / thread_count; + int block_count_nets = (num_nets + thread_count - 1) / thread_count; + + cudaError_t status; + cudaStream_t stream_nx_exp; + cudaStream_t stream_y_exp; + cudaStream_t stream_ny_exp; + status = cudaStreamCreate(&stream_y_exp); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_y_exp\n"); + fflush(stdout); + return 1; + } + + if (grad_tensor) + { + computeLogSumExpWirelengthGrad<<>>( + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + grad_tensor, + grad_x_tensor + ); + computeLogSumExpWirelengthGrad<<>>( + exp_xy+num_pins, exp_nxy+num_pins, + exp_xy_sum+num_nets, exp_nxy_sum+num_nets, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + grad_tensor, + grad_y_tensor + ); + } + else + { + status = cudaStreamCreate(&stream_nx_exp); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_nx_exp\n"); + fflush(stdout); + return 1; + } + status = cudaStreamCreate(&stream_ny_exp); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_ny_exp\n"); + fflush(stdout); + return 1; + } + + // compute max/min + computeMax<<>>( + x, + pin2net_map, + net_mask, + num_nets, + num_pins, + xy_max + ); + computeMin<<>>( + x, + pin2net_map, + net_mask, + num_nets, + num_pins, + xy_min + ); + computeMax<<>>( + y, + pin2net_map, + net_mask, + num_nets, + num_pins, + xy_max+num_nets + ); + computeMin<<>>( + y, + pin2net_map, + net_mask, + num_nets, + num_pins, + xy_min+num_nets + ); + + // compute exp and negative exp + computeExp<<>>( + x, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + xy_max, + exp_xy + ); + computeNegExp<<>>( + x, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + xy_min, + exp_nxy + ); + computeExp<<>>( + y, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + xy_max+num_nets, + exp_xy+num_pins + ); + computeNegExp<<>>( + y, + pin2net_map, + net_mask, + num_nets, + num_pins, + gamma, + xy_min+num_nets, + exp_nxy+num_pins + ); + + // compute exp sum + computeExpSum<<>>( + exp_xy, + pin2net_map, + net_mask, + num_nets, + num_pins, + exp_xy_sum + ); + computeExpSum<<>>( + exp_nxy, + pin2net_map, + net_mask, + num_nets, + num_pins, + exp_nxy_sum + ); + computeExpSum<<>>( + exp_xy+num_pins, + pin2net_map, + net_mask, + num_nets, + num_pins, + exp_xy_sum+num_nets + ); + computeExpSum<<>>( + exp_nxy+num_pins, + pin2net_map, + net_mask, + num_nets, + num_pins, + exp_nxy_sum+num_nets + ); + + // compute log sum exp + computeLogSumExp<<>>( + exp_xy_sum, + xy_max, + pin2net_map, + net_mask, + num_nets, + gamma, + partial_wl + ); + computeLogSumNegExp<<>>( + exp_nxy_sum, + xy_min, + pin2net_map, + net_mask, + num_nets, + gamma, + partial_wl+num_nets + ); + + computeLogSumExp<<>>( + exp_xy_sum+num_nets, + xy_max+num_nets, + pin2net_map, + net_mask, + num_nets, + gamma, + partial_wl+2*num_nets + ); + computeLogSumNegExp<<>>( + exp_nxy_sum+num_nets, + xy_min+num_nets, + pin2net_map, + net_mask, + num_nets, + gamma, + partial_wl+3*num_nets + ); + + // I move out the summation to use ATen + // significant speedup is observed + //sumArray<<<1, 1>>>(partial_wl, 2*num_nets, wl); + + status = cudaStreamDestroy(stream_nx_exp); + if (status != cudaSuccess) + { + printf("stream_nx_exp destroy failed\n"); + fflush(stdout); + return 1; + } + status = cudaStreamDestroy(stream_ny_exp); + if (status != cudaSuccess) + { + printf("stream_ny_exp destroy failed\n"); + fflush(stdout); + return 1; + } + } + + /* destroy stream */ + status = cudaStreamDestroy(stream_y_exp); + if (status != cudaSuccess) + { + printf("stream_y_exp destroy failed\n"); + fflush(stdout); + return 1; + } + + return 0; +} + + +#define REGISTER_KERNEL_LAUNCHER(T, V) \ + template int computeLogSumExpWirelengthCudaAtomicLauncher(\ + const T* x, const T* y, \ + const int* pin2net_map, \ + const unsigned char* net_mask, \ + int num_nets, \ + int num_pins, \ + const T* gamma, \ + T* exp_xy, T* exp_nxy, \ + T* exp_xy_sum, T* exp_nxy_sum,\ + V* xy_max, V* xy_min, \ + T* partial_wl, \ + const T* grad_tensor, \ + T* grad_x_tensor, T* grad_y_tensor \ + ); + +REGISTER_KERNEL_LAUNCHER(float, int); +REGISTER_KERNEL_LAUNCHER(double, int); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp new file mode 100644 index 00000000..86c7389f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp @@ -0,0 +1,167 @@ +/** + * @file hpwl_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeLogSumExpWirelengthCudaLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* gamma, + const T* inv_gamma, + T* partial_wl, + T* grad_intermediate_x, T* grad_intermediate_y + ); + +/// @brief add net weights to gradient +template +void integrateNetWeightsCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute log-sum-exp wirelength according to NTUPlace3 +/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param netpin_values similar to the value array in CSR format, a dummy array of all ones +/// @param pin2net_map pin2net map +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +std::vector logsumexp_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma // a scalar tensor + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + // x, y interleave + at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); + // timed with grad_in yet + at::Tensor grad_intermediate = at::zeros_like(pos); + auto inv_gamma = 1.0 / gamma; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaLauncher", [&] { + computeLogSumExpWirelengthCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins + ); + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({num_nets, 1})); + } + }); + + auto wl = partial_wl.sum(); + return {wl, grad_intermediate}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from back-propagation +/// @param pos locations of pins +/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) +/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) +/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) +/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param pin2net_map pin2net map +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +at::Tensor logsumexp_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor grad_intermediate, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(grad_intermediate); + CHECK_EVEN(grad_intermediate); + CHECK_CONTIGUOUS(grad_intermediate); + + at::Tensor grad_out = grad_intermediate.mul_(grad_pos); + //int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaLauncher", [&] { + if (net_weights.numel()) + { + integrateNetWeightsCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, + num_pins); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_forward, "LogSumExpWirelength forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_backward, "LogSumExpWirelength backward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu new file mode 100644 index 00000000..dd7a2846 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu @@ -0,0 +1,124 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeLogSumExpWirelength( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + const T* gamma, + const T *inv_gamma, + T *partial_wl, + T *grad_intermediate_x, T *grad_intermediate_y + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + int ii = i >> 1; + if (ii < num_nets && net_mask[ii]) + { + const T *values; + T *grads; + if (i & 1) + { + values = y; + grads = grad_intermediate_y; + } + else + { + values = x; + grads = grad_intermediate_x; + } + + // int degree = netpin_start[ii+1]-netpin_start[ii]; + T x_max = -FLT_MAX; + T x_min = FLT_MAX; + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + x_max = max(xx, x_max); + x_min = min(xx, x_min); + } + + T exp_x_sum = 0; + T exp_nx_sum = 0; + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + exp_x_sum += exp_x; + exp_nx_sum += exp_nx; + } + + partial_wl[i] = (log(exp_x_sum) + log(exp_nx_sum)) * (*gamma) + x_max - x_min; + + T reciprocal_exp_x_sum = 1.0 / exp_x_sum; + T reciprocal_exp_nx_sum = 1.0 / exp_nx_sum; + for (int j = netpin_start[ii]; j < netpin_start[ii+1]; ++j) + { + int jj = flat_netpin[j]; + T xx = values[jj]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + grads[jj] = (exp_x*reciprocal_exp_x_sum - exp_nx*reciprocal_exp_nx_sum); + } + } +} + +template +int computeLogSumExpWirelengthCudaLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* gamma, + const T* inv_gamma, + T* partial_wl, + T* grad_intermediate_x, T* grad_intermediate_y + ) +{ + int thread_count = 64; + int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y + + computeLogSumExpWirelength<<>>( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + gamma, + inv_gamma, + partial_wl, + grad_intermediate_x, grad_intermediate_y + ); + + return 0; +} + + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeLogSumExpWirelengthCudaLauncher(\ + const T* x, const T* y, \ + const int* flat_netpin, \ + const int* netpin_start, \ + const unsigned char* net_mask, \ + int num_nets, \ + const T* gamma, \ + const T* inv_gamma, \ + T* partial_wl, \ + T* grad_intermediate_x, T* grad_intermediate_y \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp new file mode 100644 index 00000000..46ea2b96 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp @@ -0,0 +1,231 @@ +/** + * @file logsumexp_wirelength.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 + */ +#include +#include "utility/src/torch.h" +#include "utility/src/Msg.h" +#include "weighted_average_wirelength/src/functional.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +void computeLogSumExpWirelengthLauncher( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + const T* gamma, + const T *inv_gamma, + T *partial_wl, + T *grad_intermediate_x, T *grad_intermediate_y, + int num_threads + ) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + if (net_mask[i]) + { + T x_max = -std::numeric_limits::max(); + T x_min = std::numeric_limits::max(); + T y_max = -std::numeric_limits::max(); + T y_min = std::numeric_limits::max(); + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); + x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); + T yy = y[flat_netpin[j]]; + y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); + y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); + } + + T exp_x_sum = 0; + T exp_nx_sum = 0; + + T exp_y_sum = 0; + T exp_ny_sum = 0; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + exp_x_sum += exp_x; + exp_nx_sum += exp_nx; + + T yy = y[flat_netpin[j]]; + T exp_y = exp((yy - y_max) * (*inv_gamma)); + T exp_ny = exp((y_min - yy) * (*inv_gamma)); + + exp_y_sum += exp_y; + exp_ny_sum += exp_ny; + } + + partial_wl[i] = (log(exp_x_sum) + log(exp_nx_sum)) * (*gamma) + x_max - x_min + + (log(exp_y_sum) + log(exp_ny_sum)) * (*gamma) + y_max - y_min; + + T reciprocal_exp_x_sum = 1.0 / exp_x_sum; + T reciprocal_exp_nx_sum = 1.0 / exp_nx_sum; + T reciprocal_exp_y_sum = 1.0 / exp_y_sum; + T reciprocal_exp_ny_sum = 1.0 / exp_ny_sum; + for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) + { + int jj = flat_netpin[j]; + + T xx = x[jj]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + grad_intermediate_x[jj] = (exp_x*reciprocal_exp_x_sum - exp_nx*reciprocal_exp_nx_sum); + + T yy = y[jj]; + T exp_y = exp((yy - y_max) * (*inv_gamma)); + T exp_ny = exp((y_min - yy) * (*inv_gamma)); + grad_intermediate_y[jj] = (exp_y*reciprocal_exp_y_sum - exp_ny*reciprocal_exp_ny_sum); + } + } + } +} + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x " must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x " must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x " must be contiguous") + +/// @brief Compute log-sum-exp wirelength according to NTUPlace3 +/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +std::vector logsumexp_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma, // a scalar tensor + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + // x, y interleave + at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); + // timed with grad_in yet + at::Tensor grad_intermediate = at::zeros_like(pos); + auto inv_gamma = 1.0 / gamma; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthLauncher", [&] { + computeLogSumExpWirelengthLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins, + num_threads + ); + if (net_weights.numel()) + { + partial_wl.mul_(net_weights); + } + }); + + auto wl = partial_wl.sum(); + return {wl, grad_intermediate}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from back-propagation +/// @param pos locations of pins +/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) +/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) +/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) +/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param gamma a scalar tensor for the parameter in the equation +at::Tensor logsumexp_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor grad_intermediate, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor gamma, // a scalar tensor + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(grad_intermediate); + CHECK_EVEN(grad_intermediate); + CHECK_CONTIGUOUS(grad_intermediate); + + at::Tensor grad_out = grad_intermediate.mul_(grad_pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthLauncher", [&] { + if (net_weights.numel()) + { + integrateNetWeightsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t),//Use instead of net_weights_x + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, + netpin_start.numel() - 1, + num_threads); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_forward, "LogSumExpWirelength forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_backward, "LogSumExpWirelength backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt new file mode 100644 index 00000000..3d02bf3c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt @@ -0,0 +1,37 @@ +set(OP_NAME lut_ff_legalization) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${LEMON_INCLUDE_DIRS} + ) + +set(LINK_LIBS utility_cxx + lemon + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/lut_ff_legalization.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/lut_ff_legalization_cuda.cpp + src/lut_ff_legalization_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py new file mode 100644 index 00000000..a1bfea3e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Rachel Selina (DREAMPlaceFPGA-PL) +# @date Apr 2022 +# + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py new file mode 100644 index 00000000..7eb3cda4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py @@ -0,0 +1,946 @@ +## +# @file lut_ff_legalization.py +# @author Rachel Selina (DREAMPlaceFPGA-PL) +# @date Apr 2022 +# + +import math +import torch +from torch import nn +from torch.autograd import Function +import pdb +import time +import logging +import numpy as np + +import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization_cpp as lut_ff_legalization_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization_cuda as lut_ff_legalization_cuda + +def carry_chain_checker(flat_cc2node_start_map, flat_cc2node_map, inst_curr_detSite, + site2addr_map, site_det_impl_lut, num_sites_y, lutsInSlice, + num_carry_chains, device): + + carry_chain_ck = torch.zeros(num_carry_chains, dtype=torch.int, device=device) + + for idx in range(num_carry_chains): + instId = flat_cc2node_map[flat_cc2node_start_map[idx]].item() + siteId = inst_curr_detSite[instId].item() + columnX = math.floor(siteId/num_sites_y) + currY = math.floor(siteId%num_sites_y) + s_Id = site2addr_map[siteId].item() + + if instId in site_det_impl_lut[s_Id]: + instZ = (site_det_impl_lut[s_Id] == instId).nonzero(as_tuple=True)[0].item() + else: + print("ERROR: Instance: ", instId, " not found in site_det but assigned in inst_curr_detSite for site: ", siteId, " and sIdx: ", s_Id) + + if instZ != 0: + carry_chain_ck[idx] = carry_chain_ck[idx]+1 + print("ERROR: INCORRECT START: For idx: ", idx , " start inst: ", instId, " has loc: ", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item()) + + yOffset = 0 + cnt = 0 + if instZ == 0: + cnt = 1 + for ccIdx in range(flat_cc2node_start_map[idx]+1, flat_cc2node_start_map[idx+1]): + instId = flat_cc2node_map[ccIdx].item() + siteId = inst_curr_detSite[instId].item() + nextX = math.floor(siteId/num_sites_y) + nextY = math.floor(siteId%num_sites_y) + s_Id = site2addr_map[siteId].item() + + if instId in site_det_impl_lut[s_Id]: + nextZ = (site_det_impl_lut[s_Id] == instId).nonzero(as_tuple=True)[0].item() + else: + print("ERROR: Instance: ", instId, " not found in site_det but assigned in inst_curr_detSite for site: ", siteId, " and sIdx: ", s_Id) + + if columnX != nextX or nextY != currY + yOffset or nextZ != cnt: + carry_chain_ck[idx] = carry_chain_ck[idx]+1 + print("ERROR: INCORRECT ") + if columnX != nextX: + print(" X ") + if nextY != currY + yOffset: + print(" Y ") + if nextZ != cnt: + print(" Z ") + print(" Location for carry chain (", idx , ") with inst: ", instId, " at (", nextX, ", ", nextY, ", ", nextZ, ")") + + cnt = cnt + 1 + if cnt == lutsInSlice: + cnt = 0 + yOffset = yOffset - 1 + + if carry_chain_ck[idx] > 0: + print("ERROR: Check carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") + if carry_chain_ck.sum() > 0: + logging.info("ERROR: %d Carry-chains not correct" %(carry_chain_ck.sum())) + +def carry_chain_checker_loc(flat_cc2node_start_map, flat_cc2node_map, updXloc, updYloc, + updZloc, HALF_SLICE_CAPACITY, lutsInSlice, num_carry_chains, device): + + carry_chain_ck = torch.zeros(num_carry_chains, dtype=torch.int, device=device) + for idx in range(num_carry_chains): + instId = flat_cc2node_map[flat_cc2node_start_map[idx]].item() + columnX = updXloc[instId].item() + currY = updYloc[instId].item() + instZ = updZloc[instId].item() + + if instZ != 0 and instZ != HALF_SLICE_CAPACITY: + carry_chain_ck[idx] = carry_chain_ck[idx]+1 + print("ERROR: INCORRECT START: For idx: ", idx , " start inst: ", instId, " has loc: ", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item()) + + yOffset = 0 + cnt = 0 + if instZ == 0: + cnt = 1 + elif instZ == HALF_SLICE_CAPACITY: + cnt = 1 + HALF_SLICE_CAPACITY + for ccIdx in range(flat_cc2node_start_map[idx]+1, flat_cc2node_start_map[idx+1]): + instId = flat_cc2node_map[ccIdx].item() + + if columnX != updXloc[instId].item() or updYloc[instId].item() != currY + yOffset or updZloc[instId].item() != cnt: + carry_chain_ck[idx] = carry_chain_ck[idx]+1 + print("ERROR: INCORRECT ") + if columnX != updXloc[instId].item(): + print(" X ") + if updYloc[instId].item() != currY + yOffset: + print(" Y ") + if updZloc[instId].item() != cnt: + print(" Z ") + print(" Location for carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") + + cnt = cnt + 1 + if cnt == lutsInSlice: + cnt = 0 + yOffset = yOffset - 1 + + if carry_chain_ck[idx] > 0: + print("ERROR: Check carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") + if carry_chain_ck.sum() > 0: + logging.info("ERROR: %d Carry-chains not correct" %(carry_chain_ck.sum())) + +def compute_remaining_slice_sites(slice_sites, site_det_sig_idx, addr2site_map): + + assigned_slice_site_mask = np.zeros(slice_sites.shape[0], dtype=bool) + assigned_sites = addr2site_map[torch.where(site_det_sig_idx > 0)[0].long()].cpu().detach().numpy() + + indices = np.where(np.in1d(slice_sites, assigned_sites))[0] + assigned_slice_site_mask[indices] = True + + return ~assigned_slice_site_mask + +class LegalizeCLB(nn.Module): + def __init__(self, data_collections, placedb, net_wts, #inst_areas, + site_types, num_threads, device): + + super(LegalizeCLB, self).__init__() + + if placedb.num_ccNodes == 0: + self.num_movable_nodes=placedb.num_movable_nodes + self.num_nodes=placedb.num_physical_nodes + self.lut_flop_indices=data_collections.flop_lut_indices + self.is_mlab_node = data_collections.is_mlab_node.int() + self.flop2ctrlSetId_map=data_collections.flop2ctrlSetId_map + self.flop_ctrlSets=data_collections.flop_ctrlSets + self.pin2node_map=data_collections.pin2node_map + self.flat_node2pin_map=data_collections.flat_node2pin_map + self.flat_node2pin_start_map=data_collections.flat_node2pin_start_map + self.node2fence_region_map=data_collections.node2fence_region_map + self.node2outpinIdx_map=data_collections.node2outpinIdx_map + self.node2pincount=data_collections.node2pincount_map + self.lut_type=data_collections.lut_type + self.pin_offset_x=data_collections.lg_pin_offset_x + self.pin_offset_y=data_collections.lg_pin_offset_y + self.node_size_x=data_collections.node_size_x[:self.num_nodes] + self.node_size_y=data_collections.node_size_y[:self.num_nodes] + self.flat_cc2node_map = torch.from_numpy(placedb.flat_cc2node_map).to(dtype=torch.int, device=device) + self.flat_cc2node_start_map = torch.from_numpy(placedb.flat_cc2node_start_map).to(dtype=torch.int, device=device) + else: + self.num_movable_nodes=placedb.org_num_movable_nodes + self.num_nodes=placedb.org_num_physical_nodes + self.lut_flop_indices=data_collections.org_flop_lut_indices + self.is_mlab_node = data_collections.org_is_mlab_node.int() + self.flop2ctrlSetId_map=data_collections.org_flop2ctrlSetId_map + self.flop_ctrlSets=data_collections.org_flop_ctrlSets + self.pin2node_map=data_collections.org_pin2node_map + self.flat_node2pin_map=data_collections.org_flat_node2pin_map + self.flat_node2pin_start_map=data_collections.org_flat_node2pin_start_map + self.node2fence_region_map=data_collections.org_node2fence_region_map + self.node2outpinIdx_map=data_collections.org_node2outpinIdx_map + self.node2pincount=data_collections.org_node2pincount_map + self.lut_type=data_collections.org_lut_type + self.pin_offset_x=data_collections.org_lg_pin_offset_x + self.pin_offset_y=data_collections.org_lg_pin_offset_y + self.node_size_x=data_collections.org_node_size_x[:self.num_nodes] + self.node_size_y=data_collections.org_node_size_y[:self.num_nodes] + self.flat_cc2node_map = torch.from_numpy(placedb.org_flat_cc2node_map).to(dtype=torch.int, device=device) + self.flat_cc2node_start_map = torch.from_numpy(placedb.org_flat_cc2node_start_map).to(dtype=torch.int, device=device) + + self.num_lutflops=self.lut_flop_indices.shape[0] + + self.pin_typeIds=data_collections.pin_typeIds + self.pin2net_map=data_collections.pin2net_map + self.flat_net2pin_map=data_collections.flat_net2pin_map + self.flat_net2pin_start_map=data_collections.flat_net2pin_start_map + + self.site_xy=data_collections.lg_siteXYs + self.net2pincount=data_collections.net2pincount_map + self.spiral_accessor=data_collections.spiral_accessor + + self.num_nets=placedb.num_nets + self.num_sites_x=placedb.num_sites_x + self.num_sites_y=placedb.num_sites_y + self.xWirelenWt=placedb.xWirelenWt + self.yWirelenWt=placedb.yWirelenWt + self.nbrDistEnd=placedb.nbrDistEnd + + self.xl = placedb.xl + self.yl = placedb.yl + self.xh = placedb.xh + self.yh = placedb.yh + + self.net_wts=net_wts + #self.inst_areas=inst_areas + self.site_types=site_types + self.num_threads=num_threads + self.device=device + self.dtype = self.node_size_x.dtype + + self.sliceId = placedb.sSLICEIdx + self.lutId = placedb.rLUTIdx + self.ffId = placedb.rFFIdx + + self.lut_flop_mask = torch.logical_or(self.node2fence_region_map == self.lutId,self.node2fence_region_map == self.ffId) + + lutName = [key for key, val in placedb.rsrcType2indexMap.items() if val==placedb.rLUTIdx][0] + ffName = [key for key, val in placedb.rsrcType2indexMap.items() if val==placedb.rFFIdx][0] + + self.lutsInSlice = placedb.siteRsrc2CountMap[lutName] + ffsInSlice = placedb.siteRsrc2CountMap[ffName] + + #mlabs + self.mlab_indices = torch.where(self.is_mlab_node == 1)[0].to(torch.int32) + self.num_mlab_nodes = self.mlab_indices.shape[0] + + #Carry chains + self.num_carry_chains = placedb.num_carry_chains + self.num_ccNodes = placedb.num_ccNodes + + self.slice_minX = int(placedb.slice_x_min) + self.slice_maxX = int(placedb.slice_x_max) + self.slice_minY = int(placedb.slice_y_min) + self.slice_maxY = int(placedb.slice_y_max) + self.sliceSiteXYs = placedb.sliceSiteXYs + self.slice_sites = (self.sliceSiteXYs[:,0]*self.num_sites_y + self.sliceSiteXYs[:,1]).astype(np.int32) + + #Architecture specific values + for el in placedb.sliceFFCtrls: + if 'clk'.casefold() in el[0].casefold() or 'ck'.casefold() in el[0].casefold(): + self.CKSR_IN_CLB = el[1] + else: + self.CE_IN_CLB = el[1] + + self.half_ctrl_mode = 0 #Use for FF ctrl signals + + if placedb.sliceFF_ctrl_mode == "HALF": + self.CKSR_IN_CLB *= 2 + self.CE_IN_CLB *= 2 + self.half_ctrl_mode = 1 + + for el in placedb.sliceElements: + if lutName in el[0]: + self.BLE_CAPACITY = el[1] + break + + self.ff_ctrl_type = placedb.ff_ctrl_type + self.netShareScoreMaxNetDegree = self.lutsInSlice + self.SLICE_CAPACITY = placedb.SLICE_CAPACITY + self.HALF_SLICE_CAPACITY = placedb.HALF_SLICE_CAPACITY + self.NUM_BLE_PER_SLICE = int(self.SLICE_CAPACITY/self.BLE_CAPACITY) + self.NUM_BLE_PER_HALF_SLICE = int(self.HALF_SLICE_CAPACITY/self.BLE_CAPACITY) + self.extended_ctrlSets = torch.from_numpy(placedb.extended_ctrlSets).to(dtype=torch.int, device=self.device) + self.ext_ctrlSet_start_map = torch.from_numpy(placedb.ext_ctrlSet_start_map).to(dtype=torch.int, device=self.device) + + self.PQ_IDX = 10 + self.SCL_IDX = 128 + + self.SIG_IDX = self.lutsInSlice + ffsInSlice + + #Initialize required constants + self.nbrDistBeg = 1.0 + self.nbrDistIncr = 1.0 + self.extNetCountWt = 0.3 + self.wirelenImprovWt = 0.1 + self.int_min_val = -2147483647 + self.WLscoreMaxNetDegree = 100 + self.maxList = max(128, math.ceil(0.005 * self.num_nodes)) #Based on empirical results from elfPlace + self.numGroups = math.ceil((self.nbrDistEnd-self.nbrDistBeg)/self.nbrDistIncr) + 1 + + #LUT specific entries + #lut type that occupies entire sliceunit + self.lutTypeInSliceUnit = placedb.lutTypeInSliceUnit + #max shared inputs to luts in a sliceunit + self.lut_maxShared = placedb.lut_maxShared + + #Initialize required tensors + self.net_bbox = torch.zeros(self.num_nets*4, dtype=self.dtype, device=device) + + self.net_pinIdArrayX = torch.zeros(len(self.flat_net2pin_map), dtype=torch.int, device=device) + self.net_pinIdArrayY = torch.zeros_like(self.net_pinIdArrayX) #len(flat_net2pin) + + self.flat_node2precluster_map = torch.ones((self.num_nodes,3), dtype=torch.int, device=device) + self.flat_node2precluster_map *= -1 + self.flat_node2precluster_map[:,0] = torch.arange(self.num_nodes, dtype=torch.int, device=device) + self.flat_node2prclstrCount = torch.ones(self.num_nodes, dtype=torch.int, device=device) + + #Instance Candidates + self.inst_curr_detSite = torch.zeros_like(self.flat_node2prclstrCount) #num_nodes + self.inst_curr_detSite[self.lut_flop_mask] = -1 + self.inst_curr_bestSite = torch.zeros_like(self.inst_curr_detSite) #num_nodes + self.inst_curr_bestSite[self.lut_flop_mask] = -1 + self.inst_curr_bestScoreImprov = torch.zeros(self.num_nodes, dtype=self.dtype, device=device) + self.inst_curr_bestScoreImprov[self.lut_flop_mask] = -10000.0 + + self.inst_next_detSite = torch.zeros_like(self.inst_curr_detSite) #num_nodes + self.inst_next_detSite[self.lut_flop_mask] = -1 + self.inst_next_bestSite = torch.zeros_like(self.inst_next_detSite) #num_nodes + self.inst_next_bestSite[self.lut_flop_mask] = -1 + self.inst_next_bestScoreImprov = torch.zeros_like(self.inst_curr_bestScoreImprov) #num_nodes + self.inst_next_bestScoreImprov[self.lut_flop_mask] = -10000.0 + + self.num_clb_sites = torch.bincount(self.site_types.flatten())[self.sliceId].item() + #Map from mem addr to CLB site + self.addr2site_map = self.site_types.flatten().nonzero(as_tuple=True)[0] + #Map from CLB site to mem addr + self.site2addr_map = torch.ones(self.num_sites_x*self.num_sites_y, dtype=torch.int, device=device) + self.site2addr_map *= -1 + self.site2addr_map[self.addr2site_map] = torch.arange(self.num_clb_sites, dtype=torch.int, device=device) + self.addr2site_map = self.addr2site_map.int() + + #Site Neighbors + self.site_nbrList = torch.zeros((self.num_clb_sites, self.maxList), dtype=torch.int, device=device) + self.site_nbr = torch.zeros_like(self.site_nbrList) #num_clb_sites * maxList + self.site_nbr_idx = torch.zeros(self.num_clb_sites, dtype=torch.int, device=device) + self.site_nbrRanges = torch.zeros((self.num_clb_sites, self.numGroups+1), dtype=torch.int, device=device) + self.site_nbrRanges_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_nbrGroup_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + ##Site Candidates + self.site_det_score = torch.zeros(self.num_clb_sites, dtype=self.dtype, device=device) + self.site_det_siteId = torch.ones_like(self.site_nbr_idx) #num_clb_sites + self.site_det_siteId *= -1 + self.site_det_impl_lut = torch.ones((self.num_clb_sites, self.SLICE_CAPACITY), dtype=torch.int, device=device) + self.site_det_impl_lut *= -1 + self.site_det_impl_ff = torch.ones_like(self.site_det_impl_lut) #num_clb_sites * SLICE_CAPACITY + self.site_det_impl_ff *= -1 + self.site_det_impl_cksr = torch.ones((self.num_clb_sites, self.CKSR_IN_CLB), dtype=torch.int, device=device) + self.site_det_impl_cksr *= -1 + self.site_det_impl_ce = torch.ones((self.num_clb_sites, self.CE_IN_CLB), dtype=torch.int, device=device) + self.site_det_impl_ce *= -1 + self.site_det_sig = torch.ones((self.num_clb_sites, self.SIG_IDX), dtype=torch.int, device=device) + self.site_det_sig *= -1 + self.site_det_sig_idx = torch.zeros_like(self.site_det_siteId) #num_clb_sites + + self.site_curr_stable = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_curr_scl_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_curr_scl_validIdx = torch.ones((self.num_clb_sites, self.SCL_IDX), dtype=torch.int, device=device) + self.site_curr_scl_validIdx *= -1 + self.site_curr_scl_siteId = torch.ones((self.num_clb_sites, self.SCL_IDX), dtype=torch.int, device=device) #num_clb_sites * SCL_IDX + self.site_curr_scl_siteId *= -1 + self.site_curr_scl_score = torch.zeros((self.num_clb_sites, self.SCL_IDX), dtype=self.dtype, device=device) + self.site_curr_scl_impl_lut = torch.ones((self.num_clb_sites, self.SCL_IDX, self.SLICE_CAPACITY), dtype=torch.int, device=device) + self.site_curr_scl_impl_lut *= -1 + self.site_curr_scl_impl_ff = torch.ones_like(self.site_curr_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY + self.site_curr_scl_impl_ff *= -1 + self.site_curr_scl_impl_cksr = torch.ones((self.num_clb_sites, self.SCL_IDX, self.CKSR_IN_CLB), dtype=torch.int, device=device) + self.site_curr_scl_impl_cksr *= -1 + self.site_curr_scl_impl_ce = torch.ones((self.num_clb_sites, self.SCL_IDX, self.CE_IN_CLB), dtype=torch.int, device=device) + self.site_curr_scl_impl_ce *= -1 + self.site_curr_scl_sig = torch.ones((self.num_clb_sites, self.SCL_IDX, self.SIG_IDX), dtype=torch.int, device=device) + self.site_curr_scl_sig *= -1 + self.site_curr_scl_sig_idx = torch.zeros_like(self.site_curr_scl_siteId) #num_clb_sites * SCL_IDX + + self.site_curr_pq_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_curr_pq_top_idx = torch.ones_like(self.site_nbr_idx) #num_clb_sites + self.site_curr_pq_top_idx *= -1 + self.site_curr_pq_score = torch.zeros((self.num_clb_sites, self.PQ_IDX), dtype=self.dtype, device=device) + self.site_curr_pq_validIdx = torch.ones((self.num_clb_sites, self.PQ_IDX), dtype=torch.int, device=device) + self.site_curr_pq_validIdx *= -1 + self.site_curr_pq_siteId = torch.ones((self.num_clb_sites, self.PQ_IDX), dtype=torch.int, device=device) #num_clb_sites * PQ_IDX + self.site_curr_pq_siteId *= -1 + self.site_curr_pq_sig = torch.ones((self.num_clb_sites, self.PQ_IDX, self.SIG_IDX), dtype=torch.int, device=device) + self.site_curr_pq_sig *= -1 + self.site_curr_pq_sig_idx = torch.zeros_like(self.site_curr_pq_siteId) #num_clb_sites * PQ_IDX + self.site_curr_pq_impl_lut = torch.ones((self.num_clb_sites, self.PQ_IDX, self.SLICE_CAPACITY), dtype=torch.int, device=device) + self.site_curr_pq_impl_lut *= -1 + self.site_curr_pq_impl_ff = torch.ones_like(self.site_curr_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY. + self.site_curr_pq_impl_ff *= -1 + self.site_curr_pq_impl_cksr = torch.ones((self.num_clb_sites, self.PQ_IDX, self.CKSR_IN_CLB), dtype=torch.int, device=device) + self.site_curr_pq_impl_cksr *= -1 + self.site_curr_pq_impl_ce = torch.ones((self.num_clb_sites, self.PQ_IDX, self.CE_IN_CLB), dtype=torch.int, device=device) + self.site_curr_pq_impl_ce *= -1 + + self.site_next_stable = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_next_scl_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_next_scl_validIdx = torch.ones_like(self.site_curr_scl_validIdx) #num_clb_sites * SCL_IDX + self.site_next_scl_validIdx *= -1 + self.site_next_scl_siteId = torch.ones_like(self.site_curr_scl_siteId) #num_clb_sites * SCL_IDX + self.site_next_scl_siteId *= -1 + self.site_next_scl_score = torch.zeros_like(self.site_curr_scl_score) #num_clb_sites * SCL_IDX + self.site_next_scl_impl_lut = torch.ones_like(self.site_curr_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY + self.site_next_scl_impl_lut *= -1 + self.site_next_scl_impl_ff = torch.ones_like(self.site_next_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY + self.site_next_scl_impl_ff *= -1 + self.site_next_scl_impl_cksr = torch.ones_like(self.site_curr_scl_impl_cksr) #num_clb_sites * SCL_IDX * CKSR_IN_CLB + self.site_next_scl_impl_cksr *= -1 + self.site_next_scl_impl_ce = torch.ones_like(self.site_curr_scl_impl_ce) #num_clb_sites * SCL_IDX * CE_IN_CLB + self.site_next_scl_impl_ce *= -1 + self.site_next_scl_sig = torch.ones_like(self.site_curr_scl_sig) #num_clb_sites * SCL_IDX * SIG_IDX + self.site_next_scl_sig *= -1 + self.site_next_scl_sig_idx = torch.zeros_like(self.site_next_scl_siteId) #num_clb_sites * SCL_IDX + + self.site_next_pq_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites + self.site_next_pq_top_idx = torch.ones_like(self.site_nbr_idx) #num_clb_sites + self.site_next_pq_top_idx *= -1 + self.site_next_pq_score = torch.zeros_like(self.site_curr_pq_score) #num_clb_sites * PQ_IDX + self.site_next_pq_validIdx = torch.ones_like(self.site_curr_pq_validIdx) #num_clb_sites * PQ_IDX + self.site_next_pq_validIdx *= -1 + self.site_next_pq_siteId = torch.ones_like(self.site_curr_pq_siteId) #num_clb_sites * PQ_IDX + self.site_next_pq_siteId *= -1 + self.site_next_pq_sig = torch.ones_like(self.site_curr_pq_sig) #num_clb_sites * PQ_IDX * SIG_IDX + self.site_next_pq_sig *= -1 + self.site_next_pq_sig_idx = torch.zeros_like(self.site_curr_pq_validIdx) #num_clb_sites * PQ_IDX + self.site_next_pq_impl_lut = torch.ones_like(self.site_curr_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY + self.site_next_pq_impl_lut *= -1 + self.site_next_pq_impl_ff = torch.ones_like(self.site_next_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY + self.site_next_pq_impl_ff *= -1 + self.site_next_pq_impl_cksr = torch.ones_like(self.site_curr_pq_impl_cksr) #num_clb_sites * PQ_IDX * CKSR_IN_CLB + self.site_next_pq_impl_cksr *= -1 + self.site_next_pq_impl_ce = torch.ones_like(self.site_curr_pq_impl_ce) #num_clb_sites * PQ_IDX * CE_IN_CLB + self.site_next_pq_impl_ce *= -1 + + self.inst_score_improv = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) + self.inst_score_improv[self.lut_flop_mask] = self.int_min_val + self.site_score_improv = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) + self.site_score_improv *= self.int_min_val + + self.special_nodes = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) + self.special_nodes[self.flat_cc2node_map.long()] = 1 + + def initialize(self, pos, wlPrecond, sorted_node_map, sorted_node_idx, + sorted_net_map, sorted_net_idx, sorted_pin_map): + + tt = time.time() + + preClusteringMaxDist = 4.0 + maxD = math.ceil(self.nbrDistEnd) + 1 + spiralBegin = 0 + spiralEnd_maxD = 2 * (maxD + 1) * maxD + 1 + spiralEnd = self.spiral_accessor.shape[0] #Entire chip! + + #Handling carry chains + self.sites_with_special_nodes = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) + self.is_mlab_site = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) + + carry_chain_displacements = torch.zeros(self.num_nodes, dtype=pos.dtype, device=self.device) + + if pos.is_cuda: + + lut_ff_legalization_cuda.initLegalization(pos, self.pin_offset_x, self.pin_offset_y, + sorted_net_idx, sorted_node_map, sorted_node_idx, self.flat_net2pin_map, + self.flat_net2pin_start_map, self.flop2ctrlSetId_map, self.flop_ctrlSets, + self.node2fence_region_map, self.node2outpinIdx_map, self.pin2net_map, + self.pin2node_map, self.pin_typeIds, self.net2pincount, self.is_mlab_node, + preClusteringMaxDist, self.num_nets, self.num_nodes, self.lutId, + self.ffId, self.WLscoreMaxNetDegree, + self.net_bbox, self.net_pinIdArrayX, self.net_pinIdArrayY, + self.flat_node2precluster_map, self.flat_node2prclstrCount) + + + if self.num_carry_chains > 0: + + #Handle carry-chains and arithmetic share chains + cpu_carry_chain_displacements = carry_chain_displacements.cpu() + cpu_site_det_score = self.site_det_score.cpu() + cpu_inst_curr_bestScoreImprov = self.inst_curr_bestScoreImprov.cpu() + cpu_inst_next_bestScoreImprov = self.inst_next_bestScoreImprov.cpu() + cpu_sites_with_carry_chain = self.sites_with_special_nodes.cpu() + cpu_inst_curr_detSite = self.inst_curr_detSite.cpu() + cpu_inst_curr_bestSite = self.inst_curr_bestSite.cpu() + cpu_inst_next_detSite = self.inst_next_detSite.cpu() + cpu_inst_next_bestSite = self.inst_next_bestSite.cpu() + cpu_site_det_siteId = self.site_det_siteId.cpu() + cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() + cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() + cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() + + + #Legalize carry chains + lut_ff_legalization_cpp.legalizeCarryChain( + pos.cpu(), torch.flatten(self.site_xy).cpu(), wlPrecond.cpu(), + torch.flatten(self.spiral_accessor).cpu(), torch.flatten(self.site_types).cpu(), + self.site2addr_map.cpu(), self.flat_cc2node_start_map.cpu(), + self.flat_cc2node_map.cpu(), spiralBegin, spiralEnd, self.num_sites_x, + self.num_sites_y, self.sliceId, self.SIG_IDX, self.SLICE_CAPACITY, + self.num_carry_chains, self.lutsInSlice, self.slice_minX, self.slice_maxX, + self.slice_minY, self.slice_maxY, cpu_carry_chain_displacements, + cpu_site_det_score, cpu_inst_curr_bestScoreImprov, cpu_inst_next_bestScoreImprov, + cpu_sites_with_carry_chain, cpu_inst_curr_detSite, cpu_inst_curr_bestSite, + cpu_inst_next_detSite, cpu_inst_next_bestSite, cpu_site_det_siteId, + cpu_site_det_sig, cpu_site_det_sig_idx, cpu_site_det_impl_lut, self.num_threads) + + carry_chain_displacements.data.copy_(cpu_carry_chain_displacements) + self.site_det_score.data.copy_(cpu_site_det_score) + self.inst_curr_bestScoreImprov.data.copy_(cpu_inst_curr_bestScoreImprov) + self.inst_next_bestScoreImprov.data.copy_(cpu_inst_next_bestScoreImprov) + self.sites_with_special_nodes.data.copy_(cpu_sites_with_carry_chain) + self.inst_curr_detSite.data.copy_(cpu_inst_curr_detSite) + self.inst_curr_bestSite.data.copy_(cpu_inst_curr_bestSite) + self.inst_next_detSite.data.copy_(cpu_inst_next_detSite) + self.inst_next_bestSite.data.copy_(cpu_inst_next_bestSite) + self.site_det_siteId.data.copy_(cpu_site_det_siteId) + torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig) + self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx) + torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut) + + logging.info("%d carry-chains legalized with max and avg displacements: (%f, %f)" + % (self.num_carry_chains, carry_chain_displacements.max(), carry_chain_displacements.sum()/self.num_carry_chains)) + + #TODO - When mlabs are treated as a type of LUT + ##Legalize mlabs if any + if self.num_mlab_nodes > 0: + + lg_max_dist_init=self.nbrDistEnd + lg_max_dist_incr=self.nbrDistIncr + lg_flow_cost_scale=100.0 + + #Remove already assigned slice sites if any + rem_slice_sites_mask = compute_remaining_slice_sites(self.slice_sites, self.site_det_sig_idx, self.addr2site_map) + num_sites = rem_slice_sites_mask.sum() + num_total_nodes = pos.numel()//2 + + locX = pos[:self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() + locY = pos[num_total_nodes:num_total_nodes+self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() + precondWL = wlPrecond[self.is_mlab_node.bool()].cpu().detach().numpy() + + movVal = np.zeros(2, dtype=np.float32).tolist() + outLoc = np.zeros(2*self.num_mlab_nodes, dtype=np.float32).tolist() + + lut_ff_legalization_cpp.minCostFlow(locX, locY, num_sites, self.num_mlab_nodes, self.sliceSiteXYs[rem_slice_sites_mask].flatten(), + precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) + + outLoc=np.array(outLoc) + mlab_locX = torch.from_numpy(outLoc[:self.num_mlab_nodes]).to(dtype=pos.dtype, device=self.device) + mlab_locY = torch.from_numpy(outLoc[self.num_mlab_nodes:]).to(dtype=pos.dtype, device=self.device) + + mlab_displacements = torch.zeros(self.num_mlab_nodes, dtype=pos.dtype, device=self.device) + + lut_ff_legalization_cuda.legalizeMlab(pos, torch.flatten(self.site_xy), + mlab_locX, mlab_locY, self.mlab_indices, self.site2addr_map, + self.num_mlab_nodes, self.num_sites_y, self.SIG_IDX, self.SLICE_CAPACITY, + mlab_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, + self.inst_next_bestScoreImprov, self.site_det_siteId, self.site_det_sig_idx, + self.site_det_sig, self.site_det_impl_lut, self.inst_curr_detSite, + self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, + self.sites_with_special_nodes) + + logging.info("%d mlabs legalized with max and avg displacements: (%f, %f)" + % (self.num_mlab_nodes, mlab_displacements.max(), mlab_displacements.sum()/self.num_mlab_nodes)) + + mlab_sites = self.site2addr_map[self.inst_curr_detSite[torch.where(self.is_mlab_node == 1)[0]].long()] + self.is_mlab_site[mlab_sites.long()] = 1 + + ## Initialize Site Neighbors ## + cpu_site_curr_scl_score = torch.flatten(self.site_curr_scl_score).cpu() + cpu_site_curr_scl_siteId = torch.flatten(self.site_curr_scl_siteId).cpu() + cpu_site_curr_scl_validIdx = torch.flatten(self.site_curr_scl_validIdx).cpu() + cpu_site_curr_scl_idx = self.site_curr_scl_idx.cpu() + cpu_site_curr_scl_sig = torch.flatten(self.site_curr_scl_sig).cpu() + cpu_site_curr_scl_sig_idx = torch.flatten(self.site_curr_scl_sig_idx).cpu() + cpu_site_curr_scl_impl_lut = torch.flatten(self.site_curr_scl_impl_lut).cpu() + cpu_site_nbrRanges = torch.flatten(self.site_nbrRanges).cpu() + cpu_site_nbrRanges_idx = self.site_nbrRanges_idx.cpu() + cpu_site_nbrList = torch.flatten(self.site_nbrList).cpu() + cpu_site_nbr = torch.flatten(self.site_nbr).cpu() + cpu_site_nbr_idx = self.site_nbr_idx.cpu() + cpu_site_nbrGroup_idx = self.site_nbrGroup_idx.cpu() + cpu_site_det_siteId = self.site_det_siteId.cpu() + cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() + cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() + cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() + + lut_ff_legalization_cpp.initSiteNbrs( + pos.cpu(), wlPrecond.cpu(), torch.flatten(self.site_xy).cpu(), self.site_det_score.cpu(), + sorted_node_idx.cpu(), self.node2fence_region_map.cpu(), torch.flatten(self.site_types).cpu(), + torch.flatten(self.spiral_accessor).cpu(), self.site2addr_map.cpu(), self.addr2site_map.cpu(), + torch.flatten(self.flat_node2precluster_map).cpu(), self.flat_node2prclstrCount.cpu(), + self.is_mlab_node.cpu(), self.is_mlab_site.cpu(), self.sites_with_special_nodes.cpu(), self.nbrDistEnd, + self.nbrDistBeg, self.nbrDistIncr, self.lutId, self.ffId, self.sliceId, self.num_nodes, + self.num_sites_x, self.num_sites_y, self.num_clb_sites, self.SCL_IDX, self.SIG_IDX, + self.SLICE_CAPACITY, self.numGroups, self.maxList, spiralBegin, spiralEnd_maxD, + cpu_site_curr_scl_score, cpu_site_curr_scl_siteId, cpu_site_curr_scl_validIdx, + cpu_site_curr_scl_idx, cpu_site_curr_scl_sig, cpu_site_curr_scl_sig_idx, + cpu_site_curr_scl_impl_lut, cpu_site_nbrRanges, cpu_site_nbrRanges_idx, + cpu_site_nbrList, cpu_site_nbr, cpu_site_nbr_idx, cpu_site_nbrGroup_idx, cpu_site_det_siteId, + cpu_site_det_sig, cpu_site_det_sig_idx, cpu_site_det_impl_lut, self.num_threads) + + torch.flatten(self.site_curr_scl_score).data.copy_(cpu_site_curr_scl_score) + torch.flatten(self.site_curr_scl_siteId).data.copy_(cpu_site_curr_scl_siteId) + torch.flatten(self.site_curr_scl_validIdx).data.copy_(cpu_site_curr_scl_validIdx) + self.site_curr_scl_idx.data.copy_(cpu_site_curr_scl_idx) + torch.flatten(self.site_curr_scl_sig).data.copy_(cpu_site_curr_scl_sig) + torch.flatten(self.site_curr_scl_sig_idx).data.copy_(cpu_site_curr_scl_sig_idx) + torch.flatten(self.site_curr_scl_impl_lut).data.copy_(cpu_site_curr_scl_impl_lut) + torch.flatten(self.site_nbrRanges).data.copy_(cpu_site_nbrRanges) + self.site_nbrRanges_idx.data.copy_(cpu_site_nbrRanges_idx) + torch.flatten(self.site_nbrList).data.copy_(cpu_site_nbrList.data) + torch.flatten(self.site_nbr).data.copy_(cpu_site_nbr) + self.site_nbr_idx.data.copy_(cpu_site_nbr_idx) + self.site_nbrGroup_idx.data.copy_(cpu_site_nbrGroup_idx) + self.site_det_siteId.data.copy_(cpu_site_det_siteId) + torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig) + self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx) + torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut) + + else: + lut_ff_legalization_cpp.initializeLG( + pos, self.pin_offset_x, self.pin_offset_y, sorted_net_idx, sorted_node_map, sorted_node_idx, + self.flat_net2pin_map, self.flat_net2pin_start_map, self.flop2ctrlSetId_map, self.flop_ctrlSets, + self.node2fence_region_map, self.node2outpinIdx_map, self.pin2net_map, self.pin2node_map, + self.pin_typeIds, self.net2pincount, self.is_mlab_node, preClusteringMaxDist, self.lutId, + self.ffId, self.num_nets, self.num_nodes, self.num_threads, + self.WLscoreMaxNetDegree, self.net_bbox, self.net_pinIdArrayX, self.net_pinIdArrayY, + self.flat_node2precluster_map, self.flat_node2prclstrCount) + + if self.num_carry_chains > 0: + + #Legalize carry chains and initialize site neighbors accordingly + lut_ff_legalization_cpp.legalizeCarryChain( + pos, torch.flatten(self.site_xy), wlPrecond, torch.flatten(self.spiral_accessor), + torch.flatten(self.site_types), self.site2addr_map, self.flat_cc2node_start_map, + self.flat_cc2node_map, spiralBegin, spiralEnd, self.num_sites_x, self.num_sites_y, + self.sliceId, self.SIG_IDX, self.SLICE_CAPACITY, self.num_carry_chains, self.lutsInSlice, + self.slice_minX, self.slice_maxX, self.slice_minY, self.slice_maxY, + carry_chain_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, + self.inst_next_bestScoreImprov, self.sites_with_special_nodes, self.inst_curr_detSite, + self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, + self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, + self.site_det_impl_lut, self.num_threads) + + logging.info("%d carry-chains legalized with max and avg displacements: (%f, %f)" % + (self.num_carry_chains, carry_chain_displacements.max(), carry_chain_displacements.sum()/self.num_carry_chains)) + + #TODO - When mlabs are treated as a type of LUT + ##Legalize mlabs if any + if self.num_mlab_nodes > 0: + + lg_max_dist_init=self.nbrDistEnd + lg_max_dist_incr=self.nbrDistIncr + lg_flow_cost_scale=100.0 + + #Remove already assigned slice sites if any + rem_slice_sites_mask = compute_remaining_slice_sites(self.slice_sites, self.site_det_sig_idx, self.addr2site_map) + num_sites = rem_slice_sites_mask.sum() + num_total_nodes = pos.numel()//2 + + locX = pos[:self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() + locY = pos[num_total_nodes:num_total_nodes+self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() + precondWL = wlPrecond[self.is_mlab_node.bool()].cpu().detach().numpy() + + movVal = np.zeros(2, dtype=np.float32).tolist() + outLoc = np.zeros(2*self.num_mlab_nodes, dtype=np.float32).tolist() + + lut_ff_legalization_cpp.minCostFlow(locX, locY, num_sites, self.num_mlab_nodes, self.sliceSiteXYs[rem_slice_sites_mask].flatten(), + precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) + + outLoc=np.array(outLoc) + mlab_locX = torch.from_numpy(outLoc[:self.num_mlab_nodes]).to(dtype=pos.dtype, device=self.device) + mlab_locY = torch.from_numpy(outLoc[self.num_mlab_nodes:]).to(dtype=pos.dtype, device=self.device) + + mlab_displacements = torch.zeros(self.num_mlab_nodes, dtype=pos.dtype, device=self.device) + + lut_ff_legalization_cpp.legalizeMlab(pos, torch.flatten(self.site_xy), + mlab_locX, mlab_locY, self.mlab_indices, self.site2addr_map, + self.num_mlab_nodes, self.num_sites_y, self.SIG_IDX, self.SLICE_CAPACITY, + mlab_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, + self.inst_next_bestScoreImprov, self.sites_with_special_nodes, self.inst_curr_detSite, + self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, + self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, + self.site_det_impl_lut, self.num_threads) + + logging.info("%d mlabs legalized with max and avg displacements: (%f, %f)" + % (self.num_mlab_nodes, mlab_displacements.max(), mlab_displacements.sum()/self.num_mlab_nodes)) + + mlab_sites = self.site2addr_map[self.inst_curr_detSite[torch.where(self.is_mlab_node == 1)[0]].long()] + self.is_mlab_site[mlab_sites.long()] = 1 + + ## Initialize Site Neighbors ## + lut_ff_legalization_cpp.initSiteNbrs(pos, wlPrecond, torch.flatten(self.site_xy), self.site_det_score, + sorted_node_idx, self.node2fence_region_map, torch.flatten(self.site_types), + torch.flatten(self.spiral_accessor), self.site2addr_map, self.addr2site_map, + torch.flatten(self.flat_node2precluster_map), self.flat_node2prclstrCount, self.is_mlab_node, self.is_mlab_site, + self.sites_with_special_nodes, self.nbrDistEnd, self.nbrDistBeg, self.nbrDistIncr, self.lutId, + self.ffId, self.sliceId, self.num_nodes, self.num_sites_x, self.num_sites_y, self.num_clb_sites, + self.SCL_IDX, self.SIG_IDX, self.SLICE_CAPACITY, self.numGroups, self.maxList, spiralBegin, + spiralEnd_maxD, self.site_curr_scl_score, self.site_curr_scl_siteId, self.site_curr_scl_validIdx, + self.site_curr_scl_idx, self.site_curr_scl_sig, self.site_curr_scl_sig_idx, self.site_curr_scl_impl_lut, + self.site_nbrRanges, self.site_nbrRanges_idx, self.site_nbrList, self.site_nbr, self.site_nbr_idx, + self.site_nbrGroup_idx, self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, + self.site_det_impl_lut, self.num_threads) + + #DBG + #Preclustering Info + preAll = (self.flat_node2prclstrCount[self.node2fence_region_map==self.lutId] > 1).sum().item() + pre3 = (self.flat_node2prclstrCount[self.node2fence_region_map==self.lutId] > 2).sum().item() + pre2 = preAll - pre3 + #print("# Precluster: ", preAll, " (", pre2, " + ", pre3, ")") + #DBG + print("Preclusters: %d (%d + %d) Initialization completed in %.3f seconds" % (preAll, pre2, pre3, time.time()-tt)) + + #DBG + spl_mask = self.special_nodes == 1 + if -1 in self.inst_curr_detSite[spl_mask]: + print("ERROR: INCORRECT locations for special nodes after legalization - CHECK") + + #Carry-Chain Checker + if self.num_carry_chains > 0: + carry_chain_checker(self.flat_cc2node_start_map, self.flat_cc2node_map, self.inst_curr_detSite, + self.site2addr_map, self.site_det_impl_lut, self.num_sites_y, + self.lutsInSlice, self.num_carry_chains, self.device) + #DBG + + def runDLIter(self, pos, wlPrecond, sorted_node_map, sorted_node_idx, sorted_net_map, sorted_net_idx, sorted_pin_map, + activeStatus, illegalStatus, dlIter): + maxDist = 5.0 + spiralBegin = 0 + spiralEnd = 2 * (int(maxDist) + 1) * int(maxDist) + 1 + minStableIter = 3 + minNeighbors = 10 + cumsum_curr_scl = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) + sorted_clb_siteIds = torch.zeros_like(cumsum_curr_scl) + validIndices_curr_scl = torch.ones_like(self.site_curr_scl_validIdx) + validIndices_curr_scl *= -1 + + if pos.is_cuda: + lut_ff_legalization_cuda.runDLIter(pos, self.pin_offset_x, self.pin_offset_y, + self.net_bbox, torch.flatten(self.site_xy), self.net_wts, self.net_pinIdArrayX, + self.net_pinIdArrayY, torch.flatten(self.site_types), torch.flatten(self.spiral_accessor), + self.node2fence_region_map, self.lut_flop_indices, self.flop2ctrlSetId_map, + self.flop_ctrlSets, self.extended_ctrlSets, self.ext_ctrlSet_start_map, + self.lut_type, self.flat_node2pin_start_map, self.flat_node2pin_map, self.node2outpinIdx_map, + self.node2pincount, self.net2pincount, self.pin2net_map, self.pin_typeIds, self.flat_net2pin_start_map, + self.pin2node_map, sorted_net_map, sorted_node_map, self.flat_node2prclstrCount, + torch.flatten(self.flat_node2precluster_map), self.is_mlab_node, torch.flatten(self.site_nbrList), + torch.flatten(self.site_nbrRanges), self.site_nbrRanges_idx, self.addr2site_map, + self.site2addr_map, self.special_nodes, maxDist, self.xWirelenWt, self.yWirelenWt, + self.wirelenImprovWt, self.extNetCountWt, self.num_sites_x, self.num_sites_y, + self.num_clb_sites, self.num_lutflops, minStableIter, self.maxList, self.half_ctrl_mode, + self.SLICE_CAPACITY, self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, + minNeighbors, spiralBegin, spiralEnd, self.int_min_val, self.numGroups, + self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, + self.lutTypeInSliceUnit, self.lut_maxShared, self.CKSR_IN_CLB, self.CE_IN_CLB, + self.SCL_IDX, self.PQ_IDX, self.SIG_IDX, self.lutId, self.ffId, self.sliceId, + self.site_nbr_idx, self.site_nbr, self.site_nbrGroup_idx, self.site_curr_pq_top_idx, + self.site_curr_pq_sig_idx, self.site_curr_pq_sig, self.site_curr_pq_idx, self.site_curr_stable, + self.site_curr_pq_siteId, self.site_curr_pq_validIdx, self.site_curr_pq_score, + self.site_curr_pq_impl_lut, self.site_curr_pq_impl_ff, self.site_curr_pq_impl_cksr, + self.site_curr_pq_impl_ce, self.site_curr_scl_score, self.site_curr_scl_siteId, + self.site_curr_scl_idx, cumsum_curr_scl, self.site_curr_scl_validIdx, validIndices_curr_scl, + self.site_curr_scl_sig_idx, self.site_curr_scl_sig, self.site_curr_scl_impl_lut, + self.site_curr_scl_impl_ff, self.site_curr_scl_impl_cksr, self.site_curr_scl_impl_ce, + self.site_next_pq_idx, self.site_next_pq_validIdx, self.site_next_pq_top_idx, self.site_next_pq_score, + self.site_next_pq_siteId, self.site_next_pq_sig_idx, self.site_next_pq_sig, self.site_next_pq_impl_lut, + self.site_next_pq_impl_ff, self.site_next_pq_impl_cksr, self.site_next_pq_impl_ce, self.site_next_scl_score, + self.site_next_scl_siteId, self.site_next_scl_idx, self.site_next_scl_validIdx, self.site_next_scl_sig_idx, + self.site_next_scl_sig, self.site_next_scl_impl_lut, self.site_next_scl_impl_ff, self.site_next_scl_impl_cksr, + self.site_next_scl_impl_ce, self.site_next_stable, self.site_det_score, self.site_det_siteId, self.site_det_sig_idx, + self.site_det_sig, self.site_det_impl_lut, self.site_det_impl_ff, self.site_det_impl_cksr, self.site_det_impl_ce, + self.inst_curr_detSite, self.inst_curr_bestScoreImprov, self.inst_curr_bestSite, self.inst_next_detSite, + self.inst_next_bestScoreImprov, self.inst_next_bestSite, activeStatus, illegalStatus, self.inst_score_improv, + self.site_score_improv, sorted_clb_siteIds) + + else: + lut_ff_legalization_cpp.runDLIter(pos, self.pin_offset_x, self.pin_offset_y, self.net_bbox, self.net_pinIdArrayX, + self.net_pinIdArrayY, torch.flatten(self.site_xy), self.node2fence_region_map, self.flop_ctrlSets, + self.extended_ctrlSets, self.ext_ctrlSet_start_map, self.flop2ctrlSetId_map, self.lut_type, + self.flat_node2pin_start_map, self.flat_node2pin_map, self.node2outpinIdx_map, self.node2pincount, self.net2pincount, + self.pin2net_map, self.pin_typeIds, self.flat_net2pin_start_map, self.pin2node_map, self.flat_node2prclstrCount, + torch.flatten(self.flat_node2precluster_map), self.is_mlab_node, self.is_mlab_site, torch.flatten(self.site_nbrList), + torch.flatten(self.site_nbrRanges), self.site_nbrRanges_idx, sorted_node_map, sorted_net_map, self.net_wts, + self.addr2site_map, self.special_nodes, self.num_sites_x, self.num_sites_y, self.num_clb_sites, minStableIter, + self.maxList, self.half_ctrl_mode, self.SLICE_CAPACITY, self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, + self.NUM_BLE_PER_SLICE, minNeighbors, self.numGroups, self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, + self.lutTypeInSliceUnit, self.lut_maxShared, self.xWirelenWt, self.yWirelenWt, self.wirelenImprovWt, + self.extNetCountWt, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SCL_IDX, self.PQ_IDX, self.SIG_IDX, self.lutId, + self.ffId, self.num_nodes, self.num_threads, self.site_nbr_idx, self.site_nbr, self.site_nbrGroup_idx, + self.site_curr_pq_top_idx, self.site_curr_pq_sig_idx, self.site_curr_pq_sig, self.site_curr_pq_idx, + self.site_curr_pq_validIdx, self.site_curr_stable, self.site_curr_pq_siteId, self.site_curr_pq_score, + self.site_curr_pq_impl_lut, self.site_curr_pq_impl_ff, self.site_curr_pq_impl_cksr, self.site_curr_pq_impl_ce, + self.site_curr_scl_score, self.site_curr_scl_siteId, self.site_curr_scl_idx, self.site_curr_scl_validIdx, + self.site_curr_scl_sig_idx, self.site_curr_scl_sig, self.site_curr_scl_impl_lut, self.site_curr_scl_impl_ff, + self.site_curr_scl_impl_cksr, self.site_curr_scl_impl_ce, self.site_next_pq_idx, self.site_next_pq_validIdx, + self.site_next_pq_top_idx, self.site_next_pq_score, self.site_next_pq_siteId, self.site_next_pq_sig_idx, + self.site_next_pq_sig, self.site_next_pq_impl_lut, self.site_next_pq_impl_ff, self.site_next_pq_impl_cksr, + self.site_next_pq_impl_ce, self.site_next_scl_score, self.site_next_scl_siteId, self.site_next_scl_idx, + self.site_next_scl_validIdx, self.site_next_scl_sig_idx, self.site_next_scl_sig, self.site_next_scl_impl_lut, + self.site_next_scl_impl_ff, self.site_next_scl_impl_cksr, self.site_next_scl_impl_ce, self.site_next_stable, + self.site_det_score, self.site_det_siteId, self.site_det_sig_idx, self.site_det_sig, self.site_det_impl_lut, + self.site_det_impl_ff, self.site_det_impl_cksr, self.site_det_impl_ce, self.inst_curr_detSite, + self.inst_curr_bestScoreImprov, self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestScoreImprov, + self.inst_next_bestSite, activeStatus, illegalStatus) + + ####DBG + print(dlIter,": ", (self.inst_curr_detSite[self.node2fence_region_map==self.lutId] > -1).sum().item()+(self.inst_curr_detSite[self.node2fence_region_map==self.ffId] > -1).sum().item(), "/", self.num_nodes) + print("\tactive Status : ", activeStatus.sum().item()) + print("\tillegal Status : ", illegalStatus.sum().item()) + ##DBG + + def ripUP_Greedy_slotAssign(self, pos, wlPrecond, node_z, sorted_node_map, sorted_node_idx, sorted_net_map, + sorted_net_idx, sorted_pin_map, inst_areas): + + tt = time.time() + spiralBegin = 0 + spiralEnd = self.spiral_accessor.shape[0] #Entire chip! + ripupExpansion = 1 + greedyExpansion = 5 + slotAssignFlowWeightScale = 1000.0 + slotAssignFlowWeightIncr = 0.5 + + updXloc = torch.ones(self.num_nodes, dtype=self.dtype, device=self.device) + updXloc *= -1 + updYloc = torch.ones_like(updXloc) + updYloc *= -1 + updZloc = torch.zeros(self.num_movable_nodes, dtype=torch.int, device=self.device) + + #Re-initialize preclustering Update first element as itself + self.flat_node2precluster_map = torch.ones((self.num_nodes,3), dtype=torch.int, device=self.device) + self.flat_node2precluster_map *= -1 + self.flat_node2precluster_map[:,0] = torch.arange(self.num_nodes, dtype=torch.int, device=self.device) + self.flat_node2prclstrCount = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) + self.flat_node2prclstrCount[self.lut_flop_mask] = 1 + + #RipUp + Greedy Legalization + rem_insts_mask = (self.inst_curr_detSite == -1) + num_remInsts = rem_insts_mask.sum().item() + rem_inst_areas = inst_areas[rem_insts_mask] + rem_inst_ids = torch.arange(self.num_nodes, dtype=torch.int, device=self.device)[rem_insts_mask] + + if self.num_mlab_nodes > 0: + self.sites_with_special_nodes = torch.logical_or(self.sites_with_special_nodes, self.is_mlab_site).to(torch.int32) + + if self.half_ctrl_mode == 1: + #sorted node ids only comprise of remaining instances + _, sorted_ids = torch.sort(rem_inst_areas, descending=True) + sorted_remNode_idx = rem_inst_ids[sorted_ids] + sorted_remNode_idx = sorted_remNode_idx.to(torch.int32) + + #sorted node map will consist of all instances sorted based on decreasing area + _, sort_all_ids = torch.sort(inst_areas, descending=True) + _, sorted_remNode_map = torch.sort(sort_all_ids) + sorted_remNode_map = sorted_remNode_map.to(torch.int32) + else: + #Prioritize based on lut type and inst area + inst_scores = 2*self.lut_type/self.lut_type.max() + inst_scores += (inst_areas/inst_areas.max()) + rem_inst_scores = inst_scores[rem_insts_mask] + #sorted node ids only comprise of remaining instances + _, sorted_ids = torch.sort(rem_inst_scores, descending=True) + sorted_remNode_idx = rem_inst_ids[sorted_ids] + sorted_remNode_idx = sorted_remNode_idx.to(torch.int32) + + #sorted node map will consist of all instances sorted based on decreasing area + _, sort_all_ids = torch.sort(inst_scores, descending=True) + _, sorted_remNode_map = torch.sort(sort_all_ids) + sorted_remNode_map = sorted_remNode_map.to(torch.int32) + + #DBG + #print("RipUp & Greedy LG on ", num_remInsts, "insts (neighbors within", self.nbrDistEnd, "distance)") + numFFs = (self.node2fence_region_map[rem_inst_ids.long()] == self.ffId).sum().item() + numLUTs = rem_inst_ids.shape[0] - numFFs + #print("RipUP & Greedy LG on ", num_remInsts, " insts (", numLUTs, " LUTs + ", numFFs, " FFs)") + #DBG + + if pos.is_cuda: + cpu_inst_curr_detSite = self.inst_curr_detSite.cpu() + cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() + cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() + cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() + cpu_site_det_impl_ff = torch.flatten(self.site_det_impl_ff).cpu() + cpu_site_det_impl_cksr = torch.flatten(self.site_det_impl_cksr).cpu() + cpu_site_det_impl_ce = torch.flatten(self.site_det_impl_ce).cpu() + cpu_site_det_siteId = self.site_det_siteId.cpu() + cpu_site_det_score = self.site_det_score.cpu() + cpu_node_x = updXloc.cpu() + cpu_node_y = updYloc.cpu() + cpu_node_z = updZloc.cpu() + + lut_ff_legalization_cpp.ripUp_SlotAssign(pos.cpu(), self.pin_offset_x.cpu(), self.pin_offset_y.cpu(), + self.net_wts.cpu(), self.net_bbox.cpu(), inst_areas.cpu(), wlPrecond.cpu(), + torch.flatten(self.site_xy).cpu(), self.net_pinIdArrayX.cpu(), self.net_pinIdArrayY.cpu(), + torch.flatten(self.spiral_accessor).cpu(), self.node2fence_region_map.cpu(), self.lut_type.cpu(), + torch.flatten(self.site_types).cpu(), self.node2pincount.cpu(), self.net2pincount.cpu(), + self.pin2net_map.cpu(), self.pin2node_map.cpu(), self.pin_typeIds.cpu(), + self.flop2ctrlSetId_map.cpu(), self.flop_ctrlSets.cpu(), self.extended_ctrlSets.cpu(), + self.ext_ctrlSet_start_map.cpu(), self.flat_node2pin_start_map.cpu(), + self.flat_node2pin_map.cpu(), self.flat_net2pin_start_map.cpu(), self.flat_node2prclstrCount.cpu(), + torch.flatten(self.flat_node2precluster_map).cpu(), sorted_remNode_map.cpu(), sorted_remNode_idx.cpu(), + sorted_net_map.cpu(), self.node2outpinIdx_map.cpu(), self.flat_net2pin_map.cpu(), + self.addr2site_map.cpu(), self.site2addr_map.cpu(), self.sites_with_special_nodes.cpu(), + self.special_nodes.cpu(), self.nbrDistEnd, self.xWirelenWt, self.yWirelenWt, self.extNetCountWt, + self.wirelenImprovWt, slotAssignFlowWeightScale, slotAssignFlowWeightIncr, self.lutTypeInSliceUnit, + self.lut_maxShared, num_remInsts, self.num_sites_x, self.num_sites_y, self.num_clb_sites, spiralBegin, + spiralEnd, self.half_ctrl_mode, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SLICE_CAPACITY, + self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, self.NUM_BLE_PER_HALF_SLICE, + self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, ripupExpansion, greedyExpansion, self.SIG_IDX, + self.lutId, self.ffId, self.sliceId, self.num_threads, + cpu_inst_curr_detSite, cpu_site_det_sig_idx, cpu_site_det_sig, cpu_site_det_impl_lut, cpu_site_det_impl_ff, + cpu_site_det_impl_cksr, cpu_site_det_impl_ce, cpu_site_det_siteId, cpu_site_det_score, + cpu_node_x, cpu_node_y, cpu_node_z) + + self.inst_curr_detSite.data.copy_(cpu_inst_curr_detSite.data) + self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx.data) + torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig.data) + torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut.data) + torch.flatten(self.site_det_impl_ff).data.copy_(cpu_site_det_impl_ff.data) + torch.flatten(self.site_det_impl_cksr).data.copy_(cpu_site_det_impl_cksr.data) + torch.flatten(self.site_det_impl_ce).data.copy_(cpu_site_det_impl_ce.data) + self.site_det_siteId.data.copy_(cpu_site_det_siteId.data) + self.site_det_score.data.copy_(cpu_site_det_score.data) + updXloc.data.copy_(cpu_node_x.data) + updYloc.data.copy_(cpu_node_y.data) + updZloc.data.copy_(cpu_node_z.data) + else: + lut_ff_legalization_cpp.ripUp_SlotAssign(pos, self.pin_offset_x, self.pin_offset_y, self.net_wts, self.net_bbox, inst_areas, wlPrecond, + torch.flatten(self.site_xy), self.net_pinIdArrayX, self.net_pinIdArrayY, torch.flatten(self.spiral_accessor), + self.node2fence_region_map, self.lut_type, torch.flatten(self.site_types), self.node2pincount, self.net2pincount, + self.pin2net_map, self.pin2node_map, self.pin_typeIds, self.flop2ctrlSetId_map, + self.flop_ctrlSets, self.extended_ctrlSets, self.ext_ctrlSet_start_map, self.flat_node2pin_start_map, + self.flat_node2pin_map, self.flat_net2pin_start_map, self.flat_node2prclstrCount, torch.flatten(self.flat_node2precluster_map), + sorted_remNode_map, sorted_remNode_idx, sorted_net_map, self.node2outpinIdx_map, self.flat_net2pin_map, + self.addr2site_map, self.site2addr_map, self.sites_with_special_nodes, self.special_nodes, + self.nbrDistEnd, self.xWirelenWt, self.yWirelenWt, self.extNetCountWt, self.wirelenImprovWt, slotAssignFlowWeightScale, + slotAssignFlowWeightIncr, self.lutTypeInSliceUnit, self.lut_maxShared, num_remInsts, self.num_sites_x, self.num_sites_y, + self.num_clb_sites, spiralBegin, spiralEnd, self.half_ctrl_mode, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SLICE_CAPACITY, + self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, self.NUM_BLE_PER_HALF_SLICE, + self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, ripupExpansion, greedyExpansion, self.SIG_IDX, + self.lutId, self.ffId, self.sliceId, self.num_threads, + self.inst_curr_detSite, self.site_det_sig_idx, self.site_det_sig, self.site_det_impl_lut, self.site_det_impl_ff, + self.site_det_impl_cksr, self.site_det_impl_ce, self.site_det_siteId, self.site_det_score, updXloc, updYloc, updZloc) + + #Carry-Chain Checker + if self.num_carry_chains > 0: + carry_chain_checker_loc(self.flat_cc2node_start_map, self.flat_cc2node_map, updXloc, updYloc, + updZloc, self.HALF_SLICE_CAPACITY, self.lutsInSlice, self.num_carry_chains, self.device) + + totalNodes = int(len(pos)/2) + node_z.data.copy_(updZloc) + pos[:self.num_nodes].data.masked_scatter_(self.lut_flop_mask, updXloc[self.lut_flop_mask]) + pos[totalNodes:totalNodes+self.num_nodes].data.masked_scatter_(self.lut_flop_mask, updYloc[self.lut_flop_mask]) + + ###Logic Utilization + logic_util = 100 * (self.site_det_sig_idx > 0 ).sum().item() /self.num_clb_sites + logging.info("Occupied Slices = %d, Total slices = %d and LOGIC UTILIZATION %.4f%%" % ((self.site_det_sig_idx > 0 ).sum().item(), self.num_clb_sites, logic_util)) + print("RipUP & Greedy LG on %d insts (%d LUTs + %d FFs) takes %.3f seconds" % (num_remInsts, numLUTs, numFFs, time.time()-tt)) + + return pos diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh new file mode 100644 index 00000000..791acf51 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh @@ -0,0 +1,1297 @@ +/** + * @file graph_matching.cuh + * @author Rachel Selina Rajarathnam, Zixuan Jiang (DREAMPlaceFPGA-PL) + * @date Oct 2022 + */ +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +#define INVALID -1 +#define INPUT_PIN 1 +#define MAX_SLICE_INPUTS 100 +//Reset below values if required: +//Below values are for max vertex count of N=20 +#define SLICE_MAX_CAP 20 +#define N 20 +#define N2 400 +#define NN 40 +#define M 30 +#define M2 900 + +///helper functions for Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 +inline __device__ void queue_insert( + const int Q_size, int &Q_front, int &Q_back, int* Q, const int element) +{ + if (Q_back == Q_size-1) + { + printf("ERROR: QUEUE OVERFLOW - INCREASE SIZE\n"); + } else + { + if (Q_front == INVALID) + { + Q_front = 0; + } + if (Q_back == INVALID) + { + Q_back = 0; + } else + { + Q_back += 1; + } + Q[Q_back] = element; + } +} + +inline __device__ void queue_pop( + int &Q_front, const int Q_back) +{ + if (Q_front == INVALID || Q_front > Q_back) + { + printf("WARN: QUEUE UNDERFLOW\n"); + } else + { + ++Q_front; + } + +} + +inline __device__ void add_edge( + const int u, const int v, int* g) +{ + g[u*M+v] = u; + g[v*M+u] = v; +} + +inline __device__ void match( + const int u, const int v, int* g, int* mate) +{ + g[u*M+v] = INVALID; + g[v*M+u] = INVALID; + mate[u] = v; + mate[v] = u; +} + +//Note: x should not be changed outside the function! +inline __device__ void trace( + int x, const int* bl, const int* p, int* vx, int &vx_length) +{ + while(true) + { + while(bl[x] != x) x = bl[x]; + if(vx_length > 0 && vx[vx_length - 1] == x) break; + vx[vx_length] = x; + ++vx_length; + x = p[x]; + } +} + +__device__ void contract( + const int c, int x, int y, int* vx, int &vx_length, int* vy, + int &vy_length, int* b, int* bIndex, int* bl, int* g) +{ + bIndex[c] = 0; + int r = vx[vx_length - 1]; + while(vx_length > 0 && vy_length > 0 && vx[vx_length - 1] == vy[vy_length - 1]) + { + r = vx[vx_length - 1]; + --vx_length; + --vy_length; + } + // b[c].push_back(r); + b[c * M + bIndex[c]] = r; + ++bIndex[c]; + + // b[c].insert(b[c].end(), vx.rbegin(), vx.rend()); + for (int i = vx_length - 1; i >= 0; --i) { + b[c * M + bIndex[c]] = vx[i]; + ++bIndex[c]; + } + + // b[c].insert(b[c].end(), vy.begin(), vy.end()); + for (int i = 0; i < vy_length; ++i) + { + b[c * M + bIndex[c]] = vy[i]; + ++bIndex[c]; + } + + for(int i = 0; i <= c; ++i) + { + g[c*M+i] = INVALID; + g[i*M+c] = INVALID; + } + + for (int j = 0; j < bIndex[c]; ++j) + { + int z = b[c * M + j]; + bl[z] = c; + for(int i = 0; i < c; ++i) + { + if(g[z*M+i] != INVALID) + { + g[c*M+i] = z; + g[i*M+c] = g[i*M+z]; + } + } + } +} + +__device__ void lift( + const int n, const int* g, const int* b, const int* bIndex, int* vx, + int &vx_length, int* A, int &A_length) +{ + while (vx_length >= 2) + { + int z = vx[vx_length-1]; + --vx_length; + if (z < n) + { + A[A_length] = z; + ++A_length; + continue; + } + int w = vx[vx_length-1]; + int i = 0; + if (A_length % 2 == 0) + { + //Find index of g[z][w] within b[z] + int val = g[z*M+w]; + for (int bId = 0; bId < bIndex[z]; ++bId) + { + if (b[z*M+bId] == val) + { + i = bId; + break; + } + } + } + int j = 0; + if (A_length % 2 == 1) + { + //Find index of g[z][A.back()] within b[z] + int val = g[z*M+A[A_length-1]]; + for (int bId = 0; bId < bIndex[z]; ++bId) + { + if (b[z*M+bId] == val) + { + j = bId; + break; + } + } + } + int k = bIndex[z]; + int dif = (A_length % 2 == 0 ? i%2 == 1 : j%2 == 0) ? 1 : k-1; + + while(i != j) + { + vx[vx_length] = b[z*M+i]; + ++vx_length; + i = (i + dif) % k; + } + vx[vx_length] = b[z*M+i]; + ++vx_length; + } +} + +///End of helper functions for Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 + +//Sort +inline __device__ void sort_array(int* input_array, int& num_elements) +{ + if (num_elements > 1) + { + for (int ix = 1; ix < num_elements; ++ix) + { + for (int jx = 0; jx < num_elements-1; ++jx) + { + if (input_array[jx] > input_array[jx+1]) + { + int val = input_array[jx]; + input_array[jx] = input_array[jx+1]; + input_array[jx+1] = val; + } + } + } + } +} + +//remove duplicates from a sorted array +inline __device__ void remove_duplicates(int* input_array, int& num_elements) +{ + if (num_elements > 1) + { + for (int i = 0; i < num_elements; ++i) + { + for (int j=0; j < i; ++j) + { + if (input_array[i] == input_array[j]) + { + --num_elements; + for (int k=i; k < num_elements; ++k) + { + input_array[k] = input_array[k+1]; + } + --i; + } + } + } + } +} + +//Ensure flops in subSlice share the same set of ctrl signals +inline __device__ bool ffs_ctrl_match( + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* pin_typeIds, const int* pin2net_map, + const int ffInst, const int offInst) +{ + if (ffInst == INVALID || offInst == INVALID) + { + return true; + } + + int ff_ctrl[10], off_ctrl[10]; + //Initialize ctrls to INVALID + for (int sg = 0; sg < 10; ++sg) + { + ff_ctrl[sg] = INVALID; + off_ctrl[sg] = INVALID; + } + + int ff_pins[10], off_pins[10]; + int ffpIdx(0), offpIdx(0); + + int elIt = flat_node2pin_start_map[ffInst]; + int elEnd = flat_node2pin_start_map[ffInst+1]; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + if (pinType < 3 || pinType > 9) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + ff_ctrl[pinType] = netId; + ff_pins[ffpIdx] = pinType; + ++ffpIdx; + } + + if (ffpIdx == 0) + { + return true; + } + + elIt = flat_node2pin_start_map[offInst]; + elEnd = flat_node2pin_start_map[offInst+1]; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + if (pinType < 3 || pinType > 9) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + off_ctrl[pinType] = netId; + off_pins[offpIdx] = pinType; + ++offpIdx; + } + + if (offpIdx == 0) + { + return true; + } + + //Sort contents + sort_array(ff_pins, ffpIdx); + sort_array(off_pins, offpIdx); + + int idxA = 0, idxB = 0; + int pinTypeA = ff_pins[idxA]; + int pinTypeB = off_pins[idxB]; + + //Only compare if pinType matches + while (idxA < ffpIdx && idxB < offpIdx) + { + if (pinTypeA < pinTypeB) + { + ++idxA; + + if (idxA < ffpIdx) + { + pinTypeA = ff_pins[idxA]; + } else + { + break; + } + } + else if (pinTypeA > pinTypeB) + { + ++idxB; + + if (idxB < offpIdx) + { + pinTypeB = off_pins[idxB]; + } else + { + break; + } + } else + { + if (ff_ctrl[pinTypeA] != off_ctrl[pinTypeB]) + { + return false; + } + + ++idxA; + ++idxB; + + if (idxA < ffpIdx && idxB < offpIdx) + { + pinTypeA = ff_pins[idxA]; + pinTypeB = off_pins[idxB]; + } else + { + break; + } + } + } + + return true; +} + +//Ensure unique inputs and loopbacks of subSlice are within limits +inline __device__ bool subSlice_compatibility( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, + const int* pin_typeIds, const int* node2fence_region_map, + const int* res_ff, const int* res_lut, const int lutId, + const int SLICE_CAPACITY, const int BLE_CAPACITY, + const int MAX_INPUTS_IN_SUBSLICE, const int MAX_LOOPBACK_IN_SUBSLICE, + const int k, const int ssInstId) +{ + ////DBG + //char printMsg = 0; + ////DBG + + int ssId = int(k/2)*2; + int subSliceElements[8]; + int numSSEls = 0; + + if (res_lut[ssId] != INVALID) + { + subSliceElements[numSSEls] = res_lut[ssId]; + ++numSSEls; + + ////DBG + //if (printMsg == 0 && (res_lut[ssId] == 11846 || res_lut[ssId] == 11848)) + //{ + // printMsg = 1; + //} + ////DBG + } + + if (res_ff[ssId] != INVALID) + { + subSliceElements[numSSEls] = res_ff[ssId]; + ++numSSEls; + + ////DBG + //if (res_ff[ssId] == 73994 || res_ff[ssId] == 73995) + //{ + // printMsg = 1; + //} + ////DBG + } + + if (res_lut[ssId+1] != INVALID) + { + subSliceElements[numSSEls] = res_lut[ssId+1]; + ++numSSEls; + + ////DBG + //if (printMsg == 0 && (res_lut[ssId+1] == 11846 || res_lut[ssId+1] == 11848)) + //{ + // printMsg = 1; + //} + ////DBG + } + + if (res_ff[ssId+1] != INVALID) + { + subSliceElements[numSSEls] = res_ff[ssId+1]; + ++numSSEls; + + ////DBG + //if (printMsg == 0 && (res_ff[ssId+1] == 73994 || res_ff[ssId+1] == 73995)) + //{ + // printMsg = 1; + //} + ////DBG + } + + //Check the new inst for compatibility + if (ssInstId != INVALID) + { + subSliceElements[numSSEls] = ssInstId; + ++numSSEls; + + int lut_type = (node2fence_region_map[ssInstId] == lutId); + int loc_avail = lut_type ? res_lut[k] == INVALID : res_ff[k] == INVALID; + if (loc_avail == 0) + { + return false; + } + + if (lut_type == 0 && + (res_ff[ssId] != INVALID || res_ff[ssId+1] != INVALID)) + { + int ffA = (res_ff[ssId] != INVALID) ? res_ff[ssId] : res_ff[ssId + 1]; + if(!ffs_ctrl_match(flat_node2pin_start_map, flat_node2pin_map, + pin_typeIds, pin2net_map, ffA, ssInstId)) + { + return false; + } + } + } + + if (numSSEls > 2*BLE_CAPACITY) + { + return false; + } + + ////DBG + //if (printMsg == 0 && (ssInstId == 73994 || ssInstId == 73995 || + // ssInstId == 11846 || ssInstId == 11848)) + //{ + // printMsg = 1; + //} + ////DBG + + int all_inNets[MAX_SLICE_INPUTS]; + int num_all_inNets(0); + //Check if LUT is driving FF in same subSlice + int lut_ff_conns = 0; + int lut_outNets[4]; + int num_lut_outNets = 0; + int ff_inNets[8]; + int num_ff_inNets = 0; + + for (int elId = 0; elId < numSSEls; ++elId) + { + int instId = subSliceElements[elId]; + + int lut_inst = (node2fence_region_map[instId] == lutId); + + int elIt = flat_node2pin_start_map[instId]; + int elEnd = flat_node2pin_start_map[instId+1]; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + + if (lut_inst == 1 && pinType == 0) + { + lut_outNets[num_lut_outNets] = pin2net_map[flat_node2pin_map[el]]; + ++num_lut_outNets; + } + if (pinType != INPUT_PIN) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + + all_inNets[num_all_inNets] = netId; + ++num_all_inNets; + + if (lut_inst == 0) + { + ff_inNets[num_ff_inNets] = netId; + ++num_ff_inNets; + } + } + } + + if (num_lut_outNets > 0 && num_ff_inNets > 0) + { + sort_array(lut_outNets, num_lut_outNets); + remove_duplicates(lut_outNets, num_lut_outNets); + + sort_array(ff_inNets, num_ff_inNets); + remove_duplicates(ff_inNets, num_ff_inNets); + + int idxIn = 0, idxOut = 0; + int netIn = ff_inNets[idxIn]; + int netOut = lut_outNets[idxOut]; + + while (true) + { + if (netIn < netOut) + { + ++idxIn; + if (idxIn < num_ff_inNets) + { + netIn = ff_inNets[idxIn]; + } else + { + break; + } + } else if (netIn > netOut) + { + ++idxOut; + if (idxOut < num_lut_outNets) + { + netOut = lut_outNets[idxOut]; + } else + { + break; + } + } else + { + ++lut_ff_conns; + break; + } + } + } + + sort_array(all_inNets, num_all_inNets); + remove_duplicates(all_inNets, num_all_inNets); + + ////DBG + //if (printMsg == 1) + //{ + // printf("%d insts in ALM: ", numSSEls); + // for (int elId = 0; elId < numSSEls; ++elId) + // { + // printf("%d ",subSliceElements[elId]); + // } + + // printf(" have %d unique inputs\n", num_all_inNets); + //} + ////DBG + + if (num_all_inNets > MAX_INPUTS_IN_SUBSLICE) + { + return false; + } + + //Loopbacks from subSlice driving Slice instances + //Get input nets from other subSlices + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + //Skip already visited subSlice + if (sg == ssId) continue; + + int subSlice_insts[8]; + int num_subSlice_insts = 0; + + //FFs + if (res_ff[sg] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_ff[sg]; + ++num_subSlice_insts; + } + if (res_ff[sg+1] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_ff[sg+1]; + ++num_subSlice_insts; + } + + //LUTs + if (res_lut[sg] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_lut[sg]; + ++num_subSlice_insts; + } + if (res_lut[sg+1] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_lut[sg+1]; + ++num_subSlice_insts; + } + + //Get input nets + for (int el = 0; el < num_subSlice_insts; ++el) + { + int instId = subSlice_insts[el]; + int pStart = flat_node2pin_start_map[instId]; + int pEnd = flat_node2pin_start_map[instId+1]; + + for (int pId = pStart; pId < pEnd; ++pId) + { + //Skip if not an input pin + int pinType = pin_typeIds[flat_node2pin_map[pId]]; + if (pinType != INPUT_PIN) continue; + + all_inNets[num_all_inNets] = pin2net_map[flat_node2pin_map[pId]]; + ++num_all_inNets; + } + } + } + + sort_array(all_inNets, num_all_inNets); + remove_duplicates(all_inNets, num_all_inNets); + + if (num_all_inNets == 0) + { + return true; + } + + //Get nets from all subSlices + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + int subSlice_insts[8]; + int num_subSlice_insts(0); + + int subSlice_outNets[8]; + int num_subSlice_outNets(0); + + if (sg == ssId) + { + for (int elId = 0; elId < numSSEls; ++elId) + { + subSlice_insts[num_subSlice_insts] = subSliceElements[elId]; + ++num_subSlice_insts; + } + } else + { + //FFs + if (res_ff[sg] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_ff[sg]; + ++num_subSlice_insts; + } + if (res_ff[sg+1] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_ff[sg+1]; + ++num_subSlice_insts; + } + + //LUTs + if (res_lut[sg] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_lut[sg]; + ++num_subSlice_insts; + } + if (res_lut[sg+1] != INVALID) + { + subSlice_insts[num_subSlice_insts] = res_lut[sg+1]; + ++num_subSlice_insts; + } + } + + for (int idx = 0; idx < num_subSlice_insts; ++idx) + { + int instId = subSlice_insts[idx]; + + int ndOutId = 4*instId; + int ndOutPins = ndOutId + 4; + for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + subSlice_outNets[num_subSlice_outNets] = outNetId; + ++num_subSlice_outNets; + } + } + if (num_subSlice_outNets <= MAX_LOOPBACK_IN_SUBSLICE) continue; + + sort_array(subSlice_outNets, num_subSlice_outNets); + remove_duplicates(subSlice_outNets, num_subSlice_outNets); + + int num_loopbacks = 0; + //Compare sorted subSlice outNets with Slice input nets + + int idxIn = 0, idxOut = 0; + int netIn = all_inNets[idxIn]; + int netOut = subSlice_outNets[idxOut]; + + while (num_loopbacks <= MAX_LOOPBACK_IN_SUBSLICE) + { + if (netIn < netOut) + { + ++idxIn; + if (idxIn < num_all_inNets) + { + netIn = all_inNets[idxIn]; + } else + { + break; + } + } else if (netIn > netOut) + { + ++idxOut; + if (idxOut < num_subSlice_outNets) + { + netOut = subSlice_outNets[idxOut]; + } else + { + break; + } + } else + { + ++num_loopbacks; + ++idxIn; + ++idxOut; + if (idxIn < num_all_inNets && + idxOut < num_subSlice_outNets) + { + netIn = all_inNets[idxIn]; + netOut = subSlice_outNets[idxOut]; + } else + { + break; + } + } + } + + if (sg == ssId && lut_ff_conns > 0) + { + --num_loopbacks; + } + + if (num_loopbacks > MAX_LOOPBACK_IN_SUBSLICE) + { + return false; + } + } + + return true; +} + +//two lut compatibility +inline __device__ bool two_lut_compatibility_check( + const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int lutTypeInSliceUnit, const int lut_maxShared, const int lutAId, + const int lutBId) +{ + if (lut_type[lutAId] == lutTypeInSliceUnit || + lut_type[lutBId] == lutTypeInSliceUnit) + { + return false; + } + + int numInputs = lut_type[lutAId] + lut_type[lutBId]; + + if (numInputs <= lut_maxShared) + { + return true; + } + + //Include condition for LUT0 + if (lut_type[lutAId] == 0 || lut_type[lutBId] == 0) + { + return false; + } + + int lutANets[SLICE_MAX_CAP], lutBNets[SLICE_MAX_CAP]; + int lutAIdx(0), lutBIdx(0); + + int lutAIt = flat_node2pin_start_map[lutAId]; + int lutBIt = flat_node2pin_start_map[lutBId]; + int lutAEnd = flat_node2pin_start_map[lutAId+1]; + int lutBEnd = flat_node2pin_start_map[lutBId+1]; + + for (int el = lutAIt; el < lutAEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != INPUT_PIN) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutANets[lutAIdx] = netId; + ++lutAIdx; + } + + if (lutAIdx > 1) + { + //Sort contents of lutANets + for (int ix = 1; ix < lutAIdx; ++ix) + { + for (int jx = 0; jx < lutAIdx-1; ++jx) + { + if (lutANets[jx] > lutANets[jx+1]) + { + int val = lutANets[jx]; + lutANets[jx] = lutANets[jx+1]; + lutANets[jx+1] = val; + } + } + } + } + + for (int el = lutBIt; el < lutBEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != INPUT_PIN) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutBNets[lutBIdx] = netId; + ++lutBIdx; + } + + if (lutBIdx > 1) + { + //Sort contents of lutBNets + for (int ix = 1; ix < lutBIdx; ++ix) + { + for (int jx = 0; jx < lutBIdx-1; ++jx) + { + if (lutBNets[jx] > lutBNets[jx+1]) + { + int val = lutBNets[jx]; + lutBNets[jx] = lutBNets[jx+1]; + lutBNets[jx+1] = val; + } + } + } + } + + int idxA = 0, idxB = 0; + int netIdA = lutANets[idxA]; + int netIdB = lutBNets[idxB]; + + while(numInputs > lut_maxShared) + { + if (netIdA < netIdB) + { + ++idxA; + if (idxA < lutAIdx) + { + netIdA = lutANets[idxA]; + } else + { + break; + } + } else if (netIdA > netIdB) + { + ++idxB; + if (idxB < lutBIdx) + { + netIdB = lutBNets[idxB]; + } else + { + break; + } + + } else + { + --numInputs; + ++idxA; + ++idxB; + + if (idxA < lutAIdx && idxB < lutBIdx) + { + netIdA = lutANets[idxA]; + netIdB = lutBNets[idxB]; + } else + { + break; + } + } + } + + return numInputs <= lut_maxShared; +} + +//Given lut arrangement, fit FFs +inline __device__ bool fit_ffs( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, + const int* pin_typeIds, const int* node2fence_region_map, + const int* res_lut, const int lutId, const int lut_maxShared, + const int SLICE_CAPACITY, const int BLE_CAPACITY, int* res_ff) +{ + //Rearrange all FFs based on compatibility + int temp_ff[SLICE_MAX_CAP]; + int rem_ffs[SLICE_MAX_CAP]; + int num_rem_ffs(0); + + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + temp_ff[sg] = res_ff[sg]; + temp_ff[sg+1] = res_ff[sg+1]; + + if (res_ff[sg] != INVALID || res_ff[sg+1] != INVALID) + { + if (!subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, + node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, sg, INVALID)) + { + if (res_ff[sg] != INVALID) + { + rem_ffs[num_rem_ffs] = res_ff[sg]; + ++num_rem_ffs; + res_ff[sg] = INVALID; + } + if (res_ff[sg+1] != INVALID) + { + rem_ffs[num_rem_ffs] = res_ff[sg+1]; + ++num_rem_ffs; + res_ff[sg+1] = INVALID; + } + } + } + } + + if (num_rem_ffs == 0) return true; + + //Greedily assign remaining FFs + char ffLocFound = 0; + for (int el = 0; el < num_rem_ffs; ++el) + { + int ffId = rem_ffs[el]; + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + if (res_ff[sg] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, + node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, sg, ffId)) + { + res_ff[sg] = ffId; + ++ffLocFound; + break; + } + } else if (res_ff[sg+1] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, + node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, sg+1, ffId)) + { + res_ff[sg+1] = ffId; + ++ffLocFound; + break; + } + } + } + } + + if (ffLocFound == num_rem_ffs) + { + return true; + } + + //Revert + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_ff[sg] = temp_ff[sg]; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +inline __device__ bool fit_luts_to_candidate_impl( + const int* node2outpinIdx_map, const int* lut_type, const int* pin2net_map, + const int* pin_typeIds, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* flat_node2precluster_map, + const int* node2fence_region_map, const int* special_nodes, + const int half_ctrl_mode, const int lutTypeInSliceUnit, const int lut_maxShared, + const int instPcl, const int node2prclstrCount, const int NUM_BLE_PER_SLICE, + const int SLICE_CAPACITY, const int BLE_CAPACITY, const int lutId, + int* res_lut, int* res_ff) +{ + int luts[N], lut6s[N], splNodes[N]; + int lutIdx(0), lut6Idx(0), splIdx(0); + + //Ensure subSlice-level shared input count is met + int temp_lut[SLICE_MAX_CAP]; + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + if (res_lut[sg] != INVALID) + { + if (special_nodes[res_lut[sg]] == 1) + { + splNodes[splIdx] = res_lut[sg]; + ++splIdx; + } else + { + if (lut_type[res_lut[sg]] < lutTypeInSliceUnit) + { + luts[lutIdx] = res_lut[sg]; + ++lutIdx; + } else + { + lut6s[lut6Idx] = res_lut[sg]; + ++lut6Idx; + } + } + } + if (half_ctrl_mode == 0) + { + temp_lut[sg] = res_lut[sg]; + } + } + + //int lutSize = lutIdx + lut6Idx; + for (int idx = 0; idx < node2prclstrCount; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + if (node2fence_region_map[clInstId] == lutId) + { + if (lut_type[clInstId] < lutTypeInSliceUnit) + { + luts[lutIdx] = clInstId; + ++lutIdx; + + sort_array(luts, lutIdx); + remove_duplicates(luts, lutIdx); + } else + { + lut6s[lut6Idx] = clInstId; + ++lut6Idx; + + sort_array(lut6s, lut6Idx); + remove_duplicates(lut6s, lut6Idx); + } + } + } + + int splNodeSpace = splIdx; + splNodeSpace += (splIdx & 1) ? 1 : 0; + + //graph matching can be called even if res_lut if full! + //Guard band for graph matching implementation with fixed memory + if (lutIdx + 2*lut6Idx + splNodeSpace > SLICE_CAPACITY) + { + return false; + } + + ///Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 + int n = lutIdx; //n - #vertices + //int m = (n%2 == 0) ? 3*n/2: 3*(n+1)/2; //m = 3n/2 + + int mate[N]; //array of length n; For each vertex u, if exposed mate[u] = -1 or mate[u] = u + int b[M2]; //For each blossom u, b[u] is list of all vertices contracted from u + int bIndex[M]; + int p[M]; //array of length m; For each vertex/blossom u, p[u] is parent in the search forest + int d[M]; //array of length m; For each vertex u, d[u] is status in search forest. d[u] = 0 if unvisited, d[u] = 1 is even depth from root and d[u] = 2 is odd depth from root + int bl[M]; //array of length m; For each vertex/blossom u, bl[u] is the blossom containing u. If not contracted, bl[u] = u. + int g[M2]; //table of size mxm with information of unmatched edges.g[u][v] = -1 if no unmatched vertices; g[u][v] = u, if u is a vertex. + + //Initialize mate + for (int mId = 0; mId < n; ++mId) + { + mate[mId] = INVALID; + } + for (int gId = 0; gId < M2; ++gId) + { + g[gId] = INVALID; + } + + //Create graph with luts + for(int ll = 0; ll < lutIdx; ++ll) + { + for(int rl = ll+1; rl < lutIdx; ++rl) + { + if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, + lutTypeInSliceUnit, lut_maxShared, luts[ll], luts[rl])) + { + add_edge(ll, rl, g); + } + } + } + + int totalPairs(0); + + for (int ans = 0; ; ++ans) + { + for (int dId = 0; dId < M; ++dId) + { + d[dId] = 0; + } + + int Q[NN]; + int Q_size(NN); + int Q_front(INVALID), Q_back(INVALID); + + for (int i = 0; i < M; ++i) + { + bl[i] = i; + } + for (int i = 0; i < n; ++i) + { + if (mate[i] == INVALID) + { + queue_insert(Q_size, Q_front, Q_back, Q, i); + p[i] = i; + d[i] = 1; + } + } + + int c = N; + bool aug(false); + + while ((Q_front != INVALID && Q_front <= Q_back) && !aug) + { + int x = Q[Q_front]; + //queue_pop(Q_front, Q_back, Q); + queue_pop(Q_front, Q_back); + + if (bl[x] != x) continue; + + for (int y = 0; y < c; ++y) + { + if (bl[y] == y && g[x*M+y] != INVALID) + { + if (d[y] == 0) + { + p[y] = x; + d[y] = 2; + p[mate[y]] = y; + d[mate[y]] = 1; + queue_insert(Q_size, Q_front, Q_back, Q, mate[y]); + } else if (d[y] == 1) + { + int vx[2*M], vy[2*M]; + int vx_length = 0, vy_length = 0; + trace(x, bl, p, vx, vx_length); + trace(y, bl, p, vy, vy_length); + + if (vx[vx_length-1] == vy[vy_length-1]) + { + contract(c, x, y, vx, vx_length, vy, vy_length, b, bIndex, bl, g); + queue_insert(Q_size, Q_front, Q_back, Q, c); + p[c] = p[b[c*M]]; + d[c] = 1; + ++c; + } else + { + aug = true; + int new_vx[2*M], new_vy[2*M]; + new_vx[0] = y; + for (int idx = 0; idx < vx_length; ++idx) + { + new_vx[idx+1] = vx[idx]; + } + ++vx_length; + new_vy[0] = x; + for (int idx = 0; idx < vy_length; ++idx) + { + new_vy[idx+1] = vy[idx]; + } + ++vy_length; + + int A[4*M], B[2*M]; + int A_length = 0, B_length = 0; + + lift(n, g, b, bIndex, new_vx, vx_length, A, A_length); + lift(n, g, b, bIndex, new_vy, vy_length, B, B_length); + + for (int idx = B_length-1; idx >= 0; --idx) + { + A[A_length] = B[idx]; + ++A_length; + } + + for (int i = 0; i < A_length; i += 2) + { + match(A[i], A[i+1], g, mate); + if (i + 2 < A_length) + { + add_edge(A[i+1], A[i + 2], g); + } + } + } + break; + } + } + } + } + if (!aug) + { + totalPairs = ans; + break; + } + } + + int otherNodePairs = lut6Idx + splNodeSpace/2; + + if ((lutIdx - totalPairs + otherNodePairs) > NUM_BLE_PER_SLICE) + { + return false; + } + + int idxL = 0; + + if (splIdx > 0) + { + for (int spId = 0; spId < splIdx; ++spId) + { + res_lut[idxL] = splNodes[spId]; + ++idxL; + } + if (splNodeSpace > splIdx) + { + res_lut[idxL] = INVALID; + ++idxL; + } + } + + for (int iil = 0; iil < lut6Idx; ++iil) + { + res_lut[idxL] = lut6s[iil]; + res_lut[idxL + 1] = INVALID; + idxL += BLE_CAPACITY; + } + + for (int mId = 0; mId < n; ++mId) + { + if (mate[mId] == INVALID) + { + res_lut[idxL] = luts[mId]; + res_lut[idxL + 1] = INVALID; + idxL += BLE_CAPACITY; + } + } + + int ck[N] = {0}; + for (int mId = 0; mId < n; ++mId) + { + if (mate[mId] != INVALID && ck[mId] == 0 && ck[mate[mId]] == 0) + { + ++ck[mId]; + ++ck[mate[mId]]; + + res_lut[idxL] = luts[mId]; + res_lut[idxL + 1] = luts[mate[mId]]; + idxL += BLE_CAPACITY; + } + } + for (int lIdx = idxL; lIdx < SLICE_CAPACITY; ++lIdx) + { + res_lut[lIdx] = INVALID; + } + + //Ensure subSlice-level shared input count is met + if (half_ctrl_mode == 0) + { + //Try to rearrange ffs to avoid compatibility issues + if (fit_ffs(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, + node2fence_region_map, res_lut, lutId, lut_maxShared, + SLICE_CAPACITY, BLE_CAPACITY, res_ff)) + { + return true; + } + + //Revert + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = temp_lut[sg]; + } + return false; + } + + return true; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp new file mode 100644 index 00000000..8d312fa1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp @@ -0,0 +1,7319 @@ +/** + * @file lut_ff_legalization.cpp + * @author Rachel Selina (DREAMPlaceFPGA-PL) + * @date Oct 2023 + * @brief Legalize LUT/FF + */ + +#include +#include +#include +#include +#include +#include +#include +//pybind Dependency +#include +#include +#include +//Lemon Graph Dependency +#include "lemon/list_graph.h" +#include "lemon/matching.h" +#include "lemon/network_simplex.h" +//Common Dependency +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +static const int INVALID = -1; +//Reset below values if required for the architecture +static const int SLICE_MAX_CAP = 20; +static const int SIG_MAX_CAP = 40; +static const int CE_MAX_CAP = 7; +static const int CKSR_MAX_CAP = 2; + +//Mutex for critical section +std::mutex mtx; + +//Struct for Candidate +template +struct Candidate +{ + T score = 0.0; + int siteId = INVALID; + int sigIdx = 0; + int sig[SIG_MAX_CAP]; + int impl_lut[SLICE_MAX_CAP]; + int impl_ff[SLICE_MAX_CAP]; + int impl_cksr[CKSR_MAX_CAP]; + int impl_ce[CE_MAX_CAP]; + + void reset() + { + score = 0.0; + siteId = INVALID; + sigIdx = 0; + + for(int sg = 0; sg < SIG_MAX_CAP; ++sg) + { + sig[sg] = INVALID; + } + for(int sg = 0; sg < SLICE_MAX_CAP; ++sg) + { + impl_lut[sg] = INVALID; + impl_ff[sg] = INVALID; + } + for(int sg = 0; sg < CKSR_MAX_CAP; ++sg) + { + impl_cksr[sg] = INVALID; + } + for(int sg = 0; sg < CE_MAX_CAP; ++sg) + { + impl_ce[sg] = INVALID; + } + } +}; + +//Struct for RipUpCand +template +struct RipUpCand +{ + // If a < b, then a has higher priority than b + bool operator<(const RipUpCand &rhs) const + { + return (legal == rhs.legal ? score > rhs.score : legal); + } + + int siteId = INVALID; + T score = -10000.0; + bool legal = false; + Candidate cand; + + void reset() + { + siteId = INVALID; + score = -10000.0; + legal = false; + cand.reset(); + } + +}; + +//Struct for subSlice +template +struct subSlice +{ + int lut[2] = {INVALID, INVALID}; + int ff[2] = {INVALID, INVALID}; + T score = 0.0; + T improv = 0.0; +}; + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +void clear_cand_contents( + const int tsPQ, const int SIG_IDX, const int SLICE_CAPACITY, + const int CKSR_IN_CLB, const int CE_IN_CLB, + int* site_sig_idx, int* site_sig, + int* site_impl_lut, int* site_impl_ff, + int* site_impl_cksr, int* site_impl_ce) +{ + int topIdx(tsPQ*SIG_IDX); + int lutIdx = tsPQ*SLICE_CAPACITY; + int ckIdx = tsPQ*CKSR_IN_CLB; + int ceIdx = tsPQ*CE_IN_CLB; + + for(int sg = 0; sg < SIG_IDX; ++sg) + { + site_sig[topIdx + sg] = INVALID; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_impl_lut[lutIdx + sg] = INVALID; + site_impl_ff[lutIdx + sg] = INVALID; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_impl_cksr[ckIdx + sg] = INVALID; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_impl_ce[ceIdx + sg] = INVALID; + } + site_sig_idx[tsPQ] = 0; +} + +// check if candidate is valid +// Candidate is valid if the instance is not commited to a site +inline bool candidate_validity_check( + const int* is_mlab_node, const int SLICE_CAPACITY, + const int topIdx, const int pqSigIdx, + const int siteId, const int* site_curr_pq_sig, + const int* inst_curr_detSite) +{ + //Check first instance if it is mlab + if (pqSigIdx == 2*SLICE_CAPACITY && + is_mlab_node[site_curr_pq_sig[topIdx]] == 1) + { + int pqInst = site_curr_pq_sig[topIdx]; + if (inst_curr_detSite[pqInst] != INVALID && + inst_curr_detSite[pqInst] != siteId) + { + return false; + } + } else + { + for (int i = 0; i < pqSigIdx; ++i) + { + int pqInst = site_curr_pq_sig[topIdx + i]; + + if (inst_curr_detSite[pqInst] != INVALID && + inst_curr_detSite[pqInst] != siteId) + { + return false; + } + } + } + return true; +} + +// define inst_in_sig +inline bool inst_in_sig(const int instId, const int siteDetSigSize, const int* site_det_sig, const int siteDetIdx) +{ + for(int i = 0; i < siteDetSigSize; ++i) + { + if (site_det_sig[siteDetIdx + i] == instId) + { + return true; + } + } + return false; +} + +// define two_lut_compatibility_check +inline bool two_lut_compatibility_check( + const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int lutTypeInSliceUnit, const int lut_maxShared, const int lutAId, + const int lutBId) +{ + if (lut_type[lutAId] == lutTypeInSliceUnit || lut_type[lutBId] == lutTypeInSliceUnit) + { + return false; + } + + int numInputs = lut_type[lutAId] + lut_type[lutBId]; + + if (numInputs <= lut_maxShared) + { + return true; + } + + //Handle LUT0 + if (lut_type[lutAId] == 0 || lut_type[lutBId] == 0) + { + return false; + } + + //Note: It is important that nets of lutA and lutB are sorted before comparison + std::vector lutAiNets, lutBiNets; + + int lutAIt = flat_node2pin_start_map[lutAId]; + int lutBIt = flat_node2pin_start_map[lutBId]; + int lutAEnd = flat_node2pin_start_map[lutAId+1]; + int lutBEnd = flat_node2pin_start_map[lutBId+1]; + + for (int el = lutAIt; el < lutAEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutAiNets.emplace_back(netId); + } + if (lutAiNets.size() > 1) + { + std::sort(lutAiNets.begin(), lutAiNets.end()); + } + + for (int el = lutBIt; el < lutBEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutBiNets.emplace_back(netId); + } + if (lutBiNets.size() > 1) + { + std::sort(lutBiNets.begin(), lutBiNets.end()); + } + + if (lutAiNets.size() == 0 || lutBiNets.size() == 0) + { + return false; + } + + int idxA = 0, idxB = 0; + int netIdA = lutAiNets[idxA]; + int netIdB = lutBiNets[idxB]; + + while(numInputs > lut_maxShared) + { + if (netIdA < netIdB) + { + ++idxA; + if (idxA < lutAiNets.size()) + { + netIdA = lutAiNets[idxA]; + } else + { + break; + } + } else if (netIdA > netIdB) + { + ++idxB; + if (idxB < lutBiNets.size()) + { + netIdB = lutBiNets[idxB]; + } else + { + break; + } + + } else + { + --numInputs; + ++idxA; + ++idxB; + + if (idxA < lutAiNets.size() && idxB < lutBiNets.size()) + { + netIdA = lutAiNets[idxA]; + netIdB = lutBiNets[idxB]; + } else + { + break; + } + } + } + + return numInputs <= lut_maxShared; +} + +//Ensure flops in subSlice share the same set of ctrl signals +inline bool ffs_ctrl_match( + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* pin_typeIds, const int* pin2net_map, + const int ffInst, const int offInst) +{ + if (ffInst == INVALID || offInst == INVALID) + { + return true; + } + + std::vector ff_ctrl(10, INVALID); + std::vector off_ctrl(10, INVALID); + std::vector ff_pins, off_pins; + + int elIt = flat_node2pin_start_map[ffInst]; + int elEnd = flat_node2pin_start_map[ffInst+1]; + + //std::cout << "FF " << ffInst << " has " << elEnd - elIt << " pins" << std::endl; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + if (pinType < 3 || pinType > 9) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + ff_ctrl[pinType] = netId; + ff_pins.emplace_back(pinType); + } + + if (ff_pins.size() == 0) + { + return true; + } + + elIt = flat_node2pin_start_map[offInst]; + elEnd = flat_node2pin_start_map[offInst+1]; + //std::cout << "Other FF " << offInst << " has " << elEnd - elIt << " pins" << std::endl; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + if (pinType < 3 || pinType > 9) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + off_ctrl[pinType] = netId; + off_pins.emplace_back(pinType); + } + + if (off_pins.size() == 0) + { + return true; + } + + std::sort(ff_pins.begin(), ff_pins.end()); + std::sort(off_pins.begin(), off_pins.end()); + + int idxA = 0, idxB = 0; + int pinTypeA = ff_pins[idxA]; + int pinTypeB = off_pins[idxB]; + + //Only compare if pinType matches + while (idxA < ff_pins.size() && idxB < off_pins.size()) + { + if (pinTypeA < pinTypeB) + { + ++idxA; + + if (idxA < ff_pins.size()) + { + pinTypeA = ff_pins[idxA]; + } else + { + break; + } + } + else if (pinTypeA > pinTypeB) + { + ++idxB; + + if (idxB < off_pins.size()) + { + pinTypeB = off_pins[idxB]; + } else + { + break; + } + } else + { + if (ff_ctrl[pinTypeA] != off_ctrl[pinTypeB]) + { + return false; + } + + ++idxA; + ++idxB; + + if (idxA < ff_pins.size() && idxB < off_pins.size()) + { + pinTypeA = ff_pins[idxA]; + pinTypeB = off_pins[idxB]; + } else + { + break; + } + } + } + + return true; +} + +//Ensure unique inputs and loopbacks of subSlice are within limits +inline bool subSlice_compatibility( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* node2fence_region_map, const int* res_ff, const int* res_lut, + const int lutId, const int SLICE_CAPACITY, const int BLE_CAPACITY, + const int MAX_INPUTS_IN_SUBSLICE, const int MAX_LOOPBACK_IN_SUBSLICE, + const int k, const int ssInstId) +{ + ////DBG + //char printMsg = 0, foundLut(0), foundFF(0); + //int dbgLUTId = INVALID; + //int dbgLUT1Id = INVALID; + //int dbgFFId = INVALID; + //int dbgFF1Id = INVALID; + ////DBG + + int ssId = int(k/2)*2; + std::vector subSliceElements; + + if (res_lut[ssId] != INVALID) + { + subSliceElements.emplace_back(res_lut[ssId]); + ////DBG + //if (res_lut[ssId] == dbgLUTId || res_lut[ssId] == dbgLUT1Id) + //{ + // ++foundLut; + //} + ////DBG + } + + if (res_ff[ssId] != INVALID) + { + subSliceElements.emplace_back(res_ff[ssId]); + ////DBG + //if (res_ff[ssId] == dbgFFId || res_ff[ssId] == dbgFF1Id) + //{ + // ++foundFF; + //} + ////DBG + } + + if (res_lut[ssId+1] != INVALID) + { + subSliceElements.emplace_back(res_lut[ssId+1]); + ////DBG + //if (res_lut[ssId+1] == dbgLUTId || res_lut[ssId+1] == dbgLUT1Id) + //{ + // ++foundLut; + //} + ////DBG + } + + if (res_ff[ssId+1] != INVALID) + { + subSliceElements.emplace_back(res_ff[ssId+1]); + ////DBG + //if (res_ff[ssId+1] == dbgFFId || res_ff[ssId+1] == dbgFF1Id) + //{ + // ++foundFF; + //} + ////DBG + } + + //Check the new inst for compatibility + if (ssInstId != INVALID) + { + subSliceElements.emplace_back(ssInstId); + ////DBG + //if (ssInstId == dbgLUTId || ssInstId == dbgLUT1Id) + //{ + // ++foundLut; + //} + //if (ssInstId == dbgFFId || ssInstId == dbgFF1Id) + //{ + // ++foundFF; + //} + ////DBG + + int lut_type = (node2fence_region_map[ssInstId] == lutId); + int location_available = lut_type ? res_lut[k] == INVALID : res_ff[k] == INVALID; + + if (location_available == 0) + { + return false; + } + if (lut_type == 0 && (res_ff[ssId] != INVALID || res_ff[ssId + 1] != INVALID)) + { + int ffA = (res_ff[ssId] != INVALID) ? res_ff[ssId] : res_ff[ssId + 1]; + if (!ffs_ctrl_match(flat_node2pin_start_map, flat_node2pin_map, + pin_typeIds, pin2net_map, ffA, ssInstId)) + { + return false; + } + } + } + + if(subSliceElements.size() > 2*BLE_CAPACITY) + { + return false; + } + + ////DBG + //if (foundLut > 1 && foundFF > 1) + //{ + // printMsg = 1; + //} + ////DBG + + std::vector all_inNets; + //Check if LUT is driving FF in same subSlice + int lut_ff_conns = 0; + std::vector lut_outNets; + std::vector ff_inNets; + + for (int elId = 0; elId < subSliceElements.size(); ++elId) + { + int instId = subSliceElements[elId]; + + int lut_inst = (node2fence_region_map[instId] == lutId); + + int elIt = flat_node2pin_start_map[instId]; + int elEnd = flat_node2pin_start_map[instId+1]; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + + if (lut_inst == 1 && pinType == 0) + { + lut_outNets.emplace_back(pin2net_map[flat_node2pin_map[el]]); + } + if (pinType != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + + all_inNets.emplace_back(netId); + + if (lut_inst == 0) + { + ff_inNets.emplace_back(netId); + } + } + } + + if (lut_outNets.size() > 0 && ff_inNets.size() > 0) + { + std::sort(lut_outNets.begin(), lut_outNets.end()); + lut_outNets.erase(std::unique(lut_outNets.begin(), lut_outNets.end()), lut_outNets.end()); + + std::sort(ff_inNets.begin(), ff_inNets.end()); + ff_inNets.erase(std::unique(ff_inNets.begin(), ff_inNets.end()), ff_inNets.end()); + + int idxIn = 0, idxOut = 0; + int netIn = ff_inNets[idxIn]; + int netOut = lut_outNets[idxOut]; + + while (true) + { + if (netIn < netOut) + { + ++idxIn; + if (idxIn < ff_inNets.size()) + { + netIn = ff_inNets[idxIn]; + } else + { + break; + } + } else if (netIn > netOut) + { + ++idxOut; + if (idxOut < lut_outNets.size()) + { + netOut = lut_outNets[idxOut]; + } else + { + break; + } + } else + { + ++lut_ff_conns; + break; + } + } + } + + std::sort(all_inNets.begin(), all_inNets.end()); + all_inNets.erase(std::unique(all_inNets.begin(), all_inNets.end()), all_inNets.end()); + + if (all_inNets.size() > MAX_INPUTS_IN_SUBSLICE) + { + return false; + } + + //////DBG + ////if (printMsg == 1) + ////{ + //// printf("%d insts in subSlice: ", subSliceElements.size()); + //// for (int elId = 0; elId < subSliceElements.size(); ++elId) + //// { + //// printf("%d ",subSliceElements[elId]); + //// } + //// printf("\n"); + + //// printf("There are %d input nets in ALM: ", all_inNets.size()); + //// for (int elId = 0; elId < all_inNets.size(); ++elId) + //// { + //// printf("%d ",all_inNets[elId]); + //// } + //// printf("\n"); + ////} + //////DBG + + + ////Check loopback of all subSlices in the Slice + //Get input nets from all subSlices + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + std::vector subSlice_insts; + + if (sg == ssId) continue; + + //FFs + if (res_ff[sg] != INVALID) + { + subSlice_insts.emplace_back(res_ff[sg]); + } + if (res_ff[sg+1] != INVALID) + { + subSlice_insts.emplace_back(res_ff[sg+1]); + } + + //LUTs + if (res_lut[sg] != INVALID) + { + subSlice_insts.emplace_back(res_lut[sg]); + } + if (res_lut[sg+1] != INVALID) + { + subSlice_insts.emplace_back(res_lut[sg+1]); + } + + //Get input nets + for (int el = 0; el < subSlice_insts.size(); ++el) + { + int instId = subSlice_insts[el]; + int pStart = flat_node2pin_start_map[instId]; + int pEnd = flat_node2pin_start_map[instId+1]; + + for (int pId = pStart; pId < pEnd; ++pId) + { + int pinType = pin_typeIds[flat_node2pin_map[pId]]; + + if (pinType != 1) continue; + all_inNets.emplace_back(pin2net_map[flat_node2pin_map[pId]]); + } + } + } + + std::sort(all_inNets.begin(), all_inNets.end()); + all_inNets.erase(std::unique(all_inNets.begin(), all_inNets.end()), all_inNets.end()); + + if (all_inNets.size() == 0) + { + return true; + } + + //Get nets from all subSlices + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + std::vector subSlice_insts; + std::vector subSlice_outNets; + + if (sg == ssId) + { + subSlice_insts = subSliceElements; + } else + { + //FFs + if (res_ff[sg] != INVALID) + { + subSlice_insts.emplace_back(res_ff[sg]); + } + if (res_ff[sg+1] != INVALID) + { + subSlice_insts.emplace_back(res_ff[sg+1]); + } + + //LUTs + if (res_lut[sg] != INVALID) + { + subSlice_insts.emplace_back(res_lut[sg]); + } + if (res_lut[sg+1] != INVALID) + { + subSlice_insts.emplace_back(res_lut[sg+1]); + } + } + + for (int idx = 0; idx < subSlice_insts.size(); ++idx) + { + int instId = subSlice_insts[idx]; + + int ndOutId = 4*instId; + int ndOutPins = ndOutId + 4; + for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + subSlice_outNets.emplace_back(outNetId); + } + } + if (subSlice_outNets.size() <= MAX_LOOPBACK_IN_SUBSLICE) continue; + std::sort(subSlice_outNets.begin(), subSlice_outNets.end()); + subSlice_outNets.erase(std::unique(subSlice_outNets.begin(), subSlice_outNets.end()), subSlice_outNets.end()); + + int num_loopbacks = 0; + //Compare sorted subSlice outNets with Slice input nets + + int idxIn = 0, idxOut = 0; + int netIn = all_inNets[idxIn]; + int netOut = subSlice_outNets[idxOut]; + + while (num_loopbacks <= MAX_LOOPBACK_IN_SUBSLICE) + { + if (netIn < netOut) + { + ++idxIn; + if (idxIn < all_inNets.size()) + { + netIn = all_inNets[idxIn]; + } else + { + break; + } + } else if (netIn > netOut) + { + ++idxOut; + if (idxOut < subSlice_outNets.size()) + { + netOut = subSlice_outNets[idxOut]; + } else + { + break; + } + } else + { + ++num_loopbacks; + ++idxIn; + ++idxOut; + if (idxIn < all_inNets.size() && idxOut < subSlice_outNets.size()) + { + netIn = all_inNets[idxIn]; + netOut = subSlice_outNets[idxOut]; + } else + { + break; + } + } + } + if (sg == ssId && lut_ff_conns > 0) + { + --num_loopbacks; + } + + if (num_loopbacks > MAX_LOOPBACK_IN_SUBSLICE) + { + return false; + } + } + + return true; +} + +//Given lut arrangement, fit FFs +inline bool fit_ffs( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* node2fence_region_map, const int* res_lut, const int lutId, + const int lut_maxShared, const int SLICE_CAPACITY, const int BLE_CAPACITY, + int* res_ff) +{ + ////DBG + //char printMsg = 0; + //int dbgInstId = INVALID; + ////DBG + + //Rearrange all FFs based on compatibility + std::vector rem_ffs; + int temp_ffs[SLICE_MAX_CAP]; + + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + temp_ffs[sg] = res_ff[sg]; + temp_ffs[sg + 1] = res_ff[sg + 1]; + + if (res_ff[sg] != INVALID || res_ff[sg + 1] != INVALID) + { + if (!subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, + res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, sg, INVALID)) + { + if (res_ff[sg] != INVALID) + { + rem_ffs.emplace_back(res_ff[sg]); + res_ff[sg] = INVALID; + } + if (res_ff[sg+1] != INVALID) + { + rem_ffs.emplace_back(res_ff[sg+1]); + res_ff[sg+1] = INVALID; + } + } + } + } + + if (rem_ffs.size() == 0) return true; + + //Greedily assign FF to empty compatible location + char ffLocFound = 0; + for (int el = 0; el < rem_ffs.size(); ++el) + { + int ffId = rem_ffs[el]; + for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) + { + if (res_ff[sg] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, sg, ffId)) + { + res_ff[sg] = ffId; + ++ffLocFound; + break; + } + } else if (res_ff[sg + 1] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, sg + 1, ffId)) + { + res_ff[sg + 1] = ffId; + ++ffLocFound; + break; + } + } + } + } + + if (ffLocFound == rem_ffs.size()) + { + ////DBG + //if (printMsg == 1) + //{ + // std::cout << "FFs after fit ffs: "; + // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // std::cout << res_ff[sg] << " "; + // } + // std::cout << std::endl; + //} + ////DBG + + return true; + } + + ////DBG + //if (printMsg == 1) + //{ + // std::cout << "FFs after fit ffs (failed): "; + // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // std::cout << res_ff[sg] << " "; + // } + // std::cout << std::endl; + //} + ////DBG + + //Revert + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_ff[sg] = temp_ffs[sg]; + } + + return false; +} + +//define check_sig_in_site_next_pq_sig +inline bool check_sig_in_site_next_pq_sig( + const int* nwCand_sig, const int nwCand_sigIdx, const int sPQ, + const int PQ_IDX, const int* site_next_pq_validIdx, + const int* site_next_pq_sig, const int* site_next_pq_sig_idx, + const int SIG_IDX) +{ + std::vector candEls, nextEls; + for (int x = 0; x < nwCand_sigIdx; ++x) + { + candEls.emplace_back(nwCand_sig[x]); + } + std::sort(candEls.begin(), candEls.end()); + + for (int i = 0; i < PQ_IDX; ++i) + { + int sigIdx = sPQ + i; + if (site_next_pq_validIdx[sigIdx] != INVALID && + site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) + { + int pqIdx(sigIdx*SIG_IDX); + + nextEls.clear(); + + for (int x = 0; x < site_next_pq_sig_idx[sigIdx]; ++x) + { + nextEls.emplace_back(site_next_pq_sig[pqIdx + x]); + } + + std::sort(nextEls.begin(), nextEls.end()); + + if (candEls == nextEls) + { + return true; + } + } + } + return false; +} + +//define add_inst_to_sig +inline bool add_inst_to_sig( + const int node2prclstrCount, const int* flat_node2precluster_map, + const int instPcl, int* nwCand_sig, + int& nwCand_sigIdx, const int SLICE_CAPACITY) +{ + std::vector temp; + + for (int el = 0; el < node2prclstrCount; ++el) + { + int newInstId = flat_node2precluster_map[instPcl+el]; + //Ensure instance is not in sig + if (!inst_in_sig(newInstId, nwCand_sigIdx, nwCand_sig, 0)) + { + temp.emplace_back(newInstId); + } else + { + return false; + } + } + + if (nwCand_sigIdx + temp.size() > 2*SLICE_CAPACITY) + { + return false; + } + + for (int mBIdx = 0; mBIdx < temp.size(); ++mBIdx) + { + nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; + ++nwCand_sigIdx; + } + return true; +} + +//define add flop to candidate +inline bool add_flop_to_candidate_impl( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, + const int* flop2ctrlSetId_map, const int* node2fence_region_map, + const int* res_lut, const int lutId, const int ffCKSR, + const int ffCE, const int ffId, const int half_ctrl_mode, + const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, + const int CKSR_IN_CLB, const int CE_IN_CLB, const int BLE_CAPACITY, + const int lut_maxShared, int* res_ff, int* res_cksr, int* res_ce) +{ + if (half_ctrl_mode == 1) + { + for (int i = 0; i < CKSR_IN_CLB; ++i) + { + if (res_cksr[i] != INVALID && + res_cksr[i] != ffCKSR) + { + continue; + } + + for (int j = 0; j < CKSR_IN_CLB; ++j) + { + int ceIdx = CKSR_IN_CLB*i + j; + if (res_ce[ceIdx] != INVALID && + res_ce[ceIdx] != ffCE) + { + continue; + } + + int beg = i*HALF_SLICE_CAPACITY+j; + int end = beg + HALF_SLICE_CAPACITY; + for (int k = beg; k < end; k += BLE_CAPACITY) + { + if (res_ff[k] == INVALID) + { + res_ff[k] = ffId; + res_cksr[i] = ffCKSR; + res_ce[ceIdx] = ffCE; + + return true; + } + } + } + } + } else + { + //FF Ctrls are SHARED across the SLICE + int ckID = INVALID; + for (int i = 0; i < CKSR_IN_CLB; ++i) + { + if (res_cksr[i] == ffCKSR || res_cksr[i] == INVALID) + { + ckID = i; + break; + } + } + + if (ckID != INVALID) + { + int fCtrlId = flop2ctrlSetId_map[ffId]; + int fCStartId = ext_ctrlSet_start_map[fCtrlId]; + int fCEndId = ext_ctrlSet_start_map[fCtrlId+1]; + + ////DBG + //int numffCtrls = fCEndId - fCStartId; + //std::cout << "add flop to candidate: " << ffId << " with CKSR: " + // << ffCKSR << " and " << numffCtrls << " Ctrl signals to candidate" << std::endl; + //std::cout << "FF Ctrl signals: "; + //for (int j = fCStartId; j < fCEndId; ++j) + //{ + // std::cout << extended_ctrlSets[j] << " "; + //} + //std::cout << std::endl; + //std::cout << "Existing Candidate ffs: "; + //for (int t = 0; t < SLICE_CAPACITY; ++t) + //{ + // std::cout << res_ff[t] << " "; + //} + //std::cout << std::endl; + //std::cout << " FF CKSR: "; + //for (int t = 0; t < CKSR_IN_CLB; ++t) + //{ + // std::cout << res_cksr[t] << " "; + //} + //std::cout << std::endl; + //std::cout << " FF CE: "; + //for (int t = 0; t < CE_IN_CLB; ++t) + //{ + // std::cout << res_ce[t] << " "; + //} + //std::cout << std::endl; + ////DBG + + std::vector upd_ctrls; + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + if (res_ce[sg] != INVALID) + { + upd_ctrls.emplace_back(res_ce[sg]); + } + } + + int initial_ctrls = upd_ctrls.size(); + + //For multiple ff ctrl signals - Ensure all signals can be accomodated + for (int j = fCStartId; j < fCEndId; ++j) + { + int ffCtrl = extended_ctrlSets[j]; + upd_ctrls.emplace_back(ffCtrl); + } + + if (upd_ctrls.size() > initial_ctrls) + { + std::sort(upd_ctrls.begin(), upd_ctrls.end()); + upd_ctrls.erase(std::unique(upd_ctrls.begin(), upd_ctrls.end()), upd_ctrls.end()); + } + + if (upd_ctrls.size() > CE_IN_CLB) + { + return false; + } + + int fIndex = INVALID; + for (int i = 0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_ff[i] == INVALID) + { + if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i, ffId)) + { + fIndex = i; + } + } else if (res_ff[i+1] == INVALID) + { + + if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i+1, ffId)) + { + fIndex = i+1; + } + } + + if (fIndex == INVALID) + { + continue; + } + + res_ff[fIndex] = ffId; + res_cksr[ckID] = ffCKSR; + + for (int j = 0; j < upd_ctrls.size(); ++j) + { + res_ce[j] = upd_ctrls[j]; + } + for (int j = upd_ctrls.size(); j < CE_IN_CLB; ++j) + { + res_ce[j] = INVALID; + } + + ////DBG + //std::cout << "SUCCEEDED to add flop: " << ffId << " with CKSR: " + // << ffCKSR << " and CE: " << ffCE << " to candidate" << std::endl; + //std::cout << "UPDATED Candidate ffs: "; + //for (int t = 0; t < SLICE_CAPACITY; ++t) + //{ + // std::cout << res_ff[t] << " "; + //} + //std::cout << std::endl; + //std::cout << " FF CKSR: "; + //for (int t = 0; t < CKSR_IN_CLB; ++t) + //{ + // std::cout << res_cksr[t] << " "; + //} + //std::cout << std::endl; + //std::cout << " FF CE: "; + //for (int t = 0; t < CE_IN_CLB; ++t) + //{ + // std::cout << res_ce[t] << " "; + //} + //std::cout << std::endl; + ////DBG + + return true; + } + } + } + ////DBG + //std::cout << "FAILED to add flop: " << ffId << " with CKSR: " + // << ffCKSR << " and CE: " << ffCE << "to candidate" << std::endl; + ////DBG + return false; +} + +// define remove_invalid_neighbor +inline void remove_invalid_neighbor( + const int sIdx, const int sNbrIdx, int* site_nbr_idx, int* site_nbr) +{ + std::vector temp; + for (int i = 0; i < site_nbr_idx[sIdx]; ++i) + { + if (site_nbr[sNbrIdx+i] != INVALID) + { + temp.emplace_back(site_nbr[sNbrIdx+i]); + } + } + + for(unsigned int j = 0; j < temp.size(); ++j) + { + site_nbr[sNbrIdx+j] = temp[j]; + } + for(int j = (int)temp.size(); j < site_nbr_idx[sIdx]; ++j) + { + site_nbr[sNbrIdx+j] = INVALID; + } + site_nbr_idx[sIdx] = temp.size(); +} + +// define compute_wirelength_improv +template +void compute_wirelength_improv( + const T* pos_x, const T* pos_y, const T* net_bbox, const T* pin_offset_x, + const T* pin_offset_y, const T* net_weights, const int* net_pinIdArrayX, + const int* net_pinIdArrayY, const int* flat_net2pin_start_map, + const int* pin2node_map, const int* net2pincount, const T* site_xy, + const T xWirelenWt, const T yWirelenWt, const int currNetId, + const int cand_siteId, const std::vector &pins, T &result) +{ + int cNbId(currNetId*4); + T netXlen = net_bbox[cNbId+2] - net_bbox[cNbId]; + T netYlen = net_bbox[cNbId+3] - net_bbox[cNbId+1]; + if ((int)pins.size() == net2pincount[currNetId]) + { + T bXLo(pin_offset_x[pins[0]]); + T bXHi(pin_offset_x[pins[0]]); + T bYLo(pin_offset_y[pins[0]]); + T bYHi(pin_offset_y[pins[0]]); + + for(auto poI = 1; poI < pins.size(); ++poI) + { + T poX = pin_offset_x[pins[poI]]; + T poY = pin_offset_y[pins[poI]]; + if (poX < bXLo) + { + bXLo = poX; + } else if (poX > bXHi) + { + bXHi = poX; + } + if (poY < bYLo) + { + bYLo = poY; + } else if (poY > bYHi) + { + bYHi = poY; + } + } + result += net_weights[currNetId] * (xWirelenWt * (netXlen - (bXHi-bXLo)) + yWirelenWt * (netYlen - (bYHi - bYLo))); + return; + } + T bXLo(net_bbox[cNbId]); + T bYLo(net_bbox[cNbId+1]); + T bXHi(net_bbox[cNbId+2]); + T bYHi(net_bbox[cNbId+3]); + int cStId = cand_siteId*2; + T locX = site_xy[cStId]; + T locY = site_xy[cStId+1]; + + if (locX <= bXLo) + { + bXLo = locX; + } else + { + int n2pId = flat_net2pin_start_map[currNetId]; + while(n2pId < flat_net2pin_start_map[currNetId+1] && + std::find(pins.begin(), pins.end(), net_pinIdArrayX[n2pId]) != pins.end()) + { + ++n2pId; + } + int reqPId = net_pinIdArrayX[n2pId]; + T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; + bXLo = DREAMPLACE_STD_NAMESPACE::min(pinX, locX); + } + + if (locX >= bXHi) + { + bXHi = locX; + } else + { + int n2pId = flat_net2pin_start_map[currNetId+1]-1; + while(n2pId >= flat_net2pin_start_map[currNetId] && + std::find(pins.begin(), pins.end(), net_pinIdArrayX[n2pId]) != pins.end()) + { + --n2pId; + } + int reqPId = net_pinIdArrayX[n2pId]; + T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; + bXHi = DREAMPLACE_STD_NAMESPACE::max(pinX, locX); + } + + if (locY <= bYLo) + { + bYLo = locY; + } else + { + int n2pId = flat_net2pin_start_map[currNetId]; + while(n2pId < flat_net2pin_start_map[currNetId+1] && + std::find(pins.begin(), pins.end(), net_pinIdArrayY[n2pId]) != pins.end()) + { + ++n2pId; + } + int reqPId = net_pinIdArrayY[n2pId]; + T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; + bYLo = DREAMPLACE_STD_NAMESPACE::min(pinY, locY); + } + + if (locY >= bYHi) + { + bYHi = locY; + } else + { + int n2pId = flat_net2pin_start_map[currNetId+1]-1; + while(n2pId >= flat_net2pin_start_map[currNetId] && + std::find(pins.begin(), pins.end(), net_pinIdArrayY[n2pId]) != pins.end()) + { + --n2pId; + } + int reqPId = net_pinIdArrayY[n2pId]; + T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; + bYHi = DREAMPLACE_STD_NAMESPACE::max(pinY, locY); + } + result += net_weights[currNetId] * (xWirelenWt * (netXlen - (bXHi-bXLo)) + yWirelenWt * (netYlen - (bYHi - bYLo))); + return; +} + +//addLUTToCandidateImpl +inline bool add_lut_to_cand_impl( + const int* node2outpinIdx_map, const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* node2fence_region_map, const int* res_ff, const int lutId, + const int lutTypeInSliceUnit, const int lut_maxShared, + const int lutInstId, const int SLICE_CAPACITY, const int BLE_CAPACITY, + const int half_ctrl_mode, int* res_lut) +{ + if (half_ctrl_mode == 1) + { + for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + res_lut[i] = lutInstId; + return true; + } + } + for (int i=1; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, lutTypeInSliceUnit, + lut_maxShared, res_lut[i-1], lutInstId)) + { + res_lut[i] = lutInstId; + return true; + } + } + } + } else + { + for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, + res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, i, lutInstId)) + { + res_lut[i] = lutInstId; + return true; + } + } else if (res_lut[i+1] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, + res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, + BLE_CAPACITY, i+1, lutInstId)) + { + res_lut[i+1] = lutInstId; + return true; + } + } + } + } + + return false; +} + +//computeCandidateScore +template +inline void compute_candidate_score( + const T* pos_x, const T* pos_y, const T* pin_offset_x, const T* pin_offset_y, + const T* net_bbox, const T* net_weights, const T* site_xy, + const int* net_pinIdArrayX, const int* net_pinIdArrayY, + const int* flat_net2pin_start_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* sorted_net_map, const int* pin2net_map, + const int* pin2node_map, const int* net2pincount, const int* lut_type, + const T xWirelenWt, const T yWirelenWt, const T extNetCountWt, + const T wirelenImprovWt, const int netShareScoreMaxNetDegree, + const int wlScoreMaxNetDegree, const int half_ctrl_mode, + const int* res_sig, const int res_siteId, const int res_sigIdx, T &result) +{ + T netShareScore(0.0), wirelenImprov(0.0); + T typeScore(0.0); + std::vector pins; + + for (int i = 0; i < res_sigIdx; ++i) + { + int instId = res_sig[i]; + //For macro nodes, same instId is repeated in sig + if (i != 0 && instId == res_sig[i-1]) continue; + for (int pId = flat_node2pin_start_map[instId]; pId < flat_node2pin_start_map[instId+1]; ++pId) + { + pins.emplace_back(flat_node2pin_map[pId]); + } + if (half_ctrl_mode == 0) + { + typeScore += lut_type[instId]; + } + } + std::sort(pins.begin(), pins.end(), [&pin2net_map,&sorted_net_map](const auto &a, const auto &b){ return pin2net_map[a] == pin2net_map[b] ? a < b : sorted_net_map[pin2net_map[a]] < sorted_net_map[pin2net_map[b]]; }); + + if (pins.empty()) + { + result = T(0.0); + return; + } + + int maxNetDegree = DREAMPLACE_STD_NAMESPACE::max(netShareScoreMaxNetDegree, wlScoreMaxNetDegree); + int currNetId = pin2net_map[pins[0]]; + + if (net2pincount[currNetId] > maxNetDegree) + { + result = T(0.0); + return; + } + + int numIntNets(0), numNets(0); + std::vector currNetIntPins; + + currNetIntPins.emplace_back(pins[0]); + + for(unsigned int pIdx = 1; pIdx < pins.size(); ++pIdx) + { + int netId = pin2net_map[pins[pIdx]]; + if (netId == currNetId) + { + currNetIntPins.emplace_back(pins[pIdx]); + } else + { + if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) + { + ++numNets; + numIntNets += (currNetIntPins.size() == net2pincount[currNetId] ? 1 : 0); + netShareScore += net_weights[currNetId] * (currNetIntPins.size() - 1.0) / DREAMPLACE_STD_NAMESPACE::max(1.0, net2pincount[currNetId] - 1.0); + } + if (net2pincount[currNetId] <= wlScoreMaxNetDegree) + { + compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, currNetId, res_siteId, currNetIntPins, wirelenImprov); + } + currNetId = netId; + if (net2pincount[currNetId] > maxNetDegree) + { + break; + } + currNetIntPins.clear(); + currNetIntPins.emplace_back(pins[pIdx]); + } + } + + if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) + { + ++numNets; + numIntNets += (currNetIntPins.size() == net2pincount[currNetId] ? 1 : 0); + netShareScore += net_weights[currNetId] * (currNetIntPins.size() - 1.0) / DREAMPLACE_STD_NAMESPACE::max(1.0, net2pincount[currNetId] - 1.0); + } + ////DBG + //if (res_siteId == dbg_siteId && res_sigIdx == dbg_sigIdx) + //{ + // std::cout << "Candidate score computation for res_siteId: " << res_siteId + // << " with " << numNets << " total nets and " << numIntNets + // << " internal nets and net share score: " << netShareScore << std::endl; + //} + ////DBG + + if (net2pincount[currNetId] <= wlScoreMaxNetDegree) + { + compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, + net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, + pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, currNetId, + res_siteId, currNetIntPins, wirelenImprov); + } + + netShareScore /= (T(1.0) + extNetCountWt * (numNets - numIntNets)); + result = netShareScore + wirelenImprovWt * wirelenImprov; + + if (half_ctrl_mode == 0) + { + result += T(0.1)*typeScore; + } + + ////DBG + //if (res_siteId == dbg_siteId && res_sigIdx == dbg_sigIdx) + //{ + // std::cout << "Candidate score computation for res_siteId: " << res_siteId + // << " with updated netShareScore: " << netShareScore << " and final score: " + // << result << " based on wirelength improvement of " << wirelenImprov + // << std::endl; + //} + ////DBG +} + +//fitLUTsToCandidateImpl +inline bool fit_luts_to_candidate_impl( + const int* node2outpinIdx_map, const int* lut_type, const int* node2pincount, + const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* flat_node2precluster_map, const int* node2fence_region_map, + const int* special_nodes, const int half_ctrl_mode, const int lutTypeInSliceUnit, + const int lut_maxShared, const int instPcl, const int node2prclstrCount, + const int NUM_BLE_PER_SLICE, const int SLICE_CAPACITY, const int BLE_CAPACITY, + const int lutId, int* res_ff, int* res_lut) +{ + std::vector luts, lut6s, splNodes; + + ////DBG + //std::cout << "fit luts to candidate impl: Try to add " << node2prclstrCount << " luts using graph matching to candidate: "; + //for (int idx = 0; idx < node2prclstrCount; ++idx) + //{ + // int clInstId = flat_node2precluster_map[instPcl + idx]; + // if (node2fence_region_map[clInstId] == lutId) + // { + // std::cout << clInstId << "(" << lut_type[clInstId] << ") "; + // } + //} + //std::cout << std::endl; + + //std::cout << " Candidate luts: "; + //for (int t = 0; t < SLICE_CAPACITY; ++t) + //{ + // std::cout << res_lut[t] << "(" << lut_type[res_lut[t]] << ") "; + //} + //std::cout << std::endl; + ////DBG + //Ensure subSlice-level shared input count is met + int temp_lut[SLICE_MAX_CAP]; + + for (int i = 0; i < SLICE_CAPACITY; ++i) + { + if (res_lut[i] != INVALID) + { + if (special_nodes[res_lut[i]] == 1) + { + splNodes.emplace_back(res_lut[i]); + } else + { + if (lut_type[res_lut[i]] < lutTypeInSliceUnit) + { + luts.emplace_back(res_lut[i]); + } else + { + lut6s.emplace_back(res_lut[i]); + } + } + } + if (half_ctrl_mode == 0) + { + temp_lut[i] = res_lut[i]; + } + } + + for (int idx = 0; idx < node2prclstrCount; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + if (node2fence_region_map[clInstId] == lutId) + { + if (lut_type[clInstId] < lutTypeInSliceUnit) + { + luts.emplace_back(clInstId); + std::sort(luts.begin(), luts.end()); + luts.erase(std::unique(luts.begin(), luts.end()), luts.end()); + } else + { + lut6s.emplace_back(clInstId); + std::sort(lut6s.begin(), lut6s.end()); + lut6s.erase(std::unique(lut6s.begin(), lut6s.end()), lut6s.end()); + } + } + } + + int splNodeSpace = splNodes.size(); + splNodeSpace += (splNodes.size() & 1) ? 1 : 0; + + if (luts.size() + 2*lut6s.size() + splNodeSpace > SLICE_CAPACITY) + { + return false; + } + + lemon::ListGraph graph; + std::vector nodes; + std::vector edges; + std::vector > edgePairs; + graph.clear(); + nodes.clear(); + edges.clear(); + edgePairs.clear(); + + int n = luts.size(); + // + + for (int il = 0; il < n; ++il) + { + nodes.emplace_back(graph.addNode()); + } + for(int ll = 0; ll < n; ++ll) + { + for(int rl = ll+1; rl < n; ++rl) + { + if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, lutTypeInSliceUnit, lut_maxShared, + luts[ll], luts[rl])) + { + edges.emplace_back(graph.addEdge(nodes[ll], nodes[rl])); + edgePairs.emplace_back(ll, rl); + } + } + } + + lemon::MaxMatching mm(graph); + mm.run(); + + int otherNodePairs = lut6s.size() + splNodeSpace/2; + if (n - (int)mm.matchingSize() + otherNodePairs > NUM_BLE_PER_SLICE) + { + return false; + } + + int idxL(0); + //Include special nodes first + if (splNodes.size() > 0) + { + for (int spId = 0; spId < splNodes.size(); ++spId) + { + res_lut[idxL] = splNodes[spId]; + ++idxL; + } + if (splNodeSpace > splNodes.size()) + { + res_lut[idxL] = INVALID; + ++idxL; + } + } + for (int iil = 0; iil < n; ++iil) + { + if (mm.mate(nodes[iil]) == lemon::INVALID && luts[iil] != INVALID) + { + res_lut[idxL] = luts[iil]; + res_lut[idxL + 1] = INVALID; + idxL += BLE_CAPACITY; + } + } + for(unsigned int iil = 0; iil < lut6s.size(); ++iil) + { + res_lut[idxL] = lut6s[iil]; + res_lut[idxL + 1] = INVALID; + idxL += BLE_CAPACITY; + } + for (unsigned int iil = 0; iil < edges.size(); ++iil) + { + if (mm.matching(edges[iil])) + { + const auto &p = edgePairs[iil]; + res_lut[idxL] = luts[p.first]; + res_lut[idxL + 1] = luts[p.second]; + idxL += BLE_CAPACITY; + } + } + + for (int lIdx = idxL; lIdx < SLICE_CAPACITY; ++lIdx) + { + res_lut[lIdx] = INVALID; + } + + ////DBG + //std::cout << "SUCCEEDED fit luts to candidate impl: Try to add " << node2prclstrCount << " luts using graph matching to candidate: "; + //for (int idx = 0; idx < node2prclstrCount; ++idx) + //{ + // int clInstId = flat_node2precluster_map[instPcl + idx]; + // if (node2fence_region_map[clInstId] == lutId) + // { + // std::cout << clInstId << "(" << lut_type[clInstId] << ") "; + // } + //} + //std::cout << std::endl; + + //std::cout << " Candidate luts: "; + //for (int t = 0; t < SLICE_CAPACITY; ++t) + //{ + // std::cout << res_lut[t] << "(" << lut_type[res_lut[t]] << ") "; + //} + //std::cout << std::endl; + ////DBG + + + //Ensure subSlice-level shared input count is met + if (half_ctrl_mode == 0) + { + //Try to rearrange ffs if there are compatibility issues + if (fit_ffs(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_lut, lutId, + lut_maxShared, SLICE_CAPACITY, BLE_CAPACITY, res_ff)) + { + return true; + } + + //Revert + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = temp_lut[sg]; + } + return false; + } + + return true; +} + +//template +inline bool is_inst_in_cand_feasible( + const int* node2outpinIdx_map, const int* node2fence_region_map, const int* lut_type, + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* node2pincount, const int* net2pincount, const int* pin2net_map, + const int* pin_typeIds, const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, const int* site_det_impl_lut, + const int* site_det_impl_ff, const int* site_det_impl_cksr, + const int* site_det_impl_ce, const int* special_nodes, + const int lutTypeInSliceUnit, const int lut_maxShared, + const int siteId, const int instId, const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, const int CKSR_IN_CLB, const int CE_IN_CLB, + const int lutId, const int ffId, const int half_ctrl_mode) +{ + int instPcl = instId*3; + + int sdlutId = siteId*SLICE_CAPACITY; + int sdckId = siteId*CKSR_IN_CLB; + int sdceId = siteId*CE_IN_CLB; + + ///// + int res_lut[SLICE_MAX_CAP]; + int res_ff[SLICE_MAX_CAP]; + int res_cksr[CKSR_MAX_CAP]; + int res_ce[CE_MAX_CAP]; + + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = site_det_impl_lut[sdlutId + sg]; + res_ff[sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + res_cksr[sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + res_ce[sg] = site_det_impl_ce[sdceId + sg]; + } + ///// + + bool lutFail(false); + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; + int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; + + if (node2fence_region_map[clInstId] == lutId) //LUT + { + if (!lutFail && !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, lutId, + lutTypeInSliceUnit, lut_maxShared, clInstId, SLICE_CAPACITY, BLE_CAPACITY, + half_ctrl_mode, res_lut)) + + { + lutFail = true; + } + } else if (node2fence_region_map[clInstId] == ffId) //FF + { + if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, ext_ctrlSet_start_map, + flop2ctrlSetId_map, node2fence_region_map, res_lut, lutId, flop_ctrlSets[clInstCKSR], + flop_ctrlSets[clInstCE], clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, res_cksr, res_ce)) + { + return false; + } + } + } + if (!lutFail) + { + return true; + } + + return fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, node2pincount, net2pincount, + pin2net_map, pin_typeIds, flat_node2pin_start_map, flat_node2pin_map, + flat_node2precluster_map, node2fence_region_map, special_nodes, + half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, instPcl, + flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, SLICE_CAPACITY, + BLE_CAPACITY, lutId, res_ff, res_lut); +} + +inline bool add_inst_to_cand_impl( + const int* node2outpinIdx_map, const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* node2pincount, const int* net2pincount, + const int* pin2net_map, const int* pin_typeIds, const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, const int* flop2ctrlSetId_map, + const int* node2fence_region_map, const int* flop_ctrlSets, const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, const int* special_nodes, const int lutTypeInSliceUnit, + const int lut_maxShared, const int instId, const int lutId, const int ffId, + const int half_ctrl_mode, const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, + int* nwCand_lut, int* nwCand_ff, int* nwCand_cksr, int* nwCand_ce) +{ + int instPcl = instId*3; + + int res_lut[SLICE_MAX_CAP]; + int res_ff[SLICE_MAX_CAP]; + int res_ce[CE_MAX_CAP]; + int res_cksr[CKSR_MAX_CAP]; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = nwCand_lut[sg]; + res_ff[sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + res_cksr[sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + res_ce[sg] = nwCand_ce[sg]; + } + + bool lutFail(false); + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; + int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; + + if (node2fence_region_map[clInstId] == lutId) //LUT + { + if (!lutFail && !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, lutId, + lutTypeInSliceUnit, lut_maxShared, clInstId, SLICE_CAPACITY, BLE_CAPACITY, half_ctrl_mode, + res_lut)) + { + lutFail = true; + } + } else if (node2fence_region_map[clInstId] == ffId) //FF + { + if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, extended_ctrlSets, ext_ctrlSet_start_map, flop2ctrlSetId_map, + node2fence_region_map, res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], + clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + BLE_CAPACITY, lut_maxShared, res_ff, res_cksr, res_ce)) + { + return false; + } + } + } + if (!lutFail) + { + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = res_lut[sg]; + nwCand_ff[sg] = res_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = res_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = res_ce[sg]; + } + return true; + } + if (fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, node2pincount, + net2pincount, pin2net_map, pin_typeIds, flat_node2pin_start_map, + flat_node2pin_map, flat_node2precluster_map, node2fence_region_map, + special_nodes, half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, + instPcl, flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, + SLICE_CAPACITY, BLE_CAPACITY, lutId, res_ff, res_lut)) + { + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = res_lut[sg]; + nwCand_ff[sg] = res_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = res_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = res_ce[sg]; + } + return true; + } + return false; +} + +//template +inline void remove_incompatible_neighbors( + const int* node2outpinIdx_map, const int* node2fence_region_map, const int* lut_type, + const int* flat_node2pin_start_map, const int* flat_node2pin_map, const int* node2pincount, + const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, + const int* flat_node2prclstrCount, const int* flat_node2precluster_map, + const int* flop2ctrlSetId_map, const int* flop_ctrlSets, const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, const int* site_det_impl_lut, const int* site_det_impl_ff, + const int* site_det_impl_cksr, const int* site_det_impl_ce, const int* site_det_sig, + const int* site_det_sig_idx, const int* special_nodes, const int lutTypeInSliceUnit, + const int lut_maxShared, const int siteId, const int sNbrIdx, const int half_ctrl_mode, + const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, + const int lutId, const int ffId, int* site_nbr_idx, int* site_nbr) +{ + int sdtopId = siteId*SIG_IDX; + for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) + { + int instId = site_nbr[sNbrIdx + nbrId]; + + if (inst_in_sig(instId, site_det_sig_idx[siteId], site_det_sig, sdtopId) || + !is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, + pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, + flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, + site_det_impl_ce, special_nodes, lutTypeInSliceUnit, lut_maxShared, + siteId, instId, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, half_ctrl_mode)) + { + site_nbr[sNbrIdx + nbrId] = INVALID; + } + } + //Remove invalid neighbor instances + remove_invalid_neighbor(siteId, sNbrIdx, site_nbr_idx, site_nbr); +} + +template +inline void computeBLEScore( + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* flat_net2pin_start_map, const int* flat_net2pin_map, + const int* pin2net_map, const int* pin2node_map, + const int* node2outpinIdx_map, const int* pin_typeIds, + const int* lut_type, const int half_ctrl_mode, const int lutA, + const int lutB, const int ffA, const int ffB, T& score) +{ + int numShareInputs = 0; + + if (lutA != INVALID && lutB != INVALID && lut_type[lutA] != 0 && lut_type[lutB] != 0) + { + //Note: It is important that nets of lutA and lutB are sorted before comparison + std::vector lutAiNets, lutBiNets; + + int lutAIt = flat_node2pin_start_map[lutA]; + int lutBIt = flat_node2pin_start_map[lutB]; + int lutAEnd = flat_node2pin_start_map[lutA+1]; + int lutBEnd = flat_node2pin_start_map[lutB+1]; + + for (int el = lutAIt; el < lutAEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutAiNets.emplace_back(netId); + } + std::sort(lutAiNets.begin(), lutAiNets.end()); + + for (int el = lutBIt; el < lutBEnd; ++el) + { + //Skip if not an input pin + if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + lutBiNets.emplace_back(netId); + } + std::sort(lutBiNets.begin(), lutBiNets.end()); + + int idxA = 0, idxB = 0; + int netIdA = lutAiNets[idxA]; + int netIdB = lutBiNets[idxB]; + + while(true) + { + if (netIdA < netIdB) + { + ++idxA; + if (idxA < lutAiNets.size()) + { + netIdA = lutAiNets[idxA]; + } else + { + break; + } + } else if (netIdA > netIdB) + { + ++idxB; + if (idxB < lutBiNets.size()) + { + netIdB = lutBiNets[idxB]; + } else + { + break; + } + + } else + { + ++numShareInputs; + ++idxA; + ++idxB; + + if (idxA < lutAiNets.size() && idxB < lutBiNets.size()) + { + netIdA = lutAiNets[idxA]; + netIdB = lutBiNets[idxB]; + } else + { + break; + } + } + } + } + + + int numIntNets = 0; + for (int id : {lutA, lutB}) + { + if (id == INVALID) continue; + + int ndOutId = 4*id; + int ndOutPins = ndOutId + 4; + for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + for (int pId = flat_net2pin_start_map[outNetId]; + pId < flat_net2pin_start_map[outNetId+1]; ++pId) + { + int pinId = flat_net2pin_map[pId]; + int nodeId = pin2node_map[pinId]; + if (pin_typeIds[pinId] == 1 && (nodeId == ffA || nodeId == ffB)) + { + ++numIntNets; + } + } + } + } + score = T(0.1) * numShareInputs + numIntNets; + T numFF = (ffA == INVALID ? 0:1) + (ffB == INVALID ? 0 : 1); + + if (half_ctrl_mode == 1) + { + score -= T(0.01)*numFF; + } else + { + //Prefer more ffs being packed + score += T(0.01)*numFF; + } +} + +template +inline void findBestFFs( + const int* flop_ctrlSets, const int* flop2ctrlSetId_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* flat_net2pin_start_map, const int* flat_net2pin_map, + const int* pin2net_map, const int* node2pincount, const int* pin_typeIds, const int* net2pincount, + const int* node2outpinIdx_map, const int* pin2node_map, const int* sorted_net_map, + const int* lut_type, const std::vector &ff, const int half_ctrl_mode, + const int cksr, const int ce0, const int ce1, subSlice& ble) +{ + ble.score = 0.0; + ble.ff[0] = INVALID; + ble.ff[1] = INVALID; + + for(unsigned int aIdx = 0; aIdx < ff.size(); ++aIdx) + { + const int ffA = ff[aIdx]; + int cksrA = flop_ctrlSets[flop2ctrlSetId_map[ffA]*3 + 1]; + int ceA = flop_ctrlSets[flop2ctrlSetId_map[ffA]*3 + 2]; + + if (cksrA == cksr && (ceA == ce0 || ceA == ce1)) + { + T score = 0.0; + computeBLEScore(flat_node2pin_start_map, flat_node2pin_map,flat_net2pin_start_map, + flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, + pin_typeIds, lut_type, half_ctrl_mode, ble.lut[0], ble.lut[1], ffA, INVALID, score); + + if (score > ble.score) + { + ble.ff[0] = ffA; + ble.ff[1] = INVALID; + if (ceA != ce0) + { + std::swap(ble.ff[0], ble.ff[1]); + } + ble.score = score; + } + } + //FF pairs + for(unsigned int bIdx = aIdx + 1; bIdx < ff.size(); ++bIdx) + { + int ffB = ff[bIdx]; + int cksrB = flop_ctrlSets[flop2ctrlSetId_map[ffB]*3 + 1]; + int ceB = flop_ctrlSets[flop2ctrlSetId_map[ffB]*3 + 2]; + if (cksrA == cksr && cksrB == cksr && ((ceA == ce0 && ceB == ce1) || (ceA == ce1 && ceB == ce0))) + { + T score = 0.0; + computeBLEScore(flat_node2pin_start_map, flat_node2pin_map,flat_net2pin_start_map, + flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, + pin_typeIds, lut_type, half_ctrl_mode, ble.lut[0], ble.lut[1], ffA, ffB, score); + + if (score > ble.score) + { + ble.ff[0] = ffA; + ble.ff[1] = ffB; + if (ceA != ce0) + { + std::swap(ble.ff[0], ble.ff[1]); + } + ble.score = score; + } + } + } + } +} + +template +void pairLUTs( + const std::vector &lut, const std::vector > &bleP, const std::vector > &bleS, + const T slotAssignFlowWeightScale, const T slotAssignFlowWeightIncr, const int NUM_BLE_PER_SLICE, + std::vector > &bleLP) +{ + lemon::ListGraph graph; + std::vector nodes; + std::vector edges; + graph.clear(); + nodes.clear(); + edges.clear(); + lemon::ListGraph::EdgeMap wtMap(graph); + + // Get LUT ID to index mapping + std::unordered_map idxMap; + for (unsigned int i = 0; i < lut.size(); ++i) + { + idxMap[lut[i]] = i; + } + // Build the graph use LUT pair score improvement as the edge weights + for (unsigned int i = 0; i < lut.size(); ++i) + { + nodes.emplace_back(graph.addNode()); + } + + for (const auto &ble: bleP) + { + edges.emplace_back(graph.addEdge(nodes[idxMap[ble.lut[0]]], nodes[idxMap[ble.lut[1]]])); + wtMap[edges.back()] = ble.improv * slotAssignFlowWeightScale; + } + // Use iterative max-weighted matching to find the best legal LUT pairing + while (true) + { + lemon::MaxWeightedMatching > mwm(graph, wtMap); + mwm.run(); + if (nodes.size() - mwm.matchingSize() <= NUM_BLE_PER_SLICE) + { + bleLP.clear(); + // Collect the LUT pairing solution + for (unsigned int i = 0; i < edges.size(); ++i) + { + if (mwm.matching(edges[i])) + { + bleLP.emplace_back(bleP[i]); + } + } + for (unsigned int i = 0; i < nodes.size(); ++i) + { + if (mwm.mate(nodes[i]) == lemon::INVALID) + { + bleLP.emplace_back(bleS[i]); + } + } + return; + } + // Increase all edge weight to get a tighter LUT pairing solution + int incr = slotAssignFlowWeightIncr * slotAssignFlowWeightScale; + for (const auto &e : edges) + { + wtMap[e] += incr; + } + } +} + +template +inline bool compare_pq_tops( + const T* site_curr_pq_score, const int* site_curr_pq_top_idx, const int* site_curr_pq_validIdx, + const int* site_curr_pq_siteId, const int* site_curr_pq_sig_idx, const int* site_curr_pq_sig, + const int* site_curr_pq_impl_lut, const int* site_curr_pq_impl_ff, const int* site_curr_pq_impl_cksr, + const int* site_curr_pq_impl_ce, const T* site_next_pq_score, const int* site_next_pq_top_idx, + const int* site_next_pq_validIdx, const int* site_next_pq_siteId, const int* site_next_pq_sig_idx, + const int* site_next_pq_sig, const int* site_next_pq_impl_lut, const int* site_next_pq_impl_ff, + const int* site_next_pq_impl_cksr, const int* site_next_pq_impl_ce, const int siteId, + const int sPQ, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY) +{ + //Check site_curr_pq TOP == site_next_pq TOP + int curr_pq_topId = sPQ+site_curr_pq_top_idx[siteId]; + int next_pq_topId = sPQ+site_next_pq_top_idx[siteId]; + + if (site_curr_pq_validIdx[curr_pq_topId] != site_next_pq_validIdx[next_pq_topId] || + site_curr_pq_validIdx[curr_pq_topId] != 1) + { + return false; + } + if (site_curr_pq_score[curr_pq_topId] == site_next_pq_score[next_pq_topId] && + site_curr_pq_siteId[curr_pq_topId] == site_next_pq_siteId[next_pq_topId] && + site_curr_pq_sig_idx[curr_pq_topId] == site_next_pq_sig_idx[next_pq_topId]) + { + //Check both sig + int currPQSigIdx = curr_pq_topId*SIG_IDX; + int nextPQSigIdx = next_pq_topId*SIG_IDX; + + for (int sg = 0; sg < site_curr_pq_sig_idx[curr_pq_topId]; ++sg) + { + if (site_curr_pq_sig[currPQSigIdx + sg] != site_next_pq_sig[nextPQSigIdx + sg]) + { + return false; + } + } + + //Check impl + int cCKRId = curr_pq_topId*CKSR_IN_CLB; + int cCEId = curr_pq_topId*CE_IN_CLB; + int cFFId = curr_pq_topId*SLICE_CAPACITY; + int nCKRId = next_pq_topId*CKSR_IN_CLB; + int nCEId = next_pq_topId*CE_IN_CLB; + int nFFId = next_pq_topId*SLICE_CAPACITY; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + if (site_curr_pq_impl_lut[cFFId + sg] != site_next_pq_impl_lut[nFFId + sg] || + site_curr_pq_impl_ff[cFFId + sg] != site_next_pq_impl_ff[nFFId + sg]) + { + return false; + } + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + if (site_curr_pq_impl_cksr[cCKRId + sg] != site_next_pq_impl_cksr[nCKRId + sg]) + { + return false; + } + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + if(site_curr_pq_impl_ce[cCEId + sg] != site_next_pq_impl_ce[nCEId + sg]) + { + return false; + } + } + ///// + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// Initialize Nets +template +int initializeNets(const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* sorted_net_idx, + const int* pin2node_map, + const int* net2pincount, + const int num_nets, + T* net_bbox, + int* net_pinIdArrayX, + int* net_pinIdArrayY, + int WLscoreMaxNetDegree, + const int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + const int idx = sorted_net_idx[i]; + + if (net2pincount[idx] > 0 && net2pincount[idx] <= WLscoreMaxNetDegree) + { + int xLo = idx*4; + int yLo = xLo+1; + int xHi = xLo+2; + int yHi = xLo+3; + + int pinIdBeg = flat_net2pin_start_map[idx]; + int pinIdEnd = flat_net2pin_start_map[idx+1]; + + int pnIdx = flat_net2pin_map[pinIdBeg]; + int nodeIdx = pin2node_map[pnIdx]; + + std::vector > tempX, tempY; + + net_bbox[xLo] = pos_x[nodeIdx] + pin_offset_x[pnIdx]; + net_bbox[yLo] = pos_y[nodeIdx] + pin_offset_y[pnIdx]; + net_bbox[xHi] = net_bbox[xLo]; + net_bbox[yHi] = net_bbox[yLo]; + + tempX.emplace_back(pnIdx, net_bbox[xLo]); + tempY.emplace_back(pnIdx, net_bbox[yLo]); + + //Update Net Bbox based on node location and pin offset + for (int pId = pinIdBeg+1; pId < pinIdEnd; ++pId) + { + int pinIdx = flat_net2pin_map[pId]; + int ndIdx = pin2node_map[pinIdx]; + + T valX = pos_x[ndIdx] + pin_offset_x[pinIdx]; + T valY = pos_y[ndIdx] + pin_offset_y[pinIdx]; + + if (valX < net_bbox[xLo]) + { + net_bbox[xLo] = valX; + } else if (valX > net_bbox[xHi]) + { + net_bbox[xHi] = valX; + } + + if (valY < net_bbox[yLo]) + { + net_bbox[yLo] = valY; + } else if (valY > net_bbox[yHi]) + { + net_bbox[yHi] = valY; + } + + tempX.emplace_back(pinIdx, valX); + tempY.emplace_back(pinIdx, valY); + } + + //Sort pinIdArray based on node loc and pin offset + std::sort(tempX.begin(), tempX.end(), [&](const auto &a, const auto &b){ return a.second < b.second; }); + std::sort(tempY.begin(), tempY.end(), [&](const auto &a, const auto &b){ return a.second < b.second; }); + + //Assign sorted values back + int tempId(0); + for (int pId = pinIdBeg; pId < pinIdEnd; ++pId) + { + net_pinIdArrayX[pId] = tempX[tempId].first; + net_pinIdArrayY[pId] = tempY[tempId].first; + ++tempId; + } + } + } + return 0; +} + +// preClustering +template +int preClustering(const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* sorted_node_map, + const int* sorted_node_idx, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* node2fence_region_map, + const int* node2outpinIdx_map, + const int* pin2net_map, + const int* pin2node_map, + const int* pin_typeIds, + const int lutId, + const int ffId, + const int num_nodes, + const T preClusteringMaxDist, + int* flat_node2precluster_map, + int* flat_node2prclstrCount, + const int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + const int instId = sorted_node_idx[i]; + if (node2fence_region_map[instId] == lutId) //Only consider LUTs + { + std::vector > ffs; + int nPIdx = instId*4; + for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + + int pinIdxBeg = flat_net2pin_start_map[outNetId]; + int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; + + T instLocX = pos_x[instId] + pin_offset_x[outPinId]; + T instLocY = pos_y[instId] + pin_offset_y[outPinId]; + for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) + { + int pinIdx = flat_net2pin_map[pinId]; + int nodeIdx = pin2node_map[pinIdx]; + + T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; + T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; + + T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + DREAMPLACE_STD_NAMESPACE::abs(distY); + + if (pin_typeIds[pinIdx] == 1 && node2fence_region_map[nodeIdx] == ffId && + dist <= preClusteringMaxDist) + { + ffs.emplace_back(std::make_pair(nodeIdx, dist)); + } + } + } + if (ffs.empty()) + { + continue; + } + + //Get FF index with min value (without Sort FFs) + std::sort(ffs.begin(), ffs.end(), [&sorted_node_map](const auto &a, const auto &b){ return a.second == b.second ? sorted_node_map[a.first] < sorted_node_map[b.first] : a.second < b.second; }); + + nPIdx = instId*3; + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[0].first; + ++flat_node2prclstrCount[instId]; + int fcIdx = flop2ctrlSetId_map[ffs[0].first]*3 + 1; + int cksr = flop_ctrlSets[fcIdx]; + + for (unsigned int fIdx = 1; fIdx < ffs.size(); ++fIdx) + { + int ctrlIdx = flop2ctrlSetId_map[ffs[fIdx].first]*3 + 1; + int fCksr = flop_ctrlSets[ctrlIdx]; + + if (fCksr == cksr) + { + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[fIdx].first; + ++flat_node2prclstrCount[instId]; + //std::cout << "BEFORE SORT: Inst Id: " << instId << " has precluster: " << flat_node2precluster_map[nPIdx] << ", " << flat_node2precluster_map[nPIdx + 1] << ", " << flat_node2precluster_map[nPIdx + 2] << std::endl; + break; + } + } + + std::sort(flat_node2precluster_map+nPIdx, flat_node2precluster_map+nPIdx + flat_node2prclstrCount[instId], [&sorted_node_map](const auto &a, const auto &b){return sorted_node_map[a] < sorted_node_map[b];}); + + for (int prcl = 0; prcl < flat_node2prclstrCount[instId]; ++prcl) + { + int fIdx = flat_node2precluster_map[nPIdx + prcl]; + int fID = fIdx*3; + if (fIdx != instId) + { + for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) + { + flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; + } + flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[instId]; + } + } + } + } + return 0; +} + +//TODO-Remove is_mlab_node when MLABs are treated separately +//Preclustering to handle carry chains and mlabs +template +int preClusteringUpd( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* sorted_node_map, + const int* sorted_node_idx, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* node2fence_region_map, + const int* node2outpinIdx_map, + const int* pin2net_map, + const int* pin2node_map, + const int* pin_typeIds, + const int* is_mlab_node, + const T preClusteringMaxDist, + const int lutId, + const int ffId, + const int num_nodes, + int* flat_node2precluster_map, + int* flat_node2prclstrCount, + const int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + const int instId = sorted_node_idx[i]; + //Only consider LUTs and skip MLABs + if (node2fence_region_map[instId] == lutId && is_mlab_node[instId] == 0) + { + std::vector > ffs; + int nPIdx = instId*4; + for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + + int pinIdxBeg = flat_net2pin_start_map[outNetId]; + int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; + + T instLocX = pos_x[instId] + pin_offset_x[outPinId]; + T instLocY = pos_y[instId] + pin_offset_y[outPinId]; + + for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) + { + int pinIdx = flat_net2pin_map[pinId]; + int nodeIdx = pin2node_map[pinIdx]; + + T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; + T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; + + T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + DREAMPLACE_STD_NAMESPACE::abs(distY); + + if (pin_typeIds[pinIdx] == 1 && node2fence_region_map[nodeIdx] == ffId && + dist <= preClusteringMaxDist) + { + ffs.emplace_back(std::make_pair(nodeIdx, dist)); + } + } + } + if (ffs.empty()) + { + continue; + } + + //Get FF index with min value (without Sort FFs) + std::sort(ffs.begin(), ffs.end(), [&sorted_node_map](const auto &a, const auto &b){ return a.second == b.second ? sorted_node_map[a.first] < sorted_node_map[b.first] : a.second < b.second; }); + + nPIdx = instId*3; + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[0].first; + ++flat_node2prclstrCount[instId]; + int fcIdx = flop2ctrlSetId_map[ffs[0].first]*3 + 1; + int cksr = flop_ctrlSets[fcIdx]; + + if (ffs.size() > 1) + { + for (unsigned int fIdx = 1; fIdx < ffs.size(); ++fIdx) + { + int ctrlIdx = flop2ctrlSetId_map[ffs[fIdx].first]*3 + 1; + int fCksr = flop_ctrlSets[ctrlIdx]; + + if (fCksr == cksr) + { + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[fIdx].first; + ++flat_node2prclstrCount[instId]; + //std::cout << "BEFORE SORT: Inst Id: " << instId << " has precluster: " << flat_node2precluster_map[nPIdx] << ", " << flat_node2precluster_map[nPIdx + 1] << ", " << flat_node2precluster_map[nPIdx + 2] << std::endl; + break; + } + } + } + + std::sort(flat_node2precluster_map+nPIdx, flat_node2precluster_map+nPIdx + flat_node2prclstrCount[instId], [&sorted_node_map](const auto &a, const auto &b){return sorted_node_map[a] < sorted_node_map[b];}); + + for (int prcl = 0; prcl < flat_node2prclstrCount[instId]; ++prcl) + { + int fIdx = flat_node2precluster_map[nPIdx + prcl]; + int fID = fIdx*3; + if (fIdx != instId) + { + for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) + { + flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; + } + flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[instId]; + } + } + } + } + return 0; +} + +// legalize carry chains +template +int legalizeCarryChainsLauncher( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const T* wlPrecond, + const int* site2addr_map, + const int* flat_cc2node_start_map, + const int* flat_cc2node_map, + const int* spiral_accessor, + const int* site_types, + const int spiralBegin, + const int spiralEnd, + const int num_carry_chains, + const int num_sites_x, + const int num_sites_y, + const int sliceId, + const int SIG_IDX, + const int SLICE_CAPACITY, + const int num_carryInstsInSlice, + const int slice_minX, + const int slice_maxX, + const int slice_minY, + const int slice_maxY, + T* carry_chain_displacements, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes, + const int num_threads) +{ + int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); + + std::vector > carry_chain(num_carry_chains); + std::vector carry_chain_size(num_carry_chains, 0); + std::vector centroidX(num_carry_chains, 0.0), centroidY(num_carry_chains, 0.0); + + int HALF_SLICE_CAPACITY = int(SLICE_CAPACITY/2); + + uint32_t legalcc = 0; + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_carry_chains / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_carry_chains; ++i) + { + T cenX(0), cenY(0), totalWt(0); + + for (int ccIdx = flat_cc2node_start_map[i]; ccIdx < flat_cc2node_start_map[i+1]; ++ccIdx) + { + int instId = flat_cc2node_map[ccIdx]; + carry_chain[i].emplace_back(instId); + + cenX += pos_x[instId] * wlPrecond[instId]; + cenY += pos_y[instId] * wlPrecond[instId]; + totalWt += wlPrecond[instId]; + } + + carry_chain_size[i] = carry_chain[i].size(); + + if (totalWt > 0) + { + cenX /= totalWt; + cenY /= totalWt; + } + + centroidX[i] = cenX; + centroidY[i] = cenY; + } + + //Sort based on carry chain size + std::vector sorted_carry_chains(num_carry_chains); + std::iota(sorted_carry_chains.begin(),sorted_carry_chains.end(),0); //Initializing + std::sort(sorted_carry_chains.begin(),sorted_carry_chains.end(), [&](int i,int j){return carry_chain_size[i]>carry_chain_size[j];} ); + + for (int i = 0; i < num_carry_chains; ++i) + { + const int idx = sorted_carry_chains[i]; + int elCount = carry_chain_size[idx]; + dreamplaceAssertMsg(elCount, "Carry chain of size 0 encountered - CHECK"); + + int sliceSpread = std::ceil(elCount/num_carryInstsInSlice) + 1; + + int cenX = centroidX[idx]; + int cenY = centroidY[idx]; + + int beg(spiralBegin), end(spiralEnd); + T bestX = INVALID; + T bestY = INVALID; + T bestScore = 10000000; + + for (int sId = beg; sId < end; ++sId) + { + int xVal = cenX + spiral_accessor[2*sId]; + int yVal = cenY + spiral_accessor[2*sId+1]; + int siteId = xVal*num_sites_y + yVal; + int sIdx = site2addr_map[siteId]; + + //Check within bounds + if (xVal < slice_minX || xVal > slice_maxX || yVal < slice_minY || yVal > slice_maxY) + { + continue; + } + + int startY = yVal + sliceSpread-1; + + if (site_types[siteId] == sliceId && startY <= slice_maxY) + { + char space_available = 1; + for (int yId = yVal; yId <= startY; ++yId) + { + int siteMap = xVal*num_sites_y + yId; + int sMap = site2addr_map[siteMap]; + + if (site_types[siteMap] != sliceId || site_det_sig_idx[sMap] > 0) + { + space_available = 0; + break; + } + } + + //Legalize carry chain if space is available + if (space_available == 1) + { + if (bestScore == 10000000) + { + int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId]) + + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId+1]); + r += 2; + int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); + end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; + } + + T dist_score = DREAMPLACE_STD_NAMESPACE::abs(cenX - site_xy[siteId*2]) + + DREAMPLACE_STD_NAMESPACE::abs(cenY - site_xy[siteId*2+1]); + if (dist_score < bestScore) + { + bestX = xVal; + bestY = yVal; + bestScore = dist_score; + } + } + } + } + + if (bestX != INVALID && bestY != INVALID) + { + carry_chain_displacements[idx] = 0; + int startY = bestY + sliceSpread -1; + int elements_left = elCount; + int instOffset = 0; + + for (int yId = startY; yId >= bestY; --yId) + { + int bSiteId = bestX * num_sites_y + yId; + int bSIdx = site2addr_map[bSiteId]; + int sdtopId = bSIdx*SIG_IDX; + int sdlutId = bSIdx*SLICE_CAPACITY; + + int insts_added = DREAMPLACE_STD_NAMESPACE::min(elements_left, SLICE_CAPACITY); + + elements_left -= insts_added; + + if (site_det_sig_idx[bSIdx] == 0) + { + sites_with_special_nodes[bSIdx] = 1; + site_det_score[bSIdx] = 100.0; + site_det_siteId[bSIdx] = bSiteId; + for(int sg = 0; sg < insts_added; ++sg) + { + int instAdd = carry_chain[idx][instOffset]; + site_det_sig[sdtopId + sg] = instAdd; + site_det_impl_lut[sdlutId + sg] = instAdd; + + inst_curr_detSite[instAdd] = bSiteId; + inst_curr_bestSite[instAdd] = bSiteId; + inst_curr_bestScoreImprov[instAdd] = 10000.0; + + inst_next_detSite[instAdd] = bSiteId; + inst_next_bestSite[instAdd] = bSiteId; + inst_next_bestScoreImprov[instAdd] = 10000.0; + + carry_chain_displacements[idx] += DREAMPLACE_STD_NAMESPACE::abs(pos_x[instAdd] - site_xy[bSiteId*2]) + + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instAdd] - site_xy[bSiteId*2+1]); + + ++instOffset; + } + site_det_sig_idx[bSIdx] += insts_added; + } + //DBG + else { + std::cout << "ERROR: More than one carry chain node legalized at site: (" << bestX << ", " << yId << ")" << std::endl; + } + //DBG + } + carry_chain_displacements[idx] /= elCount; + ++legalcc; + } + } + + //DBG + if (legalcc != num_carry_chains) + { + std::cout << "ERROR: Only " << legalcc << " carry chains legalized out of " << num_carry_chains << " - CHECK!" << std::endl; + } + //DBG + + return 0; +} + +// legalize Slice Macros +template +int legalizeMacrosLauncher( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const int* macro2node_map, + const int* macro_site_height, + const int* macro_element_count, + const int* site2addr_map, + const int* spiral_accessor, + const int* site_types, + const int spiralBegin, + const int spiralEnd, + const int num_macros, + const int num_sites_x, + const int num_sites_y, + const int slice_minX, + const int slice_maxX, + const int slice_minY, + const int slice_maxY, + const int sliceId, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* dist_moved, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes) +{ + int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); + //Use macro_site_x/y locations as guidance to legalize macros + std::vector sorted_macroIds(num_macros); + std::iota(sorted_macroIds.begin(),sorted_macroIds.end(),0); //Initializing + + //Sort macros based on Slices occupied + std::sort(sorted_macroIds.begin(),sorted_macroIds.end(), [&](int i,int j){return macro_element_count[i]>macro_element_count[j];} ); + + for (int i = 0; i < num_macros; ++i) + { + const int macroId = sorted_macroIds[i]; + const int instId = macro2node_map[macroId]; + int sliceSpread = macro_site_height[macroId]; + dreamplaceAssertMsg(sliceSpread, "Macro of size 0 encountered - CHECK"); + int macroElCount = macro_element_count[macroId]; + + T initX = pos_x[instId]; + T initY = pos_y[instId]; + + int beg(spiralBegin), end(spiralEnd); + T bestX = INVALID; + T bestY = INVALID; + T bestScore = 10000000; + + for (int sId = beg; sId < end; ++sId) + { + int xVal = initX + spiral_accessor[2*sId]; + int yVal = initY + spiral_accessor[2*sId+1]; + int siteId = xVal * num_sites_y + yVal; + int sIdx = site2addr_map[siteId]; + + //Check within bounds + if (xVal < slice_minX || xVal > slice_maxX || yVal < slice_minY || yVal > slice_maxY) + { + continue; + } + + int startY = yVal + sliceSpread -1; + if (site_types[siteId] == sliceId && startY <= slice_maxY) + { + char space_available = 1; + for (int yId = yVal; yId <= startY; ++yId) + { + int siteMap = xVal*num_sites_y + yId; + int sMap = site2addr_map[siteMap]; + if (site_types[siteMap] != sliceId || site_det_sig_idx[sMap] > 0) + { + space_available = 0; + } + } + + if (space_available == 1) + { + if (bestScore == 10000000) + { + int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId]) + + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId+1]); + r += 2; + int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); + end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; + } + + T dist_score = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); + if (dist_score < bestScore) + { + bestX = xVal; + bestY = yVal; + bestScore = dist_score; + } + } + } + } + + if (bestX != INVALID && bestY != INVALID) + { + dist_moved[macroId] = bestScore; + int elements_left = macroElCount; + int bSiteId = bestX * num_sites_y + bestY; + + inst_curr_detSite[instId] = bSiteId; + inst_curr_bestSite[instId] = bSiteId; + inst_curr_bestScoreImprov[instId] = 10000.0; + + inst_next_detSite[instId] = bSiteId; + inst_next_bestSite[instId] = bSiteId; + inst_next_bestScoreImprov[instId] = 10000.0; + + int startY = bestY + sliceSpread -1; + + for (int yId = startY; yId >= bestY; --yId) + { + bSiteId = bestX * num_sites_y + yId; + int bSIdx = site2addr_map[bSiteId]; + int sdtopId = bSIdx*SIG_IDX; + int sdlutId = bSIdx*SLICE_CAPACITY; + + int insts_added = DREAMPLACE_STD_NAMESPACE::min(elements_left, SLICE_CAPACITY); + + elements_left -= insts_added; + if (site_det_sig_idx[bSIdx] == 0) + { + sites_with_special_nodes[bSIdx] = 1; + site_det_score[bSIdx] = 1000.0; + site_det_siteId[bSIdx] = bSiteId; + for(int sg = 0; sg < insts_added; ++sg) + { + //Include same nodeId across the macro span + site_det_sig[sdtopId + sg] = instId; + site_det_impl_lut[sdlutId + sg] = instId; + } + site_det_sig_idx[bSIdx] += insts_added; + } + //DBG + else { + std::cout << "ERROR: Site not empty - Macro not legalized at (" << bestX << ", " << yId << ")" << std::endl; + } + //DBG + } + } + } + + return 0; +} + +// legalize mlabs +template +int legalizeMlabsLauncher( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const T* mlab_locX, + const T* mlab_locY, + const int* mlab_indices, + const int* site2addr_map, + const int num_mlab_nodes, + const int num_sites_y, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* dist_moved, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes, + const int num_threads) +{ + //Assign mlabs to site locations provided + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_mlab_nodes/ num_threads / SLICE_CAPACITY), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_mlab_nodes; ++i) + { + const int instId = mlab_indices[i]; + + T xVal = mlab_locX[i]; + T yVal = mlab_locY[i]; + + int siteId = xVal * num_sites_y + yVal; + int sIdx = site2addr_map[siteId]; + + dist_moved[i] = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); + + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + + if (site_det_sig_idx[sIdx] == 0) + { + sites_with_special_nodes[sIdx] = 1; + site_det_score[sIdx] = 1000.0; + site_det_siteId[sIdx] = siteId; + + site_det_sig_idx[sIdx] = 2*SLICE_CAPACITY; + site_det_sig[sdtopId] = instId; + site_det_impl_lut[sdlutId] = instId; + + inst_curr_detSite[instId] = siteId; + inst_curr_bestSite[instId] = siteId; + inst_curr_bestScoreImprov[instId] = 10000.0; + + inst_next_detSite[instId] = siteId; + inst_next_bestSite[instId] = siteId; + inst_next_bestScoreImprov[instId] = 10000.0; + } + //DBG + else { + std::cout << "ERROR: Slice site not empty - MLAB " << instId << " not legalized at (" << xVal << ", " << yVal << ")" << std::endl; + } + //DBG + } + + return 0; +} + +// initSiteNeighbours +template +int initSiteNeighbours( + const T* pos_x, + const T* pos_y, + const T* wlPrecond, + const T* site_xy, + const T* site_det_score, + const int* sorted_node_idx, + const int* node2fence_region_map, + const int* site_types, + const int* spiral_accessor, + const int* site2addr_map, + const int* addr2site_map, + const int* is_mlab_node, + const int* is_mlab_site, + const int* sites_with_special_nodes, + const int* flat_node2precluster_map, + const int* flat_node2prclstrCount, + const T nbrDistEnd, + const T nbrDistBeg, + const T nbrDistIncr, + const int lutId, + const int ffId, + const int sliceId, + const int num_nodes, + const int num_sites_x, + const int num_sites_y, + const int num_clb_sites, + const int spiralBegin, + const int spiralEnd, + const int maxList, + const int numGroups, + const int SCL_IDX, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* site_curr_scl_score, + int* site_nbrList, + int* site_nbrRanges, + int* site_nbrRanges_idx, + int* site_nbr, + int* site_nbr_idx, + int* site_nbrGroup_idx, + int* site_det_siteId, + int* site_det_sig, + int* site_det_sig_idx, + int* site_det_impl_lut, + int* site_curr_scl_siteId, + int* site_curr_scl_validIdx, + int* site_curr_scl_idx, + int* site_curr_scl_sig, + int* site_curr_scl_sig_idx, + int* site_curr_scl_impl_lut, + const int num_threads) +{ + std::vector > > sites_nbrListMap(num_clb_sites); + std::vector site_nbrList_idx(num_clb_sites, 0); + + //Update sites_nbrListMap_instId and sites_nbrListMap_dist + for (int i = 0; i < num_nodes; ++i) + { + const int instId = sorted_node_idx[i]; + int prIdx = instId*3; + int preclusterNodeId = flat_node2precluster_map[prIdx]; + + //Only consider LUTs & FFs AND first precluster is the same as InstID + if ((node2fence_region_map[instId] != lutId && node2fence_region_map[instId] != ffId) || + preclusterNodeId != instId || is_mlab_node[instId] == 1) + { + continue; + } + + //Centroid Calculation + //First element in inst precluster is itself + T cenX = pos_x[preclusterNodeId] * wlPrecond[preclusterNodeId]; + T cenY = pos_y[preclusterNodeId] * wlPrecond[preclusterNodeId]; + T totalWt = wlPrecond[preclusterNodeId]; + + if (flat_node2prclstrCount[instId] > 1) + { + for (int pcl = 1; pcl < flat_node2prclstrCount[instId]; ++pcl) + { + int fdx = flat_node2precluster_map[prIdx + pcl]; + cenX += pos_x[fdx] * wlPrecond[fdx]; + cenY += pos_y[fdx] * wlPrecond[fdx]; + totalWt += wlPrecond[fdx]; + } + } + + if (totalWt > 0) + { + cenX /= totalWt; + cenY /= totalWt; + } + + //Employ spiral accessor to update neighbour list + for (int sIdx = spiralBegin; sIdx < spiralEnd; ++sIdx) + { + int saIdx = sIdx*2; //For x,y + int xVal = cenX + spiral_accessor[saIdx]; + int yVal = cenY + spiral_accessor[saIdx + 1]; + + //Check within bounds + if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) + { + continue; + } + + int siteMapIdx = xVal * num_sites_y + yVal; + int siteId = site2addr_map[siteMapIdx]; + int stMpId = siteMapIdx*2; + + //Check site type and Inst type (CLB) matches + if ((node2fence_region_map[instId] == lutId || node2fence_region_map[instId] == ffId) && + site_types[siteMapIdx] == sliceId && is_mlab_site[siteId] == 0) + { + T dist = DREAMPLACE_STD_NAMESPACE::abs(cenX - site_xy[stMpId]) + + DREAMPLACE_STD_NAMESPACE::abs(cenY - site_xy[stMpId+1]); + if (dist < nbrDistEnd) + { + sites_nbrListMap[siteId].emplace_back(std::make_pair(instId, dist)); + } + } + } + } + + //Update site information based on nbrListMap update + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + // Sort neighbors by their distances + auto &list = sites_nbrListMap[sIdx]; + std::sort(list.begin(), list.end(), [&](const std::pair &l, const std::pair &r){ return l.second < r.second; }); + + int sRIdx = sIdx * (numGroups + 1); + int sNbrIdx = sIdx*maxList; + if (!list.empty()) + { + site_nbrRanges[sRIdx] = 0; + + int grpIdx = 0; + + T maxD = nbrDistBeg; + + for (unsigned il = 0; il < list.size(); ++il) + { + site_nbrList[sNbrIdx + site_nbrList_idx[sIdx]] = list[il].first; + ++site_nbrList_idx[sIdx]; + + while (list[il].second >= maxD) + { + site_nbrRanges[++grpIdx + sRIdx] = il; + maxD += nbrDistIncr; + } + } + while(++grpIdx <= numGroups) + { + site_nbrRanges[sRIdx + grpIdx] = site_nbrList_idx[sIdx]; + } + site_nbrRanges_idx[sIdx] = grpIdx; + } + } + + //runDLInit + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + int siteId = addr2site_map[sIdx]; + int sRIdx = sIdx * (numGroups + 1); + int sNbrIdx = sIdx*maxList; + + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + ////Assign site_nbr + if (numNbrGroups > 0) + { + for (int nIdx = site_nbrRanges[sRIdx]; nIdx < site_nbrRanges[sRIdx+1]; ++nIdx) + { + site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + nIdx]; + ++site_nbr_idx[sIdx]; + } + site_nbrGroup_idx[sIdx] = 1; + } + + int sSCL = sIdx*SCL_IDX; + + site_det_siteId[sIdx] = siteId; + site_curr_scl_siteId[sSCL] = siteId; + site_curr_scl_validIdx[sSCL] = 1; + ++site_curr_scl_idx[sIdx]; + + if (sites_with_special_nodes[sIdx] == 1) + { + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + int sclSigId = sSCL*SIG_IDX; + int scllutIdx = sSCL*SLICE_CAPACITY; + //Assign site_det to site_curr_scl + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + } + } + } + return 0; +} + +//run DL Iteration +template +int runDLIteration( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_bbox, + const T* site_xy, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* node2fence_region_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* flop2ctrlSetId_map, + const int* lut_type, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* node2pincount, + const int* net2pincount, + const int* pin2net_map, + const int* pin_typeIds, + const int* flat_net2pin_start_map, + const int* pin2node_map, + const int* sorted_node_map, + const int* sorted_net_map, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* is_mlab_node, + const int* is_mlab_site, + const int* site_nbrList, + const int* site_nbrRanges, + const int* site_nbrRanges_idx, + const T* net_weights, + const int* addr2site_map, + const int* special_nodes, + const int num_clb_sites, + const int minStableIter, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int minNeighbors, + const int numGroups, + const int netShareScoreMaxNetDegree, + const int wlScoreMaxNetDegree, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const T xWirelenWt, + const T yWirelenWt, + const T wirelenImprovWt, + const T extNetCountWt, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + const int num_threads, + int* site_nbr_idx, + int* site_nbr, + int* site_nbrGroup_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_validIdx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + T* site_det_score, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + T* inst_next_bestScoreImprov, + int* inst_next_bestSite + ) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); + +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + if (is_mlab_site[sIdx]) continue; + + int siteId = addr2site_map[sIdx]; + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + int sdckId = sIdx*CKSR_IN_CLB; + int sdceId = sIdx*CE_IN_CLB; + + int sclSigId = sSCL*SIG_IDX; + int scllutIdx = sSCL*SLICE_CAPACITY; + int sclckIdx = sSCL*CKSR_IN_CLB; + int sclceIdx = sSCL*CE_IN_CLB; + + //(a)Try to commit Top candidates + int commitTopCandidate(INVALID); + + int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); + int topIdx(tsPQ*SIG_IDX); + int lutIdx = tsPQ*SLICE_CAPACITY; + int ckIdx = tsPQ*CKSR_IN_CLB; + int ceIdx = tsPQ*CE_IN_CLB; + + ////DBG + //if (site_curr_pq_idx[sIdx] > 0) + //{ + // std::cout << "For site: " << siteId << "(" << sIdx << "), there are " << site_curr_pq_idx[sIdx] << " elements in site_curr_pq " << std::endl; + // int tsPQ = sPQ + site_curr_pq_top_idx[sIdx]; + + // std::cout << "site curr pq top element has score: " << site_curr_pq_score[tsPQ] << std::endl; + // std::cout << "site curr pq top element has site: " << site_curr_pq_siteId[tsPQ] << std::endl; + // std::cout << "site curr pq top element has sig elements: " << site_curr_pq_sig_idx[tsPQ] << std::endl; + // std::cout << "site curr pq top element sig: "; + // for (int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) + // { + // std::cout << site_curr_pq_sig[topIdx + sg] << " "; + // } + // std::cout << std::endl; + //} + ////DBG + + if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || + !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, + site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], + site_curr_pq_sig, inst_curr_detSite)) + { + commitTopCandidate = 0; + } else if (site_curr_pq_idx[sIdx] > 0) + { + for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) + { + int pqInst = site_curr_pq_sig[topIdx + pIdx]; + if (inst_curr_detSite[pqInst] != siteId && inst_curr_bestSite[pqInst] != siteId) + { + commitTopCandidate = 0; + break; + } + } + } + + if (commitTopCandidate == INVALID) + { + ////// + site_det_score[sIdx] = site_curr_pq_score[tsPQ]; + site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; + site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; + + for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) + { + site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; + site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; + } + ////// + + for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) + { + int sigInst = site_det_sig[sdtopId + iSig]; + inst_next_detSite[sigInst] = siteId; + } + + //Remove Incompatible Neighbors + remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, + pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, + flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, + site_det_sig, site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, + sIdx, sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, site_nbr_idx, site_nbr); + + //Clear pq and make scl only contain the committed candidate + //int sclCount(0); + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + //if (site_next_pq_validIdx[nPQId] != INVALID) + //{ + //Clear contents thoroughly + clear_cand_contents( + nPQId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[nPQId] = INVALID; + site_next_pq_siteId[nPQId] = INVALID; + site_next_pq_score[nPQId] = 0.0; + site_next_pq_sig_idx[nPQId] = 0; + + //++sclCount; + //if (sclCount == site_next_pq_idx[sIdx]) + //{ + // break; + //} + //} + } + site_next_pq_idx[sIdx] = 0; + site_next_pq_top_idx[sIdx] = INVALID; + site_next_stable[sIdx] = 0; + + int sclCount = 0; + for (int vId = 0; vId < SCL_IDX; ++vId) + { + int cSclId = sSCL + vId; + if (site_curr_scl_validIdx[cSclId] != INVALID) + { + //Clear contents thoroughly + clear_cand_contents( + cSclId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + + site_curr_scl_validIdx[cSclId] = INVALID; + site_curr_scl_sig_idx[cSclId] = 0; + site_curr_scl_siteId[cSclId] = INVALID; + site_curr_scl_score[cSclId] = 0.0; + ++sclCount; + if (sclCount == site_curr_scl_idx[sIdx]) + { + break; + } + } + } + site_curr_scl_idx[sIdx] = 0; + + //Assign site_det to site_curr_scl + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + ///// + commitTopCandidate = 1; + } + if (commitTopCandidate == 0) + { + //Remove invalid candidates from site PQ + if (site_next_pq_idx[sIdx] > 0) + { + //int snCnt = 0; + //int maxEntries = site_next_pq_idx[sIdx]; + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + int topIdx = ssPQ*SIG_IDX; + + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, + site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], + site_next_pq_sig, inst_curr_detSite)) + { + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[ssPQ] = INVALID; + site_next_pq_sig_idx[ssPQ] = 0; + site_next_pq_siteId[ssPQ] = INVALID; + site_next_pq_score[ssPQ] = 0.0; + --site_next_pq_idx[sIdx]; + } + //++snCnt; + //if (snCnt == maxEntries) + //{ + // break; + //} + } + } + + //Recompute top idx + site_next_pq_top_idx[sIdx] = INVALID; + if (site_next_pq_idx[sIdx] > 0) + { + //snCnt = 0; + //maxEntries = site_next_pq_idx[sIdx]; + T maxScore(-1000.0); + int maxScoreId(INVALID); + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (site_next_pq_score[ssPQ] > maxScore) + { + maxScore = site_next_pq_score[ssPQ]; + maxScoreId = nIdx; + } + //++snCnt; + //if (snCnt == maxEntries) + //{ + // break; + //} + } + } + site_next_pq_top_idx[sIdx] = maxScoreId; + } + } + + //Remove invalid candidates from seed candidate list (scl) + if (site_curr_scl_idx[sIdx] > 0) + { + int sclCount(0), maxEntries(site_curr_scl_idx[sIdx]); + for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) + { + int ssPQ = sSCL + nIdx; + int topIdx = ssPQ*SIG_IDX; + + if (site_curr_scl_validIdx[ssPQ] != INVALID) + { + if(!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, + site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], + site_curr_scl_sig, inst_curr_detSite)) + { + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + + site_curr_scl_validIdx[ssPQ] = INVALID; + site_curr_scl_sig_idx[ssPQ] = 0; + site_curr_scl_siteId[ssPQ] = INVALID; + site_curr_scl_score[ssPQ] = 0.0; + --site_curr_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + + //If site.scl becomes empty, add site_det into it as the seed + if (site_curr_scl_idx[sIdx] == 0) + { + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + } + } + + // (c) removeCommittedNeighbors(site) + for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) + { + int siteInst = site_nbr[sNbrIdx + sNIdx]; + if (inst_curr_detSite[siteInst] != INVALID) + { + site_nbr[sNbrIdx + sNIdx] = INVALID; + } + } + remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); + + // (d) addNeighbors(site) - Original implementation without staggering + ////DBG + //std::cout << "For site: " << siteId << "(" << sIdx << "), there are " << site_nbr_idx[sIdx] << " site nbrs and nbrGroup_idx: " + // << site_nbrGroup_idx[sIdx] << std::endl; + ////DBG + if (site_nbr_idx[sIdx] < minNeighbors && site_nbrGroup_idx[sIdx] < numNbrGroups) + { + int nbrRIdx = sIdx*(numGroups+1) + site_nbrGroup_idx[sIdx]; + int beg = site_nbrRanges[nbrRIdx]; + int end = site_nbrRanges[nbrRIdx+1]; + + for (int aNIdx = beg; aNIdx < end; ++aNIdx) + { + int instId = site_nbrList[sNbrIdx + aNIdx]; + ////DBG + //std::cout << "For site: " << siteId << "(" << sIdx << "), try to add inst " << instId << " of type " + // << node2fence_region_map[instId] << "to site nbrs" << std::endl; + ////DBG + + if (inst_curr_detSite[instId] == INVALID && + is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, + pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, + flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, + special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, instId, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, + lutId, ffId, half_ctrl_mode)) + { + ////DBG + //std::cout << "For site: " << siteId << "(" << sIdx << "), ADD inst " << instId << " of type " + // << node2fence_region_map[instId] << "to site nbrs" << std::endl; + ////DBG + site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = instId; + ++site_nbr_idx[sIdx]; + } + } + ++site_nbrGroup_idx[sIdx]; + } + ////DBG + //std::cout << "After (d) For site: " << siteId << "(" << sIdx << "), there are " << site_nbr_idx[sIdx] << " site nbrs and nbrGroup_idx: " + // << site_nbrGroup_idx[sIdx] << std::endl; + ////DBG + + // (e) createNewCandidates(site) - Original implementation without restricted candidate search space + //Generate new candidates by merging site_nbr to site_curr_scl + const int limit_x = site_curr_scl_idx[sIdx]; + const int limit_y = site_nbr_idx[sIdx]; + const int limit_cands = limit_x*limit_y; + int limit_count(0); + int sclCount(0); + if (limit_cands > 0) + { + for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) + { + int siteCurrIdx = sSCL + scsIdx; + if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) + { + if (limit_count >= limit_cands) break; + // + for (int snIdx = 0; snIdx < site_nbr_idx[sIdx]; ++snIdx) + { + ++limit_count; + + int instId = site_nbr[sNbrIdx + snIdx]; + int instPcl = instId*3; + + ///// + //New candidate = site_curr_scl_validIdx[sSCL + scsIdx] + int sCKRId = siteCurrIdx*CKSR_IN_CLB; + int sCEId = siteCurrIdx*CE_IN_CLB; + int sFFId = siteCurrIdx*SLICE_CAPACITY; + int sGId = siteCurrIdx*SIG_IDX; + + T nwCand_score = site_curr_scl_score[siteCurrIdx]; + int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; + int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; + + //array instantiation + int nwCand_sig[SIG_MAX_CAP]; + int nwCand_lut[SLICE_MAX_CAP]; + int nwCand_ff[SLICE_MAX_CAP]; + int nwCand_ce[CE_MAX_CAP]; + int nwCand_cksr[CKSR_MAX_CAP]; + + for (int sg = 0; sg < site_curr_scl_sig_idx[siteCurrIdx]; ++sg) + { + nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; + nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; + } + ///// + + if (add_inst_to_sig(flat_node2prclstrCount[instId], flat_node2precluster_map, + instPcl, nwCand_sig, nwCand_sigIdx, SLICE_CAPACITY) && + !check_sig_in_site_next_pq_sig(nwCand_sig, nwCand_sigIdx, sPQ, PQ_IDX, + site_next_pq_validIdx, site_next_pq_sig, site_next_pq_sig_idx, SIG_IDX) && + add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, + half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + BLE_CAPACITY, NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) + { + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, + nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); + + int nxtId(INVALID); + //find least score and replace if current score is greater + if (site_next_pq_idx[sIdx] < PQ_IDX) + { + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (site_next_pq_validIdx[sPQ+vId] == INVALID) + { + nxtId = vId; + ++site_next_pq_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (ckscore > site_next_pq_score[sPQ + vId]) + { + ckscore = site_next_pq_score[sPQ + vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + int nTId = sPQ + nxtId; + int nCKRId = nTId*CKSR_IN_CLB; + int nCEId = nTId*CE_IN_CLB; + int nFFId = nTId*SLICE_CAPACITY; + int nSGId = nTId*SIG_IDX; + + ///// + site_next_pq_validIdx[nTId] = 1; + site_next_pq_score[nTId] = nwCand_score; + site_next_pq_siteId[nTId] = nwCand_siteId; + site_next_pq_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + + if (site_next_pq_idx[sIdx] == 1 || nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) + { + site_next_pq_top_idx[sIdx] = nxtId; + } + + nxtId = INVALID; + + if (site_next_scl_idx[sIdx] < SCL_IDX) + { + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (site_next_scl_validIdx[sSCL+vId] == INVALID) + { + nxtId = vId; + ++site_next_scl_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (ckscore > site_next_scl_score[sSCL+vId]) + { + ckscore = site_next_scl_score[sSCL+vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + ///// + nTId = sSCL + nxtId; + nCKRId = nTId*CKSR_IN_CLB; + nCEId = nTId*CE_IN_CLB; + nFFId = nTId*SLICE_CAPACITY; + nSGId = nTId*SIG_IDX; + + site_next_scl_validIdx[nTId] = 1; + site_next_scl_score[nTId] = nwCand_score; + site_next_scl_siteId[nTId] = nwCand_siteId; + site_next_scl_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + } + } + } + } + ++sclCount; + if (sclCount == site_curr_scl_idx[sIdx]) + { + break; + } + } + } + } + + //Remove all candidates in scl that is worse than the worst candidate in PQ + if (site_next_pq_idx[sIdx] > 0) + { + //Find worst candidate in PQ + T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); + + sclCount = 0; + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + if (site_next_pq_validIdx[nPQId] != INVALID) + { + if (ckscore > site_next_pq_score[nPQId]) + { + ckscore = site_next_pq_score[nPQId]; + } + ++sclCount; + if (sclCount == site_next_pq_idx[sIdx]) + { + break; + } + } + } + + //Invalidate worst ones in scl + sclCount = 0; + int maxEntries(site_next_scl_idx[sIdx]); + for (int ckId = 0; ckId < SCL_IDX; ++ckId) + { + int vId = sSCL + ckId; + if (site_next_scl_validIdx[vId] != INVALID) + { + if (ckscore > site_next_scl_score[vId]) + { + //Clear contents thoroughly + clear_cand_contents( + vId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce); + + site_next_scl_validIdx[vId] = INVALID; + site_next_scl_sig_idx[vId] = 0; + site_next_scl_siteId[vId] = INVALID; + site_next_scl_score[vId] = 0.0; + --site_next_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + + if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && + compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, + site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, + site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, + site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, + site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, + site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, + site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, + sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) + { + //Check both sig + site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; + } else + { + site_next_stable[sIdx] = 0; + } + + // (f) broadcastTopCandidate(site) - Original implementation without updated sequential portion + if (site_next_pq_idx[sIdx] > 0) + { + int topIdx = sPQ + site_next_pq_top_idx[sIdx]; + int topSigId = topIdx*SIG_IDX; + + T scoreImprov = site_next_pq_score[topIdx] - site_det_score[sIdx]; + for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[topIdx]; ++ssIdx) + { + int instId = site_next_pq_sig[topSigId + ssIdx]; + + if (inst_curr_detSite[instId] == INVALID && scoreImprov >= inst_next_bestScoreImprov[instId]) + { + mtx.lock(); + if (scoreImprov == inst_next_bestScoreImprov[instId]) + { + if (siteId < inst_next_bestSite[instId]) + { + inst_next_bestSite[instId] = siteId; + } + } + else if (scoreImprov > inst_next_bestScoreImprov[instId]) + { + inst_next_bestSite[instId] = siteId; + inst_next_bestScoreImprov[instId] = scoreImprov; + } + mtx.unlock(); + } + } + } + } + return 0; +} + +//run DL Sync +template +int runDLSynchronize( + const int* node2fence_region_map, + const int* addr2site_map, + const int num_clb_sites, + const int SLICE_CAPACITY, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + const int num_nodes, + const int num_threads, + int* site_nbrGroup_idx, + int* site_nbrRanges_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_pq_validIdx, + int* site_curr_stable, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + int* inst_curr_detSite, + int* inst_curr_bestSite, + T* inst_curr_bestScoreImprov, + int* inst_next_detSite, + int* inst_next_bestSite, + T* inst_next_bestScoreImprov, + int* activeStatus, + int* illegalStatus + ) +{ + //int numSites = num_sites_x * num_sites_y; + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); + + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + int sPQ = sIdx*SCL_IDX; + + site_curr_stable[sIdx] = site_next_stable[sIdx]; + + int curr_scl_size = site_curr_scl_idx[sIdx]; + site_curr_scl_idx[sIdx] = 0; + + int sclCount(0); + //Include valid entries of site_next_scl to site_curr_scl + if (site_next_scl_idx[sIdx] > 0) + { + for (int id = 0; id < SCL_IDX; ++id) + { + int vIdx = sPQ+id; + if (site_next_scl_validIdx[vIdx] != INVALID) + { + int currId(sPQ+site_curr_scl_idx[sIdx]); + + site_curr_scl_validIdx[currId] = 1; + site_curr_scl_siteId[currId] = site_next_scl_siteId[vIdx]; + site_curr_scl_score[currId] = site_next_scl_score[vIdx]; + site_curr_scl_sig_idx[currId] = site_next_scl_sig_idx[vIdx]; + + int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); + int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); + int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); + int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); + + for (int sg = 0; sg < site_next_scl_sig_idx[vIdx]; ++sg) + { + site_curr_scl_sig[currSGId + sg] = site_next_scl_sig[nxtSGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[currFFId + sg] = site_next_scl_impl_lut[nxtFFId + sg]; + site_curr_scl_impl_ff[currFFId + sg] = site_next_scl_impl_ff[nxtFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[currCKId + sg] = site_next_scl_impl_cksr[nxtCKId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[currCEId + sg] = site_next_scl_impl_ce[nxtCEId + sg]; + } + ++site_curr_scl_idx[sIdx]; + ++sclCount; + if (sclCount == site_next_scl_idx[sIdx]) + { + break; + } + } + } + } + + //Invalidate the rest in site_curr_scl + if (curr_scl_size > site_next_scl_idx[sIdx]) + { + for (int ckId = site_curr_scl_idx[sIdx]; ckId < SCL_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + if (site_curr_scl_validIdx[vIdx] != INVALID) + { + site_curr_scl_validIdx[vIdx] = INVALID; + site_curr_scl_sig_idx[vIdx] = 0; + site_curr_scl_siteId[vIdx] = INVALID; + site_curr_scl_score[vIdx] = 0.0; + ++sclCount; + if (sclCount == curr_scl_size) + { + break; + } + } + } + } + + int curr_pq_size = site_curr_pq_idx[sIdx]; + site_curr_pq_idx[sIdx] = 0; + site_curr_pq_top_idx[sIdx] = INVALID; + + sPQ = sIdx*PQ_IDX; + sclCount = 0; + //Include valid entries of site_next_pq to site_curr_pq + if (site_next_pq_idx[sIdx] > 0) + { + for (int id = 0; id < PQ_IDX; ++id) + { + int vIdx = sPQ+id; + if (site_next_pq_validIdx[vIdx] != INVALID) + { + int currId(sPQ+site_curr_pq_idx[sIdx]); + + site_curr_pq_validIdx[currId] = 1; + site_curr_pq_siteId[currId] = site_next_pq_siteId[vIdx]; + site_curr_pq_score[currId] = site_next_pq_score[vIdx]; + site_curr_pq_sig_idx[currId] = site_next_pq_sig_idx[vIdx]; + + int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); + int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); + int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); + int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); + + for (int sg = 0; sg < site_next_pq_sig_idx[vIdx]; ++sg) + { + site_curr_pq_sig[currSGId + sg] = site_next_pq_sig[nxtSGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_pq_impl_lut[currFFId + sg] = site_next_pq_impl_lut[nxtFFId + sg]; + site_curr_pq_impl_ff[currFFId + sg] = site_next_pq_impl_ff[nxtFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_pq_impl_cksr[currCKId + sg] = site_next_pq_impl_cksr[nxtCKId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_pq_impl_ce[currCEId + sg] = site_next_pq_impl_ce[nxtCEId + sg]; + } + if (id == site_next_pq_top_idx[sIdx]) + { + site_curr_pq_top_idx[sIdx] = site_curr_pq_idx[sIdx]; + } + ++site_curr_pq_idx[sIdx]; + ++sclCount; + if (sclCount == site_next_pq_idx[sIdx]) + { + break; + } + } + } + } + + //Invalidate the rest in site_curr_pq + if (curr_pq_size > site_next_pq_idx[sIdx]) + { + for (int ckId = site_curr_pq_idx[sIdx]; ckId < PQ_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + if (site_curr_pq_validIdx[vIdx] != INVALID) + { + site_curr_pq_validIdx[vIdx] = INVALID; + site_curr_pq_sig_idx[vIdx] = 0; + site_curr_pq_siteId[vIdx] = INVALID; + site_curr_pq_score[vIdx] = 0.0; + ++sclCount; + if (sclCount == curr_pq_size) + { + break; + } + } + } + } + + //clear site_next_scl + sPQ = sIdx*SCL_IDX; + //sclCount = 0; + for (int ckId = 0; ckId < SCL_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + //if (site_next_scl_validIdx[vIdx] != INVALID) + //{ + //Clear contents thoroughly + clear_cand_contents( + vIdx, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce); + + site_next_scl_validIdx[vIdx] = INVALID; + site_next_scl_sig_idx[vIdx] = 0; + site_next_scl_siteId[vIdx] = INVALID; + site_next_scl_score[vIdx] = 0.0; + //++sclCount; + //if (sclCount == site_next_scl_idx[sIdx]) + //{ + // break; + //} + //} + } + site_next_scl_idx[sIdx] = 0; + + activeStatus[addr2site_map[sIdx]] = (site_curr_pq_idx[sIdx] > 0 || site_curr_scl_idx[sIdx] > 0 || + site_nbrGroup_idx[sIdx] < numNbrGroups) ? 1 : 0; + } + + chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for(int nIdx = 0; nIdx < num_nodes; ++nIdx) + { + if ((node2fence_region_map[nIdx] == lutId || node2fence_region_map[nIdx] == ffId) && inst_curr_detSite[nIdx] == INVALID) //Only LUT/FF + { + //inst.curr = inst.next + inst_curr_detSite[nIdx] = inst_next_detSite[nIdx]; + inst_curr_bestSite[nIdx] = inst_next_bestSite[nIdx]; + inst_curr_bestScoreImprov[nIdx] = inst_next_bestScoreImprov[nIdx]; + + inst_next_bestSite[nIdx] = INVALID; + inst_next_bestScoreImprov[nIdx] = -10000.0; + + illegalStatus[nIdx] = (inst_curr_detSite[nIdx] == INVALID) ? 1 : 0; + } + } + return 0; +} + + +//run ripup and greedy legalization +template +int ripUp_Greedy_LG( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_weights, + const T* net_bbox, + const T* inst_areas, + const T* wlPrecond, + const T* site_xy, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* spiral_accessor, + const int* node2fence_region_map, + const int* lut_type, + const int* site_types, + const int* node2outpinIdx_map, + const int* node2pincount, + const int* net2pincount, + const int* pin2net_map, + const int* pin2node_map, + const int* pin_typeIds, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* flat_net2pin_start_map, + int* flat_node2prclstrCount, + int* flat_node2precluster_map, + const int* sorted_node_map, + const int* sorted_node_idx, + const int* sorted_net_map, + const int* addr2site_map, + const int* site2addr_map, + const int* sites_with_special_nodes, + const int* special_nodes, + const T nbrDistEnd, + const T xWirelenWt, + const T yWirelenWt, + const T extNetCountWt, + const T wirelenImprovWt, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int num_nodes, + const int num_sites_x, + const int num_sites_y, + const int num_clb_sites, + const int spiralBegin, + const int spiralEnd, + const int half_ctrl_mode, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int netShareScoreMaxNetDegree, + const int wlScoreMaxNetDegree, + const int ripupExpansion, + const int greedyExpansion, + const int SIG_IDX, + const int lutId, + const int ffId, + const int sliceId, + int* inst_curr_detSite, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* site_det_siteId, + T* site_det_score + ) +{ + ////DBG + //std::cout << "RipUP & Greedy LG for " << num_nodes << " nodes" << std::endl; + //char collect_info = 0; + ////DBG + + int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); + for (int i = 0; i < num_nodes; ++i) + { + ////DBG + //std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + ////DBG + + //////DBG + //std::string dbgMsg = ""; + //////DBG + + const int instId = sorted_node_idx[i]; //Remaining insts sorted based on decreasing area + if (inst_curr_detSite[instId] != INVALID) continue; + int instPcl = instId*3; + + //RipUpCandidates + std::vector > ripUpCandidates; + + int xLo = DREAMPLACE_STD_NAMESPACE::max(pos_x[instId] - nbrDistEnd, T(0)); + int yLo = DREAMPLACE_STD_NAMESPACE::max(pos_y[instId] - nbrDistEnd, T(0)); + int xHi = DREAMPLACE_STD_NAMESPACE::min(pos_x[instId] + nbrDistEnd, T(num_sites_x-1)); + int yHi = DREAMPLACE_STD_NAMESPACE::min(pos_y[instId] + nbrDistEnd, T(num_sites_y-1)); + + for (int x = xLo; x <= xHi; ++x) + { + for (int y = yLo; y <= yHi; ++y) + { + int siteId = x*num_sites_y + y; + int sIdx = site2addr_map[siteId]; + if ((node2fence_region_map[instId] == lutId || + node2fence_region_map[instId] == ffId) && + site_types[siteId] == sliceId && sites_with_special_nodes[sIdx] != 1) + { + int slocId = siteId*2; + T dist = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[slocId]) + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[slocId+1]); + if (dist < nbrDistEnd) + { + RipUpCand rpCand; + rpCand.reset(); + + rpCand.siteId = siteId; + rpCand.cand.score = site_det_score[sIdx]; + rpCand.cand.siteId = site_det_siteId[sIdx]; + + rpCand.cand.sigIdx = site_det_sig_idx[sIdx]; + + /// + int sdSGId = sIdx*SIG_IDX; + int sdLutId = sIdx*SLICE_CAPACITY; + int sdCKId = sIdx*CKSR_IN_CLB; + int sdCEId = sIdx*CE_IN_CLB; + + for (int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + rpCand.cand.sig[sg] = site_det_sig[sdSGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + rpCand.cand.impl_lut[sg] = site_det_impl_lut[sdLutId + sg]; + rpCand.cand.impl_ff[sg] = site_det_impl_ff[sdLutId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + rpCand.cand.impl_cksr[sg] = site_det_impl_cksr[sdCKId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + rpCand.cand.impl_ce[sg] = site_det_impl_ce[sdCEId + sg]; + } + /// + + rpCand.legal = add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, + half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + BLE_CAPACITY, NUM_BLE_PER_SLICE, rpCand.cand.impl_lut, rpCand.cand.impl_ff, + rpCand.cand.impl_cksr, rpCand.cand.impl_ce); + + if (rpCand.legal) + { + /// + bool addInstToSig = add_inst_to_sig(flat_node2prclstrCount[instId], + flat_node2precluster_map, instPcl, + rpCand.cand.sig, rpCand.cand.sigIdx, SLICE_CAPACITY); + //DBG + if (!addInstToSig) + { + std::cout << "ERROR: Unable to add inst: " << instId << " to sig" << std::endl; + } + //DBG + + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, + rpCand.cand.sig, rpCand.cand.siteId, rpCand.cand.sigIdx, + rpCand.cand.score); + + rpCand.score = rpCand.cand.score - site_det_score[sIdx]; + + } else + { + T area = inst_areas[site_det_sig[sdSGId]]; + for (int sInst = 1; sInst < site_det_sig_idx[sIdx]; ++sInst) + { + area += inst_areas[site_det_sig[sdSGId + sInst]]; + } + T wirelenImprov(0.0); + int pStart = flat_node2pin_start_map[instId]; + int pEnd = flat_node2pin_start_map[instId+1]; + for (int pId = pStart; pId < pEnd; ++pId) + { + int pinId = flat_node2pin_map[pId]; + int netId = pin2net_map[pinId]; + if (net2pincount[netId] <= wlScoreMaxNetDegree) + { + compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, netId, siteId, std::vector{pinId}, wirelenImprov); + } + } + rpCand.score = wirelenImprovWt * wirelenImprov - site_det_score[sIdx] - area; + } + ripUpCandidates.emplace_back(rpCand); + } + } + } + } + + //Sort ripup candidate indices based on legal and score + if (ripUpCandidates.size() > 1) + { + std::sort(ripUpCandidates.begin(), ripUpCandidates.end()); + } + + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Created " + std::to_string(ripUpCandidates.size()) + " RipUPCandidates for Inst: "; + // dbgMsg += std::to_string(instId) + " of type: " + std::to_string(node2fence_region_map[instId]) + "\n"; + // if (node2fence_region_map[instId] == ffId) + // { + // int clInstCKSR = flop2ctrlSetId_map[instId]*3 + 1; + // int clInstCE = flop2ctrlSetId_map[instId]*3 + 2; + // dbgMsg += "FF Node " + std::to_string(instId) + " has clk: " + std::to_string(flop_ctrlSets[clInstCKSR]); + // dbgMsg += " and ce: " + std::to_string(flop_ctrlSets[clInstCE]) + "\n"; + // } else + // { + // dbgMsg += "LUT Node " + std::to_string(instId) + " is of type: " + std::to_string(lut_type[instId]) + "\n"; + // } + //} + //////DBG + + int ripupLegalizeInst(INVALID); + int greedyLegalizeInst(INVALID); + + for (const auto &ripUpCd : ripUpCandidates) + { + int stId = ripUpCd.siteId; + int stAdId = site2addr_map[stId]; + + int sdSGId = stAdId*SIG_IDX; + int sdLutId = stAdId*SLICE_CAPACITY; + int sdCKId = stAdId*CKSR_IN_CLB; + int sdCEId = stAdId*CE_IN_CLB; + + if (ripUpCd.legal) + { + site_det_score[stAdId] = ripUpCd.cand.score; + site_det_siteId[stAdId] = ripUpCd.cand.siteId; + site_det_sig_idx[stAdId] = ripUpCd.cand.sigIdx; + + for (auto sg = 0; sg < ripUpCd.cand.sigIdx; ++sg) + { + site_det_sig[sdSGId + sg] = ripUpCd.cand.sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdLutId + sg] = ripUpCd.cand.impl_lut[sg]; + site_det_impl_ff[sdLutId + sg] = ripUpCd.cand.impl_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdCKId + sg] = ripUpCd.cand.impl_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdCEId + sg] = ripUpCd.cand.impl_ce[sg]; + } + /// + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + inst_curr_detSite[clInstId] = ripUpCd.siteId; + } + + ripupLegalizeInst = 1; + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Found legal RipUPCandidate for Inst: " + std::to_string(instId) + " at site: " + std::to_string(ripUpCd.siteId) +"\n"; + //} + //////DBG + } else + { + //////DBG + //if (collect_info) + //{ + // dbgMsg += "RipUP Site " + std::to_string(site_det_siteId[stAdId]) + " for Inst: " + std::to_string(instId) + "\n"; + //} + //////DBG + int ripupSiteLegalizeInst(INVALID); + + std::vector > dets; + dets.reserve(site_det_sig_idx[stAdId]); + + Candidate tCand; + tCand.reset(); + tCand.score = site_det_score[stAdId]; + tCand.siteId = site_det_siteId[stAdId]; + tCand.sigIdx = site_det_sig_idx[stAdId]; + + for(int sg = 0; sg < tCand.sigIdx; ++sg) + { + tCand.sig[sg] = site_det_sig[sdSGId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + tCand.impl_lut[sg] = site_det_impl_lut[sdLutId+ sg]; + tCand.impl_ff[sg] = site_det_impl_ff[sdLutId+ sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + tCand.impl_cksr[sg] = site_det_impl_cksr[sdCKId+ sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + tCand.impl_ce[sg] = site_det_impl_ce[sdCEId+ sg]; + } + dets.emplace_back(tCand); + tCand.reset(); + + //Rip Up site + for(int sg = 0; sg < site_det_sig_idx[stAdId]; ++sg) + { + inst_curr_detSite[site_det_sig[sdSGId + sg]] = INVALID; + } + + //Clear contents of site_det_sig + clear_cand_contents(stAdId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, + CE_IN_CLB, site_det_sig_idx, site_det_sig, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce); + + site_det_sig_idx[stAdId] = flat_node2prclstrCount[instId]; + tCand.sigIdx = flat_node2prclstrCount[instId]; + + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + site_det_sig[sdSGId + idx] = clInstId; + tCand.sig[idx] = clInstId; + } + /// + + if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, + half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + BLE_CAPACITY, NUM_BLE_PER_SLICE, tCand.impl_lut, tCand.impl_ff, + tCand.impl_cksr, tCand.impl_ce)) + { + /// + site_det_sig_idx[stAdId] = tCand.sigIdx; + + for (int sg = 0; sg < tCand.sigIdx; ++sg) + { + site_det_sig[sdSGId + sg] = tCand.sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdLutId+ sg] = tCand.impl_lut[sg]; + site_det_impl_ff[sdLutId+ sg] = tCand.impl_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdCKId+ sg] = tCand.impl_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdCEId+ sg] = tCand.impl_ce[sg]; + } + /// + } else + { + //Should not reach here + std::cout << "ERROR: Could not add " << instId << " (inst: " << instId << ") of type " << node2fence_region_map[instId] << " to site: " << stId << std::endl; + } + + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, + tCand.sig, stId, tCand.sigIdx, site_det_score[stAdId]); + + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInst = flat_node2precluster_map[instPcl + idx]; + inst_curr_detSite[clInst] = stId; + } + + int sig[SIG_MAX_CAP]; + int tmp_sigIdx = dets[0].sigIdx; + for (int sg = 0; sg < dets[0].sigIdx; ++sg) + { + sig[sg] = dets[0].sig[sg]; + } + + for (int rIdx = 0; rIdx < tmp_sigIdx; ++rIdx) + { + int ruInst = sig[rIdx]; + if (inst_curr_detSite[ruInst] != INVALID) + { + continue; + } + int beg = spiralBegin; + int r = DREAMPLACE_STD_NAMESPACE::ceil(nbrDistEnd + 1.0); + int end = r ? 2 * (r + 1) * r + 1 : 1; + + int ruInstPcl = flat_node2precluster_map[ruInst*3]; + T cenX(pos_x[ruInstPcl]), cenY(pos_y[ruInstPcl]); + + if (flat_node2prclstrCount[ruInst] > 1) + { + cenX *= wlPrecond[ruInstPcl]; + cenY *= wlPrecond[ruInstPcl]; + T totalWt = wlPrecond[ruInstPcl]; + + for (int idx = 1; idx < flat_node2prclstrCount[ruInst]; ++idx) + { + int clInst = flat_node2precluster_map[ruInst*3 + idx]; + cenX += pos_x[clInst] * wlPrecond[clInst]; + cenY += pos_y[clInst] * wlPrecond[clInst]; + totalWt += wlPrecond[clInst]; + } + if (totalWt > 0) + { + cenX /= totalWt; + cenY /= totalWt; + } + } + + //BestCandidate + Candidate bestCand; + bestCand.reset(); + T bestScoreImprov(-10000.0); + + for (int spId = beg; spId < end; ++spId) + { + int slocIdx = spId*2; + int xVal = cenX + spiral_accessor[slocIdx]; + int yVal = cenY + spiral_accessor[slocIdx + 1]; + + int siteMapId = xVal * num_sites_y + yVal; + + //Check within bounds + if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) + { + continue; + } + if (site_types[siteMapId] == sliceId && + (node2fence_region_map[ruInst] == lutId || + node2fence_region_map[ruInst] == ffId)) + { + int siteMapAIdx = site2addr_map[siteMapId]; + + Candidate cand; + cand.reset(); + cand.score = site_det_score[siteMapAIdx]; + cand.siteId = site_det_siteId[siteMapAIdx]; + cand.sigIdx = site_det_sig_idx[siteMapAIdx]; + + //array instantiation + int sdId(siteMapAIdx*SIG_IDX), sdlutId(siteMapAIdx*SLICE_CAPACITY); + int sdckId(siteMapAIdx*CKSR_IN_CLB), sdceId(siteMapAIdx*CE_IN_CLB); + + for(int sg = 0; sg < cand.sigIdx; ++sg) + { + cand.sig[sg] = site_det_sig[sdId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + cand.impl_lut[sg] = site_det_impl_lut[sdlutId + sg]; + cand.impl_ff[sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + cand.impl_cksr[sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + cand.impl_ce[sg] = site_det_impl_ce[sdceId + sg]; + } + ///// + + if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, + special_nodes, lutTypeInSliceUnit, lut_maxShared, ruInst, lutId, ffId, + half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + BLE_CAPACITY, NUM_BLE_PER_SLICE, cand.impl_lut, cand.impl_ff, cand.impl_cksr, + cand.impl_ce) && + add_inst_to_sig(flat_node2prclstrCount[ruInst], flat_node2precluster_map, + ruInst*3, cand.sig, cand.sigIdx, SLICE_CAPACITY)) + { + // Adding the instance to the site is legal + // If this is the first legal position found, set the expansion search radius + if (bestScoreImprov == -10000.0) + { + int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[slocIdx]) + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[slocIdx+ 1]); + r += ripupExpansion; + + int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); + int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); + end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; + } + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, + cand.sig, cand.siteId, cand.sigIdx, cand.score); + + T scoreImprov = cand.score - site_det_score[siteMapAIdx]; + if (scoreImprov > bestScoreImprov) + { + bestCand = cand; + bestScoreImprov = scoreImprov; + } + } + } + + } + if (bestCand.siteId == INVALID) + { + // Cannot find a legal position for this rip-up instance, so moving the instance to the site is illegal + // + // Revert all affected sites' clusters + for (auto rit = dets.rbegin(); rit != dets.rend(); ++rit) + { + int sId = rit->siteId; + int sAId = site2addr_map[sId]; + int sdId(sAId*SIG_IDX), sdlutId(sAId*SLICE_CAPACITY); + int sdckId(sAId*CKSR_IN_CLB), sdceId(sAId*CE_IN_CLB); + + site_det_score[sAId] = rit->score; + site_det_siteId[sAId] = sId; + site_det_sig_idx[sAId] = rit->sigIdx; + + for(int sg = 0; sg < site_det_sig_idx[sAId]; ++sg) + { + site_det_sig[sdId + sg] = rit->sig[sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = rit->impl_lut[sg]; + site_det_impl_ff[sdlutId + sg] = rit->impl_ff[sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = rit->impl_cksr[sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = rit->impl_ce[sg]; + } + } + // Move all ripped instances back to their original sites + int sdId(stAdId*SIG_IDX); + for (int sg = 0; sg < site_det_sig_idx[stAdId]; ++sg) + { + int sdInst = site_det_sig[sdId + sg]; + inst_curr_detSite[sdInst] = stId; + } + // Set the instance as illegal + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int prclInst = flat_node2precluster_map[instPcl + idx]; + inst_curr_detSite[prclInst] = INVALID; + } + + ripupSiteLegalizeInst = 0; + break; + } else + { + int sbId = bestCand.siteId; + int sbAId = site2addr_map[sbId]; + + Candidate tCand; + tCand.reset(); + /// + tCand.score = site_det_score[sbAId]; + tCand.siteId = site_det_siteId[sbAId]; + tCand.sigIdx = site_det_sig_idx[sbAId]; + + int sbSGId = sbAId*SIG_IDX; + int sbLutId = sbAId*SLICE_CAPACITY; + int sbCKId = sbAId*CKSR_IN_CLB; + int sbCEId = sbAId*CE_IN_CLB; + + /// + for(int sg = 0; sg < tCand.sigIdx; ++sg) + { + tCand.sig[sg] = site_det_sig[sbSGId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + tCand.impl_lut[sg] = site_det_impl_lut[sbLutId+ sg]; + tCand.impl_ff[sg] = site_det_impl_ff[sbLutId+ sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + tCand.impl_cksr[sg] = site_det_impl_cksr[sbCKId+ sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + tCand.impl_ce[sg] = site_det_impl_ce[sbCEId+ sg]; + } + dets.emplace_back(tCand); + + //Move ripped instances to this site + site_det_score[sbAId] = bestCand.score; + site_det_siteId[sbAId] = bestCand.siteId; + site_det_sig_idx[sbAId] = bestCand.sigIdx; + + for(auto sg = 0; sg < bestCand.sigIdx; ++sg) + { + site_det_sig[sbSGId + sg] = bestCand.sig[sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sbLutId+ sg] = bestCand.impl_lut[sg]; + site_det_impl_ff[sbLutId+ sg] = bestCand.impl_ff[sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sbCKId+ sg] = bestCand.impl_cksr[sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sbCEId+ sg] = bestCand.impl_ce[sg]; + } + /// + for (int idx = 0; idx < flat_node2prclstrCount[ruInst]; ++idx) + { + int clInst = flat_node2precluster_map[ruInst*3 + idx]; + inst_curr_detSite[clInst] = sbId; + } + } + } + //Ensure instance is legalized to a site for ripup LG to be successful + if (ripupSiteLegalizeInst == INVALID && inst_curr_detSite[instId] != INVALID) + { + ripupLegalizeInst = 1; + } + } + if (ripupLegalizeInst == 1) break; + } + + //Greedy Legalization if RipUP LG failed + if (ripupLegalizeInst != 1) + { + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Starting greedy LG for Inst: " + std::to_string(instId) + " with spiralBegin: "; + // dbgMsg += std::to_string(spiralBegin) + " and end: " + std::to_string(spiralEnd) + "\n"; + //} + //////DBG + inst_curr_detSite[instId] = INVALID; + + int beg(spiralBegin), end(spiralEnd); + + T cenX(pos_x[flat_node2precluster_map[instPcl]] * wlPrecond[flat_node2precluster_map[instPcl]]); + T cenY(pos_y[flat_node2precluster_map[instPcl]] * wlPrecond[flat_node2precluster_map[instPcl]]); + T totalWt(wlPrecond[flat_node2precluster_map[instPcl]]); + + for (int cl = 1; cl < flat_node2prclstrCount[instId]; ++cl) + { + int pclInst = flat_node2precluster_map[instPcl + cl]; + cenX += pos_x[pclInst] * wlPrecond[pclInst]; + cenY += pos_y[pclInst] * wlPrecond[pclInst]; + totalWt += wlPrecond[pclInst]; + } + + if (totalWt > 0) + { + cenX /= totalWt; + cenY /= totalWt; + } + + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Greedy LG: Centroid of Inst: " + std::to_string(instId) + " = (" + std::to_string(cenX); + // dbgMsg += ", " + std::to_string(cenY) + ")" + "\n"; + //} + //////DBG + + //BestCandidate + Candidate bestCand; + bestCand.reset(); + T bestScoreImprov(-10000.0); + + for (int sIdx = beg; sIdx < end; ++sIdx) + { + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Consider " + std::to_string(sIdx) + " within [" + std::to_string(beg) + ", " + std::to_string(end); + // dbgMsg += ") \n"; + //} + //////DBG + + int saIdx = sIdx*2; + int xVal = cenX + spiral_accessor[saIdx]; + int yVal = cenY + spiral_accessor[saIdx + 1]; + int siteMapIdx = xVal * num_sites_y + yVal; + int siteMapAIdx = site2addr_map[siteMapIdx]; + + //Check within bounds + if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) + { + continue; + } + + if (site_types[siteMapIdx] == sliceId && (node2fence_region_map[instId] == lutId || + node2fence_region_map[instId] == ffId) && site_det_sig_idx[siteMapAIdx] < 2*SLICE_CAPACITY) + { + //////DBG + //if (collect_info) + //{ + // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId); + // dbgMsg += " consider site at (" + std::to_string(xVal) + ", " + std::to_string(yVal) + ") "; + // dbgMsg += std::to_string(siteMapIdx) + "\n"; + //} + //////DBG + + ////DBG + //std::cout << "Greedy LG: Inst: " << instId << " site addr map: " << siteMapAIdx << std::endl; + ////DBG + Candidate cand; + cand.reset(); + cand.score = site_det_score[siteMapAIdx]; + cand.siteId = site_det_siteId[siteMapAIdx]; + cand.sigIdx = site_det_sig_idx[siteMapAIdx]; + + int sdId(siteMapAIdx*SIG_IDX), sdlutId(siteMapAIdx*SLICE_CAPACITY); + int sdckId(siteMapAIdx*CKSR_IN_CLB), sdceId(siteMapAIdx*CE_IN_CLB); + + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Greedy LG: Inst: " + std::to_string(instId) + " site det sig element count: " + std::to_string(site_det_sig_idx[siteMapAIdx]) + "\n"; + // dbgMsg += "site det sig contains: "; + // for(int sg = 0; sg < site_det_sig_idx[siteMapAIdx]; ++sg) + // { + // dbgMsg += std::to_string(site_det_sig[sdId + sg]) + " "; + // } + // dbgMsg += "\nsite det impl lut contains: "; + // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // dbgMsg += std::to_string(site_det_impl_lut[sdlutId + sg]) + " "; + // } + // dbgMsg += "\nsite det impl ff contains: "; + // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // dbgMsg += std::to_string(site_det_impl_ff[sdlutId + sg]) + " "; + // } + // dbgMsg += "\n"; + // dbgMsg += "\nsite det impl cksr contains: "; + // for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + // { + // dbgMsg += std::to_string(site_det_impl_cksr[sdckId + sg]) + " "; + // } + // dbgMsg += "\n"; + // dbgMsg += "\nsite det impl ce contains: "; + // for(int sg = 0; sg < CE_IN_CLB; ++sg) + // { + // dbgMsg += std::to_string(site_det_impl_ce[sdceId + sg]) + " "; + // } + // dbgMsg += "\n"; + //} + //////DBG + + for(int sg = 0; sg < site_det_sig_idx[siteMapAIdx]; ++sg) + { + cand.sig[sg] = site_det_sig[sdId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + cand.impl_lut[sg] = site_det_impl_lut[sdlutId + sg]; + cand.impl_ff[sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + cand.impl_cksr[sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + cand.impl_ce[sg] = site_det_impl_ce[sdceId + sg]; + } + ///// + + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Greedy LG: Inst: " + std::to_string(instId) + " consider siteID: " + std::to_string(siteMapIdx) + " - created candidate" + "\n"; + //} + //////DBG + + if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, flat_node2pin_map, + node2pincount, net2pincount, pin2net_map, pin_typeIds, flat_node2prclstrCount, + flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, flop_ctrlSets, + extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, lutTypeInSliceUnit, lut_maxShared, + instId, lutId, ffId, half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + BLE_CAPACITY, NUM_BLE_PER_SLICE, cand.impl_lut, cand.impl_ff, cand.impl_cksr, cand.impl_ce) && + add_inst_to_sig(flat_node2prclstrCount[instId], flat_node2precluster_map, + instPcl, cand.sig, cand.sigIdx, SLICE_CAPACITY)) + { + //////DBG + //if (collect_info) + //{ + // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId) + " - possible location at (" + std::to_string(xVal) + ", " + std::to_string(yVal) + ") " + std::to_string(siteMapIdx) + "\n"; + //} + //////DBG + + // Adding the instance to the site is legal + // If this is the first legal position found, set the expansion search radius + if (bestScoreImprov == -10000.0) + { + int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[saIdx]) + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[saIdx + 1]); + r += greedyExpansion; + + int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); + int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); + end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; + } + //cand_score = computeCandidateScore(cand); + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, + cand.sig, cand.siteId, cand.sigIdx, cand.score); + + T scoreImprov = cand.score - site_det_score[siteMapAIdx]; + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Cand site Id: " + std::to_string(cand.siteId) + " and cand sig Idx: " + std::to_string(cand.sigIdx) + "\n"; + // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId) + " cand score: " + std::to_string(cand.score) + ", site_det_score: " + std::to_string(site_det_score[siteMapAIdx]) + " and scoreImprov: "; + // dbgMsg += std::to_string(scoreImprov) + " with bestScoreImprov " + std::to_string(bestScoreImprov) + "\n"; + //} + //////DBG + + if (scoreImprov > bestScoreImprov) + { + //std::cout << "Found best candidate for " << idx << std::endl; + bestCand = cand; + bestScoreImprov = scoreImprov; + } + } + + //////DBG + //if (collect_info) + //{ + // dbgMsg += "Greedy LG Inst of " + std::to_string(instId) + " has bestCand site: " + std::to_string(bestCand.siteId) + " with bestScoreImprov " + std::to_string(bestScoreImprov) + "\n"; + //} + //////DBG + } + } + + // Commit the found best legal solution + if (bestCand.siteId != INVALID) + { + int stId = bestCand.siteId; + int stAId = site2addr_map[stId]; + + site_det_score[stAId] = bestCand.score; + site_det_siteId[stAId] = bestCand.siteId; + site_det_sig_idx[stAId] = bestCand.sigIdx; + + int sdId(stAId*SIG_IDX), sdlutId(stAId*SLICE_CAPACITY); + int sdckId(stAId*CKSR_IN_CLB), sdceId(stAId*CE_IN_CLB); + + for (auto sg = 0; sg < bestCand.sigIdx; ++sg) + { + site_det_sig[sdId + sg] = bestCand.sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = bestCand.impl_lut[sg]; + site_det_impl_ff[sdlutId + sg] = bestCand.impl_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = bestCand.impl_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = bestCand.impl_ce[sg]; + } + ///// + + for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) + { + int prclInst = flat_node2precluster_map[instPcl + cl]; + inst_curr_detSite[prclInst] = stId; + } + greedyLegalizeInst = 1; + + } else + { + greedyLegalizeInst = 0; + } + } + + if (ripupLegalizeInst != 1 && greedyLegalizeInst != 1) + { + + if (node2fence_region_map[instId] == ffId) + { + dreamplacePrint(kERROR, "unable to legalize FF inst: %u \n", instId); + } else + { + dreamplacePrint(kERROR, "unable to legalize LUT%s inst: %u \n", lut_type[instId], instId); + } + + //DBG + //if (collect_info) + //{ + //std::cout << dbgMsg << std::endl; + //} + //DBG + break; + } + ////DBG + //std::chrono::steady_clock::time_point pt4= std::chrono::steady_clock::now(); + //std::cout << i << ": inst " << instId << " of type " + // << node2fence_region_map[instId] << " RipUP&Greedy LG takes " + // << std::chrono::duration_cast(pt4-begin).count()/1000000.0 + // << " (s)" << std::endl; + ////DBG + } + + return 0; +} + +// slot assignment +template +int slotAssign( + const T* pos_x, + const T* pos_y, + const T* wlPrecond, + const T* site_xy, + const int* flop_ctrlSets, + const int* flop2ctrlSetId_map, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* pin2net_map, + const int* pin2node_map, + const int* node2pincount, + const int* net2pincount, + const int* node2outpinIdx_map, + const int* node2fence_region_map, + const int* pin_typeIds, + const int* lut_type, + const int* site_types, + const int* sorted_net_map, + const int* addr2site_map, + const int* sites_with_special_nodes, + const T slotAssignFlowWeightScale, + const T slotAssignFlowWeightIncr, + const int num_sites_x, + const int num_sites_y, + const int num_clb_sites, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int NUM_BLE_PER_HALF_SLICE, + const int half_ctrl_mode, + const int lutId, + const int num_threads, + int* site_det_sig_idx, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* site_det_siteId + ) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); + +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + if (site_det_sig_idx[sIdx] > 0) + { + int siteId = addr2site_map[sIdx]; + + //initSlotAssign + int sdlutId(sIdx*SLICE_CAPACITY); + int sdckId(sIdx*CKSR_IN_CLB), sdceId(sIdx*CE_IN_CLB); + + //Fixed FF ctrls + if (half_ctrl_mode == 1) + { + std::vector lut, ff; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + if (site_det_impl_lut[sdlutId + sg] != INVALID) + { + lut.push_back(site_det_impl_lut[sdlutId + sg]); + } + if (site_det_impl_ff[sdlutId + sg] != INVALID) + { + ff.push_back(site_det_impl_ff[sdlutId + sg]); + } + } + + // Pre-assign control sets + // Note that the original impl FF assignment is feasible but it is optimized for minimum resource usage + // Therefore, to enlarge the solution space exploration, we do following modifications based on the original control set assignment: + // (1) if ce[1] (ce[3]) is empty, we set it to ce[0] (ce[2]), + // (2) if (cksr[1], ce[2], ce[3]) are empty, we set them to (cksr[0], ce[0], ce[1]) + int cksr[CKSR_MAX_CAP]; + int ce[CE_MAX_CAP]; + + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + cksr[sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + ce[sg] = site_det_impl_ce[sdceId + sg]; + } + + if (ce[1] == INVALID) + { + ce[1] = ce[0]; + } + if (ce[3] == INVALID) + { + ce[3] = ce[2]; + } + if (cksr[1] == INVALID) + { + cksr[1] = cksr[0]; + ce[2] = ce[0]; + ce[3] = ce[1]; + } + + //computeLUTScoreAndScoreImprov + std::vector > bleS(lut.size()), bleP, bleLP; + + for (unsigned int i = 0; i < lut.size(); ++i) + { + auto &ble = bleS[i]; + ble.lut[0] = lut[i]; + ble.lut[1] = INVALID; + + subSlice tempBLE(ble); + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, + flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, + net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, + half_ctrl_mode, cksr[0], ce[0], ce[1], ble); + + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, + flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, + net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, + half_ctrl_mode, cksr[1], ce[2], ce[3], tempBLE); + + if (tempBLE.score > ble.score) + { + ble = tempBLE; + } + } + + bleP.clear(); + // Collect all feasible LUT pairs and compute their best scores and score improvement + for(unsigned int aIdx = 0; aIdx < lut.size(); ++aIdx) + { + const int lutA = lut[aIdx]; + for(unsigned int bIdx = aIdx + 1; bIdx < lut.size(); ++bIdx) + { + const int lutB = lut[bIdx]; + if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, lutTypeInSliceUnit, lut_maxShared, lutA, lutB)) + { + bleP.emplace_back(); + + auto &ble = bleP.back(); + ble.lut[0] = lutA; + ble.lut[1] = lutB; + + subSlice tempBLE(ble); + + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, + flat_node2pin_map, flat_net2pin_start_map, flat_net2pin_map, pin2net_map, + node2pincount, pin_typeIds, net2pincount, node2outpinIdx_map, pin2node_map, + sorted_net_map, lut_type, ff, half_ctrl_mode, cksr[0], ce[0], ce[1], ble); + + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, + flat_node2pin_map, flat_net2pin_start_map, flat_net2pin_map, pin2net_map, + node2pincount, pin_typeIds, net2pincount, node2outpinIdx_map, pin2node_map, + sorted_net_map, lut_type, ff, half_ctrl_mode, cksr[1], ce[2], ce[3], tempBLE); + + if (tempBLE.score > ble.score) + { + ble = tempBLE; + } + // We define the score improvement of a compatible LUT pair (a, b) as + // improv(a, b) = max(BLEScore(a, b, *, *)) - max(BLEScore(a, -, *, *)) - max(BLEScore(b, -, *, *)) + ble.improv = ble.score - bleS[aIdx].score - bleS[bIdx].score; + } + } + } + + //pairLUTs + pairLUTs(lut, bleP, bleS, slotAssignFlowWeightScale, slotAssignFlowWeightIncr, NUM_BLE_PER_SLICE, bleLP); + + //assignLUTsandFFs + // Reset the existing slot assignment in site.det + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = INVALID; + site_det_impl_ff[sdlutId + sg] = INVALID; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = INVALID; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = INVALID; + } + + std::vector scores; + scores.assign(NUM_BLE_PER_SLICE, 0.0); + + // Sort legal LUT/LUT pairs by their score from high to low + std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score > b.score; }); + + // Record the number of available LUT pair slots in low/high half of the slice + int availLo = NUM_BLE_PER_HALF_SLICE; + int availHi = NUM_BLE_PER_HALF_SLICE; + + // Assign LUTs one by one and determine thier best FFs at the same time + for(const auto &estBLE : bleLP) + { + subSlice bleLo(estBLE); + subSlice bleHi(estBLE); + + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, + flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, + net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, + half_ctrl_mode, cksr[0], ce[0], ce[1], bleLo); + + findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, + flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, + net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, + half_ctrl_mode, cksr[1], ce[2], ce[3], bleHi); + + // Try to fit the found BLE in the preferred feasible half slice + int lh = ((availLo && bleLo.score > bleHi.score) || !availHi ? 0 : 1); + const auto &ble = (lh ? bleHi : bleLo); + (lh ? availHi : availLo) -= 1; + + int beg = lh * HALF_SLICE_CAPACITY; + int end = beg + HALF_SLICE_CAPACITY; + + for (int idx = beg; idx < end; idx += BLE_CAPACITY) + { + int tpos = idx/SLICE_CAPACITY; + int pos = idx - int(tpos * SLICE_CAPACITY); + if (site_det_impl_lut[sdlutId + pos] == INVALID && site_det_impl_lut[sdlutId + pos + 1] == INVALID) + { + + // Realize LUT assignment + // Assign LUTs with more inputs at odd slots + // In this way we can also make sure that all LUT6 are assigned at odd positions + int demA = (ble.lut[0] == INVALID ? 0 : lut_type[ble.lut[0]]); + int demB = (ble.lut[1] == INVALID ? 0 : lut_type[ble.lut[1]]); + int flip = (demA > demB ? 1 : 0); + site_det_impl_lut[sdlutId + pos] = ble.lut[flip]; + site_det_impl_lut[sdlutId + pos + 1] = ble.lut[1 - flip]; + + // Realize FF assignment + for (int k : {0, 1}) + { + site_det_impl_ff[sdlutId + pos + k] = ble.ff[k]; + if (ble.ff[k] != INVALID) + { + const int ffId = ble.ff[k]; + site_det_impl_cksr[sdckId + lh]= flop_ctrlSets[flop2ctrlSetId_map[ffId]*3 + 1]; + site_det_impl_ce[sdceId + 2*lh + k]= flop_ctrlSets[flop2ctrlSetId_map[ffId]*3 + 2]; + // Remove the FF assigned from the active list + ff.erase(std::find(ff.begin(), ff.end(), ble.ff[k])); + + } + } + scores[pos / BLE_CAPACITY] = ble.score; + break; + } + } + } + + // Assign the rest of unassigned FFs + // + // We iteratively add one FF at a time + // Each time, we add the FF gives the best score improvement + while (!ff.empty()) + { + // Find the FF gives the best score improvement + int bestFFIdx(INVALID), bestPos(INVALID); + T bestImprov(-10000.0); + for(unsigned int ffIdx = 0; ffIdx < ff.size(); ++ffIdx) + { + int ffI = ff[ffIdx]; + int ffcksr = flop_ctrlSets[flop2ctrlSetId_map[ffI]*3 + 1]; + int ffce = flop_ctrlSets[flop2ctrlSetId_map[ffI]*3 + 2]; + + for (int pos = 0; pos < SLICE_CAPACITY; ++pos) + { + int lh = pos / HALF_SLICE_CAPACITY; + int toe = pos/BLE_CAPACITY; + int oe = pos - int(toe * BLE_CAPACITY); + + if (site_det_impl_ff[sdlutId + pos] == INVALID && ffcksr == cksr[lh] && ffce == ce[2 * lh + oe]) + { + int k = pos / BLE_CAPACITY * BLE_CAPACITY; + site_det_impl_ff[sdlutId + pos] = ffI; + T improv(0.0); + computeBLEScore(flat_node2pin_start_map, flat_node2pin_map, flat_net2pin_start_map, + flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, pin_typeIds, + lut_type, half_ctrl_mode, site_det_impl_lut[sdlutId + k], + site_det_impl_lut[sdlutId + k + 1], site_det_impl_ff[sdlutId + k], + site_det_impl_ff[sdlutId + k+1], improv); + + improv -= scores[k / BLE_CAPACITY]; + site_det_impl_ff[sdlutId + pos] = INVALID; + + if (improv > bestImprov) + { + bestFFIdx = ffIdx; + bestPos = pos; + bestImprov = improv; + } + } + } + } + + // Realize the best FF assignment found + int bestFF = ff[bestFFIdx]; + int lh = bestPos / HALF_SLICE_CAPACITY; + int toe = bestPos/BLE_CAPACITY; + int oe = bestPos - int(toe * BLE_CAPACITY); + site_det_impl_ff[sdlutId + bestPos] = bestFF; + site_det_impl_cksr[sdckId + lh] = flop_ctrlSets[flop2ctrlSetId_map[bestFF]*3 + 1]; + site_det_impl_ce[sdceId + 2 * lh + oe] = flop_ctrlSets[flop2ctrlSetId_map[bestFF]*3 + 2]; + + // Remove the best FF found from the active list + ff.erase(ff.begin() + bestFFIdx); + + // Update the BLE slot score + scores[bestPos/BLE_CAPACITY] += bestImprov; + } + + //order BLEs + // Sort BLEs in each half slice by their Y centroid coordinates (cen.y - site.y) + T siteY = site_xy[2*site_det_siteId[sIdx] + 1]; + for (int lh : {0, 1}) + { + bleLP.clear(); + + int beg = lh * HALF_SLICE_CAPACITY; + int end = beg + HALF_SLICE_CAPACITY; + for (int offset = beg; offset < end; offset += BLE_CAPACITY) + { + bleLP.emplace_back(); + auto &ble = bleLP.back(); + std::vector insts; + + for (int k : {0, 1}) + { + if (site_det_impl_lut[sdlutId + offset + k] != INVALID) + { + insts.push_back(site_det_impl_lut[sdlutId + offset + k]); + ble.lut[k] = site_det_impl_lut[sdlutId + offset + k]; + } + if (site_det_impl_ff[sdlutId + offset + k] != INVALID) + { + insts.push_back(site_det_impl_ff[sdlutId + offset + k]); + ble.ff[k] = site_det_impl_ff[sdlutId + offset + k]; + } + } + + // We use ble.score to store centroid.y - site.y of this BLE + if (insts.empty()) + { + ble.score = 0.0; + } else + { + //Centroid of insts + T cenY(pos_y[insts[0]]*wlPrecond[insts[0]]), totalWt(wlPrecond[insts[0]]); + for(unsigned int el = 1; el < insts.size(); ++el) + { + cenY += pos_y[insts[el]] * wlPrecond[insts[el]]; + totalWt += wlPrecond[insts[el]]; + } + if (totalWt > 0) + { + cenY /= totalWt; + } + + ble.score = cenY - siteY; + } + } + + // Sort BLEs in this half slice by their centroid.y - site.y from low to high + // Put them back to the implementation + std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score < b.score; }); + + for(unsigned int i = 0; i < bleLP.size(); ++i) + { + const auto &ble = bleLP[i]; + int offset = lh * HALF_SLICE_CAPACITY + i * BLE_CAPACITY; + for (int k : {0, 1}) + { + site_det_impl_lut[sdlutId + offset + k] = ble.lut[k]; + site_det_impl_ff[sdlutId + offset + k] = ble.ff[k]; + } + } + } + } else + { + ////DBG + //char printMsg = 0; + //std::string dbgMsg = ""; + //dbgMsg += std::to_string(sIdx) + " consider site: " + std::to_string(siteId) + "\n"; + ////DBG + + int res_lut[SLICE_MAX_CAP]; + int res_ff[SLICE_MAX_CAP]; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = site_det_impl_lut[sdlutId + sg]; + res_ff[sg] = site_det_impl_ff[sdlutId + sg]; + } + + for (int sg = 0; sg < HALF_SLICE_CAPACITY; ++sg) + { + int ssIdx = 2*sg; + subSlice ble; + + ble.lut[0] = res_lut[ssIdx]; + ble.lut[1] = res_lut[ssIdx + 1]; + ble.ff[0] = res_ff[ssIdx]; + ble.ff[1] = res_ff[ssIdx + 1]; + + + //DBG + //printMsg = 1; + //DBG + + ////DBG + //dbgMsg += "Initial ALM" + std::to_string(sg) + " contains: "; + //dbgMsg += "lut0: " + std::to_string(ble.lut[0]) + ", "; + //dbgMsg += "lut1: " + std::to_string(ble.lut[1]) + ", "; + //dbgMsg += "ff0: " + std::to_string(ble.ff[0]) + ", "; + //dbgMsg += "ff1: " + std::to_string(ble.ff[1]) + "\n"; + ////DBG + + // Assign LUTs with more inputs at odd slots + if (sites_with_special_nodes[sIdx] != 1) + { + int demA = (ble.lut[0] == INVALID ? 0 : lut_type[ble.lut[0]]); + int demB = (ble.lut[1] == INVALID ? 0 : lut_type[ble.lut[1]]); + int flip = (demA > demB ? 1 : 0); + site_det_impl_lut[sdlutId + ssIdx] = ble.lut[flip]; + site_det_impl_lut[sdlutId + ssIdx + 1] = ble.lut[1 - flip]; + } + + //Given LUT locations, ensure LUT driving FF are in same z + if ((ble.ff[0] != INVALID || ble.ff[1] != INVALID) && + (ble.lut[0] != INVALID || ble.lut[1] != INVALID)) + { + std::vector > lutOuts(2), ffIns(2); + + for (int k : {0, 1}) + { + if (ble.lut[k] == INVALID) continue; + + int ndOutId = 4*ble.lut[k]; + int ndOutPins = ndOutId + 4; + for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + lutOuts[k].emplace_back(outNetId); + } + } + + for (int k : {0, 1}) + { + if (ble.ff[k] == INVALID) continue; + + int elIt = flat_node2pin_start_map[ble.ff[k]]; + int elEnd = flat_node2pin_start_map[ble.ff[k]+1]; + + for (int el = elIt; el < elEnd; ++el) + { + //Skip if not an input or output pin + int pinType = pin_typeIds[flat_node2pin_map[el]]; + if (pinType != 1) continue; + + int netId = pin2net_map[flat_node2pin_map[el]]; + ffIns[k].emplace_back(netId); + } + } + + for (int idx : {0, 1}) + { + if (lutOuts[idx].size() == 0) continue; + + for (int idy : {0, 1}) + { + if (ffIns[idy].size() == 0) continue; + + std::sort(lutOuts[idx].begin(), lutOuts[idx].end()); + std::sort(ffIns[idy].begin(), ffIns[idy].end()); + + //Check for matching nets + int lId = 0, fId = 0; + int netlId = lutOuts[idx][lId]; + int netfId = ffIns[idy][fId]; + + while(lId < lutOuts[idx].size() && fId < ffIns[idy].size()) + { + if (netlId < netfId) + { + ++lId; + if (lId < lutOuts[idx].size()) + { + netlId = lutOuts[idx][lId]; + } else + { + break; + } + } else if (netlId > netfId) + { + ++fId; + if (fId < ffIns[idy].size()) + { + netfId = ffIns[idy][fId]; + } else + { + break; + } + } else + { + //Move ff to same z as lut - Flip ffs + if (idx != idy) + { + int ff0 = ble.ff[0]; + int ff1 = ble.ff[1]; + + //Flip flop location + ble.ff[0] = ff1; + ble.ff[1] = ff0; + } + break; + } + } + } + } + } + + ////DBG + //dbgMsg += "Final ALM" + std::to_string(sg) + " contains: "; + //dbgMsg += "lut0: " + std::to_string(ble.lut[0]) + ", "; + //dbgMsg += "lut1: " + std::to_string(ble.lut[1]) + ", "; + //dbgMsg += "ff0: " + std::to_string(ble.ff[0]) + ", "; + //dbgMsg += "ff1: " + std::to_string(ble.ff[1]) + "\n"; + ////DBG + } + + ////DBG + //if (printMsg == 1) + //{ + // std::cout << dbgMsg << std::endl; + //} + ////DBG + + //order subSlices if site does not contain special nodes like carry chains + if (sites_with_special_nodes[sIdx] != 1) + { + // Sort subSlices by their Y centroid coordinates (cen.y - site.y) + std::vector > bleLP; + + T siteY = site_xy[2*site_det_siteId[sIdx] + 1]; + + for (int offset = 0; offset < SLICE_CAPACITY; offset += BLE_CAPACITY) + { + bleLP.emplace_back(); + auto &ble = bleLP.back(); + std::vector insts; + + for (int k : {0, 1}) + { + if (site_det_impl_lut[sdlutId + offset + k] != INVALID) + { + insts.push_back(site_det_impl_lut[sdlutId + offset + k]); + } + ble.lut[k] = site_det_impl_lut[sdlutId + offset + k]; + if (site_det_impl_ff[sdlutId + offset + k] != INVALID) + { + insts.push_back(site_det_impl_ff[sdlutId + offset + k]); + } + ble.ff[k] = site_det_impl_ff[sdlutId + offset + k]; + } + + // We use ble.score to store centroid.y - site.y of this BLE + if (insts.empty()) + { + ble.score = 0.0; + } else + { + //Centroid of insts + T cenY(pos_y[insts[0]]*wlPrecond[insts[0]]), totalWt(wlPrecond[insts[0]]); + for(unsigned int el = 1; el < insts.size(); ++el) + { + cenY += pos_y[insts[el]] * wlPrecond[insts[el]]; + totalWt += wlPrecond[insts[el]]; + } + if (totalWt > 0) + { + cenY /= totalWt; + } + + ble.score = cenY - siteY; + } + } + //std::cout << "siteId: " << siteId << " get scores for all BLEs"<< std::endl; + + // Sort BLEs in slice by their centroid.y - site.y from low to high + // Put them back to the implementation + std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score < b.score; }); + + for(unsigned int i = 0; i < bleLP.size(); ++i) + { + const auto &ble = bleLP[i]; + int offset = i * BLE_CAPACITY; + for (int k : {0, 1}) + { + site_det_impl_lut[sdlutId + offset + k] = ble.lut[k]; + site_det_impl_ff[sdlutId + offset + k] = ble.ff[k]; + } + } + //std::cout << "siteId: " << siteId << " complete sort all subSlices"<< std::endl; + } + } + } + } + + //std::cout << "Slot assignment done" << std::endl; + return 0; +} + +// Cache the solution +template +int cacheSolution( + const int* site_det_impl_lut, + const int* site_det_impl_ff, + const int* inst_curr_detSite, + const int* addr2site_map, + const int num_sites_y, + const int num_clb_sites, + const int SLICE_CAPACITY, + T* node_x, + T* node_y, + int* node_z + ) +{ + for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) + { + int siteIdX = addr2site_map[sIdx] / num_sites_y; + int siteIdY = addr2site_map[sIdx] - int(siteIdX*num_sites_y); + int sdlutId(sIdx*SLICE_CAPACITY); + for (int z = 0; z < SLICE_CAPACITY; ++z) + { + for (int id : {site_det_impl_lut[sdlutId + z], site_det_impl_ff[sdlutId + z]}) + { + if (id != INVALID) + { + node_x[id] = siteIdX; + node_y[id] = siteIdY; + node_z[id] = z; + } + } + } + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +//Initialize site neighbors +void initSiteNbrs( + at::Tensor pos, + at::Tensor wlPrecond, + at::Tensor site_xy, + at::Tensor site_det_score, + at::Tensor sorted_node_idx, + at::Tensor node2fence_region_map, + at::Tensor site_types, + at::Tensor spiral_accessor, + at::Tensor site2addr_map, + at::Tensor addr2site_map, + at::Tensor flat_node2precluster_map, + at::Tensor flat_node2prclstrCount, + at::Tensor is_mlab_node, + at::Tensor is_mlab_site, + at::Tensor sites_with_special_nodes, + double nbrDistEnd, + double nbrDistBeg, + double nbrDistIncr, + int lutId, + int ffId, + int sliceId, + int num_nodes, + int num_sites_x, + int num_sites_y, + int num_clb_sites, + int SCL_IDX, + int SIG_IDX, + int SLICE_CAPACITY, + int numGroups, + int maxList, + int spiralBegin, + int spiralEnd, + at::Tensor site_curr_scl_score, + at::Tensor site_curr_scl_siteId, + at::Tensor site_curr_scl_validIdx, + at::Tensor site_curr_scl_idx, + at::Tensor site_curr_scl_sig, + at::Tensor site_curr_scl_sig_idx, + at::Tensor site_curr_scl_impl_lut, + at::Tensor site_nbrRanges, + at::Tensor site_nbrRanges_idx, + at::Tensor site_nbrList, + at::Tensor site_nbr, + at::Tensor site_nbr_idx, + at::Tensor site_nbrGroup_idx, + at::Tensor site_det_siteId, + at::Tensor site_det_sig, + at::Tensor site_det_sig_idx, + at::Tensor site_det_impl_lut, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(wlPrecond); + CHECK_CONTIGUOUS(wlPrecond); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(sorted_node_idx); + CHECK_CONTIGUOUS(sorted_node_idx); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + + CHECK_FLAT(site_types); + CHECK_CONTIGUOUS(site_types); + + CHECK_FLAT(spiral_accessor); + CHECK_CONTIGUOUS(spiral_accessor); + + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + CHECK_FLAT(addr2site_map); + CHECK_CONTIGUOUS(addr2site_map); + + CHECK_FLAT(flat_node2precluster_map); + CHECK_CONTIGUOUS(flat_node2precluster_map); + + CHECK_FLAT(flat_node2prclstrCount); + CHECK_CONTIGUOUS(flat_node2prclstrCount); + + int numNodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initSiteNeighbours", [&] { + initSiteNeighbours( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_site, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), + nbrDistEnd, nbrDistBeg, nbrDistIncr, + lutId, ffId, sliceId, num_nodes, num_sites_x, num_sites_y, + num_clb_sites, spiralBegin, spiralEnd, maxList, + numGroups, SCL_IDX, SIG_IDX, SLICE_CAPACITY, + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), + num_threads); + }); + //std::cout << "Completed initSiteNeighbours" << std::endl; + +} + +//Initialize and handle carry chains and mlabs +void initializeLG( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor sorted_net_idx, + at::Tensor sorted_node_map, + at::Tensor sorted_node_idx, + at::Tensor flat_net2pin_map, + at::Tensor flat_net2pin_start_map, + at::Tensor flop2ctrlSetId_map, + at::Tensor flop_ctrlSets, + at::Tensor node2fence_region_map, + at::Tensor node2outpinIdx_map, + at::Tensor pin2net_map, + at::Tensor pin2node_map, + at::Tensor pin_typeIds, + at::Tensor net2pincount, + at::Tensor is_mlab_node, + double preClusteringMaxDist, + int lutId, + int ffId, + int num_nets, + int num_nodes, + int num_threads, + int WLscoreMaxNetDegree, + at::Tensor net_bbox, + at::Tensor net_pinIdArrayX, + at::Tensor net_pinIdArrayY, + at::Tensor flat_node2precluster_map, + at::Tensor flat_node2prclstrCount) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + CHECK_FLAT(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + CHECK_FLAT(sorted_net_idx); + CHECK_CONTIGUOUS(sorted_net_idx); + + CHECK_FLAT(sorted_node_map); + CHECK_CONTIGUOUS(sorted_node_map); + CHECK_FLAT(sorted_node_idx); + CHECK_CONTIGUOUS(sorted_node_idx); + + CHECK_FLAT(flat_net2pin_map); + CHECK_CONTIGUOUS(flat_net2pin_map); + CHECK_FLAT(flat_net2pin_start_map); + CHECK_CONTIGUOUS(flat_net2pin_start_map); + + CHECK_FLAT(flop2ctrlSetId_map); + CHECK_CONTIGUOUS(flop2ctrlSetId_map); + CHECK_FLAT(flop_ctrlSets); + CHECK_CONTIGUOUS(flop_ctrlSets); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + CHECK_FLAT(node2outpinIdx_map); + CHECK_CONTIGUOUS(node2outpinIdx_map); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(pin2node_map); + CHECK_CONTIGUOUS(pin2node_map); + CHECK_FLAT(pin_typeIds); + CHECK_CONTIGUOUS(pin_typeIds); + + CHECK_FLAT(net2pincount); + CHECK_CONTIGUOUS(net2pincount); + + int numNodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initializeNets", [&] { + initializeNets( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), + WLscoreMaxNetDegree, num_threads); + }); + + //std::cout << "Completed initializeNets " << std::endl; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "preClusteringUpd", [&] { + preClusteringUpd( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), + preClusteringMaxDist, lutId, ffId, num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), + num_threads); + }); + //std::cout << "Completed preclusteringUpd" << std::endl; +} + +//Legalize carry-chains +void legalizeCarryChain( + at::Tensor pos, + at::Tensor site_xy, + at::Tensor wlPrecond, + at::Tensor spiral_accessor, + at::Tensor site_types, + at::Tensor site2addr_map, + at::Tensor flat_cc2node_start_map, + at::Tensor flat_cc2node_map, + int spiralBegin, + int spiralEnd, + int num_sites_x, + int num_sites_y, + int sliceId, + int SIG_IDX, + int SLICE_CAPACITY, + int num_carry_chains, + int num_carryInstsInSlice, + int slice_minX, + int slice_maxX, + int slice_minY, + int slice_maxY, + at::Tensor carry_chain_displacements, + at::Tensor site_det_score, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_next_bestScoreImprov, + at::Tensor sites_with_special_nodes, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestSite, + at::Tensor site_det_siteId, + at::Tensor site_det_sig, + at::Tensor site_det_sig_idx, + at::Tensor site_det_impl_lut, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(wlPrecond); + CHECK_CONTIGUOUS(wlPrecond); + + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + CHECK_FLAT(flat_cc2node_start_map); + CHECK_CONTIGUOUS(flat_cc2node_start_map); + CHECK_FLAT(flat_cc2node_map); + CHECK_CONTIGUOUS(flat_cc2node_map); + + CHECK_FLAT(spiral_accessor); + CHECK_CONTIGUOUS(spiral_accessor); + + CHECK_FLAT(site_types); + CHECK_CONTIGUOUS(site_types); + + int numNodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeCarryChainsLauncher", [&] { + legalizeCarryChainsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_cc2node_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_cc2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + spiralBegin, spiralEnd, num_carry_chains, num_sites_x, num_sites_y, + sliceId, SIG_IDX, SLICE_CAPACITY, num_carryInstsInSlice, + slice_minX, slice_maxX, slice_minY, slice_maxY, + DREAMPLACE_TENSOR_DATA_PTR(carry_chain_displacements, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), + num_threads); + }); + +} + +//MinCost to find sites +void minCostFlow( + pybind11::array_t const& locX, + pybind11::array_t const& locY, + int const num_sites, int const num_nodes, + pybind11::array_t const& sites, + pybind11::array_t const& precond, + double const &lg_max_dist_init, double const &lg_max_dist_incr, + double const &lg_flow_cost_scale, pybind11::list &movVal, pybind11::list &out) +{ + typedef lemon::ListDigraph graphType; + graphType graph; + graphType::ArcMap capLo(graph); + graphType::ArcMap capHi(graph); + graphType::ArcMap cost(graph); + std::vector lNodes, rNodes; + std::vector lArcs, rArcs, mArcs; + std::vector > mArcPairs; + + //Source and target Nodes + graphType::Node s = graph.addNode(), t = graph.addNode(); + + //Add left nodes (blocks) and arcs between source node and left nodes + for (int i = 0; i < num_nodes; ++i) + { + lNodes.emplace_back(graph.addNode()); + lArcs.emplace_back(graph.addArc(s, lNodes.back())); + cost[lArcs.back()] = 0.0; + capLo[lArcs.back()] = 0.0; + capHi[lArcs.back()] = 1.0; + } + + //Add right nodes (sites) and arc between right nodes and target node + for (int j=0; j < num_sites; ++j) + { + rNodes.emplace_back(graph.addNode()); + rArcs.emplace_back(graph.addArc(rNodes.back(), t)); + cost[rArcs.back()] = 0.0; + capLo[rArcs.back()] = 0.0; + capHi[rArcs.back()] = 1.0; + } + + //To improve efficiency, we do not run matching for complete bipartite graph but incrementally add arcs when needed + double distMin = 0.0; + double distMax = lg_max_dist_init; + + while (true) + { + //Generate arcs between left (blocks) and right (sites) nodes, pruning based on distance + for (int blk = 0; blk < num_nodes; ++blk) + { + for (int st = 0; st < num_sites; ++st) + { + double dist = std::abs(locX.at(blk) - sites.at(st*2)) + std::abs(locY.at(blk) - sites.at(st*2+1)); + if (dist >= distMin && dist < distMax) + { + mArcs.emplace_back(graph.addArc(lNodes[blk], rNodes[st])); + mArcPairs.emplace_back(blk, st); + double mArcCost = dist * precond.at(blk) * lg_flow_cost_scale; + cost[mArcs.back()] = mArcCost; + capLo[mArcs.back()] = 0.0; + capHi[mArcs.back()] = 1.0; + } + } + } + + //Run min-cost flow + lemon::NetworkSimplex mcf(graph); + mcf.stSupply(s, t, num_nodes); + mcf.lowerMap(capLo).upperMap(capHi).costMap(cost); + mcf.run(); + + //A feasible solution must have flow size equal to the no of blocks + //If not, we need to increase the max distance constraint + double flowSize = 0.0; + for (const auto &arc : rArcs) + { + flowSize += mcf.flow(arc); + } + if (flowSize != num_nodes) + { + //Increase searching range + distMin = distMax; + distMax += lg_max_dist_incr; + continue; + } + + double maxMov = 0; + double avgMov = 0; + //If the execution hits here, we found a feasible solution + for (int i = 0; i < mArcs.size(); ++i) + { + if (mcf.flow(mArcs[i])) + { + const auto &p = mArcPairs[i]; + double mov = std::abs(locX.at(p.first) - sites.at(p.second*2)) + std::abs(locY.at(p.first) - sites.at(p.second*2+1)); + avgMov += mov; + maxMov = std::max(maxMov, mov); + out[p.first] = sites.at(p.second*2); + out[num_nodes+p.first] = sites.at(p.second*2+1); + } + } + if (num_nodes) + { + avgMov /= num_nodes; + } + movVal[0] = maxMov; + movVal[1] = avgMov; + return; + } +} + +//Legalize Macro +void legalizeMacro( + at::Tensor pos, + at::Tensor site_xy, + at::Tensor macro2node_map, + at::Tensor macro_site_height, + at::Tensor macro_element_count, + at::Tensor spiral_accessor, + at::Tensor site2addr_map, + at::Tensor site_types, + int spiralBegin, + int spiralEnd, + int num_macros, + int num_sites_x, + int num_sites_y, + int slice_minX, + int slice_maxX, + int slice_minY, + int slice_maxY, + int sliceId, + int SIG_IDX, + int SLICE_CAPACITY, + at::Tensor dist_moved, + at::Tensor site_det_score, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_next_bestScoreImprov, + at::Tensor site_det_siteId, + at::Tensor site_det_sig, + at::Tensor site_det_sig_idx, + at::Tensor site_det_impl_lut, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestSite, + at::Tensor sites_with_special_nodes) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + CHECK_FLAT(spiral_accessor); + CHECK_CONTIGUOUS(spiral_accessor); + + CHECK_FLAT(site_types); + CHECK_CONTIGUOUS(site_types); + + CHECK_FLAT(macro2node_map); + CHECK_CONTIGUOUS(macro2node_map); + + int numNodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMacrosLauncher", [&] { + legalizeMacrosLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(macro2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(macro_site_height, int), + DREAMPLACE_TENSOR_DATA_PTR(macro_element_count, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + spiralBegin, spiralEnd, num_macros, num_sites_x, + num_sites_y, slice_minX, slice_maxX, slice_minY, + slice_maxY, sliceId, SIG_IDX, SLICE_CAPACITY, + DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int)); + }); + //std::cout << "Completed legalizeMacro" << std::endl; +} + +//Legalize Mlab +void legalizeMlab( + at::Tensor pos, + at::Tensor site_xy, + at::Tensor mlab_locX, + at::Tensor mlab_locY, + at::Tensor mlab_indices, + at::Tensor site2addr_map, + int num_mlab_nodes, + int num_sites_y, + int SIG_IDX, + int SLICE_CAPACITY, + at::Tensor dist_moved, + at::Tensor site_det_score, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_next_bestScoreImprov, + at::Tensor sites_with_special_nodes, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestSite, + at::Tensor site_det_siteId, + at::Tensor site_det_sig, + at::Tensor site_det_sig_idx, + at::Tensor site_det_impl_lut, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(mlab_locX); + CHECK_CONTIGUOUS(mlab_locX); + CHECK_FLAT(mlab_locY); + CHECK_CONTIGUOUS(mlab_locY); + + CHECK_FLAT(mlab_indices); + CHECK_CONTIGUOUS(mlab_indices); + + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + int numNodes = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMlabsLauncher", [&] { + legalizeMlabsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_locX, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_locY, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_indices, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + num_mlab_nodes, num_sites_y, + SIG_IDX, SLICE_CAPACITY, + DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), + num_threads); + }); + //std::cout << "Completed legalizeMlab" << std::endl; +} + +//runDLIteration +void runDLIter( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor net_bbox, + at::Tensor net_pinIdArrayX, + at::Tensor net_pinIdArrayY, + at::Tensor site_xy, + at::Tensor node2fence_region_map, + at::Tensor flop_ctrlSets, + at::Tensor extended_ctrlSets, + at::Tensor ext_ctrlSet_start_map, + at::Tensor flop2ctrlSetId_map, + at::Tensor lut_type, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor node2outpinIdx_map, + at::Tensor node2pincount, + at::Tensor net2pincount, + at::Tensor pin2net_map, + at::Tensor pin_typeIds, + at::Tensor flat_net2pin_start_map, + at::Tensor pin2node_map, + at::Tensor flat_node2prclstrCount, + at::Tensor flat_node2precluster_map, + at::Tensor is_mlab_node, + at::Tensor is_mlab_site, + at::Tensor site_nbrList, + at::Tensor site_nbrRanges, + at::Tensor site_nbrRanges_idx, + at::Tensor sorted_node_map, + at::Tensor sorted_net_map, + at::Tensor net_weights, + at::Tensor addr2site_map, + at::Tensor special_nodes, + int num_sites_x, + int num_sites_y, + int num_clb_sites, + int minStableIter, + int maxList, + int half_ctrl_mode, + int SLICE_CAPACITY, + int HALF_SLICE_CAPACITY, + int BLE_CAPACITY, + int NUM_BLE_PER_SLICE, + int minNeighbors, + int numGroups, + int netShareScoreMaxNetDegree, + int wlScoreMaxNetDegree, + int lutTypeInSliceUnit, + int lut_maxShared, + double xWirelenWt, + double yWirelenWt, + double wirelenImprovWt, + double extNetCountWt, + int CKSR_IN_CLB, + int CE_IN_CLB, + int SCL_IDX, + int PQ_IDX, + int SIG_IDX, + int lutId, + int ffId, + int num_nodes, + int num_threads, + at::Tensor site_nbr_idx, + at::Tensor site_nbr, + at::Tensor site_nbrGroup_idx, + at::Tensor site_curr_pq_top_idx, + at::Tensor site_curr_pq_sig_idx, + at::Tensor site_curr_pq_sig, + at::Tensor site_curr_pq_idx, + at::Tensor site_curr_pq_validIdx, + at::Tensor site_curr_stable, + at::Tensor site_curr_pq_siteId, + at::Tensor site_curr_pq_score, + at::Tensor site_curr_pq_impl_lut, + at::Tensor site_curr_pq_impl_ff, + at::Tensor site_curr_pq_impl_cksr, + at::Tensor site_curr_pq_impl_ce, + at::Tensor site_curr_scl_score, + at::Tensor site_curr_scl_siteId, + at::Tensor site_curr_scl_idx, + at::Tensor site_curr_scl_validIdx, + at::Tensor site_curr_scl_sig_idx, + at::Tensor site_curr_scl_sig, + at::Tensor site_curr_scl_impl_lut, + at::Tensor site_curr_scl_impl_ff, + at::Tensor site_curr_scl_impl_cksr, + at::Tensor site_curr_scl_impl_ce, + at::Tensor site_next_pq_idx, + at::Tensor site_next_pq_validIdx, + at::Tensor site_next_pq_top_idx, + at::Tensor site_next_pq_score, + at::Tensor site_next_pq_siteId, + at::Tensor site_next_pq_sig_idx, + at::Tensor site_next_pq_sig, + at::Tensor site_next_pq_impl_lut, + at::Tensor site_next_pq_impl_ff, + at::Tensor site_next_pq_impl_cksr, + at::Tensor site_next_pq_impl_ce, + at::Tensor site_next_scl_score, + at::Tensor site_next_scl_siteId, + at::Tensor site_next_scl_idx, + at::Tensor site_next_scl_validIdx, + at::Tensor site_next_scl_sig_idx, + at::Tensor site_next_scl_sig, + at::Tensor site_next_scl_impl_lut, + at::Tensor site_next_scl_impl_ff, + at::Tensor site_next_scl_impl_cksr, + at::Tensor site_next_scl_impl_ce, + at::Tensor site_next_stable, + at::Tensor site_det_score, + at::Tensor site_det_siteId, + at::Tensor site_det_sig_idx, + at::Tensor site_det_sig, + at::Tensor site_det_impl_lut, + at::Tensor site_det_impl_ff, + at::Tensor site_det_impl_cksr, + at::Tensor site_det_impl_ce, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestScoreImprov, + at::Tensor inst_next_bestSite, + at::Tensor activeStatus, + at::Tensor illegalStatus + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + CHECK_FLAT(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + CHECK_FLAT(net_bbox); + CHECK_CONTIGUOUS(net_bbox); + + CHECK_FLAT(net_pinIdArrayX); + CHECK_CONTIGUOUS(net_pinIdArrayX); + CHECK_FLAT(net_pinIdArrayY); + CHECK_CONTIGUOUS(net_pinIdArrayY); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + + CHECK_FLAT(flop_ctrlSets); + CHECK_CONTIGUOUS(flop_ctrlSets); + CHECK_FLAT(flop2ctrlSetId_map); + CHECK_CONTIGUOUS(flop2ctrlSetId_map); + + CHECK_FLAT(lut_type); + CHECK_CONTIGUOUS(lut_type); + + CHECK_FLAT(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + + CHECK_FLAT(node2pincount); + CHECK_CONTIGUOUS(node2pincount); + + CHECK_FLAT(net2pincount); + CHECK_CONTIGUOUS(net2pincount); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(pin_typeIds); + CHECK_CONTIGUOUS(pin_typeIds); + + CHECK_FLAT(flat_net2pin_start_map); + CHECK_CONTIGUOUS(flat_net2pin_start_map); + + CHECK_FLAT(pin2node_map); + CHECK_CONTIGUOUS(pin2node_map); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + int numNodes = pos.numel() / 2; + + //Run DL Iteration + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "runDLIteration", [&] { + runDLIteration( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_site, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), + num_clb_sites, minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, + minNeighbors, numGroups, netShareScoreMaxNetDegree, + wlScoreMaxNetDegree, lutTypeInSliceUnit, lut_maxShared, + xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, + CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, + lutId, ffId, num_threads, + DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int)); + }); + + //std::cout << "Run DL Iter "<< std::endl; + + //Run DL Sync + DREAMPLACE_DISPATCH_FLOATING_TYPES(site_curr_pq_score, "runDLSynchronize", [&] { + runDLSynchronize( + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + num_clb_sites, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, + SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, num_nodes, num_threads, + DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(activeStatus, int), + DREAMPLACE_TENSOR_DATA_PTR(illegalStatus, int)); + }); + + //std::cout << "Run DL Sync: " << DLStatus << std::endl; +} + +//RipUp & Greedy Legalization +void ripUp_SlotAssign( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor net_weights, + at::Tensor net_bbox, + at::Tensor inst_areas, + at::Tensor wlPrecond, + at::Tensor site_xy, + at::Tensor net_pinIdArrayX, + at::Tensor net_pinIdArrayY, + at::Tensor spiral_accessor, + at::Tensor node2fence_region_map, + at::Tensor lut_type, + at::Tensor site_types, + at::Tensor node2pincount, + at::Tensor net2pincount, + at::Tensor pin2net_map, + at::Tensor pin2node_map, + at::Tensor pin_typeIds, + at::Tensor flop2ctrlSetId_map, + at::Tensor flop_ctrlSets, + at::Tensor extended_ctrlSets, + at::Tensor ext_ctrlSet_start_map, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_net2pin_start_map, + at::Tensor flat_node2prclstrCount, + at::Tensor flat_node2precluster_map, + at::Tensor sorted_node_map, + at::Tensor sorted_node_idx, + at::Tensor sorted_net_map, + at::Tensor node2outpinIdx_map, + at::Tensor flat_net2pin_map, + at::Tensor addr2site_map, + at::Tensor site2addr_map, + at::Tensor sites_with_special_nodes, + at::Tensor special_nodes, + double nbrDistEnd, + double xWirelenWt, + double yWirelenWt, + double extNetCountWt, + double wirelenImprovWt, + double slotAssignFlowWeightScale, + double slotAssignFlowWeightIncr, + int lutTypeInSliceUnit, + int lut_maxShared, + int num_nodes, + int num_sites_x, + int num_sites_y, + int num_clb_sites, + int spiralBegin, + int spiralEnd, + int half_ctrl_mode, + int CKSR_IN_CLB, + int CE_IN_CLB, + int SLICE_CAPACITY, + int HALF_SLICE_CAPACITY, + int BLE_CAPACITY, + int NUM_BLE_PER_SLICE, + int NUM_BLE_PER_HALF_SLICE, + int netShareScoreMaxNetDegree, + int wlScoreMaxNetDegree, + int ripupExpansion, + int greedyExpansion, + int SIG_IDX, + int lutId, + int ffId, + int sliceId, + int num_threads, + at::Tensor inst_curr_detSite, + at::Tensor site_det_sig_idx, + at::Tensor site_det_sig, + at::Tensor site_det_impl_lut, + at::Tensor site_det_impl_ff, + at::Tensor site_det_impl_cksr, + at::Tensor site_det_impl_ce, + at::Tensor site_det_siteId, + at::Tensor site_det_score, + at::Tensor node_x, + at::Tensor node_y, + at::Tensor node_z + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + CHECK_FLAT(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + CHECK_FLAT(net_bbox); + CHECK_CONTIGUOUS(net_bbox); + + CHECK_FLAT(inst_areas); + CHECK_CONTIGUOUS(inst_areas); + + CHECK_FLAT(wlPrecond); + CHECK_CONTIGUOUS(wlPrecond); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(net_pinIdArrayX); + CHECK_CONTIGUOUS(net_pinIdArrayX); + CHECK_FLAT(net_pinIdArrayY); + CHECK_CONTIGUOUS(net_pinIdArrayY); + + CHECK_FLAT(spiral_accessor); + CHECK_CONTIGUOUS(spiral_accessor); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + + CHECK_FLAT(lut_type); + CHECK_CONTIGUOUS(lut_type); + + CHECK_FLAT(site_types); + CHECK_CONTIGUOUS(site_types); + + CHECK_FLAT(node2pincount); + CHECK_CONTIGUOUS(node2pincount); + + CHECK_FLAT(net2pincount); + CHECK_CONTIGUOUS(net2pincount); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + CHECK_FLAT(pin2node_map); + CHECK_CONTIGUOUS(pin2node_map); + + CHECK_FLAT(pin_typeIds); + CHECK_CONTIGUOUS(pin_typeIds); + + CHECK_FLAT(flop2ctrlSetId_map); + CHECK_CONTIGUOUS(flop2ctrlSetId_map); + CHECK_FLAT(flop_ctrlSets); + CHECK_CONTIGUOUS(flop_ctrlSets); + + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + CHECK_FLAT(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + + CHECK_FLAT(flat_net2pin_start_map); + CHECK_CONTIGUOUS(flat_net2pin_start_map); + CHECK_FLAT(flat_net2pin_map); + CHECK_CONTIGUOUS(flat_net2pin_map); + + CHECK_FLAT(flat_node2prclstrCount); + CHECK_CONTIGUOUS(flat_node2prclstrCount); + CHECK_FLAT(flat_node2precluster_map); + CHECK_CONTIGUOUS(flat_node2precluster_map); + + CHECK_FLAT(sorted_node_map); + CHECK_CONTIGUOUS(sorted_node_map); + CHECK_FLAT(sorted_node_idx); + CHECK_CONTIGUOUS(sorted_node_idx); + + CHECK_FLAT(sorted_net_map); + CHECK_CONTIGUOUS(sorted_net_map); + + int numNodes = pos.numel() / 2; + + ////DBG + //CPUTimer::hr_clock_rep timer_start, timer_stop; + //timer_start = CPUTimer::getGlobaltime(); + ////DBG + + //RipUp & Greedy Legalization + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "ripUp_Greedy_LG", [&] { + ripUp_Greedy_LG( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_areas, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), + DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), + DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), + nbrDistEnd, xWirelenWt, yWirelenWt, extNetCountWt, + wirelenImprovWt, lutTypeInSliceUnit, lut_maxShared, + num_nodes, num_sites_x, num_sites_y, num_clb_sites, + spiralBegin, spiralEnd, half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, + SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, netShareScoreMaxNetDegree, + wlScoreMaxNetDegree, ripupExpansion, greedyExpansion, + SIG_IDX, lutId, ffId, sliceId, + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t)); + }); + + ////DBG + //timer_stop = CPUTimer::getGlobaltime(); + //dreamplacePrint(kINFO, "RipUp & Greedy LG takes %.4f s\n", + // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()/1000.0); + //timer_start = CPUTimer::getGlobaltime(); + ////DBG + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "slotAssign", [&] { + slotAssign( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), + slotAssignFlowWeightScale, slotAssignFlowWeightIncr, + num_sites_x, num_sites_y, num_clb_sites, lutTypeInSliceUnit, + lut_maxShared, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, + NUM_BLE_PER_HALF_SLICE, half_ctrl_mode, lutId, num_threads, + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int)); + }); + + ////DBG + //timer_stop = CPUTimer::getGlobaltime(); + //dreamplacePrint(kINFO, "Slot Assign takes %g ms\n", + // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); + //timer_start = CPUTimer::getGlobaltime(); + ////DBG + + //Cache Solution + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "cacheSolution", [&] { + cacheSolution( + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + num_sites_y, num_clb_sites, SLICE_CAPACITY, + DREAMPLACE_TENSOR_DATA_PTR(node_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_z, int)); + }); + + ////DBG + //timer_stop = CPUTimer::getGlobaltime(); + //dreamplacePrint(kINFO, "Cache Solution takes %g ms\n", + // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); + ////DBG +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("initializeLG", &DREAMPLACE_NAMESPACE::initializeLG, "Initialize LUT/FF legalization to handle carry-chains and mlabs"); + m.def("minCostFlow", &DREAMPLACE_NAMESPACE::minCostFlow, "Min Cost flow to get site locations"); + m.def("legalizeCarryChain", &DREAMPLACE_NAMESPACE::legalizeCarryChain, "Legalize carry-chains"); + m.def("legalizeMacro", &DREAMPLACE_NAMESPACE::legalizeMacro, "Legalize Slice Macros"); + m.def("legalizeMlab", &DREAMPLACE_NAMESPACE::legalizeMlab, "Legalize Mlabs"); + m.def("initSiteNbrs", &DREAMPLACE_NAMESPACE::initSiteNbrs, "Initialize site neighbors"); + m.def("runDLIter", &DREAMPLACE_NAMESPACE::runDLIter, "Run DL Iteration"); + m.def("ripUp_SlotAssign", &DREAMPLACE_NAMESPACE::ripUp_SlotAssign, "Run RipUp and Greedy Legalization and Slot Assign"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp new file mode 100644 index 00000000..93fc1ed2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp @@ -0,0 +1,760 @@ +/** + * @file lut_ff_legalization_cuda.cpp + * @author Rachel Selina + * @date Mar 2021 (DREAMPlaceFPGA-PL) + * @brief Legalize LUT/FF + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE +// Initialize +template +int initLGCudaLauncher( + const T *pos_x, + const T *pos_y, + const T *pin_offset_x, + const T *pin_offset_y, + const int *sorted_node_map, + const int *sorted_node_idx, + const int *sorted_net_idx, + const int *flat_net2pin_map, + const int *flat_net2pin_start_map, + const int *flop2ctrlSetId_map, + const int *flop_ctrlSets, + const int *node2fence_region_map, + const int *node2outpinIdx_map, + const int *pin2net_map, + const int *pin2node_map, + const int *pin_typeIds, + const int *net2pincount, + const int *is_mlab_node, + const T preClusteringMaxDist, + const int ffId, + const int lutId, + const int num_nets, + const int num_nodes, + const int wlscoreMaxNetDegree, + T *net_bbox, + int *net_pinIdArrayX, + int *net_pinIdArrayY, + int *flat_node2precluster_map, + int *flat_node2prclstrCount); + +//runDLIter +template +int runDLCudaLauncher( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_bbox, + const T* site_xy, + const T* net_weights, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* site_types, + const int* spiral_accessor, + const int* node2fence_region_map, + const int* lut_flop_indices, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* lut_type, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* node2pincount, + const int* net2pincount, + const int* pin2net_map, + const int* pin_typeIds, + const int* flat_net2pin_start_map, + const int* pin2node_map, + const int* sorted_net_map, + const int* sorted_node_map, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* is_mlab_node, + const int* site_nbrList, + const int* site_nbrRanges, + const int* site_nbrRanges_idx, + const int* addr2site_map, + const int* site2addr_map, + const int* special_nodes, + const T maxDist, + const T xWirelenWt, + const T yWirelenWt, + const T wirelenImprovWt, + const T extNetCountWt, + const int num_sites_x, + const int num_sites_y, + const int num_clb_sites, + const int num_lutflops, + const int minStableIter, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int minNeighbors, + const int spiralBegin, + const int spiralEnd, + const int intMinVal, + const int numGroups, + const int netShareScoreMaxNetDegree, + const int wlscoreMaxNetDegree, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + const int sliceId, + int* site_nbr_idx, + int* site_nbr, + int* site_nbrGroup_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + int* site_curr_pq_validIdx, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* cumsum_curr_scl, + int* site_curr_scl_validIdx, + int* validIndices_curr_scl, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + T* site_det_score, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* inst_curr_detSite, + T* inst_curr_bestScoreImprov, + int* inst_curr_bestSite, + int* inst_next_detSite, + T* inst_next_bestScoreImprov, + int* inst_next_bestSite, + int* activeStatus, + int* illegalStatus, + int* inst_score_improv, + int* site_score_improv, + int* sorted_clb_siteIds); + +//legalize Mlab +template +int legalizeMlabCudaLauncher( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const T* mlab_locX, + const T* mlab_locY, + const int* mlab_indices, + const int* site2addr_map, + const int num_mlab_nodes, + const int num_sites_y, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* dist_moved, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +void initLegalization( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor sorted_net_idx, + at::Tensor sorted_node_map, + at::Tensor sorted_node_idx, + at::Tensor flat_net2pin_map, + at::Tensor flat_net2pin_start_map, + at::Tensor flop2ctrlSetId_map, + at::Tensor flop_ctrlSets, + at::Tensor node2fence_region_map, + at::Tensor node2outpinIdx_map, + at::Tensor pin2net_map, + at::Tensor pin2node_map, + at::Tensor pin_typeIds, + at::Tensor net2pincount, + at::Tensor is_mlab_node, + double preClusteringMaxDist, + int num_nets, + int num_nodes, + int lutId, + int ffId, + int wlscoreMaxNetDegree, + at::Tensor net_bbox, + at::Tensor net_pinIdArrayX, + at::Tensor net_pinIdArrayY, + at::Tensor flat_node2precluster_map, + at::Tensor flat_node2prclstrCount) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + CHECK_FLAT(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + CHECK_FLAT(sorted_net_idx); + CHECK_CONTIGUOUS(sorted_net_idx); + + CHECK_FLAT(sorted_node_map); + CHECK_CONTIGUOUS(sorted_node_map); + CHECK_FLAT(sorted_node_idx); + CHECK_CONTIGUOUS(sorted_node_idx); + + CHECK_FLAT(flat_net2pin_map); + CHECK_CONTIGUOUS(flat_net2pin_map); + CHECK_FLAT(flat_net2pin_start_map); + CHECK_CONTIGUOUS(flat_net2pin_start_map); + + CHECK_FLAT(flop2ctrlSetId_map); + CHECK_CONTIGUOUS(flop2ctrlSetId_map); + CHECK_FLAT(flop_ctrlSets); + CHECK_CONTIGUOUS(flop_ctrlSets); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + CHECK_FLAT(node2outpinIdx_map); + CHECK_CONTIGUOUS(node2outpinIdx_map); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(pin2node_map); + CHECK_CONTIGUOUS(pin2node_map); + CHECK_FLAT(pin_typeIds); + CHECK_CONTIGUOUS(pin_typeIds); + + CHECK_FLAT(net2pincount); + CHECK_CONTIGUOUS(net2pincount); + + int numNodes = pos.numel() / 2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initLGCudaLauncher", [&] { + initLGCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), + preClusteringMaxDist, ffId, lutId, + num_nets, num_nodes, wlscoreMaxNetDegree, + DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int)); + }); + + +} + +//RunDLIteration +void runDLIter( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor net_bbox, + at::Tensor site_xy, + at::Tensor net_weights, + at::Tensor net_pinIdArrayX, + at::Tensor net_pinIdArrayY, + at::Tensor site_types, + at::Tensor spiral_accessor, + at::Tensor node2fence_region_map, + at::Tensor lut_flop_indices, + at::Tensor flop2ctrlSetId_map, + at::Tensor flop_ctrlSets, + at::Tensor extended_ctrlSets, + at::Tensor ext_ctrlSet_start_map, + at::Tensor lut_type, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor node2outpinIdx_map, + at::Tensor node2pincount, + at::Tensor net2pincount, + at::Tensor pin2net_map, + at::Tensor pin_typeIds, + at::Tensor flat_net2pin_start_map, + at::Tensor pin2node_map, + at::Tensor sorted_net_map, + at::Tensor sorted_node_map, + at::Tensor flat_node2prclstrCount, + at::Tensor flat_node2precluster_map, + at::Tensor is_mlab_node, + at::Tensor site_nbrList, + at::Tensor site_nbrRanges, + at::Tensor site_nbrRanges_idx, + at::Tensor addr2site_map, + at::Tensor site2addr_map, + at::Tensor special_nodes, + double maxDist, + double xWirelenWt, + double yWirelenWt, + double wirelenImprovWt, + double extNetCountWt, + int num_sites_x, + int num_sites_y, + int num_clb_sites, + int num_lutflops, + int minStableIter, + int maxList, + int half_ctrl_mode, + int SLICE_CAPACITY, + int HALF_SLICE_CAPACITY, + int BLE_CAPACITY, + int NUM_BLE_PER_SLICE, + int minNeighbors, + int spiralBegin, + int spiralEnd, + int intMinVal, + int numGroups, + int netShareScoreMaxNetDegree, + int wlscoreMaxNetDegree, + int lutTypeInSliceUnit, + int lut_maxShared, + int CKSR_IN_CLB, + int CE_IN_CLB, + int SCL_IDX, + int PQ_IDX, + int SIG_IDX, + int lutId, + int ffId, + int sliceId, + at::Tensor site_nbr_idx, + at::Tensor site_nbr, + at::Tensor site_nbrGroup_idx, + at::Tensor site_curr_pq_top_idx, + at::Tensor site_curr_pq_sig_idx, + at::Tensor site_curr_pq_sig, + at::Tensor site_curr_pq_idx, + at::Tensor site_curr_stable, + at::Tensor site_curr_pq_siteId, + at::Tensor site_curr_pq_validIdx, + at::Tensor site_curr_pq_score, + at::Tensor site_curr_pq_impl_lut, + at::Tensor site_curr_pq_impl_ff, + at::Tensor site_curr_pq_impl_cksr, + at::Tensor site_curr_pq_impl_ce, + at::Tensor site_curr_scl_score, + at::Tensor site_curr_scl_siteId, + at::Tensor site_curr_scl_idx, + at::Tensor cumsum_curr_scl, + at::Tensor site_curr_scl_validIdx, + at::Tensor validIndices_curr_scl, + at::Tensor site_curr_scl_sig_idx, + at::Tensor site_curr_scl_sig, + at::Tensor site_curr_scl_impl_lut, + at::Tensor site_curr_scl_impl_ff, + at::Tensor site_curr_scl_impl_cksr, + at::Tensor site_curr_scl_impl_ce, + at::Tensor site_next_pq_idx, + at::Tensor site_next_pq_validIdx, + at::Tensor site_next_pq_top_idx, + at::Tensor site_next_pq_score, + at::Tensor site_next_pq_siteId, + at::Tensor site_next_pq_sig_idx, + at::Tensor site_next_pq_sig, + at::Tensor site_next_pq_impl_lut, + at::Tensor site_next_pq_impl_ff, + at::Tensor site_next_pq_impl_cksr, + at::Tensor site_next_pq_impl_ce, + at::Tensor site_next_scl_score, + at::Tensor site_next_scl_siteId, + at::Tensor site_next_scl_idx, + at::Tensor site_next_scl_validIdx, + at::Tensor site_next_scl_sig_idx, + at::Tensor site_next_scl_sig, + at::Tensor site_next_scl_impl_lut, + at::Tensor site_next_scl_impl_ff, + at::Tensor site_next_scl_impl_cksr, + at::Tensor site_next_scl_impl_ce, + at::Tensor site_next_stable, + at::Tensor site_det_score, + at::Tensor site_det_siteId, + at::Tensor site_det_sig_idx, + at::Tensor site_det_sig, + at::Tensor site_det_impl_lut, + at::Tensor site_det_impl_ff, + at::Tensor site_det_impl_cksr, + at::Tensor site_det_impl_ce, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestScoreImprov, + at::Tensor inst_next_bestSite, + at::Tensor activeStatus, + at::Tensor illegalStatus, + at::Tensor inst_score_improv, + at::Tensor site_score_improv, + at::Tensor sorted_clb_siteIds) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(pin_offset_x); + CHECK_CONTIGUOUS(pin_offset_x); + CHECK_FLAT(pin_offset_y); + CHECK_CONTIGUOUS(pin_offset_y); + + CHECK_FLAT(net_bbox); + CHECK_CONTIGUOUS(net_bbox); + + CHECK_FLAT(net_pinIdArrayX); + CHECK_CONTIGUOUS(net_pinIdArrayX); + CHECK_FLAT(net_pinIdArrayY); + CHECK_CONTIGUOUS(net_pinIdArrayY); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + CHECK_FLAT(spiral_accessor); + CHECK_CONTIGUOUS(spiral_accessor); + CHECK_FLAT(site_types); + CHECK_CONTIGUOUS(site_types); + + CHECK_FLAT(node2fence_region_map); + CHECK_CONTIGUOUS(node2fence_region_map); + + CHECK_FLAT(lut_flop_indices); + CHECK_CONTIGUOUS(lut_flop_indices); + + CHECK_FLAT(flop_ctrlSets); + CHECK_CONTIGUOUS(flop_ctrlSets); + CHECK_FLAT(flop2ctrlSetId_map); + CHECK_CONTIGUOUS(flop2ctrlSetId_map); + + CHECK_FLAT(lut_type); + CHECK_CONTIGUOUS(lut_type); + + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + CHECK_FLAT(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + + CHECK_FLAT(node2pincount); + CHECK_CONTIGUOUS(node2pincount); + + CHECK_FLAT(net2pincount); + CHECK_CONTIGUOUS(net2pincount); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(pin_typeIds); + CHECK_CONTIGUOUS(pin_typeIds); + + CHECK_FLAT(flat_net2pin_start_map); + CHECK_CONTIGUOUS(flat_net2pin_start_map); + + CHECK_FLAT(pin2node_map); + CHECK_CONTIGUOUS(pin2node_map); + + CHECK_FLAT(sorted_net_map); + CHECK_CONTIGUOUS(sorted_net_map); + CHECK_FLAT(sorted_node_map); + CHECK_CONTIGUOUS(sorted_node_map); + + CHECK_FLAT(flat_node2prclstrCount); + CHECK_CONTIGUOUS(flat_node2prclstrCount); + CHECK_FLAT(flat_node2precluster_map); + CHECK_CONTIGUOUS(flat_node2precluster_map); + + CHECK_FLAT(site_nbrList); + CHECK_CONTIGUOUS(site_nbrList); + CHECK_FLAT(site_nbrRanges); + CHECK_CONTIGUOUS(site_nbrRanges); + CHECK_FLAT(site_nbrRanges_idx); + CHECK_CONTIGUOUS(site_nbrRanges_idx); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + CHECK_FLAT(addr2site_map); + CHECK_CONTIGUOUS(addr2site_map); + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + int numNodes = pos.numel() / 2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "runDLCudaLauncher", [&] { + runDLCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), + DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), + DREAMPLACE_TENSOR_DATA_PTR(site_types, int), + DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), + DREAMPLACE_TENSOR_DATA_PTR(lut_flop_indices, int), + DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), + DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), + DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), + DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), + maxDist, xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, + num_sites_x, num_sites_y, num_clb_sites, num_lutflops, + minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, + spiralBegin, spiralEnd, intMinVal, + numGroups, netShareScoreMaxNetDegree, wlscoreMaxNetDegree, + lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, + CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, sliceId, + DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(cumsum_curr_scl, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(validIndices_curr_scl, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(activeStatus, int), + DREAMPLACE_TENSOR_DATA_PTR(illegalStatus, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_score_improv, int), + DREAMPLACE_TENSOR_DATA_PTR(site_score_improv, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_clb_siteIds, int) + ); + }); + //std::cout << "Run DL Iter "<< std::endl; +} + +//Legalize Mlabs +void legalizeMlab( + at::Tensor pos, + at::Tensor site_xy, + at::Tensor mlab_locX, + at::Tensor mlab_locY, + at::Tensor mlab_indices, + at::Tensor site2addr_map, + int num_mlab_nodes, + int num_sites_y, + int SIG_IDX, + int SLICE_CAPACITY, + at::Tensor dist_moved, + at::Tensor site_det_score, + at::Tensor inst_curr_bestScoreImprov, + at::Tensor inst_next_bestScoreImprov, + at::Tensor site_det_siteId, + at::Tensor site_det_sig_idx, + at::Tensor site_det_sig, + at::Tensor site_det_impl_lut, + at::Tensor inst_curr_detSite, + at::Tensor inst_curr_bestSite, + at::Tensor inst_next_detSite, + at::Tensor inst_next_bestSite, + at::Tensor sites_with_special_nodes) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(site_xy); + CHECK_CONTIGUOUS(site_xy); + + CHECK_FLAT(mlab_locX); + CHECK_CONTIGUOUS(mlab_locX); + CHECK_FLAT(mlab_locY); + CHECK_CONTIGUOUS(mlab_locY); + + CHECK_FLAT(mlab_indices); + CHECK_CONTIGUOUS(mlab_indices); + + CHECK_FLAT(site2addr_map); + CHECK_CONTIGUOUS(site2addr_map); + + int numNodes = pos.numel() / 2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMlabCudaLauncher", [&] { + legalizeMlabCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, + DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_locX, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_locY, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(mlab_indices, int), + DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), + num_mlab_nodes, num_sites_y, SIG_IDX, SLICE_CAPACITY, + DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), + DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), + DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), + DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int) + ); + }); + //std::cout << "Legalize MLabs"<< std::endl; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("initLegalization", &DREAMPLACE_NAMESPACE::initLegalization, + "initialize LUT/FF legalization (CUDA)"); + m.def("runDLIter", &DREAMPLACE_NAMESPACE::runDLIter, "Run DL Iteration (CUDA)"); + m.def("legalizeMlab", &DREAMPLACE_NAMESPACE::legalizeMlab, "Legalize Mlab (CUDA)"); +} + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu new file mode 100644 index 00000000..011decf9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu @@ -0,0 +1,4322 @@ +/** + * @file lut_ff_legalization_cuda_kernel.cu + * @author Rachel Selina (DREAMPlaceFPGA-PL) + * @date Aug 2023 + * @brief Legalize LUT/FF + */ +#include +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility/src/print.cuh" +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" + +//local dependency +#include "lut_ff_legalization/src/graph_matching.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +#define THREAD_COUNT 64 +#define INVALID -1 +#define INPUT_PIN 1 +//Reset below values if required: Below values are for a maximum SLICE_CAPACITY of 20 +#define SLICE_MAX_CAP 20 +#define SIG_MAX_CAP 40 +#define CE_MAX_CAP 7 +#define CKSR_MAX_CAP 2 + +//Clear entries in candidate +inline __device__ void clear_cand_contents(const int tsPQ, const int SIG_IDX, + const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY, + int* site_sig_idx, int* site_sig, int* site_impl_lut, int* site_impl_ff, + int* site_impl_cksr, int* site_impl_ce) +{ + int topIdx(tsPQ*SIG_IDX); + int lutIdx = tsPQ*SLICE_CAPACITY; + int ckIdx = tsPQ*CKSR_IN_CLB; + int ceIdx = tsPQ*CE_IN_CLB; + + for(int sg = 0; sg < SIG_IDX; ++sg) + { + site_sig[topIdx + sg] = INVALID; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_impl_lut[lutIdx + sg] = INVALID; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_impl_ff[lutIdx + sg] = INVALID; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_impl_cksr[ckIdx + sg] = INVALID; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_impl_ce[ceIdx + sg] = INVALID; + } +} + +//Check if entry exists in array +inline __device__ bool val_in_array( + const int* array, const int arraySize, const int arrayIdx, const int val) +{ + for (int idx = 0; idx < arraySize; ++idx) + { + if (array[arrayIdx+idx] == val) + { + return true; + } + } + return false; +} + +/// define candidate_validity_check +// Candidate is valid if the instance is not commited to a site +inline __device__ bool candidate_validity_check( + const int* is_mlab_node, const int SLICE_CAPACITY, + const int topIdx, const int pqSigIdx, + const int siteId, const int* site_curr_pq_sig, + const int* inst_curr_detSite) +{ + //Check first instance if it is mlab + if (pqSigIdx == 2*SLICE_CAPACITY && + is_mlab_node[site_curr_pq_sig[topIdx]] == 1) + { + int pqInst = site_curr_pq_sig[topIdx]; + if (inst_curr_detSite[pqInst] != INVALID && + inst_curr_detSite[pqInst] != siteId) + { + return false; + } + } else + { + for (int i = 0; i < pqSigIdx; ++i) + { + int pqInst = site_curr_pq_sig[topIdx + i]; + + if (inst_curr_detSite[pqInst] != INVALID && + inst_curr_detSite[pqInst] != siteId) + { + return false; + } + } + } + return true; +} + +////SUBFUCTIONS//// + +//define add flop to candidate +inline __device__ bool add_flop_to_candidate_impl( + const int* node2outpinIdx_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, + const int* flop2ctrlSetId_map, const int* node2fence_region_map, + const int* res_lut, const int lutId, const int ffCKSR, const int ffCE, + const int ffId, const int half_ctrl_mode, const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, const int CKSR_IN_CLB, const int CE_IN_CLB, + const int BLE_CAPACITY, const int lut_maxShared, int* res_ff, int* res_cksr, + int* res_ce) +{ + if (half_ctrl_mode == 1) + { + for (int i = 0; i < CKSR_IN_CLB; ++i) + { + if (res_cksr[i] != INVALID && + res_cksr[i] != ffCKSR) + { + continue; + } + + for (int j = 0; j < CKSR_IN_CLB; ++j) + { + int ceIdx = CKSR_IN_CLB*i + j; + if (res_ce[ceIdx] != INVALID && + res_ce[ceIdx] != ffCE) + { + continue; + } + + int beg = i*HALF_SLICE_CAPACITY+j; + int end = beg + HALF_SLICE_CAPACITY; + for (int k = beg; k < end; k += BLE_CAPACITY) + { + if (res_ff[k] == INVALID) + { + res_ff[k] = ffId; + res_cksr[i] = ffCKSR; + res_ce[ceIdx] = ffCE; + return true; + } + } + } + } + } else + { + //FF Ctrls are SHARED across the SLICE + int ckID = INVALID; + for (int i = 0; i < CKSR_IN_CLB; ++i) + { + if (res_cksr[i] == ffCKSR || res_cksr[i] == INVALID) + { + ckID = i; + break; + } + } + + if (ckID != INVALID) + { + int fCtrlId = flop2ctrlSetId_map[ffId]; + int fCStartId = ext_ctrlSet_start_map[fCtrlId]; + int fCEndId = ext_ctrlSet_start_map[fCtrlId+1]; + + int upd_ctrls[SLICE_MAX_CAP]; + int num_upd_ctrls(0); + + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + if (res_ce[sg] != INVALID) + { + upd_ctrls[num_upd_ctrls] = res_ce[sg]; + ++num_upd_ctrls; + } + } + + int num_init_ctrls = num_upd_ctrls; + + //Ensure all FF ctrls can be accommodated in Slice + for (int j = fCStartId; j < fCEndId; ++j) + { + upd_ctrls[num_upd_ctrls] = extended_ctrlSets[j]; + ++num_upd_ctrls; + } + + if (num_upd_ctrls > num_init_ctrls) + { + sort_array(upd_ctrls, num_upd_ctrls); + remove_duplicates(upd_ctrls, num_upd_ctrls); + } + + if (num_upd_ctrls > CE_IN_CLB) + { + return false; + } + + int fIndex = INVALID; + for (int i = 0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_ff[i] == INVALID) + { + if(subSlice_compatibility(node2outpinIdx_map, + flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, + res_ff, res_lut, lutId, SLICE_CAPACITY, + BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, + i, ffId)) + { + fIndex = i; + break; + } + } else if (res_ff[i+1] == INVALID) + { + if(subSlice_compatibility(node2outpinIdx_map, + flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, + res_ff, res_lut, lutId, SLICE_CAPACITY, + BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, + i+1, ffId)) + { + fIndex = i+1; + break; + } + } + } + + if (fIndex != INVALID) + { + res_ff[fIndex] = ffId; + res_cksr[ckID] = ffCKSR; + + for (int j = 0; j < num_upd_ctrls; ++j) + { + res_ce[j] = upd_ctrls[j]; + } + for (int j = num_upd_ctrls; j < CE_IN_CLB; ++j) + { + res_ce[j] = INVALID; + } + return true; + } + } + } + return false; +} + +// define remove_invalid_neighbor +inline __device__ void remove_invalid_neighbor( + const int sIdx, const int sNbrIdx, int* site_nbr_idx, int* site_nbr) +{ + int temp[1024]; + int tempSize(0); + for (int i = 0; i < site_nbr_idx[sIdx]; ++i) + { + int instId = site_nbr[sNbrIdx + i]; + + if (instId != INVALID) + { + temp[tempSize] = instId; + ++tempSize; + } + } + + for (int j = 0; j < tempSize; ++j) + { + site_nbr[sNbrIdx+j] = temp[j]; + } + for (int j = tempSize; j < site_nbr_idx[sIdx]; ++j) + { + site_nbr[sNbrIdx+j] = INVALID; + } + site_nbr_idx[sIdx] = tempSize; + + //DBG + if (tempSize > 1000) + { + printf("WARN: remove_invalid_neighbor() has tempSize > 1000 for site: %d\n", sIdx); + } + //DBG +} +//addLUTToCandidateImpl +inline __device__ bool add_lut_to_cand_impl( + const int* node2outpinIdx_map, const int* lut_type, + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* pin2net_map, const int* pin_typeIds, const int* node2fence_region_map, + const int* res_ff, const int lutTypeInSliceUnit, const int lutId, + const int lut_maxShared, const int lutInstId, const int SLICE_CAPACITY, + const int BLE_CAPACITY, const int half_ctrl_mode, int* res_lut) +{ + if (half_ctrl_mode == 1) + { + for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + res_lut[i] = lutInstId; + return true; + } + } + for (int i=1; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, lutTypeInSliceUnit, + lut_maxShared, res_lut[i-1], lutInstId)) + { + res_lut[i] = lutInstId; + return true; + } + } + } + } else + { + for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) + { + if (res_lut[i] == INVALID) + { + if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i, lutInstId)) + { + res_lut[i] = lutInstId; + return true; + } + } else if(res_lut[i+1] == INVALID) + { + if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, + SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i+1, lutInstId)) + { + res_lut[i+1] = lutInstId; + return true; + } + } + } + } + return false; +} + +//template +__device__ bool is_inst_in_cand_feasible( + const int* node2outpinIdx_map, const int* node2fence_region_map, + const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* flat_node2prclstrCount, const int* flat_node2precluster_map, + const int* flop2ctrlSetId_map, const int* flop_ctrlSets, + const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, + const int* site_det_impl_lut, const int* site_det_impl_ff, + const int* site_det_impl_cksr, const int* site_det_impl_ce, + const int* special_nodes, const int lutTypeInSliceUnit, const int lut_maxShared, + const int siteId, const int instId, const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, const int CKSR_IN_CLB, const int CE_IN_CLB, + const int lutId, const int ffId, const int half_ctrl_mode) +{ + ////DBG + //int dbgInstId = 326924; + //int dbgSId = 3595; + ////DBG + + int instPcl = instId*3; + + int sdlutId = siteId*SLICE_CAPACITY; + int sdckId = siteId*CKSR_IN_CLB; + int sdceId = siteId*CE_IN_CLB; + + int res_lut[SLICE_MAX_CAP]; + int res_ff[SLICE_MAX_CAP]; + int res_cksr[CKSR_MAX_CAP]; + int res_ce[CE_MAX_CAP]; + + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = site_det_impl_lut[sdlutId + sg]; + res_ff[sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + res_cksr[sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + res_ce[sg] = site_det_impl_ce[sdceId + sg]; + } + ///// + + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: Here for instId %d \n", siteId, instId); + //} + ////DBG + + bool lutFail(false); + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; + int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; + + if (node2fence_region_map[clInstId] == lutId) //LUT + { + if (!lutFail && + !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, + lutTypeInSliceUnit, lutId, lut_maxShared, clInstId, SLICE_CAPACITY, + BLE_CAPACITY, half_ctrl_mode, res_lut)) + { + lutFail = true; + } + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: Check add lut to cand impl for instId %d, lutFail %d \n", siteId, clInstId, lutFail); + //} + ////DBG + + } else if (node2fence_region_map[clInstId] == ffId) //FF + { + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: Check add flop to candidate impl for instId %d \n", siteId, clInstId); + //} + ////DBG + + if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, + ext_ctrlSet_start_map, flop2ctrlSetId_map, node2fence_region_map, + res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], + clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, + res_cksr, res_ce)) + { + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: add flop to candidate impl for instId %d returned False\n", siteId, clInstId); + //} + ////DBG + + return false; + } + } + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: Done for instId %d \n", siteId, clInstId); + //} + ////DBG + } + if (!lutFail) + { + return true; + } + + ////DBG + //if (siteId == dbgSId && instId == dbgInstId) + //{ + // printf("%d is inst in cand feasible: lutFail %d use graph matching\n", siteId, lutFail); + // printf("Contents of res_lut: "); + // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // printf("%d ", res_lut[sg]); + // } + // printf("\n"); + + // printf("Contents of res_lut with lut_type and special_nodes: "); + // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // printf("%d", res_lut[sg]); + // if (res_lut[sg] != INVALID) + // { + // printf(" (%d, %d)", lut_type[res_lut[sg]], special_nodes[res_lut[sg]]); + // } + // printf(", "); + // } + // printf("\n"); + //} + ////DBG + + return fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, pin2net_map, pin_typeIds, + flat_node2pin_start_map, flat_node2pin_map, flat_node2precluster_map, + node2fence_region_map, special_nodes, half_ctrl_mode, lutTypeInSliceUnit, + lut_maxShared, instPcl, flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, + SLICE_CAPACITY, BLE_CAPACITY, lutId, res_lut, res_ff); +} + +inline __device__ bool add_inst_to_cand_impl( + const int* node2outpinIdx_map, const int* lut_type, + const int* flat_node2pin_start_map, const int* flat_node2pin_map, + const int* pin2net_map, const int* pin_typeIds, + const int* flat_node2prclstrCount, const int* flat_node2precluster_map, + const int* flop2ctrlSetId_map, const int* node2fence_region_map, + const int* flop_ctrlSets, const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, const int* special_nodes, + const int lutTypeInSliceUnit, const int lut_maxShared, const int instId, + const int lutId, const int ffId, const int half_ctrl_mode, const int CKSR_IN_CLB, + const int CE_IN_CLB, const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, int* nwCand_lut, + int* nwCand_ff, int* nwCand_cksr, int* nwCand_ce) +{ + int instPcl = instId*3; + + //array instantiation + int res_lut[SLICE_MAX_CAP]; + int res_ff[SLICE_MAX_CAP]; + int res_cksr[CKSR_MAX_CAP]; + int res_ce[CE_MAX_CAP]; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + res_lut[sg] = nwCand_lut[sg]; + res_ff[sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + res_cksr[sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + res_ce[sg] = nwCand_ce[sg]; + } + ///// + //DBG + + bool lutFail(false); + for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) + { + int clInstId = flat_node2precluster_map[instPcl + idx]; + int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; + int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; + + if (node2fence_region_map[clInstId] == lutId) //LUT + { + if (!lutFail && + !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, + lutTypeInSliceUnit, lutId, lut_maxShared, clInstId, SLICE_CAPACITY, + BLE_CAPACITY, half_ctrl_mode, res_lut)) + { + lutFail = true; + } + } else if (node2fence_region_map[clInstId] == ffId) //FF + { + if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, + ext_ctrlSet_start_map, flop2ctrlSetId_map, node2fence_region_map, + res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], + clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, + CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, + res_cksr, res_ce)) + { + return false; + } + } + } + + if (!lutFail) + { + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = res_lut[sg]; + nwCand_ff[sg] = res_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = res_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = res_ce[sg]; + } + + return true; + } + + if(fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, pin2net_map, + pin_typeIds, flat_node2pin_start_map, flat_node2pin_map, + flat_node2precluster_map, node2fence_region_map, special_nodes, + half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, instPcl, + flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, SLICE_CAPACITY, + BLE_CAPACITY, lutId, res_lut, res_ff)) + { + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = res_lut[sg]; + nwCand_ff[sg] = res_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = res_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = res_ce[sg]; + } + + return true; + } + return false; +} + +//template +inline __device__ void remove_incompatible_neighbors( + const int* node2outpinIdx_map, const int* node2fence_region_map, + const int* lut_type, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, + const int* flat_node2prclstrCount, const int* flat_node2precluster_map, + const int* flop2ctrlSetId_map, const int* flop_ctrlSets, + const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, + const int* site_det_impl_lut, const int* site_det_impl_ff, + const int* site_det_impl_cksr, const int* site_det_impl_ce, + const int* site_det_sig, const int* site_det_sig_idx, const int* special_nodes, + const int lutTypeInSliceUnit, const int lut_maxShared, const int siteId, + const int sNbrIdx, const int half_ctrl_mode, const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, const int SIG_IDX, const int CKSR_IN_CLB, + const int CE_IN_CLB, const int lutId, const int ffId, int* site_nbr_idx, + int* site_nbr) +{ + ////DBG + //int dbgSId = 3595; + ////DBG + + int sdtopId = siteId*SIG_IDX; + + ////DBG + //if (siteId == dbgSId) + //{ + // int sdtopId = siteId*SIG_IDX; + // int sdlutId = siteId*SLICE_CAPACITY; + // int sdckId = siteId*CKSR_IN_CLB; + // int sdceId = siteId*CE_IN_CLB; + // printf("%d remove incompatible neighbors for total neighbor instances of %d :", siteId, site_nbr_idx[siteId]); + + // for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) + // { + // printf("%d ", site_nbr[sNbrIdx + nbrId]); + // } + // printf("\n"); + + // ////// + // printf("There are %d elements in site_det_sig: ", site_det_sig_idx[siteId]); + // for(int sg = 0; sg < site_det_sig_idx[siteId]; ++sg) + // { + // printf("%d ",site_det_sig[sdtopId + sg]); + // } + // printf("\n"); + // printf("LUTs in site_det_impl_lut: "); + // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // printf("%d ", site_det_impl_lut[sdlutId + sg]); + // } + // printf("\n"); + // printf("FFs in site_det_impl_ff: "); + // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + // { + // printf("%d ", site_det_impl_ff[sdlutId + sg]); + // } + // printf("\n"); + // printf("FFs CKSR: "); + // for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + // { + // printf("%d ", site_det_impl_cksr[sdckId + sg]); + // } + // printf("\n"); + // printf("FFs CE: "); + // for(int sg = 0; sg < CE_IN_CLB; ++sg) + // { + // printf("%d ", site_det_impl_ce[sdceId + sg]); + // } + // printf("\n"); + // ////// + //} + ////DBG + + for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) + { + int instId = site_nbr[sNbrIdx + nbrId]; + + ////DBG + //if (siteId == dbgSId) + //{ + // printf("%d remove incompatible neighbors %d of %d: Consider instId %d of type %d", + // siteId, nbrId, site_nbr_idx[siteId], instId, node2fence_region_map[instId]); + // if (node2fence_region_map[instId] == 1) + // { + // int clInstCKSR = flop2ctrlSetId_map[instId]*3 + 1; + // printf(" has clk %d and ctrls: ", flop_ctrlSets[clInstCKSR]); + // int cStartId = ext_ctrlSet_start_map[flop2ctrlSetId_map[instId]]; + // int cEndId = ext_ctrlSet_start_map[flop2ctrlSetId_map[instId]+1]; + // for (int ctrlId = cStartId; ctrlId < cEndId; ++ctrlId) + // { + // printf("%d ", extended_ctrlSets[ctrlId]); + // } + // } + // printf("\n"); + //} + ////DBG + + if (val_in_array(site_det_sig, site_det_sig_idx[siteId], sdtopId, instId) || + !is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, special_nodes, + lutTypeInSliceUnit, lut_maxShared, siteId, instId, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, + CE_IN_CLB, lutId, ffId, half_ctrl_mode)) + { + site_nbr[sNbrIdx + nbrId] = INVALID; + } + + ////DBG + //if (siteId == dbgSId) + //{ + // printf("%d remove incompatible neighbors %d of %d: Done with instId %d of type %d\n", + // siteId, nbrId, site_nbr_idx[siteId], instId, node2fence_region_map[instId]); + //} + ////DBG + + } + ////DBG + //if (siteId == dbgSId) + //{ + // printf("%d remove incompatible neighbors: Marked invalid instance neighbors in site_nbr \n", siteId); + //} + ////DBG + + //Remove invalid neighbor instances + remove_invalid_neighbor(siteId, sNbrIdx, site_nbr_idx, site_nbr); + ////DBG + //if (siteId == dbgSId) + //{ + // printf("%d remove incompatible neighbors: Updated site_nbr has %d instances\n", siteId, site_nbr_idx[siteId]); + //} + ////DBG + +} + +//WL Improv +template +__device__ void compute_wirelength_improv( + const T* pos_x, const T* pos_y, const T* net_bbox, const T* pin_offset_x, + const T* pin_offset_y, const T* net_weights, const T* site_xy, + const int* net2pincount, const int* flat_net2pin_start_map, + const int* net_pinIdArrayX, const int* net_pinIdArrayY, + const int* pin2node_map, const T xWirelenWt, const T yWirelenWt, + const int currNetId, const int res_siteId, const int cNIPIdx, + const int* currNetIntPins, T& wirelenImprov) +{ + //Compute wirelenImprov + int cNbId = currNetId*4; + T netXlen = net_bbox[cNbId+2] - net_bbox[cNbId]; + T netYlen = net_bbox[cNbId+3] - net_bbox[cNbId+1]; + if (cNIPIdx == net2pincount[currNetId]) + { + T bXLo(pin_offset_x[currNetIntPins[0]]); + T bXHi(pin_offset_x[currNetIntPins[0]]); + T bYLo(pin_offset_y[currNetIntPins[0]]); + T bYHi(pin_offset_y[currNetIntPins[0]]); + for (int poI = 1; poI < cNIPIdx; ++poI) + { + T poX = pin_offset_x[currNetIntPins[poI]]; + T poY = pin_offset_y[currNetIntPins[poI]]; + if (poX < bXLo) + { + bXLo = poX; + } else if (poX > bXHi) + { + bXHi = poX; + } + if (poY < bYLo) + { + bYLo = poY; + } else if (poY > bYHi) + { + bYHi = poY; + } + } + wirelenImprov += net_weights[currNetId] * + (xWirelenWt * (netXlen - (bXHi-bXLo)) + + yWirelenWt * (netYlen - (bYHi - bYLo))); + return; + } + + T bXLo(net_bbox[cNbId]); + T bYLo(net_bbox[cNbId+1]); + T bXHi(net_bbox[cNbId+2]); + T bYHi(net_bbox[cNbId+3]); + + int sId = res_siteId*2; + T locX = site_xy[sId]; + T locY = site_xy[sId+1]; + + if (locX <= bXLo) + { + bXLo = locX; + } else + { + int n2pId = flat_net2pin_start_map[currNetId]; + while (n2pId < flat_net2pin_start_map[currNetId+1] && + val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayX[n2pId])) + { + ++n2pId; + } + int reqPId = net_pinIdArrayX[n2pId]; + T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; + bXLo = DREAMPLACE_STD_NAMESPACE::min(pinX, locX); + } + + if (locX >= bXHi) + { + bXHi = locX; + } else + { + int n2pId = flat_net2pin_start_map[currNetId+1]-1; + while (n2pId >= flat_net2pin_start_map[currNetId] && + val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayX[n2pId])) + { + --n2pId; + } + int reqPId = net_pinIdArrayX[n2pId]; + T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; + bXHi = DREAMPLACE_STD_NAMESPACE::max(pinX, locX); + } + + if (locY <= bYLo) + { + bYLo = locY; + } else + { + int n2pId = flat_net2pin_start_map[currNetId]; + while (n2pId < flat_net2pin_start_map[currNetId+1] && + val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayY[n2pId])) + { + ++n2pId; + } + int reqPId = net_pinIdArrayY[n2pId]; + T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; + bYLo = DREAMPLACE_STD_NAMESPACE::min(pinY, locY); + } + + if (locY >= bYHi) + { + bYHi = locY; + } else + { + int n2pId = flat_net2pin_start_map[currNetId+1]-1; + while (n2pId >= flat_net2pin_start_map[currNetId] && + val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayY[n2pId])) + { + --n2pId; + } + int reqPId = net_pinIdArrayY[n2pId]; + T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; + bYHi = DREAMPLACE_STD_NAMESPACE::max(pinY, locY); + } + wirelenImprov += net_weights[currNetId] * + (xWirelenWt * (netXlen - (bXHi-bXLo)) + + yWirelenWt * (netYlen - (bYHi - bYLo))); + return; +} + +//computeCandidateScore +template +__device__ void compute_candidate_score( + const T* pos_x, const T* pos_y, const T* pin_offset_x, const T* pin_offset_y, + const T* net_bbox, const T* net_weights, const T* site_xy, + const int* net_pinIdArrayX, const int* net_pinIdArrayY, + const int* flat_net2pin_start_map, const int* flat_node2pin_start_map, + const int* flat_node2pin_map, const int* sorted_net_map, const int* pin2net_map, + const int* pin2node_map, const int* net2pincount, const int* lut_type, + const T xWirelenWt, const T yWirelenWt, const T extNetCountWt, + const T wirelenImprovWt, const int netShareScoreMaxNetDegree, + const int wlscoreMaxNetDegree, const int half_ctrl_mode, const int* res_sig, + const int res_siteId, const int res_sigIdx, T &result) +{ + T netShareScore = T(0.0); + T wirelenImprov = T(0.0); + T typeScore = T(0.0); + int pins[512]; + int pinIdx = 0; + + for (int i = 0; i < res_sigIdx; ++i) + { + int instId = res_sig[i]; + //For macro nodes, same instId is repeated in sig + if (i != 0 && instId == res_sig[i-1]) continue; + for (int pId = flat_node2pin_start_map[instId]; + pId < flat_node2pin_start_map[instId+1]; ++pId) + { + pins[pinIdx] = flat_node2pin_map[pId]; + ++pinIdx; + } + if (half_ctrl_mode == 0) + { + typeScore += lut_type[instId]; + } + } + sort_array(pins, pinIdx); + //remove_duplicates(pins, pinIdx); + + if (pinIdx == 0) + { + result = T(0.0); + return; + } + + int maxNetDegree = DREAMPLACE_STD_NAMESPACE::max(netShareScoreMaxNetDegree, + wlscoreMaxNetDegree); + int currNetId = pin2net_map[pins[0]]; + + if (net2pincount[currNetId] > maxNetDegree) + { + result = T(0.0); + return; + } + + int numIntNets(0), numNets(0); + int currNetIntPins[512]; + int cNIPIdx = 0; + + currNetIntPins[cNIPIdx] = pins[0]; + ++cNIPIdx; + + for (int pId = 1; pId < pinIdx; ++pId) + { + int netId = pin2net_map[pins[pId]]; + if (netId == currNetId) + { + currNetIntPins[cNIPIdx] = pins[pId]; + ++cNIPIdx; + } else + { + if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) + { + ++numNets; + numIntNets += (cNIPIdx == net2pincount[currNetId] ? 1 : 0); + netShareScore += net_weights[currNetId] * (cNIPIdx - 1.0) / DREAMPLACE_STD_NAMESPACE::max(T(1.0), net2pincount[currNetId] - T(1.0)); + } + if (net2pincount[currNetId] <= wlscoreMaxNetDegree) + { + compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, + net_weights, site_xy, net2pincount, flat_net2pin_start_map, net_pinIdArrayX, + net_pinIdArrayY, pin2node_map, xWirelenWt, yWirelenWt, currNetId, res_siteId, + cNIPIdx, currNetIntPins, wirelenImprov); + } + currNetId = netId; + if (net2pincount[currNetId] > maxNetDegree) + { + break; + } + cNIPIdx = 0; + currNetIntPins[cNIPIdx] = pins[pId]; + ++cNIPIdx; + } + } + + //Handle last net + if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) + { + ++numNets; + numIntNets += (cNIPIdx == net2pincount[currNetId] ? 1 : 0); + netShareScore += net_weights[currNetId] * (cNIPIdx - 1.0) / DREAMPLACE_STD_NAMESPACE::max(T(1.0), net2pincount[currNetId] - T(1.0)); + } + + if (net2pincount[currNetId] <= wlscoreMaxNetDegree) + { + compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, + net_weights, site_xy, net2pincount, flat_net2pin_start_map, net_pinIdArrayX, + net_pinIdArrayY, pin2node_map, xWirelenWt, yWirelenWt, currNetId, res_siteId, + cNIPIdx, currNetIntPins, wirelenImprov); + } + netShareScore /= (T(1.0) + extNetCountWt * (numNets - numIntNets)); + result = netShareScore + wirelenImprovWt * wirelenImprov; + + if (half_ctrl_mode == 0) + { + result += T(0.1)*typeScore; + } +} + +template +inline __device__ bool compare_pq_tops( + const T* site_curr_pq_score, const int* site_curr_pq_top_idx, const int* site_curr_pq_validIdx, + const int* site_curr_pq_siteId, const int* site_curr_pq_sig_idx, const int* site_curr_pq_sig, + const int* site_curr_pq_impl_lut, const int* site_curr_pq_impl_ff, const int* site_curr_pq_impl_cksr, + const int* site_curr_pq_impl_ce, const T* site_next_pq_score, const int* site_next_pq_top_idx, + const int* site_next_pq_validIdx, const int* site_next_pq_siteId, const int* site_next_pq_sig_idx, + const int* site_next_pq_sig, const int* site_next_pq_impl_lut, const int* site_next_pq_impl_ff, + const int* site_next_pq_impl_cksr, const int* site_next_pq_impl_ce, const int siteId, + const int sPQ, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, + const int SLICE_CAPACITY) +{ + //Check site_curr_pq TOP == site_next_pq TOP + int curr_pq_topId = sPQ+site_curr_pq_top_idx[siteId]; + int next_pq_topId = sPQ+site_next_pq_top_idx[siteId]; + + if (site_curr_pq_validIdx[curr_pq_topId] != site_next_pq_validIdx[next_pq_topId] || + site_curr_pq_validIdx[curr_pq_topId] != 1) + { + return false; + } + if (site_curr_pq_score[curr_pq_topId] == site_next_pq_score[next_pq_topId] && + site_curr_pq_siteId[curr_pq_topId] == site_next_pq_siteId[next_pq_topId] && + site_curr_pq_sig_idx[curr_pq_topId] == site_next_pq_sig_idx[next_pq_topId]) + { + //Check both sig + int currPQSigIdx = curr_pq_topId*SIG_IDX; + int nextPQSigIdx = next_pq_topId*SIG_IDX; + + for (int sg = 0; sg < site_curr_pq_sig_idx[curr_pq_topId]; ++sg) + { + if (site_curr_pq_sig[currPQSigIdx + sg] != site_next_pq_sig[nextPQSigIdx + sg]) + { + return false; + } + } + + //Check impl + int cCKRId = curr_pq_topId*CKSR_IN_CLB; + int cCEId = curr_pq_topId*CE_IN_CLB; + int cFFId = curr_pq_topId*SLICE_CAPACITY; + int nCKRId = next_pq_topId*CKSR_IN_CLB; + int nCEId = next_pq_topId*CE_IN_CLB; + int nFFId = next_pq_topId*SLICE_CAPACITY; + + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + if (site_curr_pq_impl_lut[cFFId + sg] != site_next_pq_impl_lut[nFFId + sg] || + site_curr_pq_impl_ff[cFFId + sg] != site_next_pq_impl_ff[nFFId + sg]) + { + return false; + } + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + if (site_curr_pq_impl_cksr[cCKRId + sg] != site_next_pq_impl_cksr[nCKRId + sg]) + { + return false; + } + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + if(site_curr_pq_impl_ce[cCEId + sg] != site_next_pq_impl_ce[nCEId + sg]) + { + return false; + } + } + return true; + } + return false; +} + +//////////////////////////////// +//////////////////////////////// +//////////////////////////////// + +template +__global__ void initNets( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* sorted_net_idx, + const int* pin2node_map, + const int* net2pincount, + const int num_nets, + const int wlscoreMaxNetDegree, + T* net_bbox, + int* net_pinIdArrayX, + int* net_pinIdArrayY) +{ + int i = threadIdx.x + blockDim.x * blockIdx.x; + while (i < num_nets) + { + const int idx = sorted_net_idx[i]; + + if (net2pincount[idx] > 0 && net2pincount[idx] <= wlscoreMaxNetDegree) + { + int pinIdxBeg = flat_net2pin_start_map[idx]; + int pinIdxEnd = flat_net2pin_start_map[idx+1]; + + int xLo = idx*4; + int yLo = xLo+1; + int xHi = xLo+2; + int yHi = xLo+3; + + int pnIdx = flat_net2pin_map[pinIdxBeg]; + int nodeIdx = pin2node_map[pnIdx]; + + net_bbox[xLo] = pos_x[nodeIdx] + pin_offset_x[pnIdx]; + net_bbox[yLo] = pos_y[nodeIdx] + pin_offset_y[pnIdx]; + net_bbox[xHi] = net_bbox[xLo]; + net_bbox[yHi] = net_bbox[yLo]; + + int tempX[512]; + int tempY[512]; + T temp_flat_net2pinX[512]; + T temp_flat_net2pinY[512]; + int tempId = 0; + + temp_flat_net2pinX[tempId] = net_bbox[xLo]; + temp_flat_net2pinY[tempId] = net_bbox[yLo]; + tempX[tempId] = pnIdx; + tempY[tempId] = pnIdx; + + ++tempId; + + //Update Net Bbox based on node location and pin offset + for (int pId = pinIdxBeg+1; pId < pinIdxEnd; ++pId) + { + int pinIdx = flat_net2pin_map[pId]; + int ndIdx = pin2node_map[pinIdx]; + + T valX = pos_x[ndIdx] + pin_offset_x[pinIdx]; + T valY = pos_y[ndIdx] + pin_offset_y[pinIdx]; + + if (valX < net_bbox[xLo]) + { + net_bbox[xLo] = valX; + } else if (valX > net_bbox[xHi]) + { + net_bbox[xHi] = valX; + } + + if (valY < net_bbox[yLo]) + { + net_bbox[yLo] = valY; + } else if (valY > net_bbox[yHi]) + { + net_bbox[yHi] = valY; + } + + temp_flat_net2pinX[tempId] = valX; + temp_flat_net2pinY[tempId] = valY; + + tempX[tempId] = pinIdx; + tempY[tempId] = pinIdx; + + ++tempId; + } + + //Sort pinIdArray based on node loc and pin offset - Bubble sort + for (int ix = 1; ix < tempId; ++ix) + { + for (int jx = 0; jx < tempId-1; ++jx) + { + //Sort X + if (temp_flat_net2pinX[jx] > temp_flat_net2pinX[jx+1]) + { + int tempVal = tempX[jx]; + tempX[jx] = tempX[jx+1]; + tempX[jx+1] = tempVal; + + T net2pinVal = temp_flat_net2pinX[jx]; + temp_flat_net2pinX[jx] = temp_flat_net2pinX[jx+1]; + temp_flat_net2pinX[jx+1] = net2pinVal; + } + + //Sort Y + if (temp_flat_net2pinY[jx] > temp_flat_net2pinY[jx+1]) + { + int tempVal = tempY[jx]; + tempY[jx] = tempY[jx+1]; + tempY[jx+1] = tempVal; + + T net2pinVal = temp_flat_net2pinY[jx]; + temp_flat_net2pinY[jx] = temp_flat_net2pinY[jx+1]; + temp_flat_net2pinY[jx+1] = net2pinVal; + } + } + } + + //Assign sorted values back + tempId = 0; + for (int pId = pinIdxBeg; pId < pinIdxEnd; ++pId) + { + net_pinIdArrayX[pId] = tempX[tempId]; + net_pinIdArrayY[pId] = tempY[tempId]; + ++tempId; + } + } + i += blockDim.x * gridDim.x; + } +} + +//TODO-Remove is_mlab_node when MLABs are treated separately +//Preclustering to handle carry chains and mlabs +template +__global__ void preClustering( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* sorted_node_map, + const int* sorted_node_idx, + const int* flat_net2pin_start_map, + const int* flat_net2pin_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* node2fence_region_map, + const int* node2outpinIdx_map, + const int* pin2net_map, + const int* pin2node_map, + const int* pin_typeIds, + const int* is_mlab_node, + const T preClusteringMaxDist, + const int lutId, + const int ffId, + const int num_nodes, + int* flat_node2precluster_map, + int* flat_node2prclstrCount) +{ + int i = threadIdx.x + blockDim.x * blockIdx.x; + //const int blk = blockDim.x * gridDim.x; + while (i < num_nodes) + { + const int idx = sorted_node_idx[i]; + //Only consider LUTs and skip MLABs + if (node2fence_region_map[idx] == lutId && is_mlab_node[idx] == 0) + { + int ff_insts[SLICE_MAX_CAP]; + T ff_dists[SLICE_MAX_CAP]; + int ffIdx = 0; + int nPIdx = idx*4; + + for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) + { + int outPinId = node2outpinIdx_map[nodeOutId]; + if (outPinId == INVALID) continue; + + int outNetId = pin2net_map[outPinId]; + + int pinIdxBeg = flat_net2pin_start_map[outNetId]; + int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; + + T instLocX = pos_x[idx] + pin_offset_x[outPinId]; + T instLocY = pos_y[idx] + pin_offset_y[outPinId]; + + for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) + { + int pinIdx = flat_net2pin_map[pinId]; + int nodeIdx = pin2node_map[pinIdx]; + + T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; + T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; + + T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + + DREAMPLACE_STD_NAMESPACE::abs(distY); + + if (pin_typeIds[pinIdx] == INPUT_PIN && + node2fence_region_map[nodeIdx] == ffId && + dist < preClusteringMaxDist) + { + ff_insts[ffIdx] = nodeIdx; + ff_dists[ffIdx] = dist; + ++ffIdx; + } + } + } + + //Check if ff is empty + if (ffIdx > 0) + { + //Sort ff_insts/ff_dists based on dist and sorted_node_map + for (int ix = 1; ix < ffIdx; ++ix) + { + for (int jx = 0; jx < ffIdx-1; ++jx) + { + if (ff_dists[jx] == ff_dists[jx+1]) + { + if (sorted_node_map[ff_insts[jx]] > sorted_node_map[ff_insts[jx+1]]) + { + int tempVal = ff_insts[jx]; + ff_insts[jx] = ff_insts[jx+1]; + ff_insts[jx+1] = tempVal; + + T distVal = ff_dists[jx]; + ff_dists[jx] = ff_dists[jx+1]; + ff_dists[jx+1] = distVal; + } + } else + { + if (ff_dists[jx] > ff_dists[jx+1]) + { + int tempVal = ff_insts[jx]; + ff_insts[jx] = ff_insts[jx+1]; + ff_insts[jx+1] = tempVal; + + T distVal = ff_dists[jx]; + ff_dists[jx] = ff_dists[jx+1]; + ff_dists[jx+1] = distVal; + } + } + } + } + + int nPIdx = idx*3; + + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[idx]] = ff_insts[0]; + ++flat_node2prclstrCount[idx]; + + int fcIdx = flop2ctrlSetId_map[ff_insts[0]]*3 + 1; + int cksr = flop_ctrlSets[fcIdx]; + + for (int fIdx = 1; fIdx < ffIdx; ++fIdx) + { + int ctrlIdx = flop2ctrlSetId_map[ff_insts[fIdx]]*3 + 1; + int fCksr = flop_ctrlSets[ctrlIdx]; + + if (fCksr == cksr) + { + flat_node2precluster_map[nPIdx + flat_node2prclstrCount[idx]] = ff_insts[fIdx]; + ++flat_node2prclstrCount[idx]; + break; + } + } + + //Sort precluster based on idx + for (int ix = nPIdx+1; ix < nPIdx + flat_node2prclstrCount[idx]; ++ix) + { + for (int jx = nPIdx; jx < nPIdx + flat_node2prclstrCount[idx]-1; ++jx) + { + if (sorted_node_map[flat_node2precluster_map[jx]] > sorted_node_map[flat_node2precluster_map[jx+1]]) + { + int val = flat_node2precluster_map[jx]; + flat_node2precluster_map[jx] = flat_node2precluster_map[jx+1]; + flat_node2precluster_map[jx+1] = val; + } + } + } + + for (int prcl = 0; prcl < flat_node2prclstrCount[idx]; ++prcl) + { + int fIdx = flat_node2precluster_map[nPIdx + prcl]; + int fID = fIdx*3; + if (fIdx != idx) + { + for (int cl = 0; cl < flat_node2prclstrCount[idx]; ++cl) + { + flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; + } + flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[idx]; + } + } + } + } + i += blockDim.x * gridDim.x; + } +} + +//runDLIteration +template +__global__ void runDLIteration( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_bbox, + const T* site_xy, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* node2fence_region_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* lut_type, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* net2pincount, + const int* pin2net_map, + const int* pin_typeIds, + const int* flat_net2pin_start_map, + const int* pin2node_map, + const int* sorted_net_map, + const int* sorted_node_map, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* is_mlab_node, + const int* site_nbrList, + const int* site_nbrRanges, + const int* site_nbrRanges_idx, + const T* net_weights, + const int* addr2site_map, + const int* special_nodes, + const int num_clb_sites, + const int minStableIter, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int minNeighbors, + const int intMinVal, + const int numGroups, + const int netShareScoreMaxNetDegree, + const int wlscoreMaxNetDegree, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const T xWirelenWt, + const T yWirelenWt, + const T wirelenImprovWt, + const T extNetCountWt, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + int* validIndices_curr_scl, + int* site_nbr_idx, + int* site_nbr, + int* site_nbrGroup_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_validIdx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + T* site_det_score, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + T* inst_next_bestScoreImprov, + int* inst_next_bestSite, + int* inst_score_improv, + int* site_score_improv + ) +{ + for (int sIdx = threadIdx.x + blockDim.x * blockIdx.x; + sIdx < num_clb_sites; sIdx += blockDim.x*gridDim.x) + { + int siteId = addr2site_map[sIdx]; + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + + int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + int sdckId = sIdx*CKSR_IN_CLB; + int sdceId = sIdx*CE_IN_CLB; + + int sclSigId = sSCL*SIG_IDX; + int scllutIdx = sSCL*SLICE_CAPACITY; + int sclckIdx = sSCL*CKSR_IN_CLB; + int sclceIdx = sSCL*CE_IN_CLB; + + //(a)Try to commit Top candidates + char commitTopCandidate(INVALID); + + int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); + int topIdx(tsPQ*SIG_IDX); + int lutIdx = tsPQ*SLICE_CAPACITY; + int ckIdx = tsPQ*CKSR_IN_CLB; + int ceIdx = tsPQ*CE_IN_CLB; + + if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || + !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, + site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], + site_curr_pq_sig, inst_curr_detSite)) + { + commitTopCandidate = 0; + } else { + + for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) + { + int pqInst = site_curr_pq_sig[topIdx + pIdx]; + + if (inst_curr_detSite[pqInst] != siteId && + inst_curr_bestSite[pqInst] != siteId) + { + commitTopCandidate = 0; + break; + } + } + } + + if (commitTopCandidate == INVALID) + { + ////// + site_det_score[sIdx] = site_curr_pq_score[tsPQ]; + site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; + site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; + + for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) + { + site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; + site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; + } + ////// + + for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) + { + int sigInst = site_det_sig[sdtopId + iSig]; + inst_next_detSite[sigInst] = siteId; + } + + //Remove Incompatible Neighbors + remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, site_det_sig, + site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, + sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, + site_nbr_idx, site_nbr); + + //Clear pq and make scl only contain the committed candidate + //int sclCount = 0; + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + //if (site_next_pq_validIdx[nPQId] != INVALID) + //{ + //Clear contents thoroughly + clear_cand_contents( + nPQId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[nPQId] = INVALID; + site_next_pq_sig_idx[nPQId] = 0; + site_next_pq_siteId[nPQId] = INVALID; + site_next_pq_score[nPQId] = T(0.0); + //++sclCount; + //if (sclCount == site_next_pq_idx[sIdx]) + //{ + // break; + //} + //} + } + site_next_pq_idx[sIdx] = 0; + site_next_pq_top_idx[sIdx] = INVALID; + + int sclCount = 0; + for (int vId = 0; vId < SCL_IDX; ++vId) + { + int cSclId = sSCL + vId; + if (site_curr_scl_validIdx[cSclId] != INVALID) + { + //Clear contents thoroughly + clear_cand_contents( + cSclId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + + site_curr_scl_validIdx[cSclId] = INVALID; + site_curr_scl_sig_idx[cSclId] = 0; + site_curr_scl_siteId[cSclId] = INVALID; + site_curr_scl_score[cSclId] = 0.0; + ++sclCount; + if (sclCount == site_curr_scl_idx[sIdx]) + { + break; + } + } + } + site_curr_scl_idx[sIdx] = 0; + + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + ///// + commitTopCandidate = 1; + } + + if (commitTopCandidate == 0) + { + //Remove invalid candidates from site PQ + if (site_next_pq_idx[sIdx] > 0) + { + //int snCnt = 0; + //int maxEntries = site_next_pq_idx[sIdx]; + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + int tpIdx = ssPQ*SIG_IDX; + + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, + site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], + site_next_pq_sig, inst_curr_detSite)) + { + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[ssPQ] = INVALID; + site_next_pq_sig_idx[ssPQ] = 0; + site_next_pq_siteId[ssPQ] = INVALID; + site_next_pq_score[ssPQ] = 0.0; + --site_next_pq_idx[sIdx]; + } + //++snCnt; + //if (snCnt == maxEntries) + //{ + // break; + //} + } + } + + site_next_pq_top_idx[sIdx] = INVALID; + + if (site_next_pq_idx[sIdx] > 0) + { + int snCnt = 0; + int maxEntries = site_next_pq_idx[sIdx]; + T maxScore(-1000.0); + int maxScoreId(INVALID); + //Recompute top idx + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (site_next_pq_score[ssPQ] > maxScore) + { + maxScore = site_next_pq_score[ssPQ]; + maxScoreId = nIdx; + } + ++snCnt; + if (snCnt == maxEntries) + { + break; + } + } + } + site_next_pq_top_idx[sIdx] = maxScoreId; + } + } + + //Remove invalid candidates from seed candidate list (scl) + if (site_curr_scl_idx[sIdx] > 0) + { + int sclCount = 0; + int maxEntries = site_curr_scl_idx[sIdx]; + for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) + { + int ssPQ = sSCL + nIdx; + int tpIdx = ssPQ*SIG_IDX; + + if (site_curr_scl_validIdx[ssPQ] != INVALID) + { + if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, + site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], + site_curr_scl_sig, inst_curr_detSite)) + { + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + + site_curr_scl_validIdx[ssPQ] = INVALID; + site_curr_scl_sig_idx[ssPQ] = 0; + site_curr_scl_siteId[ssPQ] = INVALID; + site_curr_scl_score[ssPQ] = 0.0; + --site_curr_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + //If site.scl becomes empty, add site.det into it as the seed + if (site_curr_scl_idx[sIdx] == 0) + { + + //site.curr.scl.emplace_back(site.det); + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + } + } + + // (c) removeCommittedNeighbors(site) + for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) + { + int siteInst = site_nbr[sNbrIdx + sNIdx]; + if (inst_curr_detSite[siteInst] != INVALID) + { + site_nbr[sNbrIdx + sNIdx] = INVALID; + } + } + remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); + + // (d) addNeighbors(site) + ////reuse site_nbrGroup_idx to store the Ids for STAGGERED NEW CANDIDATE ADDITION + int maxNeighbors = site_nbrRanges[sIdx*(numGroups+1) + numGroups]; + if (site_nbr_idx[sIdx] < minNeighbors && site_nbrGroup_idx[sIdx] <= maxNeighbors) + { + int beg = site_nbrGroup_idx[sIdx]; + ////STAGGERED ADDITION SET TO SLICE/16 or SLICE_CAPACITY/8 + ///For ISPD'2016 benchmarks, SLICE=32 and SLICE_CAPACITY=16 + int end = DREAMPLACE_STD_NAMESPACE::min(site_nbrGroup_idx[sIdx]+SLICE_CAPACITY/8, maxNeighbors); + site_nbrGroup_idx[sIdx] = end; + + for (int aNIdx = beg; aNIdx < end; ++aNIdx) + { + int instId = site_nbrList[sNbrIdx + aNIdx]; + + if (inst_curr_detSite[instId] == INVALID && + is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, + lut_type, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, flat_node2prclstrCount, + flat_node2precluster_map, flop2ctrlSetId_map, flop_ctrlSets, + extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, + special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, instId, + SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, half_ctrl_mode)) + { + site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + aNIdx]; + ++site_nbr_idx[sIdx]; + } + } + } + + //Generate indices for kernel_2 + int validId = 0; + for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) + { + int siteCurrIdx = sSCL + scsIdx; + if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) + { + validIndices_curr_scl[sSCL+validId] = siteCurrIdx; + ++validId; + } + if (validId == site_curr_scl_idx[sIdx]) break; + } + + // (e) createNewCandidates(site) + //Generate new candidates by merging site_nbr to site_curr_scl + + const int limit_x = site_curr_scl_idx[sIdx]; + const int limit_y = site_nbr_idx[sIdx]; + ////RESTRICTED NEW CANDIDATE EXPLORATION SET TO SLICE/8 or SLICE_CAPACITY/4 + const int limit_cands = DREAMPLACE_STD_NAMESPACE::min(SLICE_CAPACITY/4,limit_x*limit_y); + + for (int scsIdx = 0; scsIdx < limit_cands; ++scsIdx) + { + int sclId = scsIdx/limit_y; + int snIdx = scsIdx/limit_x; + int siteCurrIdx = validIndices_curr_scl[sSCL + sclId]; + + ///// + int sCKRId = siteCurrIdx*CKSR_IN_CLB; + int sCEId = siteCurrIdx*CE_IN_CLB; + int sFFId = siteCurrIdx*SLICE_CAPACITY; + int sGId = siteCurrIdx*SIG_IDX; + + T nwCand_score = site_curr_scl_score[siteCurrIdx]; + int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; + int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; + + //array instantiation + int nwCand_sig[SIG_MAX_CAP]; + int nwCand_lut[SLICE_MAX_CAP]; + int nwCand_ff[SLICE_MAX_CAP]; + int nwCand_ce[CE_MAX_CAP]; + int nwCand_cksr[CKSR_MAX_CAP]; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; + nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; + } + + int instId = site_nbr[sNbrIdx + snIdx]; + int instPcl = instId*3; + + int addInstToSig = INVALID; + if (nwCand_sigIdx >= 2*SLICE_CAPACITY) + { + addInstToSig = 0; + } + + if (addInstToSig == INVALID) + { + int temp[4]; //Max precluster size = 3 + int tIdx(0); + + for (int el = 0; el < flat_node2prclstrCount[instId]; ++el) + { + int newInstId = flat_node2precluster_map[instPcl+el]; + if (!val_in_array(nwCand_sig, nwCand_sigIdx, 0, newInstId)) + { + temp[tIdx] = newInstId; + ++tIdx; + } else + { + addInstToSig = 0; + break; + } + } + + if (addInstToSig == INVALID && (nwCand_sigIdx + tIdx > 2*SLICE_CAPACITY)) + { + addInstToSig = 0; + } + + if (addInstToSig == INVALID) + { + for (int mBIdx = 0; mBIdx < tIdx; ++mBIdx) + { + nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; + ++nwCand_sigIdx; + } + addInstToSig = 1; + } + } + + if (addInstToSig == 1) + { + //check cand sig is in site_next_pq + int candSigInSiteNextPQ = INVALID; + //int cnt = 0; + for (int i = 0; i < PQ_IDX; ++i) + { + int sigIdx = sPQ + i; + if (site_next_pq_validIdx[sigIdx] != INVALID) + { + if (site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) + { + int pqIdx(sigIdx*SIG_IDX), mtch(0); + + for (int k = 0; k < nwCand_sigIdx; ++k) + { + for (int l = 0; l < nwCand_sigIdx; ++l) + { + if (site_next_pq_sig[pqIdx + l] == nwCand_sig[k]) + { + ++mtch; + break; + } + } + } + if (mtch == nwCand_sigIdx) + { + candSigInSiteNextPQ = 1; + break; + } + } + } + } + + if (candSigInSiteNextPQ == INVALID && + add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, flat_node2prclstrCount, + flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, + flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, + lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, half_ctrl_mode, + CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) + { + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlscoreMaxNetDegree, half_ctrl_mode, + nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); + + int nxtId(INVALID); + + if (site_next_pq_idx[sIdx] < PQ_IDX) + { + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (site_next_pq_validIdx[sPQ+vId] == INVALID) + { + nxtId = vId; + ++site_next_pq_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (ckscore > site_next_pq_score[sPQ + vId]) + { + ckscore = site_next_pq_score[sPQ + vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + int nTId = sPQ + nxtId; + int nCKRId = nTId*CKSR_IN_CLB; + int nCEId = nTId*CE_IN_CLB; + int nFFId = nTId*SLICE_CAPACITY; + int nSGId = nTId*SIG_IDX; + + ///// + site_next_pq_validIdx[nTId] = 1; + site_next_pq_score[nTId] = nwCand_score; + site_next_pq_siteId[nTId] = nwCand_siteId; + site_next_pq_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + + if (site_next_pq_idx[sIdx] == 1 || + nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) + { + site_next_pq_top_idx[sIdx] = nxtId; + } + + nxtId = INVALID; + + if (site_next_scl_idx[sIdx] < SCL_IDX) + { + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (site_next_scl_validIdx[sSCL+vId] == INVALID) + { + nxtId = vId; + ++site_next_scl_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (ckscore > site_next_scl_score[sSCL+vId]) + { + ckscore = site_next_scl_score[sSCL+vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + ///// + nTId = sSCL + nxtId; + nCKRId = nTId*CKSR_IN_CLB; + nCEId = nTId*CE_IN_CLB; + nFFId = nTId*SLICE_CAPACITY; + nSGId = nTId*SIG_IDX; + + site_next_scl_validIdx[nTId] = 1; + site_next_scl_score[nTId] = nwCand_score; + site_next_scl_siteId[nTId] = nwCand_siteId; + site_next_scl_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + } + } + } + } + } + + //Remove all candidates in scl that is worse than the worst candidate in PQ + if (site_next_pq_idx[sIdx] > 0) + { + //Find worst candidate in PQ + T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); + + int sclCount = 0; + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + if (site_next_pq_validIdx[nPQId] != INVALID) + { + if (ckscore > site_next_pq_score[nPQId]) + { + ckscore = site_next_pq_score[nPQId]; + } + ++sclCount; + if (sclCount == site_next_pq_idx[sIdx]) + { + break; + } + } + } + + //Invalidate worst ones in scl + sclCount = 0; + int maxEntries = site_next_scl_idx[sIdx]; + for (int ckId = 0; ckId < SCL_IDX; ++ckId) + { + int vId = sSCL + ckId; + if (site_next_scl_validIdx[vId] != INVALID) + { + if (ckscore > site_next_scl_score[vId]) + { + //Clear contents thoroughly + clear_cand_contents( + vId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce); + + site_next_scl_validIdx[vId] = INVALID; + site_next_scl_sig_idx[vId] = 0; + site_next_scl_siteId[vId] = INVALID; + site_next_scl_score[vId] = 0.0; + --site_next_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + + //Update stable Iteration count + if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && + compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, + site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, + site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, + site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, + site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, + site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, + site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, + sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) + { + site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; + } else + { + site_next_stable[sIdx] = 0; + } + + //// (f) broadcastTopCandidate(site) + if (site_next_pq_idx[sIdx] > 0) + { + int tpIdx = sPQ + site_next_pq_top_idx[sIdx]; + int topSigId = tpIdx*SIG_IDX; + + T scoreImprov = site_next_pq_score[tpIdx] - site_det_score[sIdx]; + + ////UPDATED SEQUENTIAL PORTION + int scoreImprovInt = DREAMPLACE_STD_NAMESPACE::max(int(scoreImprov*10000), intMinVal); + site_score_improv[sIdx] = scoreImprovInt + siteId; + + for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[tpIdx]; ++ssIdx) + { + int instId = site_next_pq_sig[topSigId + ssIdx]; + + if (inst_curr_detSite[instId] == INVALID) + { + atomicMax(&inst_score_improv[instId], scoreImprovInt); + } + } + } + } +} + +//runDLIteration split kernel 1 +template +__global__ void runDLIteration_kernel_1( + const int* node2fence_region_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* lut_type, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* pin2net_map, + const int* pin_typeIds, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* is_mlab_node, + const int* site_nbrList, + const int* site_nbrRanges, + const int* site_nbrRanges_idx, + const int* addr2site_map, + const int* special_nodes, + const int num_clb_sites, + const int minStableIter, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int minNeighbors, + const int numGroups, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + int* site_nbr_idx, + int* site_nbr, + int* site_nbrGroup_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + T* site_det_score, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* validIndices_curr_scl, + int* cumsum_curr_scl + ) +{ + for (int sIdx = threadIdx.x + blockDim.x * blockIdx.x; + sIdx < num_clb_sites; sIdx += blockDim.x*gridDim.x) + { + int siteId = addr2site_map[sIdx]; + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + + int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + int sdckId = sIdx*CKSR_IN_CLB; + int sdceId = sIdx*CE_IN_CLB; + + int sclSigId = sSCL*SIG_IDX; + int scllutIdx = sSCL*SLICE_CAPACITY; + int sclckIdx = sSCL*CKSR_IN_CLB; + int sclceIdx = sSCL*CE_IN_CLB; + + //(a)Try to commit Top candidates + int commitTopCandidate(INVALID); + + int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); + int topIdx(tsPQ*SIG_IDX); + int lutIdx = tsPQ*SLICE_CAPACITY; + int ckIdx = tsPQ*CKSR_IN_CLB; + int ceIdx = tsPQ*CE_IN_CLB; + + if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || + !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, + site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], + site_curr_pq_sig, inst_curr_detSite)) + { + commitTopCandidate = 0; + } else { + for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) + { + int pqInst = site_curr_pq_sig[topIdx + pIdx]; + + if (inst_curr_detSite[pqInst] != siteId && + inst_curr_bestSite[pqInst] != siteId) + { + commitTopCandidate = 0; + break; + } + } + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d has commitTopCandidate: %d\n", sIdx, siteId, commitTopCandidate); + //} + ////DBG + + if (commitTopCandidate == INVALID) + { + ////// + site_det_score[sIdx] = site_curr_pq_score[tsPQ]; + site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; + site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; + + for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) + { + site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; + site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; + } + ////// + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d commitTopCandidate: %d assign site_curr pq top to site_det with sig size: %d\n", sIdx, siteId, commitTopCandidate, site_det_sig_idx[sIdx]); + //} + ////DBG + + for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) + { + int sigInst = site_det_sig[sdtopId + iSig]; + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d commitTopCandidate: %d assign sigInst %d to site \n", sIdx, siteId, commitTopCandidate, sigInst); + //} + ////DBG + + inst_next_detSite[sigInst] = siteId; + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d commitTopCandidate: %d update inst_next_detSite \n", sIdx, siteId, commitTopCandidate); + //} + ////DBG + + //Remove Incompatible Neighbors + remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, + flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, + flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, site_det_sig, + site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, + sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, + site_nbr_idx, site_nbr); + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d commitTopCandidate: %d complete remove incompatible neighbors\n", sIdx, siteId, commitTopCandidate); + //} + ////DBG + + //Clear pq and make scl only contain the committed candidate + //int sclCount = 0; + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + //if (site_next_pq_validIdx[nPQId] != INVALID) + //{ + //Clear contents thoroughly + clear_cand_contents( + nPQId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[nPQId] = INVALID; + site_next_pq_sig_idx[nPQId] = 0; + site_next_pq_siteId[nPQId] = INVALID; + site_next_pq_score[nPQId] = T(0.0); + //++sclCount; + //if (sclCount == site_next_pq_idx[sIdx]) + //{ + // break; + //} + //} + } + site_next_pq_idx[sIdx] = 0; + site_next_pq_top_idx[sIdx] = INVALID; + + int sclCount = 0; + for (int vId = 0; vId < SCL_IDX; ++vId) + { + int cSclId = sSCL + vId; + if (site_curr_scl_validIdx[cSclId] != INVALID) + { + //Clear contents thoroughly + clear_cand_contents( + cSclId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + + site_curr_scl_validIdx[cSclId] = INVALID; + site_curr_scl_sig_idx[cSclId] = 0; + site_curr_scl_siteId[cSclId] = INVALID; + site_curr_scl_score[cSclId] = 0.0; + ++sclCount; + if (sclCount == site_curr_scl_idx[sIdx]) + { + break; + } + } + } + site_curr_scl_idx[sIdx] = 0; + + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + ///// + commitTopCandidate = 1; + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // //printf("%d Site %d: After possible commit has commitTopCandidate: %d\n", sIdx, siteId, commitTopCandidate); + // printf("%d Site %d: Completed (a)", sIdx, siteId); + //} + ////DBG + + if (commitTopCandidate == 0) + { + //(b) removeInvalidCandidates + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d with %d elements in next PQ\n", sIdx, siteId, commitTopCandidate, site_next_pq_idx[sIdx]); + //} + ////DBG + + //Remove invalid candidates from site PQ + if (site_next_pq_idx[sIdx] > 0) + { + //int snCnt = 0; + //int maxEntries = site_next_pq_idx[sIdx]; + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + int tpIdx = ssPQ*SIG_IDX; + + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, + site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], + site_next_pq_sig, inst_curr_detSite)) + { + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce); + + site_next_pq_validIdx[ssPQ] = INVALID; + site_next_pq_sig_idx[ssPQ] = 0; + site_next_pq_siteId[ssPQ] = INVALID; + site_next_pq_score[ssPQ] = 0.0; + --site_next_pq_idx[sIdx]; + } + //++snCnt; + //if (snCnt == maxEntries) + //{ + // break; + //} + } + } + + site_next_pq_top_idx[sIdx] = INVALID; + + if (site_next_pq_idx[sIdx] > 0) + { + int snCnt = 0; + int maxEntries = site_next_pq_idx[sIdx]; + T maxScore(-1000.0); + int maxScoreId(INVALID); + //Recompute top idx + for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) + { + int ssPQ = sPQ + nIdx; + if (site_next_pq_validIdx[ssPQ] != INVALID) + { + if (site_next_pq_score[ssPQ] > maxScore) + { + maxScore = site_next_pq_score[ssPQ]; + maxScoreId = nIdx; + } + ++snCnt; + if (snCnt == maxEntries) + { + break; + } + } + } + site_next_pq_top_idx[sIdx] = maxScoreId; + } + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d removed invalid candiates in next PQ with %d elements, site curr scl has %d elements \n", + // sIdx, siteId, commitTopCandidate, site_next_pq_idx[sIdx], site_curr_scl_idx[sIdx]); + //} + ////DBG + + //Remove invalid candidates from seed candidate list (scl) + if (site_curr_scl_idx[sIdx] > 0) + { + int sclCount = 0; + int maxEntries = site_curr_scl_idx[sIdx]; + for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) + { + int ssPQ = sSCL + nIdx; + int tpIdx = ssPQ*SIG_IDX; + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d nIdx %d ssPQ %d tpIdx %d site_curr_scl_validIdx[ssPQ] %d\n", + // sIdx, siteId, commitTopCandidate, nIdx, ssPQ, tpIdx, site_curr_scl_validIdx[ssPQ]); + //} + ////DBG + + if (site_curr_scl_validIdx[ssPQ] != INVALID) + { + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d, site_curr_scl_sig_idx[ssPQ] %d\n", + // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ], site_curr_scl_sig_idx[ssPQ]); + //} + ////DBG + + if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, + site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], + site_curr_scl_sig, inst_curr_detSite)) + { + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d before clear_cand_contents %d\n", + // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ]); + //} + ////DBG + //Clear contents thoroughly + clear_cand_contents( + ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce); + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d after clear_cand_contents %d\n", + // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ]); + //} + ////DBG + + site_curr_scl_validIdx[ssPQ] = INVALID; + site_curr_scl_sig_idx[ssPQ] = 0; + site_curr_scl_siteId[ssPQ] = INVALID; + site_curr_scl_score[ssPQ] = 0.0; + --site_curr_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d removed invalid candiates in site curr scl with %d elements \n", + // sIdx, siteId, commitTopCandidate, site_curr_scl_idx[sIdx]); + //} + ////DBG + + + //If site.scl becomes empty, add site.det into it as the seed + if (site_curr_scl_idx[sIdx] == 0) + { + + site_curr_scl_score[sSCL] = site_det_score[sIdx]; + site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; + site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; + site_curr_scl_validIdx[sSCL] = 1; + + for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) + { + site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; + } + for(int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; + site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; + } + for(int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; + } + for(int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; + } + ++site_curr_scl_idx[sIdx]; + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: commitTopCandidate %d add site det to site curr scl with %d elements \n", + // sIdx, siteId, commitTopCandidate, site_curr_scl_idx[sIdx]); + //} + ////DBG + + + } + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: Completed (a) and (b) \n", sIdx, siteId); + //} + ////DBG + + // (c) removeCommittedNeighbors(site) + + for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) + { + int siteInst = site_nbr[sNbrIdx + sNIdx]; + if (inst_curr_detSite[siteInst] != INVALID) + { + site_nbr[sNbrIdx + sNIdx] = INVALID; + } + } + remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: Completed (c) \n", sIdx, siteId); + //} + ////DBG + + // (d) addNeighbors(site) + ////STAGGERED ADDITION OF NEW NEIGHBORS + int maxNeighbors = site_nbrRanges[sIdx*(numGroups+1) + numGroups]; + if (site_nbr_idx[sIdx] < minNeighbors && + site_nbrGroup_idx[sIdx] <= maxNeighbors) + { + int beg = site_nbrGroup_idx[sIdx]; + ///STAGGERED ADDITION SET TO SLICE/16 or SLICE_CAPACITY/8 + int end = DREAMPLACE_STD_NAMESPACE::min(site_nbrGroup_idx[sIdx]+SLICE_CAPACITY/8, maxNeighbors); + site_nbrGroup_idx[sIdx] = end; + + for (int aNIdx = beg; aNIdx < end; ++aNIdx) + { + int instId = site_nbrList[sNbrIdx + aNIdx]; + + if (inst_curr_detSite[instId] == INVALID && + is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, + lut_type, flat_node2pin_start_map, flat_node2pin_map, + pin2net_map, pin_typeIds, flat_node2prclstrCount, + flat_node2precluster_map, flop2ctrlSetId_map, flop_ctrlSets, + extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, special_nodes, + lutTypeInSliceUnit, lut_maxShared, sIdx, instId, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, + CE_IN_CLB, lutId, ffId, half_ctrl_mode)) + { + site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + aNIdx]; + ++site_nbr_idx[sIdx]; + } + } + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: Completed (d) New Neighbor addition \n", sIdx, siteId); + //} + ////DBG + + //Generate indices for kernel_2 + int validId = 0; + for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) + { + int siteCurrIdx = sSCL + scsIdx; + if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) + { + validIndices_curr_scl[sSCL+validId] = siteCurrIdx; + ++validId; + } + if (validId == site_curr_scl_idx[sIdx]) break; + } + + cumsum_curr_scl[sIdx] = site_curr_scl_idx[sIdx]*site_nbr_idx[sIdx]; + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: Completed valid indices generation for kernel_2 \n", sIdx, siteId); + //} + ////DBG + } +} + +//runDLIteration split kernel 2 +template +__global__ void runDLIteration_kernel_2( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_bbox, + const T* site_xy, + const T* net_weights, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* node2fence_region_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* flat_net2pin_start_map, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* lut_type, + const int* net2pincount, + const int* pin2net_map, + const int* pin_typeIds, + const int* pin2node_map, + const int* sorted_node_map, + const int* sorted_net_map, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* validIndices_curr_scl, + const int* sorted_clb_siteIds, + const int* addr2site_map, + const int* special_nodes, + const T xWirelenWt, + const T yWirelenWt, + const T wirelenImprovWt, + const T extNetCountWt, + const int num_clb_sites, + const int intMinVal, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int netShareScoreMaxNetDegree, + const int wlscoreMaxNetDegree, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + int* site_nbr_idx, + int* site_nbr, + int* site_curr_pq_top_idx, + int* site_curr_pq_validIdx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + T* site_det_score, + int* inst_curr_detSite, + T* inst_next_bestScoreImprov, + int* inst_next_bestSite, + int* inst_score_improv, + int* site_score_improv + ) +{ + for (int sId = threadIdx.x + blockDim.x * blockIdx.x; + sId < num_clb_sites; sId += blockDim.x*gridDim.x) + { + const int sIdx = sorted_clb_siteIds[sId]; + + int siteId = addr2site_map[sIdx]; + + int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); + + // (e) createNewCandidates(site) + //Generate new candidates by merging site_nbr to site_curr_scl + const int limit_x = site_curr_scl_idx[sIdx]; + const int limit_y = site_nbr_idx[sIdx]; + ///RESTRICTED NEW CANDIDATE SPACE EXPLORATION SET TO SLICE/8 or SLICE_CAPACITY/4 + const int limit_cands = DREAMPLACE_STD_NAMESPACE::min(SLICE_CAPACITY/4,limit_x*limit_y); + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: has %d candidates\n", sIdx, siteId, limit_cands); + //} + ////DBG + + for (int scsIdx = 0; scsIdx < limit_cands; ++scsIdx) + { + int sclId = scsIdx/limit_y; + int snIdx = scsIdx/limit_x; + int siteCurrIdx = validIndices_curr_scl[sSCL + sclId]; + + ///// + int sCKRId = siteCurrIdx*CKSR_IN_CLB; + int sCEId = siteCurrIdx*CE_IN_CLB; + int sFFId = siteCurrIdx*SLICE_CAPACITY; + int sGId = siteCurrIdx*SIG_IDX; + + T nwCand_score = site_curr_scl_score[siteCurrIdx]; + int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; + int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; + + //array instantiation + int nwCand_sig[SIG_MAX_CAP]; + int nwCand_lut[SLICE_MAX_CAP]; + int nwCand_ff[SLICE_MAX_CAP]; + int nwCand_ce[CE_MAX_CAP]; + int nwCand_cksr[CKSR_MAX_CAP]; + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: cand has %d sig elements\n", sIdx, siteId, nwCand_sigIdx); + //} + ////DBG + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; + nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; + } + + int instId = site_nbr[sNbrIdx + snIdx]; + int instPcl = instId*3; + + int addInstToSig = INVALID; + if (nwCand_sigIdx >= 2*SLICE_CAPACITY) + { + addInstToSig = 0; + } + + if (addInstToSig == INVALID) + { + int temp[4]; //Max precluster size = 3 + int tIdx(0); + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: Inst %d has %d precluster count\n", sIdx, siteId, instId, flat_node2prclstrCount[instId]); + //} + ////DBG + + for (int el = 0; el < flat_node2prclstrCount[instId]; ++el) + { + int newInstId = flat_node2precluster_map[instPcl+el]; + if (!val_in_array(nwCand_sig, nwCand_sigIdx, 0, newInstId)) + { + temp[tIdx] = newInstId; + ++tIdx; + } else + { + addInstToSig = 0; + break; + } + } + + if (addInstToSig == INVALID && (nwCand_sigIdx + tIdx > 2*SLICE_CAPACITY)) + { + addInstToSig = 0; + } + + if (addInstToSig == INVALID) + { + for (int mBIdx = 0; mBIdx < tIdx; ++mBIdx) + { + nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; + ++nwCand_sigIdx; + } + addInstToSig = 1; + } + } + + if (addInstToSig == 1) + { + //check cand sig is in site_next_pq + int candSigInSiteNextPQ = INVALID; + for (int i = 0; i < PQ_IDX; ++i) + { + int sigIdx = sPQ + i; + if (site_next_pq_validIdx[sigIdx] != INVALID) + { + if (site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) + { + int pqIdx(sigIdx*SIG_IDX), mtch(0); + + for (int k = 0; k < nwCand_sigIdx; ++k) + { + for (int l = 0; l < nwCand_sigIdx; ++l) + { + if (site_next_pq_sig[pqIdx + l] == nwCand_sig[k]) + { + ++mtch; + break; + } + } + } + if (mtch == nwCand_sigIdx) + { + candSigInSiteNextPQ = 1; + break; + } + } + } + } + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: cand in site_next_pq: %d \n", sIdx, siteId, candSigInSiteNextPQ); + //} + ////DBG + + if (candSigInSiteNextPQ == INVALID && + add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, + flat_node2pin_map, pin2net_map, pin_typeIds, flat_node2prclstrCount, + flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, + flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, + lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, half_ctrl_mode, + CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, + NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) + { + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: compute cand score\n", sIdx, siteId); + //} + ////DBG + + compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, + net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, + flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, + sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, + xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, + netShareScoreMaxNetDegree, wlscoreMaxNetDegree, half_ctrl_mode, + nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) + //{ + // printf("%d Site %d: cand score: %.2f \n", sIdx, siteId, nwCand_score); + //} + ////DBG + + int nxtId(INVALID); + + if (site_next_pq_idx[sIdx] < PQ_IDX) + { + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (site_next_pq_validIdx[sPQ+vId] == INVALID) + { + nxtId = vId; + ++site_next_pq_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < PQ_IDX; ++vId) + { + if (ckscore > site_next_pq_score[sPQ + vId]) + { + ckscore = site_next_pq_score[sPQ + vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + int nTId = sPQ + nxtId; + int nCKRId = nTId*CKSR_IN_CLB; + int nCEId = nTId*CE_IN_CLB; + int nFFId = nTId*SLICE_CAPACITY; + int nSGId = nTId*SIG_IDX; + + ///// + site_next_pq_validIdx[nTId] = 1; + site_next_pq_score[nTId] = nwCand_score; + site_next_pq_siteId[nTId] = nwCand_siteId; + site_next_pq_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + + if (site_next_pq_idx[sIdx] == 1 || + nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) + { + site_next_pq_top_idx[sIdx] = nxtId; + } + + nxtId = INVALID; + + if (site_next_scl_idx[sIdx] < SCL_IDX) + { + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (site_next_scl_validIdx[sSCL+vId] == INVALID) + { + nxtId = vId; + ++site_next_scl_idx[sIdx]; + break; + } + } + } else + { + //find least score and replace if current score is greater + T ckscore(nwCand_score); + for (int vId = 0; vId < SCL_IDX; ++vId) + { + if (ckscore > site_next_scl_score[sSCL+vId]) + { + ckscore = site_next_scl_score[sSCL+vId]; + nxtId = vId; + } + } + } + + if (nxtId != INVALID) + { + ///// + nTId = sSCL + nxtId; + nCKRId = nTId*CKSR_IN_CLB; + nCEId = nTId*CE_IN_CLB; + nFFId = nTId*SLICE_CAPACITY; + nSGId = nTId*SIG_IDX; + + site_next_scl_validIdx[nTId] = 1; + site_next_scl_score[nTId] = nwCand_score; + site_next_scl_siteId[nTId] = nwCand_siteId; + site_next_scl_sig_idx[nTId] = nwCand_sigIdx; + + for (int sg = 0; sg < nwCand_sigIdx; ++sg) + { + site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; + site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; + } + ///// + } + } + } + } + + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) + //{ + // printf("%d Site %d: completed big for loop\n", sIdx, siteId); + //} + ////DBG + + //Remove all candidates in scl that is worse than the worst candidate in PQ + if (site_next_pq_idx[sIdx] > 0) + { + //Find worst candidate in PQ + T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); + + for (int vId = 0; vId < PQ_IDX; ++vId) + { + int nPQId = sPQ + vId; + if (site_next_pq_validIdx[nPQId] != INVALID) + { + if (ckscore > site_next_pq_score[nPQId]) + { + ckscore = site_next_pq_score[nPQId]; + } + } + } + + //Invalidate worst ones in scl + int sclCount = 0; + int maxEntries = site_next_scl_idx[sIdx]; + for (int ckId = 0; ckId < SCL_IDX; ++ckId) + { + int vId = sSCL + ckId; + if (site_next_scl_validIdx[vId] != INVALID) + { + if (ckscore > site_next_scl_score[vId]) + { + //Clear contents thoroughly + clear_cand_contents( + vId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce); + + site_next_scl_validIdx[vId] = INVALID; + site_next_scl_sig_idx[vId] = 0; + site_next_scl_siteId[vId] = INVALID; + site_next_scl_score[vId] = 0.0; + --site_next_scl_idx[sIdx]; + } + ++sclCount; + if (sclCount == maxEntries) + { + break; + } + } + } + } + + //Update stable Iteration count + if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && + compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, + site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, + site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, + site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, + site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, + site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, + site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, + sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) + { + site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; + } else + { + site_next_stable[sIdx] = 0; + } + + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) + //{ + // printf("%d Site %d: completed (e) \n", sIdx, siteId); + //} + ////DBG + + //// (f) broadcastTopCandidate(site) + if (site_next_pq_idx[sIdx] > 0) + { + int tpIdx = sPQ + site_next_pq_top_idx[sIdx]; + int topSigId = tpIdx*SIG_IDX; + + T scoreImprov = site_next_pq_score[tpIdx] - site_det_score[sIdx]; + + ////UPDATED SEQUENTIAL PORTION + int scoreImprovInt = DREAMPLACE_STD_NAMESPACE::max(int(scoreImprov*10000), intMinVal); + site_score_improv[sIdx] = scoreImprovInt + siteId; + + for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[tpIdx]; ++ssIdx) + { + int instId = site_next_pq_sig[topSigId + ssIdx]; + + if (inst_curr_detSite[instId] == INVALID) + { + atomicMax(&inst_score_improv[instId], scoreImprovInt); + } + } + } + ////DBG + //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) + //{ + // printf("%d Site %d: completed (f) \n", sIdx, siteId); + //} + ////DBG + } +} + +//runDLSyncSites +template +__global__ void runDLSyncSites( + const int* site_nbrRanges_idx, + const int* site_nbrGroup_idx, + const int* addr2site_map, + const int num_clb_sites, + const int SLICE_CAPACITY, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + int* site_curr_pq_top_idx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_validIdx, + int* site_curr_pq_siteId, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* site_curr_scl_validIdx, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_validIdx, + T* site_next_pq_score, + int* site_next_pq_top_idx, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_idx, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + int* activeStatus) +{ + int sIdx = threadIdx.x + blockDim.x * blockIdx.x; + while(sIdx < num_clb_sites) + { + int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; + int sPQ = sIdx*SCL_IDX; + + site_curr_stable[sIdx] = site_next_stable[sIdx]; + + int curr_scl_size = site_curr_scl_idx[sIdx]; + site_curr_scl_idx[sIdx] = 0; + int sclCount = 0; + + //Include valid entries of site.next.scl to site.curr.scl + if (site_next_scl_idx[sIdx] > 0) + { + for (int id = 0; id < SCL_IDX; ++id) + { + int vIdx = sPQ+id; + if (site_next_scl_validIdx[vIdx] != INVALID) + { + int currId = sPQ+site_curr_scl_idx[sIdx]; + + site_curr_scl_validIdx[currId] = 1; + site_curr_scl_siteId[currId] = site_next_scl_siteId[vIdx]; + site_curr_scl_score[currId] = site_next_scl_score[vIdx]; + site_curr_scl_sig_idx[currId] = site_next_scl_sig_idx[vIdx]; + + int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); + int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); + int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); + int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); + + for (int sg = 0; sg < site_next_scl_sig_idx[vIdx]; ++sg) + { + site_curr_scl_sig[currSGId + sg] = site_next_scl_sig[nxtSGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_scl_impl_lut[currFFId + sg] = site_next_scl_impl_lut[nxtFFId + sg]; + site_curr_scl_impl_ff[currFFId + sg] = site_next_scl_impl_ff[nxtFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_scl_impl_cksr[currCKId + sg] = site_next_scl_impl_cksr[nxtCKId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_scl_impl_ce[currCEId + sg] = site_next_scl_impl_ce[nxtCEId + sg]; + } + ++site_curr_scl_idx[sIdx]; + ++sclCount; + if (sclCount == site_next_scl_idx[sIdx]) + { + break; + } + } + } + } + + ////Invalidate the rest in site.curr.scl + if (curr_scl_size > site_next_scl_idx[sIdx]) + { + for (int ckId = site_curr_scl_idx[sIdx]; ckId < SCL_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + if (site_curr_scl_validIdx[vIdx] != INVALID) + { + site_curr_scl_validIdx[vIdx] = INVALID; + site_curr_scl_sig_idx[vIdx] = 0; + site_curr_scl_siteId[vIdx] = INVALID; + site_curr_scl_score[vIdx] = T(0.0); + ++sclCount; + if (sclCount == curr_scl_size) + { + break; + } + } + } + } + + int curr_pq_size = site_curr_pq_idx[sIdx]; + site_curr_pq_idx[sIdx] = 0; + site_curr_pq_top_idx[sIdx] = INVALID; + + sPQ = sIdx*PQ_IDX; + sclCount = 0; + //Include valid entries of site.next.pq to site.curr.pq + if (site_next_pq_idx[sIdx] > 0) + { + for (int id = 0; id < PQ_IDX; ++id) + { + int vIdx = sPQ+id; + if (site_next_pq_validIdx[vIdx] != INVALID) + { + int currId = sPQ+site_curr_pq_idx[sIdx]; + + site_curr_pq_validIdx[currId] = 1; + site_curr_pq_siteId[currId] = site_next_pq_siteId[vIdx]; + site_curr_pq_score[currId] = site_next_pq_score[vIdx]; + site_curr_pq_sig_idx[currId] = site_next_pq_sig_idx[vIdx]; + + int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); + int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); + int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); + int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); + + for (int sg = 0; sg < site_next_pq_sig_idx[vIdx]; ++sg) + { + site_curr_pq_sig[currSGId + sg] = site_next_pq_sig[nxtSGId + sg]; + } + for (int sg = 0; sg < SLICE_CAPACITY; ++sg) + { + site_curr_pq_impl_lut[currFFId + sg] = site_next_pq_impl_lut[nxtFFId + sg]; + site_curr_pq_impl_ff[currFFId + sg] = site_next_pq_impl_ff[nxtFFId + sg]; + } + for (int sg = 0; sg < CKSR_IN_CLB; ++sg) + { + site_curr_pq_impl_cksr[currCKId + sg] = site_next_pq_impl_cksr[nxtCKId + sg]; + } + for (int sg = 0; sg < CE_IN_CLB; ++sg) + { + site_curr_pq_impl_ce[currCEId + sg] = site_next_pq_impl_ce[nxtCEId + sg]; + } + if (id == site_next_pq_top_idx[sIdx]) + { + site_curr_pq_top_idx[sIdx] = site_curr_pq_idx[sIdx]; + } + ++site_curr_pq_idx[sIdx]; + ++sclCount; + if (sclCount == site_next_pq_idx[sIdx]) + { + break; + } + } + } + } + + //Invalidate the rest in site.curr.pq + if (curr_pq_size > site_next_pq_idx[sIdx]) + { + for (int ckId = site_curr_pq_idx[sIdx]; ckId < PQ_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + if (site_curr_pq_validIdx[vIdx] != INVALID) + { + site_curr_pq_validIdx[vIdx] = INVALID; + site_curr_pq_sig_idx[vIdx] = 0; + site_curr_pq_siteId[vIdx] = INVALID; + site_curr_pq_score[vIdx] = T(0.0); + ++sclCount; + if (sclCount == curr_pq_size) + { + break; + } + } + } + } + + sPQ = sIdx*SCL_IDX; + //sclCount = 0; + for (int ckId = 0; ckId < SCL_IDX; ++ckId) + { + int vIdx = sPQ+ckId; + //if (site_next_scl_validIdx[vIdx] != INVALID) + //{ + //Clear contents thoroughly + clear_cand_contents( + vIdx, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, + site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce); + + site_next_scl_validIdx[vIdx] = INVALID; + site_next_scl_sig_idx[vIdx] = 0; + site_next_scl_siteId[vIdx] = INVALID; + site_next_scl_score[vIdx] = 0.0; + //++sclCount; + //if (sclCount == site_next_scl_idx[sIdx]) + //{ + // break; + //} + //} + } + site_next_scl_idx[sIdx] = 0; + + activeStatus[addr2site_map[sIdx]] = (site_curr_pq_idx[sIdx] > 0 || + site_curr_scl_idx[sIdx] > 0 || + site_nbrGroup_idx[sIdx] < numNbrGroups) ? 1: 0; + + sIdx += blockDim.x * gridDim.x; + } +} + +//runDLSyncInsts +template +__global__ void runDLSyncInsts( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const int* site_types, + const int* site2addr_map, + const int* spiral_accessor, + const int* lut_flop_indices, + const int* site_score_improv, + const int* site_curr_pq_top_idx, + const int* site_curr_pq_sig_idx, + const int* site_curr_pq_sig, + const int* site_curr_pq_idx, + const T maxDist, + const int spiralBegin, + const int spiralEnd, + const int intMinVal, + const int num_nodes, + const int num_sites_x, + const int num_sites_y, + const int sliceId, + const int maxSites, + const int SIG_IDX, + const int PQ_IDX, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* inst_score_improv, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* illegalStatus) +{ + int nIdx = threadIdx.x + blockDim.x * blockIdx.x; + while(nIdx < num_nodes) + { + const int nodeId = lut_flop_indices[nIdx]; + if (inst_curr_detSite[nodeId] == INVALID) //Only LUT/FF + { + //POST PROCESSING TO IDENTIFY INSTANCE BEST SITE + //REPLACEMENT FOR SEQUENTIAL PORTION + if (inst_score_improv[nodeId] > intMinVal) + { + int bestSite = maxSites; + int &instScoreImprov = inst_score_improv[nodeId]; + T instScoreImprovT = T(instScoreImprov/10000.0); + T posX = pos_x[nodeId]; + T posY = pos_y[nodeId]; + + for (int spIdx = spiralBegin; spIdx < spiralEnd; ++spIdx) + { + int saIdx = spIdx*2; + int xVal = posX + spiral_accessor[saIdx]; + int yVal = posY + spiral_accessor[saIdx+1]; + + int siteId = xVal * num_sites_y + yVal; + + if (xVal >= 0 && xVal < num_sites_x && + yVal >= 0 && yVal < num_sites_y && + site_types[siteId] == sliceId) + { + int stMpId = siteId *2; + int sIdx = site2addr_map[siteId]; + int tsPQ = sIdx*PQ_IDX + site_curr_pq_top_idx[sIdx]; + int topIdx = tsPQ*SIG_IDX; + int site_score = site_score_improv[sIdx] - siteId; + + T dist = DREAMPLACE_STD_NAMESPACE::abs(posX - site_xy[stMpId]) + + DREAMPLACE_STD_NAMESPACE::abs(posY - site_xy[stMpId+1]); + + if (instScoreImprov == site_score && + site_curr_pq_idx[sIdx] > 0) + { + for (int idx = 0; idx < site_curr_pq_sig_idx[tsPQ]; ++idx) + { + if (site_curr_pq_sig[topIdx+idx] == nodeId && + siteId < bestSite && dist < maxDist) + { + bestSite = siteId; + inst_next_bestSite[nodeId] = siteId; + inst_next_bestScoreImprov[nodeId] = instScoreImprovT; + } + } + } + } + } + instScoreImprov = intMinVal; + } + //END post processing + + + inst_curr_detSite[nodeId] = inst_next_detSite[nodeId]; + inst_curr_bestSite[nodeId] = inst_next_bestSite[nodeId]; + inst_curr_bestScoreImprov[nodeId] = inst_next_bestScoreImprov[nodeId]; + + inst_next_bestSite[nodeId] = INVALID; + inst_next_bestScoreImprov[nodeId] = T(-10000.0); + + illegalStatus[nodeId] = (inst_curr_detSite[nodeId] == INVALID) ? 1 : 0; + } + nIdx += blockDim.x * gridDim.x; + } +} + +//legalize Mlab +template +__global__ void legalizeMlab_kernel( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const T* mlab_locX, + const T* mlab_locY, + const int* mlab_indices, + const int* site2addr_map, + const T max_score, + const int num_mlab_nodes, + const int num_sites_y, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* dist_moved, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes) +{ + int nIdx = threadIdx.x + blockDim.x * blockIdx.x; + while(nIdx < num_mlab_nodes) + { + const int instId = mlab_indices[nIdx]; + + T xVal = mlab_locX[nIdx]; + T yVal = mlab_locY[nIdx]; + + int siteId = xVal * num_sites_y + yVal; + int sIdx = site2addr_map[siteId]; + + dist_moved[nIdx] = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); + + int sdtopId = sIdx*SIG_IDX; + int sdlutId = sIdx*SLICE_CAPACITY; + + if (site_det_sig_idx[sIdx] == 0) + { + sites_with_special_nodes[sIdx] = 1; + site_det_score[sIdx] = max_score; + site_det_siteId[sIdx] = siteId; + + site_det_sig_idx[sIdx] = 2*SLICE_CAPACITY; + site_det_sig[sdtopId] = instId; + site_det_impl_lut[sdlutId] = instId; + + inst_curr_detSite[instId] = siteId; + inst_curr_bestSite[instId] = siteId; + inst_curr_bestScoreImprov[instId] = max_score; + + inst_next_detSite[instId] = siteId; + inst_next_bestSite[instId] = siteId; + inst_next_bestScoreImprov[instId] = max_score; + } + //DBG + else { + printf("ERROR: Slice site not empty - MLAB %d not legalized at (%.2f, %.2f)\n", instId, xVal, yVal); + } + //DBG + + nIdx += blockDim.x * gridDim.x; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//init nets and precluster +template +int initLGCudaLauncher( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const int* sorted_node_map, + const int* sorted_node_idx, + const int* sorted_net_idx, + const int* flat_net2pin_map, + const int* flat_net2pin_start_map, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* node2fence_region_map, + const int* node2outpinIdx_map, + const int* pin2net_map, + const int* pin2node_map, + const int* pin_typeIds, + const int* net2pincount, + const int* is_mlab_node, + const T preClusteringMaxDist, + const int ffId, + const int lutId, + const int num_nets, + const int num_nodes, + const int wlscoreMaxNetDegree, + T* net_bbox, + int* net_pinIdArrayX, + int* net_pinIdArrayY, + int* flat_node2precluster_map, + int* flat_node2prclstrCount) +{ + int block_count = ceilDiv(num_nets + THREAD_COUNT-1, THREAD_COUNT); + initNets<<>>( + pos_x, pos_y, pin_offset_x, pin_offset_y, + flat_net2pin_start_map, flat_net2pin_map, + sorted_net_idx, pin2node_map, net2pincount, + num_nets, wlscoreMaxNetDegree, net_bbox, + net_pinIdArrayX, net_pinIdArrayY); + + int nodes_block_count = ceilDiv(num_nodes + THREAD_COUNT-1, THREAD_COUNT); + preClustering<<>>( + pos_x, pos_y, pin_offset_x, pin_offset_y, + sorted_node_map, sorted_node_idx, + flat_net2pin_start_map, flat_net2pin_map, + flop2ctrlSetId_map, flop_ctrlSets, + node2fence_region_map, node2outpinIdx_map, + pin2net_map, pin2node_map, pin_typeIds, + is_mlab_node, preClusteringMaxDist, + lutId, ffId, num_nodes, + flat_node2precluster_map, flat_node2prclstrCount); + + cudaDeviceSynchronize(); + return 0; +} + +//runDLIter +template +int runDLCudaLauncher( + const T* pos_x, + const T* pos_y, + const T* pin_offset_x, + const T* pin_offset_y, + const T* net_bbox, + const T* site_xy, + const T* net_weights, + const int* net_pinIdArrayX, + const int* net_pinIdArrayY, + const int* site_types, + const int* spiral_accessor, + const int* node2fence_region_map, + const int* lut_flop_indices, + const int* flop2ctrlSetId_map, + const int* flop_ctrlSets, + const int* extended_ctrlSets, + const int* ext_ctrlSet_start_map, + const int* lut_type, + const int* flat_node2pin_start_map, + const int* flat_node2pin_map, + const int* node2outpinIdx_map, + const int* node2pincount, + const int* net2pincount, + const int* pin2net_map, + const int* pin_typeIds, + const int* flat_net2pin_start_map, + const int* pin2node_map, + const int* sorted_net_map, + const int* sorted_node_map, + const int* flat_node2prclstrCount, + const int* flat_node2precluster_map, + const int* is_mlab_node, + const int* site_nbrList, + const int* site_nbrRanges, + const int* site_nbrRanges_idx, + const int* addr2site_map, + const int* site2addr_map, + const int* special_nodes, + const T maxDist, + const T xWirelenWt, + const T yWirelenWt, + const T wirelenImprovWt, + const T extNetCountWt, + const int num_sites_x, + const int num_sites_y, + const int num_clb_sites, + const int num_lutflops, + const int minStableIter, + const int maxList, + const int half_ctrl_mode, + const int SLICE_CAPACITY, + const int HALF_SLICE_CAPACITY, + const int BLE_CAPACITY, + const int NUM_BLE_PER_SLICE, + const int minNeighbors, + const int spiralBegin, + const int spiralEnd, + const int intMinVal, + const int numGroups, + const int netShareScoreMaxNetDegree, + const int wlscoreMaxNetDegree, + const int lutTypeInSliceUnit, + const int lut_maxShared, + const int CKSR_IN_CLB, + const int CE_IN_CLB, + const int SCL_IDX, + const int PQ_IDX, + const int SIG_IDX, + const int lutId, + const int ffId, + const int sliceId, + int* site_nbr_idx, + int* site_nbr, + int* site_nbrGroup_idx, + int* site_curr_pq_top_idx, + int* site_curr_pq_sig_idx, + int* site_curr_pq_sig, + int* site_curr_pq_idx, + int* site_curr_stable, + int* site_curr_pq_siteId, + int* site_curr_pq_validIdx, + T* site_curr_pq_score, + int* site_curr_pq_impl_lut, + int* site_curr_pq_impl_ff, + int* site_curr_pq_impl_cksr, + int* site_curr_pq_impl_ce, + T* site_curr_scl_score, + int* site_curr_scl_siteId, + int* site_curr_scl_idx, + int* cumsum_curr_scl, + int* site_curr_scl_validIdx, + int* validIndices_curr_scl, + int* site_curr_scl_sig_idx, + int* site_curr_scl_sig, + int* site_curr_scl_impl_lut, + int* site_curr_scl_impl_ff, + int* site_curr_scl_impl_cksr, + int* site_curr_scl_impl_ce, + int* site_next_pq_idx, + int* site_next_pq_validIdx, + int* site_next_pq_top_idx, + T* site_next_pq_score, + int* site_next_pq_siteId, + int* site_next_pq_sig_idx, + int* site_next_pq_sig, + int* site_next_pq_impl_lut, + int* site_next_pq_impl_ff, + int* site_next_pq_impl_cksr, + int* site_next_pq_impl_ce, + T* site_next_scl_score, + int* site_next_scl_siteId, + int* site_next_scl_idx, + int* site_next_scl_validIdx, + int* site_next_scl_sig_idx, + int* site_next_scl_sig, + int* site_next_scl_impl_lut, + int* site_next_scl_impl_ff, + int* site_next_scl_impl_cksr, + int* site_next_scl_impl_ce, + int* site_next_stable, + T* site_det_score, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* site_det_impl_ff, + int* site_det_impl_cksr, + int* site_det_impl_ce, + int* inst_curr_detSite, + T* inst_curr_bestScoreImprov, + int* inst_curr_bestSite, + int* inst_next_detSite, + T* inst_next_bestScoreImprov, + int* inst_next_bestSite, + int* activeStatus, + int* illegalStatus, + int* inst_score_improv, + int* site_score_improv, + int* sorted_clb_siteIds + ) +{ + int block_count = ceilDiv(num_clb_sites + THREAD_COUNT-1, THREAD_COUNT); + + //- Uncomment below line for debg + //DL kernel split Implementation to enable scheduling + //Use below line for debg + //runDLIteration_kernel_1<<<1, 1>>>( + runDLIteration_kernel_1<<>>( + node2fence_region_map, flop2ctrlSetId_map, flop_ctrlSets, + extended_ctrlSets, ext_ctrlSet_start_map, lut_type, + flat_node2pin_start_map, flat_node2pin_map, node2outpinIdx_map, pin2net_map, + pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, + is_mlab_node, site_nbrList, site_nbrRanges, site_nbrRanges_idx, + addr2site_map, special_nodes, num_clb_sites, minStableIter, + maxList, half_ctrl_mode, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, + numGroups, lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, CE_IN_CLB, + SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, site_nbr_idx, site_nbr, + site_nbrGroup_idx, site_curr_pq_top_idx, site_curr_pq_sig_idx, + site_curr_pq_sig, site_curr_pq_idx, site_curr_stable, + site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, + site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, + site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, + site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, + site_curr_scl_impl_ce, site_next_pq_idx, site_next_pq_validIdx, + site_next_pq_top_idx, site_next_pq_impl_lut, site_next_pq_impl_ff, + site_next_pq_impl_cksr, site_next_pq_impl_ce, + site_next_pq_score, site_next_pq_siteId, + site_next_pq_sig_idx, site_next_pq_sig, site_det_score, + site_det_siteId, site_det_sig_idx, site_det_sig, site_det_impl_lut, + site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, + inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, + validIndices_curr_scl, cumsum_curr_scl); + + cudaDeviceSynchronize(); + //printf("Completed ruNDLIter_k1"); + + //////Use thrust to sort cumsum_curr_scl to compute sorted siteIds based on load + thrust::device_ptr cumsum_ptr = thrust::device_pointer_cast(cumsum_curr_scl); + thrust::device_ptr sortedId_ptr = thrust::device_pointer_cast(sorted_clb_siteIds); + + thrust::sequence(sortedId_ptr, sortedId_ptr+num_clb_sites, 0); + thrust::sort_by_key(cumsum_ptr, cumsum_ptr+num_clb_sites, sortedId_ptr, thrust::greater()); + //Note: order of cumsum_curr_scl is also changed but it is not used in the next steps + //printf("Completed sorting of indices\n"); + + //Use below line for debg + //runDLIteration_kernel_2<<<1, 1>>>( + runDLIteration_kernel_2<<>>( + pos_x, pos_y, pin_offset_x, pin_offset_y, net_bbox, site_xy, + net_weights, net_pinIdArrayX, net_pinIdArrayY, node2fence_region_map, + flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, + ext_ctrlSet_start_map, flat_net2pin_start_map, + flat_node2pin_start_map, flat_node2pin_map, node2outpinIdx_map, lut_type, net2pincount, + pin2net_map, pin_typeIds, pin2node_map, sorted_node_map, + sorted_net_map, flat_node2prclstrCount, flat_node2precluster_map, + validIndices_curr_scl, sorted_clb_siteIds, addr2site_map, + special_nodes, xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, + num_clb_sites, intMinVal, maxList, half_ctrl_mode, SLICE_CAPACITY, + HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, netShareScoreMaxNetDegree, + wlscoreMaxNetDegree, lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, + CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, site_nbr_idx, + site_nbr, site_curr_pq_top_idx, site_curr_pq_validIdx, + site_curr_pq_sig_idx, site_curr_pq_sig, site_curr_pq_impl_lut, + site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, + site_curr_pq_idx, site_curr_stable, site_curr_pq_siteId, + site_curr_pq_score, site_curr_scl_score, site_curr_scl_siteId, + site_curr_scl_idx, site_curr_scl_validIdx, site_curr_scl_sig_idx, + site_curr_scl_sig, site_curr_scl_impl_lut, site_curr_scl_impl_ff, + site_curr_scl_impl_cksr, site_curr_scl_impl_ce, site_next_pq_idx, + site_next_pq_validIdx, site_next_pq_top_idx, site_next_pq_score, + site_next_pq_siteId, site_next_pq_sig_idx, site_next_pq_sig, + site_next_pq_impl_lut, site_next_pq_impl_ff, site_next_pq_impl_cksr, + site_next_pq_impl_ce, site_next_scl_score, site_next_scl_siteId, + site_next_scl_idx, site_next_scl_validIdx, site_next_scl_sig_idx, + site_next_scl_sig, site_next_scl_impl_lut, site_next_scl_impl_ff, + site_next_scl_impl_cksr, site_next_scl_impl_ce, site_next_stable, + site_det_score, inst_curr_detSite, inst_next_bestScoreImprov, + inst_next_bestSite, inst_score_improv, site_score_improv); + + cudaDeviceSynchronize(); + //printf("Completed ruNDLIter_k2\n"); + + ////Use either combined runDLIteration or runDLIter1 + runDLIter2 + //runDLIteration<<>>( + // pos_x, pos_y, pin_offset_x, pin_offset_y, net_bbox, site_xy, + // net_pinIdArrayX, net_pinIdArrayY, node2fence_region_map, + // flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, + // ext_ctrlSet_start_map, lut_type, flat_node2pin_start_map, + // flat_node2pin_map, node2outpinIdx_map, net2pincount, pin2net_map, pin_typeIds, + // flat_net2pin_start_map, pin2node_map, sorted_net_map, sorted_node_map, + // flat_node2prclstrCount, flat_node2precluster_map, is_mlab_node, site_nbrList, + // site_nbrRanges, site_nbrRanges_idx, net_weights, addr2site_map, + // special_nodes, num_clb_sites, minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, + // HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, + // intMinVal, numGroups, netShareScoreMaxNetDegree, wlscoreMaxNetDegree, + // lutTypeInSliceUnit, lut_maxShared, xWirelenWt, yWirelenWt, + // wirelenImprovWt, extNetCountWt, CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, + // PQ_IDX, SIG_IDX, lutId, ffId, validIndices_curr_scl, site_nbr_idx, + // site_nbr, site_nbrGroup_idx, site_curr_pq_top_idx, site_curr_pq_validIdx, site_curr_pq_sig_idx, + // site_curr_pq_sig, site_curr_pq_idx, site_curr_stable, + // site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, + // site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, + // site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, + // site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, + // site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, + // site_curr_scl_impl_ce, site_next_pq_idx, site_next_pq_validIdx, + // site_next_pq_top_idx, site_next_pq_score, site_next_pq_siteId, + // site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, + // site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, + // site_next_scl_score, site_next_scl_siteId, site_next_scl_idx, + // site_next_scl_validIdx, site_next_scl_sig_idx, site_next_scl_sig, + // site_next_scl_impl_lut, site_next_scl_impl_ff, site_next_scl_impl_cksr, + // site_next_scl_impl_ce, site_next_stable, site_det_score, + // site_det_siteId, site_det_sig_idx, site_det_sig, site_det_impl_lut, + // site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, + // inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, + // inst_next_bestScoreImprov, inst_next_bestSite, inst_score_improv, + // site_score_improv); + //cudaDeviceSynchronize(); + + int nodes_block_count = ceilDiv(num_lutflops + THREAD_COUNT - 1, THREAD_COUNT); + int maxSites = num_sites_x*num_sites_y; + + runDLSyncInsts<<>>( + pos_x, pos_y, site_xy, site_types, site2addr_map, spiral_accessor, + lut_flop_indices, site_score_improv, site_curr_pq_top_idx, + site_curr_pq_sig_idx, site_curr_pq_sig, site_curr_pq_idx, maxDist, + spiralBegin, spiralEnd, intMinVal, num_lutflops, num_sites_x, + num_sites_y, sliceId, maxSites, SIG_IDX, PQ_IDX, + inst_curr_bestScoreImprov, inst_next_bestScoreImprov, inst_score_improv, + inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, + inst_next_bestSite, illegalStatus); + + cudaDeviceSynchronize(); + //printf("End of runDLSyncInsts\n"); + + runDLSyncSites<<>>( + site_nbrRanges_idx, site_nbrGroup_idx, addr2site_map, num_clb_sites, + SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, + site_curr_pq_top_idx, site_curr_pq_sig_idx, site_curr_pq_sig, + site_curr_pq_idx, site_curr_stable, site_curr_pq_validIdx, + site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, + site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, + site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, + site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, + site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, + site_curr_scl_impl_ce, site_next_pq_validIdx, site_next_pq_score, + site_next_pq_top_idx, site_next_pq_siteId, site_next_pq_sig_idx, + site_next_pq_sig, site_next_pq_idx, site_next_pq_impl_lut, + site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, + site_next_scl_score, site_next_scl_siteId, site_next_scl_idx, + site_next_scl_validIdx, site_next_scl_sig_idx, site_next_scl_sig, + site_next_scl_impl_lut, site_next_scl_impl_ff, site_next_scl_impl_cksr, + site_next_scl_impl_ce, site_next_stable, activeStatus); + + cudaDeviceSynchronize(); + //printf("End of runDLSyncSites\n"); + + return 0; +} + +//legalize Mlab +template +int legalizeMlabCudaLauncher( + const T* pos_x, + const T* pos_y, + const T* site_xy, + const T* mlab_locX, + const T* mlab_locY, + const int* mlab_indices, + const int* site2addr_map, + const int num_mlab_nodes, + const int num_sites_y, + const int SIG_IDX, + const int SLICE_CAPACITY, + T* dist_moved, + T* site_det_score, + T* inst_curr_bestScoreImprov, + T* inst_next_bestScoreImprov, + int* site_det_siteId, + int* site_det_sig_idx, + int* site_det_sig, + int* site_det_impl_lut, + int* inst_curr_detSite, + int* inst_curr_bestSite, + int* inst_next_detSite, + int* inst_next_bestSite, + int* sites_with_special_nodes) +{ + int block_count = ceilDiv(num_mlab_nodes + THREAD_COUNT-1, THREAD_COUNT); + + T max_score = T(10000.0); + + legalizeMlab_kernel<<>>( + pos_x, pos_y, site_xy, mlab_locX, mlab_locY, mlab_indices, + site2addr_map, max_score, num_mlab_nodes, num_sites_y, + SIG_IDX, SLICE_CAPACITY, dist_moved, site_det_score, + inst_curr_bestScoreImprov, inst_next_bestScoreImprov, + site_det_siteId, site_det_sig_idx, site_det_sig, + site_det_impl_lut, inst_curr_detSite, inst_curr_bestSite, + inst_next_detSite, inst_next_bestSite, sites_with_special_nodes); + + cudaDeviceSynchronize(); + //printf("Completed legalize Mlab"); + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int initLGCudaLauncher \ + (const T* pos_x, const T* pos_y, const T* pin_offset_x, \ + const T* pin_offset_y, const int* sorted_node_map, \ + const int* sorted_node_idx, const int* sorted_net_idx, \ + const int* flat_net2pin_map, const int* flat_net2pin_start_map, \ + const int* flop2ctrlSetId_map, const int* flop_ctrlSets, \ + const int* node2fence_region_map, const int* node2outpinIdx_map, \ + const int* pin2net_map, const int* pin2node_map, \ + const int* pin_typeIds, const int* net2pincount, \ + const int* is_mlab_node, const T preClusteringMaxDist, \ + const int ffId, const int lutId, const int num_nets, \ + const int num_nodes, const int WLscoreMaxNetDegre, T* net_bbox, \ + int* net_pinIdArrayX, int* net_pinIdArrayY, int* flat_node2precluster_map, \ + int* flat_node2prclstrCount); \ + \ + template int runDLCudaLauncher \ + (const T* pos_x, const T* pos_y, const T* pin_offset_x, \ + const T* pin_offset_y, const T* net_bbox, const T* site_xy, \ + const T* net_weights, const int* net_pinIdArrayX, \ + const int* net_pinIdArrayY, const int* site_types, \ + const int* spiral_accessor, const int* node2fence_region_map, \ + const int* lut_flop_indices, const int* flop2ctrlSetId_map, \ + const int* flop_ctrlSets, const int* extended_ctrlSets, \ + const int* ext_ctrlSet_start_map, const int* lut_type, \ + const int* flat_node2pin_start_map, const int* flat_node2pin_map, \ + const int* node2outpinIdx_map, const int* node2pincount, \ + const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, \ + const int* flat_net2pin_start_map, const int* pin2node_map, \ + const int* sorted_net_map, const int* sorted_node_map, \ + const int* flat_node2prclstrCount, const int* flat_node2precluster_map, \ + const int* is_mlab_node, const int* site_nbrList, \ + const int* site_nbrRanges, const int* site_nbrRanges_idx, \ + const int* addr2site_map, const int* site2addr_map, \ + const int* special_nodes, const T maxDist, const T xWirelenWt, \ + const T yWirelenWt, const T wirelenImprovWt, const T extNetCountWt, \ + const int num_sites_x, const int num_sites_y, \ + const int num_clb_sites, const int num_lutflops, \ + const int minStableIter, const int maxList, const int half_ctrl_mode, \ + const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, \ + const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, \ + const int minNeighbors, const int spiralBegin, const int spiralEnd, \ + const int intMinVal, const int numGroups, \ + const int netShareScoreMaxNetDegree, const int wlscoreMaxNetDegree, \ + const int lutTypeInSliceUnit, const int lut_maxShared, \ + const int CKSR_IN_CLB, const int CE_IN_CLB, const int SCL_IDX, \ + const int PQ_IDX, const int SIG_IDX, const int ludId, \ + const int ffId, const int sliceId, int* site_nbr_idx, \ + int* site_nbr, int* site_nbrGroup_idx, int* site_curr_pq_top_idx, \ + int* site_curr_pq_sig_idx, int* site_curr_pq_sig, \ + int* site_curr_pq_idx, int* site_curr_stable, \ + int* site_curr_pq_siteId, int* site_curr_pq_validIdx, \ + T* site_curr_pq_score, int* site_curr_pq_impl_lut, \ + int* site_curr_pq_impl_ff, int* site_curr_pq_impl_cksr, \ + int* site_curr_pq_impl_ce, T* site_curr_scl_score, \ + int* site_curr_scl_siteId, int* site_curr_scl_idx, \ + int* cumsum_curr_scl, int* site_curr_scl_validIdx, \ + int* validIndices_curr_scl, int* site_curr_scl_sig_idx, \ + int* site_curr_scl_sig, int* site_curr_scl_impl_lut, \ + int* site_curr_scl_impl_ff, int* site_curr_scl_impl_cksr, \ + int* site_curr_scl_impl_ce, int* site_next_pq_idx, \ + int* site_next_pq_validIdx, int* site_next_pq_top_idx, \ + T* site_next_pq_score, int* site_next_pq_siteId, \ + int* site_next_pq_sig_idx, int* site_next_pq_sig, \ + int* site_next_pq_impl_lut, int* site_next_pq_impl_ff, \ + int* site_next_pq_impl_cksr, int* site_next_pq_impl_ce, \ + T* site_next_scl_score, int* site_next_scl_siteId, \ + int* site_next_scl_idx, int* site_next_scl_validIdx, \ + int* site_next_scl_sig_idx, int* site_next_scl_sig, \ + int* site_next_scl_impl_lut, int* site_next_scl_impl_ff, \ + int* site_next_scl_impl_cksr, int* site_next_scl_impl_ce, \ + int* site_next_stable, T* site_det_score, int* site_det_siteId, \ + int* site_det_sig_idx, int* site_det_sig, int* site_det_impl_lut, \ + int* site_det_impl_ff, int* site_det_impl_cksr, \ + int* site_det_impl_ce, int* inst_curr_detSite, \ + T* inst_curr_bestScoreImprov, int* inst_curr_bestSite, \ + int* inst_next_detSite, T* inst_next_bestScoreImprov, \ + int* inst_next_bestSite, int* activeStatus, int* illegalStatus, \ + int* inst_score_improv, int* site_score_improv, \ + int* sorted_clb_siteIds); \ + \ + template int legalizeMlabCudaLauncher \ + (const T* pos_x, const T* pos_y, const T* site_xy, const T* mlab_locX, \ + const T* mlab_locY, const int* mlab_indices, const int* site2addr_map, \ + const int num_mlab_nodes, const int num_sites_y, const int SIG_IDX, \ + const int SLICE_CAPACITY, T* dist_moved, T* site_det_score, \ + T* inst_curr_bestScoreImprov, T* inst_next_bestScoreImprov, \ + int* site_det_siteId, int* site_det_sig_idx, int* site_det_sig, \ + int* site_det_impl_lut, int* inst_curr_detSite, int* inst_curr_bestSite, \ + int* inst_next_detSite, int* inst_next_bestSite, \ + int* sites_with_special_nodes); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt new file mode 100644 index 00000000..5c7c06c6 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt @@ -0,0 +1,35 @@ +set(OP_NAME move_boundary) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/move_boundary.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/move_boundary_cuda.cpp + src/move_boundary_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py new file mode 100644 index 00000000..88a78790 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py @@ -0,0 +1,90 @@ +## +# @file move_boundary.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# + +import math +import torch +from torch import nn +from torch.autograd import Function + +import dreamplacefpga.ops.move_boundary.move_boundary_cpp as move_boundary_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.move_boundary.move_boundary_cuda as move_boundary_cuda + +import pdb + +class MoveBoundaryFunction(Function): + """ + @brief Bound cells into layout boundary, perform in-place update + """ + @staticmethod + def forward( + pos, + node_size_x, + node_size_y, + xl, + yl, + xh, + yh, + num_movable_nodes, + num_filler_nodes, + num_threads + ): + if pos.is_cuda: + output = move_boundary_cuda.forward( + pos.view(pos.numel()), + node_size_x, + node_size_y, + xl, + yl, + xh, + yh, + num_movable_nodes, + num_filler_nodes + ) + else: + output = move_boundary_cpp.forward( + pos.view(pos.numel()), + node_size_x, + node_size_y, + xl, + yl, + xh, + yh, + num_movable_nodes, + num_filler_nodes, + num_threads + ) + return output + +class MoveBoundary(object): + """ + @brief Bound cells into layout boundary, perform in-place update + """ + def __init__(self, node_size_x, node_size_y, xl, yl, xh, yh, num_movable_nodes, num_filler_nodes, num_threads): + super(MoveBoundary, self).__init__() + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.num_movable_nodes = num_movable_nodes + self.num_filler_nodes = num_filler_nodes + self.num_threads = num_threads + def __call__(self, pos): + return MoveBoundaryFunction.forward( + pos, + node_size_x=self.node_size_x, + node_size_y=self.node_size_y, + xl=self.xl, + yl=self.yl, + xh=self.xh, + yh=self.yh, + num_movable_nodes=self.num_movable_nodes, + num_filler_nodes=self.num_filler_nodes, + num_threads=self.num_threads + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp new file mode 100644 index 00000000..725c89f5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp @@ -0,0 +1,91 @@ +/** + * @file move_boundary.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Move out-of-bound cells back to inside placement region + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeMoveBoundaryMapLauncher( + T* x_tensor, T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes, + const int num_threads + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +at::Tensor move_boundary_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + double xl, + double yl, + double xh, + double yh, + int num_movable_nodes, + int num_filler_nodes, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeMoveBoundaryMapLauncher", [&] { + computeMoveBoundaryMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + xl, yl, xh, yh, + pos.numel()/2, + num_movable_nodes, + num_filler_nodes, + num_threads + ); + }); + + return pos; +} + +template +int computeMoveBoundaryMapLauncher( + T* x_tensor, T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes, + const int num_threads + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + if (i < num_movable_nodes || i >= num_nodes-num_filler_nodes) + { + x_tensor[i] = std::max(x_tensor[i], xl); + x_tensor[i] = std::min(x_tensor[i], xh-node_size_x_tensor[i]); + + y_tensor[i] = std::max(y_tensor[i], yl); + y_tensor[i] = std::min(y_tensor[i], yh-node_size_y_tensor[i]); + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::move_boundary_forward, "MoveBoundary forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp new file mode 100644 index 00000000..258a6db9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp @@ -0,0 +1,61 @@ +/** + * @file hpwl_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Move out-of-bound cells back to inside placement region + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeMoveBoundaryMapCudaLauncher( + T* x_tensor, T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +at::Tensor move_boundary_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + double xl, + double yl, + double xh, + double yh, + int num_movable_nodes, + int num_filler_nodes + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeMoveBoundaryMapCudaLauncher", [&] { + computeMoveBoundaryMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + xl, yl, xh, yh, + pos.numel()/2, + num_movable_nodes, + num_filler_nodes + ); + }); + + return pos; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::move_boundary_forward, "MoveBoundary forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu new file mode 100644 index 00000000..7d244abb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu @@ -0,0 +1,90 @@ +#include +#include +#include +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeMoveBoundary( + T* x_tensor, + const T* node_size_x_tensor, + const T xl, const T xh, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_movable_nodes || (i >= num_nodes-num_filler_nodes && i < num_nodes)) + { + x_tensor[i] = min(xh-node_size_x_tensor[i], max(xl, x_tensor[i])); + } +} + +template +int computeMoveBoundaryMapCudaLauncher( + T* x_tensor, T* y_tensor, + const T* node_size_x_tensor, const T* node_size_y_tensor, + const T xl, const T yl, const T xh, const T yh, + const int num_nodes, + const int num_movable_nodes, + const int num_filler_nodes + ) +{ + cudaError_t status; + cudaStream_t stream_y; + status = cudaStreamCreate(&stream_y); + if (status != cudaSuccess) + { + printf("cudaStreamCreate failed for stream_y\n"); + fflush(stdout); + return 1; + } + + int thread_count = 512; + int block_count = (num_nodes - 1 + thread_count) / thread_count; + computeMoveBoundary<<>>( + x_tensor, + node_size_x_tensor, + xl, xh, + num_nodes, + num_movable_nodes, + num_filler_nodes + ); + + computeMoveBoundary<<>>( + y_tensor, + node_size_y_tensor, + yl, yh, + num_nodes, + num_movable_nodes, + num_filler_nodes + ); + + /* destroy stream */ + status = cudaStreamDestroy(stream_y); + if (status != cudaSuccess) + { + printf("stream_y destroy failed\n"); + fflush(stdout); + return 1; + } + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeMoveBoundaryMapCudaLauncher(\ + T* x_tensor, T* y_tensor, \ + const T* node_size_x_tensor, const T* node_size_y_tensor, \ + const T xl, const T yl, const T xh, const T yh, \ + const int num_nodes, \ + const int num_movable_nodes, \ + const int num_filler_nodes \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt new file mode 100644 index 00000000..c952075d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt @@ -0,0 +1,43 @@ +set(OP_NAME pin_pos) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CUB_DIR} + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/pin_pos.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/pin_pos_cuda.cpp + src/pin_pos_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda_segment + src/pin_pos_cuda_segment.cpp + src/pin_pos_cuda_segment_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + ${TARGET_NAME}_cuda_segment + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py new file mode 100644 index 00000000..f6f191cd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py @@ -0,0 +1,4 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Aug 2019 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py new file mode 100644 index 00000000..a580156e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py @@ -0,0 +1,217 @@ +## +# @file pin_pos.py +# @author Xiaohan Gao (DREAMPlace) +# @date Sep 2019 +# @brief Compute pin pos +# + +import math +import torch +from torch import nn +from torch.autograd import Function + +import dreamplacefpga.ops.pin_pos.pin_pos_cpp as pin_pos_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.pin_pos.pin_pos_cuda as pin_pos_cuda + import dreamplacefpga.ops.pin_pos.pin_pos_cuda_segment as pin_pos_cuda_segment + +import pdb + +class PinPosFunction(Function): + """ + @brief Given cell locations, compute pin locations. + """ + @staticmethod + def forward( + ctx, + pos, + pin_offset_x, + pin_offset_y, + pin2node_map, + flat_node2pin_map, + flat_node2pin_start_map, + num_physical_nodes, + num_threads + ): + ctx.pos = pos.view(pos.numel()) + if pos.is_cuda: + output = pin_pos_cuda.forward( + ctx.pos, + pin_offset_x, + pin_offset_y, + pin2node_map, + flat_node2pin_map, + flat_node2pin_start_map + ) + else: + output = pin_pos_cpp.forward( + ctx.pos, + pin_offset_x, + pin_offset_y, + pin2node_map, + flat_node2pin_map, + flat_node2pin_start_map, + num_threads + ) + ctx.pin_offset_x = pin_offset_x + ctx.pin_offset_y = pin_offset_y + ctx.pin2node_map = pin2node_map + ctx.flat_node2pin_map = flat_node2pin_map + ctx.flat_node2pin_start_map = flat_node2pin_start_map + ctx.num_physical_nodes = num_physical_nodes + ctx.num_threads = num_threads + + return output + + @staticmethod + def backward(ctx, grad_pin_pos): + # grad_pin_pos is not contiguous + if grad_pin_pos.is_cuda: + output = pin_pos_cuda.backward( + grad_pin_pos.contiguous(), + ctx.pos, + ctx.pin_offset_x, + ctx.pin_offset_y, + ctx.pin2node_map, + ctx.flat_node2pin_map, + ctx.flat_node2pin_start_map, + ctx.num_physical_nodes + ) + else: + output = pin_pos_cpp.backward( + grad_pin_pos.contiguous(), + ctx.pos, + ctx.pin_offset_x, + ctx.pin_offset_y, + ctx.pin2node_map, + ctx.flat_node2pin_map, + ctx.flat_node2pin_start_map, + ctx.num_physical_nodes, + ctx.num_threads + ) + return output, None, None, None, None, None, None, None + +class PinPosSegmentFunction(Function): + """ + @brief Given cell locations, compute pin locations. + """ + @staticmethod + def forward( + ctx, + pos, + pin_offset_x, + pin_offset_y, + pin2node_map, + flat_node2pin_map, + flat_node2pin_start_map, + num_physical_nodes + ): + ctx.pos = pos.view(pos.numel()) + if not pos.is_cuda: + assert 0, "CPU version NOT implemented" + else: + output = pin_pos_cuda_segment.forward( + ctx.pos, + pin_offset_x, + pin_offset_y, + pin2node_map, + flat_node2pin_map, + flat_node2pin_start_map + ) + ctx.pin_offset_x = pin_offset_x + ctx.pin_offset_y = pin_offset_y + ctx.pin2node_map = pin2node_map + ctx.flat_node2pin_map = flat_node2pin_map + ctx.flat_node2pin_start_map = flat_node2pin_start_map + ctx.num_physical_nodes = num_physical_nodes + + if pos.is_cuda: + torch.cuda.synchronize() + + return output + + @staticmethod + def backward(ctx, grad_pin_pos): + # grad_pin_pos is not contiguous + if grad_pin_pos.is_cuda: + output = pin_pos_cuda_segment.backward( + grad_pin_pos.contiguous(), + ctx.pos, + ctx.pin_offset_x, + ctx.pin_offset_y, + ctx.pin2node_map, + ctx.flat_node2pin_map, + ctx.flat_node2pin_start_map, + ctx.num_physical_nodes + ) + else: + assert 0, "CPU version NOT implemented" + if grad_pin_pos.is_cuda: + torch.cuda.synchronize() + + return output, None, None, None, None, None, None, None + +class PinPos(nn.Module): + """ + @brief Given cell locations, compute pin locations. + Different from torch.index_add which computes x[index[i]] += t[i], + the forward function compute x[i] += t[index[i]] + """ + def __init__(self, pin_offset_x, pin_offset_y, pin2node_map, flat_node2pin_map, flat_node2pin_start_map, num_physical_nodes, num_threads, algorithm='segment'): + """ + @brief initialization + @param pin_offset pin offset in x or y direction, only computes one direction + @param algorithm segment|node-by-node + @param num_threads number of threads + """ + super(PinPos, self).__init__() + self.pin_offset_x = pin_offset_x + self.pin_offset_y = pin_offset_y + self.pin2node_map = pin2node_map.long() + self.flat_node2pin_map = flat_node2pin_map + self.flat_node2pin_start_map = flat_node2pin_start_map + self.num_physical_nodes = num_physical_nodes + self.algorithm = algorithm + self.num_threads = num_threads + + def forward(self, pos): + """ + @brief API + @param pos cell locations. The array consists of x locations of movable cells, fixed cells, and filler cells, then y locations of them + """ + assert pos.numel() % 2 == 0 + num_nodes = pos.numel() // 2 + if pos.is_cuda: + if self.algorithm == 'segment': + return PinPosSegmentFunction.apply( + pos, + self.pin_offset_x, + self.pin_offset_y, + self.pin2node_map, + self.flat_node2pin_map, + self.flat_node2pin_start_map, + self.num_physical_nodes + ) + else: + return PinPosFunction.apply( + pos, + self.pin_offset_x, + self.pin_offset_y, + self.pin2node_map, + self.flat_node2pin_map, + self.flat_node2pin_start_map, + self.num_physical_nodes, + self.num_threads + ) + else: + return PinPosFunction.apply( + pos, + self.pin_offset_x, + self.pin_offset_y, + self.pin2node_map, + self.flat_node2pin_map, + self.flat_node2pin_start_map, + self.num_physical_nodes, + self.num_threads + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp new file mode 100644 index 00000000..805766ca --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp @@ -0,0 +1,221 @@ +/** + * @file pin_pos.cpp + * @author Yibo Lin (DREAMPlace) + * @date Aug 2019 + * @brief Given cell locations, compute pin locations on CPU + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief Given cell locations, compute pin locations +/// @param x cell locations in x direction +/// @param y cell locations in y direction +/// @param pin_offset_x pin offset in x direction +/// @param pin_offset_y pin offset in y direction +/// @param pin2node_map map pin index to node index +/// @param flat_node2pin_map map node index to pins +/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node +/// @param num_pins number of pins +/// @param num_threads number of threads +/// @param pin_x pin positions in x direction +/// @param pin_y pin positions in y direction +template +int computePinPosLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + const int num_threads, + T* pin_x, T* pin_y + ); + +template +int computePinPosGradLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + const int num_threads, + T* grad, T* grad_y + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Given cell locations, compute pin locations +/// @param pos cell locations in x and then y direction +/// @param pin_offset_x pin offset in x direction +/// @param pin_offset_y pin offset in y direction +/// @param pin2node_map map pin index to node index +/// @param flat_node2pin_map map node index to pins +/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node +/// @param num_nodes number of nodes +/// @param num_pins number of pins +/// @param num_threads number of threads +/// @return pin positions in x and then y direction +at::Tensor pin_pos_forward( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosLauncher", [&] { + computePinPosLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_pins, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins + ); + }); + + return out; +} + +at::Tensor pin_pos_backward( + at::Tensor grad_out, + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map, + int num_physical_nodes, + int num_threads + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(grad_out); + CHECK_EVEN(grad_out); + CHECK_CONTIGUOUS(grad_out); + + auto out = at::zeros_like(pos); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradLauncher", [&] { + computePinPosGradLauncher( + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_physical_nodes, + num_pins, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes + ); + }); + + return out; +} + +/// @brief Given cell locations, compute pin locations +/// @param x cell locations in x direction +/// @param y cell locations in y direction +/// @param pin_offset_x pin offset in x direction +/// @param pin_offset_y pin offset in y direction +/// @param pin2node_map map pin index to node index +/// @param flat_node2pin_map map node index to pins +/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node +/// @param num_nodes number of nodes +/// @param num_pins number of pins +/// @param num_threads number of threads +/// @param pin_x pin positions in x direction +/// @param pin_y pin positions in y direction +template +int computePinPosLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + const int num_threads, + T* pin_x, T* pin_y + ) +{ + // density_map_tensor should be initialized outside + +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_pins; ++i) + { + int node_id = pin2node_map[i]; + pin_x[i] = pin_offset_x[i] + x[node_id]; + pin_y[i] = pin_offset_y[i] + y[node_id]; + } + + return 0; +} + +template +int computePinPosGradLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + const int num_threads, + T* grad_x, T* grad_y + ) +{ +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + int bgn = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i+1]; + T& gx = grad_x[i]; + T& gy = grad_y[i]; + for (int j = bgn; j < end; ++j) + { + int pin_id = flat_node2pin_map[j]; + gx += grad_out_x[pin_id]; + gy += grad_out_y[pin_id]; + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp new file mode 100644 index 00000000..eda599f0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp @@ -0,0 +1,121 @@ +/** + * @file pin_pos_cuda.cpp + * @author Xiaohan Gao (DREAMPlace) + * @date Sep 2019 + * @brief Given cell locations, compute pin locations on CPU + */ + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computePinPosCudaLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + T* pin_x, T* pin_y + ); + +template +int computePinPosGradCudaLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + T* grad, T* grad_y + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +at::Tensor pin_pos_forward( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosCudaLauncher", [&] { + computePinPosCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins + ); + }); + + return out; +} + +at::Tensor pin_pos_backward( + at::Tensor grad_out, + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map, + int num_physical_nodes + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(grad_out); + CHECK_EVEN(grad_out); + CHECK_CONTIGUOUS(grad_out); + + auto out = at::zeros_like(pos); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradCudaLauncher", [&] { + computePinPosGradCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_physical_nodes, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes + ); + }); + + return out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu new file mode 100644 index 00000000..b9b97e98 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu @@ -0,0 +1,134 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief Compute pin position from node position +template +__global__ void computePinPos( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const K* pin2node_map, + const int num_pins, + T* pin_x, T* pin_y + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int node_id = pin2node_map[i]; + pin_x[i] = pin_offset_x[i] + x[node_id]; + pin_y[i] = pin_offset_y[i] + y[node_id]; + } +} + +template +int computePinPosCudaLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + T* pin_x, T* pin_y + ) +{ + int thread_count = 512; + + computePinPos<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(x, y, pin_offset_x, pin_offset_y, pin2node_map, num_pins, pin_x, pin_y); + + return 0; +} + +/// @brief Compute pin position from node position +template +__global__ void computeNodeGrad( + const T* grad_out_x, + const T* grad_out_y, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + const int num_nodes, + T* grad_x, + T* grad_y + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nodes) + { + T& gx = grad_x[i]; + T& gy = grad_y[i]; + gx = 0; + gy = 0; + for (int j = flat_node2pin_start_map[i]; j < flat_node2pin_start_map[i+1]; ++j) + { + int pin_id = flat_node2pin_map[j]; + gx += grad_out_x[pin_id]; + gy += grad_out_y[pin_id]; + } + } +} + +template +int computePinPosGradCudaLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + T* grad_x, T* grad_y + ) +{ + int thread_count = 512; + + computeNodeGrad<<<(num_nodes + thread_count - 1) / thread_count, thread_count>>>( + grad_out_x, + grad_out_y, + flat_node2pin_map, + flat_node2pin_start_map, + num_nodes, + grad_x, + grad_y + ); + + return 0; +} + + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computePinPosCudaLauncher(\ + const T* x, const T* y, \ + const T* pin_offset_x, \ + const T* pin_offset_y, \ + const long* pin2node_map, \ + const int* flat_node2pin_map, \ + const int* flat_node2pin_start_map, \ + int num_pins, \ + T* pin_x, T* pin_y \ + );\ + \ + template int computePinPosGradCudaLauncher(\ + const T* grad_out_x, const T* grad_out_y, \ + const T* x, const T* y, \ + const T* pin_offset_x, \ + const T* pin_offset_y, \ + const long* pin2node_map, \ + const int* flat_node2pin_map, \ + const int* flat_node2pin_start_map, \ + int num_nodes, \ + int num_pins, \ + T* grad_x, T* grad_y \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp new file mode 100644 index 00000000..7a6f4f0c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp @@ -0,0 +1,124 @@ +/** + * @file pin_pos_cuda_segment.cpp + * @author Xiaohan Gao (DREAMPlace) + * @date Sep 2019 + * @brief Given cell locations, compute pin locations on CPU + */ + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computePinPosCudaSegmentLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + T* pin_x, T* pin_y + ); + +template +int computePinPosGradCudaSegmentLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + T* grad, T* grad_y, + T* grad_perm_buf ///< 2*num_pins + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +at::Tensor pin_pos_forward( + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosCudaSegmentLauncher", [&] { + computePinPosCudaSegmentLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins + ); + }); + + return out; +} + +at::Tensor pin_pos_backward( + at::Tensor grad_out, + at::Tensor pos, + at::Tensor pin_offset_x, + at::Tensor pin_offset_y, + at::Tensor pin2node_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map, + int num_physical_nodes + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(grad_out); + CHECK_EVEN(grad_out); + CHECK_CONTIGUOUS(grad_out); + + auto out = at::zeros_like(pos); + int num_nodes = pos.numel()/2; + int num_pins = pin_offset_x.numel(); + auto grad_perm_buf = at::empty({2*num_pins}, pos.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradCudaSegmentLauncher", [&] { + computePinPosGradCudaSegmentLauncher( + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + num_physical_nodes, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(grad_perm_buf, scalar_t) + ); + }); + + return out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu new file mode 100644 index 00000000..cfd9f86a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu @@ -0,0 +1,140 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +#include "utility/src/utils_cub.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void permuteGrad( + const T* grad_out_x, + const T* grad_out_y, + const int* flat_node2pin_map, + const int num_pins, + T* grad_out_x_perm, + T* grad_out_y_perm + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int pin_id = flat_node2pin_map[i]; + grad_out_x_perm[i] = grad_out_x[pin_id]; + grad_out_y_perm[i] = grad_out_y[pin_id]; + } +} + +/// @brief Compute pin position from node position +template +__global__ void computePinPos( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const K* pin2node_map, + const int num_pins, + T* pin_x, T* pin_y + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int node_id = pin2node_map[i]; + pin_x[i] = pin_offset_x[i] + x[node_id]; + pin_y[i] = pin_offset_y[i] + y[node_id]; + } +} + +template +int computePinPosCudaSegmentLauncher( + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_pins, + T* pin_x, T* pin_y + ) +{ + int thread_count = 512; + + computePinPos<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(x, y, pin_offset_x, pin_offset_y, pin2node_map, num_pins, pin_x, pin_y); + + return 0; +} + +template +int computePinPosGradCudaSegmentLauncher( + const T* grad_out_x, const T* grad_out_y, + const T* x, const T* y, + const T* pin_offset_x, + const T* pin_offset_y, + const long* pin2node_map, + const int* flat_node2pin_map, + const int* flat_node2pin_start_map, + int num_nodes, + int num_pins, + T* grad_x, T* grad_y, + T* grad_perm_buf ///< 2*num_pins, buffer for store the permutated gradients + ) +{ + int thread_count = 512; + + T* grad_out_x_perm = grad_perm_buf; + T* grad_out_y_perm = grad_perm_buf + num_pins; + + permuteGrad<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(grad_out_x, grad_out_y, flat_node2pin_map, num_pins, grad_out_x_perm, grad_out_y_perm); + + void* d_temp_storage = NULL; + size_t temp_storage_bytes = 0; + + // allocate temp storage + cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_x_perm, grad_x, + num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); + cudaMalloc(&d_temp_storage, temp_storage_bytes); + + // for x + cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_x_perm, grad_x, + num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); + // for y + cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_y_perm, grad_y, + num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); + + cudaFree(d_temp_storage); + + return 0; +} + + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computePinPosCudaSegmentLauncher(\ + const T* x, const T* y, \ + const T* pin_offset_x, \ + const T* pin_offset_y, \ + const long* pin2node_map, \ + const int* flat_node2pin_map, \ + const int* flat_node2pin_start_map, \ + int num_pins, \ + T* pin_x, T* pin_y \ + );\ + \ + template int computePinPosGradCudaSegmentLauncher(\ + const T* grad_out_x, const T* grad_out_y, \ + const T* x, const T* y, \ + const T* pin_offset_x, \ + const T* pin_offset_y, \ + const long* pin2node_map, \ + const int* flat_node2pin_map, \ + const int* flat_node2pin_start_map, \ + int num_nodes, \ + int num_pins, \ + T* grad_x, T* grad_y, \ + T* grad_perm_buf \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt new file mode 100644 index 00000000..141afcca --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt @@ -0,0 +1,35 @@ +set(OP_NAME pin_utilization) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/pin_utilization_map.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/pin_utilization_map_cuda.cpp + src/pin_utilization_map_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py new file mode 100644 index 00000000..7caf7dcd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Dec 2019 +# + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py new file mode 100644 index 00000000..c68e2408 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py @@ -0,0 +1,106 @@ +import math +import torch +from torch import nn +from torch.autograd import Function +import pdb + +import dreamplacefpga.ops.pin_utilization.pin_utilization_cpp as pin_utilization_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.pin_utilization.pin_utilization_cuda as pin_utilization_cuda + +class PinUtilization(nn.Module): + def __init__(self, + node_size_x, node_size_y, + pin_weights, + flat_node2pin_start_map, + xl, xh, yl, yh, + num_movable_nodes, num_filler_nodes, + num_bins_x, num_bins_y, + unit_pin_capacity, + pin_stretch_ratio, + deterministic_flag, + num_threads + ): + super(PinUtilization, self).__init__() + self.node_size_x = node_size_x + self.node_size_y = node_size_y + self.xl = xl + self.xh = xh + self.yl = yl + self.yh = yh + self.num_nodes = len(node_size_x) + self.num_movable_nodes = num_movable_nodes + self.num_filler_nodes = num_filler_nodes + self.num_physical_nodes = self.num_nodes - num_filler_nodes + self.num_bins_x = num_bins_x + self.num_bins_y = num_bins_y + self.bin_size_x = (xh - xl) / num_bins_x + self.bin_size_y = (yh - yl) / num_bins_y + self.deterministic_flag = deterministic_flag + self.num_threads = num_threads + + self.unit_pin_capacity = unit_pin_capacity + self.pin_stretch_ratio = pin_stretch_ratio + + # for each physical node, we use the pin counts as the weights + if pin_weights is not None: + self.pin_weights = pin_weights + elif flat_node2pin_start_map is not None: + self.pin_weights = (flat_node2pin_start_map[1:self.num_physical_nodes + 1] - + flat_node2pin_start_map[:self.num_physical_nodes]).to(self.node_size_x.dtype) + else: + assert "either pin_weights or flat_node2pin_start_map is required" + + self.reset() + + def reset(self): + # to make the pin density map smooth, we stretch each pin to a ratio of the pin utilization bin + self.half_node_size_stretch_x = 0.5 * self.node_size_x[:self.num_physical_nodes].clamp(min=self.bin_size_x * self.pin_stretch_ratio) + self.half_node_size_stretch_y = 0.5 * self.node_size_y[:self.num_physical_nodes].clamp(min=self.bin_size_y * self.pin_stretch_ratio) + + def forward(self, pos): + if pos.is_cuda: + output = pin_utilization_cuda.forward( + pos, + self.node_size_x, + self.node_size_y, + self.half_node_size_stretch_x, + self.half_node_size_stretch_y, + self.pin_weights, + self.xl, + self.yl, + self.xh, + self.yh, + self.bin_size_x, + self.bin_size_y, + self.num_physical_nodes, + self.num_bins_x, + self.num_bins_y, + self.deterministic_flag + ) + else: + output = pin_utilization_cpp.forward( + pos, + self.node_size_x, + self.node_size_y, + self.half_node_size_stretch_x, + self.half_node_size_stretch_y, + self.pin_weights, + self.xl, + self.yl, + self.xh, + self.yh, + self.bin_size_x, + self.bin_size_y, + self.num_physical_nodes, + self.num_bins_x, + self.num_bins_y, + self.num_threads, + self.deterministic_flag + ) + + # convert demand to utilization in each bin + output.mul_(1 / (self.bin_size_x * self.bin_size_y * self.unit_pin_capacity)); + + return output diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp new file mode 100644 index 00000000..6bd98614 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp @@ -0,0 +1,170 @@ +/** + * @file pin_utilization_map.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map pin by pin +template +int pinDemandMapLauncher( + const T *node_x, const T *node_y, + const T *node_size_x, const T *node_size_y, + const T *half_node_size_stretch_x, + const T *half_node_size_stretch_y, + const T *pin_weights, + const T xl, const T yl, + const T xh, const T yh, + const T bin_size_x, + const T bin_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_nodes, + const int num_threads, + AtomicOp atomic_add_op, + typename AtomicOp::type *pin_utilization_map + ) +{ + const T inv_bin_size_x = 1.0 / bin_size_x; + const T inv_bin_size_y = 1.0 / bin_size_y; + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + const T node_center_x = node_x[i] + node_size_x[i]/2; + const T node_center_y = node_y[i] + node_size_y[i]/2; + + const T x_min = node_center_x - half_node_size_stretch_x[i]; + const T x_max = node_center_x + half_node_size_stretch_x[i]; + int bin_index_xl = int((x_min - xl) * inv_bin_size_x); + int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + const T y_min = node_center_y - half_node_size_stretch_y[i]; + const T y_max = node_center_y + half_node_size_stretch_y[i]; + int bin_index_yl = int((y_min - yl) * inv_bin_size_y); + int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + T density = pin_weights[i] / (half_node_size_stretch_x[i] * half_node_size_stretch_y[i] * 4); + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + T bin_xl = xl + x * bin_size_x; + T bin_yl = yl + y * bin_size_y; + T bin_xh = bin_xl + bin_size_x; + T bin_yh = bin_yl + bin_size_y; + T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * + DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); + int index = x * num_bins_y + y; + atomic_add_op(&pin_utilization_map[index], overlap * density); + } + } + } + return 0; +} + +at::Tensor pin_utilization_map_forward( + at::Tensor pos, at::Tensor node_size_x, at::Tensor node_size_y, + at::Tensor half_node_size_stretch_x, at::Tensor half_node_size_stretch_y, + at::Tensor pin_weights, double xl, double yl, double xh, double yh, + double bin_size_x, double bin_size_y, int num_physical_nodes, + int num_bins_x, int num_bins_y, + int num_threads, int deterministic_flag + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + CHECK_FLAT(half_node_size_stretch_x); + CHECK_CONTIGUOUS(half_node_size_stretch_x); + + CHECK_FLAT(half_node_size_stretch_y); + CHECK_CONTIGUOUS(half_node_size_stretch_y); + + CHECK_FLAT(pin_weights); + CHECK_CONTIGUOUS(pin_weights); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + at::Tensor pin_utilization_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); + auto num_nodes = pos.numel()/2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "pinDemandMapLauncher", [&] { + if (deterministic_flag == 1) { + double diearea = (xh - xl) * (yh - yl); + int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); + long scale_factor = (1L << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + + std::vector buf_map(num_bins, 0); + AtomicAdd atomic_add_op(scale_factor); + + pinDemandMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), + xl, yl, xh, yh, bin_size_x, bin_size_y, + num_bins_x, num_bins_y, num_physical_nodes, + num_threads, atomic_add_op, buf_map.data()); + + scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t), + buf_map.data(), 1.0 / scale_factor, num_bins, + num_threads); + } else { + AtomicAdd atomic_add_op; + pinDemandMapLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), + xl, yl, xh, yh, bin_size_x, bin_size_y, + num_bins_x, num_bins_y, num_physical_nodes, + num_threads, atomic_add_op, + DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t)); + } + }); + + return pin_utilization_map; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::pin_utilization_map_forward, "compute pin utilization map"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp new file mode 100644 index 00000000..edf470a2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp @@ -0,0 +1,112 @@ +/** + * @file pin_utilization_map_cuda.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map pin by pin +template +int pinDemandMapCudaLauncher( + const T *node_x, const T *node_y, + const T *node_size_x, const T *node_size_y, + const T *half_node_size_stretch_x, + const T *half_node_size_stretch_y, + const T *pin_weights, + const T xl, const T yl, + const T xh, const T yh, + const T bin_size_x, + const T bin_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_nodes, + bool deterministic_flag, + T *pin_utilization_map + ); + +at::Tensor pin_utilization_map_forward( + at::Tensor pos, + at::Tensor node_size_x, + at::Tensor node_size_y, + at::Tensor half_node_size_stretch_x, + at::Tensor half_node_size_stretch_y, + at::Tensor pin_weights, + double xl, + double yl, + double xh, + double yh, + double bin_size_x, + double bin_size_y, + int num_physical_nodes, + int num_bins_x, + int num_bins_y, + int deterministic_flag + ) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + CHECK_FLAT(half_node_size_stretch_x); + CHECK_CONTIGUOUS(half_node_size_stretch_x); + + CHECK_FLAT(half_node_size_stretch_y); + CHECK_CONTIGUOUS(half_node_size_stretch_y); + + CHECK_FLAT(pin_weights); + CHECK_CONTIGUOUS(pin_weights); + + CHECK_FLAT(node_size_x); + CHECK_CONTIGUOUS(node_size_x); + + CHECK_FLAT(node_size_y); + CHECK_CONTIGUOUS(node_size_y); + + at::Tensor pin_utilization_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); + auto num_nodes = pos.numel()/2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "pinDemandMapCudaLauncher", [&] { + pinDemandMapCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_bins_x, num_bins_y, + num_physical_nodes, + (bool)deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t) + ); + }); + + return pin_utilization_map; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::pin_utilization_map_forward, "compute pin utilization map (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu new file mode 100644 index 00000000..3a0a623f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu @@ -0,0 +1,164 @@ +/** + * @file pin_utilization_map_cuda_kernel.cu + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) + * @date Apr 2023 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +// fill the demand map net by net +template +__global__ void pinDemandMap( + const T *node_x, const T *node_y, + const T *node_size_x, const T *node_size_y, + const T *half_node_size_stretch_x, + const T *half_node_size_stretch_y, + const T *pin_weights, + const T xl, const T yl, + const T xh, const T yh, + const T bin_size_x, + const T bin_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_nodes, + AtomicOp atomic_add_op, + typename AtomicOp::type *pin_utilization_map + ) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + + if (i < num_nodes) + { + const T node_center_x = node_x[i] + node_size_x[i]/2; + const T node_center_y = node_y[i] + node_size_y[i]/2; + + const T x_min = node_center_x - half_node_size_stretch_x[i]; + const T x_max = node_center_x + half_node_size_stretch_x[i]; + int bin_index_xl = int((x_min - xl) / bin_size_x); + int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + const T y_min = node_center_y - half_node_size_stretch_y[i]; + const T y_max = node_center_y + half_node_size_stretch_y[i]; + int bin_index_yl = int((y_min - yl) / bin_size_y); + int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + T density = pin_weights[i] / (half_node_size_stretch_x[i] * half_node_size_stretch_y[i] * 4); + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + T bin_xl = xl + x * bin_size_x; + T bin_yl = yl + y * bin_size_y; + T bin_xh = bin_xl + bin_size_x; + T bin_yh = bin_yl + bin_size_y; + T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * + DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); + int index = x * num_bins_y + y; + atomic_add_op(&pin_utilization_map[index], overlap * density); + } + } + } +} + +// fill the demand map net by net +template +int pinDemandMapCudaLauncher( + const T *node_x, const T *node_y, + const T *node_size_x, const T *node_size_y, + const T *half_node_size_stretch_x, + const T *half_node_size_stretch_y, + const T *pin_weights, + const T xl, const T yl, + const T xh, const T yh, + const T bin_size_x, + const T bin_size_y, + const int num_bins_x, + const int num_bins_y, + const int num_nodes, + bool deterministic_flag, + T *pin_utilization_map + ) +{ + if (deterministic_flag) + { + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + unsigned long long int *buf_map = NULL; + allocateCUDA(buf_map, num_bins, unsigned long long int); + + AtomicAddCUDA atomic_add_op(scale_factor); + + int thread_count = 512; + int block_count = ceilDiv(num_bins, thread_count); + + copyScaleArray<<>>( + buf_map, pin_utilization_map, scale_factor, num_bins); + + block_count = ceilDiv(num_nodes, thread_count); + pinDemandMap<<>>( + node_x, node_y, + node_size_x, node_size_y, + half_node_size_stretch_x, half_node_size_stretch_y, + pin_weights, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_bins_x, num_bins_y, + num_nodes, + atomic_add_op, + buf_map + ); + + block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + pin_utilization_map, buf_map, T(1.0 / scale_factor), num_bins); + + destroyCUDA(buf_map); + } else + { + AtomicAddCUDA atomic_add_op; + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + pinDemandMap<<>>( + node_x, node_y, + node_size_x, node_size_y, + half_node_size_stretch_x, half_node_size_stretch_y, + pin_weights, + xl, yl, xh, yh, + bin_size_x, bin_size_y, + num_bins_x, num_bins_y, + num_nodes, + atomic_add_op, + pin_utilization_map + ); + } + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int pinDemandMapCudaLauncher( \ + const T *node_x, const T *node_y, const T *node_size_x, const T *node_size_y, \ + const T *half_node_size_stretch_x, const T *half_node_size_stretch_y, \ + const T *pin_weights, const T xl, const T yl, const T xh, const T yh, \ + const T bin_size_x, const T bin_size_y, const int num_bins_x, \ + const int num_bins_y, const int num_nodes, bool deterministic_flag, \ + T *pin_utilization_map \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt new file mode 100644 index 00000000..694b8177 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt @@ -0,0 +1,59 @@ +set(OP_NAME place_io) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${LIMBO_SOURCE_DIR} + ${Boost_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ) + +set(LINK_LIBS + #draw_place_cxx + utility_cxx + # begin targets in Limbo + #lefparseradapt + #defparseradapt + #verilogparser + bookshelfparser + #gdsparser + programoptions + gzstream + # end targets in Limbo + OpenMP::OpenMP_CXX + ${ZLIB_LIBRARIES} + ) + +if(CAIRO_FOUND) + set(INCLUDE_DIRS ${INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}) + set(LINK_LIBS ${LINK_LIBS} ${CAIRO_LIBRARIES}) + set(DRAWPLACE 1) +else() + set(DRAWPLACE 0) +endif() + +add_pytorch_extension(${TARGET_NAME}_cpp + src/BenchMetrics.cpp + src/BinMap.cpp + src/Enums.cpp + src/Net.cpp + src/Node.cpp + src/Region.cpp + src/Group.cpp + src/Params.cpp + src/PlaceDB.cpp + src/BookshelfWriter.cpp + src/PyPlaceDB.cpp + src/PybindPlaceDB.cpp + src/PybindPyPlaceDB.cpp + src/place_io.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS} + EXTRA_DEFINITIONS DRAWPLACE=${DRAWPLACE}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py new file mode 100644 index 00000000..824b3b37 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Aug 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py new file mode 100644 index 00000000..fe76ce5e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py @@ -0,0 +1,52 @@ +## +# @file place_io.py +# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Mar 2021 +# + +from torch.autograd import Function + +import dreamplacefpga.ops.place_io.place_io_cpp as place_io_cpp +import pdb + +class PlaceIOFunction(Function): + @staticmethod + def read(params): + """ + @brief read design and store in placement database + """ + args = params.aux_input + if "aux_input" in params.__dict__ and params.aux_input: + return place_io_cpp.forward(args) + + @staticmethod + def pydb(raw_db): + """ + @brief convert to python database + @param raw_db original placement database + """ + return place_io_cpp.pydb(raw_db) + + @staticmethod + def write(raw_db, filename, node_x, node_y, node_z): + """ + @brief write solution in specific format + @param raw_db original placement database + @param filename output file + @param sol_file_format solution file format, DEF|DEFSIMPLE|BOOKSHELF|BOOKSHELFALL - Always *.pl for FPGA + @param node_x x coordinates of cells, only need movable cells; if none, use original position + @param node_y y coordinates of cells, only need movable cells; if none, use original position + """ + return place_io_cpp.write(raw_db, filename, node_x, node_y, node_z) + + @staticmethod + def apply(raw_db, node_x, node_y, node_z): + """ + @brief apply solution + @param raw_db original placement database + @param node_x x coordinates of cells, only need movable cells + @param node_y y coordinates of cells, only need movable cells + @param node_z z coordinates of cells, only need movable cells + """ + return place_io_cpp.apply(raw_db, node_x, node_y, node_z) + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp new file mode 100644 index 00000000..671efafa --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp @@ -0,0 +1,38 @@ +/************************************************************************* + > File Name: BenchMetrics.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "BenchMetrics.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +BenchMetrics::BenchMetrics() +{ + initPlaceDBFlag = false; + //initAlgoDBFlag = false; +} + +void BenchMetrics::print() const +{ + if (initPlaceDBFlag) + { + dreamplacePrint(kINFO, "design name = %s\n", designName.c_str()); + dreamplacePrint(kINFO, "number of macros = %lu\n", numMacro); + dreamplacePrint(kINFO, "number of nodes = %lu (movable %lu, fixed %lu)\n", numNodes, numMovable, numFixed); + dreamplacePrint(kINFO, "number of nets = %lu\n", numNets); + dreamplacePrint(kINFO, "number of pin connections = %lu\n", numPins); + dreamplacePrint(kINFO, "site dimensions = (%d, %d)\n", siteWidth, rowHeight); + dreamplacePrint(kINFO, "die dimensions = (%d, %d, %d, %d)\n", dieArea.xl(), dieArea.yl(), dieArea.xh(), dieArea.yh()); + if (numIgnoredNet) + dreamplacePrint(kWARN, "# ingored nets = %lu (nets belong to the same cells)\n", numIgnoredNet); + if (numDuplicateNet) + dreamplacePrint(kWARN, "# duplicate nets = %lu\n", numDuplicateNet); + } +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h new file mode 100644 index 00000000..63544476 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h @@ -0,0 +1,50 @@ +/************************************************************************* + > File Name: BenchMetrics.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_BENCHMETRICS_H +#define DREAMPLACE_BENCHMETRICS_H + +#include +#include +#include "Box.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// ================================================ +/// a simple class storing metrics for benchmarks +/// which is to help report benchmark statistics +/// ================================================ + +struct BenchMetrics +{ + /// metrics from PlaceDB + std::string designName; + std::size_t numMacro; + std::size_t numNodes; + std::size_t numMovable; + std::size_t numFixed; + //std::size_t numIOPin; + std::size_t numNets; + std::size_t numPins; + unsigned siteWidth; + unsigned rowHeight; + Box dieArea; + std::size_t numIgnoredNet; + std::size_t numDuplicateNet; + + bool initPlaceDBFlag; ///< a flag indicates whether it is initialized, must set to true after initialization, from PlaceDB + //bool initAlgoDBFlag; + + BenchMetrics(); + + void print() const; +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h new file mode 100644 index 00000000..aef12d13 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h @@ -0,0 +1,197 @@ +/************************************************************************* + > File Name: Bin.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun Jun 21 20:06:50 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_BIN_H +#define DREAMPLACE_BIN_H + +#include "Box.h" +#include "Object.h" +#include "HrchyList.h" + +DREAMPLACE_BEGIN_NAMESPACE + +enum BinType +{ + kBin = 0, + kSBin = 1, + kBinTypeNum = 2 +}; + +inline BinType& operator++(BinType& t) +{ + if (t == kBin) return (t = kSBin); + else if (t == kSBin) return (t = kBinTypeNum); + dreamplaceAssertMsg(0, "overflow for increment BinType"); + return t; +} + +class Bin : public Box +{ + public: + typedef Object::coordinate_type coordinate_type; + typedef Box base_type; + typedef coordinate_traits::index_type index_type; + typedef coordinate_traits::area_type area_type; + typedef coordinate_traits::site_area_type site_area_type; + + /// constructor + Bin (coordinate_type xl = std::numeric_limits::max(), + coordinate_type yl = std::numeric_limits::max(), + coordinate_type xh = std::numeric_limits::min(), + coordinate_type yh = std::numeric_limits::min(), + BinType t = kBin, + index_type id1D = std::numeric_limits::max(), + index_type id2DX = std::numeric_limits::max(), + index_type id2DY = std::numeric_limits::max()); + /// copy constructor + Bin (Bin const& rhs); + /// assignment + Bin& operator=(Bin const& rhs); + + /// member functions + BinType type() const {return m_type;} + Bin& setType(BinType t) {m_type = t; return *this;} + + index_type index1D() const {return m_index1D;} + Bin& setIndex1D(index_type id) {m_index1D = id; return *this;} + + index_type index2D(Direction1DType d) const {return m_index2D[d];} + Bin& setIndex2D(Direction1DType d, index_type v) {m_index2D[d] = v; return *this;} + + index_type indexX() const {return index2D(kX);} + index_type indexY() const {return index2D(kY);} + + /// \return demand of cell area + area_type demand() const {return m_demand;} + Bin& setDemand(area_type v) {m_demand = v; return *this;} + Bin& incrDemand(area_type v) {m_demand += v; return *this;} + Bin& decrDemand(area_type v) {m_demand -= v; return *this;} + + /// \return capacity of area for cells in the bin, excluding fixed cells and forbidden regions + area_type capacity() const {return m_capacity;} + Bin& setCapacity(area_type v) {m_capacity = v; return *this;} + Bin& incrCapacity(area_type v) {m_capacity += v; return *this;} + Bin& decrCapacity(area_type v) {m_capacity -= v; return *this;} + + /// \return pin demand + index_type pinDemand() const {return m_pinDemand;} + Bin& setPinDemand(index_type v) {m_pinDemand = v; return *this;} + Bin& incrPinDemand(index_type v) {m_pinDemand += v; return *this;} + Bin& decrPinDemand(index_type v) {m_pinDemand -= v; return *this;} + + /// \return site capacity + site_area_type siteCapacity() const {return m_siteCapacity;} + Bin& setSiteCapacity(site_area_type v) {m_siteCapacity = v; return *this;} + Bin& incrSiteCapacity(site_area_type v) {m_siteCapacity += v; return *this;} + Bin& decrSiteCapacity(site_area_type v) {m_siteCapacity -= v; return *this;} + + /// \return density, area density + double density() const; + /// given a demand, \return density + double density(area_type d) const; + /// \return pin density, pin number per site + double pinDensity() const; + /// given a pin demand, \return pin density + double pinDensity(index_type d) const; + + HrchyList const& binRows() const {return m_vBinRowId;} + HrchyList& binRows() {return m_vBinRowId;} + + std::vector const& nodes() const {return m_vNodeId;} + std::vector& nodes() {return m_vNodeId;} + + protected: + void copy(Bin const& rhs); + + BinType m_type; ///< type of bin + index_type m_index1D; ///< index in bin array + index_type m_index2D[2]; ///< index in x and y direction of bin map + area_type m_capacity; ///< area available for cells + site_area_type m_siteCapacity; ///< number of sites available for cells + area_type m_demand; ///< area taken by cells + index_type m_pinDemand; ///< number of pins + + /// may not be used + std::vector m_vNodeId; ///< nodes in the bin, initialize iff it is used + /// for sub bins + HrchyList m_vBinRowId; ///< bin rows in the bin, from low to high, left to right +}; + +inline Bin::Bin(Bin::coordinate_type xl, Bin::coordinate_type yl, + Bin::coordinate_type xh, Bin::coordinate_type yh, + BinType t, + index_type id1D, index_type id2DX, index_type id2DY) + : Bin::base_type(xl, yl, xh, yh) + , m_type (t) + , m_index1D(id1D) +{ + m_index2D[kX] = id2DX; + m_index2D[kY] = id2DY; + m_capacity = 0; + m_siteCapacity = 0; + m_demand = 0; + m_pinDemand = 0; +} +inline Bin::Bin(Bin const& rhs) + : Bin::base_type(rhs) +{ + copy(rhs); +} +inline Bin& Bin::operator=(Bin const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void Bin::copy(Bin const& rhs) +{ + m_type = rhs.m_type; + m_index1D = rhs.m_index1D; + m_index2D[kX] = rhs.m_index2D[kX]; + m_index2D[kY] = rhs.m_index2D[kY]; + m_capacity = rhs.m_capacity; + m_siteCapacity = rhs.m_siteCapacity; + m_demand = rhs.m_demand; + m_pinDemand = rhs.m_pinDemand; + m_vBinRowId = rhs.m_vBinRowId; + m_vNodeId = rhs.m_vNodeId; +} +inline double Bin::density() const +{ + if (m_capacity == 0) + return (m_demand == 0)? 0.0 : std::numeric_limits::max(); + else + return (double)m_demand/m_capacity; +} +inline double Bin::density(Bin::area_type d) const +{ + if (m_capacity == 0) + return (d == 0)? 0.0 : std::numeric_limits::max(); + else + return (double)d/m_capacity; +} +inline double Bin::pinDensity() const +{ + if (m_siteCapacity == 0) + return (m_pinDemand == 0)? 0.0 : std::numeric_limits::max(); + else + return (double)m_pinDemand/m_siteCapacity; +} +inline double Bin::pinDensity(Bin::index_type d) const +{ + if (m_siteCapacity == 0) + return (d == 0)? 0.0 : std::numeric_limits::max(); + else + return (double)d/m_siteCapacity; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp new file mode 100644 index 00000000..0b7524b3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp @@ -0,0 +1,51 @@ +/************************************************************************* + > File Name: BinMap.cpp + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Tue 23 Jun 2015 08:57:40 PM CDT + ************************************************************************/ + +#include "BinMap.h" + +DREAMPLACE_BEGIN_NAMESPACE + +BinMap& BinMap::set(BinType type, BinMap::index_type xNum, BinMap::index_type yNum) +{ + m_dimension[kX] = xNum; + m_dimension[kY] = yNum; + m_vBin.resize(xNum*yNum); + + // only set bin indices + // bin coordinates are set outside since layout information is not known + index_type id2DX = 0; + index_type id2DY = 0; + for (index_type id1D = 0, id1De = m_vBin.size(); id1D < id1De; ++id1D) + { + Bin& bin = m_vBin.at(id1D); + bin.setType(type); + bin.setIndex1D(id1D); + bin.setIndex2D(kX, id2DX) + .setIndex2D(kY, id2DY); + + id2DX += 1; + if (id2DX == m_dimension[kX]) + { + id2DX = 0; + id2DY += 1; + } + } + + return *this; +} + +BinMap& BinMap::resetBinDemand() +{ + for (BinMap1DIterator it = begin1D(), ite = end1D(); it != ite; ++it) + { + it->setDemand(0); + it->setPinDemand(0); + } + return *this; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h new file mode 100644 index 00000000..8915628d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h @@ -0,0 +1,114 @@ +/************************************************************************* + > File Name: BinMap.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun Jun 21 21:23:08 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_BINMAP_H +#define DREAMPLACE_BINMAP_H + +#include +#include "Bin.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// class BinMap holds all bins +class BinMap +{ + public: + typedef Object::index_type index_type; + typedef std::vector::iterator BinMap1DIterator; + typedef std::vector::const_iterator BinMap1DConstIterator; + + /// constructor + BinMap(BinType t = kBin, index_type xNum = 0, index_type yNum = 0); + /// copy constructor + BinMap(BinMap const& rhs); + /// assignment + BinMap& operator=(BinMap const& rhs); + + /// member functions + /// initialize bin map + BinMap& set(BinType type, index_type xNum, index_type yNum); + + /// \return dimensions + index_type dimension(Direction1DType d) const {return m_dimension[d];} + index_type dimensionX() const {return dimension(kX);} + index_type dimensionY() const {return dimension(kY);} + + /// \return total number of bins + index_type size() const {return m_vBin.size();} + + /// \return bin with index 1D + Bin const& getBin(index_type id1D) const {return m_vBin.at(id1D);} + Bin& getBin(index_type id1D) {return m_vBin.at(id1D);} + + /// \return bin with index 2D (x and y) + Bin const& getBin(index_type id2DX, index_type id2DY) const {return m_vBin.at(m_dimension[kX]*id2DY + id2DX);} + Bin& getBin(index_type id2DX, index_type id2DY) {return m_vBin.at(m_dimension[kX]*id2DY + id2DX);} + + /// \return left bin of current bin + /// if current bin is leftmost, return itself + Bin const& getLeftBin(Bin const& bin) const {return (bin.index1D()%dimensionX() == 0)? bin : getBin(bin.index1D()-1);} + Bin& getLeftBin(Bin& bin) {return (bin.index1D()%dimensionX() == 0)? bin : getBin(bin.index1D()-1);} + + /// \return right bin of current bin + /// if current bin is right most, return itself + Bin const& getRightBin(Bin const& bin) const {return ((bin.index1D()+1)%dimensionX() == 0)? bin : getBin(bin.index1D()+1);} + Bin& getRightBin(Bin& bin) {return ((bin.index1D()+1)%dimensionX() == 0)? bin : getBin(bin.index1D()+1);} + + /// \return lower bin of current bin + /// if current bin is lowest, return itself + Bin const& getLowerBin(Bin const& bin) const {return (bin.index1D() < dimensionX())? bin : getBin(bin.index1D()-dimensionX());} + Bin& getLowerBin(Bin& bin) {return (bin.index1D() < dimensionX())? bin : getBin(bin.index1D()-dimensionX());} + + /// \return upper bin of current bin + /// if current bin is top, return itself + Bin const& getUpperBin(Bin const& bin) const {return (bin.index1D()+dimensionX() >= size())? bin : getBin(bin.index1D()+dimensionX());} + Bin& getUpperBin(Bin& bin) {return (bin.index1D()+dimensionX() >= size())? bin : getBin(bin.index1D()+dimensionX());} + + /// \return 1D iterators of bins + BinMap1DIterator begin1D() {return m_vBin.begin();} + BinMap1DIterator end1D() {return m_vBin.end();} + BinMap1DConstIterator begin1D() const {return m_vBin.begin();} + BinMap1DConstIterator end1D() const {return m_vBin.end();} + + /// reset bin demand to zero + BinMap& resetBinDemand(); + + protected: + void copy(BinMap const& rhs); + + std::vector m_vBin; ///< bin array, corresponds to Bin::m_index1D + ///< the indexing order is locally horizontal and globally vertical + index_type m_dimension[2]; ///< 2D dimension in x and y direction of bin map, number of bins in x and y directions +}; + +inline BinMap::BinMap(BinType t, BinMap::index_type xNum, BinMap::index_type yNum) +{ + set(t, xNum, yNum); +} +inline BinMap::BinMap(BinMap const& rhs) +{ + copy(rhs); +} +inline BinMap& BinMap::operator=(BinMap const& rhs) +{ + if (this != &rhs) + copy(rhs); + return *this; +} +inline void BinMap::copy(BinMap const& rhs) +{ + m_vBin = rhs.m_vBin; + m_dimension[kX] = rhs.m_dimension[kX]; + m_dimension[kY] = rhs.m_dimension[kY]; +} + +typedef BinMap::BinMap1DIterator BinMap1DIterator; +typedef BinMap::BinMap1DConstIterator BinMap1DConstIterator; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp new file mode 100644 index 00000000..392e3dc9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp @@ -0,0 +1,83 @@ +/************************************************************************* + > File Name: BookshelfWriter.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "BookshelfWriter.h" +#include "Iterators.h" +#include "PlaceDB.h" +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +bool BookShelfWriter::write(std::string const& outFile, + float const* x, float const* y, + PlaceDB::index_type const* z) const +{ + std::string outFileNoSuffix = limbo::trim_file_suffix(outFile); + return writePlx(outFileNoSuffix, x, y, z); +} +bool BookShelfWriter::writeAll(std::string const& outFile, + float const* x, float const* y, + PlaceDB::index_type const* z) const +{ + std::string outFileNoSuffix = limbo::trim_file_suffix(outFile); + std::string designName = "design"; + + bool flag = writePlx(outFileNoSuffix, x, y, z); + + return true; +} + +bool BookShelfWriter::writePlx(std::string const& outFileNoSuffix, + float const* x, float const* y, + PlaceDB::index_type const* z) const +{ + FILE* out = openFile(outFileNoSuffix, "pl"); + if (out == NULL) + return false; + + writeHeader(out, "pl"); // use pl instead of plx to accommodate parser + + for (int mIdx = 0; mIdx < m_db.numMovable() + m_db.numFixed(); ++mIdx) + { + float xx = m_db.nodeX(mIdx); + float yy = m_db.nodeY(mIdx); + PlaceDB::index_type zz = m_db.nodeZ(mIdx); + fprintf(out, "%s %g %g %d", m_db.nodeName(mIdx).c_str(), xx, yy, zz); + if (mIdx < m_db.numMovable()) + { + fprintf(out, "\n"); + } else + { + fprintf(out, " /FIXED \n"); + } + } + + closeFile(out); + return true; +} + +void BookShelfWriter::writeHeader(FILE* os, std::string const& fileType) const +{ + fprintf(os, "\n"); +} +FILE* BookShelfWriter::openFile(std::string const& outFileNoSuffix, std::string const& fileType) const +{ + dreamplacePrint(kINFO, "writing placement to %s\n", (outFileNoSuffix+"."+fileType).c_str()); + + FILE* out = fopen((outFileNoSuffix+"."+fileType).c_str(), "w"); + if (out == NULL) + dreamplacePrint(kERROR, "unable to open %s for write\n", (outFileNoSuffix+"."+fileType).c_str()); + return out; +} +void BookShelfWriter::closeFile(FILE* os) const +{ + fclose(os); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h new file mode 100644 index 00000000..60dd7cd0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h @@ -0,0 +1,51 @@ +/************************************************************************* + > File Name: BookshelfWriter.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_BOOKSHELFWRITER_H +#define DREAMPLACE_BOOKSHELFWRITER_H + +#include +#include +#include "PlaceWriter.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class BookShelfWriter : public PlaceSolWriter +{ + public: + typedef PlaceSolWriter base_type; + typedef PlaceDB::index_type index_type; + + BookShelfWriter(PlaceDB const& db) : base_type(db) {} + BookShelfWriter(BookShelfWriter const& rhs) : base_type(rhs) {} + + /// write .plx file + /// \param outFile is plx file name + /// \param first, last should contain components to write + bool write(std::string const& outFile, + float const* x = NULL, float const* y = NULL, + PlaceDB::index_type const* z = NULL) const; + /// write all files in book shelf format + /// \param outFile is aux file name + /// \param first, last should contain components to write + bool writeAll(std::string const& outFile, + float const* x = NULL, float const* y = NULL, + PlaceDB::index_type const* z = NULL) const; + + protected: + bool writePlx(std::string const& outFileNoSuffix, + float const* x = NULL, float const* y = NULL, + PlaceDB::index_type const* z = NULL) const; + void writeHeader(FILE* os, std::string const& fileType) const; + FILE* openFile(std::string const& outFileNoSuffix, std::string const& fileType) const; + void closeFile(FILE* os) const; +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h new file mode 100644 index 00000000..a2a7cecb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h @@ -0,0 +1,451 @@ +/*********************************************************************************** + > File Name: Box.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + **********************************************************************************/ + +#ifndef DREAMPLACE_BOX_H +#define DREAMPLACE_BOX_H + +#include "Point.h" +#include "Interval.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +class Box +{ + public: + typedef T coordinate_type; + typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; + typedef typename coordinate_traits::area_type area_type; + typedef Interval interval_type; + typedef Point point_type; + + ///==== constructors ==== + /// default constructor will result in an invalid box + /// but it is more compatible with functions such as encompass() + Box() + { + set( + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() + ); + } + Box(coordinate_type xl, + coordinate_type yl, + coordinate_type xh, + coordinate_type yh) + { + set(xl, yl, xh, yh); + } + Box(interval_type const& ix, interval_type const& iy) + { + set(kX, ix); + set(kY, iy); + } + Box(Box const& rhs) + { + copy(rhs); + } + Box& operator=(Box const& rhs) + { + copy(rhs); + return *this; + } + template + explicit Box(BoxType const& rhs) + { + copy(rhs); + } + template + Box& operator=(BoxType const& rhs) + { + copy(rhs); + return *this; + } + + ///==== member functions ==== + /// set to uninitialized status + Box& unset() + { + set(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min()); + return *this; + } + Box& set(coordinate_type xl, coordinate_type yl, coordinate_type xh, coordinate_type yh) + { + m_range[kX].set(xl, xh); + m_range[kY].set(yl, yh); + return *this; + } + Box& set(Direction1DType d, interval_type const& range) + { + m_range[d] = range; + return *this; + } + Box& set(Direction1DType xy, Direction1DType lh, coordinate_type v) + { + m_range[xy].set(lh, v); + return *this; + } + Box& set(Direction2DType d, coordinate_type v) + { + return set(getXY(d), getLH(d), v); + } + /// encompass box with an input point + Box& encompass(point_type const& p) + { + m_range[kX].encompass(p.get(kX)); + m_range[kY].encompass(p.get(kY)); + return *this; + } + /// encompass box with an input box + Box& encompass(Box const& b) + { + m_range[kX].encompass(b.get(kX)); + m_range[kY].encompass(b.get(kY)); + return *this; + } + /// make sure a valid box + Box& adjust() + { + m_range[kX].adjust(); + m_range[kY].adjust(); + return *this; + } + interval_type const& get(Direction1DType xy) const + { + return m_range[xy]; + } + coordinate_type get(Direction1DType xy, Direction1DType lh) const + { + return m_range[xy].get(lh); + } + coordinate_type get(Direction2DType d) const + { + return get(getXY(d), getLH(d)); + } + coordinate_type xl() const + { + return get(kX, kLOW); + } + coordinate_type yl() const + { + return get(kY, kLOW); + } + coordinate_type xh() const + { + return get(kX, kHIGH); + } + coordinate_type yh() const + { + return get(kY, kHIGH); + } + manhattan_distance_type delta(Direction1DType d) const + { + return m_range[d].delta(); + } + manhattan_distance_type width() const + { + return m_range[kX].delta(); + } + manhattan_distance_type height() const + { + return m_range[kY].delta(); + } + /// \return area + area_type area() const + { + // be careful about overflow + return (area_type)width()*(area_type)height(); + } + + ///==== overload operators ==== + template + bool operator==(const BoxType& rhs) const + { + return m_range[kX] == rhs.m_range[kX] && m_range[kY] == rhs.m_range[kY]; + } + template + bool operator!=(const BoxType& rhs) const { return !((*this) == rhs); } + + std::string toString() const {return "(" + limbo::to_string(xl()) + ", " + limbo::to_string(yl()) + ", " + limbo::to_string(xh()) + ", " + limbo::to_string(yh()) + ")";} + protected: + template + void copy(BoxType const& rhs) + { + m_range[kX] = rhs.m_range[kX]; + m_range[kY] = rhs.m_range[kY]; + } + + interval_type m_range[2]; ///< two ranges define a box +}; + +/// \return lower left point +template +inline typename Box::point_type +ll(Box const& b) +{ + return typename Box::point_type(b.xl(), b.yl()); +} +/// \return lower right point +template +inline typename Box::point_type +lr(Box const& b) +{ + return typename Box::point_type(b.xh(), b.yl()); +} +/// \return upper left point +template +inline typename Box::point_type +ul(Box const& b) +{ + return typename Box::point_type(b.xl(), b.yh()); +} +/// \return upper right point +template +inline typename Box::point_type +ur(Box const& b) +{ + return typename Box::point_type(b.xh(), b.yh()); +} +/// \return center coordinate +template +inline typename coordinate_traits::coordinate_type +center(Box const& b, Direction1DType d) +{ + return center(b.get(d)); +} +/// \return center point +template +inline typename Box::point_type +center(Box const& b) +{ + return typename Box::point_type(center(b, kX), center(b, kY)); +} +/// \return half perimeter +template +inline typename coordinate_traits::manhattan_distance_type +halfPerimeter(Box const& b) +{ + return b.delta(kX)+b.delta(kY); +} +/// \return perimeter +template +inline typename coordinate_traits::manhattan_distance_type +perimeter(Box const& b) +{ + return halfPerimeter(b)*2; +} +/// specialization for integers +template <> +inline coordinate_traits::manhattan_distance_type +perimeter(Box const& b) +{ + return halfPerimeter(b)<<1; +} +/// \return true if a point is inside a box +template +inline bool contain(Box const& b, Point const& p) +{ + return contain(b.get(kX), p.x()) && contain(b.get(kY), p.y()); +} +/// \return true if a box contains the other +template +inline bool contain(Box const& b1, Box const& b2) +{ + return contain(b1.get(kX), b2.get(kX)) && contain(b1.get(kY), b2.get(kY)); +} +/// move a box in x or y direction +/// \param v denotes displacement +template +inline Box& move(Box& b, Direction1DType d, typename coordinate_traits::coordinate_type v) +{ + typename Box::interval_type ivl = b.get(d); + move(ivl, v); + return b.set(d, ivl); +} +/// move a box in two directions +/// \param vx denotes displacement in x +/// \param vy denotes displacement in y +template +inline Box& move(Box& b, typename coordinate_traits::coordinate_type vx, typename coordinate_traits::coordinate_type vy) +{ + return move(move(b, kX, vx), kY, vy); +} +/// move a box in two directions +/// \param p.x() denotes destination in x +/// \param p.y() denotes destination in y +template +inline Box& move(Box& b, Point const& p) +{ + return move(b, p.x(), p.y()); +} +/// move a box to a position in x or y direction +/// \param v denotes destination of left lower corner +template +inline Box& moveTo(Box& b, Direction1DType d, typename coordinate_traits::coordinate_type v) +{ + typename Box::interval_type ivl = b.get(d); + moveTo(ivl, v); + return b.set(d, ivl); +} +/// move a box in two directions +/// \param vx denotes destination in x +/// \param vy denotes destination in y +template +inline Box& moveTo(Box& b, typename coordinate_traits::coordinate_type vx, typename coordinate_traits::coordinate_type vy) +{ + return moveTo(moveTo(b, kX, vx), kY, vy); +} +/// move a box in two directions +/// \param p.x() denotes destination in x +/// \param p.y() denotes destination in y +template +inline Box& moveTo(Box& b, Point const& p) +{ + return moveTo(b, p.x(), p.y()); +} +/// \return manhattan distance in a direction between a box and a point +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Box const& b, typename Box::point_type const& p, Direction1DType d) +{ + return distance(b.get(d), p.get(d)); +} +/// \return manhattan distance between a box and a point +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Box const& b, typename Box::point_type const& p) +{ + return manhattanDistance(b, p, kX)+manhattanDistance(b, p, kY); +} +/// \return square distance between a box and a point +template +inline typename coordinate_traits::euclidean_distance_type +squareDistance(Box const& b, typename Box::point_type const& p) +{ + typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; + return (euclidean_distance_type)( + pow(manhattanDistance(b, p, kX), 2) + + pow(manhattanDistance(b, p, kY), 2) + ); +} +/// \return euclidean distance between a box and a point +template +inline typename coordinate_traits::euclidean_distance_type +euclideanDistance(Box const& b, typename Box::point_type const& p) +{ + return sqrt(squareDistance(b, p)); +} +/// \return manhattan distance in a direction between two boxes +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Box const& b1, Box const& b2, Direction1DType d) +{ + return distance(b1.get(d), b2.get(d)); +} +/// \return manhattan distance between two boxes +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Box const& b1, Box const& b2) +{ + return manhattanDistance(b1, b2, kX) + manhattanDistance(b1, b2, kY); +} +/// \return square distance between two boxes +template +inline typename coordinate_traits::euclidean_distance_type +squareDistance(Box const& b1, Box const& b2) +{ + typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; + return (euclidean_distance_type)( + pow(manhattanDistance(b1, b2, kX), 2) + + pow(manhattanDistance(b1, b2, kY), 2) + ); +} +/// \return euclidean distance between two boxes +template +inline typename coordinate_traits::euclidean_distance_type +euclideanDistance(Box const& b1, Box const& b2) +{ + return sqrt(squareDistance(b1, b2)); +} +/// \return the intersection of two boxes +template +inline std::pair, bool> +intersection(Box const& b1, Box const& b2, bool consider_touch = true) +{ + std::pair, bool> ivl[2] = { + intersection(b1.get(kX), b2.get(kX), consider_touch), + intersection(b1.get(kY), b2.get(kY), consider_touch) + }; + return std::make_pair(Box(ivl[kX].first, ivl[kY].first), ivl[kX].second && ivl[kY].second); +} +/// \return true if two boxes have intersection +template +inline bool intersects(Box const& b1, Box const& b2, bool consider_touch = true) +{ + return intersects(b1.get(kX), b2.get(kX), consider_touch) + && intersects(b1.get(kY), b2.get(kY), consider_touch); +} +/// \return the intersection area of two boxes +template +inline typename coordinate_traits::area_type +intersectArea(Box const& b1, Box const& b2) +{ + typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; + typedef typename coordinate_traits::area_type area_type; + manhattan_distance_type dist[2] = { + intersectDistance(b1.get(kX), b2.get(kX)), + intersectDistance(b1.get(kY), b2.get(kY)) + }; + return (area_type)dist[kX]*dist[kY]; +} + +/// \return true if a point is on boundary of the box +template +inline bool onBoundary(Box const& b, Point const& p) +{ + return (onBoundary(b.get(kX), p.x()) && contain(b.get(kY), p.y())) + || (onBoundary(b.get(kY), p.y()) && contain(b.get(kX), p.x())); +} + +/// \return true if boxes can be merged - merge b1 and b2 to b2 +template +inline bool mergeBoxes(Box &b1, Box &b2, int sitePerColumn) //Rectilinear not considered +{ + if (b1.xl() == b2.xl() && b1.xh() == b2.xh()) + { + if (sitePerColumn == 1) + { + b2.set(std::min(b1.xl(), b2.xl()), std::min(b1.yl(), b2.yl()), std::max(b1.xh(), b2.xh()), std::max(b1.yh(), b2.yh())); + } else + { + //Boxes on top/bottom of each other + if (b1.yh() == b2.yl()) //b2 on top of b1 + { + b2.set(b1.xl(), b1.yl(), b2.xh(), b2.yh()); + } else if (b1.yl() == b2.yh()) //b2 below b1 + { + b2.set(b2.xl(), b2.yl(), b1.xh(), b1.yh()); + } //Boxes partially inside another box - merge to one big box + else if (b1.yl() < b2.yh() && b2.yh() < b1.yh()) + { + b2.set(b2.xl(), b2.yl(), b2.xh(), b1.yh()); + } else if (b2.yl() < b1.yh() && b1.yh() < b2.yh()) //b2 below b1 + { + b2.set(b2.xl(), b1.yl(), b2.xh(), b2.yh()); + } + } + return true; + } + return false; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp new file mode 100644 index 00000000..a05977cf --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp @@ -0,0 +1,133 @@ +/************************************************************************* + > File Name: Enums.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "Enums.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#ifndef ENUM2STR +#define ENUM2STR(map, var) \ + map[enum_wrap_type::var] = #var +#endif + +#ifndef STR2ENUM +#define STR2ENUM(map, var) \ + map[#var] = enum_wrap_type::var +#endif + +std::string InstBlk::enum2Str(InstBlk::enum_type const& e) const +{ + static std::map mEnum2Str; + static bool init = true; + + if (init) + { + ENUM2STR(mEnum2Str, LUT0); + ENUM2STR(mEnum2Str, LUT1); + ENUM2STR(mEnum2Str, LUT2); + ENUM2STR(mEnum2Str, LUT3); + ENUM2STR(mEnum2Str, LUT4); + ENUM2STR(mEnum2Str, LUT5); + ENUM2STR(mEnum2Str, LUT6); + ENUM2STR(mEnum2Str, LUT6_2); + ENUM2STR(mEnum2Str, FDRE); + ENUM2STR(mEnum2Str, DSP48E2); + ENUM2STR(mEnum2Str, RAMB36E2); + ENUM2STR(mEnum2Str, BUFGCE); + ENUM2STR(mEnum2Str, IBUF); + ENUM2STR(mEnum2Str, OBUF); + ENUM2STR(mEnum2Str, UNKNOWN); + init = false; + } + + return mEnum2Str.at(e); +} + +InstBlk::enum_type InstBlk::str2Enum(std::string const& s) const +{ + static std::map mStr2Enum; + static bool init = true; + + if (init) + { + STR2ENUM(mStr2Enum, LUT0); + STR2ENUM(mStr2Enum, LUT1); + STR2ENUM(mStr2Enum, LUT2); + STR2ENUM(mStr2Enum, LUT3); + STR2ENUM(mStr2Enum, LUT4); + STR2ENUM(mStr2Enum, LUT5); + STR2ENUM(mStr2Enum, LUT6); + STR2ENUM(mStr2Enum, LUT6_2); + STR2ENUM(mStr2Enum, FDRE); + STR2ENUM(mStr2Enum, DSP48E2); + STR2ENUM(mStr2Enum, RAMB36E2); + STR2ENUM(mStr2Enum, BUFGCE); + STR2ENUM(mStr2Enum, IBUF); + STR2ENUM(mStr2Enum, OBUF); + STR2ENUM(mStr2Enum, UNKNOWN); + init = false; + } + + std::map::const_iterator found = mStr2Enum.find(s); + if (found == mStr2Enum.end()) + { + dreamplacePrint(kWARN, "%s unknown enum type %s, set to UNKNOWN\n", __func__, s.c_str()); + return enum_wrap_type::UNKNOWN; + } + else + { + return found->second; + } +} + +std::string Site::enum2Str(Site::enum_type const& e) const +{ + static std::map mEnum2Str; + static bool init = true; + + if (init) + { + ENUM2STR(mEnum2Str, IO); + ENUM2STR(mEnum2Str, SLICE); + ENUM2STR(mEnum2Str, DSP); + ENUM2STR(mEnum2Str, BRAM); + ENUM2STR(mEnum2Str, UNKNOWN); + init = false; + } + + return mEnum2Str.at(e); +} + +Site::enum_type Site::str2Enum(std::string const& s) const +{ + static std::map mStr2Enum; + static bool init = true; + + if (init) + { + STR2ENUM(mStr2Enum, IO); + STR2ENUM(mStr2Enum, SLICE); + STR2ENUM(mStr2Enum, DSP); + STR2ENUM(mStr2Enum, BRAM); + STR2ENUM(mStr2Enum, UNKNOWN); + init = false; + } + + std::map::const_iterator found = mStr2Enum.find(s); + if (found == mStr2Enum.end()) + { + dreamplacePrint(kWARN, "%s unknown enum type %s, set to UNKNOWN\n", __func__, s.c_str()); + return enum_wrap_type::UNKNOWN; + } + else + { + return found->second; + } +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h new file mode 100644 index 00000000..63af34f3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h @@ -0,0 +1,222 @@ +/************************************************************************* + > File Name: Enums.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_ENUMS_H +#define DREAMPLACE_ENUMS_H + +#include +#include +#include +#include "Util.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// base class for enumeration types +/// these types are not recommended for storage, since they takes larger memory +template +class EnumExt +{ + public: + typedef EnumType enum_type; + EnumExt() {} + EnumExt& operator=(EnumExt const& rhs) + { + if (this != &rhs) + m_value = rhs.m_value; + return *this; + } + EnumExt& operator=(enum_type const& rhs) + { + m_value = rhs; + return *this; + } + EnumExt& operator=(std::string const& rhs) + { + m_value = str2Enum(rhs); + return *this; + } + virtual operator std::string() const + { + return enum2Str(m_value); + } + operator int() const + { + return value(); + } + enum_type value() const + { + return m_value; + } + + bool operator==(EnumExt const& rhs) const {return m_value == rhs.m_value;} + bool operator==(enum_type const& rhs) const {return m_value == rhs;} + bool operator==(std::string const& rhs) const {return *this == EnumExt(rhs);} + bool operator!=(EnumExt const& rhs) const {return m_value != rhs.m_value;} + bool operator!=(enum_type const& rhs) const {return m_value != rhs;} + bool operator!=(std::string const& rhs) const {return *this != EnumExt(rhs);} + + friend std::ostream& operator<<(std::ostream& os, const EnumExt& rhs) + { + rhs.print(os); + return os; + } + protected: + virtual void print(std::ostream& os) const {os << this->enum2Str(m_value);} + + virtual std::string enum2Str(enum_type const&) const = 0; + virtual enum_type str2Enum(std::string const&) const = 0; + + enum_type m_value; +}; + +/// class InstBlk denotes Instance type +struct InstBlkEnum +{ + enum InstBlkType + { + LUT0 = 0, + LUT1 = 0, + LUT2 = 1, + LUT3 = 2, + LUT4 = 3, + LUT5 = 4, + LUT6 = 5, + LUT6_2 = 5, + FDRE = 6, + DSP48E2 = 7, + RAMB36E2 = 8, + BUFGCE = 9, + IBUF = 10, + OBUF = 11, + UNKNOWN = 12 + }; +}; +class InstBlk : public EnumExt +{ + public: + typedef InstBlkEnum enum_wrap_type; + typedef enum_wrap_type::InstBlkType enum_type; + typedef EnumExt base_type; + + InstBlk() : base_type() {m_value = enum_wrap_type::UNKNOWN;} + InstBlk(InstBlk const& rhs) : base_type() {m_value = rhs.m_value;} + InstBlk(enum_type const& rhs) : base_type() {m_value = rhs;} + InstBlk(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} + InstBlk& operator=(InstBlk const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + InstBlk& operator=(enum_type const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + InstBlk& operator=(std::string const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + + protected: + virtual std::string enum2Str(enum_type const& e) const; + virtual enum_type str2Enum(std::string const& s) const; +}; + + +/// class Site denotes Site type +struct SiteEnum +{ + enum SiteType + { + IO = 0, + SLICE = 1, + DSP = 2, + BRAM = 3, + UNKNOWN = 4 + }; +}; +class Site : public EnumExt +{ + public: + typedef SiteEnum enum_wrap_type; + typedef enum_wrap_type::SiteType enum_type; + typedef EnumExt base_type; + + Site() : base_type() {m_value = enum_wrap_type::UNKNOWN;} + Site(Site const& rhs) : base_type() {m_value = rhs.m_value;} + Site(enum_type const& rhs) : base_type() {m_value = rhs;} + Site(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} + Site& operator=(Site const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + Site& operator=(enum_type const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + Site& operator=(std::string const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + + protected: + virtual std::string enum2Str(enum_type const& e) const; + virtual enum_type str2Enum(std::string const& s) const; +}; + +/// class RegionEnumType denotes the region type defined in DEF +struct RegionTypeEnum +{ + enum RegionEnumType + { + FENCE = 0, + GUIDE = 1, + UNKNOWN = 2 + }; +}; + +class RegionType : public EnumExt +{ + public: + typedef RegionTypeEnum enum_wrap_type; + typedef enum_wrap_type::RegionEnumType enum_type; + typedef EnumExt base_type; + + RegionType() : base_type() {m_value = enum_wrap_type::UNKNOWN;} + RegionType(RegionType const& rhs) : base_type() {m_value = rhs.m_value;} + RegionType(enum_type const& rhs) : base_type() {m_value = rhs;} + RegionType(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} + RegionType& operator=(RegionType const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + RegionType& operator=(enum_type const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + RegionType& operator=(std::string const& rhs) + { + this->base_type::operator=(rhs); + return *this; + } + + protected: + virtual std::string enum2Str(enum_type const& e) const; + virtual enum_type str2Enum(std::string const& s) const; +}; + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h new file mode 100644 index 00000000..165db190 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h @@ -0,0 +1,87 @@ +/************************************************************************* + > File Name: GeometryApi.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Tue Jun 30 16:57:11 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_GEOMETRYAPI_H +#define DREAMPLACE_GEOMETRYAPI_H + +#include +#include +#include +#include "Util.h" +#include "Point.h" +#include "Box.h" + +/// limbo.geometry api +/// with this api, algorithms in limbo.geometry are available +namespace limbo { namespace geometry { + +/// \brief specialization for boost::polygon::point_data +template +struct point_traits > +{ + typedef T coordinate_type; + typedef DREAMPLACE_NAMESPACE::Point point_type; + + static coordinate_type get(const point_type& point, orientation_2d const& orient) + { + if (orient == HORIZONTAL) return point.x(); + else if (orient == VERTICAL) return point.y(); + else {assert(0); return 0;} + } + static void set(point_type& point, orientation_2d const& orient, coordinate_type const& value) + { + if (orient == HORIZONTAL) point.set(DREAMPLACE_NAMESPACE::kX, value); + else if (orient == VERTICAL) point.set(DREAMPLACE_NAMESPACE::kY, value); + else dreamplaceAssertMsg(0, "unknown orient"); + } + static point_type construct(coordinate_type const& x, coordinate_type const& y) + { + return point_type(x, y); + } +}; + +/// \brief specialization for boost::polygon::rectangle_data +template +struct rectangle_traits > +{ + typedef T coordinate_type; + typedef DREAMPLACE_NAMESPACE::Box rectangle_type; + + static coordinate_type get(const rectangle_type& rect, direction_2d const& dir) + { + switch (dir) + { + case LEFT: return rect.xl(); + case BOTTOM: return rect.yl(); + case RIGHT: return rect.xh(); + case TOP: return rect.yh(); + default: dreamplaceAssertMsg(0, "unknown orient"); + } + return std::numeric_limits::max(); + } + static void set(rectangle_type& rect, direction_2d const& dir, coordinate_type const& value) + { + switch (dir) + { + case LEFT: rect.set(DREAMPLACE_NAMESPACE::kXLOW, value); break; + case BOTTOM: rect.set(DREAMPLACE_NAMESPACE::kYLOW, value); break; + case RIGHT: rect.set(DREAMPLACE_NAMESPACE::kXHIGH, value); break; + case TOP: rect.set(DREAMPLACE_NAMESPACE::kYHIGH, value); break; + default: dreamplaceAssertMsg(0, "unknown orient"); + } + } + static rectangle_type construct(coordinate_type const& xl, coordinate_type const& yl, + coordinate_type const& xh, coordinate_type const& yh) + { + return rectangle_type(xl, yl, xh, yh); + } +}; + +}}// namespace limbo // namespace geometry + + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp new file mode 100644 index 00000000..9bc020e0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp @@ -0,0 +1,41 @@ +/** + * @file Group.cpp + * @author Yibo Lin (DREAMPlace) + * @date Dec 2019 + */ +#include "Group.h" + +DREAMPLACE_BEGIN_NAMESPACE + +Group::Group() + : Group::base_type() + , m_name("") + , m_vNodeName() + , m_vNodeId() + , m_region_id(std::numeric_limits::max()) +{ +} +Group::Group(Group const& rhs) + : Group::base_type(rhs) +{ + copy(rhs); +} +Group& Group::operator=(Group const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +void Group::copy(Group const& rhs) +{ + m_name = rhs.m_name; + m_vNodeName = rhs.m_vNodeName; + m_vNodeId = rhs.m_vNodeId; + m_region_id = rhs.m_region_id; +} + + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h new file mode 100644 index 00000000..535acd4c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h @@ -0,0 +1,53 @@ +/** + * @file Group.h (DREAMPlace) + * @author Yibo Lin + * @date Dec 2019 + */ + +#ifndef DREAMPLACE_GROUP_H +#define DREAMPLACE_GROUP_H + +#include +#include "Object.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class Group : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef coordinate_traits::index_type index_type; + + /// default constructor + Group(); + /// copy constructor + Group(Group const& rhs); + /// assignment + Group& operator=(Group const& rhs); + + /// node functions + std::string const& name() const {return m_name;} + Group& setName(std::string const& name) {m_name = name; return *this;} + + std::vector const& nodeNames() const {return m_vNodeName;} + std::vector& nodeNames() {return m_vNodeName;} + + std::vector const& nodes() const {return m_vNodeId;} + std::vector& nodes() {return m_vNodeId;} + + index_type region() const {return m_region_id;} + Group& setRegion(index_type region_id) {m_region_id = region_id; return *this;} + + protected: + void copy(Group const& rhs); + + std::string m_name; ///< group name + std::vector m_vNodeName; ///< group node names, they names may be regex + std::vector m_vNodeId; ///< group node indices + index_type m_region_id; ///< region index +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h new file mode 100644 index 00000000..a2128ed7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h @@ -0,0 +1,262 @@ +/************************************************************************* + > File Name: HrchyList.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Mon 01 Feb 2016 02:17:29 PM CST + ************************************************************************/ + +#ifndef DREAMPLACE_HRCHYLIST_H +#define DREAMPLACE_HRCHYLIST_H + +#include +#include +#include +#include "Util.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// HrchyList defines a hierarchical list for data storage. +/// Data is stored like a matrix with 2-dimension (row/column). +/// The second dimension may have different size with different first dimension. +/// So it is not actually a matrix. +/// The api allows to iterate the HrchyList like a 1D array. + +template +class HrchyList +{ + public: + typedef T value_type; + typedef unsigned int index_type; + typedef std::vector container_type; + typedef typename container_type::iterator iterator_type; + typedef typename container_type::const_iterator const_iterator_type; + + /// constructor + HrchyList() + { + } + /// copy constructor + HrchyList(HrchyList const& rhs) + { + copy(rhs); + } + /// assignment + HrchyList& operator=(HrchyList const& rhs) + { + if (this != &rhs) + copy(rhs); + return *this; + } + /// destructor + ~HrchyList() {} + + /// \return size of all data + index_type size() const {return m_vData.size();} + /// \return number of rows + index_type numRows() const {return m_vRowBeginIdx.size();} + /// \return next row begin index + /// overflow to m_vData.size() + index_type getNextRowBeginIndex(index_type rowIdx) const + { + index_type nr = numRows(); + index_type nextRowIdx = rowIdx+1; + index_type nextRowBeginIdx; + if (nextRowIdx >= nr) // rowIdx is last row + nextRowBeginIdx = m_vData.size(); + else + { + nextRowBeginIdx = m_vRowBeginIdx[nextRowIdx]; + if (nextRowBeginIdx >= s_largeIdx)// nextRowIdx is empty + nextRowBeginIdx -= s_largeIdx; + } + return nextRowBeginIdx; + } + /// \return number of columns in a specific row + index_type numCols(index_type rowIdx) const + { + index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; + index_type nextRowBeginIdx; + if (rowBeginIdx < s_largeIdx) // non-empty row + nextRowBeginIdx = getNextRowBeginIndex(rowIdx); + else // empty row + nextRowBeginIdx = rowBeginIdx; + return nextRowBeginIdx-rowBeginIdx; + } + + /// \return a row of data + std::pair row(index_type rowIdx) + { + index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; + index_type nextRowBeginIdx; + if (rowBeginIdx < s_largeIdx) // non-empty row + nextRowBeginIdx = getNextRowBeginIndex(rowIdx); + else // empty row + nextRowBeginIdx = rowBeginIdx = rowBeginIdx-s_largeIdx; + return std::make_pair(m_vData.begin()+rowBeginIdx, m_vData.begin()+nextRowBeginIdx); + } + std::pair row(index_type rowIdx) const + { + index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; + index_type nextRowBeginIdx; + if (rowBeginIdx < s_largeIdx) // non-empty row + nextRowBeginIdx = getNextRowBeginIndex(rowIdx); + else // empty row + nextRowBeginIdx = rowBeginIdx = rowBeginIdx-s_largeIdx; + return std::make_pair(m_vData.begin()+rowBeginIdx, m_vData.begin()+nextRowBeginIdx); + } + /// \return range of rows + /// assume \param rowIdxL < \param rowIdxH and they are all within the range + std::pair rowRange(index_type rowIdxL, index_type rowIdxH) + { + index_type rowBeginIdxL = m_vRowBeginIdx[rowIdxL]; + if (rowBeginIdxL >= s_largeIdx) // empty row of rowIdxL + rowBeginIdxL -= s_largeIdx; + index_type nextRowBeginIdxH = getNextRowBeginIndex(rowIdxH); + + return std::make_pair(m_vData.begin()+rowBeginIdxL, m_vData.begin()+nextRowBeginIdxH); + } + std::pair rowRange(index_type rowIdxL, index_type rowIdxH) const + { + index_type rowBeginIdxL = m_vRowBeginIdx[rowIdxL]; + if (rowBeginIdxL >= s_largeIdx) // empty row of rowIdxL + rowBeginIdxL -= s_largeIdx; + index_type nextRowBeginIdxH = getNextRowBeginIndex(rowIdxH); + + return std::make_pair(m_vData.begin()+rowBeginIdxL, m_vData.begin()+nextRowBeginIdxH); + } + + /// \return the index of first non-empty row + index_type frontNonemptyRowIndex() const + { + index_type rowIdx = 0; + index_type nr = numRows(); + while (rowIdx < nr && m_vRowBeginIdx[rowIdx] >= s_largeIdx) + ++rowIdx; + return rowIdx; + } + /// \return the index of last non-empty row + index_type backNonemptyRowIndex() const + { + index_type rowIdx = numRows(); + while (rowIdx > 0 && m_vRowBeginIdx[rowIdx-1] >= s_largeIdx) + --rowIdx; + return rowIdx-1; // overflow if no non-empty row + } + + /// \return a single value + /// assume row is not empty + T& at(index_type rowIdx, index_type colIdx) {return m_vData[m_vRowBeginIdx[rowIdx]+colIdx];} + T const& at(index_type rowIdx, index_type colIdx) const {return m_vData[m_vRowBeginIdx[rowIdx]+colIdx];} + + /// \return begin iterator of data + iterator_type begin() {return m_vData.begin();} + const_iterator_type begin() const {return m_vData.begin();} + /// \return end iterator of data + iterator_type end() {return m_vData.end();} + const_iterator_type end() const {return m_vData.end();} + /// \return data + std::vector& data() {return m_vData;} + std::vector const& data() const {return m_vData;} + + /// \return the begin iterator of m_vRowBeginIdx + std::vector::iterator beginRowIndex() {return m_vRowBeginIdx.begin();} + std::vector::const_iterator beginRowIndex() const {return m_vRowBeginIdx.begin();} + /// \return the end iterator of m_vRowBeginIdx + std::vector::iterator endRowIndex() {return m_vRowBeginIdx.end();} + std::vector::const_iterator endRowIndex() const {return m_vRowBeginIdx.end();} + + /// construct HrchyList with a 2D array + void set(std::vector > const& mData) + { + index_type numData = 0; + for (typename std::vector >::const_iterator it1 = mData.begin(), it1e = mData.end(); it1 != it1e; ++it1) + numData += it1->size(); + m_vData.resize(numData); + m_vRowBeginIdx.resize(mData.size(), 0); + + index_type count = 0; + for (index_type rowIdx = 0; rowIdx < mData.size(); ++rowIdx) + { + m_vRowBeginIdx[rowIdx] = count; + if (mData[rowIdx].empty()) + m_vRowBeginIdx[rowIdx] += s_largeIdx; + + for (index_type colIdx = 0; colIdx < mData[rowIdx].size(); ++colIdx) + m_vData[count++] = mData[rowIdx][colIdx]; + } + } + /// construct HrchyList by three steps + /// this is the first step that append all the data to m_vData + void setPartial1(value_type v) + { + m_vData.push_back(v); + } + /// this is the second step that given all the row index of existing data + /// construct the rest, \param vRowId must have the same order with m_vData + /// \param rn denotes number of rows in the list + /// \param vRowId assumes the row id starts from 0 + void setPartial2(std::vector const& vRowId, std::vector >& mData, index_type rn) + { + mData.resize(rn); + for (index_type i = 0, ie = m_vData.size(); i < ie; ++i) + mData[vRowId[i]].push_back(m_vData[i]); + // users are allowed to manipulate mData before calling the third step + } + /// this is the third step + /// actually an alias of set() + void setPartial3(std::vector > const& mData) + { + set(mData); + } + /// clear all data + void clear() + { + m_vData.clear(); + m_vRowBeginIdx.clear(); + } + + /// for debug + void printHrchy(std::ostream& os = std::cout) const + { + for (index_type rowIdx = 0; rowIdx < numRows(); ++rowIdx) + { + std::pair found = row(rowIdx); + os << "#" << rowIdx << ": (" << numCols(rowIdx) << ")"; + for (const_iterator_type it = found.first; it != found.second; ++it) + os << *it << " "; + os << "\n"; + } + } + void print(std::ostream& os = std::cout) const + { + os << "data: "; + for (const_iterator_type it = begin(), ite = end(); it != ite; ++it) + os << *it << " "; + os << "\n"; + os << "row begin idx: "; + for (std::vector::const_iterator it = beginRowIndex(), ite = endRowIndex(); it != ite; ++it) + os << *it << " "; + os << "\n"; + os << "first non-empty row index: " << frontNonemptyRowIndex() << "\n"; + os << "last non-empty row index: " << backNonemptyRowIndex() << "\n"; + } + protected: + void copy(HrchyList const& rhs) + { + m_vData = rhs.m_vData; + m_vRowBeginIdx = rhs.m_vRowBeginIdx; + } + + std::vector m_vData; + std::vector m_vRowBeginIdx; ///< the index of row beginners + ///< if a row is empty, m_vRowBeginIdx[] is set to s_largeIdx + m_vRowBeginIdx[non-empty index] + ///< e.g. row 1 is empty, row 2 is not empty, then m_vRowBeginIdx[1] = s_largeIdx + m_vRowBeginIdx[2] + static const index_type s_largeIdx; ///< a very large number for index +}; + +template +const typename HrchyList::index_type HrchyList::s_largeIdx = std::numeric_limits::index_type>::max()>>1; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h new file mode 100644 index 00000000..b2fcdab5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h @@ -0,0 +1,69 @@ +/************************************************************************* + > File Name: Index.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sat 27 Jun 2015 04:31:39 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_INDEX_H +#define DREAMPLACE_INDEX_H + +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// 2D index class in x and y direction +template +class Index2D +{ + public: + typedef T index_type; + + ///==== constructors ==== + Index2D(index_type ix = std::numeric_limits::max(), + index_type iy = std::numeric_limits::max()) + { + set(ix, iy); + } + Index2D(Index2D const& rhs) + { + copy(rhs); + } + Index2D& operator=(Index2D const& rhs) + { + copy(rhs); + return *this; + } + + /// member functions + Index2D& set(Direction1DType d, index_type v) + { + m_index[d] = v; + } + Index2D& set(index_type ix, index_type iy) + { + m_index[kX] = ix; + m_index[kY] = iy; + } + + index_type get(Direction1DType d) const {return m_index[d];} + index_type x() const {return m_index[kX];} + index_type y() const {return m_index[kY];} + + /// compatible to operator [] + index_type const& operator[](Direction1DType d) const {return m_index[d];} + index_type& operator[](Direction1DType d) {return m_index[d];} + + protected: + void copy(Index2D const& rhs) + { + m_index[kX] = rhs.m_index[kX]; + m_index[kY] = rhs.m_index[kY]; + } + + T m_index[2]; +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h new file mode 100644 index 00000000..4a05166c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h @@ -0,0 +1,266 @@ +/************************************************************************* + > File Name: Interval.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Jun 2015 08:52:22 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_INTERVAL_H +#define DREAMPLACE_INTERVAL_H + +#include +#include +#include "Util.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// an interval class +/// it consists of a low value and a high value +template +class Interval +{ + public: + typedef T coordinate_type; + typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; + + ///==== constructors ==== + /// default constructor will result in an invalid interval + /// but it is more compatible to functions such as encompass() + Interval(coordinate_type l = std::numeric_limits::max(), + coordinate_type h = std::numeric_limits::min()) + { + set(l, h); + } + + Interval(Interval const& rhs) + { + copy(rhs); + } + + Interval& operator=(Interval const& rhs) + { + copy(rhs); + return *this; + } + + template + Interval& operator=(IntervalType const& rhs) + { + copy(rhs); + return *this; + } + + ///==== public member functions ==== + Interval& set(coordinate_type l, coordinate_type h) + { + m_coords[kLOW] = l; + m_coords[kHIGH] = h; + return *this; + } + Interval& set(Direction1DType d, coordinate_type v) + { + m_coords[d] = v; + return *this; + } + /// encompass interval with an input value + Interval& encompass(coordinate_type v) + { + if (m_coords[kLOW] > v) m_coords[kLOW] = v; + if (m_coords[kHIGH] < v) m_coords[kHIGH] = v; + return *this; + } + /// encompass interval with an input interval + Interval& encompass(Interval const& i) + { + encompass(i.get(kLOW)); + encompass(i.get(kHIGH)); + return *this; + } + /// make sure low is no larger than high + Interval& adjust() + { + if (m_coords[kLOW] > m_coords[kHIGH]) + swap(m_coords[kLOW], m_coords[kHIGH]); + return *this; + } + coordinate_type get(Direction1DType d) const + { + return m_coords[d]; + } + coordinate_type low() const + { + return m_coords[kLOW]; + } + coordinate_type high() const + { + return m_coords[kHIGH]; + } + manhattan_distance_type delta() const + { + return high() - low(); + } + + ///==== overload operators ==== + bool operator==(Interval const& rhs) const + { + return low() == rhs.low() && high() == rhs.high(); + } + + bool operator!=(Interval const& rhs) const + { + return low() != rhs.low() || high() != rhs.high(); + } + + bool operator<(Interval const& rhs) const + { + if (m_coords[0] != rhs.m_coords[0]) { + return m_coords[0] < rhs.m_coords[0]; + } + return m_coords[1] < rhs.m_coords[1]; + } + + bool operator<=(Interval const& rhs) const + { + return !(rhs < *this); + } + + bool operator>(Interval const& rhs) const + { + return rhs < *this; + } + + bool operator>=(Interval const& rhs) const + { + return !((*this) < rhs); + } + + /// comparison function objects + struct CompareByLow + { + bool operator()(Interval const& i1, Interval const& i2) const {return i1.low() < i2.low();} + }; + struct CompareByHigh + { + bool operator()(Interval const& i1, Interval const& i2) const {return i1.high() < i2.high();} + }; + struct CompareByCenter + { + bool operator()(Interval const& i1, Interval const& i2) const {return i1.center() < i2.center();} + }; + + protected: + template + void copy(IntervalType const& rhs) + { + m_coords[0] = rhs.m_coords[0]; + m_coords[1] = rhs.m_coords[1]; + } + + coordinate_type m_coords[2]; ///< low and high values +}; + +/// \return center of an interval +template +inline typename coordinate_traits::coordinate_type +center(Interval const& i1) +{ + return (i1.low()+i1.high())/2; +} +/// specialization for integers +template <> +inline coordinate_traits::coordinate_type +center(Interval const& i1) +{ + return (i1.low()+i1.high())>>1; +} +/// \return the intersection of two intervals +/// false denotes no intersection +template +inline std::pair, bool> +intersection(Interval const& i1, Interval const& i2, bool consider_touch = true) +{ + typedef typename coordinate_traits::coordinate_type coordinate_type; + coordinate_type l = std::max(i1.low(), i2.low()); + coordinate_type h = std::min(i1.high(), i2.high()); + bool valid = (consider_touch)? l <= h : l < h; + + return std::make_pair(Interval(l, h), valid); +} +/// \return true if two intervals have intersection +template +inline bool intersects(Interval const& i1, Interval const& i2, bool consider_touch = true) +{ + typedef typename coordinate_traits::coordinate_type coordinate_type; + coordinate_type l = std::max(i1.low(), i2.low()); + coordinate_type h = std::min(i1.high(), i2.high()); + return (consider_touch)? l <= h : l < h; +} +/// \return the intersection distance of two intervals +template +inline typename coordinate_traits::manhattan_distance_type +intersectDistance(Interval const& i1, Interval const& i2) +{ + typedef typename coordinate_traits::coordinate_type coordinate_type; + typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; + coordinate_type l = std::max(i1.low(), i2.low()); + coordinate_type h = std::min(i1.high(), i2.high()); + return (l < h)? (manhattan_distance_type)h-l : 0; +} + +/// \return true if an interval contains a value +template +inline bool contain(Interval const& i1, typename coordinate_traits::coordinate_type v) +{ + return i1.low() <= v && v <= i1.high(); +} +/// \return true if an interval contains the other +template +inline bool contain(Interval const& i1, Interval const& i2) +{ + return i1.low() <= i2.low() && i2.high() <= i1.high(); +} +/// move an interval +template +inline Interval& move(Interval& i1, typename coordinate_traits::coordinate_type v) +{ + return i1.set(i1.low()+v, i1.high()+v); +} +/// move an interval to a destination +/// \param v is the final lower value, the delta of interval does not change +template +inline Interval& moveTo(Interval& i1, typename coordinate_traits::coordinate_type v) +{ + typename coordinate_traits::manhattan_distance_type delta = i1.delta(); + return i1.set(v, v+delta); +} +/// \return distance +template +inline typename coordinate_traits::manhattan_distance_type +distance(Interval const& i1, typename coordinate_traits::coordinate_type v) +{ + if (v < i1.low()) return i1.low()-v; + else if (v > i1.high()) return v-i1.high(); + else return 0; +} +/// \return the distance between two intervals +/// \return 0 if two intervals have intersection +template +inline typename coordinate_traits::manhattan_distance_type +distance(Interval const& i1, Interval const& i2) +{ + typedef typename coordinate_traits::coordinate_type coordinate_type; + coordinate_type l = std::max(i1.low(), i2.low()); + coordinate_type h = std::min(i1.high(), i2.high()); + return (l > h)? l-h : 0; // different from computing intersection +} + +/// \return true if the value is on the boundary of the interval +template +inline bool onBoundary(Interval const& t, typename coordinate_traits::coordinate_type v) +{ + return t.low() == v || t.high() == v; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h new file mode 100644 index 00000000..a7ba360a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h @@ -0,0 +1,403 @@ +/************************************************************************* + > File Name: Iterators.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 21 Jun 2015 01:27:50 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_ITERATORS_H +#define DREAMPLACE_ITERATORS_H + +#include "PlaceDB.h" +#include "Index.h" +//#include "RowMap.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// whether T is a const type +template struct is_constant {const static bool value = false;}; + +template +struct is_constant {const static bool value = true;}; + +/// if T1 is const type, set T2 as const type +/// otherwise, still use T2 +template +struct constant_helper +{ + typedef T2& reference_type; + typedef T2* pointer_type; +}; +template +struct constant_helper +{ + typedef T2 const& reference_type; + typedef const T2* pointer_type; +}; + +template +struct IteratorDeref; + +template +struct IteratorDimension; + +/// database iterator, generic class for all 1D iterators +template +class DBIterator +{ + public: + typedef PlaceDBType placedb_type; + typedef IteratorTagType iterator_tag_type; + typedef typename placedb_type::index_type index_type; + typedef IteratorDeref iterator_deref_type; + typedef typename iterator_deref_type::reference_type reference_type; + typedef typename iterator_deref_type::pointer_type pointer_type; + + /// constructor + DBIterator(index_type id = std::numeric_limits::max(), + index_type idxl = std::numeric_limits::max(), + index_type idxh = std::numeric_limits::max(), + PlaceDBType* db = NULL) : m_index (id), m_range(idxl, idxh), m_db(db) {} + /// copy constructor + DBIterator(DBIterator const& rhs) {copy(rhs);} + template + DBIterator(DBIterator const& rhs) {copy(rhs);} + /// assignment + DBIterator& operator=(DBIterator const& rhs) {copy(rhs); return *this;} + /// destructor + virtual ~DBIterator() {} + + DBIterator& operator+=(int offset) {m_index += offset; return *this;} + DBIterator& operator-=(int offset) {return this->operator+=(-offset);} + DBIterator& operator++() {return this->operator+=(1);} + DBIterator& operator--() {return this->operator-=(1);} + DBIterator operator++(int) const + { + DBIterator tmp (*this); + return (++tmp); + } + DBIterator operator--(int) const + { + DBIterator tmp (*this); + return (--tmp); + } + DBIterator operator+(int offset) const + { + DBIterator tmp (*this); + return (tmp+=offset); + } + DBIterator operator-(int offset) const {return this->operator+(-offset);} + + bool operator==(DBIterator const& rhs) const {return this->compare(rhs);} + bool operator!=(DBIterator const& rhs) const {return !this->compare(rhs);} + + /// unsafe dereference + reference_type deref() const + { + dreamplaceAssertMsg(inRange(), "index = %u out of range [%u, %u]\n", m_index, m_range.low(), m_range.high()); + return s_deref(*m_db, m_index); + } + + reference_type operator*() const + { + return deref(); + } + pointer_type operator->() const + { + return &deref(); + } + bool inRange() const {return m_range.low() <= m_index && m_index < m_range.high();} + + Interval range() const {return m_range;} + PlaceDBType& placeDB() const {return *m_db;} + index_type index() const {return m_index;} + + protected: + template + void copy(DBIterator const& rhs) + { + // only allow conversion iterator -> const_iterator + // failed to use enable_if like specialization + // use static assertion instead + dreamplaceStaticAssert::value || !is_constant::value>("do not allow conversion from const iterator to iterator"); + m_index = rhs.index(); + m_range = rhs.range(); + m_db = &rhs.placeDB(); + } + /// \return true if indice are same or both out of range + bool compare(DBIterator const& rhs) const + { + return m_db == rhs.m_db && (m_index == rhs.m_index || (!inRange() && !rhs.inRange())); + } + + index_type m_index; ///< current index + Interval m_range; ///< range of index + PlaceDBType* m_db; + + static iterator_deref_type s_deref; ///< a static member for dereference to avoid frequent construction +}; + +template +typename DBIterator::iterator_deref_type DBIterator::s_deref; + +/// iterator for 2D data structures +/// in such kind of structures, a 2D map is used +/// the iterator traverses through 2D map and find the corresponding element +template +class DB2DIterator +{ + public: + typedef DBType db_type; + typedef IteratorTagType iterator_tag_type; + typedef typename db_type::index_type index_type; + typedef Index2D index2d_type; + typedef IteratorDeref iterator_deref_type; + typedef typename iterator_deref_type::reference_type reference_type; + typedef typename iterator_deref_type::pointer_type pointer_type; + /// get dimensions of 2D map + /// need to provide two functions to get level-1 and level-2 dimensions + typedef IteratorDimension iterator_dimension_type; + + /// constructor + DB2DIterator(index_type ix = std::numeric_limits::max(), + index_type iy = std::numeric_limits::max(), + DBType* db = NULL) : m_index(ix, iy), m_db(db) {} + /// copy constructor + DB2DIterator(DB2DIterator const& rhs) {copy(rhs);} + template + DB2DIterator(DB2DIterator const& rhs) {copy(rhs);} + /// assignment + DB2DIterator& operator=(DB2DIterator const& rhs) {copy(rhs); return *this;} + /// destructor + virtual ~DB2DIterator() {} + + /// member functions + DB2DIterator& operator++() {return this->increment();} + DB2DIterator operator++(int) const + { + DB2DIterator tmp (*this); + return (++tmp); + } + + bool operator==(DB2DIterator const& rhs) const {return this->compare(rhs);} + bool operator!=(DB2DIterator const& rhs) const {return !this->compare(rhs);} + + /// unsafe dereference + reference_type deref() const + { + dreamplaceAssertMsg(inRange(), "index = [%u, %u] out of range [%u, %u]\n", m_index.x(), m_index.y()); + return s_deref(*m_db, m_index); + } + + reference_type operator*() const + { + return deref(); + } + pointer_type operator->() const + { + return &deref(); + } + bool inRange() const + { + return m_index[kX] < s_dimension(*m_db, kX, m_index) && m_index[kY] < s_dimension(*m_db, kY, m_index); + } + + index2d_type const& index() const {return m_index;} + DBType& db() {return *m_db;} + + protected: + template + void copy(DB2DIterator const& rhs) + { + // only allow conversion iterator -> const_iterator + // failed to use enable_if like specialization + // use static assertion instead + dreamplaceStaticAssert::value || !is_constant::value>("do not allow conversion from const iterator to iterator"); + m_index = rhs.index(); + m_db = &rhs.db(); + } + /// \return true if indice are same or both out of range + bool compare(DB2DIterator const& rhs) const + { + return m_db == rhs.m_db && (m_index == rhs.m_index || (!inRange() && !rhs.inRange())); + } + /// self-increment 1 + /// do nothing if out-of-range + DB2DIterator& increment() + { + if (m_index[iterator_dimension_type::direct1] < s_dimension(*m_db, iterator_dimension_type::direct1, m_index)) + { + if (m_index[iterator_dimension_type::direct2]+1 < s_dimension(*m_db, iterator_dimension_type::direct2, m_index)) + m_index[iterator_dimension_type::direct2] += 1; + else + { + m_index[iterator_dimension_type::direct1] += 1; + m_index[iterator_dimension_type::direct2] = 0; + } + } + return *this; + } + + index2d_type m_index; ///< 2D index + DBType* m_db; ///< data + + static iterator_deref_type s_deref; ///< a static member for dereference to avoid frequent construction + static iterator_dimension_type s_dimension; ///< a static member to avoid frequent construction +}; + +template +typename DB2DIterator::iterator_deref_type DB2DIterator::s_deref; +template +typename DB2DIterator::iterator_dimension_type DB2DIterator::s_dimension; + +struct MovableNodeIteratorTag {}; +struct FixedNodeIteratorTag {}; +//struct PlaceBlockageIteratorTag {}; +//struct NonCoreNodeIteratorTag {}; +//struct IOPinNodeIteratorTag {}; +//struct CellMacroIteratorTag {}; +//struct IOPinMacroIteratorTag {}; +//struct SubRowMap2DIteratorTag {}; + +typedef DBIterator MovableNodeIterator; +typedef DBIterator FixedNodeIterator; +//typedef DBIterator PlaceBlockageIterator; +//typedef DBIterator NonCoreNodeIterator; +//typedef DBIterator IOPinNodeIterator; +//typedef DBIterator CellMacroIterator; +//typedef DBIterator IOPinMacroIterator; +//typedef DB2DIterator SubRowMap2DIterator; + +typedef DBIterator MovableNodeConstIterator; +typedef DBIterator FixedNodeConstIterator; +//typedef DBIterator PlaceBlockageConstIterator; +//typedef DBIterator NonCoreNodeConstIterator; +//typedef DBIterator IOPinNodeConstIterator; +//typedef DBIterator CellMacroConstIterator; +//typedef DBIterator IOPinMacroConstIterator; +//typedef DB2DIterator SubRowMap2DConstIterator; + +/// specialization for different iterators +template +struct IteratorDeref +{ + typedef typename constant_helper::reference_type reference_type; + typedef typename constant_helper::pointer_type pointer_type; + typedef DBIterator iterator_type; + typedef typename iterator_type::placedb_type placedb_type; + typedef typename iterator_type::index_type index_type; + inline reference_type operator()(placedb_type& db, index_type index) const + { + return db.nodes().at(db.movableNodeIndices().at(index)); + } +}; +template +struct IteratorDeref +{ + typedef typename constant_helper::reference_type reference_type; + typedef typename constant_helper::pointer_type pointer_type; + typedef DBIterator iterator_type; + typedef typename iterator_type::placedb_type placedb_type; + typedef typename iterator_type::index_type index_type; + inline reference_type operator()(placedb_type& db, index_type index) const + { + return db.nodes().at(db.fixedNodeIndices().at(index)); + } +}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DBIterator iterator_type; +// typedef typename iterator_type::placedb_type placedb_type; +// typedef typename iterator_type::index_type index_type; +// inline reference_type operator()(placedb_type& db, index_type index) const +// { +// return db.nodes().at(db.placeBlockageIndices().at(index)); +// } +//}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DBIterator iterator_type; +// typedef typename iterator_type::placedb_type placedb_type; +// typedef typename iterator_type::index_type index_type; +// inline reference_type operator()(placedb_type& db, index_type index) const +// { +// return db.nodes().at(db.nonCoreNodeIndices().at(index)); +// } +//}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DBIterator iterator_type; +// typedef typename iterator_type::placedb_type placedb_type; +// typedef typename iterator_type::index_type index_type; +// inline reference_type operator()(placedb_type& db, index_type index) const +// { +// return db.nodes().at(index); +// } +//}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DBIterator iterator_type; +// typedef typename iterator_type::placedb_type placedb_type; +// typedef typename iterator_type::index_type index_type; +// inline reference_type operator()(placedb_type& db, index_type index) const +// { +// return db.macros().at(index); +// } +//}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DBIterator iterator_type; +// typedef typename iterator_type::placedb_type placedb_type; +// typedef typename iterator_type::index_type index_type; +// inline reference_type operator()(placedb_type& db, index_type index) const +// { +// return db.macros().at(index); +// } +//}; +//template +//struct IteratorDeref +//{ +// typedef typename constant_helper::reference_type reference_type; +// typedef typename constant_helper::pointer_type pointer_type; +// typedef DB2DIterator iterator_type; +// typedef typename iterator_type::db_type db_type; +// typedef typename iterator_type::index2d_type index2d_type; +// inline reference_type operator()(db_type& db, index2d_type const& index) const +// { +// return db.getSubRow(index[kY], index[kX]); +// } +//}; +// +//template +//struct IteratorDimension +//{ +// typedef DB2DIterator iterator_type; +// typedef typename iterator_type::db_type db_type; +// typedef typename iterator_type::index_type index_type; +// typedef typename iterator_type::index2d_type index2d_type; +// const static Direction1DType direct1 = kY; +// const static Direction1DType direct2 = kX; +// inline index_type operator()(db_type& db, Direction1DType d, index2d_type const& index) const +// { +// return (d == kY)? db.getNumRows() : db.getSubRowsByRowIndex(index[kY]).size(); +// } +//}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h new file mode 100644 index 00000000..03fa2297 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h @@ -0,0 +1,171 @@ +/************************************************************************* + > File Name: LibCell.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_MACRO_H +#define DREAMPLACE_MACRO_H + +#include +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +//class Macro : public Box, public Object +class LibCell : public Object +{ + public: + typedef Object base_type; + typedef coordinate_traits::index_type index_type; + typedef std::map string2index_map_type; + + /// default constructor + LibCell(); + explicit LibCell(const std::string &name) : m_name(name) {} + /// copy constructor + LibCell(LibCell const& rhs); + /// assignment + LibCell& operator=(LibCell const& rhs); + + string2index_map_type const& libCellPinName2Type() const {return m_mPinName2Type;} + string2index_map_type& libCellPinName2Type() {return m_mPinName2Type;} + + int pinType(std::string const& s) const + { + string2index_map_type::const_iterator found = m_mPinName2Type.find(s); + //return (found != m_mPinName2Type.end())? found->second : std::numeric_limits::max(); + return (found != m_mPinName2Type.end())? found->second : -1; + } + + void addInputPin(std::string& s) + { + m_inputPins.push_back(s); + m_mPinName2Type.insert(std::make_pair(s, 1)); + } + void addOutputPin(std::string& s) + { + m_outputPins.push_back(s); + m_mPinName2Type.insert(std::make_pair(s, 0)); + } + void addClkPin(std::string& s) + { + m_clkPins.push_back(s); + m_mPinName2Type.insert(std::make_pair(s, 2)); + } + void addCtrlPin(std::string& s) + { + m_ctrlPins.push_back(s); + m_mPinName2Type.insert(std::make_pair(s, 3)); + } + //When LUT and adder are combined together as single cell - need to differentiate the signals + void addInputAddPin(std::string& s) + { + m_inputPins.push_back(s); + if (s == "cin0" || s.find("cin") != std::string::npos) + { + m_mPinName2Type.insert(std::make_pair(s, 31)); + } else if (s == "sharein0" || s.find("sharein") != std::string::npos) + { + m_mPinName2Type.insert(std::make_pair(s, 41)); + } else + { + m_mPinName2Type.insert(std::make_pair(s, 21)); + } + } + void addOutputAddPin(std::string& s) + { + m_outputPins.push_back(s); + if (s == "cout0" || s.find("cout") != std::string::npos) + { + m_mPinName2Type.insert(std::make_pair(s, 30)); + } else if (s == "shareout0" || s.find("shareout") != std::string::npos) + { + m_mPinName2Type.insert(std::make_pair(s, 40)); + } else + { + m_mPinName2Type.insert(std::make_pair(s, 20)); + } + } + + // Getters + const std::string & name() const { return m_name; } + index_type id() const { return m_id; } + + const std::vector & inputPinArray() const { return m_inputPins; } + std::vector & inputPinArray() { return m_inputPins; } + const std::string& inputPin(index_type i) const { return m_inputPins.at(i); } + index_type numInputPins() const { return m_inputPins.size(); } + + const std::vector & outputPinArray() const { return m_outputPins; } + std::vector & outputPinArray() { return m_outputPins; } + const std::string& outputPin(index_type i) const { return m_outputPins.at(i); } + index_type numOutputPins() const { return m_outputPins.size(); } + + const std::vector & clkPinArray() const { return m_clkPins; } + std::vector & clkPinArray() { return m_clkPins; } + const std::string& clkPin(index_type i) const { return m_clkPins.at(i); } + index_type numClkPins() const { return m_clkPins.size(); } + + const std::vector & ctrlPinArray() const { return m_ctrlPins; } + std::vector & ctrlPinArray() { return m_ctrlPins; } + const std::string& ctrlPin(index_type i) const { return m_ctrlPins.at(i); } + index_type numCtrlPins() const { return m_ctrlPins.size(); } + + // Setters + void setId(index_type id) { m_id = id; } + + + protected: + void copy(LibCell const& rhs); + + std::string m_name; ///< LibCell name + index_type m_id; + + std::vector m_inputPins; ///< standard cell pins + std::vector m_outputPins; ///< standard cell pins + std::vector m_clkPins; ///< standard cell pins + std::vector m_ctrlPins; ///< standard cell pins + string2index_map_type m_mPinName2Type; ///< map names of standard cell pins to type +}; + +inline LibCell::LibCell() + : LibCell::base_type() + , m_name("") + , m_id(std::numeric_limits::max()) + , m_inputPins() + , m_outputPins() + , m_clkPins() + , m_ctrlPins() + , m_mPinName2Type() +{ +} +inline LibCell::LibCell(LibCell const& rhs) + : LibCell::base_type(rhs) +{ + copy(rhs); +} +inline LibCell& LibCell::operator=(LibCell const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void LibCell::copy(LibCell const& rhs) +{ + m_name = rhs.m_name; + m_id = rhs.m_id; + m_inputPins = rhs.m_inputPins; + m_outputPins = rhs.m_outputPins; + m_clkPins = rhs.m_clkPins; + m_ctrlPins = rhs.m_ctrlPins; + m_mPinName2Type = rhs.m_mPinName2Type; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h new file mode 100644 index 00000000..92c5783b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h @@ -0,0 +1,160 @@ +/************************************************************************* + > File Name: Macro.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Mon Jun 15 22:57:05 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_MACRO_H +#define DREAMPLACE_MACRO_H + +#include +#include +#include +#include "MacroPin.h" +#include "MacroObs.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class Macro : public Box, public Object +{ + public: + typedef Object base_type2; + typedef base_type2::coordinate_type coordinate_type; + typedef Box base_type1; + typedef coordinate_traits::index_type index_type; + typedef Point point_type; + typedef std::map string2index_map_type; + + /// default constructor + Macro(); + /// copy constructor + Macro(Macro const& rhs); + /// assignment + Macro& operator=(Macro const& rhs); + + /// member functions + std::string const& name() const {return m_name;} + Macro& setName(std::string const& s) {m_name = s; return *this;} + + std::string const& className() const {return m_className;} + Macro& setClassName(std::string const& s) {m_className = s; return *this;} + + std::string const& siteName() const {return m_siteName;} + Macro& setSiteName(std::string const& s) {m_siteName = s; return *this;} + + std::string const& edgeName(Direction1DType d) const {return m_edgeName[d];} + Macro& setEdgeName(Direction1DType d, std::string const& s) {m_edgeName[d] = s; return *this;} + Macro& setEdgeName(std::string const& sl, std::string const& sr) {m_edgeName[kLEFT] = sl; m_edgeName[kRIGHT] = sr; return *this;} + + unsigned char symmetry() const {return m_symmetry;} + Macro& setSymmetry(unsigned char s) {m_symmetry = s; return *this;} + + point_type const& initOrigin() const {return m_initOrigin;} + Macro& setInitOrigin(point_type const& p) {m_initOrigin = p; return *this;} + Macro& setInitOrigin(coordinate_type x, coordinate_type y) {m_initOrigin.set(x, y); return *this;} + + MacroObs const& obs() const {return m_obs;} + MacroObs& obs() {return m_obs;} + + std::vector const& macroPins() const {return m_vMacroPin;} + std::vector& macroPins() {return m_vMacroPin;} + + string2index_map_type const& macroPinName2Index() const {return m_mMacroPinName2Index;} + string2index_map_type& macroPinName2Index() {return m_mMacroPinName2Index;} + + /// \return macro pin with given index + MacroPin const& macroPin(index_type id) const {return m_vMacroPin.at(id);} + MacroPin& macroPin(index_type id) {return m_vMacroPin.at(id);} + + /// \return macro pin index with given name + index_type macroPinIndex(std::string const& s) const + { + string2index_map_type::const_iterator found = m_mMacroPinName2Index.find(s); + return (found != m_mMacroPinName2Index.end())? found->second : std::numeric_limits::max(); + } + + /// add a macro pin to m_vMacroPin and insert its index to m_mMacroPinName2Index + /// \return true if succeed, false if already exists + /// the first value of return pair is the index in m_vMacroPin + /// \param n denotes name of the pin + std::pair addMacroPin(std::string const& n); + protected: + void copy(Macro const& rhs); + + std::string m_name; ///< macro name + std::string m_className; ///< class name, usually not useful + std::string m_siteName; ///< site name, usually not useful + std::string m_edgeName[2]; ///< edge name (left/right) + unsigned char m_symmetry; ///< 3-bit: x, y, R90 + point_type m_initOrigin; ///< initial origin in LEF file, the actual origins are adjusted to (0, 0) after read-in + + MacroObs m_obs; ///< obstructions + + std::vector m_vMacroPin; ///< standard cell pins + string2index_map_type m_mMacroPinName2Index; ///< map names of standard cell pins to index +}; + +inline Macro::Macro() + : Macro::base_type1() + , Macro::base_type2() + , m_name("") + , m_className("") + , m_siteName("") + , m_edgeName() + , m_symmetry(std::numeric_limits::max()) + , m_initOrigin() + , m_obs() + , m_vMacroPin() + , m_mMacroPinName2Index() +{ +} +inline Macro::Macro(Macro const& rhs) + : Macro::base_type1(rhs) + , Macro::base_type2(rhs) +{ + copy(rhs); +} +inline Macro& Macro::operator=(Macro const& rhs) +{ + if (this != &rhs) + { + this->base_type1::operator=(rhs); + this->base_type2::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void Macro::copy(Macro const& rhs) +{ + m_name = rhs.m_name; + m_className = rhs.m_className; + m_siteName = rhs.m_siteName; + m_edgeName[0] = rhs.m_edgeName[0]; + m_edgeName[1] = rhs.m_edgeName[1]; + m_symmetry = rhs.m_symmetry; + m_initOrigin = rhs.m_initOrigin; + m_obs = rhs.m_obs; + m_vMacroPin = rhs.m_vMacroPin; + m_mMacroPinName2Index = rhs.m_mMacroPinName2Index; +} +inline std::pair Macro::addMacroPin(std::string const& n) +{ + string2index_map_type::iterator found = m_mMacroPinName2Index.find(n); + if (found != m_mMacroPinName2Index.end()) // already exist + return std::make_pair(found->second, false); + else // not exist, create macro pin + { + m_vMacroPin.push_back(MacroPin()); + MacroPin& mp = m_vMacroPin.back(); + mp.setName(n); + mp.setId(m_vMacroPin.size()-1); + std::pair insertRet = m_mMacroPinName2Index.insert(std::make_pair(mp.name(), mp.id())); + dreamplaceAssertMsg(insertRet.second, "failed to insert macro pin (%s, %d).(%s, %d)", name().c_str(), id(), mp.name().c_str(), mp.id()); + return std::make_pair(mp.id(), true); + } +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h new file mode 100644 index 00000000..0a0a0c3f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h @@ -0,0 +1,102 @@ +/************************************************************************* + > File Name: MacroObs.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Wed 22 Jul 2015 11:26:26 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_MACROOBS_H +#define DREAMPLACE_MACROOBS_H + +#include +#include +#include +#include "Box.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class MacroObs : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef Box box_type; + typedef std::map > obs_map_type; + typedef obs_map_type::iterator ObsIterator; + typedef obs_map_type::const_iterator ObsConstIterator; + + /// default constructor + MacroObs(); + /// copy constructor + MacroObs(MacroObs const& rhs); + /// assignment + MacroObs& operator=(MacroObs const& rhs); + + /// member functions + obs_map_type const& obsMap() const {return m_mObs;} + obs_map_type& obsMap() {return m_mObs;} + MacroObs& add(std::string const& layerName, box_type const& box); + template + MacroObs& add(std::string const& layerName, Iterator first, Iterator last); + + bool empty() const {return m_mObs.empty();} + ObsIterator begin() {return m_mObs.begin();} + ObsIterator end() {return m_mObs.end();} + ObsConstIterator begin() const {return m_mObs.begin();} + ObsConstIterator end() const {return m_mObs.end();} + + protected: + void copy(MacroObs const& rhs); + + obs_map_type m_mObs; ///< map of obstructions, layer -> rectangles +}; + +inline MacroObs::MacroObs() + : MacroObs::base_type() + , m_mObs() +{ +} +inline MacroObs::MacroObs(MacroObs const& rhs) + : MacroObs::base_type(rhs) +{ + copy(rhs); +} +inline MacroObs& MacroObs::operator=(MacroObs const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void MacroObs::copy(MacroObs const& rhs) +{ + m_mObs = rhs.m_mObs; +} +inline MacroObs& MacroObs::add(std::string const& layerName, MacroObs::box_type const& box) +{ + obs_map_type::iterator found = m_mObs.find(layerName); + if (found == m_mObs.end()) + m_mObs.insert(std::make_pair(layerName, std::vector(1, box))); + else + found->second.push_back(box); + return *this; +} +template +inline MacroObs& MacroObs::add(std::string const& layerName, Iterator first, Iterator last) +{ + obs_map_type::iterator found = m_mObs.find(layerName); + if (found == m_mObs.end()) + m_mObs.insert(std::make_pair(layerName, std::vector(first, last))); + else + found->second.insert(found->second.end(), first, last); + return *this; +} + +typedef MacroObs::ObsIterator ObsIterator; +typedef MacroObs::ObsConstIterator ObsConstIterator; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h new file mode 100644 index 00000000..df0c57b3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h @@ -0,0 +1,205 @@ +/************************************************************************* + > File Name: MacroPin.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Tue Jun 16 21:08:59 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_MACROPIN_H +#define DREAMPLACE_MACROPIN_H + +#include +#include +#include "Object.h" +#include "Box.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// class MacroPort describes the ports in a macro pin +/// it consists of rectangles and polygons +class MacroPort : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef Box box_type; + + /// default constructor + MacroPort(); + /// copy constructor + MacroPort(MacroPort const& rhs); + /// assignment + MacroPort& operator=(MacroPort const& rhs); + + box_type const& bbox() const {return m_bbox;} + MacroPort& setBbox(box_type const& b) {m_bbox = b; return *this;} + + std::vector const& boxes() const {return m_vBox;} + std::vector& boxes() {return m_vBox;} + + std::vector const& layers() const {return m_vLayer;} + std::vector& layers() {return m_vLayer;} + protected: + void copy(MacroPort const& rhs); + + box_type m_bbox; ///< bounding box of port + std::vector m_vBox; ///< decomposed rectangles from the polygon of port + std::vector m_vLayer; ///< layers, the same number as boxes +}; + +inline MacroPort::MacroPort() + : MacroPort::base_type() + , m_bbox() + , m_vBox() + , m_vLayer() +{ +} +inline MacroPort::MacroPort(MacroPort const& rhs) + : MacroPort::base_type(rhs) +{ + copy(rhs); +} +inline MacroPort& MacroPort::operator=(MacroPort const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void MacroPort::copy(MacroPort const& rhs) +{ + m_bbox = rhs.m_bbox; + m_vBox = rhs.m_vBox; + m_vLayer = rhs.m_vLayer; +} + +/// class MacroPin describes the pins of a standard cell +/// it contains detailed physical information such as name, shape, and direction +/// since a pin is usually a rectilinear polygon, the decomposed rectangles are introduced to store its shape +class MacroPin : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef coordinate_traits::index_type index_type; + typedef Box box_type; + + /// default constructor + MacroPin(); + /// copy constructor + MacroPin(MacroPin const& rhs); + /// assignment + MacroPin& operator=(MacroPin const& rhs); + + /// member functions + std::string const& name() const {return m_name;} + MacroPin& setName(std::string const& s) {m_name = s; return *this;} + + SignalDirect const& direct() const {return m_direct;} + MacroPin& setDirect(SignalDirect const& d) {m_direct = d; return *this;} + + box_type const& bbox() const {return m_bbox;} + MacroPin& setBbox(box_type const& b) {m_bbox = b; return *this;} + + std::vector const& macroPorts() const {return m_vMacroPort;} + std::vector& macroPorts() {return m_vMacroPort;} + + MacroPort const& macroPort(index_type id) const {return m_vMacroPort.at(id);} + MacroPort& macroPort(index_type id) {return m_vMacroPort.at(id);} + + /// add macro port and set index + index_type addMacroPort(); + protected: + void copy(MacroPin const& rhs); + + std::string m_name; ///< pin name + SignalDirect m_direct; ///< signal direction of pin + box_type m_bbox; ///< bounding box of pin + std::vector m_vMacroPort; ///< ports in a pin +}; + +inline MacroPin::MacroPin() + : MacroPin::base_type() + , m_name("") + , m_direct() + , m_bbox() + , m_vMacroPort() +{ +} +inline MacroPin::MacroPin(MacroPin const& rhs) + : MacroPin::base_type(rhs) +{ + copy(rhs); +} +inline MacroPin& MacroPin::operator=(MacroPin const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void MacroPin::copy(MacroPin const& rhs) +{ + m_name = rhs.m_name; + m_direct = rhs.m_direct; + m_bbox = rhs.m_bbox; + m_vMacroPort = rhs.m_vMacroPort; +} + +inline MacroPin::index_type MacroPin::addMacroPort() +{ + m_vMacroPort.push_back(MacroPort()); + MacroPort& mp = m_vMacroPort.back(); + mp.setId(m_vMacroPort.size()-1); + + return mp.id(); +} + +///==== helper functions ==== +/// compute bounding box from a port shape +inline void deriveMacroPortBbox(MacroPort& mp) +{ + typedef MacroPort::coordinate_type coordinate_type; + typedef MacroPort::box_type box_type; + // construct an invalid box + box_type box ( + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() + ); + // compute bounding box + std::vector const& vBox = mp.boxes(); + for (std::vector::const_iterator it = vBox.begin(), ite = vBox.end(); it != ite; ++it) + box.encompass(*it); + // update bounding box + mp.setBbox(box); +} +/// compute bounding box from pin shape +inline void deriveMacroPinBbox(MacroPin& mp) +{ + // assume bounding box in macro port is update-to-date + typedef MacroPin::coordinate_type coordinate_type; + typedef MacroPin::box_type box_type; + // construct an invalid box + box_type box ( + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() + ); + // compute bounding box + std::vector const& vMacroPort = mp.macroPorts(); + for (std::vector::const_iterator it = vMacroPort.begin(), ite = vMacroPort.end(); it != ite; ++it) + box.encompass(it->bbox()); + // update bounding box + mp.setBbox(box); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp new file mode 100644 index 00000000..eddf9023 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp @@ -0,0 +1,43 @@ +/************************************************************************* + > File Name: Net.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "Net.h" + +DREAMPLACE_BEGIN_NAMESPACE + +Net::Net() + : Net::base_type() + , m_name("") + , m_id(std::numeric_limits::max()) + , m_weight(1) + , m_vPinId() +{ +} +Net::Net(Net const& rhs) + : Net::base_type(rhs) +{ + copy(rhs); +} +Net& Net::operator=(Net const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +void Net::copy(Net const& rhs) +{ + m_name = rhs.m_name; + m_id = rhs.m_id; + m_weight = rhs.m_weight; + m_vPinId = rhs.m_vPinId; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h new file mode 100644 index 00000000..43e73c66 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h @@ -0,0 +1,62 @@ +/************************************************************************* + > File Name: Net.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_NET_H +#define DREAMPLACE_NET_H + +#include +#include "Pin.h" +#include "Box.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class Net : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef coordinate_traits::index_type index_type; + typedef coordinate_traits::weight_type weight_type; + + /// default constructor + Net(); + explicit Net(const std::string &name) : m_name(name) {} + /// copy constructor + Net(Net const& rhs); + /// assignment + Net& operator=(Net const& rhs); + + + void addPin(index_type pinId) { m_vPinId.push_back(pinId); } + + // Getters + const std::string & name() const { return m_name; } + index_type id() const { return m_id; } + weight_type weight() const { return m_weight; } + + const std::vector & pinIdArray() const { return m_vPinId; } + std::vector & pinIdArray() { return m_vPinId; } + index_type pinId(index_type i) const { return m_vPinId.at(i); } + index_type numPins() const { return m_vPinId.size(); } + + // Setters + void setId(index_type id) { m_id = id; } + void setWeight(weight_type w) { m_weight = w; } + + protected: + void copy(Net const& rhs); + + std::string m_name; + index_type m_id; + weight_type m_weight; ///< weight of net + std::vector m_vPinId; ///< index of pins, the first one is source +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp new file mode 100644 index 00000000..43ef47c1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp @@ -0,0 +1,57 @@ +/************************************************************************* + > File Name: Node.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "Node.h" + +DREAMPLACE_BEGIN_NAMESPACE + +Node::Node() + : Node::base_type() + , m_name("") + , m_typeName("") + , m_id(std::numeric_limits::max()) + , m_libCellId(std::numeric_limits::max()) + , m_type(std::numeric_limits::max()) + , m_cType(InstBlkEnum::UNKNOWN) + , m_x(0.0) + , m_y(0.0) + , m_z(0) + , m_fixed(false) + , m_vPinId() +{ +} +Node::Node(Node const& rhs) + : Node::base_type(rhs) +{ + copy(rhs); +} +Node& Node::operator=(Node const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +void Node::copy(Node const& rhs) +{ + m_name = rhs.m_name; + m_typeName = rhs.m_typeName; + m_id = rhs.m_id; + m_libCellId = rhs.m_libCellId; + m_type = rhs.m_type; + m_cType = rhs.m_cType; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_z = rhs.m_z; + m_fixed = rhs.m_fixed; + m_vPinId = rhs.m_vPinId; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h new file mode 100644 index 00000000..4aa3a6de --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h @@ -0,0 +1,88 @@ +/************************************************************************* + > File Name: Node.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_NODE_H +#define DREAMPLACE_NODE_H + +#include +#include "Pin.h" +#include "Enums.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// class Node denotes an instantiation of a standard cell +class Node : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef coordinate_traits::index_type index_type; + + /// default constructor + Node(); + explicit Node(const std::string &name) : m_name(name) {} + explicit Node(const std::string &name, const std::string &type) : m_name(name), m_typeName(type) {} + /// copy constructor + Node(Node const& rhs); + /// assignment + Node& operator=(Node const& rhs); + + InstBlkEnum::InstBlkType cellType() const {return (InstBlkEnum::InstBlkType)m_cType;} + void setCellType(InstBlkEnum::InstBlkType s) {m_cType = s; m_type = (index_type)s;} + void setCellType(InstBlk const& s) {m_cType = s; m_type = (index_type)s;} + + void addPin(index_type pinId) { m_vPinId.push_back(pinId); } + + // Getters + const std::string & name() const { return m_name; } + index_type id() const { return m_id; } + index_type typeId() const { return m_type; } + index_type macroId() const { return m_libCellId; } + + const std::string & typeName() const { return m_typeName; } + float x() const { return m_x; } + float y() const { return m_y; } + index_type z() const { return m_z; } + bool fixed() const { return (m_type > 8) ? true : false; } + const std::vector & pinIdArray() const { return m_vPinId; } + std::vector & pinIdArray() { return m_vPinId; } + index_type pinId(index_type i) const { return m_vPinId.at(i); } + index_type numPins() const { return m_vPinId.size(); } + + // Setters + void setId(index_type id) { m_id = id; } + void setLibCellId(index_type lId) { m_libCellId = lId; } + void setX(float x) { m_x = x; } + void setY(float y) { m_y = y; } + void setZ(index_type z) { m_z = z; } + void setFixed(bool b) { m_fixed = b; } + + bool isLUT6() const { return (m_type == 5) ? true : false; } + bool isLUT() const { return (m_type < 6) ? true : false; } + bool isFF() const { return (m_type == 6) ? true : false; } + bool isDSP() const { return (m_type == 7) ? true : false; } + bool isRAM() const { return (m_type == 8) ? true : false; } + + + protected: + void copy(Node const& rhs); + + std::string m_name; + std::string m_typeName; + index_type m_id, m_libCellId, m_type; + char m_cType; + float m_x, m_y; + index_type m_z; + bool m_fixed; + std::vector m_vPinId; ///< index of pins + +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h new file mode 100644 index 00000000..36dcc287 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h @@ -0,0 +1,47 @@ +/************************************************************************* + > File Name: Object.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Jun 2015 04:07:46 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_OBJECT_H +#define DREAMPLACE_OBJECT_H + +#include +#include "Util.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// base class for all objects +class Object +{ + public: + typedef int coordinate_type; + typedef coordinate_traits::index_type index_type; + + /// default constructor + Object() : m_id(std::numeric_limits::max()) {} + /// copy constructor + Object(Object const& rhs) : m_id(rhs.m_id) {} + /// assignment + Object& operator=(Object const& rhs) + { + m_id = rhs.m_id; + return *this; + } + /// destructor + ~Object() {} + + index_type id() const {return m_id;} + void setId(index_type i) {m_id = i;} + + std::string toString() const {return limbo::to_string(m_id);} + + protected: + index_type m_id; ///< index of object +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp new file mode 100644 index 00000000..c6710fe0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp @@ -0,0 +1,29 @@ +/************************************************************************* + > File Name: Params.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "Params.h" +#include "Util.h" +#include +#include +#include +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +std::string toString(SolutionFileFormat ff) +{ + switch (ff) + { + case BOOKSHELF: return "BOOKSHELF"; + case BOOKSHELFALL: return "BOOKSHELFALL"; + default: return "UNKNOWN"; + } +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h new file mode 100644 index 00000000..a5093aeb --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h @@ -0,0 +1,33 @@ +/************************************************************************* + > File Name: Params.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_PARAMS_H +#define DREAMPLACE_PARAMS_H + +#include +#include +#include +#include + +#include "Enums.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// placement solution format +enum SolutionFileFormat +{ + BOOKSHELF, // write placement solution .plx in bookshlef format + BOOKSHELFALL // write .nodes, .nets, ... in bookshlef format +}; + +/// convert enums to string +extern std::string toString(SolutionFileFormat ff); + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h new file mode 100644 index 00000000..30570788 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h @@ -0,0 +1,85 @@ +/************************************************************************* + > File Name: Pin.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_PIN_H +#define DREAMPLACE_PIN_H + +#include +#include "Object.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class Pin : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef coordinate_traits::index_type index_type; + + /// default constructor + Pin(); + explicit Pin(const std::string &name) : m_name(name) {} + /// copy constructor + Pin(Pin const& rhs); + /// assignment + Pin& operator=(Pin const& rhs); + + // Getters + const std::string & name() const { return m_name; } + index_type id() const { return m_id; } + index_type nodeId() const { return m_nodeId; } + index_type netId() const { return m_netId; } + + // Setters + void setId(index_type id) { m_id = id; } + void setNodeId(index_type nodeId) { m_nodeId = nodeId; } + void setNetId(index_type netId) { m_netId = netId; } + + protected: + void copy(Pin const& rhs); + + std::string m_name; ///< index to the macro pin list of corresponding macro + index_type m_id; ///< index to the macro pin list of corresponding macro + index_type m_nodeId; ///< corresponding node + index_type m_netId; ///< corresponding net +}; + +inline Pin::Pin() + : Pin::base_type() + , m_name("") + , m_id(std::numeric_limits::max()) + , m_nodeId(std::numeric_limits::max()) + , m_netId(std::numeric_limits::max()) +{ +} +inline Pin::Pin(Pin const& rhs) + : Pin::base_type(rhs) +{ + copy(rhs); +} +inline Pin& Pin::operator=(Pin const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void Pin::copy(Pin const& rhs) +{ + m_name = rhs.m_name; + m_id = rhs.m_id; + m_nodeId = rhs.m_nodeId; + m_netId = rhs.m_netId; +} + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp new file mode 100644 index 00000000..79766dc8 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp @@ -0,0 +1,1235 @@ +/************************************************************************* + > File Name: PlaceDB.cpp + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#include "PlaceDB.h" +#include +#include +#include +#include +#include "BookshelfWriter.h" +#include "Iterators.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +int get_last_digit_from_string(const std::string &val) +{ + //Conversion of char to int + return (int)val.back()-48; +} + +/// default constructor +PlaceDB::PlaceDB() { + num_movable_nodes = 0; + num_fixed_nodes = 0; + m_numLibCell = 0; + m_numLUT = 0; + m_numFF = 0; + m_numCCs = 0; + wlXWeight = 0.0; + wlYWeight = 0.0; + pinRouteCap = 0; + routeCapH = 0; + routeCapV = 0; +} + +void PlaceDB::add_bookshelf_node(std::string& name, std::string& type) +{ + ////DBG + //std::cout << " add bookshelf node " << name << " of type " << type << std::endl; + ////DBG + index_type rsrcType(rsrcTypes.size()+1); + std::string rsrcName; + + string2string_map_type::iterator fnd = inst2RsrcMap.find(type); + if (fnd == inst2RsrcMap.end()) + { + dreamplacePrint(kWARN, "Unknown instance type not found in .scl file: %s, %s\n", + type.c_str()); + } else + { + rsrcName = inst2RsrcMap[type]; + rsrcType = rsrcType2IndexMap[rsrcName]; + } + + if (rsrcType < rsrcTypes.size()) + { + if (node_names.size() == 0 && fixed_node_names.size() == 0) + { + nodeCount.resize(rsrcTypes.size()); + } + ++nodeCount[rsrcType]; + + if (rsrcType == ioId || rsrcType == pllId) + { + ////DBG + //std::cout << "Added fixed bookshelf node " << name << " of type " << type + // << " with fixed nodeId: " << fixed_node_names.size() << std::endl; + ////DBG + fixed_node_name2id_map.insert(std::make_pair(name, fixed_node_names.size())); + fixed_node_names.emplace_back(name); + fixed_node_types.emplace_back(type); + fixed_node2fence_region_map.emplace_back(rsrcType); + fixed_node_x.emplace_back(0.0); + fixed_node_y.emplace_back(0.0); + fixed_node_z.emplace_back(0); + ++num_fixed_nodes; + + double width(0.0), height(0.0); + + string2index_map_type::iterator found = rsrcInstType2IndexMap.find(type); + if (found != rsrcInstType2IndexMap.end()) + { + width = rsrcInstWidth[rsrcInstType2IndexMap[type]]; + height = rsrcInstHeight[rsrcInstType2IndexMap[type]]; + } else + { + width = rsrcInstWidth[rsrcType2IndexMap[rsrcName]]; + height = rsrcInstHeight[rsrcType2IndexMap[rsrcName]]; + } + + fixed_node_size_x.emplace_back(width); + fixed_node_size_y.emplace_back(height); + fixed_lut_type.emplace_back(0); + fixed_cluster_lut_type.emplace_back(0); + + } else + { + ////DBG + //std::cout << "Added movable bookshelf node " << name << " of type " << type << " with nodeId: " << node_names.size() << std::endl; + ////DBG + node_name2id_map.insert(std::make_pair(name, node_names.size())); + node_names.emplace_back(name); + node_types.emplace_back(type); + node2fence_region_map.emplace_back(rsrcType); + node_x.emplace_back(0.0); + node_y.emplace_back(0.0); + node_z.emplace_back(0); + ++num_movable_nodes; + + double width(0.0), height(0.0); + + string2index_map_type::iterator found = rsrcInstType2IndexMap.find(type); + if (found != rsrcInstType2IndexMap.end()) + { + width = rsrcInstWidth[rsrcInstType2IndexMap[type]]; + height = rsrcInstHeight[rsrcInstType2IndexMap[type]]; + } else //FF, CARRY8 + { + width = rsrcInstWidth[rsrcType2IndexMap[rsrcName]]; + height = rsrcInstHeight[rsrcType2IndexMap[rsrcName]]; + } + + if (rsrcType == lutId) + { + node_size_x.emplace_back(std::sqrt(width)); + node_size_y.emplace_back(std::sqrt(height)); + int val = get_last_digit_from_string(type); + lut_type.emplace_back(val); + //std::cout << "Instance: " << name << " of type: " << type << " has lut type: " << val; + --val; + val = std::max(0, val); + cluster_lut_type.emplace_back(val); + lut_indices.emplace_back(node_names.size()-1); + //std::cout << " and cluster lut type: " << val << std::endl; + ++m_numLUT; + } + else if (rsrcType == ffId) + { + node_size_x.emplace_back(std::sqrt(width)); + node_size_y.emplace_back(std::sqrt(height)); + lut_type.emplace_back(0); + cluster_lut_type.emplace_back(0); + flop_indices.emplace_back(node_names.size()-1); + ++m_numFF; + } else + { + node_size_x.emplace_back(width); + node_size_y.emplace_back(height); + lut_type.emplace_back(0); + cluster_lut_type.emplace_back(0); + } + } + + } else + { + dreamplacePrint(kWARN, "Unknown type component found in .nodes file: %s, %s of type: %s with id: %d\n", + name.c_str(), type.c_str(), rsrcName.c_str(), rsrcType); + } + + std::vector temp; + node2pin_map.emplace_back(temp); + node2pincount_map.emplace_back(0); + is_cc_node.emplace_back(0); + node2ccId_map.emplace_back(-1); + org_node2ccIndex_map.emplace_back(-1); + ////DBG + //std::cout << "Added bookshelf node " << name << " of type " << type << std::endl; + ////DBG +} + +void PlaceDB::update_nodes() { + index_type num_nodes = 0; + //Re-arrange all nodes to accomodate carry chains + if (m_numCCs > 0) + { + ////DBG + //std::cout << " Update nodes before reading design net information" << std::endl; + ////DBG + + num_nodes = node_names.size()+fixed_node_names.size(); + + //Carry Chains are movable nodes + org_num_movable_nodes = num_movable_nodes; + org_node_name2id_map = node_name2id_map; + org_node_size_x = node_size_x; + org_node_size_y = node_size_y; + org_node2fence_region_map = node2fence_region_map; + org_node_names = node_names; + org_node_types = node_types; + org_nodeCount = nodeCount; + org_is_cc_node = is_cc_node; + org_node2ccId_map = node2ccId_map; + org_flop_indices = flop_indices; + org_lut_type = lut_type; + org_node2pincount_map = node2pincount_map; + org_node2pin_map = node2pin_map; + + org_node_x = node_x; + org_node_y = node_y; + org_node_z = node_z; + index_type numFF = m_numFF; + index_type numLUT = m_numLUT; + std::vector temp_cluster_lut_type = cluster_lut_type; + + node_name2id_map.clear(); + lut_type.clear(); + cluster_lut_type.clear(); + node_size_x.clear(); + node_size_y.clear(); + node_names.clear(); + node_types.clear(); + node_x.clear(); + node_y.clear(); + node_z.clear(); + node2fence_region_map.clear(); + is_cc_node.clear(); + node2ccId_map.clear(); + flop_indices.clear(); + nodeCount.clear(); + nodeCount.resize(org_nodeCount.size(),0); + new2org_node_map.resize(num_nodes, -1); + cc2nodeId_map.clear(); + cc2nodeId_map.resize(m_numCCs, 0); + + if (ioId != 100000) + { + nodeCount[ioId] = org_nodeCount[ioId]; + } + if (pllId != 100000) + { + nodeCount[pllId] = org_nodeCount[pllId]; + } + + m_numFF = 0; + m_numLUT = 0; + num_movable_nodes = 0; + + for (unsigned int nId = 0; nId < org_num_movable_nodes; ++nId) + { + if (org_is_cc_node[nId] == 0) //Not a carry node + { + std::string node_name = org_node_names[nId]; + index_type rId = org_node2fence_region_map[nId]; + + node_name2id_map[node_name] = node_names.size(); + node_size_x.emplace_back(org_node_size_x[nId]); + node_size_y.emplace_back(org_node_size_y[nId]); + node_types.emplace_back(org_node_types[nId]); + node2fence_region_map.emplace_back(rId); + + node_x.emplace_back(org_node_x[nId]); + node_y.emplace_back(org_node_y[nId]); + node_z.emplace_back(org_node_z[nId]); + lut_type.emplace_back(org_lut_type[nId]); + cluster_lut_type.emplace_back(temp_cluster_lut_type[nId]); + + new2org_node_map[node_names.size()] = nId; + if (rId == ffId) + { + ++m_numFF; + flop_indices.emplace_back(node_names.size()); + } + else if (rId == lutId) + { + ++m_numLUT; + } + + node_names.emplace_back(node_name); + ++nodeCount[rId]; + node2ccId_map.emplace_back(-1); + is_cc_node.emplace_back(0); + + std::vector temp; + } else + { + index_type ccId = org_node2ccId_map[nId]; + index_type cc_headId = org_flat_cc2node_map[org_flat_cc2node_start_map[ccId]]; + std::string cc_name = org_node_names[cc_headId]; + index_type rId = org_node2fence_region_map[cc_headId]; + + string2index_map_type::iterator found = node_name2id_map.find(cc_name); + + if (found != node_name2id_map.end()) + { + //Node part of existing carry chain + std::string node_name = org_node_names[nId]; + index_type cc_nodeId = node_name2id_map[cc_name]; + node_name2id_map[node_name] = cc_nodeId; + + //Assign larger type to root + if (org_lut_type[nId] > lut_type[cc_nodeId]) + { + lut_type[cc_nodeId] = org_lut_type[nId]; + cluster_lut_type[cc_nodeId] = temp_cluster_lut_type[nId]; + node_types[cc_nodeId] = org_node_types[nId]; + } + ////DBG + //std::cout << nId << " Node " << node_name << " assigned to " << node_name2id_map[cc_name] << std::endl; + ////DBG + } else + { + node_name2id_map[cc_name] = node_names.size(); //Assign nodeId for root node + node_name2id_map[org_node_names[nId]] = node_name2id_map[cc_name]; //Assign current node if not root to same nodeId + lut_type.emplace_back(org_lut_type[cc_headId]); + cluster_lut_type.emplace_back(temp_cluster_lut_type[cc_headId]); + node_types.emplace_back(org_node_types[cc_headId]); + node_x.emplace_back(org_node_x[cc_headId]); + node_y.emplace_back(org_node_y[cc_headId]); + node_z.emplace_back(org_node_z[cc_headId]); + node2fence_region_map.emplace_back(rId); + + node_size_x.emplace_back(std::sqrt(org_node_size_x[cc_headId]*org_node_size_x[cc_headId]*cc_element_count[ccId])); + node_size_y.emplace_back(std::sqrt(org_node_size_y[cc_headId]*org_node_size_y[cc_headId]*cc_element_count[ccId])); + + ////DBG + //std::cout << nId << " Carry chain " << cc_name << " " << node_name2id_map[cc_name] + // << " with " << cc_element_count[ccId] << " elements is of size " + // << node_size_x.back() << " x " + // << node_size_y.back() << std::endl; + ////DBG + + new2org_node_map[node_names.size()] = cc_headId; + if (rId == ffId) + { + ++m_numFF; + flop_indices.emplace_back(node_names.size()); + } + else if (rId == lutId) + { + ++m_numLUT; + } + + cc2nodeId_map[ccId] = node_names.size(); + node2ccId_map.emplace_back(ccId); //Same as org + is_cc_node.emplace_back(1); + node_names.emplace_back(cc_name); + ++nodeCount[rId]; + } + } + } + + index_type org_num_nodes = org_num_movable_nodes + fixed_node_names.size(); + org_is_cc_node.resize(org_num_nodes,0); + org_node2pincount_map.resize(org_num_nodes, 0); + org_node2outpinCount.resize(org_num_nodes, 0); + org_node2outpinIdx_map.resize(4*org_num_nodes, -1); + org_node2pin_map.resize(org_num_nodes); + } + + num_movable_nodes = node_names.size(); + num_nodes = num_movable_nodes + fixed_node_names.size(); + is_cc_node.resize(num_nodes,0); + node2pincount_map.resize(num_nodes, 0); + node2outpinCount.resize(num_nodes, 0); + node2outpinIdx_map.resize(4*num_nodes, -1); + node2pin_map.resize(num_nodes); + + ////DBG + //std::cout << "Total nodes: " << num_nodes + // << " = " << num_movable_nodes << " + " + // << fixed_node_names.size() << std::endl; + ////DBG + +} + +void PlaceDB::add_bookshelf_net(BookshelfParser::Net const& n) { + + ////DBG + //std::cout << " Add bookshelf net: " << n.net_name << " with " << n.vNetPin.size() << std::endl; + ////DBG + + // check the validity of nets + // if a node has multiple pins in the net, only one is kept + std::vector vNetPin = n.vNetPin; + + index_type netId(net_names.size()); + net2pincount_map.emplace_back(vNetPin.size()); + net_name2id_map.insert(std::make_pair(n.net_name, netId)); + net_names.emplace_back(n.net_name); + + std::vector netPins; + if (flat_net2pin_start_map.size() == 0) + { + flat_net2pin_start_map.emplace_back(0); + } + + for (unsigned i = 0; i < vNetPin.size(); ++i) + { + BookshelfParser::NetPin const& netPin = vNetPin[i]; + index_type nodeId, org_nodeId, pinId(pin_names.size()); + + ////DBG + //std::cout << "Consider net pin " << netPin.pin_name << std::endl; + ////DBG + + pin_names.emplace_back(netPin.pin_name); + pin2net_map.emplace_back(netId); + + string2index_map_type::iterator found = node_name2id_map.find(netPin.node_name); + std::string nodeType, org_nodeType; + + ////DBG + //std::cout << "Consider net pin connected to node " << netPin.node_name << std::endl; + ////DBG + if (found != node_name2id_map.end()) + { + nodeId = node_name2id_map.at(netPin.node_name); + ////DBG + //std::cout << "Here for net pin " << netPin.pin_name << " part of nodeId: " << nodeId << " " << netPin.node_name << std::endl; + ////DBG + + pin2nodeType_map.emplace_back(node2fence_region_map[nodeId]); + ////DBG + //std::cout << "Here for net pin " << netPin.pin_name << " with pin node type: " << node2fence_region_map[nodeId] << std::endl; + ////DBG + + nodeType = node_types[nodeId]; + ////DBG + //std::cout << "Here for net pin " << netPin.pin_name << " with node type: " << nodeType << std::endl; + ////DBG + + + if (is_cc_node[nodeId] == 0) + { + pin_offset_x.emplace_back(0.5*node_size_x[nodeId]); + ////DBG + //std::cout << "Here for net pin " << netPin.pin_name << " with pin_offset_x : " << 0.5*node_size_x[nodeId] << std::endl; + ////DBG + pin_offset_y.emplace_back(0.5*node_size_y[nodeId]); + ////DBG + //std::cout << "Here for net pin " << netPin.pin_name << " with pin_offset_y : " << 0.5*node_size_y[nodeId] << std::endl; + ////DBG + } else + { //carry chain node + pin_offset_x.emplace_back(0.5*node_size_x[nodeId]); + org_nodeId = org_node_name2id_map.at(netPin.node_name); + index_type ccId = org_node2ccId_map[org_nodeId]; + double element_height = node_size_y[nodeId]/cc_element_count[ccId]; + pin_offset_y.emplace_back((org_node2ccIndex_map[org_nodeId]+0.5)*element_height); + + ////DBG + //std::cout << "Net pin " << netPin.pin_name << " of node: " + // << netPin.node_name << " part of " << cc_element_count[ccId] + // << " node carry chain has node2ccIndex: " + // << org_node2ccIndex_map[org_nodeId] << " has pin_offset: " + // << pin_offset_x.back() << " x " + // << pin_offset_y.back() << std::endl; + ////DBG + + //index_type inv_ccIndex = cc_element_count[node2ccId_map[nodeId]] -1 - org_node2ccIndex_map[org_nodeId]; + //LibCell const& lCell = m_vLibCell.at(m_LibCellName2Index.at(nodeType)); + //int pinTypeId(lCell.pinType(netPin.pin_name)); + + //if (pinTypeId == 1) //Input + //{ + // temp_input_pin_info[ccId].emplace_back(std::make_pair(pinId, inv_ccIndex)); + //} else if (pinTypeId == 0 || pinTypeId == 20) //Output pin + //{ + // temp_output_pin_info[ccId].emplace_back(std::make_pair(pinId, inv_ccIndex)); + //} + + ////DBG + //if (ccId == 0) + //{ + // std::cout << "CC0 Consider pin " << netPin.pin_name << " of type: " << pinTypeId + // << " with offset: " << inv_ccIndex << " (" + // << org_node2ccIndex_map[org_nodeId] << ")" << std::endl; + //} + ////DBG + } + //Required to udpate original datastructures + if (m_numCCs > 0) + { + org_nodeId = org_node_name2id_map.at(netPin.node_name); + org_pin2nodeType_map.emplace_back(org_node2fence_region_map[org_nodeId]); + org_nodeType = org_node_types[org_nodeId]; + org_pin_offset_x.emplace_back(0.5*org_node_size_x[org_nodeId]); + org_pin_offset_y.emplace_back(0.5*org_node_size_y[org_nodeId]); + } + } else + { + ////DBG + //std::cout << " Node is fixed type " << nodeType << " in lib" << std::endl; + ////DBG + string2index_map_type::iterator fnd = fixed_node_name2id_map.find(netPin.node_name); + if (fnd != fixed_node_name2id_map.end()) + { + nodeId = fixed_node_name2id_map.at(netPin.node_name); + pin2nodeType_map.emplace_back(fixed_node2fence_region_map[nodeId]); + pin_offset_x.emplace_back(0.5*fixed_node_size_x[nodeId]); + pin_offset_y.emplace_back(0.5*fixed_node_size_y[nodeId]); + nodeType = fixed_node_types[nodeId]; + nodeId += num_movable_nodes; + + if (m_numCCs > 0) + { + org_nodeId = fixed_node_name2id_map.at(netPin.node_name); + org_pin2nodeType_map.emplace_back(fixed_node2fence_region_map[org_nodeId]); + org_pin_offset_x.emplace_back(0.5*fixed_node_size_x[org_nodeId]); + org_pin_offset_y.emplace_back(0.5*fixed_node_size_y[org_nodeId]); + org_nodeType = fixed_node_types[org_nodeId]; + org_nodeId += org_num_movable_nodes; + } + + } else + { + dreamplacePrint(kERROR, "Net %s connects to instance %s pin %s. However instance %s is not specified in .nodes file. FIX\n", + n.net_name.c_str(), netPin.node_name.c_str(), netPin.pin_name.c_str(), netPin.node_name.c_str()); + } + } + + std::string pType(""); + + ////DBG + //std::cout << " Check for nodeType " << nodeType << " in lib" << std::endl; + ////DBG + + LibCell const& lCell = m_vLibCell.at(m_LibCellName2Index.at(nodeType)); + int pinTypeId(lCell.pinType(netPin.pin_name)); + + if (pinTypeId == -1) + { + dreamplacePrint(kWARN, "Net %s connects to instance %s pin %s. However pin %s is not listed in .lib as a valid pin for instance type %s. FIX\n", + n.net_name.c_str(), netPin.node_name.c_str(), netPin.pin_name.c_str(), netPin.pin_name.c_str(), nodeType.c_str()); + } + + switch(pinTypeId) + { + case 2: //CLK + { + pType = "CK"; + break; + } + case 3: //CTRL + { + if (netPin.pin_name.find("CE") != std::string::npos || + netPin.pin_name.find("devclrn") != std::string::npos) + //if (netPin.pin_name == "CE" || netPin.pin_name == "devclrn" || + //netPin.pin_name == "devclrn0") + { + pType = "CE"; + } else if (netPin.pin_name == "R" || netPin.pin_name == "S" || + netPin.pin_name.find("sclr") != std::string::npos) + //netPin.pin_name == "sclr" || netPin.pin_name == "sclr0") + { + pType = "SR"; + pinTypeId = 4; + // else if (netPin.pin_name.find("prn") != std::string::npos ) + } else if (netPin.pin_name.find("prn") != std::string::npos) + // else if (netPin.pin_name == "prn" || + // netPin.pin_name == "prn0") + { + pType = "PR"; + pinTypeId = 5; + } else if (netPin.pin_name.find("aload") != std::string::npos) + // else if (netPin.pin_name == "aload" || + // netPin.pin_name == "aload0") + { + pType = "AL"; + pinTypeId = 6; + } else if (netPin.pin_name.find("sload") != std::string::npos) + // else if (netPin.pin_name == "sload" || + // netPin.pin_name == "sload0") + { + pType = "SL"; + pinTypeId = 7; + } else if (netPin.pin_name.find("devpor") != std::string::npos) + // else if (netPin.pin_name == "devpor" || + // netPin.pin_name == "devpor0") + { + pType = "DP"; + pinTypeId = 8; + } else if (netPin.pin_name.find("clrn") != std::string::npos) + // else if (netPin.pin_name == "clrn" || + // netPin.pin_name == "clrn0") + { + pType = "DC"; + pinTypeId = 9; + } + break; + } + default: + { + break; + } + } + + ////DBG + //std::cout << " pType " << pType << " and pinTypeId " << pinTypeId << std::endl; + ////DBG + + pin_types.emplace_back(pType); + pin_typeIds.emplace_back(pinTypeId); + ++node2pincount_map[nodeId]; + pin2node_map.emplace_back(nodeId); + node2pin_map[nodeId].emplace_back(pinId); + + if (m_numCCs > 0) + { + ++org_node2pincount_map[org_nodeId]; + org_pin2node_map.emplace_back(org_nodeId); + org_node2pin_map[org_nodeId].emplace_back(pinId); + } + + + //node2outpin info is mainly required for LUT/FF LG + // cout (30) and shareout (40) are not considered + if (pinTypeId == 0 || pinTypeId == 20) //Output pin + { + if (node2outpinCount[nodeId] < 4) + { + int n2oIdx = nodeId*4 + node2outpinCount[nodeId]; + node2outpinIdx_map[n2oIdx] = pinId; + ++node2outpinCount[nodeId]; + } + + if (m_numCCs > 0 && org_node2outpinCount[org_nodeId] < 4) + { + int n2oIdx = org_nodeId*4 + org_node2outpinCount[org_nodeId]; + org_node2outpinIdx_map[n2oIdx] = pinId; + ++org_node2outpinCount[org_nodeId]; + } + } + + netPins.emplace_back(pinId); + flat_net2pin_map.emplace_back(pinId); + } + flat_net2pin_start_map.emplace_back(flat_net2pin_map.size()); + net2pin_map.emplace_back(netPins); + + ////DBG + //std::cout << net_names.size() << " added net: " << n.net_name << " with " << n.vNetPin.size() << std::endl; + ////DBG +} + +void PlaceDB::add_bookshelf_carry(BookshelfParser::CarryChain const& carry_chain) +{ + ////DBG + //std::cout << "add carry chain: " << carry_chain.name << " containing " << carry_chain.elCount << " nodes " << std::endl; + ////DBG + + if (cc_element_count.size() == 0) + { + org_flat_cc2node_start_map.emplace_back(0); + } + + for (unsigned i = 0; i < carry_chain.elements.size(); ++i) + { + index_type nodeId = node_names.size()+1; + + string2index_map_type::iterator found = node_name2id_map.find(carry_chain.elements[i]); + if (found == node_name2id_map.end()) + { + std::cout << "Carry Chain node " << carry_chain.elements[i] << " is not part of design.nodes - CHECK" << std::endl; + continue; + } + + nodeId = node_name2id_map[carry_chain.elements[i]]; + org_node2ccIndex_map[nodeId] = carry_chain.elements.size()-i-1; + org_flat_cc2node_map.emplace_back(nodeId); + node2ccId_map[nodeId] = cc_element_count.size(); + is_cc_node[nodeId] = 1; + + ////DBG + // std::cout << "Carry chain element " << i << " " << carry_chain.elements[i] << " has nodeId: " << nodeId + // << ", ccId: " << node2ccId_map[nodeId] << " and cc index: " << org_node2ccIndex_map[nodeId] << std::endl; + ////DBG + } + org_flat_cc2node_start_map.emplace_back(org_flat_cc2node_map.size()); + cc_element_count.emplace_back(carry_chain.elCount); + ++m_numCCs; + + ////Initialize 2D vector + //std::vector > temp; + //temp_input_pin_info.emplace_back(temp); + //temp_output_pin_info.emplace_back(temp); + ////DBG + //std::cout << "Added carry chain " << m_numCCs << " with " << cc_element_count.back() << " nodes" << std::endl; + ////DBG +} + +void PlaceDB::resize_sites(int xSize, int ySize) +{ + m_dieArea.set(0, 0, xSize, ySize); + m_siteDB.resize(xSize, std::vector(ySize, 0)); + initSiteMapValUpd = true; +} + +void PlaceDB::site_info_update(int x, int y, std::string const& name) +{ + if (initSiteMapValUpd) + { + int xh = m_dieArea.xh(); + int yh = m_dieArea.yh(); + m_dieArea.set(x, y, xh, yh); + initSiteMapValUpd = false; + } + int siteId = siteType2IndexMap[name]; + m_siteDB[x][y] = siteId; +} + +void PlaceDB::resize_clk_regions(int xReg, int yReg) +{ + m_clkRegX = xReg; + m_clkRegY = yReg; +} + +void PlaceDB::add_clk_region(std::string const& name, int xl, int yl, int xh, int yh, int xm, int ym) +{ + clk_region temp; + temp.xl = xl; + temp.yl = yl; + temp.xh = xh; + temp.yh = yh; + temp.xm = xm; + temp.ym = ym; + m_clkRegionDB.emplace_back(temp); + m_clkRegions.emplace_back(name); +} + +void PlaceDB::add_lib_cell(std::string const& name) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(name); + if (found == m_LibCellName2Index.end()) // Ignore if already exists + { + m_vLibCell.push_back(LibCell(name)); + LibCell& lCell = m_vLibCell.back(); + //lCell.setName(name); + lCell.setId(m_vLibCell.size() - 1); + std::pair insertRet = + m_LibCellName2Index.insert(std::make_pair(lCell.name(), lCell.id())); + dreamplaceAssertMsg(insertRet.second, "failed to insert libCell (%s, %d)", + lCell.name().c_str(), lCell.id()); + + m_numLibCell = m_vLibCell.size(); // update number of libCells + } + m_libCellTemp = name; +} + +void PlaceDB::add_input_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addInputPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::add_input_add_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addInputAddPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::add_output_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addOutputPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::add_output_add_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addOutputAddPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::add_clk_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addClkPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::add_ctrl_pin(std::string& pName) +{ + string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); + + if (found != m_LibCellName2Index.end()) + { + LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); + lCell.addCtrlPin(pName); + } else + { + dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", + m_libCellTemp.c_str()); + } +} + +void PlaceDB::set_bookshelf_node_pos(std::string const& name, double x, double y, int z) +{ + string2index_map_type::iterator found = fixed_node_name2id_map.find(name); + //bool fixed(true); + + if (found != fixed_node_name2id_map.end()) + { + fixed_node_x.at(fixed_node_name2id_map.at(name)) = x; + fixed_node_y.at(fixed_node_name2id_map.at(name)) = y; + fixed_node_z.at(fixed_node_name2id_map.at(name)) = z; + } else + { + //string2index_map_type::iterator fnd = mov_node_name2id_map.find(name); + node_x.at(node_name2id_map.at(name)) = x; + node_y.at(node_name2id_map.at(name)) = y; + node_z.at(node_name2id_map.at(name)) = z; + } + +} + +void PlaceDB::add_site(BookshelfParser::Site const& st) +{ + siteType2IndexMap.insert(std::make_pair(st.name, siteTypes.size()+1)); + siteTypes.emplace_back(st.name); + + std::vector temp; + for (unsigned i = 0; i < st.rsrcs.size(); ++i) + { + std::string rsrc = st.rsrcs[i].first; + temp.emplace_back(rsrc); + siteRsrcCountMap.insert(std::make_pair(rsrc, st.rsrcs[i].second)); + rsrc2SiteMap.insert(std::make_pair(rsrc, st.name)); + } + siteResources.emplace_back(temp); + temp.clear(); +} + +void PlaceDB::add_rsrc(BookshelfParser::Rsrc const& rsrc) +{ + if (rsrc.rsrcCells.size() > 0) + { + //std::cout << "add resource: " << rsrc.name << " with " << rsrc.rsrcCells.size() << " cells " << std::endl; + rsrcType2IndexMap.insert(std::make_pair(rsrc.name, rsrcTypes.size())); + rsrcTypes.emplace_back(rsrc.name); + for (unsigned i = 0; i < rsrc.rsrcCells.size(); ++i) + { + inst2RsrcMap.insert(std::make_pair(rsrc.rsrcCells[i], rsrc.name)); + rsrcInstType2IndexMap.insert(std::make_pair(rsrc.rsrcCells[i], rsrcInstTypes.size())); + //std::cout << "rsrc Inst: " << rsrc.rsrcCells[i] << " assigned to id: " << rsrcInstType2IndexMap[rsrc.rsrcCells[i]] << std::endl; + rsrcInstTypes.emplace_back(rsrc.rsrcCells[i]); + } + rsrcInsts.emplace_back(rsrc.rsrcCells); + + if (rsrc.name.find("FF") != std::string::npos || + rsrc.name.find("dffeas") != std::string::npos) + { + ffId = rsrcTypes.size()-1; + } + else if (rsrc.name.find("LUT") != std::string::npos || + rsrc.name.find("lcell_comb") != std::string::npos) + { + lutId = rsrcTypes.size()-1; + } + else if (rsrc.name.find("IO") != std::string::npos || + rsrc.name.find("io") != std::string::npos) + { + ioId = rsrcTypes.size()-1; + } + else if (rsrc.name.find("PLL") != std::string::npos || + rsrc.name.find("pll") != std::string::npos) + { + pllId = rsrcTypes.size()-1; + } + } +} + +void PlaceDB::set_site_per_column(int val) +{ + sitePerColumn = val; +} + +void PlaceDB::set_site_dimensions(std::string const& sName, double w, double h) +{ + if (siteWidth.size() == 0 || siteHeight.size() == 0 || rsrcInstWidth.size() == 0 || rsrcInstHeight.size() == 0) + { + siteWidth.resize(siteTypes.size()+1, 1.0); + siteHeight.resize(siteTypes.size()+1, 1.0); + rsrcInstWidth.resize(rsrcInstTypes.size(), 1.0); + rsrcInstHeight.resize(rsrcInstTypes.size(), 1.0); + } + + unsigned siteId = siteType2IndexMap[sName]; + siteWidth[siteId] = w; + siteHeight[siteId] = h; + + unsigned rsrcId = rsrcType2IndexMap[siteResources[siteId-1][0]]; + for (unsigned srId = 0; srId < rsrcInsts[rsrcId].size(); ++srId) + { + std::string rsrcInst = rsrcInsts[rsrcId][srId]; + rsrcInstWidth[rsrcInstType2IndexMap[rsrcInst]] = w; + rsrcInstHeight[rsrcInstType2IndexMap[rsrcInst]] = h; + } +} + +void PlaceDB::set_slice_element(std::string const& sName, int cnt) +{ + sliceElements.emplace_back(std::make_pair(sName, cnt)); +} + +void PlaceDB::set_cell_dimensions(std::string const& cName, double w, double h) +{ + rsrcInstWidth[rsrcInstType2IndexMap[cName]] = w; + rsrcInstHeight[rsrcInstType2IndexMap[cName]] = h; +} + +void PlaceDB::set_lut_max_shared(int cnt) +{ + lutMaxShared = cnt; +} + +void PlaceDB::set_lut_type_in_sliceUnit(int cnt) +{ + lutTypeInSliceUnit = cnt; +} + +void PlaceDB::set_lut_fractureability(BookshelfParser::LUTFract const& lutFract) +{ + if (lutFractures.size() == 0) + { + lutFractures.resize(rsrcInsts[lutId].size()); + } + + int fId = get_last_digit_from_string(lutFract.name)-1; + + if (fId >= 0) + { + for (unsigned i = 0; i < lutFract.fractCells.size(); ++i) + { + lutFractures[fId].emplace_back(get_last_digit_from_string(lutFract.fractCells[i])-1); + } + } +} + +void PlaceDB::set_sliceFF_ctrl_mode(std::string const& mode) +{ + sliceFF_ctrl_mode = mode; +} + +void PlaceDB::set_sliceFF_ctrl(std::string const& sName, int cnt) +{ + sliceFFCtrl.emplace_back(std::make_pair(sName, cnt)); +} + +void PlaceDB::set_sliceUnitFF_ctrl(std::string const& sName, int cnt) +{ + sliceFFUnitCtrl.emplace_back(std::make_pair(sName, cnt)); +} + +void PlaceDB::set_FFCtrl_type(std::string const& type) +{ + ////DBG + //std::cout << "set ff ctrl type as " << type << std::endl; + ////DBG + ffCtrlType = type; +} + +void PlaceDB::set_wl_weight_x(double wt) +{ + wlXWeight = wt; +} + +void PlaceDB::set_wl_weight_y(double wt) +{ + wlYWeight = wt; +} + +void PlaceDB::set_pin_route_cap(int pinCap) +{ + pinRouteCap = pinCap; +} + +void PlaceDB::set_route_cap_H(int hRouteCap) +{ + routeCapH = hRouteCap; +} + +void PlaceDB::set_route_cap_V(int vRouteCap) +{ + routeCapV = vRouteCap; + //std::cout << "Vertical Routing Capacity = " << vRouteCap << std::endl; +} + +void PlaceDB::set_siteOut(BookshelfParser::SiteOut const& st) +{ + if (siteOutCoordinate.size() == 0) + { + siteOutCoordinate.resize(siteTypes.size()+1, ""); + siteOutValue.resize(siteTypes.size()+1, 0); + } + + for (unsigned i = 0; i < st.siteTypes.size(); ++i) + { + index_type siteId = siteType2IndexMap[st.siteTypes[i]]; + //std::cout << "Assign siteType : " << st.siteTypes[i] << " with siteId: " << siteId << " as " << st.coordinate << " " << st.value << std::endl; + siteOutCoordinate[siteId] = st.coordinate; + siteOutValue[siteId] = st.value; + } + +} + +void PlaceDB::set_bookshelf_design(std::string& name) { + m_designName.swap(name); +} + +void PlaceDB::bookshelf_end() { + // // parsing bookshelf format finishes + // // now it is necessary to init data that is not set in bookshelf + //Flatten node2pin + flat_node2pin_map.reserve(pin_names.size()); + flat_node2pin_start_map.emplace_back(0); + for (const auto& sub : node2pin_map) + { + flat_node2pin_map.insert(flat_node2pin_map.end(), sub.begin(), sub.end()); + flat_node2pin_start_map.emplace_back(flat_node2pin_map.size()); + } + + org_fixed_node_name2id_map = fixed_node_name2id_map; + for (auto& el : fixed_node_name2id_map) + { + el.second += num_movable_nodes; + } + + node_name2id_map.insert(fixed_node_name2id_map.begin(), fixed_node_name2id_map.end()); + lut_type.insert(lut_type.end(), fixed_lut_type.begin(), fixed_lut_type.end()); + cluster_lut_type.insert(cluster_lut_type.end(), fixed_cluster_lut_type.begin(), fixed_cluster_lut_type.end()); + node_size_x.insert(node_size_x.end(), fixed_node_size_x.begin(), fixed_node_size_x.end()); + node_size_y.insert(node_size_y.end(), fixed_node_size_y.begin(), fixed_node_size_y.end()); + node_names.insert(node_names.end(), fixed_node_names.begin(), fixed_node_names.end()); + node_types.insert(node_types.end(), fixed_node_types.begin(), fixed_node_types.end()); + node_x.insert(node_x.end(), fixed_node_x.begin(), fixed_node_x.end()); + node_y.insert(node_y.end(), fixed_node_y.begin(), fixed_node_y.end()); + node_z.insert(node_z.end(), fixed_node_z.begin(), fixed_node_z.end()); + node2fence_region_map.insert(node2fence_region_map.end(), fixed_node2fence_region_map.begin(), fixed_node2fence_region_map.end()); + + if (m_numCCs > 0) + { + org_flat_node2pin_map.reserve(pin_names.size()); + org_flat_node2pin_start_map.emplace_back(0); + for (const auto& sub : org_node2pin_map) + { + org_flat_node2pin_map.insert(org_flat_node2pin_map.end(), sub.begin(), sub.end()); + org_flat_node2pin_start_map.emplace_back(org_flat_node2pin_map.size()); + } + + for (auto& el : org_fixed_node_name2id_map) + { + el.second += org_num_movable_nodes; + } + + org_node_name2id_map.insert(org_fixed_node_name2id_map.begin(), org_fixed_node_name2id_map.end()); + org_lut_type.insert(org_lut_type.end(), fixed_lut_type.begin(), fixed_lut_type.end()); + org_node_size_x.insert(org_node_size_x.end(), fixed_node_size_x.begin(), fixed_node_size_x.end()); + org_node_size_y.insert(org_node_size_y.end(), fixed_node_size_y.begin(), fixed_node_size_y.end()); + org_node_names.insert(org_node_names.end(), fixed_node_names.begin(), fixed_node_names.end()); + org_node_types.insert(org_node_types.end(), fixed_node_types.begin(), fixed_node_types.end()); + org_node2fence_region_map.insert(org_node2fence_region_map.end(), fixed_node2fence_region_map.begin(), fixed_node2fence_region_map.end()); + org_node_x.insert(org_node_x.end(), fixed_node_x.begin(), fixed_node_x.end()); + org_node_y.insert(org_node_y.end(), fixed_node_y.begin(), fixed_node_y.end()); + org_node_z.insert(org_node_z.end(), fixed_node_z.begin(), fixed_node_z.end()); + + dreamplacePrint(kINFO, "Design contains %d carry chains\n", m_numCCs); + + //Generate required info on input/output pins of each carry chain node + + ////DBG + ////Verify info + //std::cout << "General info: "; + //index_type ccnodeId = cc2nodeId_map[0]; + //std::cout << " consider node " << ccnodeId << " " << node_names[ccnodeId] << std::endl; + //for (index_type idx = flat_node2pin_start_map[ccnodeId]; + // idx < flat_node2pin_start_map[ccnodeId+1]; ++idx) + //{ + // index_type pinId = flat_node2pin_map[idx]; + // if (pin_typeIds[pinId] == 1) + // { + // std::cout << "Input Pin " << pin_names[pinId] << " is of type " << pin_typeIds[pinId] << std::endl; + // } else if (pin_typeIds[pinId] == 0 || pin_typeIds[pinId] == 20) //Output pin + // { + // std::cout << "Output Pin " << pin_names[pinId] << " is of type " << pin_typeIds[pinId] << std::endl; + // } + //} + + //std::cout << "Input pins of first cc node with offset: " << std::endl; + //for (auto el : temp_input_pin_info[0]) + //{ + // std::cout << el.second << " Input Pin " << pin_names[el.first] << " is of type " << pin_typeIds[el.first] << std::endl; + //} + //std::cout << "Output pins of first cc node with offset: " << std::endl; + //for (auto el : temp_output_pin_info[0]) + //{ + // std::cout << el.second << " Output Pin " << pin_names[el.first] << " is of type " << pin_typeIds[el.first] << std::endl; + //} + ////DBG + + //overall_cc_input_pin_start_map.emplace_back(0); + //overall_cc_output_pin_start_map.emplace_back(0); + //for (index_type ccIdx = 0; ccIdx < m_numCCs; ++ccIdx) + //{ + // ////DBG + // //std::cout << "Input pins of first cc node with offset: " << std::endl; + // //for (auto el : temp_input_pin_info[ccIdx]) + // //{ + // // std::cout << el.second << " Input Pin " << pin_names[el.first] << " " << el.first << " is of type " << pin_typeIds[el.first] << std::endl; + // //} + // ////DBG + + // std::sort(temp_input_pin_info[ccIdx].begin(), temp_input_pin_info[ccIdx].end(), + // [&](const auto &a, const auto &b){ return a.second < b.second; }); + + // ////DBG + // //std::cout << "After sorting Input pins of first cc node with offset: " << std::endl; + // //for (auto el : temp_input_pin_info[ccIdx]) + // //{ + // // std::cout << el.second << " Input Pin " << pin_names[el.first] << " " << el.first << " is of type " << pin_typeIds[el.first] << std::endl; + // //} + // ////DBG + + // int currEl = -1; + // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); + // for (auto el : temp_input_pin_info[ccIdx]) + // { + // if (currEl == -1) + // { + // currEl = el.second; + // } else if (currEl != el.second) + // { + // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); + // currEl = el.second; + // } + // flat_cc_input_pins_map.emplace_back(el.first); + // } + // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); + // overall_cc_input_pin_start_map.emplace_back(flat_cc_input_pin_start_map.size()); + + // ////DBG + // //std::cout << "Contents of flat_cc_input_pins_map: " << std::endl; + // //for (auto el : flat_cc_input_pins_map) + // //{ + // // std::cout << el << " "; + // //} + // //std::cout << std::endl; + // //std::cout << "Contents of flat_cc_input_pin_start_map: " << std::endl; + // //for (auto el : flat_cc_input_pin_start_map) + // //{ + // // std::cout << el << " "; + // //} + // //std::cout << std::endl; + // //std::cout << "Contents of overall_cc_input_pin_start_map: " << std::endl; + // //for (auto el : overall_cc_input_pin_start_map) + // //{ + // // std::cout << el << " "; + // //} + // //std::cout << std::endl; + // ////DBG + + // std::sort(temp_output_pin_info[ccIdx].begin(), temp_output_pin_info[ccIdx].end(), + // [&](const auto &a, const auto &b){ return a.second < b.second; }); + // currEl = -1; + // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); + // for (auto el : temp_output_pin_info[ccIdx]) + // { + // if (currEl == -1) + // { + // currEl = el.second; + // } else if (currEl != el.second) + // { + // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); + // currEl = el.second; + // } + // flat_cc_output_pins_map.emplace_back(el.first); + // } + // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); + // overall_cc_output_pin_start_map.emplace_back(flat_cc_output_pin_start_map.size()); + //} + } +} + +bool PlaceDB::write(std::string const& filename) const { + + return write(filename, NULL, NULL); +} + +bool PlaceDB::write(std::string const& filename, + float const* x, + float const* y, + PlaceDB::index_type const* z) const { + return BookShelfWriter(*this).write(filename, x, y, z); +} + +DREAMPLACE_END_NAMESPACE + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h new file mode 100644 index 00000000..f6717a99 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h @@ -0,0 +1,682 @@ +/************************************************************************* + > File Name: PlaceDB.h + > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + > Mail: yibolin@utexas.edu + > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT + > Updated: Mar 2021 + ************************************************************************/ + +#ifndef DREAMPLACE_PLACEDB_H +#define DREAMPLACE_PLACEDB_H + +#include // bookshelf parser +#include + +#include "Node.h" +#include "Net.h" +#include "Pin.h" +#include "LibCell.h" +#include "Params.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class PlaceDB; + +//Introduce new struct for clk region information +struct clk_region +{ + int xl; + int yl; + int xm; + int ym; + int xh; + int yh; +}; + +class PlaceDB : public BookshelfParser::BookshelfDataBase +{ + public: + typedef Object::coordinate_type coordinate_type; + typedef coordinate_traits::manhattan_distance_type manhattan_distance_type; + typedef coordinate_traits::index_type index_type; + typedef coordinate_traits::area_type area_type; + typedef hashspace::unordered_map string2index_map_type; + typedef hashspace::unordered_map string2string_map_type; + typedef Box diearea_type; + + /// default constructor + PlaceDB(); + + /// destructor + virtual ~PlaceDB() {} + + /// member functions + /// data access + + std::vector const& nodeNames() const {return node_names;} + std::vector& nodeNames() {return node_names;} + std::string const& nodeName(index_type id) const {return node_names.at(id);} + std::string& nodeName(index_type id) {return node_names.at(id);} + + std::vector const& nodeTypes() const {return node_types;} + std::vector& nodeTypes() {return node_types;} + std::string const& nodeType(index_type id) const {return node_types.at(id);} + std::string& nodeType(index_type id) {return node_types.at(id);} + + std::vector const& nodeXLocs() const {return node_x;} + std::vector& nodeXLocs() {return node_x;} + double const& nodeX(index_type id) const {return node_x.at(id);} + double& nodeX(index_type id) {return node_x.at(id);} + + std::vector const& nodeYLocs() const {return node_y;} + std::vector& nodeYLocs() {return node_y;} + double const& nodeY(index_type id) const {return node_y.at(id);} + double& nodeY(index_type id) {return node_y.at(id);} + + std::vector const& nodeZLocs() const {return node_z;} + std::vector& nodeZLocs() {return node_z;} + index_type const& nodeZ(index_type id) const {return node_z.at(id);} + index_type& nodeZ(index_type id) {return node_z.at(id);} + + std::vector const& nodeXSizes() const {return node_size_x;} + std::vector& nodeXSizes() {return node_size_x;} + double const& nodeXSize(index_type id) const {return node_size_x.at(id);} + double& nodeXSize(index_type id) {return node_size_x.at(id);} + + std::vector const& nodeYSizes() const {return node_size_y;} + std::vector& nodeYSizes() {return node_size_y;} + double const& nodeYSize(index_type id) const {return node_size_y.at(id);} + double& nodeYSize(index_type id) {return node_size_y.at(id);} + + std::vector const& node2FenceRegionMap() const {return node2fence_region_map;} + std::vector& node2FenceRegionMap() {return node2fence_region_map;} + index_type const& nodeFenceRegion(index_type id) const {return node2fence_region_map.at(id);} + index_type& nodeFenceRegion(index_type id) {return node2fence_region_map.at(id);} + + std::vector const& node2OutPinId() const {return node2outpinIdx_map;} + std::vector& node2OutPinId() {return node2outpinIdx_map;} + + std::vector const& node2PinCount() const {return node2pincount_map;} + std::vector& node2PinCount() {return node2pincount_map;} + index_type const& node2PinCnt(index_type id) const {return node2pincount_map.at(id);} + index_type& node2PinCnt(index_type id) {return node2pincount_map.at(id);} + + std::vector const& flopIndices() const {return flop_indices;} + std::vector& flopIndices() {return flop_indices;} + index_type const& flopIndex(index_type id) const {return flop_indices.at(id);} + index_type& flopIndex(index_type id) {return flop_indices.at(id);} + + std::vector const& lutIndices() const {return lut_indices;} + std::vector& lutIndices() {return lut_indices;} + index_type const& lutIndex(index_type id) const {return lut_indices.at(id);} + index_type& lutIndex(index_type id) {return lut_indices.at(id);} + + std::vector const& lutTypes() const {return lut_type;} + std::vector& lutTypes() {return lut_type;} + + std::vector const& clusterlutTypes() const {return cluster_lut_type;} + std::vector& clusterlutTypes() {return cluster_lut_type;} + + std::vector const& node2OutPinCount() const {return node2outpinCount;} + std::vector& node2OutPinCount() {return node2outpinCount;} + + std::vector > const& node2PinMap() const {return node2pin_map;} + std::vector >& node2PinMap() {return node2pin_map;} + index_type const& node2PinIdx(index_type xloc, index_type yloc) const {return node2pin_map.at(xloc).at(yloc);} + index_type& node2PinIdx(index_type xloc, index_type yloc) {return node2pin_map.at(xloc).at(yloc);} + + std::vector const& node_count() const {return nodeCount;} + std::vector& node_count() {return nodeCount;} + + //Nodes part of Carry chain + std::vector const& ccElementCount() const {return cc_element_count;} + std::vector& ccElementCount() {return cc_element_count;} + + std::vector const& node2CCIdMap() const {return node2ccId_map;} + std::vector& node2CCIdMap() {return node2ccId_map;} + + std::vector const& cc2nodeIdMap() const {return cc2nodeId_map;} + std::vector& cc2nodeIdMap() {return cc2nodeId_map;} + + std::vector const& isCCNode() const {return is_cc_node;} + std::vector& isCCNode() {return is_cc_node;} + + //std::vector const& flatCCInputPinsMap() const {return flat_cc_input_pins_map;} + //std::vector& flatCCInputPinsMap() {return flat_cc_input_pins_map;} + + //std::vector const& flatCCOutputPinsMap() const {return flat_cc_output_pins_map;} + //std::vector& flatCCOutputPinsMap() {return flat_cc_output_pins_map;} + + //std::vector const& flatCCInputPinStartMap() const {return flat_cc_input_pin_start_map;} + //std::vector& flatCCInputPinStartMap() {return flat_cc_input_pin_start_map;} + + //std::vector const& flatCCOutputPinStartMap() const {return flat_cc_output_pin_start_map;} + //std::vector& flatCCOutputPinStartMap() {return flat_cc_output_pin_start_map;} + + //std::vector const& overallCCInputPinStartMap() const {return overall_cc_input_pin_start_map;} + //std::vector& overallCCInputPinStartMap() {return overall_cc_input_pin_start_map;} + + //std::vector const& overallCCOutputPinStartMap() const {return overall_cc_output_pin_start_map;} + //std::vector& overallCCOutputPinStartMap() {return overall_cc_output_pin_start_map;} + + std::vector const& netNames() const {return net_names;} + std::vector& netNames() {return net_names;} + std::string const& netName(index_type id) const {return net_names.at(id);} + std::string& netName(index_type id) {return net_names.at(id);} + + std::size_t numNets() const {return net_names.size();} + + std::vector const& net2PinCount() const {return net2pincount_map;} + std::vector& net2PinCount() {return net2pincount_map;} + index_type const& net2PinCnt(index_type id) const {return net2pincount_map.at(id);} + index_type& net2PinCnt(index_type id) {return net2pincount_map.at(id);} + + std::vector > const& net2PinMap() const {return net2pin_map;} + std::vector >& net2PinMap() {return net2pin_map;} + index_type const& net2PinIdx(index_type xloc, index_type yloc) const {return net2pin_map.at(xloc).at(yloc);} + index_type& net2PinIdx(index_type xloc, index_type yloc) {return net2pin_map.at(xloc).at(yloc);} + + std::vector const& flatNet2PinMap() const {return flat_net2pin_map;} + std::vector& flatNet2PinMap() {return flat_net2pin_map;} + + std::vector const& flatNet2PinStartMap() const {return flat_net2pin_start_map;} + std::vector& flatNet2PinStartMap() {return flat_net2pin_start_map;} + + std::vector const& flatNode2PinStartMap() const {return flat_node2pin_start_map;} + std::vector& flatNode2PinStartMap() {return flat_node2pin_start_map;} + + std::vector const& flatNode2PinMap() const {return flat_node2pin_map;} + std::vector& flatNode2PinMap() {return flat_node2pin_map;} + + std::vector const& pinNames() const {return pin_names;} + std::vector& pinNames() {return pin_names;} + std::string const& pinName(index_type id) const {return pin_names.at(id);} + std::string& pinName(index_type id) {return pin_names.at(id);} + + std::size_t numPins() const {return pin_names.size();} + + std::vector const& pin2NetMap() const {return pin2net_map;} + std::vector& pin2NetMap() {return pin2net_map;} + + std::vector const& pin2NodeMap() const {return pin2node_map;} + std::vector& pin2NodeMap() {return pin2node_map;} + index_type const& pin2Node(index_type id) const {return pin2node_map.at(id);} + index_type& pin2Node(index_type id) {return pin2node_map.at(id);} + + std::vector const& pin2NodeTypeMap() const {return pin2nodeType_map;} + std::vector& pin2NodeTypeMap() {return pin2nodeType_map;} + + std::vector const& pinTypes() const {return pin_types;} + std::vector& pinTypes() {return pin_types;} + + std::vector const& pinTypeIds() const {return pin_typeIds;} + std::vector& pinTypeIds() {return pin_typeIds;} + + std::vector const& pinOffsetX() const {return pin_offset_x;} + std::vector& pinOffsetX() {return pin_offset_x;} + + std::vector const& pinOffsetY() const {return pin_offset_y;} + std::vector& pinOffsetY() {return pin_offset_y;} + + std::vector const& libCells() const {return m_vLibCell;} + std::vector& libCells() {return m_vLibCell;} + LibCell const& libCell(index_type id) const {return m_vLibCell.at(id);} + LibCell& libCell(index_type id) {return m_vLibCell.at(id);} + + std::size_t numLibCells() const {return m_vLibCell.size();} + + std::size_t siteRows() const {return m_siteDB.size();} + std::size_t siteCols() const {return m_siteDB[0].size();} + index_type const& siteVal(index_type xloc, index_type yloc) const {return m_siteDB.at(xloc).at(yloc);} + index_type& siteVal(index_type xloc, index_type yloc) {return m_siteDB.at(xloc).at(yloc);} + + /// be careful to use die area because it is larger than the actual rowBbox() which is the placement area + /// it is safer to use rowBbox() + diearea_type const& dieArea() const {return m_dieArea;} + + string2index_map_type const& libCellName2Index() const {return m_LibCellName2Index;} + string2index_map_type& libCellName2Index() {return m_LibCellName2Index;} + + string2index_map_type const& nodeName2Index() const {return node_name2id_map;} + string2index_map_type& nodeName2Index() {return node_name2id_map;} + + string2index_map_type const& netName2Index() const {return net_name2id_map;} + string2index_map_type& netName2Index() {return net_name2id_map;} + + std::size_t numMovable() const {return node_names.size()-fixed_node_names.size();} + std::size_t numFixed() const {return fixed_node_names.size();} + + std::vector const& orgNode2CCIdMap() const {return org_node2ccId_map;} + std::vector& orgNode2CCIdMap() {return org_node2ccId_map;} + + std::vector const& isOrgCCNode() const {return org_is_cc_node;} + std::vector& isOrgCCNode() {return org_is_cc_node;} + + std::vector const& new2OrgNodeMap() const {return new2org_node_map;} + std::vector& new2OrgNodeMap() {return new2org_node_map;} + int const& orgNodeMap(index_type id) const {return new2org_node_map.at(id);} + int& orgNodeMap(index_type id) {return new2org_node_map.at(id);} + + std::size_t numOrgMovable() const {return org_node_names.size()-fixed_node_names.size();} + + string2index_map_type const& orgNodeName2Index() const {return org_node_name2id_map;} + string2index_map_type& orgNodeName2Index() {return org_node_name2id_map;} + + std::vector const& orgNodeNames() const {return org_node_names;} + std::vector& orgNodeNames() {return org_node_names;} + std::string const& orgNodeName(index_type id) const {return org_node_names.at(id);} + std::string& orgNodeName(index_type id) {return org_node_names.at(id);} + + std::vector const& orgNodeTypes() const {return org_node_types;} + std::vector& orgNodeTypes() {return org_node_types;} + std::string const& orgNodeType(index_type id) const {return org_node_types.at(id);} + std::string& orgNodeType(index_type id) {return org_node_types.at(id);} + + std::vector const& orgNodeXSizes() const {return org_node_size_x;} + std::vector& orgNodeXSizes() {return org_node_size_x;} + + std::vector const& orgNodeYSizes() const {return org_node_size_y;} + std::vector& orgNodeYSizes() {return org_node_size_y;} + + std::vector const& orgNode2FenceRegionMap() const {return org_node2fence_region_map;} + std::vector& orgNode2FenceRegionMap() {return org_node2fence_region_map;} + + std::vector const& orgNodeCount() const {return org_nodeCount;} + std::vector& orgNodeCount() {return org_nodeCount;} + + std::vector const& orgFlopIndices() const {return org_flop_indices;} + std::vector& orgFlopIndices() {return org_flop_indices;} + index_type const& orgFlopIndex(index_type id) const {return org_flop_indices.at(id);} + index_type& orgFlopIndex(index_type id) {return org_flop_indices.at(id);} + + std::vector const& orgLutTypes() const {return org_lut_type;} + std::vector& orgLutTypes() {return org_lut_type;} + + std::vector const& orgPinOffsetX() const {return org_pin_offset_x;} + std::vector& orgPinOffsetX() {return org_pin_offset_x;} + + std::vector const& orgPinOffsetY() const {return org_pin_offset_y;} + std::vector& orgPinOffsetY() {return org_pin_offset_y;} + + std::vector const& orgPin2NodeTypeMap() const {return org_pin2nodeType_map;} + std::vector& orgPin2NodeTypeMap() {return org_pin2nodeType_map;} + + std::vector const& orgNode2PinCount() const {return org_node2pincount_map;} + std::vector& orgNode2PinCount() {return org_node2pincount_map;} + + std::vector const& orgPin2NodeMap() const {return org_pin2node_map;} + std::vector& orgPin2NodeMap() {return org_pin2node_map;} + + std::vector const& orgNode2OutPinCount() const {return org_node2outpinCount;} + std::vector& orgNode2OutPinCount() {return org_node2outpinCount;} + + std::vector const& orgNode2OutPinId() const {return org_node2outpinIdx_map;} + std::vector& orgNode2OutPinId() {return org_node2outpinIdx_map;} + + std::vector const& orgFlatNode2PinMap() const {return org_flat_node2pin_map;} + std::vector& orgFlatNode2PinMap() {return org_flat_node2pin_map;} + + std::vector const& orgFlatNode2PinStartMap() const {return org_flat_node2pin_start_map;} + std::vector& orgFlatNode2PinStartMap() {return org_flat_node2pin_start_map;} + + std::vector const& orgNodeXLocs() const {return org_node_x;} + std::vector& orgNodeXLocs() {return org_node_x;} + + std::vector const& orgNodeYLocs() const {return org_node_y;} + std::vector& orgNodeYLocs() {return org_node_y;} + + std::vector const& orgNodeZLocs() const {return org_node_z;} + std::vector& orgNodeZLocs() {return org_node_z;} + + std::vector const& orgflatCCNodeMap() const {return org_flat_cc2node_map;} + std::vector& orgflatCCNodeMap() {return org_flat_cc2node_map;} + + std::vector const& orgflatCCNodeStartMap() const {return org_flat_cc2node_start_map;} + std::vector& orgflatCCNodeStartMap() {return org_flat_cc2node_start_map;} + + //Site & Resources + std::vector const& site_types() const {return siteTypes;} + std::vector& site_types() {return siteTypes;} + std::string const& site_type(index_type id) const {return siteTypes.at(id);} + std::string& site_type(index_type id) {return siteTypes.at(id);} + + std::vector > const& site_resources_map() const {return siteResources;} + std::vector >& site_resources_map() {return siteResources;} + std::string const& site_resource(index_type xloc, index_type yloc) const {return siteResources.at(xloc).at(yloc);} + std::string& site_resource(index_type xloc, index_type yloc) {return siteResources.at(xloc).at(yloc);} + + string2string_map_type const& rsrc2site_map() const {return rsrc2SiteMap;} + string2string_map_type& rsrc2site_map() {return rsrc2SiteMap;} + std::string const& rsrc_type2site(std::string rsrcType) const {return rsrc2SiteMap.at(rsrcType);} + std::string& rsrc_type2site(std::string rsrcType) {return rsrc2SiteMap.at(rsrcType);} + + string2index_map_type const& site_rsrc2count_map() const {return siteRsrcCountMap;} + string2index_map_type& site_rsrc2count_map() {return siteRsrcCountMap;} + + std::vector const& rsrc_types() const {return rsrcTypes;} + std::vector& rsrc_types() {return rsrcTypes;} + std::string const& rsrc_type(index_type id) const {return rsrcTypes.at(id);} + std::string& rsrc_type(index_type id) {return rsrcTypes.at(id);} + + std::vector > const& rsrc_insts_map() const {return rsrcInsts;} + std::vector >& rsrc_insts_map() {return rsrcInsts;} + + string2string_map_type const& inst2rsrc_map() const {return inst2RsrcMap;} + string2string_map_type& inst2rsrc_map() {return inst2RsrcMap;} + + index_type const& site_per_column() const {return sitePerColumn;} + index_type& site_per_column() {return sitePerColumn;} + + std::vector const& site_widths() const {return siteWidth;} + std::vector& site_widths() {return siteWidth;} + double const& site_width(index_type id) const {return siteWidth.at(id);} + double& site_width(index_type id) {return siteWidth.at(id);} + + std::vector const& site_heights() const {return siteHeight;} + std::vector& site_heights() {return siteHeight;} + double const& site_height(index_type id) const {return siteHeight.at(id);} + double& site_height(index_type id) {return siteHeight.at(id);} + + std::vector const& rsrc_inst_types() const {return rsrcInstTypes;} + std::vector& rsrc_inst_types() {return rsrcInstTypes;} + std::string const& rsrc_inst_type(index_type id) const {return rsrcInstTypes.at(id);} + std::string& rsrc_inst_type(index_type id) {return rsrcInstTypes.at(id);} + + std::vector const& site_out_coordinates() const {return siteOutCoordinate;} + std::vector& site_out_coordinates() {return siteOutCoordinate;} + std::string const& site_out_coordinate(index_type id) const {return siteOutCoordinate.at(id);} + std::string& site_out_coordinate(index_type id) {return siteOutCoordinate.at(id);} + + std::vector const& site_out_values() const {return siteOutValue;} + std::vector& site_out_values() {return siteOutValue;} + index_type const& site_out_value(index_type id) const {return siteOutValue.at(id);} + index_type& site_out_value(index_type id) {return siteOutValue.at(id);} + + std::vector const& rsrc_inst_widths() const {return rsrcInstWidth;} + std::vector& rsrc_inst_widths() {return rsrcInstWidth;} + double const& rsrc_inst_width(index_type id) const {return rsrcInstWidth.at(id);} + double& rsrc_inst_width(index_type id) {return rsrcInstWidth.at(id);} + + std::vector const& rsrc_inst_heights() const {return rsrcInstHeight;} + std::vector& rsrc_inst_heights() {return rsrcInstHeight;} + double const& rsrc_inst_height(index_type id) const {return rsrcInstHeight.at(id);} + double& rsrc_inst_height(index_type id) {return rsrcInstHeight.at(id);} + + std::vector > const& lut_fractures_map() const {return lutFractures;} + std::vector >& lut_fractures_map() {return lutFractures;} + index_type const& lut_fracture(index_type xloc, index_type yloc) const {return lutFractures.at(xloc).at(yloc);} + index_type& lut_fracture(index_type xloc, index_type yloc) {return lutFractures.at(xloc).at(yloc);} + + string2index_map_type const& site_type2index_map() const {return siteType2IndexMap;} + string2index_map_type& site_type2index_map() {return siteType2IndexMap;} + index_type const& site_type2index(std::string siteType) const {return siteType2IndexMap.at(siteType);} + index_type& site_type2index(std::string siteType) {return siteType2IndexMap.at(siteType);} + + string2index_map_type const& rsrc_type2index_map() const {return rsrcType2IndexMap;} + string2index_map_type& rsrc_type2index_map() {return rsrcType2IndexMap;} + index_type const& rsrc_type2index(std::string rsrcType) const {return rsrcType2IndexMap.at(rsrcType);} + index_type& rsrc_type2index(std::string rsrcType) {return rsrcType2IndexMap.at(rsrcType);} + + string2index_map_type const& rsrc_inst_type2index_map() const {return rsrcInstType2IndexMap;} + string2index_map_type& rsrc_inst_type2index_map() {return rsrcInstType2IndexMap;} + + std::vector > const& slice_elements() const {return sliceElements;} + std::vector >& slice_elements() {return sliceElements;} + + std::vector > const& slice_FF_ctrls() const {return sliceFFCtrl;} + std::vector >& slice_FF_ctrls() {return sliceFFCtrl;} + std::string const& slice_FF_ctrl_signal(index_type idx) const {return sliceFFCtrl.at(idx).first;} + std::string& slice_FF_ctrl_signal(index_type idx) {return sliceFFCtrl.at(idx).first;} + index_type const& slice_FF_ctrl_count(index_type idx) const {return sliceFFCtrl.at(idx).second;} + index_type& slice_FF_ctrl_count(index_type idx) {return sliceFFCtrl.at(idx).second;} + + std::vector > const& sliceUnit_FF_ctrls() const {return sliceFFUnitCtrl;} + std::vector >& sliceUnit_FF_ctrls() {return sliceFFUnitCtrl;} + std::string const& sliceUnit_FF_ctrl_signal(index_type idx) const {return sliceFFUnitCtrl.at(idx).first;} + std::string& sliceUnit_FF_ctrl_signal(index_type idx) {return sliceFFUnitCtrl.at(idx).first;} + index_type const& sliceUnit_FF_ctrl_count(index_type idx) const {return sliceFFUnitCtrl.at(idx).second;} + index_type& sliceUnit_FF_ctrl_count(index_type idx) {return sliceFFUnitCtrl.at(idx).second;} + + std::string const& ff_ctrl_type() const {return ffCtrlType;} + std::string& ff_ctrl_type() {return ffCtrlType;} + + double wl_weight_x() const {return wlXWeight;} + double wl_weight_y() const {return wlYWeight;} + std::string slice_ff_ctrl_mode() const {return sliceFF_ctrl_mode;} + index_type lut_shared_max_pins() const {return lutMaxShared;} + index_type lut_type_in_sliceUnit() const {return lutTypeInSliceUnit;} + index_type pin_route_cap() const {return pinRouteCap;} + index_type route_cap_h() const {return routeCapH;} + index_type route_cap_v() const {return routeCapV;} + + //std::size_t numMovable() const {return num_movable_nodes;} + //std::size_t numFixed() const {return num_fixed_nodes;} + std::size_t numLibCell() const {return m_numLibCell;} + std::size_t numLUT() const {return m_numLUT;} + std::size_t numFF() const {return m_numFF;} + std::size_t numCCNodes() const {return m_numCCs;} + std::string designName() const {return m_designName;} + + /// \return die area information of layout + double xl() const {return m_dieArea.xl();} + double yl() const {return m_dieArea.yl();} + double xh() const {return m_dieArea.xh();} + double yh() const {return m_dieArea.yh();} + manhattan_distance_type width() const {return m_dieArea.width();} + manhattan_distance_type height() const {return m_dieArea.height();} + + ///==== Bookshelf Callbacks ==== + virtual void add_bookshelf_node(std::string& name, std::string& type); //Updated for FPGA + virtual void add_bookshelf_net(BookshelfParser::Net const& n); + virtual void add_bookshelf_carry(BookshelfParser::CarryChain const& carry_chain); + virtual void set_bookshelf_node_pos(std::string const& name, double x, double y, int z); + virtual void resize_sites(int xSize, int ySize); + virtual void site_info_update(int x, int y, std::string const& name); + virtual void resize_clk_regions(int xReg, int yReg); + virtual void add_clk_region(std::string const& name, int xl, int yl, int xh, int yh, int xm, int ym); + virtual void add_lib_cell(std::string const& name); + virtual void add_input_pin(std::string& pName); + virtual void add_input_add_pin(std::string& pName); + virtual void add_output_pin(std::string& pName); + virtual void add_output_add_pin(std::string& pName); + virtual void add_clk_pin(std::string& pName); + virtual void add_ctrl_pin(std::string& pName); + virtual void add_site(BookshelfParser::Site const& st); + virtual void add_rsrc(BookshelfParser::Rsrc const& rsrc); + virtual void set_site_per_column(int val); + virtual void set_site_dimensions(std::string const& sName, double w, double h); + virtual void set_slice_element(std::string const& sName, int cnt); + virtual void set_cell_dimensions(std::string const& cName, double w, double h); + virtual void set_lut_max_shared(int cnt); + virtual void set_lut_type_in_sliceUnit(int cnt); + virtual void set_lut_fractureability(BookshelfParser::LUTFract const& lutFract); + virtual void set_sliceFF_ctrl_mode(std::string const& mode); + virtual void set_sliceFF_ctrl(std::string const& sName, int cnt); + virtual void set_sliceUnitFF_ctrl(std::string const& sName, int cnt); + virtual void set_FFCtrl_type(std::string const& type); + virtual void set_wl_weight_x(double wt); + virtual void set_wl_weight_y(double wt); + virtual void set_pin_route_cap(int pinCap); + virtual void set_route_cap_H(int hRouteCap); + virtual void set_route_cap_V(int vRouteCap); + virtual void set_siteOut(BookshelfParser::SiteOut const& st); + virtual void set_bookshelf_design(std::string& name); + virtual void update_nodes(); + virtual void bookshelf_end(); + + /// write placement solutions + virtual bool write(std::string const& filename) const; + virtual bool write(std::string const& filename, float const* x = NULL, float const* y = NULL, index_type const* z = NULL) const; + + std::vector > m_siteDB; //FPGA Site Information + std::vector m_clkRegionDB; //FPGA clkRegion Information + std::vector m_clkRegions; //FPGA clkRegion Names + int m_clkRegX; + int m_clkRegY; + std::vector m_vLibCell; ///< library definition for cell types + diearea_type m_dieArea; ///< die area, it can be larger than actual placement area + string2index_map_type m_LibCellName2Index; ///< map name of lib cell to index of m_vLibCell + + //Temp storage for libcell name considered + std::string m_libCellTemp; + + //Ensure correct node and pin_offset sizes for LUT/FF + index_type lutId = 100000; + index_type ffId = 100000; + index_type ioId = 100000; + index_type pllId = 100000; + + //Site & Resources Info + index_type sitePerColumn = 0; + std::vector > siteResources; + std::vector siteTypes; + string2index_map_type siteType2IndexMap; + std::vector siteWidth; + std::vector siteHeight; + string2index_map_type siteRsrcCountMap; + string2string_map_type rsrc2SiteMap; + + std::vector rsrcInstTypes; + string2index_map_type rsrcInstType2IndexMap; + std::vector rsrcTypes; + string2index_map_type rsrcType2IndexMap; + std::vector > rsrcInsts; + std::vector rsrcInstWidth; + std::vector rsrcInstHeight; + string2string_map_type inst2RsrcMap; + + std::vector > lutFractures; + std::vector > sliceElements; + std::vector > sliceFFCtrl; + std::vector > sliceFFUnitCtrl; + + std::vector siteOutCoordinate; + std::vector siteOutValue; + + std::string ffCtrlType; + double wlXWeight; + double wlYWeight; + index_type lutMaxShared; + index_type lutTypeInSliceUnit; + index_type pinRouteCap; + index_type routeCapH; + index_type routeCapV; + std::string sliceFF_ctrl_mode; + + std::size_t num_movable_nodes; ///< number of movable cells + std::size_t num_fixed_nodes; ///< number of fixed cells + std::size_t m_numLibCell; ///< number of standard cells in the library + std::size_t m_numLUT; ///< number of LUTs in design + std::size_t m_numFF; ///< number of FFs in design + std::size_t m_numCCs; ///< number of carry chains in design + + std::string m_designName; ///< for writing def file + + //temp flag + bool initSiteMapValUpd; + //Flattened + std::vector node_names; + std::vector node_types; + std::vector node_size_x; + std::vector node_size_y; + std::vector fixed_node_size_x; + std::vector fixed_node_size_y; + std::vector node_x; + std::vector node_y; + std::vector node_z; + std::vector fixed_node_x; + std::vector fixed_node_y; + std::vector fixed_node_z; + std::vector nodeCount; + + //New approach to parsing + std::vector fixed_node_names; + std::vector fixed_node_types; + std::vector net_names; + std::vector pin_names; + std::vector pin_types; + std::vector node2fence_region_map; + std::vector fixed_node2fence_region_map; + std::vector > node2pin_map; + std::vector node2pincount_map; + std::vector net2pincount_map; + std::vector node2outpinIdx_map; + std::vector node2outpinCount; + std::vector pin_typeIds; + std::vector pin2node_map; + std::vector pin2net_map; + std::vector pin2nodeType_map; + std::vector > net2pin_map; + std::vector flat_net2pin_map; + std::vector flat_net2pin_start_map; + std::vector flat_node2pin_map; + std::vector flat_node2pin_start_map; + std::vector cc_element_count; + std::vector is_cc_node; + std::vector flop_indices; + std::vector lut_type; + std::vector cluster_lut_type; + std::vector fixed_lut_type; + std::vector fixed_cluster_lut_type; + std::vector node2ccId_map; + std::vector cc2nodeId_map; + + std::vector pin_offset_x; + std::vector pin_offset_y; + + //string2index_map_type mov_node_name2id_map; + string2index_map_type fixed_node_name2id_map; + string2index_map_type node_name2id_map; + string2index_map_type net_name2id_map; + + //Data structures for original info when carry chains exist + string2index_map_type org_node_name2id_map; + std::vector org_node_names; + std::vector org_node_types; + std::vector org_node_size_x; + std::vector org_node_size_y; + std::vector org_node2fence_region_map; + std::vector org_nodeCount; + std::vector org_is_cc_node; + std::vector org_flop_indices; + std::vector org_lut_type; + std::vector org_node2ccId_map; + std::vector org_node2ccIndex_map; + std::vector org_flat_cc2node_map; + std::vector org_flat_cc2node_start_map; + std::vector new2org_node_map; + + std::vector org_node_x; + std::vector org_node_y; + std::vector org_node_z; + std::vector org_pin_offset_x; + std::vector org_pin_offset_y; + std::vector org_pin2nodeType_map; + std::vector org_node2pincount_map; + std::vector org_pin2node_map; + std::vector > org_node2pin_map; + std::vector org_node2outpinCount; + std::vector org_node2outpinIdx_map; + std::vector org_flat_node2pin_map; + std::vector org_flat_node2pin_start_map; + //Temporary org data structures + std::size_t org_num_movable_nodes; ///< number of movable cells + string2index_map_type org_fixed_node_name2id_map; + std::vector lut_indices; + ////Get information of carry chain node input/output pins + //std::vector flat_cc_input_pins_map; + //std::vector flat_cc_output_pins_map; + //std::vector flat_cc_input_pin_start_map; + //std::vector flat_cc_output_pin_start_map; + //std::vector overall_cc_input_pin_start_map; + //std::vector overall_cc_output_pin_start_map; + + ////Temporary datastructure + //std::vector > > temp_input_pin_info; + //std::vector > > temp_output_pin_info; +}; + +DREAMPLACE_END_NAMESPACE + +#endif + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h new file mode 100644 index 00000000..9618b1a3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h @@ -0,0 +1,30 @@ +/************************************************************************* + > File Name: PlaceWriter.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Mon 20 Jul 2015 11:34:51 AM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_PLACEWRITER_H +#define DREAMPLACE_PLACEWRITER_H + +#include +#include +#include "PlaceDB.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// class PlaceSolWriter is the base class to write placement solutions +class PlaceSolWriter +{ + public: + PlaceSolWriter(PlaceDB const& db) : m_db(db) {} + PlaceSolWriter(PlaceSolWriter const& rhs) : m_db(rhs.m_db) {} + PlaceSolWriter& operator=(PlaceSolWriter const& rhs); + protected: + PlaceDB const& m_db; +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h new file mode 100644 index 00000000..0857d5cd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h @@ -0,0 +1,189 @@ +/************************************************************************* + > File Name: Point.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Jun 2015 08:29:15 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_POINT_H +#define DREAMPLACE_POINT_H + +#include "Util.h" +#include + +DREAMPLACE_BEGIN_NAMESPACE + +template +class Point +{ + public: + typedef T coordinate_type; + + ///==== constructors ==== + Point(coordinate_type x = 0, coordinate_type y = 0) + { + set(x, y); + } + + Point(Point const& rhs) + { + copy(rhs); + } + + Point& operator=(Point const& rhs) + { + copy(rhs); + return *this; + } + + template + explicit Point(PointType const& rhs) + { + copy(rhs); + } + + template + Point& operator=(PointType const& rhs) + { + copy(rhs); + return *this; + } + + ///==== public member functions ==== + Point& set(coordinate_type x, coordinate_type y) + { + m_coords[kX] = x; + m_coords[kY] = y; + return *this; + } + Point& set(Direction1DType d, coordinate_type v) + { + m_coords[d] = v; + return *this; + } + coordinate_type get(Direction1DType d) const + { + return m_coords[d]; + } + coordinate_type x() const + { + return m_coords[kX]; + } + coordinate_type y() const + { + return m_coords[kY]; + } + + ///==== overload operators ==== + bool operator==(Point const& rhs) const + { + return (m_coords[0] == rhs.m_coords[0]) && + (m_coords[1] == rhs.m_coords[1]); + } + + bool operator!=(Point const& rhs) const + { + return !(*this == rhs); + } + + bool operator<(Point const& rhs) const + { + return (m_coords[0] < rhs.m_coords[0]) || + ((m_coords[0] == rhs.m_coords[0]) && + (m_coords[1] < rhs.m_coords[1])); + } + + bool operator<=(Point const& rhs) const + { + return !(rhs < *this); + } + + bool operator>(Point const& rhs) const + { + return rhs < *this; + } + + bool operator>=(Point const& rhs) const + { + return !(*this < rhs); + } + + Point& operator+=(Point const& rhs) + { + m_coords[kX] += rhs.get(kX); + m_coords[kY] += rhs.get(kY); + return *this; + } + Point& operator-=(Point const& rhs) + { + m_coords[kX] -= rhs.get(kX); + m_coords[kY] -= rhs.get(kY); + return *this; + } + + protected: + template + void copy(PointType const& rhs) + { + m_coords[0] = rhs.m_coords[0]; + m_coords[1] = rhs.m_coords[1]; + } + + coordinate_type m_coords[2]; ///< x and y values +}; + +/// \return manhattan distance of two points in 1D direction +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Point const& p1, Point const& p2, Direction1DType d) +{ + return limbo::abs(p1.get(d) - p2.get(d)); +} + +/// \return manhattan distance of two points +template +inline typename coordinate_traits::manhattan_distance_type +manhattanDistance(Point const& p1, Point const& p2) +{ + return manhattanDistance(p1, p2, kX) + manhattanDistance(p1, p2, kY); +} + +/// \return squared euclidean distance of two points +template +inline typename coordinate_traits::euclidean_distance_type +squareDistance(Point const& p1, Point const& p2) +{ + typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; + return (euclidean_distance_type)( + pow(manhattanDistance(p1, p2, kX), 2) + + pow(manhattanDistance(p1, p2, kY), 2) + ); +} + +/// \return euclidean distance of two points +template +inline typename coordinate_traits::euclidean_distance_type +euclideanDistance(Point const& p1, Point const& p2) +{ + return sqrt(squareDistance(p1, p2)); +} + +/// overload operator+ +template +inline Point operator+(Point const& rhs1, Point const& rhs2) +{ + Point p (rhs1); + return (p += rhs2); +} + +/// overload operator- +template +inline Point operator-(Point const& rhs1, Point const& rhs2) +{ + Point p (rhs1); + return (p -= rhs2); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp new file mode 100644 index 00000000..c868bb4f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp @@ -0,0 +1,580 @@ +/** + * @file PyPlaceDB.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Mar 2021 + * @brief Placement database for python + */ +#include +#include +#include +#include "PyPlaceDB.h" +#include + +DREAMPLACE_BEGIN_NAMESPACE + +const int INVALID = -1; + +bool readBookshelf(PlaceDB& db, std::string const& auxPath) +{ + // read bookshelf + if (!auxPath.empty()) + { + std::string const& filename = auxPath; + dreamplacePrint(kINFO, "reading %s\n", filename.c_str()); + bool flag = BookshelfParser::read(db, filename); + if (!flag) + { + dreamplacePrint(kERROR, "Bookshelf file parsing failed: %s\n", filename.c_str()); + return false; + } + ////DBG + //else + //{ + // std::cout << "Bookshelf file parsing successful " << std::endl; + //} + ////DBG + } + else dreamplacePrint(kWARN, "no Bookshelf file specified\n"); + + return true; +} + +void PyPlaceDB::set(PlaceDB const& db) +{ + num_ccNodes = db.numCCNodes(); //macros + num_terminals = db.numFixed(); //IOs + num_movable_nodes = db.numMovable(); // Movable cells + num_physical_nodes = num_terminals + num_movable_nodes; + + node_count = pybind11::cast(std::move(db.node_count())); + + //Node Info + node_names = pybind11::cast(std::move(db.nodeNames())); + node_types = pybind11::cast(std::move(db.nodeTypes())); + node_size_x = pybind11::cast(std::move(db.nodeXSizes())); + node_size_y = pybind11::cast(std::move(db.nodeYSizes())); + node2fence_region_map = pybind11::cast(std::move(db.node2FenceRegionMap())); + node_x = pybind11::cast(std::move(db.nodeXLocs())); + node_y = pybind11::cast(std::move(db.nodeYLocs())); + node_z = pybind11::cast(std::move(db.nodeZLocs())); + flop_indices = pybind11::cast(std::move(db.flopIndices())); + lut_type = pybind11::cast(std::move(db.lutTypes())); + cluster_lut_type = pybind11::cast(std::move(db.clusterlutTypes())); + + ////DBG CHECK contents of lut_type and cluster_lut_type + //for (int i = 0; i < 10; ++i) + //{ + // std::cout << "Instance: " << *node_names[i] << " of type: " << *node_types[i] << " has lut type: " << *lut_type[i]; + // std::cout << " and cluster_lut_type: " << *cluster_lut_type[i] << std::endl; + //} + ////DBG + + //Carry chain Info + if (num_ccNodes > 0) + { + org_num_movable_nodes = db.numOrgMovable(); // Movable cells + org_node_names = pybind11::cast(std::move(db.orgNodeNames())); + org_node_types = pybind11::cast(std::move(db.orgNodeTypes())); + org_node_size_x = pybind11::cast(std::move(db.orgNodeXSizes())); + org_node_size_y = pybind11::cast(std::move(db.orgNodeYSizes())); + org_node2fence_region_map = pybind11::cast(std::move(db.orgNode2FenceRegionMap())); + org_node_count = pybind11::cast(std::move(db.orgNodeCount())); + org_flop_indices = pybind11::cast(std::move(db.orgFlopIndices())); + org_lut_type = pybind11::cast(std::move(db.orgLutTypes())); + org_pin_offset_x = pybind11::cast(std::move(db.orgPinOffsetX())); + org_pin_offset_y = pybind11::cast(std::move(db.orgPinOffsetY())); + org_pin2nodeType_map = pybind11::cast(std::move(db.orgPin2NodeTypeMap())); + org_node2pincount_map = pybind11::cast(std::move(db.orgNode2PinCount())); + org_pin2node_map = pybind11::cast(std::move(db.orgPin2NodeMap())); + org_node2outpinCount = pybind11::cast(std::move(db.orgNode2OutPinCount())); + org_node2outpinIdx_map = pybind11::cast(std::move(db.orgNode2OutPinId())); + org_flat_node2pin_map = pybind11::cast(std::move(db.orgFlatNode2PinMap())); + org_flat_node2pin_start_map = pybind11::cast(std::move(db.orgFlatNode2PinStartMap())); + org_flat_cc2node_map = pybind11::cast(std::move(db.orgflatCCNodeMap())); + org_flat_cc2node_start_map = pybind11::cast(std::move(db.orgflatCCNodeStartMap())); + org_is_cc_node = pybind11::cast(std::move(db.isOrgCCNode())); + org_node_name2id_map = pybind11::cast(std::move(db.orgNodeName2Index())); + org_node_x = pybind11::cast(std::move(db.orgNodeXLocs())); + org_node_y = pybind11::cast(std::move(db.orgNodeYLocs())); + org_node_z = pybind11::cast(std::move(db.orgNodeZLocs())); + + cc_element_count = pybind11::cast(std::move(db.ccElementCount())); + node2ccId_map = pybind11::cast(std::move(db.node2CCIdMap())); + cc2nodeId_map = pybind11::cast(std::move(db.cc2nodeIdMap())); + org_node2ccId_map = pybind11::cast(std::move(db.orgNode2CCIdMap())); + new2org_node_map= pybind11::cast(std::move(db.new2OrgNodeMap())); + is_cc_node = pybind11::cast(std::move(db.isCCNode())); + //flat_cc_input_pins_map = pybind11::cast(std::move(db.flatCCInputPinsMap())); + //flat_cc_output_pins_map = pybind11::cast(std::move(db.flatCCOutputPinsMap())); + //flat_cc_input_pin_start_map = pybind11::cast(std::move(db.flatCCInputPinStartMap())); + //flat_cc_output_pin_start_map = pybind11::cast(std::move(db.flatCCOutputPinStartMap())); + //overall_cc_input_pin_start_map = pybind11::cast(std::move(db.overallCCInputPinStartMap())); + //overall_cc_output_pin_start_map = pybind11::cast(std::move(db.overallCCOutputPinStartMap())); + } + + node2outpinIdx_map = pybind11::cast(std::move(db.node2OutPinId())); + node2outpinCount = pybind11::cast(std::move(db.node2OutPinCount())); + node2pincount_map = pybind11::cast(std::move(db.node2PinCount())); + node2pin_map = pybind11::cast(std::move(db.node2PinMap())); + node_name2id_map = pybind11::cast(std::move(db.nodeName2Index())); + //movable_node_name2id_map = pybind11::cast(std::move(db.movNodeName2Index())); + //fixed_node_name2id_map = pybind11::cast(std::move(db.fixedNodeName2Index())); + flat_node2pin_map = pybind11::cast(std::move(db.flatNode2PinMap())); + flat_node2pin_start_map = pybind11::cast(std::move(db.flatNode2PinStartMap())); + + net_names = pybind11::cast(std::move(db.netNames())); + net2pincount_map = pybind11::cast(std::move(db.net2PinCount())); + net2pin_map = pybind11::cast(std::move(db.net2PinMap())); + flat_net2pin_map = pybind11::cast(std::move(db.flatNet2PinMap())); + flat_net2pin_start_map = pybind11::cast(std::move(db.flatNet2PinStartMap())); + net_name2id_map = pybind11::cast(std::move(db.netName2Index())); + + pin_names = pybind11::cast(std::move(db.pinNames())); + pin_types = pybind11::cast(std::move(db.pinTypes())); + pin_typeIds = pybind11::cast(std::move(db.pinTypeIds())); + pin_offset_x = pybind11::cast(std::move(db.pinOffsetX())); + pin_offset_y = pybind11::cast(std::move(db.pinOffsetY())); + pin2net_map = pybind11::cast(std::move(db.pin2NetMap())); + pin2node_map = pybind11::cast(std::move(db.pin2NodeMap())); + pin2nodeType_map = pybind11::cast(std::move(db.pin2NodeTypeMap())); + + //////DBG + //std::cout << "There are " << std::to_string(num_physical_nodes) << " nodes, " << std::to_string(db.numPins()) + // << " pins and " << std::to_string(db.numNets()) << " nets and " << std::to_string(db.numCCNodes()) + // << " macros in the design" << std::endl; + //////DBG + + //CtrlSets + sliceFFCtrls = pybind11::cast(std::move(db.slice_FF_ctrls())); + sliceUnitFFCtrls = pybind11::cast(std::move(db.sliceUnit_FF_ctrls())); + + //TODO - Make FF Ctrl signal generation part generic for any architecture + //Currently only US and Stratix-IV architectures supported + if (db.slice_ff_ctrl_mode() == "HALF") + { + //Xilinx US and related + std::unordered_map ceMapping; + std::unordered_map > cksrMapping; + PlaceDB::index_type numCKSR(0), numCE(0); + + for (unsigned int sFIdx = 0; sFIdx < db.numFF(); ++sFIdx) + { + unsigned int fIdx = db.flopIndex(sFIdx); + + int ck(INVALID), sr(INVALID), ce(INVALID), cksrId(INVALID), ceId(INVALID); + + //for (auto pin_id : node.pinIdArray()) + for (unsigned int pIdx = 0; pIdx < db.node2PinCnt(fIdx); ++pIdx) + { + PlaceDB::index_type pin_id = db.node2PinIdx(fIdx, pIdx); + + switch(pin_typeIds[pin_id].cast()) + { + case 2: + { + ck = pin2net_map[pin_id].cast(); + break; + } + case 3: + { + ce = pin2net_map[pin_id].cast(); + break; + } + case 4: + { + sr = pin2net_map[pin_id].cast(); + break; + } + default: + { + break; + } + } + } + + auto ckIt = cksrMapping.find(ck); + + if (ckIt == cksrMapping.end()) + { + cksrId = numCKSR; + cksrMapping[ck][sr] = numCKSR++; + } else + { + auto &srMap = ckIt->second; + auto srIt = srMap.find(sr); + if (srIt == srMap.end()) + { + cksrId = numCKSR; + srMap[sr] = numCKSR++; + } else + { + cksrId = srIt->second; + } + } + + auto ceIt = ceMapping.find(ce); + if (ceIt == ceMapping.end()) + { + ceId = numCE; + ceMapping[ce] = numCE++; + } else + { + ceId = ceIt->second; + } + ctrlSets.append(std::make_tuple(fIdx, cksrId, ceId)); + if (num_ccNodes > 0) + { + org_ctrlSets.append(std::make_tuple(db.orgNodeMap(fIdx), cksrId, ceId)); + } + } + } else + { + //Intel Stratix-IV and related + std::unordered_map clkMapping; + PlaceDB::index_type numCTRL(0), numCLK(0); + + int ctrl_signal_count = db.slice_FF_ctrl_count(1); + dreamplaceAssertMsg((ctrl_signal_count == 7), "Check if correct Ctrl signal count is provided"); + + std::vector ff_ctrls; + + ext_ctrlSet_start_map.append(0); + if (num_ccNodes > 0) + { + org_ext_ctrlSet_start_map.append(0); + } + + for (unsigned int sFIdx = 0; sFIdx < db.numFF(); ++sFIdx) + { + unsigned int fIdx = db.flopIndex(sFIdx); + + int ck(INVALID), ckId(INVALID); + + std::vector curVal(10, INVALID); + + for (unsigned int pIdx = 0; pIdx < db.node2PinCnt(fIdx); ++pIdx) + { + PlaceDB::index_type pin_id = db.node2PinIdx(fIdx, pIdx); + PlaceDB::index_type pinTypeId = pin_typeIds[pin_id].cast(); + + if(pinTypeId > 1 && pinTypeId < 10) + { + curVal[pinTypeId] = pin2net_map[pin_id].cast(); + + if (pinTypeId == 2) //CLK + { + ck = curVal[pinTypeId]; + } else if (curVal[pinTypeId] != INVALID) + { + ff_ctrls.emplace_back(curVal[pinTypeId]); + } + } + } + ext_ctrlSet_start_map.append(ff_ctrls.size()); + if (num_ccNodes > 0) + { + org_ext_ctrlSet_start_map.append(ff_ctrls.size()); + } + + auto ckIt = clkMapping.find(ck); + if (ckIt == clkMapping.end()) + { + ckId = numCLK; + clkMapping[ck] = numCLK++; + } else + { + ckId = ckIt->second; + } + + ctrlSets.append(std::make_tuple(fIdx, ckId, 0)); + if (num_ccNodes > 0) + { + org_ctrlSets.append(std::make_tuple(db.orgNodeMap(fIdx), ckId, 0)); + } + } + extended_ctrlSets = pybind11::cast(std::move(ff_ctrls)); + if (num_ccNodes > 0) + { + org_extended_ctrlSets = pybind11::cast(std::move(ff_ctrls)); + } + + dreamplacePrint(kINFO, "Design has %d unique clk signal(s) and multiple ctrl_mode combinations\n", numCLK); + } + + //SiteInfo + siteTypes = pybind11::cast(std::move(db.site_types())); + siteWidths = pybind11::cast(std::move(db.site_widths())); + siteHeights = pybind11::cast(std::move(db.site_heights())); + rsrcTypes = pybind11::cast(std::move(db.rsrc_types())); + rsrcInstWidths = pybind11::cast(std::move(db.rsrc_inst_widths())); + rsrcInstHeights = pybind11::cast(std::move(db.rsrc_inst_heights())); + siteResources = pybind11::cast(std::move(db.site_resources_map())); + rsrcInsts = pybind11::cast(std::move(db.rsrc_insts_map())); + rsrc2siteMap = pybind11::cast(std::move(db.rsrc2site_map())); + inst2rsrcMap = pybind11::cast(std::move(db.inst2rsrc_map())); + siteRsrc2CountMap = pybind11::cast(std::move(db.site_rsrc2count_map())); + siteType2indexMap = pybind11::cast(std::move(db.site_type2index_map())); + rsrcType2indexMap = pybind11::cast(std::move(db.rsrc_type2index_map())); + rsrcInstType2indexMap = pybind11::cast(std::move(db.rsrc_inst_type2index_map())); + sliceElements = pybind11::cast(std::move(db.slice_elements())); + rsrcInstTypes = pybind11::cast(std::move(db.rsrc_inst_types())); + lutFracturesMap = pybind11::cast(std::move(db.lut_fractures_map())); + siteOutCoordinates = pybind11::cast(std::move(db.site_out_coordinates())); + siteOutValues = pybind11::cast(std::move(db.site_out_values())); + + xl = db.xl(); + yl = db.yl(); + xh = db.xh(); + yh = db.yh(); + + //Initialize site_type2index values that are used + sliceIdx = INVALID; + ioIdx = INVALID; + bramIdx = INVALID; + m9kIdx = INVALID; + m144kIdx = INVALID; + dspIdx = INVALID; + pllIdx = INVALID; + emptyIdx = INVALID; + + if (db.site_type2index_map().find("SLICE") != db.site_type2index_map().end()) + { + sliceIdx = db.site_type2index("SLICE"); + } + if (db.site_type2index_map().find("io") != db.site_type2index_map().end()) + { + ioIdx = db.site_type2index("io"); + } else if(db.site_type2index_map().find("IO") != db.site_type2index_map().end()) + { + ioIdx = db.site_type2index("IO"); + } + if (db.site_type2index_map().find("BRAM") != db.site_type2index_map().end()) + { + bramIdx = db.site_type2index("BRAM"); + } + if (db.site_type2index_map().find("M9K") != db.site_type2index_map().end()) + { + m9kIdx = db.site_type2index("M9K"); + } + if (db.site_type2index_map().find("M144K") != db.site_type2index_map().end()) + { + m144kIdx = db.site_type2index("M144K"); + } + if (db.site_type2index_map().find("DSP") != db.site_type2index_map().end()) + { + dspIdx = db.site_type2index("DSP"); + } + if (db.site_type2index_map().find("PLL") != db.site_type2index_map().end()) + { + pllIdx = db.site_type2index("PLL"); + } + if (db.site_type2index_map().find("EMPTY") != db.site_type2index_map().end()) + { + emptyIdx = db.site_type2index("EMPTY"); + } + + typedef Box box_type; + std::vector > region_boxes(db.site_types().size()+1); + + int maxVal = std::max(db.siteRows(), db.siteCols()); + + for (int i = 0, ie = db.siteRows(); i < ie; ++i) + { + pybind11::list rowVals, lg_rowXY; + for (int j = 0, je = db.siteCols(); j < je; ++j) + { + pybind11::list siteXY, lg_Site; + + if (db.siteVal(i,j) == sliceIdx) + { + lg_Site.append(i+0.5); + lg_Site.append(j+0.5); + } else + { + lg_Site.append(i); + lg_Site.append(j); + } + lg_rowXY.append(lg_Site); + if(db.siteVal(i,j) != 0 && db.siteVal(i,j) != emptyIdx) + { + if (db.siteVal(i,j) == sliceIdx) + { + siteXY.append(i); + siteXY.append(j); + sliceSiteXYs.append(siteXY); + + int siteW = int(db.site_width(sliceIdx)); + int siteH = int(db.site_height(sliceIdx)); + Box slicebox(i, j, i+siteW, j+siteH); + + if (region_boxes[sliceIdx].size() == 0) + { + region_boxes[sliceIdx].emplace_back(slicebox); + } else + { + if (!mergeBoxes(slicebox, region_boxes[sliceIdx].back(), db.site_per_column())) + { + region_boxes[sliceIdx].emplace_back(slicebox); + } + } + } + else if (db.siteVal(i,j) == dspIdx || db.siteVal(i,j) == bramIdx || db.siteVal(i,j) == m9kIdx || db.siteVal(i,j) == m144kIdx) + { + double siteHeight = db.site_height(db.siteVal(i,j)); + siteXY.append(i); + + int siteW = int(db.site_width(db.siteVal(i,j))); + int siteH = int(siteHeight); + Box drbox(i, j, i+siteW, j+siteH); + + if (std::floor(siteHeight) == siteHeight) + { + siteXY.append(j); + } else + { + siteXY.append(std::round(j/siteHeight)*siteHeight); + drbox.set(i, int(std::round(j/siteHeight)*siteHeight), i+siteW, int(std::round((j+siteH)/siteHeight)*siteHeight)); + } + if (region_boxes[db.siteVal(i,j)].size() == 0) + { + region_boxes[db.siteVal(i,j)].emplace_back(drbox); + } else + { + if (!mergeBoxes(drbox, region_boxes[db.siteVal(i,j)].back(), db.site_per_column())) + { + region_boxes[db.siteVal(i,j)].emplace_back(drbox); + } + } + + if (db.siteVal(i,j) == dspIdx) + { + dspSiteXYs.append(siteXY); + } else if (db.siteVal(i,j) == bramIdx || db.siteVal(i,j) == m9kIdx) + { + ramSite0XYs.append(siteXY); + } else + { + ramSite1XYs.append(siteXY); + } + } else if (db.siteVal(i,j) == ioIdx) + { + int siteW = int(db.site_width(ioIdx)); + int siteH = int(db.site_height(ioIdx)); + Box iobox(i, j, i+siteW, j+siteH); + + if (region_boxes[ioIdx].size() == 0) + { + region_boxes[ioIdx].emplace_back(iobox); + } else + { + if (!mergeBoxes(iobox, region_boxes[ioIdx].back(), db.site_per_column())) + { + region_boxes[ioIdx].emplace_back(iobox); + } + } + } else if (db.siteVal(i,j) == pllIdx) + { + int siteW = int(db.site_width(pllIdx)); + int siteH = int(db.site_height(pllIdx)); + Box pllbox(i, j, i+siteW, j+siteH); + + if (region_boxes[pllIdx].size() == 0) + { + region_boxes[pllIdx].emplace_back(pllbox); + } else + { + if (!mergeBoxes(pllbox, region_boxes[pllIdx].back(), db.site_per_column())) + { + region_boxes[pllIdx].emplace_back(pllbox); + } + } + } + } + + rowVals.append(db.siteVal(i,j)); + } + site_type_map.append(rowVals); + lg_siteXYs.append(lg_rowXY); + } + + //Update flat_region_boxes and flat_region_boxes_start using region_boxes + unsigned int flat_len = 0; + flat_region_boxes_start.append(flat_len); + for (unsigned int rgn = 0; rgn < db.rsrc_types().size(); ++rgn) + { + int site_type = db.site_type2index(db.rsrc_type2site(db.rsrc_type(rgn))); + for (auto el : region_boxes[site_type]) + { + pybind11::list flat_region; + flat_region.append(el.xl()); + flat_region.append(el.yl()); + flat_region.append(el.xh()); + + if (db.site_per_column() == 1) + { + flat_region.append(yh); + } else + { + flat_region.append(el.yh()); + } + + flat_region_boxes.append(flat_region); + flat_len += 1; + } + flat_region_boxes_start.append(flat_len); + } + + + num_sites_x = db.siteRows(); + num_sites_y = db.siteCols(); + + // routing information initialized + num_routing_grids_x = db.width(); + num_routing_grids_y = db.height(); + routing_grid_xl = xl; + routing_grid_yl = yl; + routing_grid_xh = xh; + routing_grid_yh = yh; + + ff_ctrl_type = db.ff_ctrl_type(); + wl_weightX = db.wl_weight_x(); + wl_weightY = db.wl_weight_y(); + sliceFF_ctrl_mode = db.slice_ff_ctrl_mode(); + lut_maxShared = db.lut_shared_max_pins(); + lut_type_in_sliceUnit = db.lut_type_in_sliceUnit(); + pinRouteCap = db.pin_route_cap(); + routeCapH = db.route_cap_h(); + routeCapV = db.route_cap_v(); + + ////Spiral Accessor + unsigned int rad = std::max(num_sites_x, num_sites_y); + spiral_maxVal = (2 * rad * (1+rad)) +1; + spiral_accessor.append(std::make_tuple(0, 0)); + + for(int r = 1; r <= rad; ++r) + { + // The 1st quadrant + for (int x = r, y = 0; y < r; --x, ++y) + { + spiral_accessor.append(std::make_tuple(x, y)); + } + // The 2nd quadrant + for (int x = 0, y = r; y > 0; --x, --y) + { + spiral_accessor.append(std::make_tuple(x, y)); + } + // The 3rd quadrant + for (int x = -r, y = 0; y > -r; ++x, --y) + { + spiral_accessor.append(std::make_tuple(x, y)); + } + // The 4th quadrant + for (int x = 0, y = -r; y < 0; ++x, ++y) + { + spiral_accessor.append(std::make_tuple(x, y)); + } + } +} + +DREAMPLACE_END_NAMESPACE + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h new file mode 100644 index 00000000..f434c09b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h @@ -0,0 +1,228 @@ +/** + * @file PyPlaceDB.h + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Mar 2021 + * @brief Placement database for python + */ + +#ifndef _DREAMPLACE_PLACE_IO_PYPLACEDB_H +#define _DREAMPLACE_PLACE_IO_PYPLACEDB_H + +//#include +#include +#include +#include +//#include +#include "PlaceDB.h" +#include "Iterators.h" +#include "Box.h" +#include "utility/src/torch.h" + +DREAMPLACE_BEGIN_NAMESPACE + +bool readBookshelf(PlaceDB& db, std::string const& auxPath); + +/// database for python +struct PyPlaceDB +{ + pybind11::list node_names; ///< 1D array, cell name + pybind11::list node_size_x; ///< 1D array, cell width + pybind11::list node_size_y; ///< 1D array, cell height + pybind11::list node_types; ///< 1D array, nodeTypes(FPGA) + pybind11::list flop_indices; ///< 1D array, nodeTypes(FPGA) + //pybind11::list lut_indices; ///< 1D array, nodeTypes(FPGA) + //pybind11::list flop_lut_indices; ///< 1D array, nodeTypes(FPGA) + //pybind11::list dsp_indices; ///< 1D array, nodeTypes(FPGA) + //pybind11::list ram_indices; ///< 1D array, nodeTypes(FPGA) + //pybind11::list dsp_ram_indices; ///< 1D array, nodeTypes(FPGA) + pybind11::list node2fence_region_map; ///< only record fence regions for each cell + + pybind11::list node_x; ///< 1D array, cell position x + pybind11::list node_y; ///< 1D array, cell position y + pybind11::list node_z; ///< 1D array, cell position z (FPGA) + pybind11::list node2pin_map; ///< array of 1D array, contains pin id of each node + pybind11::list flat_node2pin_map; ///< flatten version of node2pin_map + pybind11::list flat_node2pin_start_map; ///< starting index of each node in flat_node2pin_map + pybind11::list node2pincount_map; ///< array of 1D array, number of pins in node + pybind11::list net2pincount_map; ///< array of 1D array, number of pins in net + pybind11::list node2outpinIdx_map; ///< array of 1D array, output pin idx of each node + pybind11::list node2outpinCount; ///< array of 1D array, output pin count of each node + pybind11::list lut_type; ///< 1D array, nodeTypes(FPGA) + pybind11::list cluster_lut_type; ///< 1D array, LUT types for clustering + pybind11::dict node_name2id_map; ///< node name to id map, cell name + + //pybind11::dict movable_node_name2id_map; ///< node name to id map, cell name + //pybind11::dict fixed_node_name2id_map; ///< node name to id map, cell name + //pybind11::list fixedNodes; ///< 1D array, nodeTypes(FPGA) + unsigned int num_terminals; ///< number of terminals, essentially IOs + unsigned int num_movable_nodes; ///< number of movable nodes + unsigned int num_physical_nodes; ///< number of movable nodes + terminals (FPGA) + pybind11::list node_count; ///< 1D array, count of resource types + + unsigned int num_ccNodes; ///< number of carry chain nodes in design (FPGA) + pybind11::list cc_element_count; ///< No of elements in all the carry chains + pybind11::list is_cc_node; ///< Specifies if node is part of carry chain + pybind11::list node2ccId_map; ///< Node id to carry chain indexing + pybind11::list cc2nodeId_map; ///< carry chain id to node indexing + //pybind11::list flat_cc2node_map; ///< flat cc2node map + //pybind11::list flat_cc2node_start_map; ///< flat cc2node start map + //pybind11::list carry_chain_nets; ///< cc nets + //unsigned int num_carry_chains; ///< number of carry chain nodes in design (FPGA) + + unsigned int org_num_movable_nodes; ///< number of movable nodes + pybind11::dict org_node_name2id_map; ///< node name to id map, cell name + pybind11::list org_node_names; ///< 1D array, cell name + pybind11::list org_node_types; ///< 1D array, nodeTypes(FPGA) + pybind11::list org_node_size_x; ///< 1D array, cell width + pybind11::list org_node_size_y; ///< 1D array, cell width + pybind11::list org_node2fence_region_map; ///< only record fence regions for each cell + pybind11::list org_node_count; ///< count based on node type + pybind11::list org_flop_indices; ///< 1D array, nodeTypes(FPGA) + pybind11::list org_lut_type; ///< 1D array, nodeTypes(FPGA) + pybind11::list org_pin_offset_x; ///< 1D array, pin offset x to its node + pybind11::list org_pin_offset_y; ///< 1D array, pin offset y to its node + pybind11::list org_pin2nodeType_map; ///< 1D array, pin to node type + pybind11::list org_node2pincount_map; ///< array of 1D array, number of pins in node + pybind11::list org_pin2node_map; ///< 1D array, contain parent node id of each pin + pybind11::list org_node2outpinCount; ///< array of 1D array, output pin count of each node + pybind11::list org_node2outpinIdx_map; ///< array of 1D array, output pin idx of each node + pybind11::list org_flat_node2pin_map; ///< flatten version of node2pin_map + pybind11::list org_flat_node2pin_start_map; ///< starting index of each node in flat_node2pin_map + pybind11::list org_flat_cc2node_map; ///< flat cc2node map + pybind11::list org_flat_cc2node_start_map; ///< flat cc2node start map + pybind11::list org_is_cc_node; ///< Specifies if node is part of carry chain + pybind11::list org_node2ccId_map; ///< Node id to carry chain indexing + pybind11::list org_node_x; ///< 1D array, cell position x + pybind11::list org_node_y; ///< 1D array, cell position y + pybind11::list org_node_z; ///< 1D array, cell position z (FPGA) + pybind11::list new2org_node_map; ///< Node id to org node id mapping + pybind11::list org_ctrlSets; ///< 1D array, FF ctrl set (FPGA) + pybind11::list org_extended_ctrlSets; ///< 1D array, FF ctrl signals (FPGA) + pybind11::list org_ext_ctrlSet_start_map; ///< 1D array, FF ctrl set start map (FPGA) + //pybind11::list flat_cc_input_pins_map; ///< flat carry chain node input pin mapping + //pybind11::list flat_cc_output_pins_map; ///< flat carry chain node output pin mapping + //pybind11::list flat_cc_input_pin_start_map; ///< flat carry chain node input pin mapping + //pybind11::list flat_cc_output_pin_start_map; ///< flat carry chain node output pin mapping + //pybind11::list overall_cc_input_pin_start_map; ///< overall carry chain node input pin mapping + //pybind11::list overall_cc_output_pin_start_map; ///< overall carry chain node output pin mapping + + pybind11::list pin_offset_x; ///< 1D array, pin offset x to its node + pybind11::list pin_offset_y; ///< 1D array, pin offset y to its node + pybind11::list pin_names; ///< 1D array, pin names (FPGA) + pybind11::list pin_types; ///< 1D array, pin types (FPGA) + pybind11::list pin_typeIds; ///< 1D array, pin types (FPGA) + pybind11::list pin2node_map; ///< 1D array, contain parent node id of each pin + pybind11::list pin2net_map; ///< 1D array, contain parent net id of each pin + pybind11::list pin2nodeType_map; ///< 1D array, pin to node type + + pybind11::list net_names; ///< net name + pybind11::list net2pin_map; ///< array of 1D array, each row stores pin id + pybind11::list flat_net2pin_map; ///< flatten version of net2pin_map + pybind11::list flat_net2pin_start_map; ///< starting index of each net in flat_net2pin_map + pybind11::dict net_name2id_map; ///< net name to id map + //pybind11::list net_weights; ///< net weight + + int num_sites_x; ///< number of sites in horizontal direction (FPGA) + int num_sites_y; ///< number of sites in vertical direction (FPGA) + pybind11::list siteTypes; ///< 1D array of site types + pybind11::list siteWidths; ///< 1D array of site widths + pybind11::list siteHeights; ///< 1D array of site heights + pybind11::list rsrcTypes; ///< 1D array of rsrc types + pybind11::list rsrcInstWidths; ///< 1D array of rsrc Inst widths + pybind11::list rsrcInstHeights; ///< 1D array of rsrc Inst heights + pybind11::list siteResources; ///< 2D array of site resources + pybind11::list rsrcInsts; ///< 2D array of resource instances + pybind11::list rsrcInstTypes; ///< 1D array of resource instances + pybind11::dict rsrc2siteMap; ///< rsrc to site map + pybind11::dict inst2rsrcMap; ///< inst to rsrc map + pybind11::dict siteRsrc2CountMap; ///< site rsrc to count map + pybind11::dict siteType2indexMap; ///< site type to id map + pybind11::dict rsrcType2indexMap; ///< rsrc type to id map + pybind11::dict rsrcInstType2indexMap; ///< rsrc inst type to id map + pybind11::list sliceElements; ///< 1D array of pairs - rsrc type and count + pybind11::list lutFracturesMap; ///< 2D array of LUT fractures + pybind11::list sliceFFCtrls; ///< 1D array of pairs - slice ff ctrls + pybind11::list sliceUnitFFCtrls; ///< 1D array of pairs - slice unit ff ctrls + pybind11::list siteOutCoordinates; ///< 1D array of site output coordinates + pybind11::list siteOutValues; ///< 1D array of site output values + pybind11::list site_type_map; ///< 2D array, site type of each site (FPGA) + pybind11::list lg_siteXYs; ///< 2D array, site XYs for CLB at center (FPGA) + //pybind11::list regions; ///< array of 1D array, each region contains rectangles + pybind11::list dspSiteXYs; ///< 1D array of DSP sites (FPGA) + pybind11::list ramSite0XYs; ///< 1D array of RAM sites (FPGA) + pybind11::list ramSite1XYs; ///< 1D array of RAM sites (FPGA) + pybind11::list sliceSiteXYs; ///< 1D array of Slice sites (FPGA) + //pybind11::list regionsLimits; ///< array of 1D array, each region contains rectangles + pybind11::list flat_region_boxes; ///< flatten version of regions + pybind11::list flat_region_boxes_start; ///< starting index of each region in flat_region_boxes + + pybind11::list spiral_accessor; ///< spiral accessor + + pybind11::list ctrlSets; ///< 1D array, FF ctrl set (FPGA) + pybind11::list extended_ctrlSets; ///< 1D array, FF ctrl signals (FPGA) + pybind11::list ext_ctrlSet_start_map; ///< 1D array, FF ctrl set start map (FPGA) + //pybind11::list flat_ctrlSets; ///< 1D array, FF ctrl set (FPGA) + //unsigned int num_nodes; ///< number of nodes, including terminals and terminal_NIs + unsigned int spiral_maxVal; ///< maxVal in spiral_accessor + unsigned int num_routing_grids_x; ///< number of routing grids in x + unsigned int num_routing_grids_y; ///< number of routing grids in y + int routing_grid_xl; ///< routing grid region may be different from placement region + int routing_grid_yl; + int routing_grid_xh; + int routing_grid_yh; + int xl; + int yl; + int xh; + int yh; + + std::string ff_ctrl_type; + double wl_weightX; + double wl_weightY; + std::string sliceFF_ctrl_mode; + int lut_maxShared; + int lut_type_in_sliceUnit; + int pinRouteCap; + int routeCapH; + int routeCapV; + + //Site type Identifier + int sliceIdx; + int ioIdx; + int bramIdx; + int m9kIdx; + int m144kIdx; + int dspIdx; + int pllIdx; + int emptyIdx; + + //pybind11::list node2orig_node_map; ///< due to some fixed nodes may have non-rectangular shapes, we flat the node list; + // ///< this map maps the new indices back to the original ones + //pybind11::list pin_direct; ///< 1D array, pin direction IO + //pybind11::list rows; ///< NumRows x 4 array, stores xl, yl, xh, yh of each row + //pybind11::list node_count; ///< Node count based on resource type (FPGA) + //pybind11::list unit_horizontal_capacities; ///< number of horizontal tracks of layers per unit distance + //pybind11::list unit_vertical_capacities; /// number of vertical tracks of layers per unit distance + //pybind11::list initial_horizontal_demand_map; ///< initial routing demand from fixed cells, indexed by (layer, grid x, grid y) + //pybind11::list initial_vertical_demand_map; ///< initial routing demand from fixed cells, indexed by (layer, grid x, grid y) + //pybind11::list binCapMaps; ///< array of 2D array, Bin Capacity map for all resource types (FPGA) + //pybind11::list fixedDemandMaps; ///< array of 2D array, Bin Capacity map for all resource types (FPGA) + //double total_space_area; ///< total placeable space area excluding fixed cells. + // ///< This is not the exact area, because we cannot exclude the overlapping fixed cells within a bin. + //int num_movable_pins; + + PyPlaceDB() + { + } + + PyPlaceDB(PlaceDB const& db) + { + set(db); + } + + void set(PlaceDB const& db); +}; + +DREAMPLACE_END_NAMESPACE + +#endif + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp new file mode 100644 index 00000000..20fcbd79 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp @@ -0,0 +1,258 @@ +/** + * @file PybindPlaceDB.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Mar 2021 + * @brief Python binding for PlaceDB + */ + +#include "PyPlaceDB.h" + +PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector >); +//PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector>); + +PYBIND11_MAKE_OPAQUE(DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type); + +PYBIND11_MAKE_OPAQUE(std::vector>); +PYBIND11_MAKE_OPAQUE(std::vector>); +//PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); +PYBIND11_MAKE_OPAQUE(std::vector); +//PYBIND11_MAKE_OPAQUE(std::vector); + +void bind_PlaceDB(pybind11::module& m) +{ + pybind11::bind_vector >(m, "VectorBool"); + pybind11::bind_vector >(m, "VectorCoordinate", pybind11::buffer_protocol()); + pybind11::bind_vector >(m, "VectorIndex", pybind11::buffer_protocol()); + //pybind11::bind_vector > >(m, "2DVectorIndex", pybind11::buffer_protocol()); + //pybind11::bind_vector >(m, "VectorLong", pybind11::buffer_protocol()); + //pybind11::bind_vector >(m, "VectorULong", pybind11::buffer_protocol()); + //pybind11::bind_vector >(m, "VectorFloat", pybind11::buffer_protocol()); + //pybind11::bind_vector >(m, "VectorDouble", pybind11::buffer_protocol()); + pybind11::bind_vector >(m, "VectorString"); + + pybind11::bind_map(m, "MapString2Index"); + + // DREAMPLACE_NAMESPACE::Object.h + pybind11::class_ (m, "Object") + .def(pybind11::init<>()) + .def("id", &DREAMPLACE_NAMESPACE::Object::id) + .def("__str__", &DREAMPLACE_NAMESPACE::Object::toString) + ; + + // Box.h + pybind11::class_> (m, "BoxCoordinate") + .def(pybind11::init<>()) + .def(pybind11::init::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type>()) + .def("xl", &DREAMPLACE_NAMESPACE::Box::xl) + .def("yl", &DREAMPLACE_NAMESPACE::Box::yl) + .def("xh", &DREAMPLACE_NAMESPACE::Box::xh) + .def("yh", &DREAMPLACE_NAMESPACE::Box::yh) + .def("width", &DREAMPLACE_NAMESPACE::Box::width) + .def("height", &DREAMPLACE_NAMESPACE::Box::height) + .def("area", &DREAMPLACE_NAMESPACE::Box::area) + .def("__str__", &DREAMPLACE_NAMESPACE::Box::toString) + ; + pybind11::class_> (m, "BoxIndex") + .def(pybind11::init<>()) + .def(pybind11::init::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type>()) + .def("xl", &DREAMPLACE_NAMESPACE::Box::xl) + .def("yl", &DREAMPLACE_NAMESPACE::Box::yl) + .def("xh", &DREAMPLACE_NAMESPACE::Box::xh) + .def("yh", &DREAMPLACE_NAMESPACE::Box::yh) + .def("width", &DREAMPLACE_NAMESPACE::Box::width) + .def("height", &DREAMPLACE_NAMESPACE::Box::height) + .def("area", &DREAMPLACE_NAMESPACE::Box::area) + .def("__str__", &DREAMPLACE_NAMESPACE::Box::toString) + ; + pybind11::bind_vector> >(m, "VectorBoxCoordinate"); + pybind11::bind_vector> >(m, "VectorBoxIndex"); + + // DREAMPLACE_NAMESPACE::LibCell.h + pybind11::class_ (m, "LibCell") + .def(pybind11::init<>()) + .def("name", &DREAMPLACE_NAMESPACE::LibCell::name) + .def("id", &DREAMPLACE_NAMESPACE::LibCell::id) + .def("inputPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::inputPinArray) + .def("outputPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::outputPinArray) + .def("clkPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::clkPinArray) + .def("ctrlPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::ctrlPinArray) + .def("libCellPinName2Type", (DREAMPLACE_NAMESPACE::LibCell::string2index_map_type const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::libCellPinName2Type) + ; + pybind11::bind_vector >(m, "VectorLibCell"); + + // DREAMPLACE_NAMESPACE::PlaceDB.h + pybind11::class_ (m, "PlaceDB") + .def(pybind11::init<>()) + .def("nodeNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeNames) + .def("nodeName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeName) + .def("nodeTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeTypes) + .def("nodeType", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeType) + .def("nodeXLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXLocs) + .def("nodeX", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeX) + .def("nodeYLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYLocs) + .def("nodeY", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeY) + .def("nodeZLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeZLocs) + .def("nodeZ", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeZ) + .def("nodeXSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXSizes) + .def("nodeXSize", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXSize) + .def("nodeYSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYSizes) + .def("nodeYSize", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYSize) + .def("node2FenceRegionMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2FenceRegionMap) + .def("nodeFenceRegion", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeFenceRegion) + .def("node2OutPinId", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2OutPinId) + .def("node2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinCount) + .def("node2PinCnt", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinCnt) + .def("flopIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flopIndices) + .def("flopIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::flopIndex) + .def("lutIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lutIndices) + .def("lutIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::lutIndex) + .def("lutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lutTypes) + .def("clusterlutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::clusterlutTypes) + .def("node2OutPinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2OutPinCount) + .def("orgflatCCNodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgflatCCNodeMap) + .def("orgflatCCNodeStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgflatCCNodeStartMap) + .def("ccElementCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::ccElementCount) + .def("node2CCIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2CCIdMap) + .def("cc2nodeIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::cc2nodeIdMap) + .def("isCCNode", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::isCCNode) + //.def("flatCCInputPinsMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCInputPinsMap) + //.def("flatCCOutputPinsMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCOutputPinsMap) + //.def("flatCCInputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCInputPinStartMap) + //.def("flatCCOutputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCOutputPinStartMap) + //.def("overallCCInputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::overallCCInputPinStartMap) + //.def("overallCCOutputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::overallCCOutputPinStartMap) + .def("node2PinMap", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinMap) + .def("node2PinIdx", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinIdx) + .def("netNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::netNames) + .def("netName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::netName) + .def("net2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinCount) + .def("net2PinCnt", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinCnt) + .def("net2PinMap", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinMap) + .def("net2PinIdx", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinIdx) + .def("flatNet2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNet2PinMap) + .def("flatNet2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNet2PinStartMap) + .def("flatNode2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNode2PinMap) + .def("flatNode2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNode2PinStartMap) + .def("pinNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinNames) + .def("pinName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::pinName) + .def("pin2NetMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NetMap) + .def("pin2NodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NodeMap) + .def("pin2Node", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2Node) + .def("pin2NodeTypeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NodeTypeMap) + .def("pinTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinTypes) + .def("pinTypeIds", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinTypeIds) + .def("pinOffsetX", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinOffsetX) + .def("pinOffsetY", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinOffsetY) + .def("orgNodeNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeNames) + .def("orgNodeName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeName) + .def("orgNodeTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeTypes) + .def("orgNodeType", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeType) + .def("orgNodeXSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeXSizes) + .def("orgNodeYSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeYSizes) + .def("orgFlatNode2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlatNode2PinMap) + .def("orgFlatNode2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlatNode2PinStartMap) + .def("orgPin2NodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPin2NodeMap) + .def("orgPin2NodeTypeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPin2NodeTypeMap) + .def("orgNode2OutPinId", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2OutPinId) + .def("orgNode2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2PinCount) + .def("orgFlopIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlopIndices) + .def("orgFlopIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlopIndex) + .def("orgLutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgLutTypes) + .def("orgNode2OutPinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2OutPinCount) + .def("orgNode2FenceRegionMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2FenceRegionMap) + .def("orgNodeCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeCount) + .def("orgNode2CCIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2CCIdMap) + .def("orgNodeMap", (int const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeMap) + .def("isOrgCCNode", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::isOrgCCNode) + .def("orgPinOffsetX", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPinOffsetX) + .def("orgPinOffsetY", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPinOffsetY) + .def("orgNodeName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeName2Index) + .def("orgNodeXLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeXLocs) + .def("orgNodeYLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeYLocs) + .def("orgNodeZLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeZLocs) + .def("new2OrgNodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::new2OrgNodeMap) + .def("libCells", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::libCells) + .def("libCell", (DREAMPLACE_NAMESPACE::LibCell const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::libCell) + .def("siteRows", &DREAMPLACE_NAMESPACE::PlaceDB::siteRows) + .def("siteCols", &DREAMPLACE_NAMESPACE::PlaceDB::siteCols) + .def("siteVal", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::siteVal) + .def("dieArea", &DREAMPLACE_NAMESPACE::PlaceDB::dieArea) + .def("nodeName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeName2Index) + .def("libCellName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::libCellName2Index) + .def("netName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::netName2Index) + .def("site_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_types) + .def("site_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type) + .def("site_resources_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_resources_map) + .def("site_resource", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_resource) + .def("rsrc2site_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2string_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc2site_map) + .def("site_rsrc2count_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_rsrc2count_map) + .def("rsrc_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_types) + .def("rsrc_insts_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_insts_map) + .def("inst2rsrc_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2string_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::inst2rsrc_map) + .def("site_per_column", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_per_column) + .def("site_widths", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_widths) + .def("site_width", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_width) + .def("site_heights", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_heights) + .def("site_height", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_height) + .def("rsrc_inst_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_types) + .def("rsrc_inst_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_type) + .def("site_out_coordinates", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_coordinates) + .def("site_out_coordinate", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_coordinate) + .def("site_out_values", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_values) + .def("site_out_value", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_value) + .def("rsrc_inst_widths", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_widths) + .def("rsrc_inst_width", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_width) + .def("rsrc_inst_heights", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_heights) + .def("rsrc_inst_height", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_height) + .def("lut_fractures_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lut_fractures_map) + .def("lut_fracture", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::lut_fracture) + .def("site_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type2index_map) + .def("site_type2index", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(std::string) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type2index) + .def("rsrc_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_type2index_map) + .def("rsrc_type2index", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(std::string) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_type2index) + .def("rsrc_inst_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_type2index_map) + .def("slice_elements", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_elements) + .def("slice_FF_ctrls", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrls) + .def("slice_FF_ctrl_signal", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrl_signal) + .def("slice_FF_ctrl_count", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrl_count) + .def("sliceUnit_FF_ctrls", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrls) + .def("sliceUnit_FF_ctrl_signal", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrl_signal) + .def("sliceUnit_FF_ctrl_count", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrl_count) + .def("ff_ctrl_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::ff_ctrl_type) + .def("wl_weight_x", &DREAMPLACE_NAMESPACE::PlaceDB::wl_weight_x) + .def("wl_weight_y", &DREAMPLACE_NAMESPACE::PlaceDB::wl_weight_y) + .def("slice_ff_ctrl_mode", &DREAMPLACE_NAMESPACE::PlaceDB::slice_ff_ctrl_mode) + .def("lut_shared_max_pins", &DREAMPLACE_NAMESPACE::PlaceDB::lut_shared_max_pins) + .def("lut_type_in_sliceUnit", &DREAMPLACE_NAMESPACE::PlaceDB::lut_type_in_sliceUnit) + .def("pin_route_cap", &DREAMPLACE_NAMESPACE::PlaceDB::pin_route_cap) + .def("route_cap_h", &DREAMPLACE_NAMESPACE::PlaceDB::route_cap_h) + .def("route_cap_v", &DREAMPLACE_NAMESPACE::PlaceDB::route_cap_v) + .def("numMovable", &DREAMPLACE_NAMESPACE::PlaceDB::numMovable) + .def("numOrgMovable", &DREAMPLACE_NAMESPACE::PlaceDB::numOrgMovable) + .def("numFixed", &DREAMPLACE_NAMESPACE::PlaceDB::numFixed) + .def("numLibCell", &DREAMPLACE_NAMESPACE::PlaceDB::numLibCell) + .def("numLUT", &DREAMPLACE_NAMESPACE::PlaceDB::numLUT) + .def("numFF", &DREAMPLACE_NAMESPACE::PlaceDB::numFF) + .def("numCCNodes", &DREAMPLACE_NAMESPACE::PlaceDB::numCCNodes) + .def("numNets", &DREAMPLACE_NAMESPACE::PlaceDB::numNets) + .def("numPins", &DREAMPLACE_NAMESPACE::PlaceDB::numPins) + .def("designName", &DREAMPLACE_NAMESPACE::PlaceDB::designName) + .def("xl", &DREAMPLACE_NAMESPACE::PlaceDB::xl) + .def("yl", &DREAMPLACE_NAMESPACE::PlaceDB::yl) + .def("xh", &DREAMPLACE_NAMESPACE::PlaceDB::xh) + .def("yh", &DREAMPLACE_NAMESPACE::PlaceDB::yh) + .def("width", &DREAMPLACE_NAMESPACE::PlaceDB::width) + .def("height", &DREAMPLACE_NAMESPACE::PlaceDB::height) + ; +} + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp new file mode 100644 index 00000000..7627bbfc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp @@ -0,0 +1,161 @@ +/** + * @file PybindPyPlaceDB.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Mar 2021 + * @brief Python binding for PyPlaceDB + */ + +#include "PyPlaceDB.h" + +void bind_PyPlaceDB(pybind11::module& m) +{ + pybind11::class_(m, "PyPlaceDB") + .def(pybind11::init<>()) + .def_readwrite("node_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_names) + .def_readwrite("node_size_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_size_x) + .def_readwrite("node_size_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_size_y) + .def_readwrite("node_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_types) + .def_readwrite("flop_indices", &DREAMPLACE_NAMESPACE::PyPlaceDB::flop_indices) + .def_readwrite("node2fence_region_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2fence_region_map) + .def_readwrite("node_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_x) + .def_readwrite("node_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_y) + .def_readwrite("node_z", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_z) + .def_readwrite("node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2pin_map) + .def_readwrite("flat_node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_node2pin_map) + .def_readwrite("flat_node2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_node2pin_start_map) + .def_readwrite("node2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2pincount_map) + .def_readwrite("net2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net2pincount_map) + .def_readwrite("node2outpinIdx_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2outpinIdx_map) + .def_readwrite("node2outpinCount", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2outpinCount) + .def_readwrite("lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_type) + .def_readwrite("cluster_lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::cluster_lut_type) + .def_readwrite("node_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_name2id_map) + .def_readwrite("num_terminals", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_terminals) + .def_readwrite("num_movable_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_movable_nodes) + .def_readwrite("num_physical_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_physical_nodes) + .def_readwrite("node_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_count) + .def_readwrite("num_ccNodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_ccNodes) + .def_readwrite("cc_element_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::cc_element_count) + .def_readwrite("is_cc_node", &DREAMPLACE_NAMESPACE::PyPlaceDB::is_cc_node) + .def_readwrite("node2ccId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2ccId_map) + .def_readwrite("cc2nodeId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::cc2nodeId_map) + //.def_readwrite("flat_cc2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc2node_map) + //.def_readwrite("flat_cc2node_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc2node_start_map) + //.def_readwrite("carry_chain_nets", &DREAMPLACE_NAMESPACE::PyPlaceDB::carry_chain_nets) + //.def_readwrite("num_carry_chains", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_carry_chains) + .def_readwrite("org_node_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_name2id_map) + .def_readwrite("org_num_movable_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_num_movable_nodes) + .def_readwrite("org_node_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_names) + .def_readwrite("org_node_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_types) + .def_readwrite("org_node_size_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_size_x) + .def_readwrite("org_node_size_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_size_y) + .def_readwrite("org_node_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_x) + .def_readwrite("org_node_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_y) + .def_readwrite("org_node_z", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_z) + .def_readwrite("org_node2fence_region_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2fence_region_map) + .def_readwrite("org_node_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_count) + .def_readwrite("org_flop_indices", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flop_indices) + .def_readwrite("org_lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_lut_type) + .def_readwrite("org_pin_offset_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin_offset_x) + .def_readwrite("org_pin_offset_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin_offset_y) + .def_readwrite("org_pin2nodeType_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin2nodeType_map) + .def_readwrite("org_node2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2pincount_map) + .def_readwrite("org_pin2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin2node_map) + .def_readwrite("org_node2outpinCount", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2outpinCount) + .def_readwrite("org_node2outpinIdx_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2outpinIdx_map) + .def_readwrite("org_flat_node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_node2pin_map) + .def_readwrite("org_flat_node2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_node2pin_start_map) + .def_readwrite("org_flat_cc2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_cc2node_map) + .def_readwrite("org_flat_cc2node_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_cc2node_start_map) + .def_readwrite("org_node2ccId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2ccId_map) + .def_readwrite("org_is_cc_node", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_is_cc_node) + .def_readwrite("org_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_ctrlSets) + .def_readwrite("org_extended_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_extended_ctrlSets) + .def_readwrite("org_ext_ctrlSet_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_ext_ctrlSet_start_map) + .def_readwrite("new2org_node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::new2org_node_map) + //.def_readwrite("flat_cc_input_pins_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_input_pins_map) + //.def_readwrite("flat_cc_output_pins_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_output_pins_map) + //.def_readwrite("flat_cc_input_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_input_pin_start_map) + //.def_readwrite("flat_cc_output_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_output_pin_start_map) + //.def_readwrite("overall_cc_input_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::overall_cc_input_pin_start_map) + //.def_readwrite("overall_cc_output_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::overall_cc_output_pin_start_map) + .def_readwrite("pin_offset_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_offset_x) + .def_readwrite("pin_offset_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_offset_y) + .def_readwrite("pin2nodeType_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2nodeType_map) + .def_readwrite("pin_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_names) + .def_readwrite("pin_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_types) + .def_readwrite("pin_typeIds", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_typeIds) + .def_readwrite("pin2net_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2net_map) + .def_readwrite("pin2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2node_map) + .def_readwrite("net_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::net_names) + .def_readwrite("net2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net2pin_map) + .def_readwrite("flat_net2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_net2pin_map) + .def_readwrite("flat_net2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_net2pin_start_map) + .def_readwrite("net_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net_name2id_map) + .def_readwrite("num_sites_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_sites_x) + .def_readwrite("num_sites_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_sites_y) + .def_readwrite("siteTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteTypes) + .def_readwrite("siteWidths", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteWidths) + .def_readwrite("siteHeights", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteHeights) + .def_readwrite("rsrcTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcTypes) + .def_readwrite("rsrcInstWidths", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstWidths) + .def_readwrite("rsrcInstHeights", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstHeights) + .def_readwrite("siteResources", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteResources) + .def_readwrite("rsrcInsts", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInsts) + .def_readwrite("rsrcInstTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstTypes) + .def_readwrite("siteRsrc2CountMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteRsrc2CountMap) + .def_readwrite("rsrc2siteMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrc2siteMap) + .def_readwrite("inst2rsrcMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::inst2rsrcMap) + .def_readwrite("siteType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteType2indexMap) + .def_readwrite("rsrcType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcType2indexMap) + .def_readwrite("rsrcInstType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstType2indexMap) + .def_readwrite("sliceElements", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceElements) + .def_readwrite("lutFracturesMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::lutFracturesMap) + .def_readwrite("sliceFFCtrls", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceFFCtrls) + .def_readwrite("sliceUnitFFCtrls", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceUnitFFCtrls) + .def_readwrite("siteOutCoordinates", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteOutCoordinates) + .def_readwrite("siteOutValues", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteOutValues) + .def_readwrite("site_type_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::site_type_map) + .def_readwrite("lg_siteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::lg_siteXYs) + .def_readwrite("sliceSiteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceSiteXYs) + .def_readwrite("dspSiteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::dspSiteXYs) + .def_readwrite("ramSite0XYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::ramSite0XYs) + .def_readwrite("ramSite1XYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::ramSite1XYs) + .def_readwrite("flat_region_boxes", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_region_boxes) + .def_readwrite("flat_region_boxes_start", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_region_boxes_start) + .def_readwrite("spiral_accessor", &DREAMPLACE_NAMESPACE::PyPlaceDB::spiral_accessor) + .def_readwrite("spiral_maxVal", &DREAMPLACE_NAMESPACE::PyPlaceDB::spiral_maxVal) + .def_readwrite("ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::ctrlSets) + .def_readwrite("extended_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::extended_ctrlSets) + .def_readwrite("ext_ctrlSet_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::ext_ctrlSet_start_map) + //.def_readwrite("flat_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_ctrlSets) + .def_readwrite("num_routing_grids_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_routing_grids_x) + .def_readwrite("num_routing_grids_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_routing_grids_y) + .def_readwrite("routing_grid_xl", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_xl) + .def_readwrite("routing_grid_yl", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_yl) + .def_readwrite("routing_grid_xh", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_xh) + .def_readwrite("routing_grid_yh", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_yh) + .def_readwrite("xl", &DREAMPLACE_NAMESPACE::PyPlaceDB::xl) + .def_readwrite("yl", &DREAMPLACE_NAMESPACE::PyPlaceDB::yl) + .def_readwrite("xh", &DREAMPLACE_NAMESPACE::PyPlaceDB::xh) + .def_readwrite("yh", &DREAMPLACE_NAMESPACE::PyPlaceDB::yh) + .def_readwrite("ff_ctrl_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::ff_ctrl_type) + .def_readwrite("wl_weightX", &DREAMPLACE_NAMESPACE::PyPlaceDB::wl_weightX) + .def_readwrite("wl_weightY", &DREAMPLACE_NAMESPACE::PyPlaceDB::wl_weightY) + .def_readwrite("sliceFF_ctrl_mode", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceFF_ctrl_mode) + .def_readwrite("lut_maxShared", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_maxShared) + .def_readwrite("lut_type_in_sliceUnit", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_type_in_sliceUnit) + .def_readwrite("pinRouteCap", &DREAMPLACE_NAMESPACE::PyPlaceDB::pinRouteCap) + .def_readwrite("routeCapH", &DREAMPLACE_NAMESPACE::PyPlaceDB::routeCapH) + .def_readwrite("routeCapV", &DREAMPLACE_NAMESPACE::PyPlaceDB::routeCapV) + .def_readwrite("sliceIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceIdx) + .def_readwrite("ioIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::ioIdx) + .def_readwrite("bramIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::bramIdx) + .def_readwrite("m9kIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::m9kIdx) + .def_readwrite("m144kIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::m144kIdx) + .def_readwrite("dspIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::dspIdx) + .def_readwrite("pllIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::pllIdx) + ; + +} + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp new file mode 100644 index 00000000..7001295b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp @@ -0,0 +1,38 @@ +/** + * @file Region.cpp + * @author Yibo Lin (DREAMPlace) + * @date Dec 2019 + */ +#include "Region.h" + +DREAMPLACE_BEGIN_NAMESPACE + +Region::Region() + : Region::base_type() + , m_vBox() + , m_name("") + , m_type(RegionTypeEnum::UNKNOWN) +{ +} +Region::Region(Region const& rhs) + : Region::base_type(rhs) +{ + copy(rhs); +} +Region& Region::operator=(Region const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +void Region::copy(Region const& rhs) +{ + m_vBox = rhs.m_vBox; + m_name = rhs.m_name; + m_type = rhs.m_type; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h new file mode 100644 index 00000000..24824df0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h @@ -0,0 +1,58 @@ +/** + * @file Region.h + * @author Yibo Lin (DREAMPlace) + * @mail yibolin@pku.edu.cn + * @date Dec 2019 + */ + +#ifndef DREAMPLACE_REGION_H +#define DREAMPLACE_REGION_H + +#include +#include "Object.h" +#include "Box.h" +#include "Enums.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// class Region denotes a region like fence or guide +class Region : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + typedef Box box_type; + typedef coordinate_traits::index_type index_type; + typedef Point point_type; + + /// default constructor + Region(); + /// copy constructor + Region(Region const& rhs); + /// assignment + Region& operator=(Region const& rhs); + + /// member functions + std::vector const& boxes() const {return m_vBox;} + std::vector& boxes() {return m_vBox;} + Region& addBox(box_type const& box) {m_vBox.push_back(box); return *this;} + Region& setBox(index_type i, box_type const& box) {m_vBox.at(i) = box; return *this;} + + std::string const& name() const {return m_name;} + Region& setName(std::string const& name) {m_name = name; return *this;} + + RegionTypeEnum::RegionEnumType type() const {return (RegionTypeEnum::RegionEnumType)m_type;} + Region& setType(RegionTypeEnum::RegionEnumType t) {m_type = t; return *this;} + Region& setType(RegionType const& t) {return setType(t.value());} + + protected: + void copy(Region const& rhs); + + std::vector m_vBox; ///< rectangles for the region + std::string m_name; ///< region name + char m_type; ///< region type +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h new file mode 100644 index 00000000..f572af5a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h @@ -0,0 +1,239 @@ +/************************************************************************* + > File Name: Row.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Wed Jun 17 21:54:17 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_ROW_H +#define DREAMPLACE_ROW_H + +#include "Object.h" +#include "Box.h" +#include "Enums.h" +#include + +DREAMPLACE_BEGIN_NAMESPACE + +class Row : public Box, public Object +{ + public: + typedef Object base_type2; + typedef base_type2::coordinate_type coordinate_type; + typedef Box base_type1; + typedef coordinate_traits::index_type index_type; + + /// default constructor + Row(); + /// copy constructor + Row(Row const& rhs); + /// assignment + Row& operator=(Row const& rhs); + + /// member functions + std::string const& name() const {return m_name;} + Row& setName(std::string const& s) {m_name = s; return *this;} + + std::string const& macroName() const {return m_macroName;} + Row& setMacroName(std::string const& s) {m_macroName = s; return *this;} + + Orient const& orient() const {return m_orient;} + Row& setOrient(Orient const& o) {m_orient = o; return *this;} + + coordinate_type const& step(Direction1DType d) const {return m_step[d];} + Row& setStep(Direction1DType d, coordinate_type v) {m_step[d] = v; return *this;} + Row& setStep(coordinate_type vx, coordinate_type vy) {m_step[kX] = vx; m_step[kY] = vy; return *this;} + + index_type numSites(Direction1DType d) const {return delta(d)/step(d);} + protected: + void copy(Row const& rhs); + + std::string m_name; ///< name of row + std::string m_macroName; ///< macro name of row, usually not used + Orient m_orient; + coordinate_type m_step[2]; ///< step of rows in x and y direction, usually same as site width and height +}; + +inline Row::Row() + : Row::base_type1() + , Row::base_type2() + , m_name("") + , m_macroName("") + , m_orient() +{ + m_step[kX] = std::numeric_limits::max(); + m_step[kY] = std::numeric_limits::max(); +} +inline Row::Row(Row const& rhs) + : Row::base_type1(rhs) + , Row::base_type2(rhs) +{ + copy(rhs); +} +inline Row& Row::operator=(Row const& rhs) +{ + if (this != &rhs) + { + this->base_type1::operator=(rhs); + this->base_type2::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void Row::copy(Row const& rhs) +{ + m_name = rhs.m_name; + m_macroName = rhs.m_macroName; + m_orient = rhs.m_orient; + m_step[kX] = rhs.m_step[kX]; + m_step[kY] = rhs.m_step[kY]; +} + +/// compare by row bottom edge +/// tie break by row id +struct CompareByRowBottomCoord +{ + bool operator()(Row const& row1, Row const& row2) const + { + return row1.yl() < row2.yl() || (row1.yl() == row2.yl() && row1.id() < row2.id()); + } +}; + +/// a row is divided into sub row due to fixed cells or blockages +class SubRow : public Box +{ + public: + typedef Object::coordinate_type coordinate_type; + typedef Box base_type; + typedef coordinate_traits::index_type index_type; + typedef coordinate_traits::area_type area_type; + + /// default constructor + SubRow(); + /// copy constructor + SubRow(SubRow const& rhs); + /// assignment + SubRow& operator=(SubRow const& rhs); + + /// member functions + index_type index1D() const {return m_index1D;} + SubRow& setIndex1D(index_type id) {m_index1D = id; return *this;} + + index_type rowId() const {return m_rowId;} + SubRow& setRowId(index_type id) {m_rowId = id; return *this;} + + /// \return sub row id in a row for sub row map indexing + index_type subRowId() const {return m_subRowId;} + SubRow& setSubRowId(index_type id) {m_subRowId = id; return *this;} + + std::vector const& binRows() const {return m_vBinRowId;} + std::vector& binRows() {return m_vBinRowId;} + + protected: + void copy(SubRow const& rhs); + + index_type m_index1D; ///< index in sub row array + index_type m_rowId; ///< parent row index + index_type m_subRowId; ///< sub row id in a row + std::vector m_vBinRowId; ///< indices of bin sub rows +}; +inline SubRow::SubRow() + : SubRow::base_type() + , m_index1D (std::numeric_limits::max()) + , m_rowId(std::numeric_limits::max()) + , m_subRowId(std::numeric_limits::max()) +{ +} +inline SubRow::SubRow(SubRow const& rhs) + : SubRow::base_type(rhs) +{ + copy(rhs); +} +inline SubRow& SubRow::operator=(SubRow const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void SubRow::copy(SubRow const& rhs) +{ + m_index1D = rhs.m_index1D; + m_rowId = rhs.m_rowId; + m_subRowId = rhs.m_subRowId; + m_vBinRowId = rhs.m_vBinRowId; +} + +/// row in a bin +class BinRow : public Box +{ + public: + typedef Object::coordinate_type coordinate_type; + typedef Box base_type; + typedef coordinate_traits::index_type index_type; + typedef coordinate_traits::area_type area_type; + + /// default constructor + BinRow(); + /// copy constructor + BinRow(BinRow const& rhs); + /// assignment + BinRow& operator=(BinRow const& rhs); + + /// member functions + index_type index1D() const {return m_index1D;} + BinRow& setIndex1D(index_type id) {m_index1D = id; return *this;} + + index_type binId() const {return m_binId;} + BinRow& setBinId(index_type id) {m_binId = id; return *this;} + + index_type subRowId() const {return m_subRowId;} + BinRow& setSubRowId(index_type id) {m_subRowId = id; return *this;} + + //std::vector const& binSubRows() const {return m_vBSRowId;} + //std::vector& binSubRows() {return m_vBSRowId;} + + protected: + void copy(BinRow const& rhs); + + index_type m_index1D; ///< bin row index in 1D array + index_type m_binId; ///< parent bin 1D index + index_type m_subRowId; ///< parent sub row 1D index + //std::vector m_vBSRowId; ///< indices of bin sub rows +}; + +inline BinRow::BinRow() + : BinRow::base_type() + , m_index1D(std::numeric_limits::max()) + , m_binId(std::numeric_limits::max()) + , m_subRowId(std::numeric_limits::max()) +{ +} +inline BinRow::BinRow(BinRow const& rhs) + : BinRow::base_type(rhs) +{ + copy(rhs); +} +inline BinRow& BinRow::operator=(BinRow const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void BinRow::copy(BinRow const& rhs) +{ + m_index1D = rhs.m_index1D; + m_binId = rhs.m_binId; + m_subRowId = rhs.m_subRowId; + //m_vBSRowId = rhs.m_vBSRowId; +} + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp new file mode 100644 index 00000000..8e5462dd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp @@ -0,0 +1,206 @@ +/************************************************************************* + > File Name: RowMap.cpp + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Wed Jun 24 21:19:32 2015 + ************************************************************************/ + +#include "RowMap.h" +#include "AlgoDB.h" +#include "AlgoHelpers.h" +#include "Iterators.h" +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +/// helper object of ObsWraper for collectBlockageIntervals() +struct CollectFixedCellIntervalHelper +{ + typedef AlgoDB::index_type index_type; + typedef AlgoDB::interval_type interval_type; + typedef AlgoDB::box_type box_type; + + PlaceDB const& placeDB; + SubRowMap const& subRowMap; + std::vector >& mBlkInterval; + + CollectFixedCellIntervalHelper(PlaceDB const& db, SubRowMap const& map, std::vector >& mInterval) + : placeDB(db) + , subRowMap(map) + , mBlkInterval(mInterval) + { + } + CollectFixedCellIntervalHelper(CollectFixedCellIntervalHelper const& rhs) + : placeDB(rhs.placeDB) + , subRowMap(rhs.subRowMap) + , mBlkInterval(rhs.mBlkInterval) + { + } + inline void operator()(Node const& /*node*/, box_type const& box) + { + subRowMap.collectBlockageIntervals(placeDB, box, mBlkInterval); + } +}; + +SubRowMap& SubRowMap::set(PlaceDB const& db) +{ + std::vector const& vRow = db.rows(); + index_type numRow = vRow.size(); + m_vSubRow.clear(); + m_mSubRowId.resize(numRow); + // create sub rows + // construct a 2D blockage map + std::vector > mBlkInterval (numRow); + ObsWraper cfciHelper (db, CollectFixedCellIntervalHelper(db, *this, mBlkInterval)); + for (FixedNodeConstIterator it = db.fixedNodeBegin(); it.inRange(); ++it) + { + Node const& node = *it; + cfciHelper(node); + } + for (std::vector::const_iterator it = db.placeBlockages().begin(); it != db.placeBlockages().end(); ++it) + collectBlockageIntervals(db, *it, mBlkInterval); + // clean blockage map so that there is no overlap between row slots + for (std::vector > >::iterator it1 = mBlkInterval.begin(), it1e = mBlkInterval.end(); it1 != it1e; ++it1) + { + std::vector > vMergedBlkInterval; + // sort by low coordinates + std::sort(it1->begin(), it1->end(), Interval::CompareByLow()); + // merge intervals + for (index_type i = 0, ie = it1->size(); i < ie; ++i) + { + Interval const& icur = it1->at(i); + if (!vMergedBlkInterval.empty() && intersects(vMergedBlkInterval.back(), icur, true)) + vMergedBlkInterval.back().encompass(icur); + else + vMergedBlkInterval.push_back(icur); + } + // apply vMergedBlkInterval to mBlkInterval + it1->swap(vMergedBlkInterval); + } + // create sub rows in each row + for (index_type i = 0; i < numRow; ++i) + { + Row const& row = db.rows().at(i); + std::vector > const& vBlkInterval = mBlkInterval.at(i); + std::vector& vSubRowId = m_mSubRowId.at(i); + vSubRowId.reserve(vBlkInterval.size()+1); + for (index_type j = 0, je = vBlkInterval.size(); j <= je; ++j) + { + coordinate_type xl = (j == 0)? row.xl() : vBlkInterval[j-1].high(); + coordinate_type xh = (j < vBlkInterval.size())? vBlkInterval[j].low() : row.xh(); + + if (xl+db.siteWidth() <= xh) // at least 1 site + { + // create and add sub row + // set data that is not going to change + m_vSubRow.push_back(SubRow()); + SubRow& srow = m_vSubRow.back(); + srow.setIndex1D(m_vSubRow.size()-1); // object id + srow.set(xl, row.yl(), xh, row.yh()); + srow.setRowId(row.id()); // id for level-1 indexing + srow.setSubRowId(vSubRowId.size()); // id for level-2 indexing + vSubRowId.push_back(srow.index1D()); + } + } + } + + return *this; +} + +void SubRowMap::collectBlockageIntervals(PlaceDB const& db, SubRowMap::box_type const& box, std::vector >& mBlkInterval) const +{ + std::vector const& vRow = db.rows(); + // collect intervals to blockage map + // it is possible that the blockage may not align to sites + // scale it up so that all the sub rows start and end to sites + box_type adjustBox ( + db.rowXL()+floor((box.xl()-db.rowXL())/db.siteWidth())*db.siteWidth(), + box.yl(), + db.rowXL()+ceil((double)(box.xh()-db.rowXL())/db.siteWidth())*db.siteWidth(), + box.yh() + ); + Interval idxRange (db.getRowIndexRange(adjustBox.yl(), adjustBox.yh())); + for (index_type i = idxRange.low(); i <= idxRange.high(); ++i) + { + if (i < mBlkInterval.size() && intersects(vRow[i], adjustBox, false)) // only collect valid intervals + mBlkInterval[i].push_back(adjustBox.get(kX)); + } +} + +BinRowMap& BinRowMap::set(AlgoDB& algo) +{ + BinType bt = kSBin; // use sbin to initialize bin row + SubRowMap const& subRowMap = algo.subRowMap(); + m_vBinRow.clear(); + m_mBinRowId.resize(subRowMap.numRows()); + // create bin rows according to sub row map and bin map + // traverse through all sub rows + for (SubRowMap1DConstIterator it = subRowMap.begin1D(), ite = subRowMap.end1D(); it != ite; ++it) + { + SubRow const& srow = *it; + std::vector& vBinRowId = m_mBinRowId.at(srow.rowId()); + Box idxBox (algo.getBinIndexRange(bt, srow.xl(), srow.yl(), srow.xh(), srow.yh())); + for (index_type iy = idxBox.yl(); iy <= idxBox.yh(); ++iy) + { + vBinRowId.reserve(idxBox.width()+1); + for (index_type ix = idxBox.xl(); ix <= idxBox.xh(); ++ix) + { + Bin& bin = algo.getBinByIndex(bt, ix, iy); + std::pair, bool> intersectBox = intersection(srow, bin, false); + if (intersectBox.second) // at least have intersection + { + // create bin row + m_vBinRow.push_back(BinRow()); + BinRow& brow = m_vBinRow.back(); + brow.set(kX, intersectBox.first.get(kX)).set(kY, srow.get(kY)); + brow.setIndex1D(m_vBinRow.size()-1); // object id + brow.setBinId(bin.index1D()); + brow.setSubRowId(srow.index1D()); // id to find parent sub row + vBinRowId.push_back(brow.index1D()); + // add to bin + // first step + bin.binRows().setPartial1(brow.index1D()); + } + } + } + } + // iterate through all bins to construct m_vBinRowId in bins + for (BinMap1DIterator it = algo.binMap(bt).begin1D(), ite = algo.binMap(bt).end1D(); it != ite; ++it) + { + Bin& bin = *it; + std::vector vRowIdOfBinRows (bin.binRows().size()); // row id of bin rows, because row id is continuous vertically + index_type count = 0; + for (HrchyList::const_iterator_type itBR = bin.binRows().begin(), itBRe = bin.binRows().end(); itBR != itBRe; ++itBR) + vRowIdOfBinRows[count++] = algo.getSubRow(getBinRow(*itBR).subRowId()).rowId(); // assume sub row is initialized + + Interval rowIdxInv = algo.placeDB().getRowIndexRange(bin.yl(), bin.yh()-1); // min, max row id + for (std::vector::iterator itSRID = vRowIdOfBinRows.begin(), itSRIDe = vRowIdOfBinRows.end(); itSRID != itSRIDe; ++itSRID) + { + *itSRID -= rowIdxInv.low(); // normalize to 0 + } + std::vector > mData; + // second step + bin.binRows().setPartial2(vRowIdOfBinRows, mData, rowIdxInv.delta()+1); + // the target is to sort bin rows from left to right within each row + // considering that bin row id is ordered from bottom to top, left to right, by construction + // it should be enough to sort by the indices + for (std::vector >::iterator itd = mData.begin(), itde = mData.end(); itd != itde; ++itd) + std::sort(itd->begin(), itd->end()); + // third step + bin.binRows().setPartial3(mData); + } +#ifdef DEBUG + // maybe no longer reasonable check + for (BinMap1DConstIterator it = algo.binMap(bt).begin1D(), ite = algo.binMap(bt).end1D(); it != ite; ++it) + { + Bin const& bin = *it; + // check whether bin rows in a sbin are sorted + dreamplaceAssert(boost::algorithm::is_sorted(bin.binRows().begin(), bin.binRows().end())); + } +#endif + + return *this; +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h new file mode 100644 index 00000000..2f060951 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h @@ -0,0 +1,208 @@ +/************************************************************************* + > File Name: RowMap.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Wed Jun 24 21:03:54 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_ROWMAP_H +#define DREAMPLACE_ROWMAP_H + +#include "Row.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// forward declaration of data +class BinMap; +class PlaceDB; +class AlgoDB; + +/// sub row map +/// sub rows should not contain any region of fixed cells +class SubRowMap +{ + public: + typedef Object::index_type index_type; + typedef Object::coordinate_type coordinate_type; + typedef Interval interval_type; + typedef Box box_type; + typedef std::vector::iterator SubRowMap1DIterator; ///< 1D iterator + typedef std::vector::const_iterator SubRowMap1DConstIterator; ///< 1D const iterator + + /// constructor + SubRowMap(); + /// copy constructor + SubRowMap(SubRowMap const& rhs); + /// assignment + SubRowMap& operator=(SubRowMap const& rhs); + + /// member functions + /// initialize + SubRowMap& set(PlaceDB const& db); + + /// \return level-1 indexing number + index_type numRows() const {return m_mSubRowId.size();} + + /// \return total number of sub rows + index_type numSubRows() const {return m_vSubRow.size();} + /// \return sub rows + std::vector const& getSubRows() const {return m_vSubRow;} + std::vector& getSubRows() {return m_vSubRow;} + + /// \return sub row index array in a row + std::vector const& getSubRowsByRowIndex(index_type i) const {return m_mSubRowId.at(i);} + /// \return sub row array iterators in a row + /// this is more convenient than getSubRowsByRowIndex() + std::pair::const_iterator, std::vector::const_iterator> getSubRowItersByRowIndex(index_type i) const + { + if (i+1 < numRows()) + return std::make_pair(m_vSubRow.begin()+m_mSubRowId[i].front(), m_vSubRow.begin()+m_mSubRowId[i+1].front()); + else // last row + return std::make_pair(m_vSubRow.begin()+m_mSubRowId[i].front(), m_vSubRow.end()); + } + /// index version of getSubRowItersByRowIndex() + std::pair getSubRowIndicesByRowIndex(index_type i) const + { + if (i+1 < numRows()) + return std::make_pair(m_mSubRowId[i].front(), m_mSubRowId[i+1].front()); + else // last row + return std::make_pair(m_mSubRowId[i].front(), numSubRows()); + } + + /// \return sub row from object id + SubRow const& getSubRow(index_type id) const {return m_vSubRow.at(id);} + SubRow& getSubRow(index_type id) {return m_vSubRow.at(id);} + + /// \return sub row from row id and sub row id + SubRow const& getSubRow(index_type rowId, index_type subRowId) const {return m_vSubRow.at(m_mSubRowId[rowId][subRowId]);} + SubRow& getSubRow(index_type rowId, index_type subRowId) {return m_vSubRow.at(m_mSubRowId[rowId][subRowId]);} + + SubRowMap1DIterator begin1D() {return m_vSubRow.begin();} + SubRowMap1DIterator end1D() {return m_vSubRow.end();} + + SubRowMap1DConstIterator begin1D() const {return m_vSubRow.begin();} + SubRowMap1DConstIterator end1D() const {return m_vSubRow.end();} +#if 0 + /// TO DO: create iterator class for such kind of data structure + /// traversal through level-1 + SubRowMap1DIterator1 begin1() {return m_mSubRow.begin();} + SubRowMap1DIterator1 end1() {return m_mSubRow.end();} + SubRowMap1DConstIterator1 begin1() const {return m_mSubRow.begin();} + SubRowMap1DConstIterator1 end1() const {return m_mSubRow.end();} + /// traversal through level-2 + SubRowMap1DIterator2 begin2(SubRowMap1DIterator1 it) {return it->begin();} + SubRowMap1DIterator2 end2(SubRowMap1DIterator1 it) {return it->end();} + SubRowMap1DConstIterator2 begin2(SubRowMap1DConstIterator1 it) const {return it->begin();} + SubRowMap1DConstIterator2 end2(SubRowMap1DConstIterator1 it) const {return it->end();} +#endif + protected: + void copy(SubRowMap const& rhs); + /// a helper function to set() + void collectBlockageIntervals(PlaceDB const& db, box_type const& box, std::vector >& mBlkInterval) const; + + std::vector m_vSubRow; ///< actual sub rows are saved in a 1D array, must keep low to high, left to right order + std::vector > m_mSubRowId; ///< 2D bin row map, level-1 indexing is number of rows + ///< number of sub rows may be different between rows + + friend struct CollectFixedCellIntervalHelper; +}; + +inline SubRowMap::SubRowMap() + : m_vSubRow () + , m_mSubRowId() +{ +} +inline SubRowMap::SubRowMap(SubRowMap const& rhs) +{ + copy(rhs); +} +inline SubRowMap& SubRowMap::operator=(SubRowMap const& rhs) +{ + if (this != &rhs) + copy(rhs); + return *this; +} +inline void SubRowMap::copy(SubRowMap const& rhs) +{ + m_vSubRow = rhs.m_vSubRow; + m_mSubRowId = rhs.m_mSubRowId; +} + +typedef SubRowMap::SubRowMap1DIterator SubRowMap1DIterator; +typedef SubRowMap::SubRowMap1DConstIterator SubRowMap1DConstIterator; + +/// bin row map +/// bin rows should not contain any region of fixed cells +class BinRowMap +{ + public: + typedef Object::index_type index_type; + typedef BinRow::coordinate_type coordinate_type; + typedef std::vector::iterator BinRowMap1DIterator; ///< 1D iterator + typedef std::vector::const_iterator BinRowMap1DConstIterator; ///< 1D const iterator + + /// constructor + BinRowMap(); + /// copy constructor + BinRowMap(BinRowMap const& rhs); + /// assignment + BinRowMap& operator=(BinRowMap const& rhs); + + /// member functions + /// initialize + BinRowMap& set(AlgoDB& algo); + + index_type numRows() const {return m_mBinRowId.size();} + index_type numBinRows() const {return m_vBinRow.size();} + + /// \return bin rows + std::vector const& getBinRows() const {return m_vBinRow;} + std::vector& getBinRows() {return m_vBinRow;} + + /// \return bin row array in a row + std::vector const& getBinRowsByRowIndex(index_type i) const {return m_mBinRowId.at(i);} + + /// \return bin row from object id + BinRow const& getBinRow(index_type id) const {return m_vBinRow.at(id);} + BinRow& getBinRow(index_type id) {return m_vBinRow.at(id);} + + BinRowMap1DIterator begin1D() {return m_vBinRow.begin();} + BinRowMap1DIterator end1D() {return m_vBinRow.end();} + + BinRowMap1DConstIterator begin1D() const {return m_vBinRow.begin();} + BinRowMap1DConstIterator end1D() const {return m_vBinRow.end();} + protected: + void copy(BinRowMap const& rhs); + + std::vector m_vBinRow; ///< actual bin rows are saved in a 1D array, must be in low to high, left to right order + std::vector > m_mBinRowId; ///< 2D bin row map for indexing, level-1 indexing is number of rows + ///< number of bin rows may be different between rows +}; + +inline BinRowMap::BinRowMap() + : m_vBinRow () + , m_mBinRowId() +{ +} +inline BinRowMap::BinRowMap(BinRowMap const& rhs) +{ + copy(rhs); +} +inline BinRowMap& BinRowMap::operator=(BinRowMap const& rhs) +{ + if (this != &rhs) + copy(rhs); + return *this; +} +inline void BinRowMap::copy(BinRowMap const& rhs) +{ + m_vBinRow = rhs.m_vBinRow; + m_mBinRowId = rhs.m_mBinRowId; +} + +typedef BinRowMap::BinRowMap1DIterator BinRowMap1DIterator; +typedef BinRowMap::BinRowMap1DConstIterator BinRowMap1DConstIterator; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h new file mode 100644 index 00000000..a2ca443c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h @@ -0,0 +1,87 @@ +/************************************************************************* + > File Name: Site.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Wed Jun 17 22:09:40 2015 + ************************************************************************/ + +#ifndef DREAMPLACE_SITE_H +#define DREAMPLACE_SITE_H + +#include "Object.h" + +DREAMPLACE_BEGIN_NAMESPACE + +class Site : public Object +{ + public: + typedef Object base_type; + typedef base_type::coordinate_type coordinate_type; + + /// default constructor + Site(); + /// copy constructor + Site(Site const& rhs); + /// assignment + Site& operator=(Site const& rhs); + + /// member functions + std::string const& name() const {return m_name;} + Site& setName(std::string const& s) {m_name = s; return *this;} + + std::string const& className() const {return m_className;} + Site& setClassName(std::string const& s) {m_className = s; return *this;} + + unsigned char symmetry() const {return m_symmetry;} + Site& setSymmetry(unsigned char s) {m_symmetry = s; return *this;} + + coordinate_type size(Direction1DType d) const {return m_size[d];} + Site& setSize(Direction1DType d, coordinate_type v) {m_size[d] = v; return *this;} + coordinate_type width() const {return m_size[kX];} + coordinate_type height() const {return m_size[kY];} + + protected: + void copy(Site const& rhs); + + std::string m_name; ///< site name + std::string m_className; ///< class name + unsigned char m_symmetry; ///< 3-bit: x, y, R90 + coordinate_type m_size[2]; ///< width and height +}; + +inline Site::Site() + : Site::base_type() + , m_name("") + , m_className("") + , m_symmetry(std::numeric_limits::max()) +{ + m_size[kX] = std::numeric_limits::max(); + m_size[kY] = std::numeric_limits::max(); +} +inline Site::Site(Site const& rhs) + : Site::base_type(rhs) +{ + copy(rhs); +} +inline Site& Site::operator=(Site const& rhs) +{ + if (this != &rhs) + { + this->base_type::operator=(rhs); + copy(rhs); + } + return *this; +} +inline void Site::copy(Site const& rhs) +{ + m_name = rhs.m_name; + m_className = rhs.m_className; + m_symmetry = rhs.m_symmetry; + m_size[kX] = rhs.m_size[kX]; + m_size[kY] = rhs.m_size[kY]; +} + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp new file mode 100644 index 00000000..935de9cd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp @@ -0,0 +1,260 @@ +/************************************************************************* + > File Name: SubRowNodeMap.cpp + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Feb 2016 11:16:56 AM CST + ************************************************************************/ + +#include "SubRowNodeMap.h" +#include "AlgoDB.h" +#include "Iterators.h" +#include + +DREAMPLACE_BEGIN_NAMESPACE + +SubRowNodeMap::SubRowNodeMap(AlgoDB const* db) +{ + if (db) // only initialize if it is set + set(db); +} + +SubRowNodeMap::SubRowNodeMap(SubRowNodeMap const& rhs) +{ + copy(rhs); +} + +SubRowNodeMap& SubRowNodeMap::operator=(SubRowNodeMap const& rhs) +{ + if (this != &rhs) + copy(rhs); + return *this; +} + +SubRowNodeMap::~SubRowNodeMap() +{ +} + +SubRowNodeMap& SubRowNodeMap::set(AlgoDB const* db) +{ + m_algoDB = db; + m_vMap.clear(); + m_vMap.resize(m_algoDB->subRowMap().numSubRows()); + m_avgMovableWidth = m_algoDB->placeDB().avgMovableNodeWidth(); + + return *this; +} + +void SubRowNodeMap::copy(SubRowNodeMap const& rhs) +{ + m_algoDB = rhs.m_algoDB; + m_vMap = rhs.m_vMap; + m_avgMovableWidth = rhs.m_avgMovableWidth; +} + +void SubRowNodeMap::setSubRowNodes(bool moveDummyFixedCellOnly) +{ + // reset maps + for (std::vector::iterator it = m_vMap.begin(), ite = m_vMap.end(); it != ite; ++it) + it->clear(); + // distribute nodes + coordinate_type rowHeight = m_algoDB->placeDB().rowHeight(); + std::vector > mNode (m_algoDB->subRowMap().numSubRows()); + for (MovableNodeConstIterator it = m_algoDB->placeDB().movableNodeBegin(); it.inRange(); ++it) + { + Node const& node = *it; + if (moveDummyFixedCellOnly && node.status() != PlaceStatusEnum::DUMMY_FIXED) + continue; + // this for loop only works for movable cells, because fixed instances may occupy partial row + coordinate_type y = node.yl(); + while (y < node.yh()) + { + index_type idx = m_algoDB->getSubRowIndexSafe(node.xl(), y+1); // compute sub row index + mNode[idx].push_back(map_element_type(node.id(), node.initPos().x(), node.get(kX))); + + y += rowHeight; // next row + } + } + for (index_type i = 0, ie = m_vMap.size(); i < ie; ++i) + { +#ifdef USE_RTREE + map_type(mNode[i].begin(), mNode[i].end()).swap(m_vMap.at(i)); +#elif defined(USE_INTERVALHASHMAP) + map_type(m_algoDB->getSubRow(i).xl(), m_algoDB->getSubRow(i).xh(), m_avgMovableWidth, mNode[i].begin(), mNode[i].end()).swap(m_vMap.at(i)); +#endif + } +} + +std::vector SubRowNodeMap::queryRange(Box const& box) const +{ + // go through bins and extract sub rows + Box idxBinBox = m_algoDB->getBinIndexRange(kSBin, box.xl(), box.yl(), box.xh(), box.yh()); + std::vector vSubRow; + for (index_type idxX = idxBinBox.xl(); idxX <= idxBinBox.xh(); ++idxX) + for (index_type idxY = idxBinBox.yl(); idxY <= idxBinBox.yh(); ++idxY) + { + Bin const& bin = m_algoDB->getBinByIndex(kSBin, idxX, idxY); + for (HrchyList::const_iterator_type itBR = bin.binRows().begin(), itBRe = bin.binRows().end(); itBR != itBRe; ++itBR) + { + BinRow const& brow = m_algoDB->getBinRow(*itBR); + vSubRow.push_back(brow.subRowId()); + } + } + // remove duplicates of sub rows + removeDuplicates(vSubRow); + + // go through sub rows and extract nodes + std::vector vNodeInBox; // result + for (std::vector::const_iterator itSR = vSubRow.begin(), itSRe = vSubRow.end(); itSR != itSRe; ++itSR) + { + SubRow const& srow = m_algoDB->getSubRow(*itSR); + if (intersects(srow, box, false)) // consider sub row that has overlap with the box + { + std::pair found = queryRange(srow.index1D(), box.xl(), box.xh(), true); + for (map_const_iterator_type itn = found.first; itn != found.second; ++itn) + { +#ifdef DEBUG +#ifdef USE_RTREE + dreamplaceAssert(getMapElementHigh(itn) > box.xl()); // skip cells that do not have overlap with the range +#endif +#endif +#ifdef USE_RTREE + vNodeInBox.push_back(getMapElementId(itn)); +#elif defined(USE_INTERVALHASHMAP) + if (getMapElementHigh(itn) > box.xl()) // skip cells that do not have overlap with the range + vNodeInBox.push_back(getMapElementId(itn)); +#endif + } + } + } + return vNodeInBox; +} + +std::vector SubRowNodeMap::queryRange( + SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type yl, + SubRowNodeMap::coordinate_type xh, SubRowNodeMap::coordinate_type yh) const +{ + return queryRange(Box(xl, yl, xh, yh)); +} + +std::pair +SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, + SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary) const +{ + map_type const& map = subRowMap(idx); +#ifdef USE_RTREE + if (noBoundary) + return std::make_pair( + map.qbegin(bgi::intersects(interval_type(xl, xh)) + && bgi::satisfies(NoBoundaryPredicate(xl, xh))), + map.qend() + ); + else + return std::make_pair( + map.qbegin(bgi::intersects(interval_type(xl, xh))), + map.qend() + ); +#elif defined(USE_INTERVALHASHMAP) + return query(map, xl, xh, !noBoundary); +#endif +} + +void SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary, std::vector& vNode) const +{ + map_type const& map = subRowMap(idx); +#ifdef USE_RTREE + if (noBoundary) + map.query(bgi::intersects(interval_type(xl, xh)), + std::back_inserter(vNode)); + else + map.query(bgi::intersects(interval_type(xl, xh)) && bgi::satisfies(NoBoundaryPredicate(xl, xh)), + std::back_inserter(vNode)); +#elif defined(USE_INTERVALHASHMAP) + std::pair found = query(map, xl, xh, !noBoundary); + vNode.assign(found.first, found.second); +#endif +} + +void SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary, SubRowNodeMap::map_type& targetMap) const +{ + map_type const& map = subRowMap(idx); +#ifdef USE_RTREE + if (noBoundary) + map.query(bgi::intersects(interval_type(xl, xh)), + bgi::inserter(targetMap)); + else + map.query(bgi::intersects(interval_type(xl, xh)) && bgi::satisfies(NoBoundaryPredicate(xl, xh)), + bgi::inserter(targetMap)); +#elif defined(USE_INTERVALHASHMAP) + std::pair found = query(map, xl, xh, !noBoundary); + for (map_const_iterator_type it = found.first; it != found.second; ++it) + targetMap.insert(*it); +#endif +} + +bool SubRowNodeMap::count(SubRowNodeMap::index_type idx, Node const& node) const +{ + return subRowMap(idx).count( + map_element_type(node.id(), node.initPos().x(), node.get(kX)) + ); +} + +bool SubRowNodeMap::erase(SubRowNodeMap::index_type idx, Node const& node) +{ + return subRowMap(idx).remove( + map_element_type(node.id(), node.initPos().x(), node.get(kX)) + ); +} + +void SubRowNodeMap::insert(SubRowNodeMap::index_type idx, Node const& node) +{ + subRowMap(idx).insert( + map_element_type(node.id(), node.initPos().x(), node.get(kX)) + ); +} + +void SubRowNodeMap::print(SubRowNodeMap::index_type idx) const +{ + printRange(idx, m_algoDB->placeDB().rowXL(), m_algoDB->placeDB().rowXH()); +} + +void SubRowNodeMap::printRange(SubRowNodeMap::index_type idx, + SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh) const +{ + char prefix[16]; + dreamplaceSPrint(kNONE, prefix, "r%u: ", idx); +#ifdef USE_RTREE + for (map_const_iterator_type it = subRowMap(idx).qbegin( + bgi::intersects( + interval_type(xl, xh) + ) + && bgi::satisfies( + NoBoundaryPredicate(xl, xh) + )); it != subRowMap(idx).qend(); ++it) + { + dreamplacePrint(kNONE, "%s%u@%d", prefix, getMapElementId(it), getMapElementLow(it)); + dreamplaceSPrint(kNONE, prefix, ", "); + } +#elif defined(USE_INTERVALHASHMAP) + std::pair found = query(subRowMap(idx), xl, xh, true); + for (map_const_iterator_type it = found.first; it != found.second; ++it) + { + dreamplacePrint(kNONE, "%s%u@%d", prefix, getMapElementId(it), getMapElementLow(it)); + dreamplaceSPrint(kNONE, prefix, ", "); + } +#endif + dreamplacePrint(kNONE, "\n"); +} + +#if 0 +Node const& SubRowNodeMap::getMapElement(SubRowNodeMap::map_const_iterator_type const& it) const +{ + return getMapElement(*it); +} + +Node const& SubRowNodeMap::getMapElement(SubRowNodeMap::map_element_type const& v) const +{ + return m_algoDB->node(getMapElementId(v)); +} +#endif + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h new file mode 100644 index 00000000..d7121052 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h @@ -0,0 +1,169 @@ +/************************************************************************* + > File Name: SubRowNodeMap.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Feb 2016 10:27:34 AM CST + ************************************************************************/ + +#ifndef DREAMPLACE_SUBROWNODEMAP_H +#define DREAMPLACE_SUBROWNODEMAP_H + +#include +#include "Interval.h" +#include "Box.h" +#include "Object.h" +#include "Node.h" +#include "NodeMapElement.h" +#include "SubRowNodeMapHelper.h" + +// IntervalHashMap is slightly faster than rtree for large benchmarks +//#define USE_RTREE +#define USE_INTERVALHASHMAP + +DREAMPLACE_BEGIN_NAMESPACE + +/// forward declaration of data +class AlgoDB; + +#ifdef USE_RTREE +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; +namespace bgm = boost::geometry::model; +#endif + +/// a query map for nodes stored in sub rows, indexed with rtree +/// Node pointer is directly used for rtree indexing, +/// which means you have to be careful when update the position of nodes +/// +/// it must be initialized after SubRowMap and BinRowMap are ready + +/// query map +class SubRowNodeMap +{ + public: + typedef Object::coordinate_type coordinate_type; + typedef Object::index_type index_type; + typedef Interval interval_type; + typedef NodeMapElement map_element_type; + +#ifdef USE_RTREE + /// use 1D point in boost to adapt rtree + typedef bgi::rtree > map_type; + typedef map_type::const_query_iterator map_const_iterator_type; +#elif defined(USE_INTERVALHASHMAP) + typedef IntervalHashMap > map_type; + typedef map_type::const_iterator_type map_const_iterator_type; +#endif + + /// constructor + SubRowNodeMap(AlgoDB const* db = NULL); + /// copy constructor + SubRowNodeMap(SubRowNodeMap const& rhs); + /// assignment + SubRowNodeMap& operator=(SubRowNodeMap const& rhs); + /// destructor + ~SubRowNodeMap(); + + /// member functions + /// initialize + SubRowNodeMap& set(AlgoDB const* db); + + /// distribute nodes into sub rows, must be called before using the map + /// can be called multiple times + void setSubRowNodes(bool moveDummyFixedCellOnly); + + /// \return true if an element exists in a sub row + bool count(index_type idx, Node const& node) const; + /// erase an element + bool erase(index_type idx, Node const& node); + /// emplace + void insert(index_type idx, Node const& node); + + /// query a range in a sub row + /// the left element is not the absolute boundary, it is relaxed to make sure all the nodes with possible overlap are included + /// \param noBoundary, if true, remove boundary intersects + std::pair queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary) const; + /// directly set \param vNode instead of returning iterators + void queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary, std::vector& vNode) const; + void queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary, map_type& targetMap) const; + /// query with a region + /// might be slow, as we need to go through all the bins and extract sub rows + /// if the sub row is known, directly call subRowMap() and then queryRange() is recommended + std::vector queryRange(Box const& box) const; + std::vector queryRange(coordinate_type xl, coordinate_type yl, coordinate_type xh, coordinate_type yh) const; + + /// helper functions for boost box + /// it is better to write accessors with map_const_iterator_type because we can change the internal map_type more easily + static inline coordinate_type getMapElementLow(map_const_iterator_type const& it) {return it->inv.low();} + static inline coordinate_type getMapElementLow(map_element_type const& v) {return v.inv.low();} + static inline coordinate_type getMapElementHigh(map_const_iterator_type const& it) {return it->inv.high();} + static inline coordinate_type getMapElementHigh(map_element_type const& v) {return v.inv.high();} + static inline index_type getMapElementId(map_const_iterator_type const& it) {return it->nodeId;} + static inline index_type getMapElementId(map_element_type const& v) {return v.nodeId;} + //Node const& getMapElement(map_const_iterator_type const& it) const; + //Node const& getMapElement(map_element_type const& v) const; + + /// for debug + /// print cells in a sub row + void print(index_type idx) const; + /// print range of cells in a sub row + void printRange(index_type idx, coordinate_type xl, coordinate_type xh) const; + protected: + void copy(SubRowNodeMap const& rhs); + + /// forbiden to public due to security + /// \return map of a single sub row + map_type& subRowMap(index_type idx) {return m_vMap.at(idx);} + map_type const& subRowMap(index_type idx) const {return m_vMap.at(idx);} + + AlgoDB const* m_algoDB; ///< AlgoDB has some helper functions useful for indexing + std::vector m_vMap; ///< same number of sub rows + coordinate_type m_avgMovableWidth; ///< average width of movable cells, only used in USE_INTERVALHASHMAP +}; + +/// the overlap predicate in boost only works for box, it does not give the correct solution for segments +/// the intersects will count the boundaries, which is not preferred +struct OverlapPredicate +{ + typedef Object::coordinate_type coordinate_type; + typedef Interval interval_type; + + interval_type inv; + + OverlapPredicate(coordinate_type xl, coordinate_type xh) : inv(xl, xh) {} + OverlapPredicate(interval_type const& i) : inv(i) {} + OverlapPredicate(OverlapPredicate const& rhs) : inv(rhs.inv) {} + + template + inline bool operator()(ValueType const& v) const + { + return !(SubRowNodeMap::getMapElementHigh(v) <= inv.low() + || SubRowNodeMap::getMapElementLow(v) >= inv.high()); + } +}; + +/// remove boundary intersection because bgi::intersects allows intersection of boundaries +/// I found that it is around 1.5x faster than OverlapPredicate if I combine it with bgi::intersects +struct NoBoundaryPredicate +{ + typedef Object::coordinate_type coordinate_type; + + coordinate_type xl; + coordinate_type xh; + + NoBoundaryPredicate(coordinate_type l, coordinate_type h) : xl(l), xh(h) {} + NoBoundaryPredicate(NoBoundaryPredicate const& rhs) : xl(rhs.xl), xh(rhs.xh) {} + + /// \return true if it is not boundary intersection + template + inline bool operator()(ValueType const& v) const + { + return SubRowNodeMap::getMapElementHigh(v) != xl + && SubRowNodeMap::getMapElementLow(v) != xh; + } + +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h new file mode 100644 index 00000000..8e05c30a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h @@ -0,0 +1,133 @@ +/************************************************************************* + > File Name: SubRowNodeMapHelper.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Feb 2016 09:24:33 PM CST + ************************************************************************/ + +#ifndef DREAMPLACE_SUBROWNODEMAPHELPER_H +#define DREAMPLACE_SUBROWNODEMAPHELPER_H + +#include +#include +#include "Interval.h" +#include "IntervalHashMap.h" + +/// API for Boost.Geometry +namespace boost { namespace geometry { namespace index { + +template +struct indexable< std::pair >, T> > +{ + typedef std::pair >, T> V; + + typedef boost::geometry::model::point point_type; + typedef boost::geometry::model::box const& result_type; + + result_type operator()(V const& v) const { return v.first; } +}; + +template <> +struct indexable +{ + typedef DREAMPLACE_NAMESPACE::Node const* value_type; + typedef DREAMPLACE_NAMESPACE::Interval const& result_type; + + result_type operator()(value_type const& v) const {return v->get(DREAMPLACE_NAMESPACE::kX);} +}; + +template <> +struct indexable +{ + typedef DREAMPLACE_NAMESPACE::NodeMapElement value_type; + typedef DREAMPLACE_NAMESPACE::NodeMapElement::interval_type const& result_type; + + result_type operator()(value_type const& v) const {return v.inv;} +}; + +}}} // namespace boost // namespace geometry // namespace index + +namespace boost { namespace geometry { namespace traits { + +//////// for intervals //////// +template +struct tag > +{ + typedef box_tag type; +}; + +template +struct point_type > +{ + // a 1D point + typedef boost::geometry::model::point type; +}; + +template +struct indexed_access +< + DREAMPLACE_NAMESPACE::Interval, + min_corner, Dimension +> +{ + typedef CoordinateType coordinate_type; + + static inline coordinate_type get(DREAMPLACE_NAMESPACE::Interval const& inv) + { + return inv.low(); + } + static inline void set(DREAMPLACE_NAMESPACE::Interval& inv, coordinate_type const& value) + { + inv.set(DREAMPLACE_NAMESPACE::kLOW, value); + } +}; + + +template +struct indexed_access +< + DREAMPLACE_NAMESPACE::Interval, + max_corner, Dimension +> +{ + typedef CoordinateType coordinate_type; + + static inline coordinate_type get(DREAMPLACE_NAMESPACE::Interval const& inv) + { + return inv.high(); + } + static inline void set(DREAMPLACE_NAMESPACE::Interval& inv, coordinate_type const& value) + { + inv.set(DREAMPLACE_NAMESPACE::kHIGH, value); + } +}; + +}}} // namespace boost // namespace geometry // namespace traits + +/// API for IntervalHashMap +DREAMPLACE_BEGIN_NAMESPACE + +template <> +struct IntervalHashMapTraits +{ + typedef NodeMapElement value_type; + typedef value_type::coordinate_type coordinate_type; + typedef unsigned index_type; + + static coordinate_type low(value_type const& v) {return v.inv.low(); } + static coordinate_type high(value_type const& v) {return v.inv.high();} + static bool equal(value_type const& v1, value_type const& v2) {return v1.nodeId == v2.nodeId;} + + /// compare object for sorting + struct CompareByLow + { + bool operator()(value_type const& v1, value_type const& v2) const {return low(v1) < low(v2) || (low(v1) == low(v2) && v1.nodeId < v2.nodeId);} + bool operator()(coordinate_type v1, value_type const& v2) const {return v1 < low(v2);} + bool operator()(value_type const& v1, coordinate_type v2) const {return low(v1) < v2;} + }; +}; + +DREAMPLACE_END_NAMESPACE + + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h new file mode 100644 index 00000000..e089b4d5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h @@ -0,0 +1,214 @@ +/************************************************************************* + > File Name: util.h + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Sun 14 Jun 2015 04:08:18 PM CDT + ************************************************************************/ + +#ifndef DREAMPLACE_UTIL_H +#define DREAMPLACE_UTIL_H + +#include +#include +#include +#include +#include "utility/src/Msg.h" + +/// headers for hash tables +#include +#include +DREAMPLACE_BEGIN_NAMESPACE +namespace hashspace = std; +DREAMPLACE_END_NAMESPACE + +DREAMPLACE_BEGIN_NAMESPACE + +enum Direction1DType { + kLOW = 0, + kHIGH = 1, + kX = 0, + kY = 1, + kLEFT = 0, + kRIGHT = 1, + kBOTTOM = 0, + kTOP = 1 +}; + +enum Direction2DType { + kXLOW = 0, + kXHIGH = 1, + kYLOW = 2, + kYHIGH = 3 +}; + +/// extract x/y information from Direction2DType +inline Direction1DType getXY(Direction2DType d) +{ + return Direction1DType(d>1); +} +/// extract low/high information from Direction2DType +inline Direction1DType getLH(Direction2DType d) +{ + return Direction1DType(d&1); +} +/// construct Direction2DType from x/y and low/high information +inline Direction2DType to2D(Direction1DType xy, Direction1DType lh) +{ + return Direction2DType(((int)xy<<1)+(int)lh); +} + +/// data traits +/// define a template class of data traits +/// which will make it easier for generic change of data type +template +struct coordinate_traits; + +/// specialization for int +template <> +struct coordinate_traits +{ + typedef float coordinate_type; + typedef double euclidean_distance_type; + typedef long manhattan_distance_type; + typedef long area_type; + typedef unsigned int site_index_type; ///< site index in a row structure + typedef unsigned long site_area_type; ///< number of sites for a region + typedef unsigned int index_type; ///< index (id) + typedef float weight_type; ///< type for net or node weights +}; +/// specialization for unsigned int +template <> +struct coordinate_traits +{ + typedef float coordinate_type; + typedef double euclidean_distance_type; + typedef long manhattan_distance_type; + typedef long area_type; + typedef unsigned int site_index_type; ///< site index in a row structure + typedef unsigned long site_area_type; ///< number of sites for a region + typedef unsigned int index_type; ///< index (id) + typedef float weight_type; ///< type for net or node weights +}; +/// specialization for float +template <> +struct coordinate_traits +{ + typedef float coordinate_type; + typedef double euclidean_distance_type; + typedef double manhattan_distance_type; + typedef double area_type; + typedef unsigned int site_index_type; ///< site index in a row structure + typedef double site_area_type; ///< number of sites for a region + typedef unsigned int index_type; ///< index (id) + typedef float weight_type; ///< type for net or node weights +}; +/// specialization for double +template <> +struct coordinate_traits +{ + typedef float coordinate_type; + typedef long double euclidean_distance_type; + typedef long double manhattan_distance_type; + typedef long double area_type; + typedef unsigned long site_index_type; ///< site index in a row structure + typedef long double site_area_type; ///< number of sites for a region + typedef unsigned long index_type; ///< index (id) + typedef float weight_type; ///< type for net or node weights +}; + +/// type helper for non-const/const +template struct ConstTypeHelper; +template struct ConstTypeHelper +{ + typedef T value_type; + typedef T& reference_type; + typedef T* pointer_type; +}; +template struct ConstTypeHelper +{ + typedef T value_type; + typedef T const& reference_type; + typedef T const* pointer_type; +}; + +/// @brief Match a string with a wildcard pattern. +/// Copied from geeksforgeeks +/// https://www.geeksforgeeks.org/wildcard-pattern-matching/ +class WildcardMatch +{ + public: + /// @param str target string + /// @param pattern target pattern + /// @param n length of string + /// @param m length of pattern + inline bool operator()(const char* str, const char* pattern, std::size_t n, std::size_t m) + { + // empty pattern can only match with + // empty string + if (m == 0) + return (n == 0); + + // lookup table for storing results of + // subproblems + m_n = n; + m_m = m; + m_lookup.resize((n + 1)*(m + 1)); + + // initailze lookup table to false + memset(m_lookup.data(), false, sizeof(unsigned char)*m_lookup.size()); + + // empty pattern can match with empty string + lookup(0, 0) = true; + + // Only '*' can match with empty string + for (std::size_t j = 1; j <= m; j++) + if (pattern[j - 1] == '*') + lookup(0, j) = lookup(0, j - 1); + + // fill the table in bottom-up fashion + for (std::size_t i = 1; i <= n; i++) + { + for (std::size_t j = 1; j <= m; j++) + { + // Two cases if we see a '*' + // a) We ignore ‘*’ character and move + // to next character in the pattern, + // i.e., ‘*’ indicates an empty sequence. + // b) '*' character matches with ith + // character in input + if (pattern[j - 1] == '*') + lookup(i, j) = lookup(i, j - 1) || lookup(i - 1, j); + + // Current characters are considered as + // matching in two cases + // (a) current character of pattern is '?' + // (b) characters actually match + else if (pattern[j - 1] == '?' || + str[i - 1] == pattern[j - 1]) + lookup(i, j) = lookup(i - 1, j - 1); + + // If characters don't match + else lookup(i, j) = false; + } + } + + return lookup(n, m); + } + protected: + inline unsigned char lookup(std::size_t i, std::size_t j) const + { + return m_lookup.at(i*(m_m + 1) + j); + } + inline unsigned char& lookup(std::size_t i, std::size_t j) + { + return m_lookup.at(i*(m_m + 1) + j); + } + + std::vector m_lookup; ///< lookup table + std::size_t m_n; ///< length of string + std::size_t m_m; /// < length of pattern +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp new file mode 100644 index 00000000..8309b8f7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp @@ -0,0 +1,143 @@ +/** + * @file place_io.cpp + * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Mar 2021 + * @brief Python binding + */ + +#include "PyPlaceDB.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// take numpy array +template +bool write(PlaceDB const& db, + std::string const& filename, + pybind11::array_t const& x, + pybind11::array_t const& y + ) +{ + float* vx = NULL; + float* vy = NULL; + + // assume all the movable nodes are in front of fixed nodes + // this is ensured by PlaceDB::sortNodeByPlaceStatus() + PlaceDB::index_type lenx = x.size(); + if (lenx >= db.numMovable()) + { + vx = new float [lenx]; + for (PlaceDB::index_type i = 0; i < lenx; ++i) + { + vx[i] = x.at(i); + } + } + PlaceDB::index_type leny = y.size(); + if (leny >= db.numMovable()) + { + vy = new float [leny]; + for (PlaceDB::index_type i = 0; i < leny; ++i) + { + vy[i] = y.at(i); + } + } + + //bool flag = db.write(filename, ff, vx, vy); + bool flag = db.write(filename, vx, vy); + + if (vx) + { + delete [] vx; + } + if (vy) + { + delete [] vy; + } + + return flag; +} + +/// take numpy array +template +void apply(PlaceDB& db, + pybind11::array_t const& x, + pybind11::array_t const& y, + pybind11::array_t const& z + ) +{ + // assume all the movable nodes are in front of fixed nodes + if (db.numCCNodes() == 0) + { + for (int nIdx = 0; nIdx < db.numMovable()+db.numFixed(); ++nIdx) + { + float xx = x.at(nIdx); + float yy = y.at(nIdx); + PlaceDB::index_type zz = z.at(nIdx); + db.nodeXLocs().at(nIdx) = xx; + db.nodeYLocs().at(nIdx) = yy; + db.nodeZLocs().at(nIdx) = zz; + } + } else + { + for (int nIdx = 0; nIdx < db.numOrgMovable()+db.numFixed(); ++nIdx) + { + float xx = x.at(nIdx); + float yy = y.at(nIdx); + PlaceDB::index_type zz = z.at(nIdx); + db.orgNodeXLocs().at(nIdx) = xx; + db.orgNodeYLocs().at(nIdx) = yy; + db.orgNodeZLocs().at(nIdx) = zz; + } + } +} + +PlaceDB place_io_forward(pybind11::str const& auxPath) +{ + + DREAMPLACE_NAMESPACE::PlaceDB db; + + bool flag; + + // read bookshelf + flag = DREAMPLACE_NAMESPACE::readBookshelf(db, auxPath); + dreamplaceAssertMsg(flag, "Failed to read input Bookshelf files"); + + return db; +} + +DREAMPLACE_END_NAMESPACE + +// create Python binding + +void bind_PlaceDB(pybind11::module&); +void bind_PyPlaceDB(pybind11::module&); + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + + bind_PlaceDB(m); + bind_PyPlaceDB(m); + + m.def("write", [](DREAMPLACE_NAMESPACE::PlaceDB const& db, + std::string const& filename, + pybind11::array_t const& x, + pybind11::array_t const& y) {return write(db, filename, x, y);}, + "Write Placement Solution (float)"); + m.def("write", [](DREAMPLACE_NAMESPACE::PlaceDB const& db, + std::string const& filename, + pybind11::array_t const& x, + pybind11::array_t const& y) {return write(db, filename, x, y);}, + "Write Placement Solution (double)"); + m.def("apply", [](DREAMPLACE_NAMESPACE::PlaceDB& db, + pybind11::array_t const& x, + pybind11::array_t const& y, + pybind11::array_t const& z) {apply(db, x, y, z);}, + "Apply Placement Solution (float)"); + m.def("apply", [](DREAMPLACE_NAMESPACE::PlaceDB& db, + pybind11::array_t const& x, + pybind11::array_t const& y, + pybind11::array_t const& z) {apply(db, x, y, z);}, + "Apply Placement Solution (double)"); + m.def("pydb", [](DREAMPLACE_NAMESPACE::PlaceDB const& db){return DREAMPLACE_NAMESPACE::PyPlaceDB(db);}, "Convert PlaceDB to PyPlaceDB"); + m.def("forward", &DREAMPLACE_NAMESPACE::place_io_forward, "PlaceDB IO Read"); +} + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt new file mode 100644 index 00000000..853b1662 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt @@ -0,0 +1,33 @@ +set(OP_NAME precondWL) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(precondWL_cpp + src/precondWL.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + precondWL_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(precondWL_cuda + src/precondWL_cuda.cpp + src/precondWL_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + precondWL_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py new file mode 100644 index 00000000..995c4cb7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Nov 2020 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py new file mode 100644 index 00000000..04ba16e5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py @@ -0,0 +1,73 @@ +## +# @file precondWL.py +# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) +# @date Nov 2020 +# + +import torch +from torch.autograd import Function +from torch import nn +import numpy as np +import pdb +import time + +import dreamplacefpga.ops.precondWL.precondWL_cpp as precondWL_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.precondWL.precondWL_cuda as precondWL_cuda + +class PrecondWL(nn.Module): + """ + @brief Compute wirelength preconditioner. + """ + def __init__(self, flat_node2pin_start, flat_node2pin, pin2net_map, flat_net2pin, net_weights, num_nodes, num_movable_nodes, device, num_threads): + """ + @brief initialization + @param flat_node2pin_start_map + @param flat_node2pin_map node to pin map + @param flat_netpin flat netpin map, length of #pins + @param pin2net_map pin to net map + @param net_weights weight of nets + @param num_nodes + @param num_threads + """ + super(PrecondWL, self).__init__() + self.flat_node2pin_start=flat_node2pin_start + self.flat_node2pin=flat_node2pin + self.flat_net2pin=flat_net2pin + self.pin2net_map=pin2net_map + self.net_weights=net_weights + self.num_nodes=num_nodes + self.num_movable_nodes=num_movable_nodes + self.num_threads=num_threads + self.device=device + + def forward(self): + out = torch.zeros(self.num_nodes, dtype=torch.float32, device=self.device) + ##TODO - Temporary fix expecting net_weights to be empty + if self.net_weights.shape == []: + net_weights = torch.ones(self.num_nodes, dtype=torch.float32, device=self.device) + else: + net_weights = torch.clamp(self.net_weights, min=1.0) + + if out.is_cuda: + precondWL_cuda.forward( + self.flat_node2pin_start, + self.flat_node2pin, + self.pin2net_map, + self.flat_net2pin, + net_weights, + self.num_movable_nodes, + out) + else: + precondWL_cpp.forward( + net_weights, + self.flat_node2pin_start, + self.flat_node2pin, + self.flat_net2pin, + self.pin2net_map, + self.num_movable_nodes, + self.num_threads, + out) + return out + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp new file mode 100644 index 00000000..2743db50 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp @@ -0,0 +1,105 @@ +/** + * @file precondWL.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Nov 2020 + * @brief Compute precond WL + */ +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computePrecondWLLauncher( + const T *net_weights, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const int *flat_net2pin, + const int *pin2net_map, + int num_nodes, + int num_threads, + T *out + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute wirelength preconditioner +void forward( + at::Tensor net_weights, + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor flat_net2pin, + at::Tensor pin2net_map, + int num_nodes, + int num_threads, + at::Tensor out) +{ + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + CHECK_FLAT(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + + CHECK_FLAT(flat_net2pin); + CHECK_CONTIGUOUS(flat_net2pin); + + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(out, "computePrecondWLLauncher", [&] { + computePrecondWLLauncher( + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + num_nodes, num_threads, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t) + ); + }); +} + +template +int computePrecondWLLauncher( + const T *net_weights, + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const int *flat_net2pin, + const int *pin2net_map, + int num_nodes, + int num_threads, + T *out + ) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes/ num_threads / 16), 1); + //#pragma omp parallel for schedule(static) + //#pragma omp parallel for num_threads(num_threads) + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nodes; ++i) + { + int beg = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i+1]; + + for (int p = beg; p < end; ++p) + { + int netId = pin2net_map[flat_node2pin_map[p]]; + int numPins = flat_net2pin[netId+1] - flat_net2pin[netId]; + //Ignore single pin nets + if (numPins > 1) + { + out[i] += net_weights[netId]/(numPins-1.0); + } + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "PrecondWL forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp new file mode 100644 index 00000000..33122e38 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp @@ -0,0 +1,73 @@ +/** + * @file precondWL_cuda.cpp + * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) + * @date Nov 2020 + * @brief Compute precond WL + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" +using namespace torch::indexing; + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computePrecondWLCudaLauncher( + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const int *pin2net_map, + const int *flat_net2pin, + const T *net_weights, + int num_nodes, + T *out + ); + + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + + +/// @brief Compute half-perimeter wirelength along with net bbox +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_mask an array to record whether compute the where for a net or not +void forward( + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor pin2net_map, + at::Tensor flat_net2pin, + at::Tensor net_weights, + int num_nodes, + at::Tensor out + ) +{ + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + CHECK_FLAT(flat_node2pin_map); + CHECK_CONTIGUOUS(flat_node2pin_map); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(flat_net2pin); + CHECK_CONTIGUOUS(flat_net2pin); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(out, "computePrecondWLCudaLauncher", [&] { + computePrecondWLCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin, int), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t) + ); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "PrecondWL forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu new file mode 100644 index 00000000..74d57f77 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu @@ -0,0 +1,78 @@ +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computePrecondWL( + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const int *pin2net_map, + const int *flat_net2pin, + const T *net_weights, + int num_nodes, + T *out + ) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nodes) + { + int beg = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i+1]; + + for (int p = beg; p < end; ++p) + { + int netId = pin2net_map[flat_node2pin_map[p]]; + int numPins = flat_net2pin[netId+1] - flat_net2pin[netId]; + //Ignore single pin nets + if (numPins > 1) + { + out[i] += net_weights[netId]/(numPins-1.0); + } + } + } +} + +template +int computePrecondWLCudaLauncher( + const int *flat_node2pin_start_map, + const int *flat_node2pin_map, + const int *pin2net_map, + const int *flat_net2pin, + const T *net_weights, + int num_nodes, + T *out + ) +{ + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + + computePrecondWL<<>>( + flat_node2pin_start_map, + flat_node2pin_map, + pin2net_map, + flat_net2pin, + net_weights, + num_nodes, + out + ); + + return 0; +} + +// manually instantiate the template function +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computePrecondWLCudaLauncher( \ + const int *flat_node2pin_start_map, \ + const int *flat_node2pin_map, \ + const int *pin2net_map, \ + const int *flat_net2pin, \ + const T *net_weights, \ + int num_nodes, \ + T *out \ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt new file mode 100644 index 00000000..8e84a8bc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt @@ -0,0 +1,24 @@ +set(OP_NAME rmst_wl) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${FLUTE_INCLUDE_DIRS} + ) + +set(LINK_LIBS utility_cxx + ${FLUTE_LINK_DIRS}/libflute.a + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/rmst_wl.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py new file mode 100644 index 00000000..7356da9b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py new file mode 100644 index 00000000..33dce44c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py @@ -0,0 +1,50 @@ +## +# @file rmst_wl.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# + +import torch +from torch.autograd import Function +from torch import nn + +import dreamplace.ops.rmst_wl.rmst_wl_cpp as rmst_wl_cpp + +class RMSTWLFunction(Function): + """compute half-perimeter wirelength. + @param pos pin location (x array, y array), not cell location + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param ignore_net_degree ignore nets with degree larger than some value + """ + @staticmethod + def forward(ctx, pos, flat_netpin, netpin_start, ignore_net_degree, read_lut_flag, POWVFILE, POSTFILE): + output = pos.new_empty(netpin_start.numel()-1) + if pos.is_cuda: + assert 0, "CUDA version NOT IMPLEMENTED" + rmst_wl_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, ignore_net_degree, output) + else: + rmst_wl_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, ignore_net_degree, read_lut_flag, POWVFILE, POSTFILE, output) + return output + + +class RMSTWL(nn.Module): + def __init__(self, flat_netpin, netpin_start, ignore_net_degree=None, POWVFILE="POWV9.dat", POSTFILE="POST9.dat"): + super(RMSTWL, self).__init__() + self.flat_netpin = flat_netpin + self.netpin_start = netpin_start + if ignore_net_degree is None: + self.ignore_net_degree = self.flat_netpin.numel() + else: + self.ignore_net_degree = ignore_net_degree + self.POWVFILE = POWVFILE + self.POSTFILE = POSTFILE + def forward(self, pos, read_lut_flag): + return RMSTWLFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.ignore_net_degree, + read_lut_flag, + self.POWVFILE, + self.POSTFILE + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp new file mode 100644 index 00000000..b14897e4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp @@ -0,0 +1,120 @@ +/** + * @file rmst_wl.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +extern "C" +{ +#include +} + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeRMSTWLLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const int ignore_net_degree, + int num_nets, + int read_lut_flag, + const char* POWVFILE, + const char* POSTFILE, + T* rmst_wl + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +int rmst_wl_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + int ignore_net_degree, + int read_lut_flag, + const char* POWVFILE, + const char* POSTFILE, + at::Tensor rmst_wl) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(rmst_wl); + CHECK_CONTIGUOUS(rmst_wl); + + int ret = 0; + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeRMSTWLLauncher", [&] { + computeRMSTWLLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + ignore_net_degree, + netpin_start.numel()-1, + read_lut_flag, + POWVFILE, + POSTFILE, + DREAMPLACE_TENSOR_DATA_PTR(rmst_wl, scalar_t) + ); + }); + return ret; +} + +template +int computeRMSTWLLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const int ignore_net_degree, + int num_nets, + int read_lut_flag, + const char* POWVFILE, + const char* POSTFILE, + T* rmst_wl + ) +{ + // read look-up table for flute + if (read_lut_flag) + { + readLUT(POWVFILE, POSTFILE); + } + // temporary store x and y positions + std::vector vx (ignore_net_degree, 0); + std::vector vy (ignore_net_degree, 0); + int scale = 1000; // scale factor, flute only supports integer + for (int i = 0; i < num_nets; ++i) + { + int degree = netpin_start[i+1]-netpin_start[i]; + // ignore large degree nets + if (degree >= ignore_net_degree) + { + rmst_wl[i] = 0; + continue; + } + + std::fill(vx.begin(), vx.end(), 0); + std::fill(vy.begin(), vy.end(), 0); + for (int j = netpin_start[i], k = 0; j < netpin_start[i+1]; ++j, ++k) + { + vx[k] = x[flat_netpin[j]]*scale; + vy[k] = y[flat_netpin[j]]*scale; + } + int wl = flute_wl(degree, vx.data(), vy.data(), ACCURACY); + rmst_wl[i] = wl/(T)scale; + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::rmst_wl_forward, "RMSTWL forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt new file mode 100644 index 00000000..9cbb90a9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt @@ -0,0 +1,36 @@ +set(OP_NAME rudy) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/rudy.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) + +add_pytorch_extension(${TARGET_NAME}_cuda + src/rudy_cuda.cpp + src/rudy_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py new file mode 100644 index 00000000..7caf7dcd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Dec 2019 +# + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py new file mode 100644 index 00000000..7f1e822d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py @@ -0,0 +1,106 @@ +''' +@File rudy.py +@Author: Jake Gu (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) +@Date Apr 2023 +''' + +import math +import torch +from torch import nn +from torch.autograd import Function +import matplotlib.pyplot as plt +import pdb + +import dreamplacefpga.ops.rudy.rudy_cpp as rudy_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.rudy.rudy_cuda as rudy_cuda + +class Rudy(nn.Module): + def __init__(self, + netpin_start, flat_netpin, net_weights, + xl, xh, yl, yh, + num_bins_x, num_bins_y, + unit_horizontal_capacity, + unit_vertical_capacity, + deterministic_flag, + initial_horizontal_utilization_map=None, + initial_vertical_utilization_map=None, + num_threads=None + ): + super(Rudy, self).__init__() + self.netpin_start = netpin_start + self.flat_netpin = flat_netpin + self.net_weights = net_weights + self.xl = xl + self.yl = yl + self.xh = xh + self.yh = yh + self.num_threads = num_threads + self.num_bins_x = num_bins_x + self.num_bins_y = num_bins_y + self.bin_size_x = (xh - xl) / num_bins_x + self.bin_size_y = (yh - yl) / num_bins_y + + # initialize parameters + self.unit_horizontal_capacity = unit_horizontal_capacity + self.unit_vertical_capacity = unit_vertical_capacity + + self.deterministic_flag = deterministic_flag + + self.initial_horizontal_utilization_map = initial_horizontal_utilization_map + self.initial_vertical_utilization_map = initial_vertical_utilization_map + + #plt.imsave("rudy_initial.png", (self.initial_horizontal_utilization_map + self.initial_vertical_utilization_map).data.cpu().numpy().T, origin='lower') + + def forward(self, pin_pos): + horizontal_utilization_map = torch.zeros((self.num_bins_x, self.num_bins_y), dtype=pin_pos.dtype, device=pin_pos.device) + vertical_utilization_map = torch.zeros_like(horizontal_utilization_map) + if pin_pos.is_cuda: + rudy_cuda.forward( + pin_pos, + self.netpin_start, + self.flat_netpin, + self.net_weights, + self.bin_size_x, + self.bin_size_y, + self.xl, + self.yl, + self.xh, + self.yh, + self.num_bins_x, + self.num_bins_y, + self.deterministic_flag, + horizontal_utilization_map, + vertical_utilization_map + ) + else: + rudy_cpp.forward( + pin_pos, + self.netpin_start, + self.flat_netpin, + self.net_weights, + self.bin_size_x, + self.bin_size_y, + self.xl, + self.yl, + self.xh, + self.yh, + self.num_bins_x, + self.num_bins_y, + self.num_threads, + self.deterministic_flag, + horizontal_utilization_map, + vertical_utilization_map + ) + + # convert demand to utilization in each bin + bin_area = self.bin_size_x * self.bin_size_y + horizontal_utilization_map.mul_(1 / (bin_area * self.unit_horizontal_capacity)) + vertical_utilization_map.mul_(1 / (bin_area * self.unit_vertical_capacity)) + + # infinity norm + route_utilization_map = torch.max(horizontal_utilization_map.abs_(), vertical_utilization_map.abs_()) + + return route_utilization_map + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h new file mode 100644 index 00000000..371dc002 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h @@ -0,0 +1,49 @@ +#ifndef DREAMPLACE_ROUTABILITY_PARAMETERS_H +#define DREAMPLACE_ROUTABILITY_PARAMETERS_H + +/// The function returns the mean wirelength weight for a net with 'num_pins' pins using wiring distribution map (WDM). +/// For more details, refer the paper 'RISA: Accurate and efficient placement routability modeling' +// WARNING: the 'low ... high' syntax below is a GCC extension +// It is not guaranteed to be supported by other compilers +#define DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT \ + T netWiringDistributionMapWeight(int num_pins) \ + { \ + \ + switch (num_pins) \ + { \ + case 1: case 2: case 3: \ + return 1.0000; \ + case 4: \ + return 1.0828; \ + case 5: \ + return 1.1536; \ + case 6: \ + return 1.2206; \ + case 7: \ + return 1.2823; \ + case 8: \ + return 1.3385; \ + case 9: \ + return 1.3991; \ + case 10: \ + return 1.4493; \ + case 11: case 12: case 13: case 14: case 15: \ + return 1.6899; \ + case 16: case 17: case 18: case 19: case 20: \ + return 1.8924; \ + case 21: case 22: case 23: case 24: case 25: \ + return 2.0743; \ + case 26: case 27: case 28: case 29: case 30: \ + return 2.2334; \ + case 31: case 32: case 33: case 34: case 35: \ + return 2.3892; \ + case 36: case 37: case 38: case 39: case 40: \ + return 2.5356; \ + case 41: case 42: case 43: case 44: case 45: \ + return 2.6625; \ + default: \ + return 2.7933; \ + } \ + } + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp new file mode 100644 index 00000000..53a46c59 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp @@ -0,0 +1,188 @@ +/** + * @file rudy.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/torch.h" +#include "utility/src/utils.h" +#include "rudy/src/parameters.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +template +inline DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT; + +// fill the demand map net by net +template +int rudyLauncher( + const T *pin_pos_x, + const T *pin_pos_y, + const int *netpin_start, + const int *flat_netpin, + const T *net_weights, + const T bin_size_x, + const T bin_size_y, + T xl, T yl, + T xh, T yh, + int num_bins_x, + int num_bins_y, + int num_nets, + int num_threads, + AtomicOp atomic_add_op, + typename AtomicOp::type *horizontal_utilization_map, + typename AtomicOp::type *vertical_utilization_map) +{ + const T inv_bin_size_x = 1.0 / bin_size_x; + const T inv_bin_size_y = 1.0 / bin_size_y; + + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); + #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + T x_max = -std::numeric_limits::max(); + T x_min = std::numeric_limits::max(); + T y_max = -std::numeric_limits::max(); + T y_min = std::numeric_limits::max(); + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + const T xx = pin_pos_x[pin_id]; + x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); + x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); + const T yy = pin_pos_y[pin_id]; + y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); + y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); + } + + // compute the bin box that this net will affect + int bin_index_xl = int((x_min - xl) * inv_bin_size_x); + int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + int bin_index_yl = int((y_min - yl) * inv_bin_size_y); + int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + T wt = netWiringDistributionMapWeight(netpin_start[i + 1] - netpin_start[i]); + if (net_weights) + { + wt *= net_weights[i]; + } + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + T bin_xl = xl + x * bin_size_x; + T bin_yl = yl + y * bin_size_y; + T bin_xh = bin_xl + bin_size_x; + T bin_yh = bin_yl + bin_size_y; + T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * + DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); + overlap *= wt; + int index = x * num_bins_y + y; + // Following Wuxi's implementation, a tolerance is added to avoid 0-size bounding box + atomic_add_op(&horizontal_utilization_map[index], overlap / (y_max - y_min + std::numeric_limits::epsilon())); + atomic_add_op(&vertical_utilization_map[index], overlap / (x_max - x_min + std::numeric_limits::epsilon())); + } + } + } + return 0; +} + +void rudy_forward( + at::Tensor pin_pos, + at::Tensor netpin_start, + at::Tensor flat_netpin, + at::Tensor net_weights, + double bin_size_x, + double bin_size_y, + double xl, + double yl, + double xh, + double yh, + int num_bins_x, + int num_bins_y, + int num_threads, + int deterministic_flag, + at::Tensor horizontal_utilization_map, + at::Tensor vertical_utilization_map + ) +{ + CHECK_FLAT(pin_pos); + CHECK_EVEN(pin_pos); + CHECK_CONTIGUOUS(pin_pos); + + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pin_pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pin_pos, "rudyLauncher", [&] { + if (deterministic_flag == 1) { + double diearea = (xh - xl) * (yh - yl); + int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); + long scale_factor = (1L << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + + std::vector horizontal_buf_map(num_bins, 0); + std::vector vertical_buf_map(num_bins, 0); + AtomicAdd atomic_add_op(scale_factor); + + rudyLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + (net_weights.numel()) ? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, + bin_size_x, bin_size_y, xl, yl, xh, yh, + num_bins_x, num_bins_y, num_nets, num_threads, + atomic_add_op, horizontal_buf_map.data(), vertical_buf_map.data()); + + scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), + horizontal_buf_map.data(), 1.0 / scale_factor, num_bins, num_threads); + scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t), + vertical_buf_map.data(), 1.0 / scale_factor, num_bins, num_threads); + } else { + AtomicAdd atomic_add_op; + rudyLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + (net_weights.numel()) ? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, + bin_size_x, bin_size_y, xl, yl, xh, yh, + num_bins_x, num_bins_y, num_nets, num_threads, + atomic_add_op, + DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t)); + } + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::rudy_forward, "compute RUDY map"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp new file mode 100644 index 00000000..009430b7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp @@ -0,0 +1,93 @@ +/** + * @file rudy_cuda.cpp + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) + * @date Apr 2023 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +// fill the demand map net by net +template +int rudyCudaLauncher(const T *pin_pos_x, + const T *pin_pos_y, + const int *netpin_start, + const int *flat_netpin, + const T *net_weights, + const T bin_size_x, + const T bin_size_y, + const T xl, const T yl, + const T xh, const T yh, + const int num_bins_x, + const int num_bins_y, + const int num_nets, + bool deterministic_flag, + T *horizontal_utilization_map, + T *vertical_utilization_map); + +void rudy_forward( + at::Tensor pin_pos, + at::Tensor netpin_start, + at::Tensor flat_netpin, + at::Tensor net_weights, + double bin_size_x, + double bin_size_y, + double xl, + double yl, + double xh, + double yh, + int num_bins_x, + int num_bins_y, + int deterministic_flag, + at::Tensor horizontal_utilization_map, + at::Tensor vertical_utilization_map + ) +{ + CHECK_FLAT(pin_pos); + CHECK_EVEN(pin_pos); + CHECK_CONTIGUOUS(pin_pos); + + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pin_pos.numel() / 2; + + // Call the cuda kernel launcher + DREAMPLACE_DISPATCH_FLOATING_TYPES(pin_pos, "rudyCudaLauncher", [&] { + rudyCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + (net_weights.numel())? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, + bin_size_x, bin_size_y, + xl, yl, xh, yh, + num_bins_x, num_bins_y, num_nets, + (bool)deterministic_flag, + DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t)); + }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::rudy_forward, "compute RUDY map (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu new file mode 100644 index 00000000..e99b5114 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu @@ -0,0 +1,190 @@ +/** + * @file rudy_cuda_kernel.cu + * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) + * @date Dec 2019 + * @brief Compute the RUDY/RISA map for routing demand. + * A routing/pin utilization estimator based on the following two papers + * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 + * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 + */ + +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" +#include "rudy/src/parameters.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +inline __device__ DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT; + +template +__global__ void rudy( + const T *pin_pos_x, + const T *pin_pos_y, + const int *netpin_start, + const int *flat_netpin, + const T *net_weights, + const T bin_size_x, + const T bin_size_y, + const T xl, const T yl, + const T xh, const T yh, + const int num_bins_x, + const int num_bins_y, + const int num_nets, + AtomicOp atomic_add_op, + typename AtomicOp::type *horizontal_utilization_map, + typename AtomicOp::type *vertical_utilization_map) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nets) + { + const int start = netpin_start[i]; + const int end = netpin_start[i + 1]; + + T x_max = -cuda::numeric_limits::max(); + T x_min = cuda::numeric_limits::max(); + T y_max = -cuda::numeric_limits::max(); + T y_min = cuda::numeric_limits::max(); + + for (int j = start; j < end; ++j) + { + int pin_id = flat_netpin[j]; + const T xx = pin_pos_x[pin_id]; + x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); + x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); + const T yy = pin_pos_y[pin_id]; + y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); + y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); + } + + // compute the bin box that this net will affect + int bin_index_xl = int((x_min - xl) / bin_size_x); + int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; + bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); + bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); + + int bin_index_yl = int((y_min - yl) / bin_size_y); + int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; + bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); + bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); + + T wt = netWiringDistributionMapWeight(end - start); + if (net_weights) + { + wt *= net_weights[i]; + } + + for (int x = bin_index_xl; x < bin_index_xh; ++x) + { + for (int y = bin_index_yl; y < bin_index_yh; ++y) + { + T bin_xl = xl + x * bin_size_x; + T bin_yl = yl + y * bin_size_y; + T bin_xh = bin_xl + bin_size_x; + T bin_yh = bin_yl + bin_size_y; + T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * + DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); + overlap *= wt; + int index = x * num_bins_y + y; + // Following Wuxi's implementation, a tolerance is added to avoid 0-size bounding box + atomic_add_op(&horizontal_utilization_map[index], overlap / (y_max - y_min + cuda::numeric_limits::epsilon())); + atomic_add_op(&vertical_utilization_map[index], overlap / (x_max - x_min + cuda::numeric_limits::epsilon())); + } + } + } +} + +// fill the demand map net by net +template +int rudyCudaLauncher(const T *pin_pos_x, + const T *pin_pos_y, + const int *netpin_start, + const int *flat_netpin, + const T *net_weights, + const T bin_size_x, + const T bin_size_y, + const T xl, const T yl, + const T xh, const T yh, + const int num_bins_x, + const int num_bins_y, + const int num_nets, + bool deterministic_flag, + T *horizontal_utilization_map, + T *vertical_utilization_map) +{ + if (deterministic_flag) + { + // total die area + double diearea = (xh - xl) * (yh - yl); + int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); + int fraction_bits = max(64 - integer_bits, 0); + unsigned long long int scale_factor = (1UL << fraction_bits); + int num_bins = num_bins_x * num_bins_y; + unsigned long long int *buf_map = NULL; + allocateCUDA(buf_map, num_bins*2, unsigned long long int); + + unsigned long long int *horizontal_buf_map = buf_map; + unsigned long long int *vertical_buf_map = buf_map + num_bins; + + AtomicAddCUDA atomic_add_op(scale_factor); + + int thread_count = 512; + int block_count = ceilDiv(num_bins, thread_count); + + copyScaleArray<<>>( + horizontal_buf_map, horizontal_utilization_map, scale_factor, num_bins); + copyScaleArray<<>>( + vertical_buf_map, vertical_utilization_map, scale_factor, num_bins); + + block_count = ceilDiv(num_nets, thread_count); + rudy<<>>( + pin_pos_x, pin_pos_y, netpin_start, + flat_netpin, net_weights, bin_size_x, + bin_size_y, xl, yl, xh, yh, + num_bins_x, num_bins_y, num_nets, + atomic_add_op, + horizontal_buf_map, + vertical_buf_map + ); + + block_count = ceilDiv(num_bins, thread_count); + copyScaleArray<<>>( + horizontal_utilization_map, horizontal_buf_map, T(1.0 / scale_factor), num_bins); + copyScaleArray<<>>( + vertical_utilization_map, vertical_buf_map, T(1.0 / scale_factor), num_bins); + + destroyCUDA(buf_map); + } else + { + AtomicAddCUDA atomic_add_op; + int thread_count = 512; + int block_count = ceilDiv(num_nets, thread_count); + rudy<<>>( + pin_pos_x, pin_pos_y, netpin_start, + flat_netpin, net_weights, bin_size_x, + bin_size_y, xl, yl, xh, yh, + num_bins_x, num_bins_y, num_nets, + atomic_add_op, + horizontal_utilization_map, + vertical_utilization_map + ); + } + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int rudyCudaLauncher( \ + const T *pin_pos_x, const T *pin_pos_y, \ + const int *netpin_start, const int *flat_netpin, \ + const T *net_weights, const T bin_size_x, \ + const T bin_size_y, const T xl, const T yl, \ + const T xh, const T yh, const int num_bins_x, \ + const int num_bins_y, const int num_nets, \ + bool deterministic_flag, \ + T *horizontal_utilization_map, \ + T *vertical_utilization_map); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt new file mode 100644 index 00000000..7aef9a01 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt @@ -0,0 +1,35 @@ +set(OP_NAME sortNode2Pin) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/sortNode2Pin.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) +add_pytorch_extension(${TARGET_NAME}_cuda + src/sortNode2Pin_cuda.cpp + src/sortNode2Pin_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py new file mode 100644 index 00000000..11284438 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py @@ -0,0 +1,5 @@ +## +# @file __init__.py +# @author Rachel Selina (DREAMPlaceFPGA-PL) +# @date Nov 2021 +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py new file mode 100644 index 00000000..c460595c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py @@ -0,0 +1,58 @@ +## +# @file sortNode2Pin.py +# @author Rachel Selina (DREAMPlaceFPGA-PL) +# @date Nov 2021 +# + +import torch +from torch.autograd import Function +from torch import nn +import numpy as np +import pdb +import time + +import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin_cpp as sortNode2Pin_cpp +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin_cuda as sortNode2Pin_cuda + +class SortNode2Pin(nn.Module): + """ + @brief Compute wirelength preconditioner. + """ + def __init__(self, flat_node2pin_start, flat_node2pin, num_nodes, device, num_threads): + """ + @brief initialization + @param flat_node2pin_start_map + @param flat_node2pin_map node to pin map + @param num_nodes + @param num_threads + """ + super(SortNode2Pin, self).__init__() + self.flat_node2pin_start=flat_node2pin_start + self.flat_node2pin=flat_node2pin + self.num_nodes=num_nodes + self.device=device + self.num_threads = num_threads + + def forward(self, sorted_pin_map): + node2pinId = torch.zeros(self.num_nodes, dtype=torch.int32, device=self.device) + + if node2pinId.is_cuda: + sortNode2Pin_cuda.forward( + self.flat_node2pin_start, + self.flat_node2pin, + sorted_pin_map, + self.num_nodes, + node2pinId) + else: + sortNode2Pin_cpp.forward( + self.flat_node2pin_start, + self.flat_node2pin, + sorted_pin_map, + self.num_nodes, + self.num_threads, + node2pinId) + + return node2pinId + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp new file mode 100644 index 00000000..0dc31ba6 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp @@ -0,0 +1,78 @@ +/** + * @file sortNode2Pin.cpp + * @author Rachel Selina (DREAMPlaceFPGA-PL) + * @date Nov 2021 + * @brief sort node2pin + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +int computeSortNode2PinLauncher( + const int *flat_node2pin_start_map, + int *flat_node2pin_map, + const int *sorted_pin_map, + const int num_nodes, + const int num_threads, + int *out + ); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Sort node2pin +void forward( + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor sorted_pin_map, + int num_nodes, + int num_threads, + at::Tensor out) +{ + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + //CHECK_FLAT(flat_node2pin_map); + //CHECK_CONTIGUOUS(flat_node2pin_map); + CHECK_FLAT(sorted_pin_map); + CHECK_CONTIGUOUS(sorted_pin_map); + + //DREAMPLACE_DISPATCH_FLOATING_TYPES(out.type(), "computeSortNode2PinLauncher", [&] { + computeSortNode2PinLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_pin_map, int), + num_nodes, + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(out, int) + ); + // }); +} + +int computeSortNode2PinLauncher( + const int *flat_node2pin_start_map, + int *flat_node2pin_map, + const int *sorted_pin_map, + const int num_nodes, + const int num_threads, + int *out + ) +{ + #pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < num_nodes; ++i) + { + int beg = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i+1]; + + std::sort(flat_node2pin_map+beg, flat_node2pin_map+end, [&sorted_pin_map](const auto &a, const auto &b){return sorted_pin_map[a] < sorted_pin_map[b];}); + out[i] = sorted_pin_map[flat_node2pin_map[beg]]; + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "SortNode2Pin forward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp new file mode 100644 index 00000000..d78907a2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp @@ -0,0 +1,60 @@ +/** + * @file sortNode2Pin_cuda.cpp + * @author Rachel Selina (DREAMPlaceFPGA-PL) + * @date Nov 2021 + * @brief sort node2pin + */ + +#include "utility/src/torch.h" +#include "utility/src/Msg.h" +using namespace torch::indexing; + +DREAMPLACE_BEGIN_NAMESPACE + +int computeSortNode2PinCudaLauncher( + const int *flat_node2pin_start_map, + int *flat_node2pin_map, + const int *sorted_pin_map, + const int num_nodes, + int *out + ); + + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + + +/// @brief sort node2pin map +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +void forward( + at::Tensor flat_node2pin_start_map, + at::Tensor flat_node2pin_map, + at::Tensor sorted_pin_map, + int num_nodes, + at::Tensor out + ) +{ + CHECK_FLAT(flat_node2pin_start_map); + CHECK_CONTIGUOUS(flat_node2pin_start_map); + //CHECK_FLAT(flat_node2pin_map); + //CHECK_CONTIGUOUS(flat_node2pin_map); + CHECK_FLAT(sorted_pin_map); + CHECK_CONTIGUOUS(sorted_pin_map); + + //DREAMPLACE_DISPATCH_FLOATING_TYPES(out.type(), "computeSortNode2PinCudaLauncher", [&] { + computeSortNode2PinCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), + DREAMPLACE_TENSOR_DATA_PTR(sorted_pin_map, int), + num_nodes, + DREAMPLACE_TENSOR_DATA_PTR(out, int) + ); + // }); +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &DREAMPLACE_NAMESPACE::forward, "SortNode2Pin forward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu new file mode 100644 index 00000000..5b6b900e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu @@ -0,0 +1,83 @@ +/** + * @file sortNode2Pin.cpp + * @author Rachel Selina (DREAMPlaceFPGA-PL) + * @date Nov 2021 + * @brief sort node2pin + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utility/src/utils.cuh" +#include "utility/src/limits.h" + +DREAMPLACE_BEGIN_NAMESPACE + +__global__ void computeSortNode2Pin( + const int *flat_node2pin_start_map, + int *flat_node2pin_map, + const int *sorted_pin_map, + const int num_nodes, + int *out + ) +{ + const int i = threadIdx.x + blockDim.x * blockIdx.x; + if (i < num_nodes) + { + int beg = flat_node2pin_start_map[i]; + int end = flat_node2pin_start_map[i+1]; + + //Sort + for (int ix = beg+1; ix < end; ++ix) + { + for (int jx = beg; jx < end-1; ++jx) + { + if (sorted_pin_map[flat_node2pin_map[jx]] > sorted_pin_map[flat_node2pin_map[jx+1]]) + { + int val = flat_node2pin_map[jx]; + flat_node2pin_map[jx] = flat_node2pin_map[jx+1]; + flat_node2pin_map[jx+1] = val; + } + } + } + //Sort + + out[i] = sorted_pin_map[flat_node2pin_map[beg]]; + } +} + +int computeSortNode2PinCudaLauncher( + const int *flat_node2pin_start_map, + int *flat_node2pin_map, + const int *sorted_pin_map, + const int num_nodes, + int *out + ) +{ + int thread_count = 512; + int block_count = ceilDiv(num_nodes, thread_count); + + computeSortNode2Pin<<>>( + flat_node2pin_start_map, + flat_node2pin_map, + sorted_pin_map, + num_nodes, + out + ); + + return 0; +} + +// manually instantiate the template function +#define REGISTER_KERNEL_LAUNCHER() \ + template int computeSortNode2PinCudaLauncher() \ + (const int *flat_node2pin_start_map, int *flat_node2pin_map, \ + const int *sorted_pin_map, const int num_nodes, int *out ); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt new file mode 100644 index 00000000..421f1261 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt @@ -0,0 +1,19 @@ +set(OP_NAME utility) + +file(GLOB CPP_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c" + ) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS "") + +add_torch_extension(${TARGET_NAME}_cxx ${CPP_SOURCES} + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS} + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h new file mode 100644 index 00000000..ff8f4612 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h @@ -0,0 +1,120 @@ +/** + * @file Box.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef _DREAMPLACE_UTILITY_BOX_H +#define _DREAMPLACE_UTILITY_BOX_H + +#include +#include "utility/src/limits.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct Box +{ + T xl; + T yl; + T xh; + T yh; + + /// @brief default constructor + DREAMPLACE_HOST_DEVICE Box() { invalidate(); } + + /// @brief constructor + /// @param xxl xl + /// @param yyl yl + /// @param xxh xh + /// @param yyh yh + DREAMPLACE_HOST_DEVICE Box(T xxl, T yyl, T xxh, T yyh) + : xl(xxl), yl(yyl), xh(xxh), yh(yyh) {} + /// @brief invalidate the box + DREAMPLACE_HOST_DEVICE void invalidate() { + xl = cuda::numeric_limits::max(); + yl = cuda::numeric_limits::max(); + xh = cuda::numeric_limits::lowest(); + yh = cuda::numeric_limits::lowest(); + } + /// @brief check if the box is valid + DREAMPLACE_HOST_DEVICE bool valid() const { return (xl <= xh) && (yl <= yh); } + /// @brief encompass a point + /// @param x + /// @param y + DREAMPLACE_HOST_DEVICE void encompass(T x, T y) { + xl = DREAMPLACE_STD_NAMESPACE::min(xl, x); + xh = DREAMPLACE_STD_NAMESPACE::max(xh, x); + yl = DREAMPLACE_STD_NAMESPACE::min(yl, y); + yh = DREAMPLACE_STD_NAMESPACE::min(yh, y); + } + /// @brief encompass a box + /// @param xxl xl + /// @param yyl yl + /// @param xxh xh + /// @param yyh yh + DREAMPLACE_HOST_DEVICE void encompass(T xxl, T yyl, T xxh, T yyh) { + encompass(xxl, yyl); + encompass(xxh, yyh); + } + /// @brief bloat x direction by 2*dx, and y direction by 2*dy + /// @param dx + /// @param dy + DREAMPLACE_HOST_DEVICE void bloat(T dx, T dy) { + xl -= dx; + xh += dx; + yl -= dy; + yh += dy; + } + /// @brief check if a point is contained by the box + /// @param x + /// @param y + /// @return true if contains + DREAMPLACE_HOST_DEVICE bool contains(T x, T y) const { + return xl <= x && x <= xh && yl <= y && y <= yh; + } + /// @brief check if a box is contained by the box + /// @param xxl xl + /// @param yyl yl + /// @param xxh xh + /// @param yyh yh + /// @return true if contains + DREAMPLACE_HOST_DEVICE bool contains(T xxl, T yyl, T xxh, T yyh) const { + return contains(xxl, yyl) && contains(xxh, yyh); + } + /// @return width of the box + DREAMPLACE_HOST_DEVICE T width() const { return xh - xl; } + /// @return height of the box + DREAMPLACE_HOST_DEVICE T height() const { return yh - yl; } + /// @return x coordinate of the center of the box + DREAMPLACE_HOST_DEVICE T center_x() const { return (xl + xh) / 2; } + /// @return y coordinate of the center of the box + DREAMPLACE_HOST_DEVICE T center_y() const { return (yl + yh) / 2; } + /// @return center manhattan distance to another box + DREAMPLACE_HOST_DEVICE T center_distance(const Box& rhs) const { + return fabs(rhs.center_x() - center_x()) + + fabs(rhs.center_y() - center_y()); + } + /// @return area of the box + DREAMPLACE_HOST_DEVICE T area() const { return width() * height(); } + /// @brief print the box + DREAMPLACE_HOST_DEVICE void print() const { + printf("(%g, %g, %g, %g)\n", (double)xl, (double)yl, (double)xh, + (double)yh); + } +}; + +/// @brief simplest box for shared memory +/// Non-empty constructor may result in data race +template +struct SharedBox { + T xl; + T yl; + T xh; + T yh; +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h new file mode 100644 index 00000000..e0be9fa9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h @@ -0,0 +1,92 @@ +/** + * @file ComplexNumber.h + * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) + * @date Aug 2019 + * @brief Complex number for CPU + */ + +#ifndef DREAMPLACE_UTILITY_COMPLEXNUMBER_H +#define DREAMPLACE_UTILITY_COMPLEXNUMBER_H + +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct ComplexType +{ + T x; + T y; + DREAMPLACE_HOST_DEVICE ComplexType() { + x = 0; + y = 0; + } + + DREAMPLACE_HOST_DEVICE ComplexType(T real, T imag) { + x = real; + y = imag; + } + + DREAMPLACE_HOST_DEVICE ~ComplexType() {} +}; + +template +inline DREAMPLACE_HOST_DEVICE ComplexType complexMul( + const ComplexType &x, const ComplexType &y) { + ComplexType res; + res.x = x.x * y.x - x.y * y.y; + res.y = x.x * y.y + x.y * y.x; + return res; +} + +template +inline DREAMPLACE_HOST_DEVICE T RealPartOfMul(const ComplexType &x, + const ComplexType &y) { + return x.x * y.x - x.y * y.y; +} + +template +inline DREAMPLACE_HOST_DEVICE T ImaginaryPartOfMul(const ComplexType &x, + const ComplexType &y) { + return x.x * y.y + x.y * y.x; +} + +template +inline DREAMPLACE_HOST_DEVICE ComplexType complexAdd( + const ComplexType &x, const ComplexType &y) { + ComplexType res; + res.x = x.x + y.x; + res.y = x.y + y.y; + return res; +} + +template +inline DREAMPLACE_HOST_DEVICE ComplexType complexSubtract( + const ComplexType &x, const ComplexType &y) { + ComplexType res; + res.x = x.x - y.x; + res.y = x.y - y.y; + return res; +} + +template +inline DREAMPLACE_HOST_DEVICE ComplexType complexConj( + const ComplexType &x) { + ComplexType res; + res.x = x.x; + res.y = -x.y; + return res; +} + +template +inline DREAMPLACE_HOST_DEVICE ComplexType complexMulConj( + const ComplexType &x, const ComplexType &y) { + ComplexType res; + res.x = x.x * y.x - x.y * y.y; + res.y = -(x.x * y.y + x.y * y.x); + return res; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh new file mode 100644 index 00000000..8da5de47 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh @@ -0,0 +1,566 @@ +/** + * @file DetailedPlaceDB.cuh + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDB_CUH +#define _DREAMPLACE_UTILITY_DETAILEDPLACEDB_CUH + +#include +#include "utility/src/utils.cuh" +#include "utility/src/utils_cub.cuh" +#include "legality_check/src/legality_check.h" +#include "draw_place/src/draw_place.h" +//#include +//#include +//#include +//#include +//#include + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct Space +{ + T xl; + T xh; +}; + +struct BinMapIndex +{ + int bin_id; + int sub_id; +}; + +struct RowMapIndex +{ + int row_id; + int sub_id; +}; + +/// @brief a wrapper class of required data for detailed placement +template +struct DetailedPlaceDB +{ + typedef T type; + + const T* init_x; + const T* init_y; + const T* node_size_x; + const T* node_size_y; + const T* flat_region_boxes; ///< number of boxes x 4 + const int* flat_region_boxes_start; ///< number of regions + 1 + const int* node2fence_region_map; ///< length of number of movable cells + T* x; + T* y; + const int* flat_net2pin_map; + const int* flat_net2pin_start_map; + const int* pin2net_map; + const int* flat_node2pin_map; + const int* flat_node2pin_start_map; + const int* pin2node_map; + const T* pin_offset_x; + const T* pin_offset_y; + const unsigned char* net_mask; + T xl; + T yl; + T xh; + T yh; + T site_width; + T row_height; + T bin_size_x; + T bin_size_y; + int num_bins_x; + int num_bins_y; + int num_sites_x; + int num_sites_y; + int num_nodes; + int num_movable_nodes; + int num_nets; + int num_pins; + int num_regions; ///< number of regions for flat_region_boxes and flat_region_boxes_start + + inline __device__ int pos2site_x(T xx) const + { + return min(max((int)floorDiv((xx-xl), site_width), 0), num_sites_x-1); + } + inline __device__ int pos2site_y(T yy) const + { + return min(max((int)floorDiv((yy-yl), row_height), 0), num_sites_y-1); + } + /// @brief site index as an upper bound + inline __device__ int pos2site_ub_x(T xx) const + { + return min(max(ceilDiv((xx-xl), site_width), 1), num_sites_x); + } + /// @brief site index as an upper bound + inline __device__ int pos2site_ub_y(T yy) const + { + return min(max(ceilDiv((yy-yl), row_height), 1), num_sites_y); + } + inline __device__ int pos2bin_x(T xx) const + { + int bx = floorDiv((xx-xl), bin_size_x); + bx = max(bx, 0); + bx = min(bx, num_bins_x-1); + return bx; + } + inline __device__ int pos2bin_y(T yy) const + { + int by = floorDiv((yy-yl), bin_size_y); + by = max(by, 0); + by = min(by, num_bins_y-1); + return by; + } + inline __device__ void shift_box_to_layout(Box& box) const + { + box.xl = max(box.xl, xl); + box.xl = min(box.xl, xh); + box.xh = max(box.xh, xl); + box.xh = min(box.xh, xh); + box.yl = max(box.yl, yl); + box.yl = min(box.yl, yh); + box.yh = max(box.yh, yl); + box.yh = min(box.yh, yh); + } + inline __device__ Box box2sitebox(const Box& box) const + { + // xh, yh are exclusive + Box sitebox ( + pos2site_x(box.xl), + pos2site_y(box.yl), + pos2site_ub_x(box.xh), + pos2site_ub_y(box.yh) + ); + + return sitebox; + } + inline __device__ Box box2binbox(const Box& box) const + { + Box binbox ( + pos2bin_x(box.xl), + pos2bin_y(box.yl), + pos2bin_x(box.xh), + pos2bin_y(box.yh) + ); + + return binbox; + } + /// @brief align x coordinate to site + inline __device__ T align2site(T xx) const + { + return (int)floorDiv((xx - xl), site_width) * site_width + xl; + } + /// @brief align x coordinate to site for a space; + /// make sure the space is shrinked. + inline __device__ Space align2site(Space space) const + { + space.xl = ceilDiv((space.xl - xl), site_width) * site_width + xl; + space.xh = floorDiv((space.xh - xl), site_width) * site_width + xl; + return space; + } + /// @brief compute optimal region for a cell + /// The method to compute optimal region ignores the pin offsets of the target cell. + /// If we want to consider the pin offsets, there may not be feasible box for the optimal region. + /// Thus, this is just an approximate optimal region. + /// When using the optimal region, one needs to refer to the center of the cell to the region, or the region completely covers the entire cell. + __device__ Box compute_optimal_region(int node_id, const T* xx, const T* yy) const + { + Box box ( + xh, // some large number + yh, // some large number + xl, // some small number + yl // some small number + ); + for (int node2pin_id = flat_node2pin_start_map[node_id]; node2pin_id < flat_node2pin_start_map[node_id+1]; ++node2pin_id) + { + int node_pin_id = flat_node2pin_map[node2pin_id]; + int net_id = pin2net_map[node_pin_id]; + if (net_mask[net_id]) + { + for (int net2pin_id = flat_net2pin_start_map[net_id]; net2pin_id < flat_net2pin_start_map[net_id+1]; ++net2pin_id) + { + int net_pin_id = flat_net2pin_map[net2pin_id]; + int other_node_id = pin2node_map[net_pin_id]; + if (node_id != other_node_id) + { + box.xl = min(box.xl, xx[other_node_id]+pin_offset_x[net_pin_id]); + box.xh = max(box.xh, xx[other_node_id]+pin_offset_x[net_pin_id]); + box.yl = min(box.yl, yy[other_node_id]+pin_offset_y[net_pin_id]); + box.yh = max(box.yh, yy[other_node_id]+pin_offset_y[net_pin_id]); + } + } + } + } + shift_box_to_layout(box); + + return box; + } + /// @brief compute HPWL for a net + __device__ T compute_net_hpwl(int net_id, const T* xx, const T* yy) const + { + Box box ( + xh, // some large number + yh, // some large number + xl, // some small number + yl // some small number + ); + for (int net2pin_id = flat_net2pin_start_map[net_id]; net2pin_id < flat_net2pin_start_map[net_id+1]; ++net2pin_id) + { + int net_pin_id = flat_net2pin_map[net2pin_id]; + int other_node_id = pin2node_map[net_pin_id]; + box.xl = min(box.xl, xx[other_node_id]+pin_offset_x[net_pin_id]); + box.xh = max(box.xh, xx[other_node_id]+pin_offset_x[net_pin_id]); + box.yl = min(box.yl, yy[other_node_id]+pin_offset_y[net_pin_id]); + box.yh = max(box.yh, yy[other_node_id]+pin_offset_y[net_pin_id]); + } + if (box.xl == xh || box.yl == yh) // use xh/yh as some large number + { + return (T)0; + } + return (box.xh-box.xl) + (box.yh-box.yl); + } + /// @brief compute HPWL for all nets + __device__ T compute_total_hpwl() const + { + //printf("[D] start compute_total_hpwl\n"); + T total_hpwl = 0; + for (int net_id = 0; net_id < num_nets; ++net_id) + { + //if (net_mask[net_id]) + { + total_hpwl += compute_net_hpwl(net_id, x, y); + } + } + //printf("[D] end compute_total_hpwl\n"); + return total_hpwl; + } + /// @brief check whether a cell is within its fence region + __device__ bool inside_fence(int node_id, T xx, T yy) const + { + T node_xl = xx; + T node_yl = yy; + T node_xh = node_xl + node_size_x[node_id]; + T node_yh = node_yl + node_size_y[node_id]; + + bool legal_flag = true; + int region_id = node2fence_region_map[node_id]; + if (region_id < num_regions) + { + int box_bgn = flat_region_boxes_start[region_id]; + int box_end = flat_region_boxes_start[region_id + 1]; + T node_area = (node_xh - node_xl) * (node_yh - node_yl); + // I assume there is no overlap between boxes of a region + // otherwise, preprocessing is required + for (int box_id = box_bgn; box_id < box_end; ++box_id) + { + int box_offset = box_id*4; + T box_xl = flat_region_boxes[box_offset]; + T box_yl = flat_region_boxes[box_offset + 1]; + T box_xh = flat_region_boxes[box_offset + 2]; + T box_yh = flat_region_boxes[box_offset + 3]; + + T dx = max(min(node_xh, box_xh) - max(node_xl, box_xl), (T)0); + T dy = max(min(node_yh, box_yh) - max(node_yl, box_yl), (T)0); + T overlap = dx*dy; + if (overlap > 0) + { + node_area -= overlap; + } + } + if (node_area > 0) // not consumed by boxes within a region + { + legal_flag = false; + } + } + return legal_flag; + } + /// @brief distribute cells to rows + __host__ void make_row2node_map(const T* host_x, const T* host_y, + const T* host_node_size_x, const T* host_node_size_y, + int host_num_nodes, + std::vector >& row2node_map, + int num_threads) const + { + // distribute cells to rows + for (int i = 0; i < host_num_nodes; ++i) + { + T node_yl = host_y[i]; + T node_yh = node_yl+host_node_size_y[i]; + + int row_idxl = floorDiv(node_yl-yl, row_height); + int row_idxh = ceilDiv(node_yh-yl, row_height); + row_idxl = max(row_idxl, 0); + row_idxh = min(row_idxh, num_sites_y); + + for (int row_id = row_idxl; row_id < row_idxh; ++row_id) + { + T row_yl = yl+row_id*row_height; + T row_yh = row_yl+row_height; + + if (node_yl < row_yh && node_yh > row_yl) // overlap with row + { + row2node_map[row_id].push_back(i); + } + } + } + + // sort cells within rows +#ifdef _OPENMP +#pragma omp parallel for num_threads (num_threads) schedule(dynamic, 1) +#endif + for (int i = 0; i < num_sites_y; ++i) + { + auto& row2nodes = row2node_map[i]; + // sort cells within rows according to left edges + std::sort(row2nodes.begin(), row2nodes.end(), + [&] (int node_id1, int node_id2) { + T x1 = host_x[node_id1]; + T x2 = host_x[node_id2]; + return x1 < x2 || (x1 == x2 && node_id1 < node_id2); + }); + // After sorting by left edge, + // there is a special case for fixed cells where + // one fixed cell is completely within another in a row. + // This will cause failure to detect some overlaps. + // We need to remove the "small" fixed cell that is inside another. + if (!row2nodes.empty()) + { + std::vector tmp_nodes; + tmp_nodes.reserve(row2nodes.size()); + tmp_nodes.push_back(row2nodes.front()); + for (int j = 1, je = row2nodes.size(); j < je; ++j) + { + int node_id1 = row2nodes.at(j-1); + int node_id2 = row2nodes.at(j); + // two fixed cells + if (node_id1 >= num_movable_nodes && node_id2 >= num_movable_nodes) + { + T xl1 = host_x[node_id1]; + T xl2 = host_x[node_id2]; + T width1 = host_node_size_x[node_id1]; + T width2 = host_node_size_x[node_id2]; + T xh1 = xl1 + width1; + T xh2 = xl2 + width2; + // only collect node_id2 if its right edge is righter than node_id1 + if (xh1 < xh2) + { + tmp_nodes.push_back(node_id2); + } + } + else + { + tmp_nodes.push_back(node_id2); + } + } + row2nodes.swap(tmp_nodes); + + // sort according to center + std::sort(row2nodes.begin(), row2nodes.end(), + [&] (int node_id1, int node_id2) { + T x1 = host_x[node_id1] + host_node_size_x[node_id1]/2; + T x2 = host_x[node_id2] + host_node_size_x[node_id2]/2; + return x1 < x2 || (x1 == x2 && node_id1 < node_id2); + }); + } + } + } + /// @brief distribute cells to rows + __host__ void make_row2node_map_with_spaces(const T* host_x, const T* host_y, + const T* host_node_size_x, const T* host_node_size_y, + std::vector >& row2node_map, std::vector& node2row_map, std::vector >& spaces, + int num_threads) const + { + make_row2node_map(host_x, host_y, host_node_size_x, host_node_size_y, num_nodes + 2, + row2node_map, num_threads); + + // construct node2row_map + for (int i = 0; i < num_sites_y; ++i) + { + for (unsigned int j = 0; j < row2node_map[i].size(); ++j) + { + int node_id = row2node_map[i][j]; + if (node_id < num_movable_nodes) + { + RowMapIndex& row_id = node2row_map[node_id]; + row_id.row_id = i; + row_id.sub_id = j; + } + } + } + + // construct spaces + for (int i = 0; i < num_sites_y; ++i) + { + for (unsigned int j = 0; j < row2node_map[i].size(); ++j) + { + int node_id = row2node_map[i][j]; + if (node_id < num_movable_nodes) + { + assert(j); + int left_node_id = row2node_map[i][j-1]; + spaces[node_id].xl = host_x[left_node_id] + host_node_size_x[left_node_id]; + assert(j+1 < row2node_map[i].size()); + int right_node_id = row2node_map[i][j+1]; + spaces[node_id].xh = host_x[right_node_id]; + } + } + } + } + /// @brief distribute movable cells to bins according to cell (xl, yl) + /// @param bin2node_map flatten bin map, column-major + /// @param node2bin_index_map the index of cell in bin2node_map + __host__ void make_bin2node_map(const T* host_x, const T* host_y, + const T* host_node_size_x, const T* host_node_size_y, + std::vector >& bin2node_map, std::vector& node2bin_map) const + { + // construct bin2node_map + for (int i = 0; i < num_movable_nodes; ++i) + { + int node_id = i; + T node_x = host_x[node_id] + host_node_size_x[node_id]/2; + T node_y = host_y[node_id] + host_node_size_y[node_id]/2; + + int bx = min(max((int)floorDiv(node_x-xl, bin_size_x), 0), num_bins_x-1); + int by = min(max((int)floorDiv(node_y-yl, bin_size_y), 0), num_bins_y-1); + int bin_id = bx*num_bins_y+by; + int sub_id = bin2node_map.at(bin_id).size(); + bin2node_map.at(bin_id).push_back(node_id); + } + // sort cells within bins + //auto comp = [&] (int node_id1, int node_id2) { + // return host_x[node_id1] < host_x[node_id2] || (host_x[node_id1] == host_x[node_id2] && host_y[node_id1] < host_y[node_id2]); + //}; + //for (auto& bin2nodes : bin2node_map) + //{ + // std::sort(bin2nodes.begin(), bin2nodes.end(), comp); + //} + // construct node2bin_map + for (int bin_id = 0; bin_id < bin2node_map.size(); ++bin_id) + { + for (int sub_id = 0; sub_id < bin2node_map[bin_id].size(); ++sub_id) + { + int node_id = bin2node_map[bin_id][sub_id]; + BinMapIndex& bm_idx = node2bin_map.at(node_id); + bm_idx.bin_id = bin_id; + bm_idx.sub_id = sub_id; + } + } +#ifdef DEBUG + int max_num_nodes_per_bin = 0; + for (int i = 0; i < bin2node_map.size(); ++i) + { + max_num_nodes_per_bin = max(max_num_nodes_per_bin, (int)bin2node_map[i].size()); + } + printf("[D] max_num_nodes_per_bin = %d\n", max_num_nodes_per_bin); +#endif + } + /// @brief check whether placement is legal + bool check_legality(const T* host_x, const T* host_y, const T* host_node_size_x, const T* host_node_size_y) const + { + std::vector host_flat_region_boxes_start (num_regions + 1); + std::vector host_node2fence_region_map (num_movable_nodes); + checkCUDA(cudaMemcpy(host_flat_region_boxes_start.data(), flat_region_boxes_start, sizeof(int)*host_flat_region_boxes_start.size(), cudaMemcpyDeviceToHost)); + checkCUDA(cudaMemcpy(host_node2fence_region_map.data(), node2fence_region_map, sizeof(int)*host_node2fence_region_map.size(), cudaMemcpyDeviceToHost)); + std::vector host_flat_region_boxes (host_flat_region_boxes_start.back()*4); + checkCUDA(cudaMemcpy(host_flat_region_boxes.data(), flat_region_boxes, sizeof(T)*host_flat_region_boxes.size(), cudaMemcpyDeviceToHost)); + + return legalityCheckKernelCPU( + host_x, host_y, + host_node_size_x, host_node_size_y, + host_flat_region_boxes.data(), host_flat_region_boxes_start.data(), host_node2fence_region_map.data(), + xl, yl, xh, yh, + site_width, row_height, + num_nodes, + num_movable_nodes, + num_regions + ); + } + /// @brief draw placement + void draw_place(const char* filename) const + { + drawPlaceLauncher( + x, y, + node_size_x, node_size_y, + pin_offset_x, pin_offset_y, + pin2node_map, + num_nodes, + num_movable_nodes, + 0, + flat_net2pin_start_map[num_nets], + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + filename + ); + } +}; + +/// @brief Automatic determine scaling factor by types +template ::value> +struct HPWLScaleTraits; + +/// @brief For floating point numbers, no scaling. +template +struct HPWLScaleTraits +{ + static constexpr T scale = 1; +}; + +/// @brief For integers, scale. +template +struct HPWLScaleTraits +{ + static constexpr int scale = 1000; +}; + +/// @brief compute total HPWL +/// This function is mainly for evaluation, so the performance is not highly tuned. +/// Consistency is more important. +/// Thus integer is adopted. +template +__global__ void compute_total_hpwl_kernel(DetailedPlaceDB db, const T* xx, const T* yy, V* net_hpwls) +{ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < db.num_nets; i += blockDim.x * gridDim.x) + { + net_hpwls[i] = V(db.compute_net_hpwl(i, xx, yy)*HPWLScaleTraits::scale); + //if (db.net_mask[i]) + //{ + // net_hpwls[i] = V(db.compute_net_hpwl(i, xx, yy)*(T)scale); + //} + //else + //{ + // net_hpwls[i] = 0; + //} + } +} + +template +T compute_total_hpwl(const DetailedPlaceDB& db, const T* xx, const T* yy, V* net_hpwls) +{ + compute_total_hpwl_kernel<<>>(db, xx, yy, net_hpwls); + //auto hpwl = thrust::reduce(thrust::device, net_hpwls, net_hpwls+db.num_nets); + + V* d_out = NULL; + // Determine temporary device storage requirements + void *d_temp_storage = NULL; + size_t temp_storage_bytes = 0; + cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, net_hpwls, d_out, db.num_nets); + // Allocate temporary storage + checkCUDA(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + checkCUDA(cudaMalloc(&d_out, sizeof(V))); + // Run sum-reduction + cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, net_hpwls, d_out, db.num_nets); + // copy d_out to hpwl + V hpwl = 0; + checkCUDA(cudaMemcpy(&hpwl, d_out, sizeof(V), cudaMemcpyDeviceToHost)); + destroyCUDA(d_temp_storage); + destroyCUDA(d_out); + + return T(hpwl)/HPWLScaleTraits::scale; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h new file mode 100644 index 00000000..7fea33d0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h @@ -0,0 +1,393 @@ +/** + * @file DetailedPlaceDB.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDB_H +#define _DREAMPLACE_UTILITY_DETAILEDPLACEDB_H + +#include "utility/src/utils.h" +// helper functions +#include "draw_place/src/draw_place.h" +#include "legality_check/src/legality_check.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct Space +{ + T xl; + T xh; +}; + +struct BinMapIndex +{ + int bin_id; + int sub_id; +}; + +struct RowMapIndex +{ + int row_id; + int sub_id; +}; + +/// @brief a wrapper class of required data for detailed placement +template +struct DetailedPlaceDB +{ + typedef T type; + + const T* init_x; + const T* init_y; + const T* node_size_x; + const T* node_size_y; + const T* flat_region_boxes; ///< number of boxes x 4 + const int* flat_region_boxes_start; ///< number of regions + 1 + const int* node2fence_region_map; ///< length of number of movable cells + T* x; + T* y; + const int* flat_net2pin_map; + const int* flat_net2pin_start_map; + const int* pin2net_map; + const int* flat_node2pin_map; + const int* flat_node2pin_start_map; + const int* pin2node_map; + const T* pin_offset_x; + const T* pin_offset_y; + const unsigned char* net_mask; + T xl; + T yl; + T xh; + T yh; + T site_width; + T row_height; + T bin_size_x; + T bin_size_y; + int num_bins_x; + int num_bins_y; + int num_sites_x; + int num_sites_y; + int num_nodes; + int num_movable_nodes; + int num_nets; + int num_pins; + int num_regions; ///< number of regions for flat_region_boxes and + ///< flat_region_boxes_start + + inline int pos2site_x(T xx) const { + int sx = floorDiv(xx - xl, site_width); + sx = std::max(sx, 0); + sx = std::min(sx, num_sites_x - 1); + return sx; + } + inline int pos2site_y(T yy) const { + int sy = floorDiv(yy - yl, row_height); + sy = std::max(sy, 0); + sy = std::min(sy, num_sites_y - 1); + return sy; + } + /// @brief site index as an upper bound + inline int pos2site_ub_x(T xx) const { + int sx = ceilDiv(xx - xl, site_width); + sx = std::max(sx, 1); + sx = std::min(sx, num_sites_x); + return sx; + } + /// @brief site index as an upper bound + inline int pos2site_ub_y(T yy) const { + int sy = ceilDiv(yy - yl, row_height); + sy = std::max(sy, 1); + sy = std::min(sy, num_sites_y); + return sy; + } + inline int pos2bin_x(T xx) const { + int bx = floorDiv(xx - xl, bin_size_x); + bx = std::max(bx, 0); + bx = std::min(bx, num_bins_x - 1); + return bx; + } + inline int pos2bin_y(T yy) const { + int by = floorDiv(yy - yl, bin_size_y); + by = std::max(by, 0); + by = std::min(by, num_bins_y - 1); + return by; + } + inline void shift_box_to_layout(Box& box) const { + box.xl = std::max(box.xl, xl); + box.xl = std::min(box.xl, xh); + box.xh = std::max(box.xh, xl); + box.xh = std::min(box.xh, xh); + box.yl = std::max(box.yl, yl); + box.yl = std::min(box.yl, yh); + box.yh = std::max(box.yh, yl); + box.yh = std::min(box.yh, yh); + } + inline Box box2sitebox(const Box& box) const { + // xh, yh are exclusive + Box sitebox(pos2site_x(box.xl), pos2site_y(box.yl), + pos2site_ub_x(box.xh), pos2site_ub_y(box.yh)); + + return sitebox; + } + inline Box box2binbox(const Box& box) const { + Box binbox(pos2bin_x(box.xl), pos2bin_y(box.yl), pos2bin_x(box.xh), + pos2bin_y(box.yh)); + + return binbox; + } + /// @brief align x coordinate to site + inline T align2site(T xx) const { + return floorDiv(xx - xl, site_width) * site_width + xl; + } + /// @brief align x coordinate to site for a space; + /// make sure the space is shrinked. + inline Space align2site(Space space) const { + space.xl = ceilDiv(space.xl - xl, site_width) * site_width + xl; + space.xh = floorDiv(space.xh - xl, site_width) * site_width + xl; + return space; + } + /// @brief compute optimal region for a cell + /// The method to compute optimal region ignores the pin offsets of the target + /// cell. If we want to consider the pin offsets, there may not be feasible + /// box for the optimal region. Thus, this is just an approximate optimal + /// region. When using the optimal region, one needs to refer to the center of + /// the cell to the region, or the region completely covers the entire cell. + Box compute_optimal_region(int node_id) const { + Box box(std::numeric_limits::max(), std::numeric_limits::max(), + -std::numeric_limits::max(), -std::numeric_limits::max()); + for (int node2pin_id = flat_node2pin_start_map[node_id]; + node2pin_id < flat_node2pin_start_map[node_id + 1]; ++node2pin_id) { + int node_pin_id = flat_node2pin_map[node2pin_id]; + int net_id = pin2net_map[node_pin_id]; + if (net_mask[net_id]) { + for (int net2pin_id = flat_net2pin_start_map[net_id]; + net2pin_id < flat_net2pin_start_map[net_id + 1]; ++net2pin_id) { + int net_pin_id = flat_net2pin_map[net2pin_id]; + int other_node_id = pin2node_map[net_pin_id]; + if (node_id != other_node_id) { + box.xl = + std::min(box.xl, x[other_node_id] + pin_offset_x[net_pin_id]); + box.xh = + std::max(box.xh, x[other_node_id] + pin_offset_x[net_pin_id]); + box.yl = + std::min(box.yl, y[other_node_id] + pin_offset_y[net_pin_id]); + box.yh = + std::max(box.yh, y[other_node_id] + pin_offset_y[net_pin_id]); + } + } + } + } + shift_box_to_layout(box); + + return box; + } + /// @brief compute HPWL for a net + T compute_net_hpwl(int net_id) const { + Box box(std::numeric_limits::max(), std::numeric_limits::max(), + -std::numeric_limits::max(), -std::numeric_limits::max()); + for (int net2pin_id = flat_net2pin_start_map[net_id]; + net2pin_id < flat_net2pin_start_map[net_id + 1]; ++net2pin_id) { + int net_pin_id = flat_net2pin_map[net2pin_id]; + int other_node_id = pin2node_map[net_pin_id]; + box.xl = std::min(box.xl, x[other_node_id] + pin_offset_x[net_pin_id]); + box.xh = std::max(box.xh, x[other_node_id] + pin_offset_x[net_pin_id]); + box.yl = std::min(box.yl, y[other_node_id] + pin_offset_y[net_pin_id]); + box.yh = std::max(box.yh, y[other_node_id] + pin_offset_y[net_pin_id]); + } + if (box.xl == std::numeric_limits::max() || + box.yl == std::numeric_limits::max()) { + return (T)0; + } + return (box.xh - box.xl) + (box.yh - box.yl); + } + /// @brief compute HPWL for all nets + T compute_total_hpwl() const { + // dreamplacePrint(kDEBUG, "start compute_total_hpwl\n"); + T total_hpwl = 0; + for (int net_id = 0; net_id < num_nets; ++net_id) { + // if (net_mask[net_id]) + { total_hpwl += compute_net_hpwl(net_id); } + } + // dreamplacePrint(kDEBUG, "end compute_total_hpwl\n"); + return total_hpwl; + } + /// @brief distribute cells to rows + void make_row2node_map(const T* vx, const T* vy, + std::vector >& row2node_map, + int num_threads) const { + // distribute cells to rows + for (int i = 0; i < num_nodes; ++i) { + // T node_xl = vx[i]; + T node_yl = vy[i]; + // T node_xh = node_xl+node_size_x[i]; + T node_yh = node_yl + node_size_y[i]; + + int row_idxl = floorDiv(node_yl - yl, row_height); + int row_idxh = ceilDiv(node_yh - yl, row_height); + row_idxl = std::max(row_idxl, 0); + row_idxh = std::min(row_idxh, num_sites_y); + + for (int row_id = row_idxl; row_id < row_idxh; ++row_id) { + T row_yl = yl + row_id * row_height; + T row_yh = row_yl + row_height; + + if (node_yl < row_yh && node_yh > row_yl) // overlap with row + { + row2node_map[row_id].push_back(i); + } + } + } + + // sort cells within rows + // it is safer to sort by center + // sometimes there might be cells with 0 sizes +#ifdef _OPENMP +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, 1) +#endif + for (int i = 0; i < num_sites_y; ++i) { + auto& row2nodes = row2node_map[i]; + // sort cells within rows according to left edges + std::sort(row2nodes.begin(), row2nodes.end(), + [&](int node_id1, int node_id2) { + T x1 = vx[node_id1]; + T x2 = vx[node_id2]; + return x1 < x2 || (x1 == x2 && node_id1 < node_id2); + }); + // After sorting by left edge, + // there is a special case for fixed cells where + // one fixed cell is completely within another in a row. + // This will cause failure to detect some overlaps. + // We need to remove the "small" fixed cell that is inside another. + if (!row2nodes.empty()) { + std::vector tmp_nodes; + tmp_nodes.reserve(row2nodes.size()); + tmp_nodes.push_back(row2nodes.front()); + for (int j = 1, je = row2nodes.size(); j < je; ++j) { + int node_id1 = row2nodes.at(j - 1); + int node_id2 = row2nodes.at(j); + // two fixed cells + if (node_id1 >= num_movable_nodes && node_id2 >= num_movable_nodes) { + T xl1 = vx[node_id1]; + T xl2 = vx[node_id2]; + T width1 = node_size_x[node_id1]; + T width2 = node_size_x[node_id2]; + T xh1 = xl1 + width1; + T xh2 = xl2 + width2; + // only collect node_id2 if its right edge is righter than node_id1 + if (xh1 < xh2) { + tmp_nodes.push_back(node_id2); + } + } else { + tmp_nodes.push_back(node_id2); + } + } + row2nodes.swap(tmp_nodes); + + // sort according to center + std::sort(row2nodes.begin(), row2nodes.end(), + [&](int node_id1, int node_id2) { + T x1 = vx[node_id1] + node_size_x[node_id1] / 2; + T x2 = vx[node_id2] + node_size_x[node_id2] / 2; + return x1 < x2 || (x1 == x2 && node_id1 < node_id2); + }); + } + } + } + /// @brief distribute movable cells to bins + void make_bin2node_map(const T* host_x, const T* host_y, + const T* host_node_size_x, const T* host_node_size_y, + std::vector >& bin2node_map, + std::vector& node2bin_map) const { + // construct bin2node_map + for (int i = 0; i < num_movable_nodes; ++i) { + int node_id = i; + T node_x = host_x[node_id] + host_node_size_x[node_id] / 2; + T node_y = host_y[node_id] + host_node_size_y[node_id] / 2; + + int bx = std::min(std::max((int)floorDiv(node_x - xl, bin_size_x), 0), + num_bins_x - 1); + int by = std::min(std::max((int)floorDiv(node_y - yl, bin_size_y), 0), + num_bins_y - 1); + int bin_id = bx * num_bins_y + by; + // int sub_id = bin2node_map.at(bin_id).size(); + bin2node_map.at(bin_id).push_back(node_id); + } + // construct node2bin_map + for (unsigned int bin_id = 0; bin_id < bin2node_map.size(); ++bin_id) { + for (unsigned int sub_id = 0; sub_id < bin2node_map[bin_id].size(); + ++sub_id) { + int node_id = bin2node_map[bin_id][sub_id]; + BinMapIndex& bm_idx = node2bin_map.at(node_id); + bm_idx.bin_id = bin_id; + bm_idx.sub_id = sub_id; + } + } +#ifdef DEBUG + int max_num_nodes_per_bin = 0; + for (unsigned int i = 0; i < bin2node_map.size(); ++i) { + max_num_nodes_per_bin = + std::max(max_num_nodes_per_bin, (int)bin2node_map[i].size()); + } + printf("[D] max_num_nodes_per_bin = %d\n", max_num_nodes_per_bin); +#endif + } + /// @brief check whether placement is legal + bool check_legality() const { + return legalityCheckKernelCPU( + x, y, node_size_x, node_size_y, flat_region_boxes, + flat_region_boxes_start, node2fence_region_map, xl, yl, xh, yh, + site_width, row_height, num_nodes, num_movable_nodes, num_regions); + } + /// @brief check whether a cell is within its fence region + bool inside_fence(int node_id, T xx, T yy) const { + T node_xl = xx; + T node_yl = yy; + T node_xh = node_xl + node_size_x[node_id]; + T node_yh = node_yl + node_size_y[node_id]; + + bool legal_flag = true; + int region_id = node2fence_region_map[node_id]; + if (region_id < num_regions) { + int box_bgn = flat_region_boxes_start[region_id]; + int box_end = flat_region_boxes_start[region_id + 1]; + T node_area = (node_xh - node_xl) * (node_yh - node_yl); + // I assume there is no overlap between boxes of a region + // otherwise, preprocessing is required + for (int box_id = box_bgn; box_id < box_end; ++box_id) { + int box_offset = box_id * 4; + T box_xl = flat_region_boxes[box_offset]; + T box_yl = flat_region_boxes[box_offset + 1]; + T box_xh = flat_region_boxes[box_offset + 2]; + T box_yh = flat_region_boxes[box_offset + 3]; + + T dx = std::max(std::min(node_xh, box_xh) - std::max(node_xl, box_xl), + (T)0); + T dy = std::max(std::min(node_yh, box_yh) - std::max(node_yl, box_yl), + (T)0); + T overlap = dx * dy; + if (overlap > 0) { + node_area -= overlap; + } + } + if (node_area > 0) // not consumed by boxes within a region + { + legal_flag = false; + } + } + return legal_flag; + } + /// @brief draw placement + void draw_place(const char* filename) const { + drawPlaceLauncher(x, y, node_size_x, node_size_y, pin_offset_x, + pin_offset_y, pin2node_map, num_nodes, + num_movable_nodes, 0, flat_net2pin_start_map[num_nets], + xl, yl, xh, yh, site_width, row_height, bin_size_x, + bin_size_y, filename); + } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh new file mode 100644 index 00000000..7dfb2211 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh @@ -0,0 +1,142 @@ +/** + * @file FlatNestedVector.cuh + * @author Yibo Lin (DREAMPlace) + * @date Mar 2019 + */ +#ifndef _DREAMPLACE_UTILITY_FLATNESTEDVECTOR_CUH +#define _DREAMPLACE_UTILITY_FLATNESTEDVECTOR_CUH + +#include +#include +//#include +//#include + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct FlatNestedVector +{ + T* flat_element_map; ///< allocate on device, length of flat_dim1_start_map[size1] + unsigned int* flat_dim1_start_map; ///< allocate on device, length of size1+1 + unsigned int size1; ///< length in dimension 1 + + /// @brief constructor + __host__ FlatNestedVector() + : flat_element_map(nullptr) + , flat_dim1_start_map(nullptr) + , size1(0) + { + } + + /// @brief initialization + __host__ void initialize(const std::vector >& nested_map) + { + // construct flat map on host + unsigned int num_elements = 0; + for (typename std::vector >::const_iterator it = nested_map.begin(); it != nested_map.end(); ++it) + { + num_elements += it->size(); + } + std::vector host_flat_element_map (num_elements); + std::vector host_flat_dim1_start_map (nested_map.size()+1); + + num_elements = 0; + for (unsigned int i = 0; i < nested_map.size(); ++i) + { + const std::vector& vec = nested_map[i]; + std::copy(vec.begin(), vec.end(), host_flat_element_map.begin()+num_elements); + host_flat_dim1_start_map[i] = num_elements; + num_elements += vec.size(); + } + host_flat_dim1_start_map[nested_map.size()] = num_elements; + + // copy to device + size1 = nested_map.size(); + allocateCopyCUDA(flat_element_map, host_flat_element_map.data(), host_flat_element_map.size()); + allocateCopyCUDA(flat_dim1_start_map, host_flat_dim1_start_map.data(), host_flat_dim1_start_map.size()); + } + + __host__ void destroy() + { + if (flat_element_map) + { + destroyCUDA(flat_element_map); + destroyCUDA(flat_dim1_start_map); + } + } + + /// @brief access element + inline __device__ const T& operator()(unsigned int i, unsigned int j) const + { +#ifdef DEBUG + if (!(i < size1 && j < size(i))) + { + printf("%u < %u && %u < %u\n", i, size1, j, size(i)); + } +#endif + assert(i < size1 && j < size(i)); + return flat_element_map[flat_dim1_start_map[i]+j]; + } + + /// @brief access element + inline __device__ T& operator()(unsigned int i, unsigned int j) + { +#ifdef DEBUG + if (!(i < size1 && j < size(i))) + { + printf("%u < %u && %u < %u\n", i, size1, j, size(i)); + } +#endif + assert(i < size1 && j < size(i)); + return flat_element_map[flat_dim1_start_map[i]+j]; + } + + /// @brief access each row + inline __device__ const T* operator()(unsigned int i) const + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return flat_element_map+flat_dim1_start_map[i]; + } + + /// @brief access each row + inline __device__ T* operator()(unsigned int i) + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return flat_element_map+flat_dim1_start_map[i]; + } + + /// @brief length of each row + inline __device__ unsigned int size(unsigned int i) const + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return flat_dim1_start_map[i+1]-flat_dim1_start_map[i]; + } + + /// @brief total number of elements + inline __device__ unsigned int size() const + { + return flat_dim1_start_map[size1]; + } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h new file mode 100644 index 00000000..b5142d4c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h @@ -0,0 +1,87 @@ +/** + * @file LegalizationDB.h + * @author Yibo Lin (DREAMPlace) + * @date Nov 2019 + */ + +#ifndef _DREAMPLACE_UTILITY_LEGALIZATIONDB_H +#define _DREAMPLACE_UTILITY_LEGALIZATIONDB_H + +#include +#include "legality_check/src/legality_check.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief a wrapper class of required data for legalization +template +struct LegalizationDB +{ + typedef T type; + + const T* init_x; + const T* init_y; + const T* node_size_x; + const T* node_size_y; + const T* node_weights; + const T* flat_region_boxes; ///< number of boxes x 4 + const int* flat_region_boxes_start; ///< number of regions + 1 + const int* node2fence_region_map; ///< length of number of movable cells + T* x; + T* y; + + T xl; + T yl; + T xh; + T yh; + + T site_width; + T row_height; + T bin_size_x; + T bin_size_y; + + int num_bins_x; + int num_bins_y; + int num_sites_x; + int num_sites_y; + + int num_nodes; + int num_movable_nodes; + int num_regions; ///< number of regions for flat_region_boxes and + ///< flat_region_boxes_start + + /// @brief check whether a cell is regarded as movable macros in legalization. + /// This is mainly because it is painful to handle these cells for + /// legalization. + inline bool is_dummy_fixed(int node_id) const { +#ifdef DEBUG + dreamplaceAssert(node_id < db.num_nodes); +#endif + T height = node_size_y[node_id]; + return (node_id < num_movable_nodes && + height > (row_height * DUMMY_FIXED_NUM_ROWS)); + } + /// @brief align cell to a row + inline T align2row(T y, T height) const { + T yy = std::max(std::min(y, yh - height), yl); + yy = floorDiv(yy - yl, row_height) * row_height + yl; + return yy; + } + /// @brief align cell to a site + inline T align2site(T x, T width) const { + T xx = std::max(std::min(x, xh - width), xl); + xx = floorDiv(xx - xl, site_width) * site_width + xl; + return xx; + } + /// @brief check whether placement is legal + bool check_legality() const { + return legalityCheckKernelCPU( + x, y, node_size_x, node_size_y, flat_region_boxes, + flat_region_boxes_start, node2fence_region_map, xl, yl, xh, yh, + site_width, row_height, num_nodes, num_movable_nodes, num_regions); + } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp new file mode 100644 index 00000000..47c8117b --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp @@ -0,0 +1,112 @@ +/************************************************************************* + > File Name: Msg.cpp + > Author: Yibo Lin (DREAMPlace) + > Mail: yibolin@utexas.edu + > Created Time: Fri 31 Jul 2015 03:20:14 PM CDT + ************************************************************************/ + +#include "utility/src/Msg.h" +#include +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +int dreamplacePrint(MessageType m, const char* format, ...) +{ + va_list args; + va_start(args, format); + int ret = dreamplaceVPrintStream(m, stdout, format, args); + va_end(args); + + return ret; +} + +int dreamplacePrintStream(MessageType m, FILE* stream, const char* format, ...) +{ + va_list args; + va_start(args, format); + int ret = dreamplaceVPrintStream(m, stream, format, args); + va_end(args); + + return ret; +} + +int dreamplaceVPrintStream(MessageType m, FILE* stream, const char* format, va_list args) +{ + // print prefix + char prefix[16]; + dreamplaceSPrintPrefix(m, prefix); + fprintf(stream, "%s", prefix); + + // print message + int ret = vfprintf(stream, format, args); + + return ret; +} + +int dreamplaceSPrint(MessageType m, char* buf, const char* format, ...) +{ + va_list args; + va_start(args, format); + int ret = dreamplaceVSPrint(m, buf, format, args); + va_end(args); + + return ret; +} + +int dreamplaceVSPrint(MessageType m, char* buf, const char* format, va_list args) +{ + // print prefix + char prefix[16]; + dreamplaceSPrintPrefix(m, prefix); + sprintf(buf, "%s", prefix); + + // print message + int ret = vsprintf(buf+strlen(prefix), format, args); + + return ret; +} + +int dreamplaceSPrintPrefix(MessageType m, char* prefix) +{ + switch (m) + { + case kNONE: + return sprintf(prefix, "%c", '\0'); + case kINFO: + return sprintf(prefix, "[INFO ] "); + case kWARN: + return sprintf(prefix, "[WARNING] "); + case kERROR: + return sprintf(prefix, "[ERROR ] "); + case kDEBUG: + return sprintf(prefix, "[DEBUG ] "); + case kASSERT: + return sprintf(prefix, "[ASSERT ] "); + default: + dreamplaceAssertMsg(0, "unknown message type"); + } + return 0; +} + +void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName, const char* format, ...) +{ + // construct message + char buf[1024]; + va_list args; + va_start(args, format); + vsprintf(buf, format, args); + va_end(args); + + // print message + dreamplacePrintStream(kASSERT, stderr, "%s:%u: %s: Assertion `%s' failed: %s\n", fileName, lineNum, funcName, expr, buf); +} + +void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName) +{ + // print message + dreamplacePrintStream(kASSERT, stderr, "%s:%u: %s: Assertion `%s' failed\n", fileName, lineNum, funcName, expr); +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h new file mode 100644 index 00000000..dc17de60 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h @@ -0,0 +1,72 @@ +/** + * @file Msg.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ + +#ifndef DREAMPLACE_MSG_H +#define DREAMPLACE_MSG_H + +#include +#include +#include +#include +#include "utility/src/namespace.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// message type for print functions +enum MessageType { + kNONE = 0, + kINFO = 1, + kWARN = 2, + kERROR = 3, + kDEBUG = 4, + kASSERT = 5 +}; + +/// print to screen (stdout) +int dreamplacePrint(MessageType m, const char* format, ...); +/// print to stream +int dreamplacePrintStream(MessageType m, FILE* stream, const char* format, ...); +/// core function to print formatted data from variable argument list +int dreamplaceVPrintStream(MessageType m, FILE* stream, const char* format, va_list args); +/// format to a buffer +int dreamplaceSPrint(MessageType m, char* buf, const char* format, ...); +/// core function to format a buffer +int dreamplaceVSPrint(MessageType m, char* buf, const char* format, va_list args); +/// format prefix +int dreamplaceSPrintPrefix(MessageType m, char* buf); + +/// assertion +void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName, const char* format, ...); +void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName); + +#define dreamplaceAssertMsg(condition, args...) do {\ + if (!(condition)) \ + {\ + ::DREAMPLACE_NAMESPACE::dreamplacePrintAssertMsg(#condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, args); \ + abort(); \ + }\ +} while (false) +#define dreamplaceAssert(condition) do {\ + if (!(condition)) \ + {\ + ::DREAMPLACE_NAMESPACE::dreamplacePrintAssertMsg(#condition, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + abort(); \ + }\ +} while (false) + +/// static assertion +template +struct dreamplaceStaticAssert; +template <> +struct dreamplaceStaticAssert +{ + dreamplaceStaticAssert(const char* = NULL) {} +}; + + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh new file mode 100644 index 00000000..184daac2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh @@ -0,0 +1,146 @@ +/** + * @file PitchNestedVector.cuh + * @author Yibo Lin (DREAMPlace) + * @date Mar 2019 + * @brief Flat nested array using pitched memory + */ +#ifndef _DREAMPLACE_UTILITY_PITCHNESTEDVECTOR_CUH +#define _DREAMPLACE_UTILITY_PITCHNESTEDVECTOR_CUH + +#include +#include +//#include +//#include + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct PitchNestedVector +{ + T* flat_element_map; ///< allocate on device, length of size1*size2 + unsigned int* dim2_sizes; ///< sizes of dimension 2 + unsigned int size1; ///< length in dimension 1 + unsigned int size2; ///< maximum length in dimension 2 + unsigned int num_elements; ///< total number of elements + + /// @brief constructor + __host__ PitchNestedVector() + : flat_element_map(nullptr) + , dim2_sizes(nullptr) + , size1(0) + , size2(0) + { + } + + /// @brief initialization + __host__ void initialize(const std::vector >& nested_map) + { + // construct flat map on host + unsigned int max_num_elements = 0; + num_elements = 0; + for (typename std::vector >::const_iterator it = nested_map.begin(); it != nested_map.end(); ++it) + { + max_num_elements = max(max_num_elements, (unsigned int)it->size()); + num_elements += it->size(); + } + std::vector host_flat_element_map (nested_map.size()*max_num_elements, std::numeric_limits::max()); + std::vector host_dim2_sizes (nested_map.size()); + + for (unsigned int i = 0; i < nested_map.size(); ++i) + { + const std::vector& vec = nested_map[i]; + std::copy(vec.begin(), vec.end(), host_flat_element_map.begin()+max_num_elements*i); + host_dim2_sizes[i] = vec.size(); + } + + // copy to device + size1 = nested_map.size(); + size2 = max_num_elements; + allocateCopyCUDA(flat_element_map, host_flat_element_map.data(), host_flat_element_map.size()); + allocateCopyCUDA(dim2_sizes, host_dim2_sizes.data(), host_dim2_sizes.size()); + } + + __host__ void destroy() + { + if (flat_element_map) + { + destroyCUDA(flat_element_map); + destroyCUDA(dim2_sizes); + } + } + + /// @brief access element + inline __device__ const T& operator()(unsigned int i, unsigned int j) const + { +#ifdef DEBUG + if (!(i < size1 && j < size(i))) + { + printf("%u < %u && %u < %u\n", i, size1, j, size(i)); + } +#endif + assert(i < size1 && j < size(i)); + return flat_element_map[i*size2+j]; + } + + /// @brief access element + inline __device__ T& operator()(unsigned int i, unsigned int j) + { +#ifdef DEBUG + if (!(i < size1 && j < size(i))) + { + printf("%u < %u && %u < %u\n", i, size1, j, size(i)); + } +#endif + assert(i < size1 && j < size(i)); + return flat_element_map[i*size2+j]; + } + + /// @brief access each row + inline __device__ const T* operator()(unsigned int i) const + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return flat_element_map+i*size2; + } + + /// @brief access each row + inline __device__ T* operator()(unsigned int i) + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return flat_element_map+i*size2; + } + + /// @brief length of each row + inline __device__ unsigned int size(unsigned int i) const + { +#ifdef DEBUG + if (!(i < size1)) + { + printf("%u < %u\n", i, size1); + } +#endif + assert(i < size1); + return dim2_sizes[i]; + } + + /// @brief total number of elements + inline __device__ unsigned int size() const + { + return num_elements; + } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh new file mode 100644 index 00000000..d3be0563 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh @@ -0,0 +1,61 @@ +/** + * @file atomic_ops.cuh + * @author Yibo Lin (DREAMPlace) + * @date Oct 2019 + */ +#include +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief A class generalized scaled atomic addition for floating point number and integers. +/// For integer, we use it as a fixed point number with the LSB part for fractions. +template ::value> +struct AtomicAddCUDA +{ + typedef T type; + + /// @brief constructor + AtomicAddCUDA(type = 1) + { + } + + template + __device__ __forceinline__ type operator()(type* dst, V v) const + { + return atomicAdd(dst, (type)v); + } +}; + +/// @brief For atomic addition of fixed point number using integers. +template +struct AtomicAddCUDA +{ + typedef T type; + + type scale_factor; ///< a scale factor to scale fraction into integer + + /// @brief constructor + /// @param sf scale factor + AtomicAddCUDA(type sf = 1) + : scale_factor(sf) + { + } + + template + __device__ __forceinline__ type operator()(type* dst, V v) const + { + type sv = v*scale_factor; + return atomicAdd(dst, sv); + } +}; + +template +__global__ void copyScaleArray(T *dst, V *src, T scale_factor, int n) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < n) { + dst[i] = src[i] * scale_factor; + } +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h new file mode 100644 index 00000000..de8d8b15 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h @@ -0,0 +1,56 @@ +/** + * @file atomic_ops.h + * @author Yibo Lin (DREAMPlace) + * @date Apr 2020 + */ +#include +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief A class generalized scaled atomic addition for floating point number +/// and integers. For integer, we use it as a fixed point number with the LSB +/// part for fractions. +template ::value> +struct AtomicAdd { + typedef T type; + + /// @brief constructor + AtomicAdd(type = 1) {} + + template + inline void operator()(type* dst, V v) const { +#pragma omp atomic + *dst += v; + } +}; + +/// @brief For atomic addition of fixed point number using integers. +template +struct AtomicAdd { + typedef T type; + + type scale_factor; ///< a scale factor to scale fraction into integer + + /// @brief constructor + /// @param sf scale factor + AtomicAdd(type sf = 1) : scale_factor(sf) {} + + template + inline void operator()(type* dst, V v) const { + type sv = v * scale_factor; +#pragma omp atomic + *dst += sv; + } +}; + +/// @brief Perform a += b * scale_factor +template +void scaleAdd(T* dst, const V* src, W scale_factor, int n, int num_threads) { +#pragma omp parallel for num_threads(num_threads) + for (int i = 0; i < n; ++i) { + dst[i] += src[i] * scale_factor; + } +} + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h new file mode 100644 index 00000000..f4fd5ff3 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h @@ -0,0 +1,78 @@ +/** + * @file defs.h + * @author Yibo Lin (DREAMPlace) + * @date Apr 2020 + */ +#ifndef _DREAMPLACE_UTILITY_DEFS_H +#define _DREAMPLACE_UTILITY_DEFS_H + +#include "utility/src/namespace.h" + +DREAMPLACE_BEGIN_NAMESPACE + +#ifndef __NVCC__ + +/// namespace definition to make functions like +/// min/max general between C++ and CUDA +#define DREAMPLACE_STD_NAMESPACE std +/// namespace definition to make functions +/// general between C++ and CUDA +#define DREAMPLACE_HOST_DEVICE + +#else + +#define DREAMPLACE_STD_NAMESPACE +#define DREAMPLACE_HOST_DEVICE __host__ __device__ + +#define allocateCUDA(var, size, type) \ + { \ + cudaError_t status = cudaMalloc(&(var), (size) * sizeof(type)); \ + if (status != cudaSuccess) { \ + dreamplacePrint(kERROR, "cudaMalloc failed for " #var "\n"); \ + } \ + } + +#define destroyCUDA(var) \ + { \ + cudaError_t status = cudaFree(var); \ + if (status != cudaSuccess) { \ + dreamplacePrint(kERROR, "cudaFree failed for " #var "\n"); \ + } \ + } + +#define checkCUDA(status) \ + { \ + dreamplaceAssertMsg(status == cudaSuccess, "CUDA Runtime Error: %s\n", \ + cudaGetErrorString(status)); \ + } + +#define allocateCopyCUDA(var, rhs, size) \ + { \ + allocateCUDA(var, size, decltype(*rhs)); \ + checkCUDA(cudaMemcpy(var, rhs, sizeof(decltype(*rhs)) * (size), \ + cudaMemcpyHostToDevice)); \ + } + +#define checkCURAND(x) \ + { dreamplaceAssert(x == CURAND_STATUS_SUCCESS); } + +#define allocateCopyCPU(var, rhs, size, T) \ + { \ + var = (T*)malloc(sizeof(T) * (size)); \ + checkCUDA(cudaMemcpy((void*)var, (void*)rhs, sizeof(T) * (size), \ + cudaMemcpyDeviceToHost)); \ + } + +#endif + +#define destroyCPU(var) \ + { free((void*)var); } + +/// A heuristic to detect movable macros. +/// If a cell has a height larger than how many rows, we regard them as movable +/// macros. +#define DUMMY_FIXED_NUM_ROWS 2 + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h new file mode 100644 index 00000000..e4bbec06 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h @@ -0,0 +1,141 @@ +/** + * @file diamond_search.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ +#ifndef _DREAMPLACE_GLOBAL_MOVE_DIAMOND_SEARCH_H +#define _DREAMPLACE_GLOBAL_MOVE_DIAMOND_SEARCH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief grid index +template +struct GridIndex { + T ir; ///< row index + T ic; ///< column index + + GridIndex() + : ir(std::numeric_limits::max()), ic(std::numeric_limits::max()) {} + + GridIndex(T r, T c) : ir(r), ic(c) {} + + T manhattan_distance(const GridIndex& rhs) const { + return fabs(ir - rhs.ir) + fabs(ic - rhs.ic); + } + double angle(const GridIndex& rhs) const { + T dx = ir - rhs.ir; + T dy = ic - rhs.ic; + return atan2(dy, dx); + } +}; + +/// @brief compare grid (row index, column index) by its manhattan distance to a +/// target grid +template +struct CompareGridByDistance2Target { + GridIndex target; + CompareGridByDistance2Target(const GridIndex& g) : target(g) {} + bool operator()(const GridIndex& g1, const GridIndex& g2) const { + T d1 = g1.manhattan_distance(target); + double angle1 = g1.angle(target); + T d2 = g2.manhattan_distance(target); + double angle2 = g2.angle(target); + return d1 < d2 || (d1 == d2 && (angle1 < angle2)); + } +}; + +/// @brief kernel to generate the sequence for diamond search +/// @tparam the template must be a signed integer +/// @param num_rows number of rows +/// @param num_cols number of columns +/// @return the sequence in order from small distance to the center grid to +/// large +template +std::vector > diamond_search_sequence_kernel(T num_rows, + T num_cols) { + //// 2D grid map in row major + //// each element is the (row index, column index) + // std::vector > grid_map (num_rows*num_cols, GridIndex(0, + // 0)); for (T ir = 0; ir < num_rows; ++ir) + //{ + // for (T ic = 0; ic < num_cols; ++ic) + // { + // grid_map[ir*num_cols+ic] = GridIndex(-(T)num_rows/2+ir, + // -(T)num_cols/2+ic); + // } + //} + + //// sort from small distance to large + // std::sort(grid_map.begin(), grid_map.end(), + // CompareGridByDistance2Target(GridIndex(0, 0))); + + // directly generate diamond shape grids + // in clock-wise direction + // the sequence covers the following shape + // 1 + // 111 + // 11111 + // 111 + // 1 + std::vector > grid_map; + grid_map.reserve(num_rows * num_cols / 2); + T max_sum = std::min(num_rows, num_cols) / 2; + grid_map.push_back(GridIndex(0, 0)); + for (T sum = 1; sum <= max_sum; ++sum) { + // y > 0, x [-sum, sum] + for (T ir = -sum; ir < sum; ++ir) { + grid_map.push_back(GridIndex(ir, sum - std::abs(ir))); + } + // y < 0, x [sum, -sum] + for (T ir = sum; ir > -sum; --ir) { + grid_map.push_back(GridIndex(ir, -(sum - std::abs(ir)))); + } + } + + return grid_map; +} + +/// @brief top API to generate the sequence for diamond search +/// @param num_rows number of rows +/// @param num_cols number of columns +/// @return the sequence in order from small distance to the center grid to +/// large +template +std::vector::type> > +diamond_search_sequence(T num_rows, T num_cols) { + return diamond_search_sequence_kernel::type>( + num_rows, num_cols); +} + +template +void diamond_search_print(const std::vector >& grid_sequence) { + unsigned int sum = 0; + unsigned int count = 0; + GridIndex target(0, 0); + printf("[0] "); + for (typename std::vector >::const_iterator + it = grid_sequence.begin(); + it != grid_sequence.end(); ++it, ++count) { + T distance = it->manhattan_distance(target); + if (sum != distance) { + sum = distance; + printf("\n[%u] ", count); + } + printf("(%d,%d) ", it->ir, it->ic); + } + printf("\n"); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h new file mode 100644 index 00000000..730aa556 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h @@ -0,0 +1,316 @@ +/** + * @file limits.h + * @author Yibo Lin (DREAMPlace) + * @date Feb 2019 + */ + +#ifndef _DREAMPLACE_UTILITY_LIMITS_H +#define _DREAMPLACE_UTILITY_LIMITS_H + +#include +#include + +DREAMPLACE_BEGIN_NAMESPACE + +namespace cuda { // namespace cuda + +template +struct numeric_limits_base { + typedef T type; +}; +template +struct numeric_limits : public numeric_limits_base {}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return CHAR_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return CHAR_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return CHAR_MIN; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits + : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return UCHAR_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return SHRT_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return SHRT_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return SHRT_MIN; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits + : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return USHRT_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return INT_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return INT_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return INT_MIN; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return UINT_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return LONG_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return LONG_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return LONG_MIN; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits + : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return ULONG_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return LLONG_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return LLONG_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return LLONG_MIN; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits + : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return ULLONG_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return FLT_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return FLT_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return -FLT_MAX; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { + return FLT_EPSILON; + } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return DBL_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return DBL_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return -DBL_MAX; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { + return DBL_EPSILON; + } +}; + +template <> +struct numeric_limits : public numeric_limits_base { + /** The minimum finite value, or for floating types with + denormalization, the minimum positive normalized value. */ + DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { + return LDBL_MIN; + } + + /** The maximum finite value. */ + DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { + return LDBL_MAX; + } + + /** A finite value x such that there is no other finite value y + * where y < x. */ + DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { + return -LDBL_MAX; + } + + /** A the machine epsilon. */ + DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { + return LDBL_EPSILON; + } +}; +} // namespace cuda + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h new file mode 100644 index 00000000..d00bb2da --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h @@ -0,0 +1,194 @@ +/** + * @file DetailedPlaceDBUtils.h + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ +#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDBUTILS_H +#define _DREAMPLACE_UTILITY_DETAILEDPLACEDBUTILS_H + +DREAMPLACE_BEGIN_NAMESPACE + +template +struct DetailedPlaceDB; + +template +struct LegalizationDB; + +/// @brief make a database for detailed placement. +/// Only movable nodes will be moved. Fixed nodes and filler nodes are fixed. +/// +/// @param init_pos initial x/y location of nodes, including movable nodes, +/// fixed nodes, and filler nodes, [0, num_movable_nodes) are movable nodes, +/// [num_movable_nodes, num_nodes-num_filler_nodes) are fixed nodes, +/// [num_nodes-num_filler_nodes, num_nodes) are filler nodes. x values are at +/// the first half of the array, y values are at the second half of the array +/// @param node_size_x width of nodes, including movable nodes, fixed nodes, and +/// filler nodes, [0, num_movable_nodes) are movable nodes, [num_movable_nodes, +/// num_nodes-num_filler_nodes) are fixed nodes, [num_nodes-num_filler_nodes, +/// num_nodes) are filler nodes +/// @param node_size_y height of nodes, including movable nodes, fixed nodes, +/// and filler nodes, same as node_size_x +/// @param flat_net2pin_map consists pins of each net, pins belonging to the +/// same net are abutting to each other. +/// @param flat_net2pin_start_map bookmark for the starting index of each net in +/// flat_net2pin_map. The length is number of nets. The last entry equals to the +/// number of pins. +/// @param pin2net_map maps pin index to net index. +/// @param flat_node2pin_map consists pins of each node, pins belonging to the +/// same node are abutting to each other. +/// @param flat_node2pin_start_map bookmark for the starting index of each node +/// in flat_node2pin_map. The length is number of nodes. The last entry equals +/// to the number of pins. +/// @param pin2node_map maps pin index to node index. +/// @param pin_offset_x pin offset in x direction +/// @param pin_offset_y pin offset in y direction +/// @param net_mask whether a net should be considered for wirelength +/// @param xl left edge of bounding box of layout area +/// @param yl bottom edge of bounding box of layout area +/// @param xh right edge of bounding box of layout area +/// @param yh top edge of bounding box of layout area +/// @param site_width width of a placement site +/// @param row_height height of a placement row +/// @param num_bins_x number of bins in horizontal direction +/// @param num_bins_y number of bins in vertical direction +/// @param num_nodes total number of nodes, including movable nodes, fixed +/// nodes, and filler nodes; fixed nodes are in the range of [num_movable_nodes, +/// num_nodes-num_filler_nodes) +/// @param num_movable_nodes number of movable nodes, movable nodes are in the +/// range of [0, num_movable_nodes) +/// @param num_terminal_NIs number of terminal_NIs, essential fixed IO pins, in +/// the range of [num_movable_nodes+num_terminal, num_nodes-num_filler_nodes) +/// @param num_filler_nodes number of filler nodes, filler nodes are in the +/// range of [num_nodes-num_filler_nodes, num_nodes) +template +DetailedPlaceDB make_placedb( + at::Tensor init_pos, at::Tensor pos, at::Tensor node_size_x, + at::Tensor node_size_y, at::Tensor flat_region_boxes, + at::Tensor flat_region_boxes_start, at::Tensor node2fence_region_map, + at::Tensor flat_net2pin_map, at::Tensor flat_net2pin_start_map, + at::Tensor pin2net_map, at::Tensor flat_node2pin_map, + at::Tensor flat_node2pin_start_map, at::Tensor pin2node_map, + at::Tensor pin_offset_x, at::Tensor pin_offset_y, at::Tensor net_mask, + double xl, double yl, double xh, double yh, double site_width, + double row_height, int num_bins_x, int num_bins_y, int num_movable_nodes, + int num_terminal_NIs, int num_filler_nodes) { + DetailedPlaceDB db; + int num_nodes = init_pos.numel() / 2; + + db.init_x = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T); + db.init_y = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T) + num_nodes; + db.node_size_x = DREAMPLACE_TENSOR_DATA_PTR(node_size_x, T); + db.node_size_y = DREAMPLACE_TENSOR_DATA_PTR(node_size_y, T); + db.flat_region_boxes = DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes, T); + db.flat_region_boxes_start = + DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes_start, int); + db.node2fence_region_map = + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int); + db.x = DREAMPLACE_TENSOR_DATA_PTR(pos, T); + db.y = DREAMPLACE_TENSOR_DATA_PTR(pos, T) + num_nodes; + db.flat_net2pin_map = DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int); + db.flat_net2pin_start_map = + DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int); + db.pin2net_map = DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int); + db.flat_node2pin_map = DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int); + db.flat_node2pin_start_map = + DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int); + db.pin2node_map = DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int); + db.pin_offset_x = DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, T); + db.pin_offset_y = DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, T); + db.net_mask = DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char); + db.xl = xl; + db.yl = yl; + db.xh = xh; + db.yh = yh; + db.site_width = site_width; + db.row_height = row_height; + db.bin_size_x = (xh - xl) / num_bins_x; + db.bin_size_y = (yh - yl) / num_bins_y; + db.num_bins_x = num_bins_x; + db.num_bins_y = num_bins_y; + db.num_sites_x = std::round((xh - xl) / site_width); + db.num_sites_y = std::round((yh - yl) / row_height); + db.num_nodes = num_nodes - num_filler_nodes - num_terminal_NIs; + db.num_movable_nodes = num_movable_nodes; + db.num_nets = flat_net2pin_start_map.numel() - 1; + db.num_pins = pin2net_map.numel(); + db.num_regions = flat_region_boxes_start.numel() - 1; + + return db; +} + +/// @brief make a database for detailed placement. +/// Only movable nodes will be moved. Fixed nodes and filler nodes are fixed. +/// +/// @param init_pos initial x/y location of nodes, including movable nodes, +/// fixed nodes, and filler nodes, [0, num_movable_nodes) are movable nodes, +/// [num_movable_nodes, num_nodes-num_filler_nodes) are fixed nodes, +/// [num_nodes-num_filler_nodes, num_nodes) are filler nodes. x values are at +/// the first half of the array, y values are at the second half of the array +/// @param pos x/y locations to write +/// @param node_size_x width of nodes, including movable nodes, fixed nodes, and +/// filler nodes, [0, num_movable_nodes) are movable nodes, [num_movable_nodes, +/// num_nodes-num_filler_nodes) are fixed nodes, [num_nodes-num_filler_nodes, +/// num_nodes) are filler nodes +/// @param node_size_y height of nodes, including movable nodes, fixed nodes, +/// and filler nodes, same as node_size_x +/// @param xl left edge of bounding box of layout area +/// @param yl bottom edge of bounding box of layout area +/// @param xh right edge of bounding box of layout area +/// @param yh top edge of bounding box of layout area +/// @param site_width width of a placement site +/// @param row_height height of a placement row +/// @param num_bins_x number of bins in horizontal direction +/// @param num_bins_y number of bins in vertical direction +/// @param num_movable_nodes number of movable nodes, movable nodes are in the +/// range of [0, num_movable_nodes) +/// @param number of filler nodes, filler nodes are in the range of +/// [num_nodes-num_filler_nodes, num_nodes) +template +LegalizationDB make_placedb( + at::Tensor init_pos, at::Tensor pos, at::Tensor node_size_x, + at::Tensor node_size_y, at::Tensor node_weights, + at::Tensor flat_region_boxes, at::Tensor flat_region_boxes_start, + at::Tensor node2fence_region_map, double xl, double yl, double xh, + double yh, double site_width, double row_height, int num_bins_x, + int num_bins_y, int num_movable_nodes, int num_terminal_NIs, + int num_filler_nodes) { + LegalizationDB db; + int num_nodes = init_pos.numel() / 2; + + db.init_x = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T); + db.init_y = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T) + num_nodes; + db.node_size_x = DREAMPLACE_TENSOR_DATA_PTR(node_size_x, T); + db.node_size_y = DREAMPLACE_TENSOR_DATA_PTR(node_size_y, T); + db.node_weights = DREAMPLACE_TENSOR_DATA_PTR(node_weights, T); + db.flat_region_boxes = DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes, T); + db.flat_region_boxes_start = + DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes_start, int); + db.node2fence_region_map = + DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int); + db.x = DREAMPLACE_TENSOR_DATA_PTR(pos, T); + db.y = DREAMPLACE_TENSOR_DATA_PTR(pos, T) + num_nodes; + db.xl = xl; + db.yl = yl; + db.xh = xh; + db.yh = yh; + db.site_width = site_width; + db.row_height = row_height; + db.bin_size_x = (xh - xl) / num_bins_x; + db.bin_size_y = (yh - yl) / num_bins_y; + db.num_bins_x = num_bins_x; + db.num_bins_y = num_bins_y; + db.num_sites_x = std::round((xh - xl) / site_width); + db.num_sites_y = std::round((yh - yl) / row_height); + // ignore fillers and terminal_NIs + db.num_nodes = num_nodes - num_filler_nodes - num_terminal_NIs; + db.num_movable_nodes = num_movable_nodes; + db.num_regions = flat_region_boxes_start.numel() - 1; + + return db; +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h new file mode 100644 index 00000000..e81a3707 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h @@ -0,0 +1,61 @@ +/** + * File : math.h + * Author : Yibo Lin (DREAMPlace) + * Date : 05.05.2020 + */ + +#ifndef _DREAMPLACE_UTILITY_MATH_H +#define _DREAMPLACE_UTILITY_MATH_H + +#include +#include +#include "utility/src/defs.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// relative error tolerance for floating point floor/ceil +#define DREAMPLACE_RTOL 1e-3 + +template +inline DREAMPLACE_HOST_DEVICE T div(T a, V b) { + return a / b; +} + +/// @brief template specialization for non-integral types +template +inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type +floorDiv(T a, V b, T rtol = DREAMPLACE_RTOL) { + return floor(div(a + rtol * b, b)); +} + +/// @brief template specialization for integral types +template +inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type +floorDiv(T a, T b) { + return a / b; +} + +/// @brief template specialization for non-integral types +template +inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type +ceilDiv(T a, V b, T rtol = DREAMPLACE_RTOL) { + return ceil(div(a - rtol * b, b)); +} + +/// @brief template specialization for integral types +template +inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type +ceilDiv(T a, T b) { + return (a + b - 1) / b; +} + +/// @brief template specialization for non-integral types +template +inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type +roundDiv(T a, V b) { + return round(div(a, b)); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h new file mode 100644 index 00000000..28bce2ce --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h @@ -0,0 +1,14 @@ +/** + * @file Namespace.h + * @author Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) + * @date Jan 2019 + */ + +#ifndef DREAMPLACE_UTILITY_NAMESPACE_H +#define DREAMPLACE_UTILITY_NAMESPACE_H + +#define DREAMPLACE_NAMESPACE DreamPlaceFPGA +#define DREAMPLACE_BEGIN_NAMESPACE namespace DreamPlaceFPGA { +#define DREAMPLACE_END_NAMESPACE } + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh new file mode 100644 index 00000000..b9749f40 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh @@ -0,0 +1,49 @@ +/** + * @file print.cuh + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + */ +#ifndef _DREAMPLACE_UTILITY_PRINT_CUH +#define _DREAMPLACE_UTILITY_PRINT_CUH + +#include +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +void printArray(const T* x, const int n, const char* str) { + dreamplacePrint(kNONE, "%s[%d] = ", str, n); + std::vector host_x(n); + cudaMemcpy(host_x.data(), x, n * sizeof(T), cudaMemcpyDeviceToHost); + for (int i = 0; i < n; ++i) { + dreamplacePrint(kNONE, "%g ", double(host_x[i])); + } + dreamplacePrint(kNONE, "\n"); +} + +template +void printScalar(const T& x, const char* str) { + dreamplacePrint(kNONE, "%s = ", str); + T host_x = 0; + cudaMemcpy(&host_x, &x, sizeof(T), cudaMemcpyDeviceToHost); + dreamplacePrint(kNONE, "%g\n", double(host_x)); +} + +template +void print2DArray(const T* x, const int m, const int n, const char* str) { + dreamplacePrint(kNONE, "%s[%dx%d] = \n", str, m, n); + std::vector host_x(m * n, 0); + cudaMemcpy(host_x.data(), x, m * n * sizeof(T), cudaMemcpyDeviceToHost); + for (int i = 0; i < m * n; ++i) { + if (i && (i % n) == 0) { + dreamplacePrint(kNONE, "\n"); + } + dreamplacePrint(kNONE, "%g ", double(host_x[i])); + } + dreamplacePrint(kNONE, "\n"); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh new file mode 100644 index 00000000..985ccede --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh @@ -0,0 +1,32 @@ +/** + * @file timer.cuh + * @author Yibo Lin (DREAMPlace) + * @date Apr 2020 + */ + +#ifndef DREAMPLACE_UTILITY_TIMER_CUH +#define DREAMPLACE_UTILITY_TIMER_CUH + +#include +#include "utility/src/namespace.h" + +DREAMPLACE_BEGIN_NAMESPACE + +struct CUDATimer { + typedef std::chrono::high_resolution_clock::rep hr_clock_rep; + + __device__ static inline long long int getGlobaltime(void) { + long long int ret; + + asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(ret)); + + return ret; + } + + // Returns the period in miliseconds + __device__ static inline double getTimerPeriod(void) { return 1.0e-6; } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h new file mode 100644 index 00000000..4b368bb7 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h @@ -0,0 +1,31 @@ +/** + * @file timer.h + * @author Yibo Lin (DREAMPlace) + * @date Apr 2020 + */ + +#ifndef DREAMPLACE_UTILITY_TIMER_H +#define DREAMPLACE_UTILITY_TIMER_H + +#include +#include "utility/src/namespace.h" + +DREAMPLACE_BEGIN_NAMESPACE + +struct CPUTimer { + typedef std::chrono::high_resolution_clock::rep hr_clock_rep; + + static inline hr_clock_rep getGlobaltime(void) { + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); + } + + // Returns the period in miliseconds + static inline double getTimerPeriod(void) { + return 1000.0 * std::chrono::high_resolution_clock::period::num / + std::chrono::high_resolution_clock::period::den; + } +}; + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h new file mode 100644 index 00000000..9014efa1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h @@ -0,0 +1,108 @@ +//** +// * @file torch.h +// * @author Yibo Lin (DREAMPlace) +// * @date Mar 2019 +// * @brief Required heads from torch +// */ +#ifndef _DREAMPLACE_UTILITY_TORCH_H +#define _DREAMPLACE_UTILITY_TORCH_H + +/// As torch may change the header inclusion conventions, it is better to manage +/// it in a consistent way. +#if TORCH_VERSION_MAJOR >= 1 +#include + +#if TORCH_VERSION_MINOR >= 3 + +#define DREAMPLACE_TENSOR_DATA_PTR(TENSOR, TYPE) \ + ((TENSOR.defined())? TENSOR.data_ptr() : nullptr) +#define DREAMPLACE_TENSOR_SCALARTYPE(TENSOR) TENSOR.scalar_type() + +#else + +#define DREAMPLACE_TENSOR_DATA_PTR(TENSOR, TYPE) \ + ((TENSOR.defined())? TENSOR.data() : nullptr) +#define DREAMPLACE_TENSOR_SCALARTYPE(TENSOR) TENSOR.type().scalarType() + +#endif + +// torch version 1.8 or later +#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 8) + +// torch version 1.13 or later +#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 13) + +// AT_PRIVATE_CASE_TYPE was recently removed from the public dispatch API (look in the Dispatch.h) +#define AT_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ + case enum_type: { \ + using scalar_t = type; \ + return __VA_ARGS__(); \ + } + +#endif + +#define DREAMPLACE_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ + AT_PRIVATE_CASE_TYPE(NAME, enum_type, type, __VA_ARGS__) + +#else + +#define DREAMPLACE_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ + AT_PRIVATE_CASE_TYPE(enum_type, type, __VA_ARGS__) + +#endif + +#else + +#include + +#endif + +#include + +#define CHECK_CPU(x) AT_ASSERTM(!x.is_cuda(), #x " must be a tensor on CPU") +#define CHECK_CUDA(x) AT_ASSERTM(x.is_cuda(), #x " must be a tensor on CUDA") +#define CHECK_FLAT(x) AT_ASSERTM(x.ndimension() == 1, #x "must be a flat tensor") + +#define CHECK_FLAT_CPU(x) \ + CHECK_CPU(x); \ + CHECK_FLAT(x); +#define CHECK_FLAT_CUDA(x) \ + CHECK_CUDA(x); \ + CHECK_FLAT(x); + +#define CHECK_EVEN(x) \ + AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) \ + AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// As the API for torch changes, customize a DREAMPlace version to remove +/// warnings + +#include "utility/src/torch_fft_api.h" + +#define DREAMPLACE_DISPATCH_FLOATING_TYPES(TENSOR, NAME, ...) \ + [&] { \ + at::ScalarType _st = DREAMPLACE_TENSOR_SCALARTYPE(TENSOR); \ + switch (_st) { \ + DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Double, double, __VA_ARGS__) \ + DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Float, float, __VA_ARGS__) \ + default: \ + AT_ERROR(#NAME, " not implemented for '", toString(_st), "'"); \ + } \ + }() + +/// I remove the support to Char, since int8_t does not compile for CUDA +/// char does not compile for ATen either +#define DREAMPLACE_DISPATCH_INT_FLOAT_TYPES(TENSOR, NAME, ...) \ + [&] { \ + at::ScalarType _st = DREAMPLACE_TENSOR_SCALARTYPE(TENSOR); \ + switch (_st) { \ + DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Float, float, __VA_ARGS__) \ + DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Double, double, __VA_ARGS__) \ + DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Int, int, __VA_ARGS__) \ + default: \ + AT_ERROR(#NAME, " not implemented for '", at::toString(_st), "'"); \ + } \ + }() + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h new file mode 100644 index 00000000..e6a524a8 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h @@ -0,0 +1,121 @@ +/** + * File : torch_fft_api.h + * Author : Yihua Liu + * Date : 08.29.2022 + * Last Modified By : Yibo Lin (DREAMPlace) + * Comment : Copy from DREAMPlace (ASIC) to enable pytorch 1.13 support + */ + +#pragma once + +#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 8) + +namespace at { + static inline Tensor rfft(const Tensor & input, int signal_ndim, bool normalized = false, bool onesided = true) { + at::Tensor y; + if (onesided) { + if (normalized) { + if (signal_ndim == 1) { + y = fft_rfft(input, c10::nullopt, -1, "ortho"); + } else if (signal_ndim == 2) { + y = fft_rfft2(input, c10::nullopt, {-2, -1}, "ortho"); + } else if (signal_ndim == 3) { + y = fft_rfftn(input, c10::nullopt, std::vector{-3, -2, -1}, "ortho"); + } else { + TORCH_CHECK_VALUE(false, "Ortho-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } else { + if (signal_ndim == 1) { + y = fft_rfft(input, c10::nullopt, -1, "backward"); + } else if (signal_ndim == 2) { + y = fft_rfft2(input, c10::nullopt, {-2, -1}, "backward"); + } else if (signal_ndim == 3) { + y = fft_rfftn(input, c10::nullopt, std::vector{-3, -2, -1}, "backward"); + } else { + TORCH_CHECK_VALUE(false, "Backward-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } + } else { + if (normalized) { + if (signal_ndim == 1) { + y = fft_fft(input, c10::nullopt, -1, "ortho"); + } else if (signal_ndim == 2) { + y = fft_fft2(input, c10::nullopt, {-2, -1}, "ortho"); + } else if (signal_ndim == 3) { + y = fft_fftn(input, c10::nullopt, std::vector{-3, -2, -1}, "ortho"); + } else { + TORCH_CHECK_VALUE(false, "Ortho-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } else { + if (signal_ndim == 1) { + y = fft_fft(input, c10::nullopt, -1, "backward"); + } else if (signal_ndim == 2) { + y = fft_fft2(input, c10::nullopt, {-2, -1}, "backward"); + } else if (signal_ndim == 3) { + y = fft_fftn(input, c10::nullopt, std::vector{-3, -2, -1}, "backward"); + } else { + TORCH_CHECK_VALUE(false, "Backward-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } + } + + return view_as_real(y).contiguous(); + } + + static inline Tensor irfft(const Tensor & input, int signal_ndim, bool normalized = false, bool onesided = true, c10::optional signal_sizes = c10::nullopt) { + at::Tensor y; + // user controls onesided actually by the signal_sizes; + // in other words, the parameter onesided is not really used + TORCH_CHECK_VALUE(signal_sizes, "Parameter signal_sizes is required"); + + if (onesided) { + if (normalized) { + if (signal_ndim == 1) { + y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "ortho"); + } else if (signal_ndim == 2) { + y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "ortho"); + } else if (signal_ndim == 3) { + y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "ortho"); + } else { + TORCH_CHECK_VALUE(false, "Ortho-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } else { + if (signal_ndim == 1) { + y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "backward"); + } else if (signal_ndim == 2) { + y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "backward"); + } else if (signal_ndim == 3) { + y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "backward"); + } else { + TORCH_CHECK_VALUE(false, "Backward-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } + } else { + if (normalized) { + if (signal_ndim == 1) { + y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "ortho"); + } else if (signal_ndim == 2) { + y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "ortho"); + } else if (signal_ndim == 3) { + y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "ortho"); + } else { + TORCH_CHECK_VALUE(false, "Ortho-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } else { + if (signal_ndim == 1) { + y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "backward"); + } else if (signal_ndim == 2) { + y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "backward"); + } else if (signal_ndim == 3) { + y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "backward"); + } else { + TORCH_CHECK_VALUE(false, "Backward-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); + } + } + } + + return y.contiguous(); + } +} + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh new file mode 100644 index 00000000..295dcba5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh @@ -0,0 +1,133 @@ +/** + * @file utils.cuh + * @author Yibo Lin (DREAMPlace) + * @date Jan 2019 + */ +#ifndef _DREAMPLACE_UTILITY_UTILS_CUH +#define _DREAMPLACE_UTILITY_UTILS_CUH + +#include +#include +#include +// namespace +#include "utility/src/namespace.h" +// macro definitions +#include "utility/src/defs.h" +// print utilities +#include "utility/src/Msg.h" +#include "utility/src/print.cuh" +// math utilities +#include "utility/src/math.h" +// timer utilities +#include "utility/src/timer.cuh" +#include "utility/src/timer.h" +// numeric limits +#include "utility/src/limits.h" +// simple data structures +#include "utility/src/Box.h" +#include "utility/src/ComplexNumber.h" +#include "utility/src/diamond_search.h" +#include "utility/src/FlatNestedVector.cuh" +#include "utility/src/PitchNestedVector.cuh" +// atomic ops +#include "utility/src/atomic_ops.cuh" + +DREAMPLACE_BEGIN_NAMESPACE +/// to replace thrust::swap +template +__host__ __device__ inline void host_device_swap(Assignable1 &a, Assignable2 &b) { + Assignable1 tmp = a; + a = b; + b = tmp; +} + +template +__global__ void iota(T* a, int n) { + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; + i += blockDim.x * gridDim.x) { + a[i] = i; + } +} + +template +__global__ void fill_array_kernel(T* array, int n, T v) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < n) { + array[i] = v; + } +} + +template +inline void fill_array(T* array, int n, T v) { + fill_array_kernel<<>>(array, n, v); +} + +template +__global__ void reset_element_set_sizes_kernel(int num_sets, + T* element_set_sizes) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_sets) { + element_set_sizes[i] = 0; + } +} + +template +__global__ void collect_element_sets_kernel(int n, int num_sets, + int max_set_size, const T* elements, + const int* element2partition_map, + T* element_sets, + int* element_set_sizes) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < n) { + const T& element = elements[i]; + int partition_id = element2partition_map[i]; + assert(partition_id < num_sets); + int& size = element_set_sizes[partition_id]; + int index = atomicAdd(&size, 1); + if (index < max_set_size) { + element_sets[partition_id * max_set_size + index] = element; + } + } +} + +template +__global__ void correct_element_set_sizes_kernel(int num_sets, T max_set_size, + T* element_set_sizes) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_sets) { + T& size = element_set_sizes[i]; + size = min(size, max_set_size); + } +} + +/// @brief gather elements into sets according to element2partition_map +/// For example, elements = {a0, a1, a2, a3, a4, a5} +/// element2partition_map = {1, 0, 0, 1, 2, 1} +/// expected result element_sets = {{a1, a2, null}, {a0, a3, a5}, {a4, null, +/// null}} Current implementation is not deterministic. If introducing sorting, +/// determinism is possible. +/// @param n length of elements +/// @param num_sets number of partitions +/// @param max_set_size maximum number of elements in a partition +/// @param elements array of elements +/// @param element2partition_map map element index to partition +/// @param element_sets output element sets in dimension num_sets x max_set_size +/// @param element_set_sizes size of each set in dimension num_sets x 1 +template +inline __host__ void gather(int n, int num_sets, int max_set_size, + const T* elements, const int* element2partition_map, + T* element_sets, int* element_set_sizes) { + fill_array(element_sets, num_sets * max_set_size, + std::numeric_limits::max()); + reset_element_set_sizes_kernel<<>>( + num_sets, element_set_sizes); + collect_element_sets_kernel<<>>( + n, num_sets, max_set_size, elements, element2partition_map, element_sets, + element_set_sizes); + correct_element_set_sizes_kernel<<>>( + num_sets, max_set_size, element_set_sizes); +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h new file mode 100644 index 00000000..b4f56926 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h @@ -0,0 +1,37 @@ +/** + * @file utils.h + * @author Yibo Lin (DREAMPlace) + * @date Nov 2019 + */ +#ifndef _DREAMPLACE_UTILITY_UTILS_H +#define _DREAMPLACE_UTILITY_UTILS_H + +// namespace +#include "utility/src/namespace.h" +// macro definitions +#include "utility/src/defs.h" +// print utilities +#include "utility/src/Msg.h" +// math utilities +#include "utility/src/math.h" +// timer utilities +#include "utility/src/timer.h" +// numeric limits +#include "utility/src/limits.h" +// simple data structures +#include "utility/src/Box.h" +#include "utility/src/ComplexNumber.h" +#include "utility/src/diamond_search.h" +// atomic ops +#include "utility/src/atomic_ops.h" + +// placement database +//#include "utility/src/detailed_place_db.h" +//#include "utility/src/legalization_db.h" +//#include "utility/src/make_placedb.h" + +DREAMPLACE_BEGIN_NAMESPACE + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh new file mode 100644 index 00000000..4a32182c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh @@ -0,0 +1,21 @@ +/** + * File : utils_cub.cuh + * Author : Yibo Lin (DREAMPlace) + * Date : 06.25.2021 + */ + +#ifndef _DREAMPLACE_UTILITY_UTILS_CUB_CUH +#define _DREAMPLACE_UTILITY_UTILS_CUB_CUH + +#include "utility/src/namespace.h" + +// include cub in a safe manner +#define CUB_NS_PREFIX namespace DREAMPLACE_NAMESPACE { +#define CUB_NS_POSTFIX } +#define CUB_NS_QUALIFIER DREAMPLACE_NAMESPACE::cub +#include "cub/cub.cuh" +#undef CUB_NS_QUALIFIER +#undef CUB_NS_POSTFIX +#undef CUB_NS_PREFIX + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore new file mode 100644 index 00000000..771a9fab --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore @@ -0,0 +1,2 @@ +*.pkl +*.dat diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt new file mode 100644 index 00000000..a160f81a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt @@ -0,0 +1,67 @@ +set(OP_NAME weighted_average_wirelength) + +set(TARGET_NAME ${OP_NAME}) + +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + +set(LINK_LIBS utility_cxx + OpenMP::OpenMP_CXX + ) + +add_pytorch_extension(${TARGET_NAME}_cpp + src/weighted_average_wirelength.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cpp_atomic + src/weighted_average_wirelength_atomic.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cpp_merged + src/weighted_average_wirelength_merged.cpp + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cpp + ${TARGET_NAME}_cpp_atomic + ${TARGET_NAME}_cpp_merged + DESTINATION dreamplacefpga/ops/${OP_NAME}) + +if(TORCH_ENABLE_CUDA) + +add_torch_extension(integrate_net_weights_cuda + src/integrate_net_weights_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda + src/weighted_average_wirelength_cuda.cpp + src/weighted_average_wirelength_cuda_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda_atomic + src/weighted_average_wirelength_cuda_atomic.cpp + src/weighted_average_wirelength_cuda_atomic_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) + +add_pytorch_extension(${TARGET_NAME}_cuda_merged + src/weighted_average_wirelength_cuda_merged.cpp + src/weighted_average_wirelength_cuda_merged_kernel.cu + EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} + EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) + +install(TARGETS + ${TARGET_NAME}_cuda + ${TARGET_NAME}_cuda_atomic + ${TARGET_NAME}_cuda_merged + DESTINATION dreamplacefpga/ops/${OP_NAME}) +endif(TORCH_ENABLE_CUDA) + +file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) +install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py new file mode 100644 index 00000000..1d18f4f0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py @@ -0,0 +1,6 @@ +## +# @file __init__.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# @brief ops +# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore new file mode 100644 index 00000000..a2258671 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore @@ -0,0 +1,5 @@ +c +build +dist +*.egg-info +*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h new file mode 100644 index 00000000..7dce67fc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h @@ -0,0 +1,197 @@ +#ifndef GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_H +#define GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_H + +#include "utility/src/torch.h" +#include "utility/src/utils.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +void integrateNetWeightsLauncher( + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + const T *net_weights, + const T *net_weights_x, + T *grad_x_tensor, T *grad_y_tensor, + int num_nets, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int net_id = 0; net_id < num_nets; ++net_id) + { + if (net_mask[net_id]) + { + T weight = net_weights[net_id]; + T weight_x = net_weights_x[net_id]; + for (int j = netpin_start[net_id]; j < netpin_start[net_id + 1]; ++j) + { + int pin_id = flat_netpin[j]; + grad_x_tensor[pin_id] *= weight_x; + grad_y_tensor[pin_id] *= weight; + } + } + } +} + +// V has to be int, or long long int +template +void computeMaxMinNetByNet( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + V *x_max_ptr, + V *x_min_ptr, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + if (net_mask[i]) + { + const int x_index = i; + const int y_index = i + num_nets; + + V x_max = x_max_ptr[x_index]; + V x_min = x_min_ptr[x_index]; + V y_max = x_max_ptr[y_index]; + V y_min = x_min_ptr[y_index]; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = DREAMPLACE_STD_NAMESPACE::max((V)xx, x_max); + x_min = DREAMPLACE_STD_NAMESPACE::min((V)xx, x_min); + + T yy = y[flat_netpin[j]]; + y_max = DREAMPLACE_STD_NAMESPACE::max((V)yy, y_max); + y_min = DREAMPLACE_STD_NAMESPACE::min((V)yy, y_min); + } + + x_max_ptr[x_index] = x_max; + x_min_ptr[x_index] = x_min; + x_max_ptr[y_index] = y_max; + x_min_ptr[y_index] = y_min; + } + } +} + +template +void computeABCKernelsPinByPin( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_x, T *exp_nx, + T *exp_x_sum, T *exp_nx_sum, + T *xexp_x_sum, T *xexp_nx_sum, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_pins / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_pins; ++i) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); + exp_nx[i] = exp((x_min[net_id] - x[i]) * (*inv_gamma)); + +#pragma omp atomic + exp_x_sum[net_id] += exp_x[i]; +#pragma omp atomic + exp_nx_sum[net_id] += exp_nx[i]; +#pragma omp atomic + xexp_x_sum[net_id] += x[i] * exp_x[i]; +#pragma omp atomic + xexp_nx_sum[net_id] += x[i] * exp_nx[i]; + + net_id += num_nets; + int pin_id = i + num_pins; + exp_x[pin_id] = exp((y[i] - x_max[net_id]) * (*inv_gamma)); + exp_nx[pin_id] = exp((x_min[net_id] - y[i]) * (*inv_gamma)); + +#pragma omp atomic + exp_x_sum[net_id] += exp_x[pin_id]; +#pragma omp atomic + exp_nx_sum[net_id] += exp_nx[pin_id]; +#pragma omp atomic + xexp_x_sum[net_id] += y[i] * exp_x[pin_id]; +#pragma omp atomic + xexp_nx_sum[net_id] += y[i] * exp_nx[pin_id]; + } + } +} + +template +void computeXExpSumByExpSumXY( + const T *xexp_x_sum, const T *xexp_nx_sum, + const T *exp_x_sum, const T *exp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + //T *partial_wl, + T *partial_wl_x, + T *partial_wl_y, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + if (net_mask[i]) + { + //T wl_x = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; + partial_wl_x[i] = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; + int y_index = i + num_nets; + //T wl_y = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; + partial_wl_y[i] = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; + + //partial_wl[i] = wl_x + wl_y; + } + } +} + +template +void computeWeightedAverageWirelengthGradPinByPin( + const T *x, const T *y, + const T *exp_x, const T *exp_nx, + const T *exp_x_sum, const T *exp_nx_sum, + const T *xexp_x_sum, const T *xexp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_pins / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_pins; ++i) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + grad_x_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[i] - ((1 - (*inv_gamma) * x[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[i]); + + net_id += num_nets; + int pin_id = i + num_pins; + grad_y_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * y[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); + } + } +} + +DREAMPLACE_END_NAMESPACE + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h new file mode 100644 index 00000000..89f2a5a2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h @@ -0,0 +1,719 @@ +/** + * @file functional_cuda.h + * @author Yibo Lin (DREAMPlace) + * @date Nov 2018 + */ + +#ifndef GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_CUDA_H +#define GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_CUDA_H + +// V has to be int, or long long int +template +__global__ void computeMax( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + V *x_max) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicMax(&x_max[net_id], (V)(x[i])); + } + } +} + +// V has to be int, or long long int +template +__global__ void computeMin( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + V *x_min) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicMin(&x_min[net_id], (V)(x[i])); + } + } +} + +// V has to be int, or long long int +template +__global__ void computeMaxMinPinByPin( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + int num_nets, + V *x_max, + V *x_min) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicMax(&x_max[net_id], (V)(x[i])); + atomicMin(&x_min[net_id], (V)(x[i])); + + net_id += num_nets; + atomicMax(&x_max[net_id], (V)(y[i])); + atomicMin(&x_min[net_id], (V)(y[i])); + } + } +} + +// V has to be int, or long long int +template +__global__ void computeMaxMinInterleavePinByPin( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_pins, + int num_nets, + V *x_max, + V *x_min) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + net_id += threadIdx.y * num_nets; + int pin_id = i + threadIdx.y * num_pins; + + atomicMax(&x_max[net_id], (V)(x[pin_id])); + atomicMin(&x_min[net_id], (V)(x[pin_id])); + } + } +} + +// V has to be int, or long long int +template +__global__ void computeMaxMinNetByNet( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + V *x_max_ptr, + V *x_min_ptr) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + const int x_index = i; + const int y_index = i + num_nets; + + V x_max = x_max_ptr[x_index]; + V x_min = x_min_ptr[x_index]; + V y_max = x_max_ptr[y_index]; + V y_min = x_min_ptr[y_index]; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = max((V)xx, x_max); + x_min = min((V)xx, x_min); + + T yy = y[flat_netpin[j]]; + y_max = max((V)yy, y_max); + y_min = min((V)yy, y_min); + } + + x_max_ptr[x_index] = x_max; + x_min_ptr[x_index] = x_min; + x_max_ptr[y_index] = y_max; + x_min_ptr[y_index] = y_min; + } +} + +// V has to be int, or long long int +template +__global__ void computeMaxMinInterleaveNetByNet( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + V *pos_max_ptr, + V *pos_min_ptr) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + const T *pos_ptr = threadIdx.y == 0 ? x : y; + const int net_id = i + threadIdx.y * num_nets; + V pos_max = pos_max_ptr[net_id]; + V pos_min = pos_min_ptr[net_id]; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T pos = pos_ptr[flat_netpin[j]]; + pos_max = max((V)pos, pos_max); + pos_min = min((V)pos, pos_min); + } + pos_max_ptr[net_id] = pos_max; + pos_min_ptr[net_id] = pos_min; + } +} + +template +__global__ void computeExp( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, + T *exp_x) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); + } + } +} + +template +__global__ void computeNegExp( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_min, + T *exp_nx) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_nx[i] = exp(-(x[i] - x_min[net_id]) * (*inv_gamma)); + } + } +} + +template +__global__ void computeExpSum( + const T *exp_x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + T *exp_x_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicAdd(&exp_x_sum[net_id], exp_x[i]); + } + } +} + +template +__global__ void computeXExpSum( + const T *x, + const T *exp_x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + T *xexp_x_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + atomicAdd(&xexp_x_sum[net_id], x[i] * exp_x[i]); + } + } +} + +template +__global__ void computeABCKernelsPinByPin( + const T *x, const T *y, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_x, T *exp_nx, + T *exp_x_sum, T *exp_nx_sum, + T *xexp_x_sum, T *xexp_nx_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); + exp_nx[i] = exp((x_min[net_id] - x[i]) * (*inv_gamma)); + + atomicAdd(&exp_x_sum[net_id], exp_x[i]); + atomicAdd(&exp_nx_sum[net_id], exp_nx[i]); + atomicAdd(&xexp_x_sum[net_id], x[i] * exp_x[i]); + atomicAdd(&xexp_nx_sum[net_id], x[i] * exp_nx[i]); + + net_id += num_nets; + int pin_id = i + num_pins; + exp_x[pin_id] = exp((y[i] - x_max[net_id]) * (*inv_gamma)); + exp_nx[pin_id] = exp((x_min[net_id] - y[i]) * (*inv_gamma)); + + atomicAdd(&exp_x_sum[net_id], exp_x[pin_id]); + atomicAdd(&exp_nx_sum[net_id], exp_nx[pin_id]); + atomicAdd(&xexp_x_sum[net_id], y[i] * exp_x[pin_id]); + atomicAdd(&xexp_nx_sum[net_id], y[i] * exp_nx[pin_id]); + } + } +} + +template +__global__ void computeABCKernelsInterleavePinByPin( + const T *x, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_x, T *exp_nx, + T *exp_x_sum, T *exp_nx_sum, + T *xexp_x_sum, T *xexp_nx_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + net_id += threadIdx.y * num_nets; + int pin_id = i + threadIdx.y * num_pins; + + exp_x[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); + exp_nx[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); + + atomicAdd(&exp_x_sum[net_id], exp_x[pin_id]); + atomicAdd(&exp_nx_sum[net_id], exp_nx[pin_id]); + atomicAdd(&xexp_x_sum[net_id], x[pin_id] * exp_x[pin_id]); + atomicAdd(&xexp_nx_sum[net_id], x[pin_id] * exp_nx[pin_id]); + } + } +} + +template +__global__ void computeABCKernelsNetByNet( + const T *x, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int x_index = i; + int y_index = i + num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + exp_xy[pin_id] = exp((x[pin_id] - x_max[x_index]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[x_index] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[x_index] += exp_xy[pin_id]; + exp_nxy_sum[x_index] += exp_nxy[pin_id]; + xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; + + pin_id += num_pins; + exp_xy[pin_id] = exp((x[pin_id] - x_max[y_index]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[y_index] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[y_index] += exp_xy[pin_id]; + exp_nxy_sum[y_index] += exp_nxy[pin_id]; + xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; + } + } +} + +template +__global__ void computeABCKernelsInterleaveNetByNet( + const T *x, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int net_id = i + threadIdx.y * num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j] + threadIdx.y * num_pins; + + exp_xy[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[net_id] += exp_xy[pin_id]; + exp_nxy_sum[net_id] += exp_nxy[pin_id]; + xyexp_xy_sum[net_id] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[net_id] += x[pin_id] * exp_nxy[pin_id]; + } + } +} + +template +__global__ void computeABCKernelsAndWLNetByNet( + const T *x, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + T * partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int x_index = i; + int y_index = i + num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + exp_xy[pin_id] = exp((x[pin_id] - x_max[x_index]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[x_index] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[x_index] += exp_xy[pin_id]; + exp_nxy_sum[x_index] += exp_nxy[pin_id]; + xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; + + pin_id += num_pins; + exp_xy[pin_id] = exp((x[pin_id] - x_max[y_index]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[y_index] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[y_index] += exp_xy[pin_id]; + exp_nxy_sum[y_index] += exp_nxy[pin_id]; + xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; + } + partial_wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + + xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; + } +} + +template +__global__ void computeABCKernelsInterleaveAndWLNetByNet( + const T *x, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + V *x_max, V *x_min, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + T *partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int net_id = i + threadIdx.y * num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j] + threadIdx.y * num_pins; + + exp_xy[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[net_id] += exp_xy[pin_id]; + exp_nxy_sum[net_id] += exp_nxy[pin_id]; + xyexp_xy_sum[net_id] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[net_id] += x[pin_id] * exp_nxy[pin_id]; + } + atomicAdd(&partial_wl[i], xyexp_xy_sum[net_id] / exp_xy_sum[net_id] - xyexp_nxy_sum[net_id] / exp_nxy_sum[net_id]); + } +} + +template +__global__ void computeXExpSumByExpSum( + const T *xexp_x_sum, + const T *exp_x_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + T *partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + if (net_mask[i]) + { + partial_wl[i] = xexp_x_sum[i] / exp_x_sum[i]; + } + } +} + +template +__global__ void computeXNegExpSumByNegExpSum( + const T *xexp_nx_sum, + const T *exp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + T *partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets) + { + if (net_mask[i]) + { + partial_wl[i] = -xexp_nx_sum[i] / exp_nx_sum[i]; + } + } +} + +template +__global__ void computeXExpSumByExpSumXY( + const T *xexp_x_sum, const T *xexp_nx_sum, + const T *exp_x_sum, const T *exp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + T *partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + T wl_x = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; + int y_index = i + num_nets; + T wl_y = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; + + partial_wl[i] = wl_x + wl_y; + } +} + +template +__global__ void computeWeightedAverageWirelength( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + T *partial_wl) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + const int x_index = i; + const int y_index = i + num_nets; + + T x_max = -FLT_MAX; + T x_min = FLT_MAX; + T y_max = -FLT_MAX; + T y_min = FLT_MAX; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = max(xx, x_max); + x_min = min(xx, x_min); + + T yy = y[flat_netpin[j]]; + y_max = max(yy, y_max); + y_min = min(yy, y_min); + } + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + exp_xy[pin_id] = exp((x[pin_id] - x_max) * (*inv_gamma)); + exp_nxy[pin_id] = exp((x_min - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[x_index] += exp_xy[pin_id]; + exp_nxy_sum[x_index] += exp_nxy[pin_id]; + xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; + + pin_id += num_pins; + exp_xy[pin_id] = exp((x[pin_id] - y_max) * (*inv_gamma)); + exp_nxy[pin_id] = exp((y_min - x[pin_id]) * (*inv_gamma)); + exp_xy_sum[y_index] += exp_xy[pin_id]; + exp_nxy_sum[y_index] += exp_nxy[pin_id]; + xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; + } + + partial_wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + + xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; + } +} + +template +__global__ void computeWeightedAverageWirelengthGradPinByPin( + const T *x, const T *y, + const T *exp_x, const T *exp_nx, + const T *exp_x_sum, const T *exp_nx_sum, + const T *xexp_x_sum, const T *xexp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + grad_x_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[i] - ((1 - (*inv_gamma) * x[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[i]); + + net_id += num_nets; + int pin_id = i + num_pins; + grad_y_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * y[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); + } + } +} + +template +__global__ void computeWeightedAverageWirelengthGradInterleavePinByPin( + const T *x, const T *y, + const T *exp_x, const T *exp_nx, + const T *exp_x_sum, const T *exp_nx_sum, + const T *xexp_x_sum, const T *xexp_nx_sum, + const int *pin2net_map, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + net_id += threadIdx.y * num_nets; + int pin_id = i + threadIdx.y * num_pins; + + grad_x_tensor[pin_id] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); + } + } +} + +template +__global__ void computeWeightedAverageWirelengthGradNetByNet( + const T *x, const T *y, + const T *exp_x, const T *exp_nx, + const T *exp_x_sum, const T *exp_nx_sum, + const T *xexp_x_sum, const T *xexp_nx_sum, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int x_index = i; + int y_index = i + num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + grad_x_tensor[pin_id] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[x_index] - (*inv_gamma) * xexp_x_sum[x_index]) / (exp_x_sum[x_index] * exp_x_sum[x_index]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[x_index] + (*inv_gamma) * xexp_nx_sum[x_index]) / (exp_nx_sum[x_index] * exp_nx_sum[x_index]) * exp_nx[pin_id]); + + int pin_id_y = pin_id + num_pins; + grad_y_tensor[pin_id] = (*grad_tensor) * + (((1 + (*inv_gamma) * y[pin_id]) * exp_x_sum[y_index] - (*inv_gamma) * xexp_x_sum[y_index]) / (exp_x_sum[y_index] * exp_x_sum[y_index]) * exp_x[pin_id_y] - ((1 - (*inv_gamma) * y[pin_id]) * exp_nx_sum[y_index] + (*inv_gamma) * xexp_nx_sum[y_index]) / (exp_nx_sum[y_index] * exp_nx_sum[y_index]) * exp_nx[pin_id_y]); + } + } +} + +template +__global__ void computeWeightedAverageWirelengthGradInterleaveNetByNet( + const T *x, const T *y, + const T *exp_x, const T *exp_nx, + const T *exp_x_sum, const T *exp_nx_sum, + const T *xexp_x_sum, const T *xexp_nx_sum, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_nets && net_mask[i]) + { + int net_id = i + threadIdx.y * num_nets; + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j] + threadIdx.y * num_pins; + grad_x_tensor[pin_id] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); + } + } +} + +#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu new file mode 100644 index 00000000..da3ef8f0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu @@ -0,0 +1,66 @@ +/** + * @file integrate_net_weights_cuda_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Jul 2019 + */ + +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +#include "weighted_average_wirelength/src/functional_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void integrateNetWeights( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + const T* net_weights_x, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_pins) + { + int net_id = pin2net_map[i]; + T weight = net_weights[net_id]; + T weight_x = net_weights_x[net_id]; + if (net_id >= 0 && net_mask[net_id]) + { + grad_x_tensor[i] *= weight_x; + grad_y_tensor[i] *= weight; + } + } +} + +template +void integrateNetWeightsCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + const T* net_weights_x, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ) +{ + integrateNetWeights<<>>(pin2net_map, net_mask, net_weights, net_weights_x, grad_x_tensor, grad_y_tensor, num_pins); +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template void integrateNetWeightsCudaLauncher(\ + const int* pin2net_map, \ + const unsigned char* net_mask, \ + const T* net_weights, \ + const T* net_weights_x, \ + T* grad_x_tensor, T* grad_y_tensor, \ + int num_pins\ + ); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp new file mode 100644 index 00000000..877ccffa --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp @@ -0,0 +1,290 @@ +/** + * @file src/weighted_average_wirelength.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute weighted-average wirelength and gradient according to e-place + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" +#include "weighted_average_wirelength/src/functional.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeWeightedAverageWirelengthLauncher( + const T *x, const T *y, + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + //T *wl, + T *wl_x, T* wl_y, + const T *grad_tensor, + int num_threads, + T *grad_x_tensor, T *grad_y_tensor); + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x " must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x " must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x " must be contiguous") + +/// @brief Compute weighted-average wirelength according to e-place +/// \sum(x*exp(x/gamma)) / \sum(exp(x/gamma)) - \sum(x*exp(-x/gamma)) / \sum(exp(-x/gamma)) +/// @param pos cell locations, array of x locations and then y locations +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma a scalar tensor for the parameter in the equation +std::vector weighted_average_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_weights_x, //Directional net weighting for carry chains + at::Tensor net_mask, + at::Tensor inv_gamma, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + at::Tensor exp_xy = at::empty_like(pos); + at::Tensor exp_nxy = at::empty_like(pos); + at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + //Update to compute x and y separately + //at::Tensor wl = at::zeros(num_nets, pos.options()); + at::Tensor wl_x = at::zeros(num_nets, pos.options()); + at::Tensor wl_y = at::zeros(num_nets, pos.options()); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthLauncher", [&] { + computeWeightedAverageWirelengthLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel() / 2, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + //DREAMPLACE_TENSOR_DATA_PTR(wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(wl_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(wl_y, scalar_t), + nullptr, + num_threads, + nullptr, nullptr); + }); + + if (net_weights.numel()) + { + //Apply different weights for x and y directions + //wl.mul_(net_weights); + wl_x.mul_(net_weights_x); + wl_y.mul_(net_weights); + } + + //return {wl.sum(), exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; + return {wl_x.sum() + wl_y.sum(), exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma a scalar tensor for the parameter in the equation +at::Tensor weighted_average_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor exp_xy, at::Tensor exp_nxy, + at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, + at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + at::Tensor inv_gamma, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + at::Tensor grad_out = at::zeros_like(pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthLauncher", [&] { + computeWeightedAverageWirelengthLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel() / 2, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + netpin_start.numel() - 1, + pos.numel() / 2, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + nullptr, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + num_threads, + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2); + if (net_weights.numel()) + { + integrateNetWeightsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, + netpin_start.numel() - 1, + num_threads); + } + }); + return grad_out; +} + +template +int computeWeightedAverageWirelengthLauncher( + const T *x, const T *y, + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + //T *wl, + T *wl_x, + T *wl_y, + const T *grad_tensor, + int num_threads, + T *grad_x_tensor, T *grad_y_tensor) +{ + if (grad_tensor) + { + int chunk_size = std::max(int(num_pins / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_pins; ++i) + { + int net_id = pin2net_map[i]; + if (net_mask[net_id]) + { + grad_x_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * x[i]) * exp_xy_sum[net_id] - (*inv_gamma) * xyexp_xy_sum[net_id]) / (exp_xy_sum[net_id] * exp_xy_sum[net_id]) * exp_xy[i] - ((1 - (*inv_gamma) * x[i]) * exp_nxy_sum[net_id] + (*inv_gamma) * xyexp_nxy_sum[net_id]) / (exp_nxy_sum[net_id] * exp_nxy_sum[net_id]) * exp_nxy[i]); + + net_id += num_nets; + int pin_id = i + num_pins; + grad_y_tensor[i] = (*grad_tensor) * + (((1 + (*inv_gamma) * y[i]) * exp_xy_sum[net_id] - (*inv_gamma) * xyexp_xy_sum[net_id]) / (exp_xy_sum[net_id] * exp_xy_sum[net_id]) * exp_xy[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nxy_sum[net_id] + (*inv_gamma) * xyexp_nxy_sum[net_id]) / (exp_nxy_sum[net_id] * exp_nxy_sum[net_id]) * exp_nxy[pin_id]); + } + } + } + else + { + int chunk_size = std::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + if (!net_mask[i]) + { + continue; + } + + int x_index = i; + int y_index = i + num_nets; + + //int degree = netpin_start[i+1]-netpin_start[i]; + T x_max = -std::numeric_limits::max(); + T x_min = std::numeric_limits::max(); + T y_max = -std::numeric_limits::max(); + T y_min = std::numeric_limits::max(); + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = std::max(xx, x_max); + x_min = std::min(xx, x_min); + T yy = y[flat_netpin[j]]; + y_max = std::max(yy, y_max); + y_min = std::min(yy, y_min); + } + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + int pin_id = flat_netpin[j]; + exp_xy[pin_id] = exp((x[pin_id] - x_max) * (*inv_gamma)); + exp_nxy[pin_id] = exp(-(x[pin_id] - x_min) * (*inv_gamma)); + exp_xy_sum[x_index] += exp_xy[pin_id]; + exp_nxy_sum[x_index] += exp_nxy[pin_id]; + xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; + + pin_id += num_pins; + exp_xy[pin_id] = exp((x[pin_id] - y_max) * (*inv_gamma)); + exp_nxy[pin_id] = exp(-(x[pin_id] - y_min) * (*inv_gamma)); + exp_xy_sum[y_index] += exp_xy[pin_id]; + exp_nxy_sum[y_index] += exp_nxy[pin_id]; + xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; + xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; + } + + //Split as x and y + //wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + + // xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; + wl_x[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index]; + wl_y[i] = xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; + } + } + + return 0; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp new file mode 100644 index 00000000..6d3a1fec --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp @@ -0,0 +1,245 @@ +#include "utility/src/Msg.h" +#include "utility/src/torch.h" +#include "weighted_average_wirelength/src/functional.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeWeightedAverageWirelengthAtomicLauncher( + const T *pos, const int *pin2net_map, const int *flat_netpin, + const int *netpin_start, const unsigned char *net_mask, int num_nets, + int num_pins, const T *inv_gamma, T *exp_xy, T *exp_nxy, T *exp_xy_sum, + T *exp_nxy_sum, T *xyexp_xy_sum, T *xyexp_nxy_sum, V *xy_max, V *xy_min, + //T *partial_wl, // wirelength of each net + T *partial_wl_x, T *partial_wl_y, // wirelength of each net + const T *grad_tensor, T *grad_x_tensor, + T *grad_y_tensor, // the gradient is partial total wirelength to partial pin position + int num_threads) +{ + const T *x = pos; + const T *y = pos + num_pins; + + if (grad_tensor) + { + computeWeightedAverageWirelengthGradPinByPin( + x, y, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, + xyexp_nxy_sum, pin2net_map, net_mask, num_nets, num_pins, inv_gamma, + grad_tensor, grad_x_tensor, grad_y_tensor, num_threads); + } + else + { + // PinByPin implementation needs atomic min/max operations, which are not supported by OpenMP. + computeMaxMinNetByNet( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + xy_max, + xy_min, + num_threads); + + // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA + // kernels (pin by pin) corresponding to the plus and minus a b c kernels in + // the DREAMPlace paper + computeABCKernelsPinByPin( + x, y, pin2net_map, net_mask, num_nets, num_pins, inv_gamma, xy_max, + xy_min, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, + xyexp_nxy_sum, num_threads); + + // compute partial wirelength + computeXExpSumByExpSumXY( + xyexp_xy_sum, xyexp_nxy_sum, exp_xy_sum, exp_nxy_sum, pin2net_map, + net_mask, num_nets, partial_wl_x, partial_wl_y, num_threads); + } + + return 0; +} + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +typedef int V; + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i +/// x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma inverse of gamma coefficient in weighted average +/// wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_atomic_forward( + at::Tensor pos, at::Tensor pin2net_map, at::Tensor flat_netpin, + at::Tensor netpin_start, at::Tensor net_weights, + at::Tensor net_weights_x, at::Tensor net_mask, + at::Tensor inv_gamma, int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for + // -y + at::Tensor exp_xy = at::empty_like(pos); + at::Tensor exp_nxy = at::empty_like(pos); + at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + //at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); + at::Tensor partial_wl_x = at::zeros({num_nets}, pos.options()); + at::Tensor partial_wl_y = at::zeros({num_nets}, pos.options()); + + // it is ok for xy_max and xy_min to be integer + // we do not really need accurate max/min, just some values to scale x/y + // therefore, there is no need to scale xy_max and xy_min to improve accuracy + at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::kInt); + at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::kInt); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeWeightedAverageWirelengthAtomicLauncher", [&] { + computeWeightedAverageWirelengthAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), num_nets, + num_pins, DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), + //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), nullptr, nullptr, nullptr, + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), + nullptr, nullptr, nullptr, + num_threads); + }); + + if (net_weights.numel()) + { + //partial_wl.mul_(net_weights.view({num_nets})); + partial_wl_x.mul_(net_weights_x.view({num_nets})); + partial_wl_y.mul_(net_weights.view({num_nets})); + } + // significant speedup is achieved by using summation in ATen + //auto wl = partial_wl.sum(); + auto wl = partial_wl_x.sum() + partial_wl_y.sum(); + return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) +/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) +/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then +/// \sum(exp(y/gamma)) +/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then +/// \sum(exp(-y/gamma)) +/// @param xyexp_xy_sum array of \sum(x*exp(x/gamma)) for each net and then +/// \sum(y*exp(y/gamma)) +/// @param xyexp_nxy_sum array of \sum(x*exp(-x/gamma)) for each net and then +/// \sum(y*exp(-y/gamma)) +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or +/// not +/// @param inv_gamma inverse of gamma, a scalar tensor for the parameter in the +/// equation +at::Tensor weighted_average_wirelength_atomic_backward( + at::Tensor grad_pos, at::Tensor pos, at::Tensor exp_xy, at::Tensor exp_nxy, + at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, at::Tensor xyexp_xy_sum, + at::Tensor xyexp_nxy_sum, at::Tensor pin2net_map, at::Tensor flat_netpin, + at::Tensor netpin_start, at::Tensor net_weights, + at::Tensor net_weights_x, at::Tensor net_mask, + at::Tensor inv_gamma, int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(exp_xy); + CHECK_EVEN(exp_xy); + CHECK_CONTIGUOUS(exp_xy); + CHECK_FLAT(exp_nxy); + CHECK_EVEN(exp_nxy); + CHECK_CONTIGUOUS(exp_nxy); + CHECK_FLAT(exp_xy_sum); + CHECK_EVEN(exp_xy_sum); + CHECK_CONTIGUOUS(exp_xy_sum); + CHECK_FLAT(exp_nxy_sum); + CHECK_EVEN(exp_nxy_sum); + CHECK_CONTIGUOUS(exp_nxy_sum); + CHECK_FLAT(xyexp_xy_sum); + CHECK_EVEN(xyexp_xy_sum); + CHECK_CONTIGUOUS(xyexp_xy_sum); + CHECK_FLAT(xyexp_nxy_sum); + CHECK_EVEN(xyexp_nxy_sum); + CHECK_CONTIGUOUS(xyexp_nxy_sum); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + at::Tensor grad_out = at::zeros_like(pos); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES( + pos, "computeWeightedAverageWirelengthAtomicLauncher", [&] { + computeWeightedAverageWirelengthAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), num_nets, + num_pins, DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), nullptr, nullptr, nullptr, nullptr, + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, + num_threads); + if (net_weights.numel()) + { + integrateNetWeightsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, + netpin_start.numel() - 1, + num_threads); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_forward, "WeightedAverageWirelength forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_backward, "WeightedAverageWirelength backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp new file mode 100644 index 00000000..70b2ad78 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp @@ -0,0 +1,217 @@ +/** + * @file hpwl_cuda.cpp + * @author Yibo Lin (DREAMPlace) + * @date Jun 2018 + * @brief Compute weighted-average wirelength and gradient according to e-place + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param x x location of pins. +/// @param y y location of pins. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_mask whether compute the wirelength for a net or not +/// @param net_weights weight of nets +/// @param num_nets number of nets. +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. +/// @param grad_tensor back-propagated gradient from previous stage. +/// @param grad_x_tensor gradient in x direction. +/// @param grad_y_tensor gradient in y direction. +/// @return 0 if successfully done. +template +int computeWeightedAverageWirelengthCudaLauncher( + const T *x, const T *y, + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + V *xy_max, V *xy_min, + T *partial_wl, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor); + +/// @brief add net weights to gradient +template +void integrateNetWeightsCudaLauncher( + const int *pin2net_map, + const unsigned char *net_mask, + const T *net_weights, + const T *net_weights_x, + T *grad_x_tensor, T *grad_y_tensor, + int num_pins); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +typedef int V; + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + at::Tensor exp_xy = at::empty_like(pos); + at::Tensor exp_nxy = at::empty_like(pos); + at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); + + // it is ok for xy_max and xy_min to be integer + // we do not really need accurate max/min, just some values to scale x/y + // therefore, there is no need to scale xy_max and xy_min to improve accuracy + at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); + at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaLauncher", [&] { + computeWeightedAverageWirelengthCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + nullptr, + nullptr, nullptr); + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({num_nets})); + } + }); + + auto wl = partial_wl.sum(); + return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma a scalar tensor for the parameter in the equation +at::Tensor weighted_average_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor exp_xy, at::Tensor exp_nxy, + at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, + at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + at::Tensor grad_out = at::zeros_like(pos); + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaLauncher", [&] { + computeWeightedAverageWirelengthCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + nullptr, nullptr, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins); + if (net_weights.numel()) + { + integrateNetWeightsCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t),//Use instead of net_weights_x + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, + num_pins); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp new file mode 100644 index 00000000..8030c254 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp @@ -0,0 +1,228 @@ +/** + * @file weighted_average_wirelength_cuda_atomic.cpp + * @author Yibo Lin (DREAMPlace) + * @date Aug 2018 + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeWeightedAverageWirelengthCudaAtomicLauncher( + const T *pos, + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + V *xy_max, V *xy_min, + T *partial_wl, // wirelength of each net + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor // the gradient is partial total wirelength to partial pin position +); + +/// @brief add net weights to gradient +template +void integrateNetWeightsCudaLauncher( + const int* pin2net_map, + const unsigned char* net_mask, + const T* net_weights, + const T* net_weights_x, + T* grad_x_tensor, T* grad_y_tensor, + int num_pins + ); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +typedef int V; + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma inverse of gamma coefficient in weighted average wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_atomic_forward( + at::Tensor pos, + at::Tensor pin2net_map, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for -y + at::Tensor exp_xy = at::empty_like(pos); + at::Tensor exp_nxy = at::empty_like(pos); + at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); + at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); + + // it is ok for xy_max and xy_min to be integer + // we do not really need accurate max/min, just some values to scale x/y + // therefore, there is no need to scale xy_max and xy_min to improve accuracy + at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); + at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaAtomicLauncher", [&] { + computeWeightedAverageWirelengthCudaAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + nullptr, + nullptr, nullptr); + }); + + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({num_nets})); + } + // significant speedup is achieved by using summation in ATen + auto wl = partial_wl.sum(); + return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) +/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) +/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) +/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) +/// @param xyexp_xy_sum array of \sum(x*exp(x/gamma)) for each net and then \sum(y*exp(y/gamma)) +/// @param xyexp_nxy_sum array of \sum(x*exp(-x/gamma)) for each net and then \sum(y*exp(-y/gamma)) +/// @param pin2net_map map pin to net +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma inverse of gamma, a scalar tensor for the parameter in the equation +at::Tensor weighted_average_wirelength_atomic_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor exp_xy, at::Tensor exp_nxy, + at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, + at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, + at::Tensor pin2net_map, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(exp_xy); + CHECK_EVEN(exp_xy); + CHECK_CONTIGUOUS(exp_xy); + CHECK_FLAT(exp_nxy); + CHECK_EVEN(exp_nxy); + CHECK_CONTIGUOUS(exp_nxy); + CHECK_FLAT(exp_xy_sum); + CHECK_EVEN(exp_xy_sum); + CHECK_CONTIGUOUS(exp_xy_sum); + CHECK_FLAT(exp_nxy_sum); + CHECK_EVEN(exp_nxy_sum); + CHECK_CONTIGUOUS(exp_nxy_sum); + CHECK_FLAT(xyexp_xy_sum); + CHECK_EVEN(xyexp_xy_sum); + CHECK_CONTIGUOUS(xyexp_xy_sum); + CHECK_FLAT(xyexp_nxy_sum); + CHECK_EVEN(xyexp_nxy_sum); + CHECK_CONTIGUOUS(xyexp_nxy_sum); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + + at::Tensor grad_out = at::zeros_like(pos); + + int num_nets = net_mask.numel(); + int num_pins = pin2net_map.numel(); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaAtomicLauncher", [&] { + computeWeightedAverageWirelengthCudaAtomicLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), + nullptr, nullptr, + nullptr, + DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins + ); + if (net_weights.numel()) + { + integrateNetWeightsCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), //Re-use for net_weights_x + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, + num_pins + ); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_forward, "WeightedAverageWirelength forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_backward, "WeightedAverageWirelength backward (CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu new file mode 100644 index 00000000..a17f07ae --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu @@ -0,0 +1,155 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +#include "weighted_average_wirelength/src/functional_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeWeightedAverageWirelengthCudaAtomicLauncher( + const T *pos, // x then y + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + V *xy_max, V *xy_min, + T *partial_wl, // wirelength of each net + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor // the gradient is partial total wirelength to partial pin position +) +{ + int thread_count = 64; + int block_count_pins = (num_pins - 1 + thread_count) / thread_count; + int block_count_nets = (num_nets - 1 + thread_count) / thread_count; + dim3 block_size(thread_count, 2, 1); + + const T* x = pos; + const T* y = pos + num_pins; + + if (grad_tensor) + { + // computeWeightedAverageWirelengthGradInterleavePinByPin<<>>( + computeWeightedAverageWirelengthGradPinByPin<<>>( + x, y, + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + xyexp_xy_sum, xyexp_nxy_sum, + pin2net_map, + net_mask, + num_nets, + num_pins, + inv_gamma, + grad_tensor, + grad_x_tensor, grad_y_tensor); + } + else + { + #if 0 + // compute max and min in one kernel (pin by pin) + // computeMaxMinInterleavePinByPin<<>>( + computeMaxMinPinByPin<<>>( + x, y, + pin2net_map, + net_mask, + num_pins, + num_nets, + xy_max, + xy_min); + #else + // compute max and min in one kernel (net by net) + computeMaxMinInterleaveNetByNet<<>>( + // computeMaxMinNetByNet<<>>( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + xy_max, + xy_min); + #endif + + #if 1 + // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (pin by pin) + // corresponding to the plus and minus a b c kernels in the DREAMPlace paper + // computeABCKernelsInterleavePinByPin<<>>( + computeABCKernelsPinByPin<<>>( + // pos, + x, y, + pin2net_map, + net_mask, + num_nets, + num_pins, + inv_gamma, + xy_max, xy_min, + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + xyexp_xy_sum, xyexp_nxy_sum); + + // compute partial wirelength + computeXExpSumByExpSumXY<<>>( + xyexp_xy_sum, xyexp_nxy_sum, + exp_xy_sum, exp_nxy_sum, + pin2net_map, + net_mask, + num_nets, + partial_wl); + #else + // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (net by net) + // corresponding to the plus and minus a b c kernels in the DREAMPlace paper + // compute partial wirelength at the same time + // computeABCKernelsInterleaveAndWLNetByNet<<>>( + computeABCKernelsAndWLNetByNet<<>>( + pos, + flat_netpin, + netpin_start, + net_mask, + num_nets, + num_pins, + inv_gamma, + xy_max, xy_min, + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + xyexp_xy_sum, xyexp_nxy_sum, + partial_wl); + + #endif + + // Yibo: move out the summation to use ATen + // significant speedup is observed + //sumArray<<<1, 1>>>(partial_wl, num_nets, wl); + } + + return 0; +} + + +#define REGISTER_KERNEL_LAUNCHER(T, V) \ + template int computeWeightedAverageWirelengthCudaAtomicLauncher( \ + const T *pos, \ + const int *pin2net_map, \ + const int *flat_netpin, \ + const int *netpin_start, \ + const unsigned char *net_mask, \ + int num_nets, \ + int num_pins, \ + const T *inv_gamma, \ + T *exp_xy, T *exp_nxy, \ + T *exp_xy_sum, T *exp_nxy_sum, \ + T *xyexp_xy_sum, T *xyexp_nxy_sum, \ + V *xy_max, V *xy_min, \ + T* partial_wl, \ + const T *grad_tensor, \ + T *grad_x_tensor, T *grad_y_tensor); + +REGISTER_KERNEL_LAUNCHER(float, int); +REGISTER_KERNEL_LAUNCHER(double, int); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu new file mode 100644 index 00000000..3bd0822d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu @@ -0,0 +1,106 @@ +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" +#include "weighted_average_wirelength/src/functional_cuda.h" + +DREAMPLACE_BEGIN_NAMESPACE + +template +int computeWeightedAverageWirelengthCudaLauncher( + const T *x, const T *y, + const int *pin2net_map, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + int num_pins, + const T *inv_gamma, + T *exp_xy, T *exp_nxy, + T *exp_xy_sum, T *exp_nxy_sum, + T *xyexp_xy_sum, T *xyexp_nxy_sum, + V *xy_max, V *xy_min, + T *partial_wl, + const T *grad_tensor, + T *grad_x_tensor, T *grad_y_tensor) +{ + int thread_count = 64; + int block_count_pins = (num_pins - 1 + thread_count) / thread_count; + int block_count_nets = (num_nets - 1 + thread_count) / thread_count; + dim3 block_size(thread_count, 2, 1); + + if (grad_tensor) + { + // computeWeightedAverageWirelengthGradInterleaveNetByNet<<>>( + computeWeightedAverageWirelengthGradNetByNet<<>>( + x, y, + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + xyexp_xy_sum, xyexp_nxy_sum, + flat_netpin, + netpin_start, + net_mask, + num_nets, + num_pins, + inv_gamma, + grad_tensor, + grad_x_tensor, grad_y_tensor); + } + else + { + // compute max and min in one kernel (net by net) + // computeMaxMinInterleaveNetByNet<<>>( + computeMaxMinNetByNet<<>>( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + xy_max, + xy_min); + + // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (net by net) + // corresponding to the plus and minus a b c kernels in the DREAMPlace paper + // compute partial wirelength at the same time + computeABCKernelsInterleaveAndWLNetByNet<<>>( + // computeABCKernelsAndWLNetByNet<<>>( + x, + flat_netpin, + netpin_start, + net_mask, + num_nets, + num_pins, + inv_gamma, + xy_max, xy_min, + exp_xy, exp_nxy, + exp_xy_sum, exp_nxy_sum, + xyexp_xy_sum, xyexp_nxy_sum, + partial_wl); + } + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T, V) \ + template int computeWeightedAverageWirelengthCudaLauncher( \ + const T *x, const T *y, \ + const int *pin2net_map, \ + const int *flat_netpin, \ + const int *netpin_start, \ + const unsigned char *net_mask, \ + int num_nets, \ + int num_pins, \ + const T *inv_gamma, \ + T *exp_xy, T *exp_nxy, \ + T *exp_xy_sum, T *exp_nxy_sum, \ + T *xyexp_xy_sum, T *xyexp_nxy_sum, \ + V *xy_max, V *xy_min, \ + T *partial_wl, \ + const T *grad_tensor, \ + T *grad_x_tensor, T *grad_y_tensor); + +REGISTER_KERNEL_LAUNCHER(float, int); +REGISTER_KERNEL_LAUNCHER(double, int); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp new file mode 100644 index 00000000..09edca2c --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp @@ -0,0 +1,302 @@ +/** + * @file weighted_average_wirelength_cuda_merged.cpp + * @author Yibo Lin (DREAMPlace) + * @date Sep 2019 + * @brief Compute weighted-average wirelength and gradient according to e-place + */ +#include "utility/src/torch.h" +#include "utility/src/Msg.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param x x location of pins. +/// @param y y location of pins. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_mask whether compute the wirelength for a net or not +/// @param net_weights weight of nets +/// @param num_nets number of nets. +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. +/// @param grad_tensor back-propagated gradient from previous stage. +/// @param grad_x_tensor gradient in x direction. +/// @param grad_y_tensor gradient in y direction. +/// @return 0 if successfully done. +template +int computeWeightedAverageWirelengthCudaMergedLauncher( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* inv_gamma, + //T* partial_wl, + T* partial_wl_x, + T* partial_wl_y, + T* grad_intermediate_x, T* grad_intermediate_y + ); + + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param x x location of pins. +/// @param y y location of pins. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_mask whether compute the wirelength for a net or not +/// @param net_weights weight of nets +/// @param num_nets number of nets. +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. +/// @param grad_tensor back-propagated gradient from previous stage. +/// @param grad_x_tensor gradient in x direction. +/// @param grad_y_tensor gradient in y direction. +/// @return 0 if successfully done. +template +int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* inv_gamma, + const T* bbox_min_x, const T* bbox_min_y, + const T* bbox_max_x, const T* bbox_max_y, + T* partial_wl, + T* grad_intermediate_x, T* grad_intermediate_y + ); + +/// @brief add net weights to gradient +template +void integrateNetWeightsCudaLauncher( + const int *pin2net_map, + const unsigned char *net_mask, + const T *net_weights, + const T *net_weights_x, + T *grad_x_tensor, T *grad_y_tensor, + int num_pins); + +#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_weights_x); + CHECK_CONTIGUOUS(net_weights_x); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + // x, y interleave + //at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); + at::Tensor partial_wl_x = at::zeros({num_nets, 2}, pos.options()); + at::Tensor partial_wl_y = at::zeros({num_nets, 2}, pos.options()); + // timed with grad_in yet + at::Tensor grad_intermediate = at::zeros_like(pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncher", [&] { + computeWeightedAverageWirelengthCudaMergedLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins + ); + if (net_weights.numel()) + { + //partial_wl.mul_(net_weights.view({num_nets, 1})); + partial_wl_x.mul_(net_weights_x.view({num_nets, 1})); + partial_wl_y.mul_(net_weights.view({num_nets, 1})); + } + }); + + //auto wl = partial_wl.sum(); + auto wl = partial_wl_x.sum() + partial_wl_y.sum(); + //at::Tensor wl = at::zeros(1, pos.options()); + return {wl, grad_intermediate}; +} + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_forward_fpga( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_mask, + at::Tensor inv_gamma, + at::Tensor net_bounding_box_min, + at::Tensor net_bounding_box_max) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(net_bounding_box_min); + CHECK_EVEN(net_bounding_box_min); + CHECK_CONTIGUOUS(net_bounding_box_min); + CHECK_FLAT(net_bounding_box_max); + CHECK_EVEN(net_bounding_box_max); + CHECK_CONTIGUOUS(net_bounding_box_max); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + // x, y interleave + at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); + // timed with grad_in yet + at::Tensor grad_intermediate = at::zeros_like(pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncherFPGA", [&] { + computeWeightedAverageWirelengthCudaMergedLauncherFPGA( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t) + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t) + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins + ); + if (net_weights.numel()) + { + partial_wl.mul_(net_weights.view({num_nets, 1})); + } + }); + + auto wl = partial_wl.sum(); + //at::Tensor wl = at::zeros(1, pos.options()); + return {wl, grad_intermediate}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma a scalar tensor for the parameter in the equation +at::Tensor weighted_average_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor grad_intermediate, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + at::Tensor inv_gamma) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_weights_x); + CHECK_CONTIGUOUS(net_weights_x); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(grad_intermediate); + CHECK_EVEN(grad_intermediate); + CHECK_CONTIGUOUS(grad_intermediate); + + at::Tensor grad_out = grad_intermediate.mul_(grad_pos); + //int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncher", [&] { + if (net_weights.numel()) + { + integrateNetWeightsCudaLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, + num_pins); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward (CUDA)"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward (CUDA)"); + m.def("forward_fpga", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward_fpga, "WeightedAverageWirelength forward reuse net bbox(CUDA)"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu new file mode 100644 index 00000000..1d948fe4 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu @@ -0,0 +1,264 @@ +/** + * @file weighted_average_wirelength_cuda_merged_kernel.cu + * @author Yibo Lin (DREAMPlace) + * @date Sep 2019 + */ + +#include +#include +#include "assert.h" +#include "cuda_runtime.h" +#include "utility/src/utils.cuh" + +DREAMPLACE_BEGIN_NAMESPACE + +template +__global__ void computeWeightedAverageWirelength( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* inv_gamma, + //T* partial_wl, + T* partial_wl_x, + T* partial_wl_y, + T* grad_intermediate_x, T* grad_intermediate_y) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + int ii = i >> 1; + if (ii < num_nets && net_mask[ii]) + { + const T *values; + T *grads; + T *partial_wl; + if (i & 1) + { + values = y; + grads = grad_intermediate_y; + partial_wl = partial_wl_y; + } + else + { + values = x; + grads = grad_intermediate_x; + partial_wl = partial_wl_x; + } + + // int degree = netpin_start[ii+1]-netpin_start[ii]; + T x_max = -FLT_MAX; + T x_min = FLT_MAX; + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + x_max = max(xx, x_max); + x_min = min(xx, x_min); + } + + T xexp_x_sum = 0; + T xexp_nx_sum = 0; + T exp_x_sum = 0; + T exp_nx_sum = 0; + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + xexp_x_sum += xx * exp_x; + xexp_nx_sum += xx * exp_nx; + exp_x_sum += exp_x; + exp_nx_sum += exp_nx; + } + + partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; + + T b_x = (*inv_gamma) / (exp_x_sum); + T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; + T b_nx = -(*inv_gamma) / (exp_nx_sum); + T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; + + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + grads[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; + } + } +} + +template +__global__ void computeWeightedAverageWirelengthFPGA( + const T* x, const T* y, + const int* flat_netpin, + const int* netpin_start, + const unsigned char* net_mask, + int num_nets, + const T* inv_gamma, + const T* bbox_min_x, const T* bbox_min_y, + const T* bbox_max_x, const T* bbox_max_y, + T* partial_wl, + T* grad_intermediate_x, T* grad_intermediate_y) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + int ii = i >> 1; + if (ii < num_nets && net_mask[ii]) + { + const T *values; + const T *bbox_min; + const T *bbox_max; + T *grads; + if (i & 1) + { + values = y; + grads = grad_intermediate_y; + bbox_min = bbox_min_y; + bbox_max = bbox_max_y; + } + else + { + values = x; + grads = grad_intermediate_x; + bbox_min = bbox_min_x; + bbox_max = bbox_max_x; + } + + //// int degree = netpin_start[ii+1]-netpin_start[ii]; + //T x_max = -FLT_MAX; + //T x_min = FLT_MAX; + //for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + //{ + // T xx = values[flat_netpin[j]]; + // x_max = max(xx, x_max); + // x_min = min(xx, x_min); + //} + + T xexp_x_sum = 0; + T xexp_nx_sum = 0; + T exp_x_sum = 0; + T exp_nx_sum = 0; + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + //T exp_x = exp((xx - x_max) * (*inv_gamma)); + //T exp_nx = exp((x_min - xx) * (*inv_gamma)); + T exp_x = exp((xx - bbox_max[ii]) * (*inv_gamma)); + T exp_nx = exp((bbox_min[ii] - xx) * (*inv_gamma)); + + xexp_x_sum += xx * exp_x; + xexp_nx_sum += xx * exp_nx; + exp_x_sum += exp_x; + exp_nx_sum += exp_nx; + } + + // partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; + + T b_x = (*inv_gamma) / (exp_x_sum); + T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; + T b_nx = -(*inv_gamma) / (exp_nx_sum); + T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; + + for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) + { + T xx = values[flat_netpin[j]]; + //T exp_x = exp((xx - x_max) * (*inv_gamma)); + //T exp_nx = exp((x_min - xx) * (*inv_gamma)); + T exp_x = exp((xx - bbox_max[ii]) * (*inv_gamma)); + T exp_nx = exp((bbox_min[ii] - xx) * (*inv_gamma)); + + grads[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; + } + } +} + +template +int computeWeightedAverageWirelengthCudaMergedLauncher( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + const T *inv_gamma, + //T *partial_wl, + T *partial_wl_x, + T *partial_wl_y, + T *grad_intermediate_x, T *grad_intermediate_y) +{ + int thread_count = 64; + int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y + + computeWeightedAverageWirelength<<>>( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + inv_gamma, + //partial_wl, + partial_wl_x, + partial_wl_y, + grad_intermediate_x, grad_intermediate_y); + + return 0; +} + +template +int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + const T *inv_gamma, + const T* bbox_min_x, const T* bbox_min_y, + const T* bbox_max_x, const T* bbox_max_y, + T *partial_wl, + T *grad_intermediate_x, T *grad_intermediate_y) +{ + int thread_count = 64; + int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y + + computeWeightedAverageWirelengthFPGA<<>>( + x, y, + flat_netpin, + netpin_start, + net_mask, + num_nets, + inv_gamma, + bbox_min_x, bbox_min_y, + bbox_max_x, bbox_max_y, + partial_wl, + grad_intermediate_x, grad_intermediate_y); + + return 0; +} + +#define REGISTER_KERNEL_LAUNCHER(T) \ + template int computeWeightedAverageWirelengthCudaMergedLauncher( \ + const T *x, const T *y, \ + const int *flat_netpin, \ + const int *netpin_start, \ + const unsigned char *net_mask, \ + int num_nets, \ + const T *inv_gamma, \ + T *partial_wl_x, T *partial_wl_y, \ + T *grad_intermediate_x, T *grad_intermediate_y); \ + \ + template int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( \ + const T *x, const T *y, \ + const int *flat_netpin, \ + const int *netpin_start, \ + const unsigned char *net_mask, \ + int num_nets, \ + const T *inv_gamma, \ + const T *bbox_min_x, const T *bbox_min_y, \ + const T *bbox_max_x, const T *bbox_max_y, \ + T *partial_wl, \ + T *grad_intermediate_x, T *grad_intermediate_y); + +REGISTER_KERNEL_LAUNCHER(float); +REGISTER_KERNEL_LAUNCHER(double); + +DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp new file mode 100644 index 00000000..bfee207f --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp @@ -0,0 +1,269 @@ +#include "utility/src/torch.h" +#include "utility/src/utils.h" +#include "weighted_average_wirelength/src/functional.h" + +DREAMPLACE_BEGIN_NAMESPACE + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param x x location of pins. +/// @param y y location of pins. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_mask whether compute the wirelength for a net or not +/// @param net_weights weight of nets +/// @param num_nets number of nets. +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. +/// @param grad_tensor back-propagated gradient from previous stage. +/// @param grad_x_tensor gradient in x direction. +/// @param grad_y_tensor gradient in y direction. +/// @return 0 if successfully done. +template +int computeWeightedAverageWirelengthMergedLauncher( + const T *x, const T *y, + const int *flat_netpin, + const int *netpin_start, + const unsigned char *net_mask, + int num_nets, + const T *inv_gamma, + //T *partial_wl, + T *partial_wl_x, + T *partial_wl_y, + T *grad_intermediate_x, T *grad_intermediate_y, + int num_threads) +{ + int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); +#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) + for (int i = 0; i < num_nets; ++i) + { + if (net_mask[i]) + { + // int degree = netpin_start[ii+1]-netpin_start[ii]; + T x_max = -std::numeric_limits::max(); + T x_min = std::numeric_limits::max(); + T y_max = -std::numeric_limits::max(); + T y_min = std::numeric_limits::max(); + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); + x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); + T yy = y[flat_netpin[j]]; + y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); + y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); + } + + T xexp_x_sum = 0; + T xexp_nx_sum = 0; + T exp_x_sum = 0; + T exp_nx_sum = 0; + + T yexp_y_sum = 0; + T yexp_ny_sum = 0; + T exp_y_sum = 0; + T exp_ny_sum = 0; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + xexp_x_sum += xx * exp_x; + xexp_nx_sum += xx * exp_nx; + exp_x_sum += exp_x; + exp_nx_sum += exp_nx; + + T yy = y[flat_netpin[j]]; + T exp_y = exp((yy - y_max) * (*inv_gamma)); + T exp_ny = exp((y_min - yy) * (*inv_gamma)); + + yexp_y_sum += yy * exp_y; + yexp_ny_sum += yy * exp_ny; + exp_y_sum += exp_y; + exp_ny_sum += exp_ny; + } + + //partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum + + // yexp_y_sum / exp_y_sum - yexp_ny_sum / exp_ny_sum; + partial_wl_x[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; + partial_wl_y[i] = yexp_y_sum / exp_y_sum - yexp_ny_sum / exp_ny_sum; + + T b_x = (*inv_gamma) / (exp_x_sum); + T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; + T b_nx = -(*inv_gamma) / (exp_nx_sum); + T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; + + T b_y = (*inv_gamma) / (exp_y_sum); + T a_y = (1.0 - b_y * yexp_y_sum) / exp_y_sum; + T b_ny = -(*inv_gamma) / (exp_ny_sum); + T a_ny = (1.0 - b_ny * yexp_ny_sum) / exp_ny_sum; + + for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) + { + T xx = x[flat_netpin[j]]; + T exp_x = exp((xx - x_max) * (*inv_gamma)); + T exp_nx = exp((x_min - xx) * (*inv_gamma)); + + grad_intermediate_x[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; + + T yy = y[flat_netpin[j]]; + T exp_y = exp((yy - y_max) * (*inv_gamma)); + T exp_ny = exp((y_min - yy) * (*inv_gamma)); + + grad_intermediate_y[flat_netpin[j]] = (a_y + b_y * yy) * exp_y - (a_ny + b_ny * yy) * exp_ny; + } + } + } + + return 0; +} + +#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") +#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") +#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") + +/// @brief Compute weighted average wirelength and gradient. +/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), +/// where x_i is pin location. +/// +/// @param pos location of pins, x array followed by y array. +/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. +/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. +/// @param net_weights weight of nets +/// @param net_mask whether compute the wirelength for a net or not +/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. +/// @return total wirelength cost. +std::vector weighted_average_wirelength_forward( + at::Tensor pos, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + at::Tensor inv_gamma, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_weights_x); + CHECK_CONTIGUOUS(net_weights_x); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + + int num_nets = netpin_start.numel() - 1; + int num_pins = pos.numel() / 2; + + // x, y interleave + //at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); + at::Tensor partial_wl_x = at::zeros({num_nets}, pos.options()); + at::Tensor partial_wl_y = at::zeros({num_nets}, pos.options()); + // timed with grad_in yet + at::Tensor grad_intermediate = at::zeros_like(pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthMergedLauncher", [&] { + computeWeightedAverageWirelengthMergedLauncher( + DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + num_nets, + DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), + //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins, + num_threads); + if (net_weights.numel()) + { + //partial_wl.mul_(net_weights); + partial_wl_x.mul_(net_weights_x); + partial_wl_y.mul_(net_weights); + } + }); + + //auto wl = partial_wl.sum(); + auto wl = partial_wl_x.sum() + partial_wl_y.sum(); + //at::Tensor wl = at::zeros(1, pos.options()); + return {wl, grad_intermediate}; +} + +/// @brief Compute gradient +/// @param grad_pos input gradient from backward propagation +/// @param pos locations of pins +/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) +/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 +/// @param net_weights weight of nets +/// @param net_mask an array to record whether compute the where for a net or not +/// @param inv_gamma a scalar tensor for the parameter in the equation +at::Tensor weighted_average_wirelength_backward( + at::Tensor grad_pos, + at::Tensor pos, + at::Tensor grad_intermediate, + at::Tensor flat_netpin, + at::Tensor netpin_start, + at::Tensor pin2net_map, + at::Tensor net_weights, + at::Tensor net_weights_x, + at::Tensor net_mask, + at::Tensor inv_gamma, + int num_threads) +{ + CHECK_FLAT(pos); + CHECK_EVEN(pos); + CHECK_CONTIGUOUS(pos); + CHECK_FLAT(flat_netpin); + CHECK_CONTIGUOUS(flat_netpin); + CHECK_FLAT(netpin_start); + CHECK_CONTIGUOUS(netpin_start); + CHECK_FLAT(net_weights); + CHECK_CONTIGUOUS(net_weights); + CHECK_FLAT(net_weights_x); + CHECK_CONTIGUOUS(net_weights_x); + CHECK_FLAT(net_mask); + CHECK_CONTIGUOUS(net_mask); + CHECK_FLAT(pin2net_map); + CHECK_CONTIGUOUS(pin2net_map); + CHECK_FLAT(grad_intermediate); + CHECK_EVEN(grad_intermediate); + CHECK_CONTIGUOUS(grad_intermediate); + + at::Tensor grad_out = grad_intermediate.mul_(grad_pos); + + DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthMergedLauncher", [&] { + if (net_weights.numel()) + { + integrateNetWeightsLauncher( + DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), + DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), + DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), + DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), + DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, + netpin_start.numel() - 1, + num_threads); + } + }); + return grad_out; +} + +DREAMPLACE_END_NAMESPACE + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward"); + m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward"); +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py new file mode 100644 index 00000000..5fdaa133 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py @@ -0,0 +1,352 @@ +## +# @file weighted_average_wirelength.py +# @author Yibo Lin (DREAMPlace) +# @date Jun 2018 +# @brief Compute weighted-average wirelength according to e-place +# + +import time +import torch +from torch import nn +from torch.autograd import Function +import logging + +import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp as weighted_average_wirelength_cpp +import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp_atomic as weighted_average_wirelength_cpp_atomic +import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp_merged as weighted_average_wirelength_cpp_merged +import dreamplacefpga.configure as configure +if configure.compile_configurations["CUDA_FOUND"] == "TRUE": + import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda as weighted_average_wirelength_cuda + import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda_atomic as weighted_average_wirelength_cuda_atomic + import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda_merged as weighted_average_wirelength_cuda_merged +import pdb + +logger = logging.getLogger(__name__) + +class WeightedAverageWirelengthFunction(Function): + """ + @brief compute weighted average wirelength. + """ + @staticmethod + def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, num_threads): + """ + @param pos pin location (x array, y array), not cell location + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore + @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute + @param inv_gamma 1/gamma, the larger, the closer to HPWL + """ + tt = time.time() + if pos.is_cuda: + output = weighted_average_wirelength_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, inv_gamma) + else: + output = weighted_average_wirelength_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) + ctx.num_threads = num_threads + ctx.flat_netpin = flat_netpin + ctx.netpin_start = netpin_start + ctx.pin2net_map = pin2net_map + ctx.net_weights = net_weights + ctx.net_weights_x = net_weights_x + ctx.net_mask = net_mask + ctx.pin_mask = pin_mask + ctx.inv_gamma = inv_gamma + ctx.pos = pos + ctx.exp_xy = output[1] + ctx.exp_nxy = output[2] + ctx.exp_xy_sum = output[3] + ctx.exp_nxy_sum = output[4] + ctx.xyexp_xy_sum = output[5] + ctx.xyexp_nxy_sum = output[6] + + if pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + tt = time.time() + if grad_pos.is_cuda: + output = weighted_average_wirelength_cuda.backward( + grad_pos, + ctx.pos, + ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), + ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), + ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_mask, + ctx.inv_gamma + ) + else: + output = weighted_average_wirelength_cpp.backward( + grad_pos, + ctx.pos, + ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), + ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), + ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_weights_x, + ctx.net_mask, + ctx.inv_gamma, + ctx.num_threads + ) + output[:output.numel()//2].masked_fill_(ctx.pin_mask, 0.0) + output[output.numel()//2:].masked_fill_(ctx.pin_mask, 0.0) + if grad_pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) + return output, None, None, None, None, None, None, None, None + +class WeightedAverageWirelengthAtomicFunction(Function): + """ + @brief compute weighted average wirelength. + """ + @staticmethod + def forward(ctx, pos, pin2net_map, flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, num_threads): + """ + @param pos pin location (x array, y array), not cell location + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength + @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute + @param inv_gamma 1/gamma, the larger, the closer to HPWL + """ + tt = time.time() + if pos.is_cuda: + output = weighted_average_wirelength_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, flat_netpin, netpin_start, net_weights, net_mask, inv_gamma) + else: + output = weighted_average_wirelength_cpp_atomic.forward(pos.view(pos.numel()), pin2net_map, flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) + ctx.num_threads = num_threads + ctx.pin2net_map = pin2net_map + ctx.flat_netpin = flat_netpin + ctx.netpin_start = netpin_start + ctx.net_weights = net_weights + ctx.net_weights_x = net_weights_x + ctx.net_mask = net_mask + ctx.pin_mask = pin_mask + ctx.inv_gamma = inv_gamma + ctx.exp_xy = output[1] + ctx.exp_nxy = output[2] + ctx.exp_xy_sum = output[3] + ctx.exp_nxy_sum = output[4] + ctx.xyexp_xy_sum = output[5] + ctx.xyexp_nxy_sum = output[6] + ctx.pos = pos + #if torch.isnan(ctx.exp_xy).any() or torch.isnan(ctx.exp_nxy).any() or torch.isnan(ctx.exp_xy_sum).any() or torch.isnan(ctx.exp_nxy_sum).any() or torch.isnan(output[0]).any(): + # pdb.set_trace() + if pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + tt = time.time() + if grad_pos.is_cuda: + output = weighted_average_wirelength_cuda_atomic.backward( + grad_pos, + ctx.pos, + ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), + ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), + ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), + ctx.pin2net_map, + ctx.flat_netpin, + ctx.netpin_start, + ctx.net_weights, + ctx.net_mask, + ctx.inv_gamma + ) + else: + output = weighted_average_wirelength_cpp_atomic.backward( + grad_pos, + ctx.pos, + ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), + ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), + ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), + ctx.pin2net_map, + ctx.flat_netpin, + ctx.netpin_start, + ctx.net_weights, + ctx.net_weights_x, + ctx.net_mask, + ctx.inv_gamma, + ctx.num_threads + ) + output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) + output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) + if grad_pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) + return output, None, None, None, None, None, None + +class WeightedAverageWirelengthMergedFunction(Function): + """ + @brief compute weighted average wirelength. + """ + @staticmethod + def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, net_bounding_box_min, net_bounding_box_max, num_threads): + """ + @param pos pin location (x array, y array), not cell location + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength + @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute + @param inv_gamma 1/gamma, the larger, the closer to HPWL + """ + tt = time.time() + if pos.is_cuda: + output = weighted_average_wirelength_cuda_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, inv_gamma) + #output = weighted_average_wirelength_cuda_merged.forward_fpga(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, inv_gamma, net_bounding_box_min, net_bounding_box_max) + else: + output = weighted_average_wirelength_cpp_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) + ctx.num_threads = num_threads + ctx.pin2net_map = pin2net_map + ctx.flat_netpin = flat_netpin + ctx.netpin_start = netpin_start + ctx.net_weights = net_weights + ctx.net_weights_x = net_weights_x + ctx.net_mask = net_mask + ctx.pin_mask = pin_mask + ctx.inv_gamma = inv_gamma + ctx.grad_intermediate = output[1] + ctx.pos = pos + if pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) + return output[0] + + @staticmethod + def backward(ctx, grad_pos): + tt = time.time() + if grad_pos.is_cuda: + output = weighted_average_wirelength_cuda_merged.backward( + grad_pos, + ctx.pos, + ctx.grad_intermediate, + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_weights_x, + ctx.net_mask, + ctx.inv_gamma + ) + else: + output = weighted_average_wirelength_cpp_merged.backward( + grad_pos, + ctx.pos, + ctx.grad_intermediate, + ctx.flat_netpin, + ctx.netpin_start, + ctx.pin2net_map, + ctx.net_weights, + ctx.net_weights_x, + ctx.net_mask, + ctx.inv_gamma, + ctx.num_threads + ) + output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) + output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) + if grad_pos.is_cuda: + torch.cuda.synchronize() + logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) + return output, None, None, None, None, None, None, None, None, None, None, None + +class WeightedAverageWirelength(nn.Module): + """ + @brief Compute weighted average wirelength. + CPU only supports net-by-net algorithm. + GPU supports three algorithms: net-by-net, atomic, merged. + Different parameters are required for different algorithms. + """ + def __init__(self, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, num_carry_chains=None, cc_net_weight=None, dir_net_weight=None, net_mask=None, pin_mask=None, gamma=None, net_bounding_box_min=None, net_bounding_box_max=None, num_threads=None, algorithm='atomic'): + """ + @brief initialization + @param flat_netpin flat netpin map, length of #pins + @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins + @param pin2net_map pin2net map + @param net_weights weight of nets + @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore + @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute + @param gamma the smaller, the closer to HPWL + @param algorithm must be net-by-net | atomic | merged + """ + super(WeightedAverageWirelength, self).__init__() + assert net_weights is not None \ + and net_mask is not None \ + and pin_mask is not None \ + and gamma is not None, "net_weights, net_mask, pin_mask, gamma are requried parameters" + if algorithm in ['net-by-net', 'merged']: + assert flat_netpin is not None and netpin_start is not None and pin2net_map is not None, "flat_netpin, netpin_start, pin2net_map are requried parameters for algorithm %s" % (algorithm) + elif algorithm == 'atomic': + assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" + + self.flat_netpin = flat_netpin + self.netpin_start = netpin_start + self.netpin_values = None + self.pin2net_map = pin2net_map + self.net_weights = net_weights + self.net_weights_x = net_weights.detach().clone() + self.num_carry_chains = num_carry_chains + self.cc_net_weight = cc_net_weight + self.dir_net_weight = dir_net_weight + self.net_mask = net_mask + self.pin_mask = pin_mask + self.gamma = gamma + self.net_bounding_box_min = net_bounding_box_min + self.net_bounding_box_max = net_bounding_box_max + self.algorithm = algorithm + self.num_threads = num_threads + + #Weight x direction 50% more than y for carry chains + if self.num_carry_chains > 0 and self.cc_net_weight > 1.0 and self.dir_net_weight: + self.net_weights_x[self.net_weights_x == cc_net_weight] = 1.5*cc_net_weight + + def forward(self, pos): + if self.algorithm == 'net-by-net': + return WeightedAverageWirelengthFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.pin2net_map, + self.net_weights, + self.net_weights_x, + self.net_mask, + self.pin_mask, + 1.0/self.gamma, # do not store inv_gamma as gamma is changing + self.num_threads + ) + elif self.algorithm == 'atomic': + return WeightedAverageWirelengthAtomicFunction.apply(pos, + self.pin2net_map, + self.flat_netpin, + self.netpin_start, + self.net_weights, + self.net_weights_x, + self.net_mask, + self.pin_mask, + 1.0/self.gamma, # do not store inv_gamma as gamma is changing + self.num_threads + ) + elif self.algorithm == 'merged': + return WeightedAverageWirelengthMergedFunction.apply(pos, + self.flat_netpin, + self.netpin_start, + self.pin2net_map, + self.net_weights, + self.net_weights_x, + self.net_mask, + self.pin_mask, + 1.0/self.gamma, # do not store inv_gamma as gamma is changing + self.net_bounding_box_min, + self.net_bounding_box_max, + self.num_threads + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json new file mode 100644 index 00000000..a26a03f9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json @@ -0,0 +1,255 @@ +{ +"scl_file" : { + "descripton" : "input .scl file", + "default" : "", + "required" : "required for Bookshelf" + }, +"instance_file" : { + "descripton" : "input .inst file", + "default" : "", + "required" : "required for Bookshelf" + }, +"pin_file" : { + "descripton" : "input .pin file", + "default" : "", + "required" : "required for Bookshelf" + }, +"net_file" : { + "descripton" : "input .net file", + "default" : "", + "required" : "required for Bookshelf" + }, +"routing_file" : { + "descripton" : "input .routingUtil file", + "default" : "", + "required" : "required for Bookshelf" + }, +"util_file" : { + "descripton" : "input .util file", + "default" : "", + "required" : "required for Bookshelf" + }, +"pickle_file" : { + "descripton" : "input .pklz file", + "default" : "", + "required" : "required for Bookshelf" + }, +"load_pickle" : { + "descripton" : "enable loading of pickle file", + "default" : 0 + }, +"aux_input" : { + "descripton" : "input .aux file", + "default" : "", + "required" : "required for Bookshelf" + }, +"gpu" : { + "descripton" : "enable gpu or not", + "default" : 1 + }, +"num_bins_x" : { + "descripton" : "number of bins in horizontal direction", + "default" : 512 + }, +"num_bins_y" : { + "descripton" : "number of bins in vertical direction", + "default" : 512 + }, +"global_place_stages" : { + "descripton" : "global placement configurations of each stage, a dictionary of {\"num_bins_x\", \"num_bins_y\", \"iteration\", \"learning_rate\", \"learning_rate_decay\", \"wirelength\", \"optimizer\", \"Llambda_density_weight_iteration\", \"Lsub_iteration\"}", + "default" : "", + "required" : "required" + }, +"target_density" : { + "descripton" : "target density", + "default" : 1.0 + }, +"density_weight" : { + "descripton" : "initial weight of density cost", + "default" : 8e-5 + }, +"random_seed" : { + "descripton" : "random seed", + "default" : 1000 + }, +"result_dir" : { + "descripton" : "result directory for output", + "default" : "results" + }, +"scale_factor" : { + "descripton" : "scale factor to avoid numerical overflow; 0.0 means not set", + "default" : 0.0 + }, +"ignore_net_degree" : { + "descripton" : "ignore net degree larger than some value", + "default" : 3000 + }, +"gp_noise_ratio" : { + "descripton" : "noise to initial positions for global placement", + "default" : 0.025 + }, +"enable_fillers" : { + "descripton" : "enable filler cells", + "default" : 1 + }, +"cc_net_weight" : { + "descripton" : "carry chain net weights", + "default" : 1.0 + }, +"dir_net_weight" : { + "descripton" : "directional carry chain net weights wt_x = 1.5*wt_y", + "default" : 0 + }, +"global_place_flag" : { + "descripton" : "whether to run global placement", + "default" : 1 + }, +"legalize_flag" : { + "descripton" : "whether to run packing-legalization", + "default" : 1 + }, +"stop_overflow" : { + "descripton" : "stopping criteria, consider stop when the overflow reaches to a ratio", + "default" : 0.1 + }, +"dtype" : { + "descripton" : "data type, float32 (or) float64", + "default" : "float32" + }, +"detailed_place_engine" : { + "descripton" : "external detailed placement engine to be called after placement", + "default" : "" + }, +"detailed_place_command" : { + "descripton" : "commands for external detailed placement engine", + "default" : "-nolegal -nodetail" + }, +"plot_flag" : { + "descripton" : "whether to plot solution or not (Increases runtime)", + "default" : 0 + }, +"RePlAce_ref_hpwl" : { + "descripton" : "reference HPWL used in RePlAce for updating density weight", + "default" : 350000 + }, +"RePlAce_LOWER_PCOF" : { + "descripton" : "lower bound ratio used in RePlAce for updating density weight", + "default" : 0.95 + }, +"RePlAce_UPPER_PCOF" : { + "descripton" : "upper bound ratio used in RePlAce for updating density weight", + "default" : 1.05 + }, +"gamma" : { + "descripton" : "elfPlace: The base wirelenGamma is * average bin size", + "default" : 5.0 + }, +"random_center_init_flag" : { + "descripton" : "whether perform random initialization around the center for global placement", + "default" : 1 + }, +"sort_nets_by_degree" : { + "descripton" : "whether sort nets by degree or not", + "default" : 0 + }, +"num_threads" : { + "descripton" : "number of CPU threads", + "default" : 8 + }, +"dump_global_place_solution_flag" : { + "descripton" : "whether dump intermediate global placement solution as a compressed pickle object", + "default" : 0 + }, +"dump_legalize_solution_flag" : { + "descripton" : "whether dump intermediate legalization solution as a compressed pickle object", + "default" : 0 + }, +"routability_opt_flag" : { + "descripton" : "whether enable routability optimization", + "default" : 0 + }, +"route_num_bins_x" : { + "descripton" : "number of routing grids/tiles", + "default" : 512 + }, +"route_num_bins_y" : { + "descripton" : "number of routing grids/tiles", + "default" : 512 + }, +"node_area_adjust_overflow" : { + "descripton" : "the overflow where to adjust node area", + "default" : 0.15 + }, +"max_num_area_adjust" : { + "descripton" : "maximum times to adjust node area", + "default" : 3 + }, +"adjust_resource_area_flag" : { + "descripton" : "Compute LUT/FF resource areas", + "default" : 1 + }, +"adjust_route_area_flag" : { + "descripton" : "whether use RUDY/RISA map to guide area adjustment", + "default" : 1 + }, +"adjust_pin_area_flag" : { + "descripton" : "whether use pin utilization map to guide area adjustment", + "default" : 1 + }, +"area_adjust_stop_ratio" : { + "descripton" : "area_adjust_stop_ratio", + "default" : 0.01 + }, +"route_area_adjust_stop_ratio" : { + "descripton" : "route_area_adjust_stop_ratio", + "default" : 0.01 + }, +"pin_area_adjust_stop_ratio" : { + "descripton" : "pin_area_adjust_stop_ratio", + "default" : 0.05 + }, +"unit_horizontal_capacity" : { + "descripton" : "number of horizontal routing tracks per unit distance for FPGA", + "default" : 209 + }, +"unit_vertical_capacity" : { + "descripton" : "number of vertical routing tracks per unit distance for FPGA", + "default" : 239 + }, +"unit_pin_capacity" : { + "descripton" : "number of pins per unit area for FPGA", + "default" : 50 + }, +"max_route_opt_adjust_rate" : { + "descripton" : "max_route_opt_adjust_rate", + "default" : 2.0 + }, +"route_opt_adjust_exponent" : { + "descripton" : "exponent to adjust the routing utilization map", + "default" : 2.0 + }, +"pin_stretch_ratio" : { + "descripton" : "pin_stretch_ratio", + "default" : 1.414213562 + }, +"max_pin_opt_adjust_rate" : { + "descripton" : "max_pin_opt_adjust_rate", + "default" : 1.5 + }, +"ffPinWeight" : { + "descripton" : "The weight of FF pins for pin density optimization", + "default" : 3.0 + }, +"deterministic_flag" : { + "descripton" : "Ensures reproducible run-to-run results (Slightly increases runtime)", + "default" : 1 + }, +"name_map_file_dump" : { + "descripton" : "Creates mapping to simplify long node and net names - Use for debug purposes", + "default" : 0 + }, +"enable_if" : { + "descripton" : "Enable IF Writer", + "default" : 0 + } +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json new file mode 100644 index 00000000..a26a03f9 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json @@ -0,0 +1,255 @@ +{ +"scl_file" : { + "descripton" : "input .scl file", + "default" : "", + "required" : "required for Bookshelf" + }, +"instance_file" : { + "descripton" : "input .inst file", + "default" : "", + "required" : "required for Bookshelf" + }, +"pin_file" : { + "descripton" : "input .pin file", + "default" : "", + "required" : "required for Bookshelf" + }, +"net_file" : { + "descripton" : "input .net file", + "default" : "", + "required" : "required for Bookshelf" + }, +"routing_file" : { + "descripton" : "input .routingUtil file", + "default" : "", + "required" : "required for Bookshelf" + }, +"util_file" : { + "descripton" : "input .util file", + "default" : "", + "required" : "required for Bookshelf" + }, +"pickle_file" : { + "descripton" : "input .pklz file", + "default" : "", + "required" : "required for Bookshelf" + }, +"load_pickle" : { + "descripton" : "enable loading of pickle file", + "default" : 0 + }, +"aux_input" : { + "descripton" : "input .aux file", + "default" : "", + "required" : "required for Bookshelf" + }, +"gpu" : { + "descripton" : "enable gpu or not", + "default" : 1 + }, +"num_bins_x" : { + "descripton" : "number of bins in horizontal direction", + "default" : 512 + }, +"num_bins_y" : { + "descripton" : "number of bins in vertical direction", + "default" : 512 + }, +"global_place_stages" : { + "descripton" : "global placement configurations of each stage, a dictionary of {\"num_bins_x\", \"num_bins_y\", \"iteration\", \"learning_rate\", \"learning_rate_decay\", \"wirelength\", \"optimizer\", \"Llambda_density_weight_iteration\", \"Lsub_iteration\"}", + "default" : "", + "required" : "required" + }, +"target_density" : { + "descripton" : "target density", + "default" : 1.0 + }, +"density_weight" : { + "descripton" : "initial weight of density cost", + "default" : 8e-5 + }, +"random_seed" : { + "descripton" : "random seed", + "default" : 1000 + }, +"result_dir" : { + "descripton" : "result directory for output", + "default" : "results" + }, +"scale_factor" : { + "descripton" : "scale factor to avoid numerical overflow; 0.0 means not set", + "default" : 0.0 + }, +"ignore_net_degree" : { + "descripton" : "ignore net degree larger than some value", + "default" : 3000 + }, +"gp_noise_ratio" : { + "descripton" : "noise to initial positions for global placement", + "default" : 0.025 + }, +"enable_fillers" : { + "descripton" : "enable filler cells", + "default" : 1 + }, +"cc_net_weight" : { + "descripton" : "carry chain net weights", + "default" : 1.0 + }, +"dir_net_weight" : { + "descripton" : "directional carry chain net weights wt_x = 1.5*wt_y", + "default" : 0 + }, +"global_place_flag" : { + "descripton" : "whether to run global placement", + "default" : 1 + }, +"legalize_flag" : { + "descripton" : "whether to run packing-legalization", + "default" : 1 + }, +"stop_overflow" : { + "descripton" : "stopping criteria, consider stop when the overflow reaches to a ratio", + "default" : 0.1 + }, +"dtype" : { + "descripton" : "data type, float32 (or) float64", + "default" : "float32" + }, +"detailed_place_engine" : { + "descripton" : "external detailed placement engine to be called after placement", + "default" : "" + }, +"detailed_place_command" : { + "descripton" : "commands for external detailed placement engine", + "default" : "-nolegal -nodetail" + }, +"plot_flag" : { + "descripton" : "whether to plot solution or not (Increases runtime)", + "default" : 0 + }, +"RePlAce_ref_hpwl" : { + "descripton" : "reference HPWL used in RePlAce for updating density weight", + "default" : 350000 + }, +"RePlAce_LOWER_PCOF" : { + "descripton" : "lower bound ratio used in RePlAce for updating density weight", + "default" : 0.95 + }, +"RePlAce_UPPER_PCOF" : { + "descripton" : "upper bound ratio used in RePlAce for updating density weight", + "default" : 1.05 + }, +"gamma" : { + "descripton" : "elfPlace: The base wirelenGamma is * average bin size", + "default" : 5.0 + }, +"random_center_init_flag" : { + "descripton" : "whether perform random initialization around the center for global placement", + "default" : 1 + }, +"sort_nets_by_degree" : { + "descripton" : "whether sort nets by degree or not", + "default" : 0 + }, +"num_threads" : { + "descripton" : "number of CPU threads", + "default" : 8 + }, +"dump_global_place_solution_flag" : { + "descripton" : "whether dump intermediate global placement solution as a compressed pickle object", + "default" : 0 + }, +"dump_legalize_solution_flag" : { + "descripton" : "whether dump intermediate legalization solution as a compressed pickle object", + "default" : 0 + }, +"routability_opt_flag" : { + "descripton" : "whether enable routability optimization", + "default" : 0 + }, +"route_num_bins_x" : { + "descripton" : "number of routing grids/tiles", + "default" : 512 + }, +"route_num_bins_y" : { + "descripton" : "number of routing grids/tiles", + "default" : 512 + }, +"node_area_adjust_overflow" : { + "descripton" : "the overflow where to adjust node area", + "default" : 0.15 + }, +"max_num_area_adjust" : { + "descripton" : "maximum times to adjust node area", + "default" : 3 + }, +"adjust_resource_area_flag" : { + "descripton" : "Compute LUT/FF resource areas", + "default" : 1 + }, +"adjust_route_area_flag" : { + "descripton" : "whether use RUDY/RISA map to guide area adjustment", + "default" : 1 + }, +"adjust_pin_area_flag" : { + "descripton" : "whether use pin utilization map to guide area adjustment", + "default" : 1 + }, +"area_adjust_stop_ratio" : { + "descripton" : "area_adjust_stop_ratio", + "default" : 0.01 + }, +"route_area_adjust_stop_ratio" : { + "descripton" : "route_area_adjust_stop_ratio", + "default" : 0.01 + }, +"pin_area_adjust_stop_ratio" : { + "descripton" : "pin_area_adjust_stop_ratio", + "default" : 0.05 + }, +"unit_horizontal_capacity" : { + "descripton" : "number of horizontal routing tracks per unit distance for FPGA", + "default" : 209 + }, +"unit_vertical_capacity" : { + "descripton" : "number of vertical routing tracks per unit distance for FPGA", + "default" : 239 + }, +"unit_pin_capacity" : { + "descripton" : "number of pins per unit area for FPGA", + "default" : 50 + }, +"max_route_opt_adjust_rate" : { + "descripton" : "max_route_opt_adjust_rate", + "default" : 2.0 + }, +"route_opt_adjust_exponent" : { + "descripton" : "exponent to adjust the routing utilization map", + "default" : 2.0 + }, +"pin_stretch_ratio" : { + "descripton" : "pin_stretch_ratio", + "default" : 1.414213562 + }, +"max_pin_opt_adjust_rate" : { + "descripton" : "max_pin_opt_adjust_rate", + "default" : 1.5 + }, +"ffPinWeight" : { + "descripton" : "The weight of FF pins for pin density optimization", + "default" : 3.0 + }, +"deterministic_flag" : { + "descripton" : "Ensures reproducible run-to-run results (Slightly increases runtime)", + "default" : 1 + }, +"name_map_file_dump" : { + "descripton" : "Creates mapping to simplify long node and net names - Use for debug purposes", + "default" : 0 + }, +"enable_if" : { + "descripton" : "Enable IF Writer", + "default" : 0 + } +} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt new file mode 100644 index 00000000..4aa41588 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt @@ -0,0 +1,10 @@ +pyunpack>=0.1.2 +patool>=1.12 +matplotlib>=2.2.2 +cairocffi>=0.9.0 +pkgconfig>=1.4.0 +setuptools>=39.1.0 +scipy>=1.1.0 +numpy>=1.15.4 +torch>=1.6.0 +pycapnp diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/result_log.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/result_log.txt new file mode 100644 index 00000000..3e2457ff --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/result_log.txt @@ -0,0 +1,16 @@ +Baseline Result: FPGAPlacementOptimization (fpga-example1) +========================================================= +Date: 2026-07-09 +Program: scripts/init.py (row-scan placer) + +Metrics: + combined_score: -210721.0 + hpwl: 210721.0 + valid: 1.0 + gate_site_type: 1.0 + gate_capacity: 1.0 + gate_carry_chain: 1.0 + runtime_s: 0.19 + +All three legality gates pass. +Total instances: 3336 (72 fixed, 3264 movable) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/solution.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/solution.py new file mode 100644 index 00000000..832fb3af --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/solution.py @@ -0,0 +1,43 @@ +"""FPGA Placement Optimization ? baseline reference solution. + +This is the reference implementation used for comparison. +It implements a simple row-scan placer (identical to scripts/init.py). + +The expected output for fpga-example1: + HPWL = 210721 + All three legality gates pass + combined_score = -210721 +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +_src_dir = Path(__file__).resolve().parent.parent / "scripts" +if str(_src_dir) not in sys.path: + sys.path.insert(0, str(_src_dir)) + + +def main() -> None: + # Import and delegate to the same logic as scripts/init.py + from pathlib import Path as _Path + + # Locate benchmark files + ref_dir = _Path(__file__).resolve().parent.parent / "references" + + # Run the same logic as the initial solver + import runpy + init_path = _src_dir / "init.py" + sys.argv = [ + str(init_path), + "--nodes", str(ref_dir / "design.nodes"), + "--pl", str(ref_dir / "design.pl"), + "--scl", str(ref_dir / "design.scl"), + "--output", "temp/baseline_solution.pl", + ] + runpy.run_path(str(init_path), run_name="__main__") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt new file mode 100644 index 00000000..67e36ccc --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(ops) + +file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") +install(FILES ${INSTALL_SRCS} DESTINATION unittest) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore new file mode 100644 index 00000000..20837254 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore @@ -0,0 +1,2 @@ +*.gds +*.png diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt new file mode 100644 index 00000000..cf922d08 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") +install( + FILES ${INSTALL_SRCS} DESTINATION unitest/ops + ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py new file mode 100644 index 00000000..e6a39d55 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py @@ -0,0 +1,194 @@ +## +# @file adjust_node_area_unitest.py +# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import unittest +import torch +import numpy as np +import pdb + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.adjust_node_area import adjust_node_area +sys.path.pop() + +class AdjustNodeAreaUnittest(unittest.TestCase): + def test_adjust_node_area(self): + dtype = torch.float32 + pos = torch.Tensor([[1, 10], [2, 20], [3, 30]]).to(dtype) + pin_offset_x = torch.Tensor([0.1, 0.2, 0.3, 0.4, 0.5]).to(dtype) + pin_offset_y = torch.Tensor([0.01, 0.02, 0.03, 0.04, 0.05]).to(dtype) + node_size_x = torch.Tensor([0.5, 0.5, 0.5]).to(dtype) + node_size_y = torch.Tensor([0.05, 0.05, 0.05]).to(dtype) + + flop_lut_indices = torch.Tensor([0, 1, 2]).to(torch.int32) + flop_lut_mask = torch.Tensor([True, True, True]) + flop_mask = torch.Tensor([True, False, False]) + lut_mask = torch.Tensor([False, True, True]) + filler_start_map = torch.Tensor([0, 1, 2]).to(torch.int32) + resource_areas = torch.Tensor([0.1,0.1,0.1,0.1,0.1]).to(dtype) + + node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + num_movable_nodes = len(node2pin_map) + num_filler_nodes = 2 + # assume no terminals + + num_pins = 0 + for pins in node2pin_map: + num_pins += len(pins) + pin2node_map = np.zeros(num_pins, dtype=np.int32) + for node_id, pins in enumerate(node2pin_map): + for pin in pins: + pin2node_map[pin] = node_id + + # construct flat_node2pin_map and flat_node2pin_start_map + flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) + # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins + flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(node2pin_map)): + flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] + flat_node2pin_start_map[i] = count + count += len(node2pin_map[i]) + flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) + + flat_node2pin_start_map = torch.from_numpy(flat_node2pin_start_map) + flat_node2pin_map = torch.from_numpy(flat_node2pin_map) + + xl, xh = 0, 8 + yl, yh = 0, 64 + route_num_bins_x, route_num_bins_y = 8, 8 + pin_num_bins_x, pin_num_bins_y = 16, 16 + + total_place_area = (xh - xl) * (yh - yl) + total_whitespace_area = (total_place_area - (node_size_x[:num_movable_nodes] * node_size_y[:num_movable_nodes]).sum()).item() + + route_utilization_map = torch.ones([route_num_bins_x, route_num_bins_y]).uniform_(0.5, 2) + pin_utilization_map = torch.ones([pin_num_bins_x, pin_num_bins_y]).uniform_(0.5, 2) + + area_adjust_stop_ratio = 0.01 + route_area_adjust_stop_ratio = 0.01 + pin_area_adjust_stop_ratio = 0.05 + unit_pin_capacity = 0.5 + pin_weights = None + + max_route_opt_adjust_rate = 3.0 + max_pin_opt_adjust_rate = 2.5 + route_opt_adjust_exponent = 2.5 + + target_density = torch.Tensor([0.9]) + + # test cpu + adjust_node_area_op = adjust_node_area.AdjustNodeArea( + flat_node2pin_map=flat_node2pin_map, + flat_node2pin_start_map=flat_node2pin_start_map, + pin_weights=pin_weights, + flop_lut_indices=flop_lut_indices, + flop_lut_mask=flop_lut_mask, + flop_mask=flop_mask, + lut_mask=lut_mask, + filler_start_map=filler_start_map, + xl=xl, + yl=yl, + xh=xh, + yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + route_num_bins_x=route_num_bins_x, + route_num_bins_y=route_num_bins_y, + pin_num_bins_x=pin_num_bins_x, + pin_num_bins_y=pin_num_bins_y, + total_place_area=total_place_area, + total_whitespace_area=total_whitespace_area, + max_route_opt_adjust_rate=max_route_opt_adjust_rate, + route_opt_adjust_exponent=route_opt_adjust_exponent, + max_pin_opt_adjust_rate=max_pin_opt_adjust_rate, + area_adjust_stop_ratio=area_adjust_stop_ratio, + route_area_adjust_stop_ratio=route_area_adjust_stop_ratio, + pin_area_adjust_stop_ratio=pin_area_adjust_stop_ratio, + unit_pin_capacity=unit_pin_capacity + ) + + pos_cpu = pos.clone().t().contiguous().view(-1) + node_size_x_cpu = node_size_x.clone() + node_size_y_cpu = node_size_y.clone() + pin_offset_x_cpu = pin_offset_x.clone() + pin_offset_y_cpu = pin_offset_y.clone() + resource_areas_cpu = resource_areas.clone() + flag1_cpu, flag2_cpu, flag3_cpu, flag4_cpu = adjust_node_area_op.forward( + pos_cpu, + node_size_x_cpu, + node_size_y_cpu, + pin_offset_x_cpu, + pin_offset_y_cpu, + target_density, + resource_areas_cpu, + route_utilization_map.clone(), + pin_utilization_map.clone()) + + if torch.cuda.device_count(): + adjust_node_area_op_cuda = adjust_node_area.AdjustNodeArea( + flat_node2pin_map=flat_node2pin_map.cuda(), + flat_node2pin_start_map=flat_node2pin_start_map.cuda(), + pin_weights=pin_weights, + flop_lut_indices=flop_lut_indices.cuda(), + flop_lut_mask=flop_lut_mask.cuda(), + flop_mask=flop_mask.cuda(), + lut_mask=lut_mask.cuda(), + filler_start_map=filler_start_map.cuda(), + xl=xl, + yl=yl, + xh=xh, + yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + route_num_bins_x=route_num_bins_x, + route_num_bins_y=route_num_bins_y, + pin_num_bins_x=pin_num_bins_x, + pin_num_bins_y=pin_num_bins_y, + total_place_area=total_place_area, + total_whitespace_area=total_whitespace_area, + max_route_opt_adjust_rate=max_route_opt_adjust_rate, + route_opt_adjust_exponent=route_opt_adjust_exponent, + max_pin_opt_adjust_rate=max_pin_opt_adjust_rate, + area_adjust_stop_ratio=area_adjust_stop_ratio, + route_area_adjust_stop_ratio=route_area_adjust_stop_ratio, + pin_area_adjust_stop_ratio=pin_area_adjust_stop_ratio, + unit_pin_capacity=unit_pin_capacity + ) + pos_cuda = pos.t().contiguous().view(-1).cuda() + node_size_x_cuda = node_size_x.cuda() + node_size_y_cuda = node_size_y.cuda() + pin_offset_x_cuda = pin_offset_x.cuda() + pin_offset_y_cuda = pin_offset_y.cuda() + resource_areas_cuda = resource_areas.cuda() + flag1_cuda, flag2_cuda, flag3_cuda, flag4_cuda = adjust_node_area_op_cuda.forward( + pos_cuda, + node_size_x_cuda, + node_size_y_cuda, + pin_offset_x_cuda, + pin_offset_y_cuda, + target_density.cuda(), + resource_areas_cuda, + route_utilization_map.cuda(), + pin_utilization_map.cuda()) + + assert (flag1_cpu == flag1_cuda) and \ + (flag2_cpu == flag2_cuda) and \ + (flag3_cpu == flag3_cuda) and \ + (flag4_cpu == flag4_cuda), "the flags via CPU and GPU are different" + + if flag1_cpu: + assert torch.allclose(pos_cuda.cpu(), pos_cpu) and \ + torch.allclose(node_size_x_cuda.cpu(), node_size_x_cpu) and \ + torch.allclose(node_size_y_cuda.cpu(), node_size_y_cpu) and \ + torch.allclose(pin_offset_x_cuda.cpu(), pin_offset_x_cpu) and \ + torch.allclose(pin_offset_y_cuda.cpu(), pin_offset_y_cpu), \ + "the results via CPU and GPU are different" + + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py new file mode 100644 index 00000000..0bcacd59 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py @@ -0,0 +1,121 @@ +## +# @file dct_electrical_potential_unitest.py +# @author Zixuan Jiang, Jiaqi Gu +# @date Mar 2019 +# @brief compare two different transforms to calculate the electric potential +# The fitst apporach is used in electric_potential_backup.py +# The second approach is used in electric_potential.py + +import torch +import os +import sys +import numpy as np +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.dct import dct, discrete_spectral_transform, dct2_fft2 +sys.path.pop() + + +def compare_different_methods(cuda_flag, M=1024, N=1024, dtype=torch.float64): + density_map = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0) + if cuda_flag: + density_map = density_map.cuda() + expkM = discrete_spectral_transform.get_expk(M, dtype, density_map.device) + expkN = discrete_spectral_transform.get_expk(N, dtype, density_map.device) + exact_expkM = discrete_spectral_transform.get_exact_expk(M, dtype, density_map.device) + exact_expkN = discrete_spectral_transform.get_exact_expk(N, dtype, density_map.device) + print("M = {}, N = {}".format(M, N)) + + wu = torch.arange(M, dtype=density_map.dtype, device=density_map.device).mul(2 * np.pi / M).view([M, 1]) + wv = torch.arange(N, dtype=density_map.dtype, device=density_map.device).mul(2 * np.pi / N).view([1, N]) + wu2_plus_wv2 = wu.pow(2) + wv.pow(2) + wu2_plus_wv2[0, 0] = 1.0 # avoid zero-division, it will be zeroed out + + inv_wu2_plus_wv2_2X = 2.0 / wu2_plus_wv2 + inv_wu2_plus_wv2_2X[0, 0] = 0.0 + wu_by_wu2_plus_wv2_2X = wu.mul(inv_wu2_plus_wv2_2X) + wv_by_wu2_plus_wv2_2X = wv.mul(inv_wu2_plus_wv2_2X) + + # the first approach is used as the ground truth + auv_golden = dct.dct2(density_map, expk0=expkM, expk1=expkN) + auv = auv_golden.clone() + auv[0, :].mul_(0.5) + auv[:, 0].mul_(0.5) + auv_by_wu2_plus_wv2_wu = auv.mul(wu_by_wu2_plus_wv2_2X) + auv_by_wu2_plus_wv2_wv = auv.mul(wv_by_wu2_plus_wv2_2X) + field_map_x_golden = dct.idsct2(auv_by_wu2_plus_wv2_wu, expkM, expkN) + field_map_y_golden = dct.idcst2(auv_by_wu2_plus_wv2_wv, expkM, expkN) + # compute potential phi + # auv / (wu**2 + wv**2) + auv_by_wu2_plus_wv2 = auv.mul(inv_wu2_plus_wv2_2X).mul_(2) + #potential_map = discrete_spectral_transform.idcct2(auv_by_wu2_plus_wv2, expkM, expkN) + potential_map_golden = dct.idcct2(auv_by_wu2_plus_wv2, expkM, expkN) + # compute energy + energy_golden = potential_map_golden.mul(density_map).sum() + + if density_map.is_cuda: + torch.cuda.synchronize() + + # the second approach uses the idxst_idct and idct_idxst + dct2 = dct2_fft2.DCT2(exact_expkM, exact_expkN) + idct2 = dct2_fft2.IDCT2(exact_expkM, exact_expkN) + idct_idxst = dct2_fft2.IDCT_IDXST(exact_expkM, exact_expkN) + idxst_idct = dct2_fft2.IDXST_IDCT(exact_expkM, exact_expkN) + + inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 + inv_wu2_plus_wv2[0, 0] = 0.0 + wu_by_wu2_plus_wv2_half = wu.mul(inv_wu2_plus_wv2).mul_(0.5) + wv_by_wu2_plus_wv2_half = wv.mul(inv_wu2_plus_wv2).mul_(0.5) + + buv = dct2.forward(density_map) + + buv_by_wu2_plus_wv2_wu = buv.mul(wu_by_wu2_plus_wv2_half) + buv_by_wu2_plus_wv2_wv = buv.mul(wv_by_wu2_plus_wv2_half) + field_map_x = idxst_idct.forward(buv_by_wu2_plus_wv2_wu) + field_map_y = idct_idxst.forward(buv_by_wu2_plus_wv2_wv) + buv_by_wu2_plus_wv2 = buv.mul(inv_wu2_plus_wv2) + potential_map = idct2.forward(buv_by_wu2_plus_wv2) + energy = potential_map.mul(density_map).sum() + + if density_map.is_cuda: + torch.cuda.synchronize() + + # compare results + np.testing.assert_allclose(buv.data.cpu().numpy(), auv_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(field_map_x.data.cpu().numpy(), field_map_x_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(field_map_y.data.cpu().numpy(), field_map_y_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(potential_map.data.cpu().numpy(), potential_map_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(energy.data.cpu().numpy(), energy_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + + # the third approach uses the dct.idxst_idct and dct.idxst_idct + dct2 = dct.DCT2(expkM, expkN) + idct2 = dct.IDCT2(expkM, expkN) + idct_idxst = dct.IDCT_IDXST(expkM, expkN) + idxst_idct = dct.IDXST_IDCT(expkM, expkN) + + cuv = dct2.forward(density_map) + + cuv_by_wu2_plus_wv2_wu = cuv.mul(wu_by_wu2_plus_wv2_half) + cuv_by_wu2_plus_wv2_wv = cuv.mul(wv_by_wu2_plus_wv2_half) + field_map_x = idxst_idct.forward(cuv_by_wu2_plus_wv2_wu) + field_map_y = idct_idxst.forward(cuv_by_wu2_plus_wv2_wv) + cuv_by_wu2_plus_wv2 = cuv.mul(inv_wu2_plus_wv2) + potential_map = idct2.forward(cuv_by_wu2_plus_wv2) + energy = potential_map.mul(density_map).sum() + + if density_map.is_cuda: + torch.cuda.synchronize() + + # compare results + np.testing.assert_allclose(cuv.data.cpu().numpy(), auv_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(field_map_x.data.cpu().numpy(), field_map_x_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(field_map_y.data.cpu().numpy(), field_map_y_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(potential_map.data.cpu().numpy(), potential_map_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + np.testing.assert_allclose(energy.data.cpu().numpy(), energy_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) + + +if __name__ == "__main__": + compare_different_methods(cuda_flag=False, M=1024, N=1024, dtype=torch.float64) + print("Verified different methods in CPU") + if torch.cuda.device_count(): + compare_different_methods(cuda_flag=True, M=1024, N=1024, dtype=torch.float64) + print("Verified different methods in CUDA") diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py new file mode 100644 index 00000000..22812f32 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py @@ -0,0 +1,1089 @@ +## +# @file dct_unitest.py +# @author Yibo Lin +# @date Mar 2019 +# + +import pdb +import os +import sys +import numpy as np +import unittest + +import torch +from torch.autograd import Function, Variable +import time +import scipy +from scipy import fftpack + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.dct import dct2_fft2 +from dreamplacefpga.ops.dct import discrete_spectral_transform +from dreamplacefpga.ops.dct import dct_lee +from dreamplacefpga.ops.dct import dct +sys.path.pop() + +dtype = torch.float32 + + +class DCTOpTest(unittest.TestCase): + def test_dctRandom(self): + N = 4 + x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) + #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) + + golden_value = discrete_spectral_transform.dct_2N(x).data.numpy() + print("golden_value") + print(golden_value) + + # test cpu using N-FFT + # pdb.set_trace() + custom = dct.DCT(algorithm='N') + dct_value = custom.forward(x) + print("dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using 2N-FFT + # pdb.set_trace() + custom = dct.DCT(algorithm='2N') + dct_value = custom.forward(x) + print("dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using dct_lee + # pdb.set_trace() + custom = dct_lee.DCT() + dct_value = custom.forward(x) + print("dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.DCT(algorithm='N') + dct_value = custom.forward(x.cuda()).cpu() + print("dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct.DCT(algorithm='2N') + dct_value = custom.forward(x.cuda()).cpu() + print("dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct_lee.DCT() + dct_value = custom.forward(x.cuda()).cpu() + print("dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + #golden_value = discrete_spectral_transform.dct2_2N(x).data.numpy() + #print("2D golden_value") + # print(golden_value) + + #custom = dct.DCT() + #dct2_value = custom.forward(dct_value.cuda().t().contiguous()).cpu() + #dct2_value = dct2_value.t().contiguous() + #print("dct2_value cuda") + # print(dct2_value.data.numpy()) + + #np.testing.assert_allclose(dct2_value.data.numpy(), golden_value) + + def test_idctRandom(self): + N = 4 + x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) + #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) + print("x") + print(x) + + y = discrete_spectral_transform.dct_N(x) + print("y") + print(y.data.numpy()) + + golden_value = discrete_spectral_transform.idct_2N(y).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu use N-FFT + # pdb.set_trace() + custom = dct.IDCT(algorithm='N') + dct_value = custom.forward(y) + print("idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + # test cpu use 2N-FFT + # pdb.set_trace() + custom = dct.IDCT(algorithm='2N') + dct_value = custom.forward(y) + print("idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + # test cpu use dct_lee + # pdb.set_trace() + custom = dct_lee.IDCT() + dct_value = custom.forward(y) + print("idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDCT(algorithm='N') + dct_value = custom.forward(y.cuda()).cpu() + print("idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + # test gpu + custom = dct.IDCT(algorithm='2N') + dct_value = custom.forward(y.cuda()).cpu() + print("idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + # test gpu + custom = dct_lee.IDCT() + dct_value = custom.forward(y.cuda()).cpu() + print("idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) + + def test_dct2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0) + expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) + expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) + + golden_value = discrete_spectral_transform.dct2_N(x).data.numpy() + print("2D DCT golden_value") + print(golden_value) + + # test cpu using N-FFT + # pdb.set_trace() + custom = dct.DCT2(algorithm='N') + dct_value = custom.forward(x) + print("2D dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using 2N-FFT + # pdb.set_trace() + custom = dct.DCT2(algorithm='2N') + dct_value = custom.forward(x) + print("2D dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using dct_lee + # pdb.set_trace() + custom = dct_lee.DCT2() + dct_value = custom.forward(x) + print("2D dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using fft2 + custom = dct2_fft2.DCT2(expkM, expkN) + dct_value = custom.forward(x) + print("2D dct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.DCT2(algorithm='N') + dct_value = custom.forward(x.cuda()).cpu() + print("2D dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct.DCT2(algorithm='2N') + dct_value = custom.forward(x.cuda()).cpu() + print("2D dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct_lee.DCT2() + dct_value = custom.forward(x.cuda()).cpu() + print("2D dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu using fft2 + custom = dct2_fft2.DCT2(expkM.cuda(), expkN.cuda()) + dct_value = custom.forward(x.cuda()).cpu() + print("2D dct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + def test_idct2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) + expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) + + y = discrete_spectral_transform.dct2_2N(x) + + golden_value = discrete_spectral_transform.idct2_2N(y).data.numpy() + print("2D idct golden_value") + print(golden_value) + + # test cpu using N-FFT + # pdb.set_trace() + custom = dct.IDCT2(algorithm='N') + dct_value = custom.forward(y) + print("2D idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using 2N-FFT + # pdb.set_trace() + custom = dct.IDCT2(algorithm='2N') + dct_value = custom.forward(y) + print("2D idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using dct_lee + # pdb.set_trace() + custom = dct_lee.IDCT2() + dct_value = custom.forward(y) + print("2D idct_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test cpu using fft2 + custom = dct2_fft2.IDCT2(expkM, expkN) + dct_value = custom.forward(y) + print("2D idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDCT2(algorithm='N') + dct_value = custom.forward(y.cuda()).cpu() + print("2D idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct.IDCT2(algorithm='2N') + dct_value = custom.forward(y.cuda()).cpu() + print("2D idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu + custom = dct_lee.IDCT2() + dct_value = custom.forward(y.cuda()).cpu() + print("2D idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + # test gpu using ifft2 + custom = dct2_fft2.IDCT2(expkM.cuda(), expkN.cuda()) + dct_value = custom.forward(y.cuda()).cpu() + print("2D idct_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) + + def test_idxct2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + golden_value = discrete_spectral_transform.idxt(x, 0).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDXCT() + dct_value = custom.forward(x) + print("dxt_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDXCT() + dct_value = custom.forward(x) + print("dxt_value") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDXCT() + dct_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) + + # test gpu + custom = dct_lee.IDXCT() + dct_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dct_value.data.numpy()) + + np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) + + +class DSTOpTest(unittest.TestCase): + def test_dstRandom(self): + N = 4 + x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) + #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) + import scipy + from scipy import fftpack + + #golden_value = discrete_spectral_transform.dst(x).data.numpy() + golden_value = torch.from_numpy(fftpack.dst(x.data.numpy())).data.numpy() / N + print("golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.DST() + dst_value = custom.forward(x) + print("dst_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + # test cpu + # pdb.set_trace() + custom = dct_lee.DST() + dst_value = custom.forward(x) + print("dst_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.DST() + dst_value = custom.forward(x.cuda()).cpu() + print("dst_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + # test gpu + custom = dct_lee.DST() + dst_value = custom.forward(x.cuda()).cpu() + print("dst_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + def test_idstRandom(self): + N = 4 + x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) + #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) + print("x") + print(x) + import scipy + from scipy import fftpack + + #y = discrete_spectral_transform.dst(x) + y = torch.from_numpy(fftpack.dst(x.data.numpy())) + print("y") + print(y.data.numpy()) + + #golden_value = discrete_spectral_transform.idst(y).data.numpy() + golden_value = torch.from_numpy(fftpack.idst(y.data.numpy())).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDST() + dst_value = custom.forward(y) + print("idst_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDST() + dst_value = custom.forward(y) + print("idst_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDST() + dst_value = custom.forward(y.cuda()).cpu() + print("idst_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + # test gpu + custom = dct_lee.IDST() + dst_value = custom.forward(y.cuda()).cpu() + print("idst_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) + + def test_idxst2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + golden_value = discrete_spectral_transform.idxt(x, 1).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDXST() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDXST() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDXST() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test gpu + custom = dct_lee.IDXST() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + +class DXTOpTest(unittest.TestCase): + def test_idcct2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + golden_value = discrete_spectral_transform.idcct2(x).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDCCT2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDCCT2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDCCT2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test gpu + custom = dct_lee.IDCCT2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + def test_idcst2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + golden_value = discrete_spectral_transform.idcst2(x).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDCST2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDCST2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDCST2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test gpu + custom = dct_lee.IDCST2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + def test_idsct2Random(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + golden_value = discrete_spectral_transform.idsct2(x).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + # pdb.set_trace() + custom = dct.IDSCT2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test cpu + # pdb.set_trace() + custom = dct_lee.IDSCT2() + dst_value = custom.forward(x) + print("dxt_value") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDSCT2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + # test gpu + custom = dct_lee.IDSCT2() + dst_value = custom.forward(x.cuda()).cpu() + print("dxt_value cuda") + print(dst_value.data.numpy()) + + np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) + + def test_idct_idxstRandom(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) + expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) + + golden_value = discrete_spectral_transform.idct_idxst(x).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + custom = dct.IDCT_IDXST() + idct_idxst_value = custom.forward(x) + print("2D dct.idct_idxst") + print(idct_idxst_value.data.numpy()) + + np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) + + # test gpu + custom = dct2_fft2.IDCT_IDXST(expkM, expkN) + idct_idxst_value = custom.forward(x) + print("2D dct2_fft2.idct_idxst cuda") + print(idct_idxst_value.data.numpy()) + + # note the scale factor + np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDCT_IDXST() + idct_idxst_value = custom.forward(x.cuda()).cpu() + print("2D dct.idct_idxst cuda") + print(idct_idxst_value.data.numpy()) + + np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) + + # test gpu + custom = dct2_fft2.IDCT_IDXST(expkM.cuda(), expkN.cuda()) + idct_idxst_value = custom.forward(x.cuda()).cpu() + print("2D dct2_fft2.idct_idxst cuda") + print(idct_idxst_value.data.numpy()) + + # note the scale factor + np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) + + def test_idxst_idctRandom(self): + torch.manual_seed(10) + M = 4 + N = 8 + x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() + print("2D x") + print(x) + + expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) + expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) + + golden_value = discrete_spectral_transform.idxst_idct(x).data.numpy() + print("2D golden_value") + print(golden_value) + + # test cpu + custom = dct.IDXST_IDCT() + idxst_idct_value = custom.forward(x) + print("2D dct.idxst_idct") + print(idxst_idct_value.data.numpy()) + + np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value * 2, atol=1e-14) + + # test cpu + custom = dct2_fft2.IDXST_IDCT(expkM, expkN) + idxst_idct_value = custom.forward(x) + print("2D dct2_fft2.idxst_idct cuda") + print(idxst_idct_value.data.numpy()) + + # note the scale factor + np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value* 2, atol=1e-14) + + if torch.cuda.device_count(): + # test gpu + custom = dct.IDXST_IDCT() + idxst_idct_value = custom.forward(x.cuda()).cpu() + print("2D dct.idxst_idct cuda") + print(idxst_idct_value.data.numpy()) + + np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value * 2, atol=1e-14) + + # test gpu + custom = dct2_fft2.IDXST_IDCT(expkM.cuda(), expkN.cuda()) + idxst_idct_value = custom.forward(x.cuda()).cpu() + print("2D dct2_fft2.idxst_idct cuda") + print(idxst_idct_value.data.numpy()) + + # note the scale factor + np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value* 2, atol=1e-14) + +def eval_torch_rfft1d(x, runs): + for i in range(100): + a = torch.rfft(x, signal_ndim=1, onesided=True) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + a = torch.rfft(x, signal_ndim=1, onesided=True) + torch.cuda.synchronize() + print("torch.rfft1d takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + b = torch.irfft(a, signal_ndim=1, onesided=True, signal_sizes=x.shape[1:]) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + b = torch.irfft(a, signal_ndim=1, onesided=True, signal_sizes=x.shape[1:]) + torch.cuda.synchronize() + print("torch.irfft1d takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_torch_rfft2d(x, runs): + for i in range(100): + a = torch.rfft(x, signal_ndim=2, onesided=True) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + a = torch.rfft(x, signal_ndim=2, onesided=True) + torch.cuda.synchronize() + print("torch.rfft2d takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + b = torch.irfft(a, signal_ndim=2, onesided=True, signal_sizes=x.shape) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + b = torch.irfft(a, signal_ndim=2, onesided=True, signal_sizes=x.shape) + torch.cuda.synchronize() + print("torch.irfft2d takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_dct2d(x, expk0, expk1, expkM, expkN, runs): + x_numpy = x.data.cpu().numpy() + torch.cuda.synchronize() + tt = time.time() + y = fftpack.dct(fftpack.dct(x_numpy.T, norm=None).T/x.size(1), norm=None)/x.size(0) + torch.cuda.synchronize() + print("CPU scipy.fftpack.dct2d takes %.7f ms" % ((time.time()-tt)*1000)) + + # 9s for 200 iterations 1024x1024 on GTX 1080 + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_2N = discrete_spectral_transform.dct2_2N(x, expk0=expk0, expk1=expk1) + torch.cuda.synchronize() + # print(prof) + print("PyTorch: dct2d_2N takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + # 11s for 200 iterations 1024x1024 on GTX 1080 + perm0 = discrete_spectral_transform.get_perm(x.size(-2), dtype=torch.int64, device=x.device) + perm1 = discrete_spectral_transform.get_perm(x.size(-1), dtype=torch.int64, device=x.device) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = discrete_spectral_transform.dct2_N(x, perm0=perm0, expk0=expk0, perm1=perm1, expk1=expk1) + torch.cuda.synchronize() + # print(prof) + print("PyTorch: dct2d_N takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct.DCT2(expk0, expk1, algorithm='2N') + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_2N = dct2func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("DCT2d_2N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct.DCT2(expk0, expk1, algorithm='N') + y_N = dct2func.forward(x) + torch.cuda.synchronize() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + tt = time.time() + for i in range(runs): + y_N = dct2func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("DCT2d_N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + # The implementation below only supports float64 by now + dct2func = dct_lee.DCT2(expk0, expk1) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = dct2func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("DCT2d_Lee Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct2_fft2.DCT2(expkM, expkN) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("DCT2_FFT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_idct2d(x, expk0, expk1, expkM, expkN, runs): + y_N = discrete_spectral_transform.idct2_N(x, expk0=expk0, expk1=expk1) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_N = discrete_spectral_transform.idct2_N(x, expk0=expk0, expk1=expk1) + torch.cuda.synchronize() + print("PyTorch idct2_N takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + idct2func = dct.IDCT2(expk0, expk1, algorithm='2N') + y_N = idct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_N = idct2func.forward(x) + torch.cuda.synchronize() + print("IDCT2_2N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + idct2func = dct.IDCT2(expk0, expk1, algorithm='N') + y_N = idct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = idct2func.forward(x)/x.size(0)/x.size(1)/4 + torch.cuda.synchronize() + # print(prof) + print("IDCT2_N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct2_fft2.IDCT2(expkM, expkN) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("IDCT2_FFT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_idxt2d(x, expk0, expk1, expkM, expkN, runs): + dct2func = dct.IDXST_IDCT(expk0, expk1) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("dct.IDXST_IDCT Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + y_N = discrete_spectral_transform.idxst_idct(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_N = discrete_spectral_transform.idxst_idct(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + print("PyTorch: idxst_idct takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct2_fft2.IDXST_IDCT(expkM, expkN) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("dct2_fft2.IDXST_IDCT takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct.IDCT_IDXST(expk0, expk1) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("dct.IDCT_IDXST takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + y_N = discrete_spectral_transform.idct_idxst(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_N = discrete_spectral_transform.idct_idxst(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + print("PyTorch: idct_idxst takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + dct2func = dct2_fft2.IDCT_IDXST(expkM, expkN) + y = dct2func.forward(x) + torch.cuda.synchronize() + tt = time.time() + for i in range(runs): + y_test = dct2func.forward(x) + torch.cuda.synchronize() + print("dct2_fft2.IDCT_IDXST takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_others(x, expk0, expk1, expkM, expkN, runs): + y_N = discrete_spectral_transform.idcct2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = discrete_spectral_transform.idcct2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + # print(prof) + print("idcct2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + func = dct.IDCCT2(expk0, expk1) + y_N = func.forward(x) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("IDCCT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + y_N = discrete_spectral_transform.idcst2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = discrete_spectral_transform.idcst2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + # print(prof) + print("idcst2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + func = dct.IDCST2(expk0, expk1) + y_N = func.forward(x) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("IDCST2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + y_N = discrete_spectral_transform.idsct2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = discrete_spectral_transform.idsct2(x, expk_0=expk0, expk_1=expk1) + torch.cuda.synchronize() + # print(prof) + print("idsct2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + func = dct.IDSCT2(expk0, expk1) + y_N = func.forward(x) + torch.cuda.synchronize() + tt = time.time() + # with torch.autograd.profiler.profile(use_cuda=True) as prof: + for i in range(runs): + y_N = func.forward(x) + torch.cuda.synchronize() + # print(prof) + print("IDSCT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) + + print("") + + +def eval_runtime(): + runs = 100 + + M = 1024 + N = 1024 + dtype = torch.float64 + x = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0).cuda() + + print("M = {}, N = {}".format(M, N)) + + # 2cos(), 2sin() + expk0 = discrete_spectral_transform.get_expk(M, dtype=x.dtype, device=x.device) + expk1 = discrete_spectral_transform.get_expk(N, dtype=x.dtype, device=x.device) + # cos(), -sin() + expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) + expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) + + eval_torch_rfft1d(x, runs) + eval_torch_rfft2d(x, runs) + eval_dct2d(x, expk0, expk1, expkM, expkN, runs) + eval_idct2d(x, expk0, expk1, expkM, expkN, runs) + eval_idxt2d(x, expk0, expk1, expkM, expkN, runs) + eval_others(x, expk0, expk1, expkM, expkN, runs) + + +if __name__ == '__main__': + torch.manual_seed(10) + np.random.seed(10) + + print("usage: python dct_unitest.py test|eval") + + if len(sys.argv) > 1 and sys.argv[1] == "eval": + eval_runtime() + else: + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py new file mode 100644 index 00000000..3d3d1faf --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py @@ -0,0 +1,210 @@ +## +# @file density_potential_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import numpy as np +import unittest + +import torch +from torch.autograd import Function, Variable +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.density_potential import density_potential +sys.path.pop() +import inspect +import pdb + +class DensityPotentialOpTest(unittest.TestCase): + def test_densityOverflowRandom(self): + dtype = np.float32 + xx = np.array([1.0, 2.0]).astype(dtype) + yy = np.array([3.0, 1.5]).astype(dtype) + node_size_x = np.array([0.5, 1.0]).astype(dtype) + node_size_y = np.array([1.0, 1.0]).astype(dtype) + #xx = np.array([2.0]).astype(dtype) + #yy = np.array([1.5]).astype(dtype) + #node_size_x = np.array([1.0]).astype(dtype) + #node_size_y = np.array([1.0]).astype(dtype) + num_nodes = len(xx) + + scale_factor = 1.0 + + xl = 0.0 + yl = 0.0 + xh = 5.0 + yh = 5.0 + bin_size_x = 1.0 + bin_size_y = 1.0 + target_density = 0.1 + num_bins_x = int(np.ceil((xh-xl)/bin_size_x)) + num_bins_y = int(np.ceil((yh-yl)/bin_size_y)) + + ax = (4 / (node_size_x + 2*bin_size_x) / (node_size_x + 4*bin_size_x)).astype(dtype) + bx = (2 / bin_size_x / (node_size_x + 4*bin_size_x)).astype(dtype) + ay = (4 / (node_size_y + 2*bin_size_y) / (node_size_y + 4*bin_size_y)).astype(dtype) + by = (2 / bin_size_y / (node_size_y + 4*bin_size_y)).astype(dtype) + + #cx = np.zeros(num_nodes) + #cy = np.zeros(num_nodes) + #for i in range(num_nodes): + # sum_potential = 0.0 + # count = 0 + # for dist in np.arange(-(node_size_x[i]/2+2*bin_size_x)+node_size_x[i]/2, node_size_x[i]/2+2*bin_size_x, bin_size_x): + # if np.absolute(dist) < node_size_x[i]/2+bin_size_x: + # print("dist1 = %g, add %g" % (dist, 1 - ax[i]*dist*dist)) + # sum_potential += 1 - ax[i]*dist*dist + # else: + # print("dist2 = %g, add %g" % (dist, bx[i]*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x))) + # print("dddd = %g" % (np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)) + # sum_potential += bx[i]*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x) + # count += 1 + # #if count > num_bins_x: + # # break + # print("sum_potential = ", sum_potential) + # cx[i] = node_size_x[i]/sum_potential + #for i in range(num_nodes): + # sum_potential = 0.0 + # count = 0 + # for dist in np.arange(-(node_size_y[i]/2+2*bin_size_y)+node_size_y[i]/2, node_size_y[i]/2+2*bin_size_y, bin_size_y): + # if np.absolute(dist) < node_size_y[i]/2+bin_size_y: + # sum_potential += 1 - ay[i]*dist*dist + # else: + # sum_potential += by[i]*(np.absolute(dist)-node_size_y[i]/2-2*bin_size_y)*(np.absolute(dist)-node_size_y[i]/2-2*bin_size_y) + # count += 1 + # #if count > num_bins_x: + # # break + # cy[i] = node_size_y[i]/sum_potential + #print("cx = ", cx) + #print("cy = ", cy) + + # bell shape overlap function + def npfx1(dist): + # ax will be broadcast from num_nodes*1 to num_nodes*num_bins_x + return 1.0-ax.reshape([num_nodes, 1])*np.square(dist) + def npfx2(dist): + # bx will be broadcast from num_nodes*1 to num_nodes*num_bins_x + return bx.reshape([num_nodes, 1])*np.square(dist-node_size_x/2-2*bin_size_x).reshape([num_nodes, 1]) + def npfy1(dist): + # ay will be broadcast from num_nodes*1 to num_nodes*num_bins_y + return 1.0-ay.reshape([num_nodes, 1])*np.square(dist) + def npfy2(dist): + # by will be broadcast from num_nodes*1 to num_nodes*num_bins_y + return by.reshape([num_nodes, 1])*np.square(dist-node_size_y/2-2*bin_size_y).reshape([num_nodes, 1]) + # should not use integral, but sum; basically sample 5 distances, -2wb, -wb, 0, wb, 2wb; the sum does not change much when shifting cells + integral_potential_x = npfx1(0) + 2*npfx1(bin_size_x) + 2*npfx2(2*bin_size_x) + print("integral_potential_x = ", integral_potential_x) + cx = (node_size_x.reshape([num_nodes, 1]) / integral_potential_x).reshape([num_nodes, 1]) + # should not use integral, but sum; basically sample 5 distances, -2wb, -wb, 0, wb, 2wb; the sum does not change much when shifting cells + integral_potential_y = npfy1(0) + 2*npfy1(bin_size_y) + 2*npfy2(2*bin_size_y) + cy = (node_size_y.reshape([num_nodes, 1]) / integral_potential_y).reshape([num_nodes, 1]) + + """ + return bin xl + """ + def bin_xl(id_x): + return xl+id_x*bin_size_x + + """ + return bin xh + """ + def bin_xh(id_x): + return min(bin_xl(id_x)+bin_size_x, xh) + + """ + return bin yl + """ + def bin_yl(id_y): + return yl+id_y*bin_size_y + + """ + return bin yh + """ + def bin_yh(id_y): + return min(bin_yl(id_y)+bin_size_y, yh) + + bin_center_x = np.zeros(num_bins_x, dtype=dtype) + for id_x in range(num_bins_x): + bin_center_x[id_x] = (bin_xl(id_x)+bin_xh(id_x))/2*scale_factor + + bin_center_y = np.zeros(num_bins_y, dtype=dtype) + for id_y in range(num_bins_y): + bin_center_y[id_y] = (bin_yl(id_y)+bin_yh(id_y))/2*scale_factor + + print("target_area = ", target_density*bin_size_x*bin_size_y) + + sigma = 0.25 + delta = 2.0 + + # test cpu + custom = density_potential.DensityPotential( + torch.tensor(node_size_x, requires_grad=False), + torch.tensor(node_size_y, requires_grad=False), + torch.tensor(ax, requires_grad=False), + torch.tensor(bx, requires_grad=False), + torch.tensor(cx, requires_grad=False), + torch.tensor(ay, requires_grad=False), + torch.tensor(by, requires_grad=False), + torch.tensor(cy, requires_grad=False), + torch.tensor(bin_center_x, requires_grad=False), + torch.tensor(bin_center_y, requires_grad=False), + target_density=torch.tensor(target_density, requires_grad=False), + xl=torch.tensor(xl, requires_grad=False), + yl=torch.tensor(yl, requires_grad=False), + xh=torch.tensor(xh, requires_grad=False), + yh=torch.tensor(yh, requires_grad=False), + bin_size_x=torch.tensor(bin_size_x, requires_grad=False), + bin_size_y=torch.tensor(bin_size_y, requires_grad=False), + num_movable_nodes=torch.tensor(num_nodes, requires_grad=False), + num_terminals=0, + num_filler_nodes=0, + padding=torch.tensor(0, dtype=torch.int32, requires_grad=False), + sigma=sigma, delta=delta, num_threads=1) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy])), requires_grad=True) + result = custom.forward(pos) + print("custom_result = ", result) + result.backward() + grad = pos.grad.clone() + print("custom_grad = ", grad) + + # test cuda + if torch.cuda.device_count(): + custom_cuda = density_potential.DensityPotential( + torch.tensor(node_size_x, requires_grad=False).cuda(), + torch.tensor(node_size_y, requires_grad=False).cuda(), + torch.tensor(ax, requires_grad=False).cuda(), + torch.tensor(bx, requires_grad=False).cuda(), + torch.tensor(cx, requires_grad=False).cuda(), + torch.tensor(ay, requires_grad=False).cuda(), + torch.tensor(by, requires_grad=False).cuda(), + torch.tensor(cy, requires_grad=False).cuda(), + torch.tensor(bin_center_x, requires_grad=False).cuda(), + torch.tensor(bin_center_y, requires_grad=False).cuda(), + target_density=torch.tensor(target_density, requires_grad=False).cuda(), + xl=torch.tensor(xl, requires_grad=False).cuda(), + yl=torch.tensor(yl, requires_grad=False).cuda(), + xh=torch.tensor(xh, requires_grad=False).cuda(), + yh=torch.tensor(yh, requires_grad=False).cuda(), + bin_size_x=torch.tensor(bin_size_x, requires_grad=False).cuda(), + bin_size_y=torch.tensor(bin_size_y, requires_grad=False).cuda(), + num_movable_nodes=torch.tensor(num_nodes, requires_grad=False).cuda(), + num_terminals=0, + num_filler_nodes=0, + padding=torch.tensor(0, dtype=torch.int32, requires_grad=False).cuda(), + sigma=sigma, delta=delta, num_threads=1) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy])).cuda(), requires_grad=True) + #pos.grad.zero_() + result_cuda = custom_cuda.forward(pos) + print("custom_result_cuda = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pos.grad.clone() + print("custom_grad_cuda = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result.detach().numpy(), result_cuda.data.cpu().detach().numpy()) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py new file mode 100644 index 00000000..12e986f1 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py @@ -0,0 +1,68 @@ +## +# @file draw_place_unitest.py +# @author Yibo Lin +# @date Mar 2019 +# + +import os +import sys +import time +import numpy as np +import unittest +import gzip +import pdb + +import torch +from torch.autograd import Function, Variable +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.draw_place import draw_place +sys.path.pop() + +class DrawPlaceOpTest(unittest.TestCase): + def test_drawPlaceRandom(self): + dtype = np.float32 + np.random.seed(100) + xx = np.array([1.0, 2.0]).astype(dtype) + yy = np.array([3.0, 1.5]).astype(dtype) + node_size_x = np.array([0.5, 1.0]).astype(dtype) + node_size_y = np.array([1.0, 1.0]).astype(dtype) + pin2node_map = np.array([0, 0, 1, 1]).astype(np.int32) + pin_offset_x = np.array([0.1, 0.3, 0.2, 0.6]).astype(dtype) + pin_offset_y = np.array([0.1, 0.8, 0.2, 0.6]).astype(dtype) + num_nodes = len(xx) + + xl = 1.0 + yl = 1.0 + xh = 5.0 + yh = 5.0 + bin_size_x = 2.0 + bin_size_y = 2.0 + site_width = 1.0 + row_height = 2.0 + num_bins_x = int(np.ceil((xh-xl)/bin_size_x)) + num_bins_y = int(np.ceil((yh-yl)/bin_size_y)) + num_movable_nodes = len(xx) + num_terminals = 0 + num_filler_nodes = 0 + + # test cpu + custom = draw_place.DrawPlaceFunction.forward( + torch.from_numpy(np.concatenate([xx, yy])), + torch.from_numpy(node_size_x), torch.from_numpy(node_size_y), + torch.from_numpy(pin_offset_x), torch.from_numpy(pin_offset_y), + torch.from_numpy(pin2node_map), + xl, yl, xh, yh, + site_width, row_height, + bin_size_x, bin_size_y, + num_movable_nodes, + num_filler_nodes, + "test.png" # png, jpg, eps, pdf + ) + print(custom) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py new file mode 100644 index 00000000..7473dac0 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py @@ -0,0 +1,326 @@ +## +# @file electric_potential_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import time +import numpy as np +import unittest +import logging +import random + +import torch +from torch.autograd import Function, Variable +import os +import sys +import gzip + +sys.path.append( + os.path.dirname(os.path.dirname(os.path.dirname( + os.path.abspath(__file__))))) +from dreamplacefpga.ops.dct import dct +from dreamplacefpga.ops.dct import discrete_spectral_transform +from dreamplacefpga.ops.electric_potential import electric_potential +sys.path.pop() + +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle +import inspect +import pdb +from scipy import fftpack + +import matplotlib +matplotlib.use('Agg') +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +class PlaceDB (object): + def __init__(self): + self.num_nodes = 0 + self.num_terminals = 0 + self.num_movable_nodes = 0 + self.num_filler_nodes = 0 + self.xWirelenWt = 1.0 + self.yWirelenWt = 1.0 + self.filler_start_map = [] + self.overflowInstDensityStretchRatio = [] + +class ElectricPotentialOpTest(unittest.TestCase): + def test_densityOverflowRandom(self): + dtype = np.float64 + + stretchRatio = np.sqrt(2.0) + ## Create a database + placedb = PlaceDB() + placedb.num_nodes = 84 + placedb.num_terminals = 1 + placedb.num_movable_nodes = placedb.num_nodes - placedb.num_terminals + placedb.filler_start_map = np.array([0, 0, 0, 0, 0, 0], dtype=np.int32) + placedb.overflowInstDensityStretchRatio = np.array([stretchRatio, stretchRatio, 1.0, 1.0, 0], dtype=dtype) + + xl = 0.0 + yl = 0.0 + xh = 100.0 + yh = 360.0 + + xx = np.array([random.randint(int(xl),int(xh)) for i in range(placedb.num_nodes)]).astype(dtype) + yy = np.array([random.randint(int(yl),int(yh)) for i in range(placedb.num_nodes)]).astype(dtype) + node2fence_region_map = np.array([0 for i in range(placedb.num_nodes)]).astype(np.int32) + #Keep last entry as fixed + xx[-1] = 0 + yy[-1] = 0 + node2fence_region_map[-1] = 4 + + node_size_x = np.array([ + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1.0 + ]).astype(dtype) + node_size_y = np.array([ + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1.0 + ]).astype(dtype) + + region_id = 0 + + num_bins_x = 256 + num_bins_y = 256 + bin_size_x = (xh - xl) / num_bins_x + bin_size_y = (yh - yl) / num_bins_y + initial_density_map = np.zeros([num_bins_x, num_bins_y], dtype=dtype) + fixed_cols = int(num_bins_x/(xh-xl)) + 1 + for val in range(fixed_cols): + initial_density_map[val] = bin_size_x*bin_size_y + initial_density_map[:,val] = bin_size_x*bin_size_y + + print("target_area = ", bin_size_x * bin_size_y) + + if dtype == np.float64: + dtype = torch.float64 + elif dtype == np.float32: + dtype = torch.float32 + + movable_size_x = node_size_x[:placedb.num_movable_nodes] + _, sorted_node_map = torch.sort( + torch.tensor(movable_size_x, requires_grad=False, dtype=dtype)) + sorted_node_map = sorted_node_map.to(torch.int32).contiguous() + + # test cpu + custom = electric_potential.ElectricPotential( + torch.tensor(node_size_x, requires_grad=False, dtype=dtype), + torch.tensor(node_size_y, requires_grad=False, dtype=dtype), + xl=xl, + yl=yl, + xh=xh, + yh=yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=0, + deterministic_flag=True, + sorted_node_map=sorted_node_map, + region_id=region_id, + fence_regions=torch.tensor(initial_density_map, requires_grad=False, dtype=dtype), + node2fence_region_map=torch.tensor(node2fence_region_map, requires_grad=False, dtype=torch.int32), + placedb=placedb, + stretchRatio=stretchRatio) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy])), + requires_grad=True) + result = custom.forward(pos) + print("custom_result = ", result) + print(result.type()) + result.backward() + grad = pos.grad.clone() + print("custom_grad = ", grad) + + # test cuda + if torch.cuda.device_count(): + custom_cuda = electric_potential.ElectricPotential( + torch.tensor(node_size_x, requires_grad=False, + dtype=dtype).cuda(), + torch.tensor(node_size_y, requires_grad=False, + dtype=dtype).cuda(), + xl=xl, + yl=yl, + xh=xh, + yh=yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=0, + deterministic_flag=False, + sorted_node_map=sorted_node_map.cuda(), + region_id=region_id, + fence_regions=torch.tensor(initial_density_map, requires_grad=False, + dtype=dtype).cuda(), + node2fence_region_map=torch.tensor(node2fence_region_map, requires_grad=False, + dtype=torch.int32).cuda(), + placedb=placedb, + stretchRatio=stretchRatio) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy])).cuda(), + requires_grad=True) + #pos.grad.zero_() + result_cuda = custom_cuda.forward(pos) + print("custom_result_cuda = ", result_cuda.data.cpu()) + print(result_cuda.type()) + result_cuda.backward() + grad_cuda = pos.grad.clone() + print("custom_grad_cuda = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result.detach().numpy(), + result_cuda.data.cpu().detach().numpy()) + np.testing.assert_allclose(grad.detach().numpy(), + grad_cuda.data.cpu().detach().numpy()) + + +def plot(plot_count, density_map, padding, name): + """ + density map contour and heat map + """ + density_map = density_map[padding:density_map.shape[0] - padding, + padding:density_map.shape[1] - padding] + print("max density = %g" % (np.amax(density_map))) + print("mean density = %g" % (np.mean(density_map))) + + fig = plt.figure() + ax = fig.gca(projection='3d') + + x = np.arange(density_map.shape[0]) + y = np.arange(density_map.shape[1]) + + x, y = np.meshgrid(x, y) + # looks like x and y should be swapped + ax.plot_surface(y, x, density_map, alpha=0.8) + + ax.set_xlabel('x') + ax.set_ylabel('y') + ax.set_zlabel('density') + + # plt.tight_layout() + plt.savefig(name + ".3d.png") + plt.close() + + # plt.clf() + + #fig, ax = plt.subplots() + + # ax.pcolor(density_map) + + # Loop over data dimensions and create text annotations. + # for i in range(density_map.shape[0]): + # for j in range(density_map.shape[1]): + # text = ax.text(j, i, density_map[i, j], + # ha="center", va="center", color="w") + # fig.tight_layout() + #plt.savefig(name+".2d.%d.png" % (plot_count)) + # plt.close() + + +def eval_runtime(design): + ## e.g., adaptec1_density.pklz + #with gzip.open(design, "rb") as f: + # node_size_x, node_size_y, bin_center_x, bin_center_y, xl, yl, xh, yh, bin_size_x, bin_size_y, num_movable_nodes, num_terminals, num_filler_nodes = pickle.load( + # f) + + dtype = torch.float64 + num_threads = 10 + torch.set_num_threads(num_threads) + print("num_threads = %d" % (torch.get_num_threads())) + movable_size_x = node_size_x[:placedb.num_movable_nodes] + _, sorted_node_map = torch.sort( + torch.tensor(movable_size_x, requires_grad=False, dtype=dtype).cuda()) + sorted_node_map = sorted_node_map.to(torch.int32).contiguous() + node2fence_region_map = node2fence_region_map.to(torch.int32).contiguous() + initial_density_map = initial_density_map.to(dtype).contiguous() + + pos_var = Variable(torch.empty(len(node_size_x) * 2, + dtype=dtype).uniform_(xl, xh), + requires_grad=True) + custom = electric_potential.ElectricPotential( + torch.tensor(node_size_x, requires_grad=False, dtype=dtype).cpu(), + torch.tensor(node_size_y, requires_grad=False, dtype=dtype).cpu(), + xl=xl, + yl=yl, + xh=xh, + yh=yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=num_filler_nodes, + deterministic_flag=True, + sorted_node_map=sorted_node_map.cpu(), + region_id=region_id, + fence_regions=initial_density_map.cpu(), + node2fence_region_map=node2fence_region_map.cpu(), + placedb=placedb, + stretchRatio=stretchRatio) + + custom_cuda = electric_potential.ElectricPotential( + torch.tensor(node_size_x, requires_grad=False, dtype=dtype).cuda(), + torch.tensor(node_size_y, requires_grad=False, dtype=dtype).cuda(), + xl=xl, + yl=yl, + xh=xh, + yh=yh, + bin_size_x=bin_size_x, + bin_size_y=bin_size_y, + num_movable_nodes=placedb.num_movable_nodes, + num_terminals=placedb.num_terminals, + num_filler_nodes=num_filler_nodes, + deterministic_flag=False, + sorted_node_map=sorted_node_map, + region_id=region_id, + fence_regions=initial_density_map, + node2fence_region_map=node2fence_region_map, + placedb=placedb, + stretchRatio=stretchRatio) + + torch.cuda.synchronize() + iters = 100 + tbackward = 0 + tt = time.time() + for i in range(iters): + result = custom.forward(pos_var) + ttb = time.time() + result.backward() + tbackward += time.time() - ttb + torch.cuda.synchronize() + print("custom takes %.3f ms, backward %.3f ms" % + ((time.time() - tt) / iters * 1000, (tbackward / iters * 1000))) + + pos_var = pos_var.cuda() + tt = time.time() + for i in range(iters): + result = custom_cuda.forward(pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_cuda takes %.3f ms" % ((time.time() - tt) / iters * 1000)) + + +if __name__ == '__main__': + logging.root.name = 'DREAMPlaceFPGA' + logging.basicConfig(level=logging.DEBUG, + format='[%(levelname)-7s] %(name)s - %(message)s', + stream=sys.stdout) + if len(sys.argv) < 2: + unittest.main() + else: + design = sys.argv[1] + eval_runtime(design) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py new file mode 100644 index 00000000..7cb6f2dd --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py @@ -0,0 +1,152 @@ +## +# @file hpwl_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import numpy as np +import unittest + +import torch +from torch.autograd import Function, Variable +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.hpwl import hpwl +sys.path.pop() +import pdb + +""" +return hpwl of a net +""" +def net_hpwl(x, y, net2pin_map, net_weights, net_id): + pins = net2pin_map[net_id] + hpwl_x = np.amax(x[pins]) - np.amin(x[pins]) + hpwl_y = np.amax(y[pins]) - np.amin(y[pins]) + + return (hpwl_x+hpwl_y)*net_weights[net_id] + +""" +return hpwl of all nets +""" +def all_hpwl(x, y, net2pin_map, net_weights): + wl = 0 + for net_id in range(len(net2pin_map)): + wl += net_hpwl(x, y, net2pin_map, net_weights, net_id) + return wl + +class HPWLOpTest(unittest.TestCase): + def test_hpwlRandom(self): + pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32) + net2pin_map = [[0, 4], [1, 2, 3]] + num_nets = 2 + # net weights + net_weights = np.array([1, 2], dtype=np.float32) + print("net_weights = ", net_weights) + + pin_x = pin_pos[:, 0] + pin_y = pin_pos[:, 1] + + # construct flat_net2pin_map and flat_net2pin_start_map + # flat netpin map, length of #pins + flat_net2pin_map = np.array([0, 4, 1, 2, 3], dtype=np.int32) + # starting index in netpin map for each net, length of #nets+1, the last entry is #pins + flat_net2pin_start_map = np.array([0, 2, 5], dtype=np.int32) + count = 0 + #for i in range(len(net2pin_map)): + # flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] + # flat_net2pin_start_map[i] = count + # count += len(net2pin_map[i]) + #flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) + + print("flat_net2pin_map = ", flat_net2pin_map) + print("flat_net2pin_start_map = ", flat_net2pin_start_map) + + # construct pin2net_map + pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) + for i in range(num_nets): + for pin_id in net2pin_map[i]: + pin2net_map[pin_id] = i + print("pin2net_map = ", pin2net_map) + + # net degrees + net_degrees = np.array([2, 3], dtype=np.int32) + net_mask = (net_degrees <= np.amax(net_degrees)).astype(np.uint8) + print("net_mask = ", net_mask) + + golden_value = all_hpwl(pin_x, pin_y, net2pin_map, net_weights) + print("golden_value = ", golden_value) + + # test cpu + print(np.transpose(pin_pos)) + pin_pos_var = Variable(torch.from_numpy(pin_pos)) + print(pin_pos_var) + # clone is very important, because the custom op cannot deep copy the data + pin_pos_var = torch.t(pin_pos_var).contiguous() + custom = hpwl.HPWL( + xWeight=1.0, + yWeight=1.0, + flat_netpin=torch.from_numpy(flat_net2pin_map), + netpin_start=torch.from_numpy(flat_net2pin_start_map), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + num_threads=1, + algorithm='net-by-net' + ) + hpwl_value = custom.forward(pin_pos_var) + print("hpwl_value = ", hpwl_value.data.numpy()) + np.testing.assert_allclose(hpwl_value.data.numpy(), golden_value) + + # test gpu + if torch.cuda.device_count(): + custom_cuda = hpwl.HPWL( + xWeight=1.0, + yWeight=1.0, + flat_netpin=torch.from_numpy(flat_net2pin_map).cuda(), + netpin_start=torch.from_numpy(flat_net2pin_start_map).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + num_threads=1, + algorithm='net-by-net' + ) + hpwl_value = custom_cuda.forward(pin_pos_var.cuda()) + print("hpwl_value cuda = ", hpwl_value.data.cpu().numpy()) + np.testing.assert_allclose(hpwl_value.data.cpu().numpy(), golden_value) + + # test atomic cpu + custom_atomic = hpwl.HPWL( + xWeight=1.0, + yWeight=1.0, + flat_netpin=torch.from_numpy(flat_net2pin_map), + netpin_start=torch.from_numpy(flat_net2pin_start_map), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + num_threads=1, + algorithm='atomic' + ) + hpwl_value = custom_atomic.forward(pin_pos_var) + print("hpwl_value atomic = ", hpwl_value.data.numpy()) + np.testing.assert_allclose(hpwl_value.data.numpy(), golden_value) + + # test atomic gpu + if torch.cuda.device_count(): + custom_cuda_atomic = hpwl.HPWL( + xWeight=1.0, + yWeight=1.0, + flat_netpin=torch.from_numpy(flat_net2pin_map).cuda(), + netpin_start=torch.from_numpy(flat_net2pin_start_map).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + num_threads=1, + algorithm='atomic' + ) + hpwl_value = custom_cuda_atomic.forward(pin_pos_var.cuda()) + print("hpwl_value cuda atomic = ", hpwl_value.data.cpu().numpy()) + np.testing.assert_allclose(hpwl_value.data.cpu().numpy(), golden_value) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py new file mode 100644 index 00000000..77a00049 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py @@ -0,0 +1,197 @@ +## +# @file logsumexp_wirelength_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import numpy as np +import unittest +import pdb + +import torch +from torch.autograd import Function, Variable + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.logsumexp_wirelength import logsumexp_wirelength +sys.path.pop() + +def print_val(array, name): + temp = "Contents of " + name + ": " + for el in array: + if hasattr(el, "__len__"): + temp += "[" + for subEl in el: + temp += str(subEl.item()) + " " + temp += "] " + else: + temp += str(el.item()) + " " + print(temp) + +def unsorted_segment_max(pin_x, pin2net_map, num_nets): + result = torch.zeros(num_nets, dtype=pin_x.dtype) + for i in range(len(pin2net_map)): + result[pin2net_map[i]] = result[pin2net_map[i]].max(pin_x[i]) + return result + +def unsorted_segment_min(pin_x, pin2net_map, num_nets): + result = torch.zeros(num_nets, dtype=pin_x.dtype) + for i in range(len(pin2net_map)): + result[pin2net_map[i]] = result[pin2net_map[i]].min(pin_x[i]) + return result + +def unsorted_segment_sum(pin_x, pin2net_map, num_nets): + result = torch.zeros(num_nets, dtype=pin_x.dtype) + for i in range(len(pin2net_map)): + result[pin2net_map[i]] += pin_x[i] + return result + +def build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights): + # wirelength cost + # log-sum-exp + # ignore_net_degree is not supported yet + + # temporily store exp(x) + scaled_pin_x = pin_x/gamma + scaled_pin_y = pin_y/gamma + + exp_pin_x = torch.exp(scaled_pin_x) + exp_pin_y = torch.exp(scaled_pin_y) + nexp_pin_x = torch.exp(-scaled_pin_x) + nexp_pin_y = torch.exp(-scaled_pin_y) + + # sum of exp(x) + sum_exp_pin_x = unsorted_segment_sum(exp_pin_x, pin2net_map, len(net2pin_map)) + sum_exp_pin_y = unsorted_segment_sum(exp_pin_y, pin2net_map, len(net2pin_map)) + sum_nexp_pin_x = unsorted_segment_sum(nexp_pin_x, pin2net_map, len(net2pin_map)) + sum_nexp_pin_y = unsorted_segment_sum(nexp_pin_y, pin2net_map, len(net2pin_map)) + + wl = (torch.log(sum_exp_pin_x) + torch.log(sum_nexp_pin_x) + torch.log(sum_exp_pin_y) + torch.log(sum_nexp_pin_y))*gamma + wl *= torch.from_numpy(net_weights) + + wirelength = torch.sum(wl) + + return wirelength + +class LogSumExpWirelengthOpTest(unittest.TestCase): + def test_logsumexp_wirelength_random(self): + dtype = torch.float32 + pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32)*10 + net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) + for net_id, pins in enumerate(net2pin_map): + for pin in pins: + pin2net_map[pin] = net_id + net_weights = np.array([1, 2], dtype=np.float32) + + pin_x = pin_pos[:, 0] + pin_y = pin_pos[:, 1] + gamma = torch.tensor(0.5, dtype=torch.float32) + ignore_net_degree = 4 + pin_mask = np.zeros(len(pin2net_map), dtype=np.uint8) + + # net mask + net_mask = np.ones(len(net2pin_map), dtype=np.uint8) + for i in range(len(net2pin_map)): + if len(net2pin_map[i]) >= ignore_net_degree: + net_mask[i] = 0 + + # construct flat_net2pin_map and flat_net2pin_start_map + # flat netpin map, length of #pins + flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) + # starting index in netpin map for each net, length of #nets+1, the last entry is #pins + flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(net2pin_map)): + flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] + flat_net2pin_start_map[i] = count + count += len(net2pin_map[i]) + flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) + + #print("flat_net2pin_map = ", flat_net2pin_map) + print_val(flat_net2pin_map, "flat_net2pin_map") + #print("flat_net2pin_start_map = ", flat_net2pin_start_map) + print_val(flat_net2pin_start_map, "flat_net2pin_start_map") + + #print(np.transpose(pin_pos)) + print_val(np.transpose(pin_pos), "pin_pos_transpose") + pin_pos_var = Variable(torch.from_numpy(np.transpose(pin_pos)).reshape([-1]), requires_grad=True) + #pin_pos_var = torch.nn.Parameter(torch.from_numpy(np.transpose(pin_pos)).reshape([-1])) + print(pin_pos_var) + + golden = build_wirelength(pin_pos_var[:pin_pos_var.numel()//2], pin_pos_var[pin_pos_var.numel()//2:], pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights) + print("golden_value = ", golden.item()) + golden.backward() + golden_grad = pin_pos_var.grad.clone() + print("golden_grad = ", golden_grad.data) + + # test cpu + # clone is very important, because the custom op cannot deep copy the data + pin_pos_var.grad.zero_() + custom = logsumexp_wirelength.LogSumExpWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + algorithm='merged' + ) + result = custom.forward(pin_pos_var) + print("custom = ", result.item()) + result.backward() + grad = pin_pos_var.grad.clone() + print("custom_grad = ", grad) + + np.testing.assert_allclose(result.data.numpy(), golden.data.detach().numpy()) + np.testing.assert_allclose(grad.data.numpy(), golden_grad.data.numpy()) + + # test gpu + if torch.cuda.device_count(): + pin_pos_var.grad.zero_() + custom_cuda = logsumexp_wirelength.LogSumExpWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + algorithm='merged' + ) + result_cuda = custom_cuda.forward(pin_pos_var.cuda()) + print("custom_cuda_result = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pin_pos_var.grad.clone() + print("custom_grad_cuda = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden.data.detach().numpy()) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-7, atol=1e-11) + + # test gpu atomic + if torch.cuda.device_count(): + pin_pos_var.grad.zero_() + custom_cuda = logsumexp_wirelength.LogSumExpWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=gamma.cuda(), + algorithm='atomic' + ) + result_cuda = custom_cuda.forward(pin_pos_var.cuda()) + print("custom_cuda_result atomic = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pin_pos_var.grad.clone() + print("custom_grad_cuda atomic = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden.data.detach().numpy()) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-7, atol=1e-15) + + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py new file mode 100644 index 00000000..947404b2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py @@ -0,0 +1,75 @@ +## +# @file move_boundary_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import numpy as np +import unittest + +import torch +from torch.autograd import Function, Variable + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.move_boundary import move_boundary +sys.path.pop() + +class MoveBoundaryOpTest(unittest.TestCase): + def test_densityOverflowRandom(self): + dtype = np.float32 + xx = np.array([1.0, 4.6]).astype(dtype) + yy = np.array([0.5, 4.1]).astype(dtype) + node_size_x = np.array([0.5, 1.0]).astype(dtype) + node_size_y = np.array([1.0, 1.0]).astype(dtype) + #xx = np.array([2.0]).astype(dtype) + #yy = np.array([1.5]).astype(dtype) + #node_size_x = np.array([1.0]).astype(dtype) + #node_size_y = np.array([1.0]).astype(dtype) + num_nodes = len(xx) + scale_factor = 1.0 + + xl = 1.0 + yl = 1.0 + xh = 5.0 + yh = 5.0 + num_movable_nodes = len(xx) + num_terminals = 0 + num_filler_nodes = 0 + + # test cpu + custom = move_boundary.MoveBoundary( + torch.from_numpy(node_size_x), torch.from_numpy(node_size_y), + xl=xl, yl=yl, xh=xh, yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + num_threads=1) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy]))) + result = custom(pos) + print("custom_result = ", result) + + #result.retain_grad() + #result.sum().backward() + #print("custom_result.grad = ", result.grad) + + # test cuda + if torch.cuda.device_count(): + custom_cuda = move_boundary.MoveBoundary( + torch.from_numpy(node_size_x).cuda(), + torch.from_numpy(node_size_y).cuda(), + xl=xl, yl=yl, xh=xh, yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + num_threads=1) + + pos = Variable(torch.from_numpy(np.concatenate([xx, yy]))).cuda() + result_cuda = custom_cuda(pos) + print("custom_result = ", result_cuda.data.cpu()) + + + np.testing.assert_allclose(result, result_cuda.data.cpu()) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py new file mode 100644 index 00000000..10a80049 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py @@ -0,0 +1,128 @@ +## +# @file pin_pos_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import time +import numpy as np +import unittest +#import pickle +import gzip +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.pin_pos import pin_pos +sys.path.pop() + +import pdb + +import torch +from torch.autograd import Function, Variable + +def build_pin_pos(pos, pin_offset_x, pin_offset_y, pin2node_map, num_physical_nodes): + num_nodes = pos.numel()//2 + pin_x = pin_offset_x.add(torch.index_select(pos[0:num_physical_nodes], dim=0, index=pin2node_map.long())) + pin_y = pin_offset_y.add(torch.index_select(pos[num_nodes:num_nodes+num_physical_nodes], dim=0, index=pin2node_map.long())) + pin_pos = torch.cat([pin_x, pin_y], dim=0) + return pin_pos + +class WeightedAverageWirelengthOpTest(unittest.TestCase): + def test_pin_pos_random(self): + dtype = torch.float32 + num_threads = 1 + pos = np.array([[1, 10], [2, 20], [3, 30]], dtype=np.float32) + node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + num_physical_nodes = len(node2pin_map) + num_pins = 0 + for pins in node2pin_map: + num_pins += len(pins) + pin2node_map = np.zeros(num_pins, dtype=np.int32) + for node_id, pins in enumerate(node2pin_map): + for pin in pins: + pin2node_map[pin] = node_id + + pin_offset_x = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=pos.dtype) + pin_offset_y = np.array([0.01, 0.02, 0.03, 0.04, 0.05], dtype=pos.dtype) + + # construct flat_node2pin_map and flat_node2pin_start_map + # flat nodepin map, length of #pins + flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) + # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins + flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(node2pin_map)): + flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] + flat_node2pin_start_map[i] = count + count += len(node2pin_map[i]) + flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) + + print("flat_node2pin_map = ", flat_node2pin_map) + print("flat_node2pin_start_map = ", flat_node2pin_start_map) + + pos_var = Variable(torch.from_numpy(pos).reshape([-1]), requires_grad=True) + + golden_value = build_pin_pos(pos_var, torch.from_numpy(pin_offset_x), + torch.from_numpy(pin_offset_y), + torch.from_numpy(pin2node_map), num_physical_nodes) + golden_loss = golden_value.sum() + print("golden_value = ", golden_value) + print("golden_loss = ", golden_loss) + golden_loss.backward() + golden_grad = pos_var.grad.clone() + print("golden grad = ", golden_grad) + golden_value = golden_value.detach().numpy() + golden_grad = golden_grad.detach().numpy() + + # test cpu + print(pos_var) + # clone is very important, because the custom op cannot deep copy the data + custom = pin_pos.PinPos( + pin_offset_x=torch.from_numpy(pin_offset_x), + pin_offset_y=torch.from_numpy(pin_offset_y), + pin2node_map=torch.from_numpy(pin2node_map), + flat_node2pin_map=torch.from_numpy(flat_node2pin_map), + flat_node2pin_start_map=torch.from_numpy(flat_node2pin_start_map), + num_physical_nodes=num_physical_nodes, + num_threads=num_threads + ) + result = custom.forward(pos_var) + custom_loss = result.sum() + print("custom = ", result) + pos_var.grad.zero_() + custom_loss.backward() + grad = pos_var.grad.clone() + print("custom_grad = ", grad) + + np.testing.assert_allclose(result.data.detach().numpy(), golden_value, atol=1e-6) + np.testing.assert_allclose(grad.data.detach().numpy(), golden_grad, atol=1e-6) + + # test gpu + if torch.cuda.device_count(): + pos_var.grad.zero_() + custom_cuda = pin_pos.PinPos( + pin_offset_x=torch.from_numpy(pin_offset_x).cuda(), + pin_offset_y=torch.from_numpy(pin_offset_y).cuda(), + pin2node_map=torch.from_numpy(pin2node_map).cuda(), + flat_node2pin_map=torch.from_numpy(flat_node2pin_map).cuda(), + flat_node2pin_start_map=torch.from_numpy(flat_node2pin_start_map).cuda(), + num_physical_nodes=num_physical_nodes, + num_threads=num_threads + ) + result_cuda = custom_cuda.forward(pos_var.cuda()) + custom_cuda_loss = result_cuda.sum() + print("custom_cuda_result = ", result_cuda.data.cpu()) + custom_cuda_loss.backward() + grad_cuda = pos_var.grad.clone() + print("custom_grad_cuda = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-6) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-6, atol=1e-6) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py new file mode 100644 index 00000000..2963b607 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py @@ -0,0 +1,112 @@ +## +# @file pin_utilization_unitest.py +# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import unittest +import torch +import numpy as np +import math + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.pin_utilization import pin_utilization +sys.path.pop() + +class PinUtilizationUnittest(unittest.TestCase): + def test_pin_utilization(self): + # the data of nodes are from unitest/ops/pin_pos_unitest.py + dtype = torch.float32 + + pos = torch.Tensor([[1, 10], [2, 20], [3, 30]]).to(dtype) + node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + num_movable_nodes = len(node2pin_map) + num_filler_nodes = 1 + num_nodes = num_movable_nodes + num_filler_nodes + + + num_pins = 0 + for pins in node2pin_map: + num_pins += len(pins) + pin2node_map = np.zeros(num_pins, dtype=np.int32) + for node_id, pins in enumerate(node2pin_map): + for pin in pins: + pin2node_map[pin] = node_id + + # construct flat_node2pin_map and flat_node2pin_start_map + # flat nodepin map, length of #pins + flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) + # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins + flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(node2pin_map)): + flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] + flat_node2pin_start_map[i] = count + count += len(node2pin_map[i]) + flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) + flat_node2pin_start_map = torch.from_numpy(flat_node2pin_start_map) + + node_size_x = torch.Tensor([3, 3, 3]).to(dtype) + node_size_y = torch.Tensor([6, 6, 3]).to(dtype) + xl, xh = 0, 8 + yl, yh = 0, 64 + num_bins_x, num_bins_y = 2, 16 + bin_size_x = (xh - xl) / num_bins_x + bin_size_y = (yh - yl) / num_bins_y + + pin_weights = None + unit_pin_capacity = 0.5 + pin_stretch_ratio = math.sqrt(2) + + # test cpu + pin_utilization_op = pin_utilization.PinUtilization( + node_size_x=node_size_x, + node_size_y=node_size_y, + pin_weights=pin_weights, + flat_node2pin_start_map=flat_node2pin_start_map, + xl=xl, + xh=xh, + yl=yl, + yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + num_bins_x=num_bins_x, + num_bins_y=num_bins_y, + unit_pin_capacity=unit_pin_capacity, + pin_stretch_ratio=pin_stretch_ratio, + deterministic_flag=True, + num_threads=1 + ) + + result_cpu = pin_utilization_op.forward(pos.t().contiguous().view(-1)) + print("Test on CPU. pin_utilization map = ", result_cpu) + + if torch.cuda.device_count(): + # test gpu + pin_utilization_op_cuda = pin_utilization.PinUtilization( + node_size_x=node_size_x.cuda(), + node_size_y=node_size_y.cuda(), + pin_weights=pin_weights, + flat_node2pin_start_map=flat_node2pin_start_map.cuda(), + xl=xl, + xh=xh, + yl=yl, + yh=yh, + num_movable_nodes=num_movable_nodes, + num_filler_nodes=num_filler_nodes, + num_bins_x=num_bins_x, + num_bins_y=num_bins_y, + unit_pin_capacity=unit_pin_capacity, + pin_stretch_ratio=pin_stretch_ratio, + deterministic_flag=False, + num_threads=1 + ) + + result_cuda = pin_utilization_op_cuda.forward(pos.t().contiguous().view(-1).cuda()) + print("Test on GPU. pin_utilization map = ", result_cuda) + np.testing.assert_allclose(result_cpu, result_cuda.cpu()) + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py new file mode 100644 index 00000000..47eea2da --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py @@ -0,0 +1,99 @@ +## +# @file rudy_unitest.py +# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import unittest +import torch +import numpy as np + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.rudy import rudy +sys.path.pop() + +class RudyUnittest(unittest.TestCase): + def test_rudy(self): + # the data of net and pin are from unitest/ops/weighted_average_wirelength_unitest.py + dtype = torch.float32 + pin_pos = torch.Tensor([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]]).to(dtype) + net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + net_weights = torch.Tensor([1, 2]).to(dtype) + + # construct flat_net2pin_map and flat_net2pin_start_map + # flat netpin map, length of #pins + flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) + # starting index in netpin map for each net, length of #nets+1, the last entry is #pins + flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(net2pin_map)): + flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] + flat_net2pin_start_map[i] = count + count += len(net2pin_map[i]) + flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) + + print("flat_net2pin_map = ", flat_net2pin_map) + print("flat_net2pin_start_map = ", flat_net2pin_start_map) + flat_net2pin_map = torch.from_numpy(flat_net2pin_map) + flat_net2pin_start_map = torch.from_numpy(flat_net2pin_start_map) + + # parameters for this test + xl, xh = 0.0, 2.0 + yl, yh = 0.0, 4.0 + num_bins_x = 8 + num_bins_y = 8 + unit_horizontal_capacity = 0.1 + unit_vertical_capacity = 0.2 + + # test cpu + rudy_op = rudy.Rudy( + netpin_start=flat_net2pin_start_map, + flat_netpin=flat_net2pin_map, + net_weights=net_weights, + xl=xl, + xh=xh, + yl=yl, + yh=yh, + num_bins_x=num_bins_x, + num_bins_y=num_bins_y, + unit_horizontal_capacity=unit_horizontal_capacity, + unit_vertical_capacity=unit_vertical_capacity, + deterministic_flag=True, + initial_horizontal_utilization_map=None, + initial_vertical_utilization_map=None, + num_threads=1 + ) + + result_cpu = rudy_op.forward(pin_pos.t().contiguous().view(-1)) + print("Test on CPU. rudy map = ", result_cpu) + + if torch.cuda.device_count(): + # test gpu + rudy_op_cuda = rudy.Rudy( + netpin_start=flat_net2pin_start_map.cuda(), + flat_netpin=flat_net2pin_map.cuda(), + net_weights=net_weights.cuda(), + xl=xl, + xh=xh, + yl=yl, + yh=yh, + num_bins_x=num_bins_x, + num_bins_y=num_bins_y, + unit_horizontal_capacity=unit_horizontal_capacity, + unit_vertical_capacity=unit_vertical_capacity, + deterministic_flag=False, + initial_horizontal_utilization_map=None, + initial_vertical_utilization_map=None, + num_threads=1 + ) + + result_cuda = rudy_op_cuda.forward(pin_pos.t().contiguous().view(-1).cuda()) + print("Test on GPU. rudy map = ", result_cuda) + + np.testing.assert_allclose(result_cpu, result_cuda.cpu()) + + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py new file mode 100644 index 00000000..70b86012 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py @@ -0,0 +1,433 @@ +## +# @file weighted_average_wirelength_unitest.py +# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) +# @date Mar 2024 +# + +import os +import sys +import time +import numpy as np +import unittest +#import pickle +import gzip +if sys.version_info[0] < 3: + import cPickle as pickle +else: + import _pickle as pickle + +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +from dreamplacefpga.ops.weighted_average_wirelength import weighted_average_wirelength +sys.path.pop() + +import pdb + +import torch +from torch.autograd import Function, Variable + +def unsorted_segment_sum(pin_x, pin2net_map, num_nets): + result = np.zeros(num_nets, dtype=pin_x.dtype) + for i in range(len(pin2net_map)): + result[pin2net_map[i]] += pin_x[i] + return result + +def build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights): + # wirelength cost + # weighted-average + + # temporily store exp(x) + scaled_pin_x = pin_x/gamma + scaled_pin_y = pin_y/gamma + + exp_pin_x = np.exp(scaled_pin_x) + exp_pin_y = np.exp(scaled_pin_y) + nexp_pin_x = np.exp(-scaled_pin_x) + nexp_pin_y = np.exp(-scaled_pin_y) + + # sum of exp(x) + sum_exp_pin_x = unsorted_segment_sum(exp_pin_x, pin2net_map, len(net2pin_map)) + sum_exp_pin_y = unsorted_segment_sum(exp_pin_y, pin2net_map, len(net2pin_map)) + sum_nexp_pin_x = unsorted_segment_sum(nexp_pin_x, pin2net_map, len(net2pin_map)) + sum_nexp_pin_y = unsorted_segment_sum(nexp_pin_y, pin2net_map, len(net2pin_map)) + + # sum of x*exp(x) + sum_x_exp_pin_x = unsorted_segment_sum(pin_x*exp_pin_x, pin2net_map, len(net2pin_map)) + sum_y_exp_pin_y = unsorted_segment_sum(pin_y*exp_pin_y, pin2net_map, len(net2pin_map)) + sum_x_nexp_pin_x = unsorted_segment_sum(pin_x*nexp_pin_x, pin2net_map, len(net2pin_map)) + sum_y_nexp_pin_y = unsorted_segment_sum(pin_y*nexp_pin_y, pin2net_map, len(net2pin_map)) + + sum_exp_pin_x = sum_exp_pin_x + sum_x_exp_pin_x = sum_x_exp_pin_x + + wl = sum_x_exp_pin_x / sum_exp_pin_x - sum_x_nexp_pin_x / sum_nexp_pin_x \ + + sum_y_exp_pin_y / sum_exp_pin_y - sum_y_nexp_pin_y / sum_nexp_pin_y + + for i in range(len(net2pin_map)): + if len(net2pin_map[i]) >= ignore_net_degree: + wl[i] = 0 + + wl *= net_weights + + wirelength = np.sum(wl) + + return wirelength + +class WeightedAverageWirelengthOpTest(unittest.TestCase): + def test_weighted_average_wirelength_random(self): + dtype = torch.float32 + #pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32) + + #net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) + #pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) + #for net_id, pins in enumerate(net2pin_map): + # for pin in pins: + # pin2net_map[pin] = net_id + #net_weights = np.array([1, 1], dtype=np.float32) + + #pin_x = pin_pos[:, 0] + #pin_y = pin_pos[:, 1] + #gamma = 0.5 + #ignore_net_degree = 4 + + pin_pos = np.array([[103.7478, 127.4158], [103.5688, 127.7118], [103.6268, 127.5448], [103.804, 127.548], [103.462, 127.399], [103.502, 127.211], [103.4118, 127.4038], [103.6178, 127.8198]], dtype=np.float32) + + net2pin_map = np.array([np.array([0, 1, 2, 3]), np.array([4, 5, 6, 7])]) + pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) + for net_id, pins in enumerate(net2pin_map): + for pin in pins: + pin2net_map[pin] = net_id + net_weights = np.array([1, 1], dtype=np.float32) + + pin_x = pin_pos[:, 0] + pin_y = pin_pos[:, 1] + gamma = 31.6409 + ignore_net_degree = 8 + + pin_mask = np.zeros(len(pin2net_map), dtype=np.uint8) + + #elfPlace Impl + xy_max = [pin_pos[net2pin_map[i]].max(axis=0) for i in range(2)] + xy_min = [pin_pos[net2pin_map[i]].min(axis=0) for i in range(2)] + + expNegX = [] + expPosX = [] + expNegY = [] + expPosY = [] + xexpNegX = [] + xexpPosX = [] + yexpNegY = [] + yexpPosY = [] + wl = [] + for i, pin in enumerate(net2pin_map): + expNegX.append(np.e**(-(pin_pos[pin][:,0] - xy_min[i][0])/gamma)) + expPosX.append(np.e**((pin_pos[pin][:,0] - xy_max[i][0])/gamma)) + expNegY.append(np.e**(-(pin_pos[pin][:,1] - xy_min[i][1])/gamma)) + expPosY.append(np.e**((pin_pos[pin][:,1] - xy_max[i][1])/gamma)) + xexpNegX.append(pin_pos[pin][:,0] * expNegX[i]) + xexpPosX.append(pin_pos[pin][:,0] * expPosX[i]) + yexpNegY.append(pin_pos[pin][:,1] * expNegY[i]) + yexpPosY.append(pin_pos[pin][:,1] * expPosY[i]) + + #pdb.set_trace() + for i in range(2): + wl.append(xexpPosX[i].sum()/expPosX[i].sum() - xexpNegX[i].sum()/expNegX[i].sum() + yexpPosY[i].sum()/expPosY[i].sum() - yexpNegY[i].sum()/expNegY[i].sum()) + + totalWl = np.sum([np.sum(i) for i in wl]) + print("elfPlace WL = ", totalWl) + + + # net mask + net_mask = np.ones(len(net2pin_map), dtype=np.uint8) + for i in range(len(net2pin_map)): + if len(net2pin_map[i]) >= ignore_net_degree: + net_mask[i] = 0 + + net_bounding_box_min = np.ones_like(net_mask, dtype=np.float32) + net_bounding_box_min *= pin_pos.max() + net_bounding_box_max = np.zeros_like(net_mask, dtype=np.float32) + # construct flat_net2pin_map and flat_net2pin_start_map + # flat netpin map, length of #pins + flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) + # starting index in netpin map for each net, length of #nets+1, the last entry is #pins + flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) + count = 0 + for i in range(len(net2pin_map)): + flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] + flat_net2pin_start_map[i] = count + count += len(net2pin_map[i]) + flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) + + print("flat_net2pin_map = ", flat_net2pin_map) + print("flat_net2pin_start_map = ", flat_net2pin_start_map) + + golden_value = np.array([build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights)]) + print("golden_value = ", golden_value) + + print(np.transpose(pin_pos)) + pin_pos_var = Variable(torch.tensor(np.transpose(pin_pos), dtype=dtype).reshape([-1]), requires_grad=True) + #pin_pos_var = torch.nn.Parameter(torch.from_numpy(np.transpose(pin_pos)).reshape([-1])) + print(pin_pos_var) + # clone is very important, because the custom op cannot deep copy the data + + # test cpu net-by-net + custom = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=torch.from_numpy(flat_net2pin_map), + netpin_start=torch.from_numpy(flat_net2pin_start_map), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='net-by-net' + ) + result = custom.forward(pin_pos_var) + print("custom = ", result) + result.backward() + grad = pin_pos_var.grad.clone() + print("custom_grad = ", grad) + + np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) + + # test cpu atomic + pin_pos_var.grad.zero_() + custom = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='atomic' + ) + result = custom.forward(pin_pos_var) + print("custom_cpu_result atomic = ", result.data) + result.backward() + grad_atomic = pin_pos_var.grad.clone() + print("custom_grad_cpu atomic = ", grad_atomic.data) + + np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) + np.testing.assert_allclose(grad_atomic.data.numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) + + # test cpu merged + pin_pos_var.grad.zero_() + custom = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=torch.from_numpy(net_weights), + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='merged' + ) + result = custom.forward(pin_pos_var) + print("custom_cpu_result merged = ", result.data) + result.backward() + grad_merged = pin_pos_var.grad.clone() + print("custom_grad_cpu merged = ", grad_merged.data) + + np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) + np.testing.assert_allclose(grad_merged.data.numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) + + # test gpu + if torch.cuda.device_count(): + pin_pos_var.grad.zero_() + custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='net-by-net' + ) + result_cuda = custom_cuda.forward(pin_pos_var.cuda()) + print("custom_cuda_result = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pin_pos_var.grad.clone() + print("custom_grad_cuda = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-6) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-6, atol=1e-6) + + # test gpu atomic + if torch.cuda.device_count(): + pin_pos_var.grad.zero_() + custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='atomic' + ) + result_cuda = custom_cuda.forward(pin_pos_var.cuda()) + print("custom_cuda_result atomic = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pin_pos_var.grad.clone() + print("custom_grad_cuda atomic = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-4) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) + + # test gpu merged + if torch.cuda.device_count(): + pin_pos_var.grad.zero_() + custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=torch.from_numpy(net_weights).cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + num_threads=1, + algorithm='merged' + ) + result_cuda = custom_cuda.forward(pin_pos_var.cuda()) + print("custom_cuda_result merged = ", result_cuda.data.cpu()) + result_cuda.backward() + grad_cuda = pin_pos_var.grad.clone() + print("custom_grad_cuda merged = ", grad_cuda.data.cpu()) + + np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-4) + np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) + +def eval_runtime(design): + ## e.g,. adaptec1_wirelength.pklz + #with gzip.open(design, "rb") as f: + # flat_net2pin_map, flat_net2pin_start_map, pin2net_map, net_mask, pin_mask, gamma = pickle.load(f) + dtype = torch.float64 + net_weights = torch.Tensor() + pin_pos_var = Variable(torch.empty(len(pin2net_map)*2, dtype=dtype).uniform_(0, 1000), requires_grad=True) + custom_net_by_net_cpu = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=net_weights, + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + algorithm='net-by-net', + num_threads=10 + ) + custom_atomic_cpu = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), + pin2net_map=torch.from_numpy(pin2net_map), + net_weights=net_weights, + net_mask=torch.from_numpy(net_mask), + pin_mask=torch.from_numpy(pin_mask), + gamma=torch.tensor(gamma, dtype=dtype), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + algorithm='atomic', + num_threads=10 + ) + custom_net_by_net = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=net_weights.cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + algorithm='net-by-net' + ) + custom_atomic = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=net_weights.cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + algorithm='atomic' + ) + custom_sparse = weighted_average_wirelength.WeightedAverageWirelength( + flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), + netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), + pin2net_map=torch.from_numpy(pin2net_map).cuda(), + net_weights=net_weights.cuda(), + net_mask=torch.from_numpy(net_mask).cuda(), + pin_mask=torch.from_numpy(pin_mask).cuda(), + gamma=torch.tensor(gamma, dtype=dtype).cuda(), + net_bounding_box_min=torch.from_numpy(net_bounding_box_min), + net_bounding_box_max=torch.from_numpy(net_bounding_box_max), + algorithm='sparse' + ) + + torch.cuda.synchronize() + iters = 100 + tt = time.time() + for i in range(iters): + result = custom_net_by_net_cpu.forward(pin_pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_net_by_net cpu takes %.3f ms" % ((time.time()-tt)/iters*1000)) + + tt = time.time() + for i in range(iters): + result = custom_atomic_cpu.forward(pin_pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_atomic cpu takes %.3f ms" % ((time.time()-tt)/iters*1000)) + + pin_pos_var = pin_pos_var.cuda() + torch.cuda.synchronize() + tt = time.time() + for i in range(iters): + result = custom_net_by_net.forward(pin_pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_net_by_net takes %.3f ms" % ((time.time()-tt)/iters*1000)) + + tt = time.time() + for i in range(iters): + result = custom_atomic.forward(pin_pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_atomic takes %.3f ms" % ((time.time()-tt)/iters*1000)) + + tt = time.time() + for i in range(iters): + result = custom_sparse.forward(pin_pos_var) + result.backward() + torch.cuda.synchronize() + print("custom_sparse takes %.3f ms" % ((time.time()-tt)/iters*1000)) + + +if __name__ == '__main__': + + if len(sys.argv) < 2: + unittest.main() + else: + design = sys.argv[1] + eval_runtime(design) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/agent_files.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/agent_files.txt new file mode 100644 index 00000000..cf5ccc3a --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/agent_files.txt @@ -0,0 +1,4 @@ +README.md +Task.md +scripts/init.py +frontier_eval/constraints.txt diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/artifact_files.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/artifact_files.txt new file mode 100644 index 00000000..a52b8b16 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/artifact_files.txt @@ -0,0 +1,2 @@ +# No extra artifact files are auto-collected by default for this benchmark. +# metrics.json and artifacts.json are handled separately by UnifiedTask. diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/candidate_destination.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/candidate_destination.txt new file mode 100644 index 00000000..b9411b3d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/candidate_destination.txt @@ -0,0 +1 @@ +scripts/init.py diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/constraints.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/constraints.txt new file mode 100644 index 00000000..c6ff4953 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/constraints.txt @@ -0,0 +1,14 @@ +FPGAPlacementOptimization UnifiedTask constraints: +1) Only modify `scripts/init.py`. +2) Preserve the output contract: the program must produce a `solution.pl` + file in Bookshelf format with one line per instance: +3) Do not modify benchmark assets, documentation, references, verification + code, runtime helpers, or `frontier_eval/` metadata. +4) Fixed instances (IO pads, PLLs) must remain at their original (x, y, z) + coordinates from the input .pl file. +5) Every movable instance must be assigned to a legal (x, y, z) coordinate. +6) The placement must satisfy all three legality gates: + - G1: Site-type compatibility (LUT/FF on SLICE, DSP on DSP, BRAM on BRAM) + - G2: Resource capacity (max 16 LUT and 16 FF per SLICE site) + - G3: Carry-chain integrity (adjacent vertical placement) +7) Prioritize correctness (legal placement) before optimization (HPWL). diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/copy_files.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/copy_files.txt new file mode 100644 index 00000000..9c558e35 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/copy_files.txt @@ -0,0 +1 @@ +. diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_command.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_command.txt new file mode 100644 index 00000000..cc598545 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_command.txt @@ -0,0 +1 @@ +{python} verification/evaluator.py {candidate} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_cwd.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_cwd.txt new file mode 100644 index 00000000..9c558e35 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/eval_cwd.txt @@ -0,0 +1 @@ +. diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/evaluator.py b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/evaluator.py new file mode 100644 index 00000000..4e8618f2 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/evaluator.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import inspect +from importlib.util import module_from_spec, spec_from_file_location +from pathlib import Path +from typing import Any + + +def _load_verification_module() -> Any: + evaluator_path = ( + Path(__file__).resolve().parents[1] / "verification" / "evaluator.py" + ).resolve() + spec = spec_from_file_location("_frontier_eval_verification_evaluator", evaluator_path) + if spec is None or spec.loader is None: + raise RuntimeError(f"Failed to load verification evaluator from {evaluator_path}") + module = module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def evaluate(program_path: str, *, repo_root: Path | None = None) -> Any: + module = _load_verification_module() + evaluate_fn = getattr(module, "evaluate") + kwargs: dict[str, Any] = {} + if "repo_root" in inspect.signature(evaluate_fn).parameters and repo_root is not None: + kwargs["repo_root"] = repo_root + return evaluate_fn(program_path, **kwargs) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/initial_program.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/initial_program.txt new file mode 100644 index 00000000..b9411b3d --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/initial_program.txt @@ -0,0 +1 @@ +scripts/init.py diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/readonly_files.txt b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/readonly_files.txt new file mode 100644 index 00000000..d09a619e --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/readonly_files.txt @@ -0,0 +1,5 @@ +README.md +Task.md +references +verification +frontier_eval diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/run_eval.py b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/run_eval.py new file mode 100644 index 00000000..cfb93ac5 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/frontier_eval/run_eval.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import argparse +import inspect +import json +import os +import sys +import traceback +from importlib.util import module_from_spec, spec_from_file_location +from pathlib import Path +from typing import Any + +INVALID_COMBINED_SCORE = -1e18 + + +def _write_json(path: Path, obj: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text( + json.dumps(obj, ensure_ascii=False, indent=2, default=str) + "\n", + encoding="utf-8", + ) + + +def _normalize_result(result: Any) -> tuple[dict[str, Any], dict[str, Any]]: + if hasattr(result, "metrics") and hasattr(result, "artifacts"): + return dict(getattr(result, "metrics")), dict(getattr(result, "artifacts")) + + if isinstance(result, dict): + raw_metrics = result.get("metrics") + raw_artifacts = result.get("artifacts") + if isinstance(raw_metrics, dict): + return dict(raw_metrics), dict(raw_artifacts or {}) + return dict(result), {} + + raise TypeError( + "Evaluator must return an EvaluationResult-like object or a dict of metrics." + ) + + +def _load_local_evaluator() -> Any: + evaluator_path = Path(__file__).with_name("evaluator.py").resolve() + spec = spec_from_file_location("_frontier_eval_local_evaluator", evaluator_path) + if spec is None or spec.loader is None: + raise RuntimeError(f"Failed to load local evaluator from {evaluator_path}") + module = module_from_spec(spec) + spec.loader.exec_module(module) + try: + return getattr(module, "evaluate") + except AttributeError as exc: + raise RuntimeError( + f"Local evaluator does not define evaluate(): {evaluator_path}" + ) from exc + + +def _find_repo_root() -> Path: + env_root = os.environ.get("FRONTIER_ENGINEERING_ROOT") + if env_root: + return Path(env_root).expanduser().resolve() + + here = Path(__file__).resolve() + for parent in [here.parent, *here.parents]: + if (parent / "frontier_eval").is_dir() and (parent / "benchmarks").is_dir(): + return parent + return Path.cwd().resolve() + + +def _build_kwargs(evaluate_fn: Any) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + try: + parameters = inspect.signature(evaluate_fn).parameters + except Exception: + return kwargs + + if "repo_root" in parameters: + kwargs["repo_root"] = _find_repo_root() + if "kernel_python" in parameters: + kwargs["kernel_python"] = sys.executable + return kwargs + + +def _parse_args(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Run a benchmark-local unified evaluator and export metrics/artifacts JSON." + ) + parser.add_argument("--candidate", required=True) + parser.add_argument("--metrics-out", default="metrics.json") + parser.add_argument("--artifacts-out", default="artifacts.json") + return parser.parse_args(argv) + + +def main(argv: list[str]) -> int: + args = _parse_args(argv) + + candidate_path = Path(args.candidate).expanduser().resolve() + metrics_out = Path(args.metrics_out).expanduser().resolve() + artifacts_out = Path(args.artifacts_out).expanduser().resolve() + + metrics: dict[str, Any] = { + "combined_score": INVALID_COMBINED_SCORE, + "valid": 0.0, + } + artifacts: dict[str, Any] = { + "local_evaluator_path": str(Path(__file__).with_name("evaluator.py").resolve()), + "candidate_path": str(candidate_path), + } + + try: + evaluate_fn = _load_local_evaluator() + result = evaluate_fn(str(candidate_path), **_build_kwargs(evaluate_fn)) + metrics, evaluator_artifacts = _normalize_result(result) + artifacts.update(evaluator_artifacts) + except Exception as exc: + artifacts["error_message"] = str(exc) + artifacts["traceback"] = traceback.format_exc() + + _write_json(metrics_out, metrics) + _write_json(artifacts_out, artifacts) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/references/README b/benchmarks/FPGA/FPGAPlacementOptimization/references/README new file mode 100644 index 00000000..dd59ec13 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/references/README @@ -0,0 +1,64 @@ +======================================================================================= + Bookshelf Format for FPGA Placement +======================================================================================= + +This document explains the differences (new features) of FPGA placement bookshelf format. + +1. Library cell (.lib file): + - Each instance has a corresponding master library cell. It is defined in nodes file; + - All library cells are defined in design.lib, a new addition to bookshelf format; + +2. PIN: + - All pins are defined in library file (.lib) cell section; + - Each instance has the same number of pins as defined in its master cell; + - Not all the pins of an instance are used. Some are left unconnected; + - Library file defines certain attributes associated with pins: direction, clock, and control; + - Each net is a collection of pins, as specified in nets file; + +3. Layout file (.scl file) + - Layout file is re-defined to accomodate FPGA placement; + - There are two section in layout file: site definition section and site map section; + - SITE definition speicifies available resources (LUT/FF/RAMB/DSP) that can be placed in one site; + - RESOURCES specifies cell names that correspond to certain resource; + - SITEMAP specifies the two-dimension array of sites for the entire device/chip: + +4. Placement file (.pl file) + - The location of an instance has three fields: x-coord, y-coord (to determine the SITE) and BEL (index within the SITE); + - In released benchmarks, placement file only contains locations of fixed instances (IBUF/OBUF/BUFGCE etc); + These instances' locations, including BEL numbers, are not allowed to change during placement; + - Placer's output placement file should contain locations of all instances; + - The following diagram shows the BEL number for LUTs/FFs placed inside a SLICE SITE: + ========================== + | LUT 15 | FF 15 | + -------------------------- + | LUT 14 | FF 14 | + -------------------------- + | LUT 13 | FF 13 | + -------------------------- + | LUT 12 | FF 12 | + -------------------------- + | LUT 11 | FF 11 | + -------------------------- + | LUT 10 | FF 10 | + -------------------------- + | LUT 9 | FF 9 | + -------------------------- + | LUT 8 | FF 8 | + -------------------------- + | LUT 7 | FF 7 | + -------------------------- + | LUT 6 | FF 6 | + -------------------------- + | LUT 5 | FF 5 | + -------------------------- + | LUT 4 | FF 4 | + -------------------------- + | LUT 3 | FF 3 | + -------------------------- + | LUT 2 | FF 2 | + -------------------------- + | LUT 1 | FF 1 | + -------------------------- + | LUT 0 | FF 0 | + ========================== + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/references/design.aux b/benchmarks/FPGA/FPGAPlacementOptimization/references/design.aux new file mode 100644 index 00000000..78a55a23 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/references/design.aux @@ -0,0 +1,2 @@ +# version 3.1 02/08/2016 +design : design.nodes design.nets design.wts design.pl design.scl design.lib diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/references/design.dcp b/benchmarks/FPGA/FPGAPlacementOptimization/references/design.dcp new file mode 100644 index 0000000000000000000000000000000000000000..bd1cdf4332689065ca6298c993ab34460f96e325 GIT binary patch literal 635315 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%0D? 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

LTOoY{RH+WJYWVVZ{V&CpT_z*hn@CxP( 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 z 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

!q363~f8i6;U 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


GdLhSI^QgKQjB*dl<(_@%=)GyvE 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

- -

- -The [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) reads in a flat placement solution and constructs a cluster-level netlist for [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s placer. The [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) also repairs any legality or mode-related errors in the clusters, thus allowing any external flat placer to integrate with [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s place and route tool. - -The placement solution from ``aug-elfPlace`` can be routed in [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s router and validated to evaluate overall performance. In addition, [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s annealing-based placer can further refine the ``aug-elfPlace`` solution to improve the overall quality of results. - -Please refer to our [paper](#publications) for more details on the performance of ``aug-elfPlace`` as part of the hybrid placement framework on the [Titan23 benchmarks](https://www.eecg.utoronto.ca/~kmurray/titan.html) benchmarks. - -### Target Architecture -``aug-elfPlace`` can target simplified versions of the Ultrascale and Stratix-IV architectures and requires the locations of the fixed input-output (IO) and phase-locked loop (PLL) blocks to be provided as part of the input, similar to [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA). - -

- -

- -FPGA architectures consist of DSP blocks and different memory blocks - BRAM, M9K, and M144K, with Slice blocks that consist of LUT, FF, and adder instances. -> Carry adder instances in the Ultrascale architecture are not shown in the Figure, as the benchmarks do not contain any. - -Slice blocks are configurable logic blocks (CLBs) in Ultrascale architecture and logic array blocks (LABs) in Stratix-IV architecture. The architectures differ significantly in the configuration of the Slice blocks and the legality constraints for LUTs and FFs. - -#### Simplified Ultrascale Architecture -- The [ISPD'2016 benchmarks](http://www.ispd.cc/contests/16/FAQ.html) targeting a simplified [AMD/Xilinx Ultrascale](https://docs.amd.com/v/u/en-US/ds890-ultrascale-overview) architecture. *``aug-elfPlace`` can be run on GPU and CPU for Ultrascale-like architectures.* - -#### Simplified Stratix-IV Architecture -- The [Titan23 benchmarks](https://www.eecg.utoronto.ca/~kmurray/titan.html) targeting a simplified [Intel/Altera Stratix-IV](https://www.intel.com/content/www/us/en/content-details/654799/stratix-iv-device-handbook.html) architecture. *Due to large packer-legalizer runtime for the Stratix-IV-like architecture, ``aug-elfPlace`` is run on CPU.* - -> Note: ``aug-elfPlace`` is not tested on the AMD/Xilinx Ultrascale+ or other architecture. - -## Publication(s) - -* Rachel Selina Rajarathnam, Kate Thurmer, Vaughn Betz, Mahesh A. Iyer, and [David Z. Pan](http://users.ece.utexas.edu/~dpan), "**Better Together: Combining Analytical and Annealing Methods for FPGA Placement**," *34th International Conference on Field-Programmable Logic and Applications (FPL)*, 2024 (accepted). - -## Developer(s) - -- Rachel Selina Rajarathnam, [UTDA](https://www.cerc.utexas.edu/utda), ECE Department, The University of Texas at Austin - -## Cloning the Repository - -External dependencies are the same as [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/9b86a09437e08947fb65c2a0cd351d004256bcc5?tab=readme-ov-file#dependencies). - -To pull git submodules in the root directory -``` -git submodule init -git submodule update -``` - -Alternatively, pull all the submodules when cloning the repository. -``` -git clone --recursive https://github.com/rachelselinar/aug-elfPlace.git -``` - -## Build Instructions - -### To install Python dependency - -There is an alternative way to install ``aug-elfPlace`` using Docker. If you want to use Docker, skip this step and go to [Docker installation](#Docker). - -At the root directory: -``` -pip install -r requirements.txt -``` -> For example, if the repository was cloned in directory ***~/Downloads***, then the root directory is ***~/Downloads/aug-elfPlace*** - -> You can also use a [python virtual environment](https://docs.python.org/3/library/venv.html) to install all the required packages to run ``aug-elfPlace`` - -### To install with Docker - -You can use the Docker container to avoid building all the dependencies yourself. - -1. Install Docker on [Linux](https://docs.docker.com/install/) (Win and Mac are not tested). -2. To enable the GPU features, install [NVIDIA-docker](https://github.com/NVIDIA/nvidia-docker); otherwise, skip this step. -3. Get the docker image using one of the options - Build the image locally. - ``` - docker build . --file Dockerfile --tag /dreamplacefpga:1.0 - ``` - Replace `` with a username, for instance, 'utda_placer.' -4. Enter the bash environment of the container. - Mount the repo and all the Designs into the Docker, which allows the Docker container to access and modify these files directly. - - To run on a Linux machine without GPU: - ``` - docker run -it -v $(pwd):/aug-elfPlace /dreamplacefpga:1.0 bash - ``` - To run on a Linux machine with GPU: (Docker verified on NVIDIA GPUs with compute capability 6.1, 7.5, and 8.0) - ``` - docker run --gpus 1 -it -v $(pwd):/aug-elfPlace /dreamplacefpga:1.0 bash - ``` - - For example, to run on a Linux machine without GPU: - ``` - docker run -it -v $(pwd):/aug-elfPlace utda_placer/dreamplacefpga:1.0 bash - ``` -5. Go to the `aug-elfPlace` directory in the Docker, which is the root directory of the project - ``` - cd /aug-elfPlace - ``` - - -### To Build - -At the root directory, -``` -mkdir build -cd build -cmake .. -DCMAKE_INSTALL_PREFIX=path_to_root_dir -make -make install -``` - -If you are using Docker, use the following at the root directory, - ``` -rm -rf build -mkdir build -cd build -cmake .. -DCMAKE_INSTALL_PREFIX=/aug-elfPlace -DPYTHON_EXECUTABLE=$(which python) -make -make install -``` - -Third-party submodules are automatically built except for [Boost](https://www.boost.org). - -> For example, - -> ***~/Downloads/aug-elfPlace:*** *mkdir build; cd build* - -> ***~/Downloads/aug-elfPlace/build:*** *cmake . . -DCMAKE_INSTALL_PREFIX=~/Downloads/aug-elfPlace* - -> ***~/Downloads/aug-elfPlace/build:*** *make; make install* - -> The directory ***~/Downloads/aug-elfPlace/build*** is the install dir - -When packages or parser code are changed, the contents of the ***build*** directory must be deleted for a clean build and proper operation. -``` -rm -r build -``` -> For example, - -> ***~/Downloads/aug-elfPlace:*** *rm -r build* - -For cmake options, refer to [DREAMPlaceFPGA](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/main?tab=readme-ov-file#cmake). - -## Benchmarks - -``aug-elfPlace`` only accepts inputs in the [Bookshelf](./benchmarks/sample_ispd2016_benchmarks/README) format and requires IO/PLL locations to be fixed. -- 12 designs for *AMD/Xilinx Ultrascale Architecture* in the updated bookshelf format with fixed IOs are provided from the [ISPD'2016 contest](http://www.ispd.cc/contests/16/FAQ.html). -- [Titan23](https://www.eecg.utoronto.ca/~kmurray/titan.html) designs based on the simplified *Intel/Altera Stratix-IV Architecture*, generated by [VPR](https://docs.verilogtorouting.org/en/latest/vpr/), are included in bookshelf format. - -All the designs are in the [benchmarks](./benchmarks) directory, and sample JSON configuration files are in the [test](./test) directory. For the complete list of available options in the JSON file, please refer to [paramsFPGA.json](./dreamplacefpga/paramsFPGA.json). - -## Running aug-elfPlace - -Before running, ensure that all python dependent packages have been installed. -Go to the ***root directory*** and run with the JSON configuration file. -``` -python dreamplacefpga/Placer.py .json -``` -> Run from ***~/Downloads/aug-elfPlace*** directory - -For example: -``` -python dreamplacefpga/Placer.py test/FPGA01.json -``` -> ***~/Downloads/aug-elfPlace:*** *python dreamplacefpga/Placer.py test/FPGA01.json* - -> If you are not using the GPU, change the gpu flag in the *.json file to 0. - -Unit tests for some of the pytorch operators are provided. For instance, to run the unit test for hpwl, use the below command: -``` -python unitest/ops/hpwl_unitest.py -``` -> Note: If your machine does not have an NVIDIA GPU, set the '***gpu***' flag in the JSON configuration file to '***0***' to run on the CPU. - -### Integration with [VPR](https://docs.verilogtorouting.org/en/latest/vpr/) -- Generate flat placement solution from ``aug-elfPlace`` in [VPR compatible format](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) as a *.pl* file: -``` - x y s z -``` -Where '*s*' refers to subtile location, set to zero '*s=0*' for IO/PLL instances, whereas '*z=0*' for DSP/memory/Slice instances. - -- Use [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place) to construct a cluster-level netlist for VPR after fixing any legality or mode-related failures. A '*.net*' clustered netlist and '*.fix_clusters*' placement file is generated from the VPR legalizer's output. - -- To validate the placement solution without refinement in VPR, use the '*.fix_clusters*' file generated as '*.place*' input placement file to VPR. For details, refer to this [comment](https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/2484#issuecomment-1938993673). VPR router is run on the input placement followed by validation. - -To refine the placement solution in VPR, list only IO/PLL instances in the '*.fix_clusters*' file and let the VPR placer refine the placement before routing and validation. -> Note: Integration with VPR is verified only for the Titan23 benchmarks. - -## Bug Report - -Please file an [issue](https://github.com/rachelselinar/aug-elfPlace/issues) to report a bug. - -## Copyright - -This software is released under a BSD 3-Clause "New" or "Revised" License. Please refer to [LICENSE](./LICENSE) for details. - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake deleted file mode 100644 index 1a768e32..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake +++ /dev/null @@ -1,81 +0,0 @@ -# - Try to find Cairo -# Once done, this will define -# -# CAIRO_FOUND - system has Cairo -# CAIRO_INCLUDE_DIRS - the Cairo include directories -# CAIRO_LIBRARIES - link these to use Cairo -# -# Copyright (C) 2012 Raphael Kubo da Costa -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS -# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -FIND_PACKAGE(PkgConfig) -PKG_CHECK_MODULES(PC_CAIRO cairo) # FIXME: After we require CMake 2.8.2 we can pass QUIET to this call. - -FIND_PATH(CAIRO_INCLUDE_DIRS - NAMES cairo.h - HINTS ${PC_CAIRO_INCLUDEDIR} - ${PC_CAIRO_INCLUDE_DIRS} - PATH_SUFFIXES cairo -) - -FIND_LIBRARY(CAIRO_LIBRARIES - NAMES cairo - HINTS ${PC_CAIRO_LIBDIR} - ${PC_CAIRO_LIBRARY_DIRS} -) - -IF (CAIRO_INCLUDE_DIRS) - IF (EXISTS "${CAIRO_INCLUDE_DIRS}/cairo-version.h") - FILE(READ "${CAIRO_INCLUDE_DIRS}/cairo-version.h" CAIRO_VERSION_CONTENT) - - STRING(REGEX MATCH "#define +CAIRO_VERSION_MAJOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") - SET(CAIRO_VERSION_MAJOR "${CMAKE_MATCH_1}") - - STRING(REGEX MATCH "#define +CAIRO_VERSION_MINOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") - SET(CAIRO_VERSION_MINOR "${CMAKE_MATCH_1}") - - STRING(REGEX MATCH "#define +CAIRO_VERSION_MICRO +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}") - SET(CAIRO_VERSION_MICRO "${CMAKE_MATCH_1}") - - SET(CAIRO_VERSION "${CAIRO_VERSION_MAJOR}.${CAIRO_VERSION_MINOR}.${CAIRO_VERSION_MICRO}") - ENDIF () -ENDIF () - -# FIXME: Should not be needed anymore once we start depending on CMake 2.8.3 -SET(VERSION_OK TRUE) -IF (Cairo_FIND_VERSION) - IF (Cairo_FIND_VERSION_EXACT) - IF ("${Cairo_FIND_VERSION}" VERSION_EQUAL "${CAIRO_VERSION}") - # FIXME: Use IF (NOT ...) with CMake 2.8.2+ to get rid of the ELSE block - ELSE () - SET(VERSION_OK FALSE) - ENDIF () - ELSE () - IF ("${Cairo_FIND_VERSION}" VERSION_GREATER "${CAIRO_VERSION}") - SET(VERSION_OK FALSE) - ENDIF () - ENDIF () -ENDIF () - -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cairo DEFAULT_MSG CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES VERSION_OK) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake deleted file mode 100644 index 8c7fbfa2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake +++ /dev/null @@ -1,132 +0,0 @@ -# @file TorchExtension.cmake -# @author Zizheng Guo -# @brief Use CMake to compile PyTorch extensions - -add_subdirectory(thirdparty/pybind11) - -execute_process(COMMAND ${PYTHON_EXECUTABLE} -c - "import torch; print(torch.__path__[0]); print(int(torch.cuda.is_available())); print(torch.__version__);" - OUTPUT_VARIABLE TORCH_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) -string(REPLACE "\n" ";" TORCH_OUTPUT_LIST ${TORCH_OUTPUT}) -list(GET TORCH_OUTPUT_LIST 0 TORCH_INSTALL_PREFIX) -list(GET TORCH_OUTPUT_LIST 1 TORCH_ENABLE_CUDA) -list(GET TORCH_OUTPUT_LIST 2 TORCH_VERSION) -string(REPLACE "." ";" TORCH_VERSION_LIST ${TORCH_VERSION}) -list(GET TORCH_VERSION_LIST 0 TORCH_VERSION_MAJOR) -list(GET TORCH_VERSION_LIST 1 TORCH_VERSION_MINOR) - -message(STATUS TORCH_INSTALL_PREFIX=${TORCH_INSTALL_PREFIX}) -message(STATUS TORCH_VERSION=${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}) - -if ("${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}" VERSION_LESS 1.6) - message(SEND_ERROR "require PyTorch version >=1.6") -#elseif ("${TORCH_VERSION_MAJOR}.${TORCH_VERSION_MINOR}" VERSION_GREATER_EQUAL 1.8) -# message(SEND_ERROR "require PyTorch version < 1.8") -endif() - -if (TORCH_ENABLE_CUDA) - find_package(CUDA 9.0) - if (NOT CUDA_FOUND) - set(TORCH_ENABLE_CUDA 0 CACHE BOOL "Whether enable CUDA" FORCE) - endif(NOT CUDA_FOUND) -endif() -message(STATUS TORCH_ENABLE_CUDA=${TORCH_ENABLE_CUDA}) - -add_library(torch STATIC IMPORTED) -find_library(TORCH_PYTHON_LIBRARY torch_python PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) -find_library(TORCH_LIBRARY torch PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) -find_library(C10_LIBRARY c10 PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) -find_library(C10_CUDA_LIBRARY c10_cuda PATHS "${TORCH_INSTALL_PREFIX}/lib") -find_library(TORCH_CPU_LIBRARY torch_cpu PATHS "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) -find_library(TORCH_CUDA_LIBRARY torch_cuda PATHS "${TORCH_INSTALL_PREFIX}/lib") - -if (EXISTS ${TORCH_INSTALL_PREFIX}/include) - # torch version 1.4+ - set(TORCH_HEADER_PREFIX ${TORCH_INSTALL_PREFIX}/include) -elseif (EXISTS ${TORCH_INSTALL_PREFIX}/lib/include) - # torch version 1.0 - set(TORCH_HEADER_PREFIX ${TORCH_INSTALL_PREFIX}/lib/include) -endif() -set(TORCH_INCLUDE_DIRS - ${TORCH_HEADER_PREFIX} - ${TORCH_HEADER_PREFIX}/torch/csrc/api/include) - -set(LINK_LIBS ${C10_LIBRARY} ${TORCH_CPU_LIBRARY}) -if (TORCH_ENABLE_CUDA) - set(LINK_LIBS ${LINK_LIBS} - ${C10_CUDA_LIBRARY} - ${TORCH_CUDA_LIBRARY}) -endif() - -set_target_properties(torch PROPERTIES - IMPORTED_LOCATION "${TORCH_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${TORCH_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${LINK_LIBS}" - INTERFACE_COMPILE_OPTIONS "-D_GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}" - ) - -# CXX only -function(add_torch_extension target_name) - set(multiValueArgs EXTRA_INCLUDE_DIRS EXTRA_LINK_LIBRARIES EXTRA_DEFINITIONS) - cmake_parse_arguments(ARG "" "" "${multiValueArgs}" ${ARGN}) - if (TORCH_ENABLE_CUDA) - cuda_add_library(${target_name} STATIC ${ARG_UNPARSED_ARGUMENTS}) - else() - # remove cuda files - list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cu$") - list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cuh$") - add_library(${target_name} STATIC ${ARG_UNPARSED_ARGUMENTS}) - endif() - target_include_directories(${target_name} PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) - target_link_libraries(${target_name} ${ARG_EXTRA_LINK_LIBRARIES} torch pybind11::module) - target_compile_definitions(${target_name} PRIVATE - TORCH_EXTENSION_NAME=${target_name} - TORCH_VERSION_MAJOR=${TORCH_VERSION_MAJOR} - TORCH_VERSION_MINOR=${TORCH_VERSION_MINOR} - ENABLE_CUDA=${TORCH_ENABLE_CUDA} - ${ARG_EXTRA_DEFINITIONS}) - set_target_properties(${target_name} PROPERTIES - POSITION_INDEPENDENT_CODE ON - CXX_VISIBILITY_PRESET "hidden" - CUDA_VISIBILITY_PRESET "hidden" - ) -endfunction() - -function(add_pytorch_extension target_name) - set(multiValueArgs EXTRA_INCLUDE_DIRS EXTRA_LINK_LIBRARIES EXTRA_DEFINITIONS) - cmake_parse_arguments(ARG "" "" "${multiValueArgs}" ${ARGN}) - if (TORCH_ENABLE_CUDA) - set(CUDA_SRCS "${ARG_UNPARSED_ARGUMENTS}") - list(FILTER CUDA_SRCS INCLUDE REGEX ".*cu$") - if (CUDA_SRCS) - cuda_add_library(${target_name}_cuda_tmp STATIC ${CUDA_SRCS}) - target_include_directories(${target_name}_cuda_tmp PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) - target_link_libraries(${target_name}_cuda_tmp ${ARG_EXTRA_LINK_LIBRARIES}) - target_compile_definitions(${target_name}_cuda_tmp PRIVATE - TORCH_EXTENSION_NAME=${target_name} - TORCH_MAJOR_VERSION=${TORCH_MAJOR_VERSION} - TORCH_MINOR_VERSION=${TORCH_MINOR_VERSION} - ENABLE_CUDA=${TORCH_ENABLE_CUDA} - ${ARG_EXTRA_DEFINITIONS}) - set_target_properties(${target_name}_cuda_tmp PROPERTIES - POSITION_INDEPENDENT_CODE ON - CXX_VISIBILITY_PRESET "hidden" - CUDA_VISIBILITY_PRESET "hidden" - ) - endif() - endif() - list(FILTER ARG_UNPARSED_ARGUMENTS EXCLUDE REGEX ".*cu$") - pybind11_add_module(${target_name} MODULE ${ARG_UNPARSED_ARGUMENTS}) - target_include_directories(${target_name} PRIVATE ${ARG_EXTRA_INCLUDE_DIRS}) - if (TORCH_ENABLE_CUDA AND CUDA_SRCS) - target_link_libraries(${target_name} PRIVATE ${target_name}_cuda_tmp ${ARG_EXTRA_LINK_LIBRARIES} torch ${TORCH_PYTHON_LIBRARY}) - else() - target_link_libraries(${target_name} PRIVATE ${ARG_EXTRA_LINK_LIBRARIES} torch ${TORCH_PYTHON_LIBRARY}) - endif() - target_compile_definitions(${target_name} PRIVATE - TORCH_EXTENSION_NAME=${target_name} - TORCH_VERSION_MAJOR=${TORCH_VERSION_MAJOR} - TORCH_VERSION_MINOR=${TORCH_VERSION_MINOR} - ENABLE_CUDA=${TORCH_ENABLE_CUDA} - ${ARG_EXTRA_DEFINITIONS}) -endfunction() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py deleted file mode 100644 index f6f28eb2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py +++ /dev/null @@ -1,781 +0,0 @@ -## -# @file BasicPlace.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief Base placement class -# - -import os -import sys -import time -import gzip -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle -import re -import numpy as np -import logging -import torch -import torch.nn as nn -import dreamplacefpga.ops.move_boundary.move_boundary as move_boundary -import dreamplacefpga.ops.hpwl.hpwl as hpwl -import dreamplacefpga.ops.electric_potential.electric_overflow as electric_overflow -import dreamplacefpga.ops.draw_place.draw_place as draw_place -import dreamplacefpga.ops.pin_pos.pin_pos as pin_pos -import dreamplacefpga.ops.precondWL.precondWL as precondWL -import dreamplacefpga.ops.demandMap.demandMap as demandMap -import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin as sortNode2Pin -import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization as lut_ff_legalization -import pdb - -datatypes = { - 'float32' : torch.float32, - 'float64' : torch.float64 - } - -class PlaceDataCollectionFPGA(object): - """ - @brief A wraper for all data tensors on device for building ops - """ - def __init__(self, pos, params, placedb, device): - """ - @brief initialization - @param pos locations of cells - @param params parameters - @param placedb placement database - @param device cpu or cuda - """ - self.device = device - self.dtype = datatypes[params.dtype] - torch.set_num_threads(params.num_threads) - # position should be parameter - self.pos = pos - - with torch.no_grad(): - # other tensors required to build ops - - self.node_size_x = torch.from_numpy(placedb.node_size_x).to(device) - self.node_size_y = torch.from_numpy(placedb.node_size_y).to(device) - self.resource_size_x = torch.from_numpy(placedb.resource_size_x).to(device) - self.resource_size_y = torch.from_numpy(placedb.resource_size_y).to(device) - self.node_x = torch.from_numpy(placedb.node_x).to(device) - self.node_y = torch.from_numpy(placedb.node_y).to(device) - self.node_z = torch.from_numpy(placedb.node_z.astype(np.int32)).to(device) - self.site_type_map = torch.from_numpy(placedb.site_type_map.astype(np.int32)).to(device) - self.lg_siteXYs = torch.from_numpy(placedb.lg_siteXYs).to(device) - - if params.routability_opt_flag: - self.original_node_size_x = self.node_size_x.clone() - self.original_node_size_y = self.node_size_y.clone() - - self.pin_offset_x = torch.from_numpy(placedb.pin_offset_x).to(device) - self.pin_offset_y = torch.from_numpy(placedb.pin_offset_y).to(device) - self.lg_pin_offset_x = torch.from_numpy(placedb.lg_pin_offset_x).to(device) - self.lg_pin_offset_y = torch.from_numpy(placedb.lg_pin_offset_y).to(device) - - # original pin offset for legalization, since they will be adjusted in global placement - if params.routability_opt_flag: - self.original_pin_offset_x = self.pin_offset_x.clone() - self.original_pin_offset_y = self.pin_offset_y.clone() - - self.node_areas = self.node_size_x * self.node_size_y - self.movable_macro_mask = None - - self.pin2node_map = torch.from_numpy(placedb.pin2node_map).to(device) - self.flat_node2pin_map = torch.from_numpy(placedb.flat_node2pin_map).to(device) - self.flat_node2pin_start_map = torch.from_numpy(placedb.flat_node2pin_start_map).to(device) - self.node2outpinIdx_map = torch.from_numpy(placedb.node2outpinIdx_map).to(device) - self.node2pincount_map = torch.from_numpy(placedb.node2pincount_map).to(device) - self.net2pincount_map = torch.from_numpy(placedb.net2pincount_map).to(device) - - # number of pins for each cell - self.pin_weights = (self.flat_node2pin_start_map[1:] - - self.flat_node2pin_start_map[:-1]).to( - self.dtype) - ## Resource type masks - self.flop_mask = torch.from_numpy(placedb.flop_mask).to(device) - self.lut_mask = torch.from_numpy(placedb.lut_mask).to(device) - if placedb.sDSPIdx != -1: - self.dsp_mask = torch.from_numpy(placedb.dsp_mask).to(device) - if placedb.sBRAMIdx != -1 or placedb.sM9KIdx != -1: - self.ram0_mask = torch.from_numpy(placedb.ram0_mask).to(device) - if placedb.sM144KIdx != -1: - self.ram1_mask = torch.from_numpy(placedb.ram1_mask).to(device) - self.flop_lut_mask = self.flop_mask | self.lut_mask - - if placedb.sBRAMIdx == -1: - self.dsp_ram_mask = self.dsp_mask | self.ram0_mask | self.ram1_mask - else: - self.dsp_ram_mask = self.dsp_mask | self.ram0_mask - - self.io_mask = torch.from_numpy(placedb.io_mask).to(device) - self.fixed_rsrcIds = torch.from_numpy(placedb.fixed_rsrcIds).to(dtype=torch.int32,device=device) - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - self.is_mlab_node = torch.from_numpy(placedb.is_mlab_node).to(device) - - #LUT type list - self.lut_type = torch.from_numpy(placedb.lut_type).to(dtype=torch.int32,device=device) - self.cluster_lut_type = torch.from_numpy(placedb.cluster_lut_type).to(dtype=torch.int32,device=device) - self.pin_typeIds = torch.from_numpy(placedb.pin_typeIds).to(dtype=torch.int32,device=device) - - #FF control sets - self.flop_ctrlSets = torch.from_numpy(placedb.flat_ctrlSets).to(dtype=torch.int32,device=device) - #FF to ctrlset ID - self.flop2ctrlSetId_map = torch.from_numpy(placedb.flop2ctrlSetId_map).to(dtype=torch.int32,device=device) - #Spiral accessor for legalization - self.spiral_accessor = torch.from_numpy(placedb.spiral_accessor).to(dtype=torch.int32,device=device) - #Resource type indexing - self.flop_indices = torch.from_numpy(placedb.flop_indices).to(dtype=torch.int32,device=device) - self.lut_indices = torch.nonzero(self.lut_mask, as_tuple=True)[0].to(dtype=torch.int32) - self.flop_lut_indices = torch.nonzero(self.flop_lut_mask, as_tuple=True)[0].to(dtype=torch.int32) - self.dsp_ram_indices = torch.nonzero(self.dsp_ram_mask, as_tuple=True)[0].to(dtype=torch.int32) - self.pin_weights[self.flop_mask] = params.ffPinWeight - self.unit_pin_capacity = torch.empty(1, dtype=self.dtype, device=device) - self.unit_pin_capacity.data.fill_(params.unit_pin_capacity) - - # routing information - # project initial routing utilization map to one layer - self.initial_horizontal_utilization_map = None - self.initial_vertical_utilization_map = None - if params.routability_opt_flag and placedb.initial_horizontal_demand_map is not None: - self.initial_horizontal_utilization_map = torch.from_numpy( - placedb.initial_horizontal_demand_map).to(device).div_( - placedb.routing_grid_size_y * - placedb.unit_horizontal_capacity) - self.initial_vertical_utilization_map = torch.from_numpy( - placedb.initial_vertical_demand_map).to(device).div_( - placedb.routing_grid_size_x * - placedb.unit_vertical_capacity) - - self.pin2net_map = torch.from_numpy(placedb.pin2net_map.astype(np.int32)).to(device) - self.flat_net2pin_map = torch.from_numpy(placedb.flat_net2pin_map).to(device) - self.flat_net2pin_start_map = torch.from_numpy(placedb.flat_net2pin_start_map).to(device) - if np.amin(placedb.net_weights) == np.amax(placedb.net_weights): # empty tensor - logging.warning("net weights are all the same, ignored") - #self.net_weights = torch.Tensor().to(device) - self.net_weights = torch.from_numpy(placedb.net_weights).to(device) - - # regions - self.region_boxes = [torch.tensor(region).to(device) for region in placedb.region_boxes] - self.flat_region_boxes = torch.from_numpy( - placedb.flat_region_boxes).to(device) - self.flat_region_boxes_start = torch.from_numpy( - placedb.flat_region_boxes_start).to(device) - self.node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map).to(device) - - self.num_nodes = torch.tensor(placedb.num_nodes, dtype=torch.int32, device=device) - self.num_movable_nodes = torch.tensor(placedb.num_movable_nodes, dtype=torch.int32, device=device) - self.num_filler_nodes = torch.tensor(placedb.num_filler_nodes, dtype=torch.int32, device=device) - self.num_physical_nodes = torch.tensor(placedb.num_physical_nodes, dtype=torch.int32, device=device) - self.filler_start_map = torch.from_numpy(placedb.filler_start_map).to(device) - - ## this is for overflow op - self.total_movable_node_area_fence_region = torch.from_numpy(placedb.total_movable_node_area_fence_region).to(device) - ## this is for gamma update - self.num_movable_nodes_fence_region = torch.from_numpy(placedb.num_movable_nodes_fence_region).to(device) - ## this is not used yet - self.num_filler_nodes_fence_region = torch.from_numpy(placedb.num_filler_nodes_fence_region).to(device) - - self.net_mask_all = torch.from_numpy(np.ones(placedb.num_nets,dtype=np.uint8)).to(device) # all nets included - net_degrees = np.array([len(net2pin) for net2pin in placedb.net2pin_map]) - net_mask = np.logical_and(2 <= net_degrees, - net_degrees < params.ignore_net_degree).astype(np.uint8) - self.net_mask_ignore_large_degrees = torch.from_numpy(net_mask).to(device) # nets with large degrees are ignored - - # For WL computation - self.net_bounding_box_min = torch.zeros(placedb.num_nets * 2, dtype=self.dtype, device=self.device) - self.net_bounding_box_max = torch.zeros_like(self.net_bounding_box_min) - - # avoid computing gradient for fixed macros - # 1 is for fixed macros - IOs - self.pin_mask_ignore_fixed_macros = (self.pin2node_map >= placedb.num_movable_nodes) - - # sort nodes by size, return their sorted indices, designed for memory coalesce in electrical force - movable_size_x = self.node_size_x[:placedb.num_movable_nodes] - _, self.sorted_node_map = torch.sort(movable_size_x) - self.sorted_node_map = self.sorted_node_map.to(torch.int32) - - self.targetOverflow = torch.from_numpy(placedb.targetOverflow).to(dtype=self.dtype, device=device) - self.node_area_adjust_overflow = torch.from_numpy(placedb.node_area_adjust_overflow).to(dtype=self.dtype, device=device) - - #Filler start/end for FF and LUT for resource area update - self.ff_filler_start = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rFFIdx]] - self.ff_filler_end = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rFFIdx]+1] - self.lut_filler_start = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rLUTIdx]] - self.lut_filler_end = placedb.filler_start_map[placedb.rsrc2compId_map[placedb.rLUTIdx]+1] - - #Carry chain nodes as single entity - if placedb.num_ccNodes > 0: - self.org_node_x = torch.from_numpy(placedb.org_node_x).to(device) - self.org_node_y = torch.from_numpy(placedb.org_node_y).to(device) - self.org_node_z = torch.from_numpy(placedb.org_node_z.astype(np.int32)).to(device) - org_flop_lut_mask = torch.from_numpy(placedb.org_lut_flop_mask).to(device) - self.org_flop_lut_indices = torch.nonzero(org_flop_lut_mask, as_tuple=True)[0].to(dtype=torch.int32) - self.org_is_mlab_node = torch.from_numpy(placedb.org_is_mlab_node).to(device) - self.org_flop2ctrlSetId_map = torch.from_numpy(placedb.org_flop2ctrlSetId_map).to(dtype=torch.int32,device=device) - self.org_flop_ctrlSets = torch.from_numpy(placedb.flat_org_ctrlSets).to(dtype=torch.int32,device=device) - self.org_pin2node_map = torch.from_numpy(placedb.org_pin2node_map).to(device) - self.org_flat_node2pin_map = torch.from_numpy(placedb.org_flat_node2pin_map).to(device) - self.org_flat_node2pin_start_map = torch.from_numpy(placedb.org_flat_node2pin_start_map).to(device) - self.org_node2outpinIdx_map = torch.from_numpy(placedb.org_node2outpinIdx_map).to(device) - self.org_node2pincount_map = torch.from_numpy(placedb.org_node2pincount_map).to(device) - self.org_node2fence_region_map = torch.from_numpy(placedb.org_node2fence_region_map).to(device) - self.org_lut_type = torch.from_numpy(placedb.org_lut_type).to(dtype=torch.int32,device=device) - self.org_lg_pin_offset_x = torch.from_numpy(placedb.org_lg_pin_offset_x).to(device) - self.org_lg_pin_offset_y = torch.from_numpy(placedb.org_lg_pin_offset_y).to(device) - self.org_node_size_x = torch.from_numpy(placedb.org_node_size_x).to(device) - self.org_node_size_y = torch.from_numpy(placedb.org_node_size_y).to(device) - self.org_node_areas = self.org_node_size_x * self.org_node_size_y - -class PlaceOpCollectionFPGA(object): - """ - @brief A wrapper for all ops - """ - def __init__(self): - """ - @brief initialization - """ - self.demandMap_op = None - self.pin_pos_op = None - self.move_boundary_op = None - self.hpwl_op = None - self.precondwl_op = None - self.wirelength_op = None - self.update_gamma_op = None - self.density_op = None - self.update_density_weight_op = None - self.lg_precondition_op = None - self.noise_op = None - self.draw_place_op = None - self.route_utilization_map_op = None - self.pin_utilization_map_op = None - self.clustering_compatibility_lut_area_op= None - self.clustering_compatibility_ff_area_op= None - self.adjust_node_area_op = None - self.sort_node2pin_op = None - self.lut_ff_legalization_op = None - -class BasicPlaceFPGA(nn.Module): - """ - @brief Base placement class. - All placement engines should be derived from this class. - """ - def __init__(self, params, placedb): - """ - @brief initialization - @param params parameter - @param placedb placement database - """ - torch.manual_seed(params.random_seed) - super(BasicPlaceFPGA, self).__init__() - - #Assign carry chain net weighting if specified - if params.cc_net_weight: - placedb.carry_chain_net_weight = params.cc_net_weight - else: - placedb.carry_chain_net_weight = 1.0 - - ################################################### - ##IDENTIFY IF THERE ARE CARRY CHAINS IN THE DESIGN - ################################################### - if placedb.num_ccNodes == 0: - #nodes_with_carry_chain = np.zeros(placedb.num_physical_nodes, dtype=np.int32) - placedb.carry_chain_driver = np.ones(placedb.num_physical_nodes, dtype=np.int32) - placedb.carry_chain_driver *= -1 - placedb.carry_chain_sink = np.ones_like(placedb.carry_chain_driver) - placedb.carry_chain_sink *= -1 - placedb.carry_chain_nets = np.ones(placedb.num_nets, dtype=np.int32) - placedb.carry_chain_nets *= -1 - - lut_indices = np.nonzero(placedb.lut_mask)[0].astype(np.int32) - #Check for carry chains if cout-cin connections exist - if 30 in placedb.pin_typeIds and 31 in placedb.pin_typeIds: - #Obtain carry chain information - for instId in lut_indices: - pinIdBeg = placedb.flat_node2pin_start_map[instId] - pinIdEnd = placedb.flat_node2pin_start_map[instId+1] - for pinId in range(pinIdBeg, pinIdEnd, 1): - outPinId = placedb.flat_node2pin_map[pinId] - if placedb.pin_typeIds[outPinId] != 30: continue - outNetId = placedb.pin2net_map[outPinId] - pinIdxBeg = placedb.flat_net2pin_start_map[outNetId] - pinIdxEnd = placedb.flat_net2pin_start_map[outNetId+1] - for pinId in range(pinIdxBeg, pinIdxEnd, 1): - pinIdx = placedb.flat_net2pin_map[pinId] - nodeIdx = placedb.pin2node_map[pinIdx] - if placedb.pin_typeIds[pinIdx] == 31 and nodeIdx != instId: - placedb.net_weights[outNetId] = placedb.carry_chain_net_weight - placedb.carry_chain_nets[outNetId] = placedb.carry_chain_net_weight - placedb.carry_chain_sink[instId] = nodeIdx - placedb.carry_chain_driver[nodeIdx] = instId - ccd = placedb.carry_chain_driver > -1 - ccs = placedb.carry_chain_sink > -1 - #placedb.nodes_with_carry_chain=np.logical_or(ccd, ccs) - #placedb.nodes_cc_start=np.logical_and(~ccd, ccs) - #placedb.non_root_cc_nodes=np.logical_and(placedb.nodes_with_carry_chain,~placedb.nodes_cc_start) - #carry_chain_nodeIds = np.where(np.logical_or(ccd, ccs))[0].astype(np.int32) - #Instance ids that are the start of carry chains - placedb.carry_chain_start = np.where(np.logical_and(~ccd, ccs))[0].astype(np.int32) - placedb.num_carry_chains = placedb.carry_chain_start.shape[0] - - ##placedb.node_area = placedb.node_size_x * placedb.node_size_y - node_cc_id=np.ones(placedb.num_physical_nodes, dtype=np.int32) - node_cc_id*=-1 - node_cc_id[placedb.carry_chain_start]=np.arange(placedb.num_carry_chains) - - #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - ####Get number of nodes in each carry chain - #cc_element_count=np.zeros(placedb.num_carry_chains, dtype=np.int32) - #for el in placedb.carry_chain_start: - # ccId=node_cc_id[el] - # #Get node info - # cc_element_count[ccId]=cc_element_count[ccId]+1 - # sink_node=placedb.carry_chain_sink[el] - # while sink_node > -1: - # #Get node info - # cc_element_count[ccId]=cc_element_count[ccId]+1 - # #Next sink - # sink_node=placedb.carry_chain_sink[sink_node] - #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - - flat_cc2node_map = [] - flat_cc2node_start_map = [] - - flat_cc2node_start_map.append(0) - for el in placedb.carry_chain_start: - ccId=node_cc_id[el] - #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - #header="carry " + placedb.node_names[el] + " " + str(cc_element_count[ccId]) - #midportion="\t" + placedb.node_names[el] + "\n" - #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - flat_cc2node_map.append(el) - sink_node=placedb.carry_chain_sink[el] - while sink_node > -1: - flat_cc2node_map.append(sink_node) - #### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - #midportion=midportion+"\t" + placedb.node_names[sink_node] + "\n" - #### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - sink_node=placedb.carry_chain_sink[sink_node] - flat_cc2node_start_map.append(len(flat_cc2node_map)) - ### PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - #footer="endcarry" - ##TODO - Uncomment below 3 lines to generate carry chain information - #print(header) - #print(midportion) - #print(footer) - ### END PRINT CARRY CHAINS INFO AS DESIGN.CC FILE - placedb.flat_cc2node_map = np.array(flat_cc2node_map, dtype=np.int32) - placedb.flat_cc2node_start_map = np.array(flat_cc2node_start_map, dtype=np.int32) - - if placedb.num_carry_chains > 0: - logging.info("There are %d carry chains across %d nodes and %d nets" % - (placedb.num_carry_chains, placedb.flat_cc2node_map.shape[0], (placedb.carry_chain_nets > -1).sum())) - else: - placedb.num_carry_chains = placedb.num_ccNodes - ################################################### - ##END OF CARRY CHAINS IDENTIFICATION - ################################################### - - - ## Random Initial Placement - self.init_pos = np.zeros(placedb.num_nodes * 2, dtype=placedb.dtype) - - ##Settings to ensure reproduciblity - manualSeed = 0 - np.random.seed(manualSeed) - torch.manual_seed(manualSeed) - if params.gpu: - torch.cuda.manual_seed(manualSeed) - torch.cuda.manual_seed_all(manualSeed) - - numPins = 0 - initLocX = 0 - initLocY = 0 - - if placedb.num_terminals > 0: - ##Use the average fixed pin location (weighted by pin count) as the initial location - for nodeID in range(placedb.num_movable_nodes,placedb.num_physical_nodes): - for pID in placedb.node2pin_map[nodeID]: - initLocX += placedb.node_x[nodeID] + placedb.pin_offset_x[pID] - initLocY += placedb.node_y[nodeID] + placedb.pin_offset_y[pID] - numPins += len(placedb.node2pin_map[nodeID]) - initLocX /= numPins - initLocY /= numPins - else: ##Design does not have IO pins - place in center - initLocX = 0.5 * (placedb.xh - placedb.xl) - initLocY = 0.5 * (placedb.yh - placedb.yl) - - # x position - self.init_pos[0:placedb.num_physical_nodes] = placedb.node_x - if params.global_place_flag and params.random_center_init_flag: # move to centroid of layout - #logging.info("Move cells to the centroid of fixed IOs with random noise") - self.init_pos[0:placedb.num_movable_nodes] = np.random.normal( - loc = initLocX, - scale = min(placedb.xh - placedb.xl, placedb.yh - placedb.yl) * 0.001, - size = placedb.num_movable_nodes) - self.init_pos[0:placedb.num_movable_nodes] -= (0.5 * placedb.node_size_x[0:placedb.num_movable_nodes]) - - # y position - self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes] = placedb.node_y - if params.global_place_flag and params.random_center_init_flag: # move to center of layout - self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes] = np.random.normal( - loc = initLocY, - scale = min(placedb.xh - placedb.xl, placedb.yh - placedb.yl) * 0.001, - size = placedb.num_movable_nodes) - self.init_pos[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes] -= (0.5 * placedb.node_size_y[0:placedb.num_movable_nodes]) - #logging.info("Random Init Place in python takes %.2f seconds" % (time.time() - tt)) - - if placedb.num_filler_nodes: # uniformly distribute filler cells in the layout - ### uniformly spread fillers in fence region - ### for cells in the fence region - for idx in range(placedb.regions): - i = placedb.rsrc2compId_map[idx] - if i != -1: - region = placedb.region_boxes[i] - #Construct Nx4 np array for region using placedb.flat_region_boxes - filler_beg, filler_end = placedb.filler_start_map[i:i+2] - if filler_end-filler_beg > 0: - num_region_fillers = filler_end-filler_beg - subregion_areas = (region[:,2]-region[:,0])*(region[:,3]-region[:,1]) - total_area = np.sum(subregion_areas) - subregion_area_ratio = subregion_areas / total_area - subregion_num_filler = np.floor((filler_end - filler_beg) * subregion_area_ratio) - rem_fillers = num_region_fillers - int(subregion_num_filler.sum()) - subregion_num_filler[:rem_fillers] += 1 - #subregion_num_filler[-1] = (filler_end - filler_beg) - np.sum(subregion_num_filler[:-1]) - subregion_num_filler_start_map = np.concatenate([np.zeros([1]),np.cumsum(subregion_num_filler)],0).astype(np.int32) - for j, subregion in enumerate(region): - sub_filler_beg, sub_filler_end = subregion_num_filler_start_map[j:j+2] - self.init_pos[placedb.num_physical_nodes+filler_beg+sub_filler_beg:placedb.num_physical_nodes+filler_beg+sub_filler_end]=np.random.uniform( - low=subregion[0], - high=subregion[2] - - placedb.filler_size_x_fence_region[i], - size=sub_filler_end-sub_filler_beg) - self.init_pos[placedb.num_nodes+placedb.num_physical_nodes+filler_beg+sub_filler_beg:placedb.num_nodes+placedb.num_physical_nodes+filler_beg+sub_filler_end]=np.random.uniform( - low=subregion[1], - high=subregion[3] - - placedb.filler_size_y_fence_region[i], - size=sub_filler_end-sub_filler_beg) - #Skip for IOs - else: - continue - - #logging.info("Random Init Place in Python takes %.2f seconds" % (time.time() - t2)) - - self.device = torch.device("cuda" if params.gpu else "cpu") - - # position should be parameter - # must be defined in BasicPlace - #tbp = time.time() - self.pos = nn.ParameterList( - [nn.Parameter(torch.from_numpy(self.init_pos).to(self.device))]) - #logging.info("build pos takes %.2f seconds" % (time.time() - tbp)) - # shared data on device for building ops to avoid constructing data from placedb again and again - #tt = time.time() - self.data_collections = PlaceDataCollectionFPGA(self.pos, params, placedb, self.device) - #logging.info("build data_collections takes %.2f seconds" % - # (time.time() - tt)) - - # All ops are wrapped - #tt = time.time() - self.op_collections = PlaceOpCollectionFPGA() - #logging.info("build op_collections takes %.2f seconds" % - # (time.time() - tt)) - - tt = time.time() - # Demand Map computation - self.op_collections.demandMap_op = self.build_demandMap(params, placedb, self.data_collections, self.device) - # position to pin position - self.op_collections.pin_pos_op = self.build_pin_pos(params, placedb, self.data_collections, self.device) - # bound nodes to layout region - self.op_collections.move_boundary_op = self.build_move_boundary(params, placedb, self.data_collections, self.device) - # hpwl and density overflow ops for evaluation - self.op_collections.hpwl_op = self.build_hpwl(params, placedb, self.data_collections, self.op_collections.pin_pos_op, self.device) - # WL preconditioner - self.op_collections.precondwl_op = self.build_precondwl(params, placedb, self.data_collections, self.device) - self.op_collections.lg_precondition_op = self.build_LGprecondwl(params, placedb, self.data_collections, self.device) - # Sorting node2pin map - self.op_collections.sort_node2pin_op = self.build_sortNode2Pin(params, placedb, self.data_collections, self.device) - # rectilinear minimum steiner tree wirelength from flute - # can only be called once - self.op_collections.density_overflow_op = self.build_electric_overflow(params, placedb, self.data_collections, self.device) - - ##Legalization - self.op_collections.lut_ff_legalization_op = self.build_lut_ff_legalization(params, placedb, self.data_collections, self.device) - - # draw placement - self.op_collections.draw_place_op = self.build_draw_placement(params, placedb) - - #logging.info("build BasicPlace ops takes %.2f seconds" % - # (time.time() - tt)) - - def __call__(self, params, placedb): - """ - @brief Solve placement. - placeholder for derived classes. - @param params parameters - @param placedb placement database - """ - pass - - def build_pin_pos(self, params, placedb, data_collections, device): - """ - @brief sum up the pins for each cell - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - # Yibo: I found CPU version of this is super slow, more than 2s for ISPD2005 bigblue4 with 10 threads. - # So I implemented a custom CPU version, which is around 20ms - #pin2node_map = data_collections.pin2node_map.long() - #def build_pin_pos_op(pos): - # pin_x = data_collections.pin_offset_x.add(torch.index_select(pos[0:placedb.num_physical_nodes], dim=0, index=pin2node_map)) - # pin_y = data_collections.pin_offset_y.add(torch.index_select(pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes], dim=0, index=pin2node_map)) - # pin_pos = torch.cat([pin_x, pin_y], dim=0) - - # return pin_pos - #return build_pin_pos_op - - return pin_pos.PinPos( - pin_offset_x=data_collections.pin_offset_x, - pin_offset_y=data_collections.pin_offset_y, - pin2node_map=data_collections.pin2node_map, - flat_node2pin_map=data_collections.flat_node2pin_map, - flat_node2pin_start_map=data_collections.flat_node2pin_start_map, - num_physical_nodes=placedb.num_physical_nodes, - num_threads=params.num_threads, - algorithm="node-by-node") - - def build_move_boundary(self, params, placedb, data_collections, device): - """ - @brief bound nodes into layout region - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - return move_boundary.MoveBoundary( - data_collections.node_size_x, - data_collections.node_size_y, - xl=placedb.xl, - yl=placedb.yl, - xh=placedb.xh, - yh=placedb.yh, - num_movable_nodes=placedb.num_movable_nodes, - num_filler_nodes=placedb.num_filler_nodes, - num_threads=params.num_threads) - - def build_hpwl(self, params, placedb, data_collections, pin_pos_op, device): - """ - @brief compute half-perimeter wirelength - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param pin_pos_op the op to compute pin locations according to cell locations - @param device cpu or cuda - """ - wirelength_for_pin_op = hpwl.HPWL( - placedb=placedb, - flat_netpin=data_collections.flat_net2pin_map, - netpin_start=data_collections.flat_net2pin_start_map, - pin2net_map=data_collections.pin2net_map, - net_weights=data_collections.net_weights, - num_carry_chains=placedb.num_carry_chains, - cc_net_weight=placedb.carry_chain_net_weight, - dir_net_weight=params.dir_net_weight, - #net_mask=data_collections.net_mask_all, - net_mask=data_collections.net_mask_ignore_large_degrees, - net_bounding_box_min=data_collections.net_bounding_box_min, - net_bounding_box_max=data_collections.net_bounding_box_max, - num_threads=params.num_threads, - algorithm='net-by-net') - - # wirelength for position - def build_wirelength_op(pos): - return wirelength_for_pin_op(pin_pos_op(pos)) - - return build_wirelength_op - - def build_demandMap(self, params, placedb, data_collections, device): - """ - @brief Build binCapMap and fixedDemandMap - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - return demandMap.DemandMap( - placedb=placedb, - site_type_map=data_collections.site_type_map, - site_size_x=data_collections.resource_size_x, - site_size_y=data_collections.resource_size_y, - deterministic_flag=params.deterministic_flag, - device=device, - num_threads=params.num_threads) - - def build_precondwl(self, params, placedb, data_collections, device): - """ - @brief compute wirelength precondtioner - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - return precondWL.PrecondWL( - flat_node2pin_start=data_collections.flat_node2pin_start_map, - flat_node2pin=data_collections.flat_node2pin_map, - pin2net_map=data_collections.pin2net_map, - flat_net2pin=data_collections.flat_net2pin_start_map, - net_weights=data_collections.net_weights, - num_nodes=placedb.num_nodes, - num_movable_nodes=placedb.num_physical_nodes,#Compute for fixed nodes as well for Legalization - device=device, - num_threads=params.num_threads) - - def build_LGprecondwl(self, params, placedb, data_collections, device): - """ - @brief compute wirelength precondtioner - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - if placedb.num_ccNodes > 0: - return precondWL.PrecondWL( - flat_node2pin_start=data_collections.org_flat_node2pin_start_map, - flat_node2pin=data_collections.org_flat_node2pin_map, - pin2net_map=data_collections.pin2net_map, - flat_net2pin=data_collections.flat_net2pin_start_map, - net_weights=data_collections.net_weights, - num_nodes=placedb.org_num_physical_nodes + placedb.num_filler_nodes, - num_movable_nodes=placedb.org_num_physical_nodes,#Compute for fixed nodes as well for Legalization - device=device, - num_threads=params.num_threads) - - def build_sortNode2Pin(self, params, placedb, data_collections, device): - """ - @brief sort instance node2pin mapping - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - #Only used for LG - if placedb.num_ccNodes == 0: - return sortNode2Pin.SortNode2Pin( - flat_node2pin_start=data_collections.flat_node2pin_start_map, - flat_node2pin=data_collections.flat_node2pin_map, - num_nodes=placedb.num_physical_nodes, - device=device, - num_threads=params.num_threads) - else: - return sortNode2Pin.SortNode2Pin( - flat_node2pin_start=data_collections.org_flat_node2pin_start_map, - flat_node2pin=data_collections.org_flat_node2pin_map, - num_nodes=placedb.org_num_physical_nodes, - device=device, - num_threads=params.num_threads) - - def build_electric_overflow(self, params, placedb, data_collections, device): - """ - @brief compute electric density overflow - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - return electric_overflow.ElectricOverflow( - node_size_x=data_collections.node_size_x, - node_size_y=data_collections.node_size_y, - xl=placedb.xl, - yl=placedb.yl, - xh=placedb.xh, - yh=placedb.yh, - bin_size_x=placedb.bin_size_x, - bin_size_y=placedb.bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=0, - deterministic_flag=params.deterministic_flag, - sorted_node_map=data_collections.sorted_node_map) - - - def build_lut_ff_legalization(self, params, placedb, data_collections, device): - """ - @brief legalization of LUT/FF Instances - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param device cpu or cuda - """ - # legalize LUT/FF - ###Avg areas - ##avgLUTArea = data_collections.node_areas[:placedb.num_physical_nodes][placedb.lut_mask].sum() - ##avgLUTArea /= placedb.node_count[placedb.rLUTIdx] - ##avgFFArea = data_collections.node_areas[:placedb.num_physical_nodes][placedb.flop_mask].sum() - ##avgFFArea /= placedb.node_count[placedb.rFFIdx] - ###Inst Areas - ##inst_areas = data_collections.node_areas[:placedb.num_physical_nodes].detach().clone() - ##inst_areas[~placedb.lut_flop_mask] = 0.0 #Area of non SLICE nodes set to 0.0 - ##inst_areas[placedb.lut_mask] /= avgLUTArea - ##inst_areas[placedb.flop_mask] /= avgFFArea - #Site types - site_types = data_collections.site_type_map.detach().clone() - site_types[site_types != placedb.sSLICEIdx] = 0 #Set non SLICE to 0 - - if (len(data_collections.net_weights)): - net_wts = data_collections.net_weights - else: - net_wts = torch.ones(placedb.num_nets, dtype=self.pos[0].dtype, device=device) - - return lut_ff_legalization.LegalizeCLB( - data_collections=data_collections, - placedb=placedb, - net_wts=net_wts, - #inst_areas=inst_areas, - site_types=site_types, - num_threads=params.num_threads, - device=device) - - def build_draw_placement(self, params, placedb): - """ - @brief plot placement - @param params parameters - @param placedb placement database - """ - return draw_place.DrawPlaceFPGA(placedb) - - def validate(self, placedb, pos, iteration): - """ - @brief validate placement - @param placedb placement database - @param pos locations of cells - @param iteration optimization step - """ - pos = torch.from_numpy(pos).to(self.device) - hpwl = self.op_collections.hpwl_op(pos) - overflow, max_density = self.op_collections.density_overflow_op(pos) - - return hpwl, overflow, max_density - - def plot(self, params, placedb, iteration, pos): - """ - @brief plot layout - @param params parameters - @param placedb placement database - @param iteration optimization step - @param pos locations of cells - """ - tt = time.time() - path = "%s/%s" % (params.result_dir, params.design_name()) - figname = "%s/plot/iter%s.png" % (path, '{:04}'.format(iteration)) - os.system("mkdir -p %s" % (os.path.dirname(figname))) - if isinstance(pos, np.ndarray): - pos = torch.from_numpy(pos) - self.op_collections.draw_place_op(pos, figname) - logging.info("plotting to %s takes %.3f seconds" % - (figname, time.time() - tt)) - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt deleted file mode 100644 index c3ec0149..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_subdirectory(ops) - -file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py" "${CMAKE_CURRENT_SOURCE_DIR}/paramsFPGA.json") -install( - FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py deleted file mode 100644 index d2b05ce3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py +++ /dev/null @@ -1,198 +0,0 @@ -## -# @file EvalMetrics.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief Evaluation metrics -# - -import time -import torch -import pdb - -class EvalMetrics (object): - """ - @brief evaluation metrics at one step - """ - def __init__(self, iteration=None, detailed_step=None): - """ - @brief initialization - @param iteration optimization step - """ - self.iteration = iteration - self.detailed_step = detailed_step - self.objective = None - self.wirelength = None - self.density = None - self.density_weight = None - self.hpwl = None - self.rmst_wl = None - self.overflow = None - self.route_utilization = None - self.pin_utilization = None - self.max_density = None - self.gamma = None - self.eval_time = None - - def __str__(self): - """ - @brief convert to string - """ - content = "" - if self.iteration is not None: - content = "iter: %4d" % (self.iteration) - #if self.detailed_step is not None: - # content += ", (%4d, %2d, %2d)" % (self.detailed_step[0], self.detailed_step[1], self.detailed_step[2]) - #if self.objective is not None: - # content += ", Obj %.6E" % (self.objective) - #if self.wirelength is not None: - # content += ", WL %.3E" % (self.wirelength) - #if self.density is not None: - # content += ", Density [%s]" % ", ".join(["%.3E" % i for i in self.density]) - #if self.density_weight is not None: - # content += ", DensityWeight [%s]" % ", ".join(["%.3E" % i for i in self.density_weight]) - if self.hpwl is not None: - content += ", HPWL %.6E" % (self.hpwl) - if self.overflow is not None: - content += ", Overflow [%s]" % ", ".join(["%.3E" % i for i in self.overflow]) - #if self.max_density is not None: - # content += ", MaxDensity [%s]" % ", ".join(["%.3E" % i for i in self.max_density]) - #if self.route_utilization is not None: - # content += ", RouteOverflow %.6E" % (self.route_utilization) - #if self.pin_utilization is not None: - # content += ", PinOverflow %.6E" % (self.pin_utilization) - #if self.gamma is not None: - # content += ", gamma %.6E" % (self.gamma) - if self.eval_time is not None: - content += ", time %.3fms" % (self.eval_time*1000) - - return content - - def __repr__(self): - """ - @brief print - """ - return self.__str__() - - def evaluate(self, placedb, ops, var, data_collections=None): - """ - @brief evaluate metrics - @param placedb placement database - @param ops a list of ops - @param var variables - """ - tt = time.time() - with torch.no_grad(): - if "objective" in ops: - self.objective = ops["objective"](var).data - if "wirelength" in ops: - self.wirelength = ops["wirelength"](var).data - if "density" in ops: - self.density = ops["density"](var).data - if "hpwl" in ops: - self.hpwl = ops["hpwl"](var).data - if "rmst_wls" in ops: - rmst_wls = ops["rmst_wls"](var) - self.rmst_wl = rmst_wls.sum().data - if "overflow" in ops: - overflow, max_density = ops["overflow"](var) - if(overflow.numel() == 1): - self.overflow = overflow.data / placedb.total_movable_node_area - self.max_density = max_density.data - else: - self.overflow = overflow.data / data_collections.total_movable_node_area_fence_region - self.max_density = max_density.data - if "goverflow" in ops: - overflow, max_density = ops["goverflow"](var) - self.goverflow = overflow.data / placedb.total_movable_node_area - self.gmax_density = max_density.data - if "route_utilization" in ops: - route_utilization_map = ops["route_utilization"](var) - route_utilization_map_sum = route_utilization_map.sum() - self.route_utilization = route_utilization_map.sub_(1).clamp_(min=0).sum() / route_utilization_map_sum - if "pin_utilization" in ops: - pin_utilization_map = ops["pin_utilization"](var) - pin_utilization_map_sum = pin_utilization_map.sum() - self.pin_utilization = pin_utilization_map.sub_(1).clamp_(min=0).sum() / pin_utilization_map_sum - self.eval_time = time.time()-tt - -#Added by Rachel -class EvalMetricsFPGA (object): - """ - @brief evaluation metrics at one step - """ - def __init__(self, iteration=None, detailed_step=None): - """ - @brief initialization - @param iteration optimization step - """ - self.iteration = iteration - self.detailed_step = detailed_step - self.objective = None - self.wirelength = None - self.density = None - self.density_weight = None - self.hpwl = None - self.overflow = None - self.max_density = None - self.gmax_density = None - self.gamma = None - self.eval_time = None - - def __str__(self): - """ - @brief convert to string - """ - content = "" - if self.iteration is not None: - content = "iter: %4d" % (self.iteration) - #if self.detailed_step is not None: - # content += ", (%4d, %2d, %2d)" % (self.detailed_step[0], self.detailed_step[1], self.detailed_step[2]) - #if self.objective is not None: - # content += ", Obj %.6E" % (self.objective) - #if self.wirelength is not None: - # content += ", WL %.3E" % (self.wirelength) - #if self.density is not None: - # content += ", Density [%s]" % ", ".join(["%.3E" % i for i in self.density]) - #if self.density_weight is not None: - # content += ", DensityWeight [%s]" % ", ".join(["%.3E" % i for i in self.density_weight]) - if self.hpwl is not None: - content += ", HPWL %.6E" % (self.hpwl) - if self.overflow is not None: - content += ", Overflow [%s]" % ", ".join(["%.3E" % i for i in self.overflow]) - #if self.max_density is not None: - # content += ", MaxDensity [%s]" % ", ".join(["%.3E" % i for i in self.max_density]) - #if self.gamma is not None: - # content += ", gamma %.6E" % (self.gamma) - if self.eval_time is not None: - content += ", time %.3fms" % (self.eval_time*1000) - - return content - - def __repr__(self): - """ - @brief print - """ - return self.__str__() - - def evaluate(self, placedb, ops, var, data_collections=None): - """ - @brief evaluate metrics - @param placedb placement database - @param ops a list of ops - @param var variables - """ - tt = time.time() - with torch.no_grad(): - if "objective" in ops: - self.objective = ops["objective"](var).data - if "wirelength" in ops: - self.wirelength = ops["wirelength"](var).data - if "density" in ops: - self.density = ops["density"](var).data - if "hpwl" in ops: - self.hpwl = ops["hpwl"](var).data - if "overflow" in ops: - overflow, max_density = ops["overflow"](var) - self.overflow = overflow.data / data_collections.total_movable_node_area_fence_region.clamp(min=1) - self.max_density = max_density.data - self.eval_time = time.time()-tt diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py deleted file mode 100644 index 76633dbc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py +++ /dev/null @@ -1,2152 +0,0 @@ -## -# @file IFWriter.py -# @author Zhili Xiong -# @date Dec 2022 -# @brief Convert bookshelf outputs to interchange .phys file. -# - -import capnp -import capnp.lib.capnp -capnp.remove_import_hook() - -import os -import sys -import time -import gzip -import enum -import numpy as np -import logging -import Params -import dreamplacefpga -import dreamplacefpga.ops.place_io.place_io as place_io -import pdb -from collections import namedtuple -from Params import * -from PlaceDB import * - - -NO_TRAVERSAL_LIMIT = 2**63 - 1 - -NESTING_LIMIT = 1024 - -PhysicalNet = namedtuple('PhysicalNet', 'name type sources stubs stubNodes') - - -class IFWriter(): - """ IFWirter calss to write out IF file from phys_netlist. - phys_netlist - object input, contains all the data of each field in PhysicalNetlist.capnp - """ - def __init__(self, schema_dir): - """ initialize and compile PhysicalNetlist.capnp """ - import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] - # import_path.append(os.path.join(schema_dir, '../../schema')) - import_path.append('IFsupport') - self.physical_netlist_capnp = capnp.load(os.path.join(schema_dir, 'PhysicalNetlist.capnp'), imports=import_path) - self.strList = [] - self.str2idx = {} - - def StringIdx(self, string): - """ build strList and return the index """ - if string not in self.str2idx: - self.str2idx[string] = len(self.strList) - self.strList.append(string) - - return self.str2idx[string] - - def build_IF(self, phys_netlist): - """ build IF object from phys_netlist - - phys_netlist - object input, contains all the data of each field in PhysicalNetlist.capnp - struct PhysNetlist { - part @0 : Text; - placements @1 : List(CellPlacement); - physNets @2 : List(PhysNet); - physCells @3 : List(PhysCell); - strList @4 : List(Text) $hashSet(); - siteInsts @5 : List(SiteInstance); - properties @6 : List(Property); - nullNet @7 : PhysNet; - } - """ - # initialize a new capnp object - physical_netlist = self.physical_netlist_capnp.PhysNetlist.new_message() - - # part name in textStringIdx - # part @0 : Text; - physical_netlist.part = phys_netlist.part - - # List of placement - # placements @1 : List(CellPlacement); - placements = physical_netlist.init('placements', len(phys_netlist.placements)) - for i in range(len(phys_netlist.placements)): - # struct CellPlacement { - # cellName @0 : StringIdx $stringRef(); - # type @1 : StringIdx $stringRef(); - # site @2 : StringIdx $stringRef(); - # bel @3 : StringIdx $stringRef(); - # pinMap @4 : List(PinMapping); - # otherBels @5 : List(StringIdx) $stringRef(); - # isBelFixed @6 : Bool; - # isSiteFixed @7 : Bool; - # altSiteType @8 : StringIdx $stringRef(); - # } - placement = phys_netlist.placements[i] - # cellName - placements[i].cellName = self.StringIdx(placement.cell_name) - # type - placements[i].type = self.StringIdx(placement.cell_type) - # site - placements[i].site = self.StringIdx(placement.site_name) - # bel - placements[i].bel = self.StringIdx(placement.bel_name) - # pinMap - pinMap = placements[i].init('pinMap', len(placement.pins)) - for j in range(len(placement.pins)): - # struct PinMapping { - # cellPin @0 : StringIdx $stringRef(); - # bel @1 : StringIdx $stringRef(); - # belPin @2 : StringIdx $stringRef(); - # isFixed @3 : Bool; - # union { - # multi @4 : Void; - # otherCell @5 : MultiCellPinMapping; - # } - pins = placement.pins[j] - # cellPin - pinMap[j].cellPin = self.StringIdx(pins[0]) - pinMap[j].bel = self.StringIdx(pins[1]) - pinMap[j].belPin = self.StringIdx(pins[2]) - pinMap[j].isFixed = False - - placements[i].isBelFixed = True - placements[i].isSiteFixed = True - placements[i].altSiteType = self.StringIdx('SLICE_X57Y129') - - # siteInsts - site_insts = physical_netlist.init('siteInsts', len(phys_netlist.siteInsts)) - for idx, (key, value) in enumerate(phys_netlist.siteInsts.items()): - site_insts[idx].site = self.StringIdx(key) - site_insts[idx].type = self.StringIdx(value) - - # physCells - phys_cells = physical_netlist.init('physCells', len(phys_netlist.physCells)) - for idx, (cell_name, cell_type) in enumerate(phys_netlist.physCells.items()): - phys_cells[idx].cellName = self.StringIdx(cell_name) - phys_cells[idx].physType = self.physical_netlist_capnp.PhysNetlist.PhysCellType.__dict__[ - cell_type.lower()] - - # properties - properties = physical_netlist.init('properties', 2) - properties[0].key = self.StringIdx('DISABLE_AUTO_IO_BUFFERS') - properties[0].value = self.StringIdx('0') - properties[1].key = self.StringIdx('OUT_OF_CONTEXT') - properties[1].value = self.StringIdx('0') - - # nullNet - physical_netlist.nullNet.name = self.StringIdx('SLICE_X57Y129') - - # PhysNet - nets = physical_netlist.init('physNets', len(phys_netlist.nets)) - - for idx, net in enumerate(phys_netlist.nets): - net_obj = nets[idx] - - net_obj.name = self.StringIdx(net.name) - net_obj.init('sources', len(net.sources)) - for root_obj, root in zip(net_obj.sources, net.sources): - root.output_interchange(root_obj, self.StringIdx) - - net_obj.init('stubs', len(net.stubs)) - for stub_obj, stub in zip(net_obj.stubs, net.stubs): - stub.output_interchange(stub_obj, self.StringIdx) - - net_obj.type = self.physical_netlist_capnp.PhysNetlist.NetType.__dict__[ - net.type.name.lower()] - - # strList - physical_netlist.init('strList', len(self.strList)) - - for idx, s in enumerate(self.strList): - physical_netlist.strList[idx] = s - - return physical_netlist - - def write_IF(self, physical_netlist, if_file): - """ Write out IF into file - The file name is the design_name - """ - # zipped the file into gzip file - with gzip.open(if_file, 'wb') as f_zip: - f_zip.write(physical_netlist.to_bytes()) - - -class Cellplacement(): - """ Cellplacement class for constructing placement of a single cell. - struct CellPlacement { - cellName @0 : StringIdx $stringRef(); - type @1 : StringIdx $stringRef(); - site @2 : StringIdx $stringRef(); - bel @3 : StringIdx $stringRef(); - pinMap @4 : List(PinMapping); - otherBels @5 : List(StringIdx) $stringRef(); - isBelFixed @6 : Bool; - isSiteFixed @7 : Bool; - altSiteType @8 : StringIdx $stringRef(); - } - """ - def __init__(self, cell_name, cell_type, site_name, bel_name): - """ initialize placement information """ - self.cell_name = cell_name - self.cell_type = cell_type - self.site_name = site_name - self.bel_name = bel_name - self.pins = [] - - - def add_pins(self, cellpin, belpin): - """ add pins for cell - struct PinMapping { - cellPin @0 : StringIdx $stringRef(); - bel @1 : StringIdx $stringRef(); - belPin @2 : StringIdx $stringRef(); - isFixed @3 : Bool; - union { - multi @4 : Void; - otherCell @5 : MultiCellPinMapping; - } - """ - self.pins.append((cellpin, self.bel_name, belpin)) - - -def add_branch(branch_obj, phys_node, string_idx): - """ Add a branch to continue outputting the interchange to capnp object. - - branch_obj - One RouteBranch capnp object from PhysicalNetlist - phys_node - an object of PhysicalBelpin or PhysicalSitepip - - """ - branch_obj.init('branches', len(phys_node.branches)) - - for branch_obj, branch in zip(branch_obj.branches, phys_node.branches): - branch.output_interchange(branch_obj, string_idx) - - -class PhysicalBelPin(): - """ PhysicalBelpin class for intra-site routing. - struct PhysBelPin { - site @0 : StringIdx $stringRef(); - bel @1 : StringIdx $stringRef(); - pin @2 : StringIdx $stringRef(); - } - """ - - def __init__(self, site_name, bel_name, pin_name): - self.site_name = site_name - self.bel_name = bel_name - self.pin_name = pin_name - - self.branches = [] - - def output_interchange(self, branch_obj, string_idx): - """ Add one route segment and all the branches under it. - branch_obj - One RouteBranch capnp object from PhysicalNetlist - - struct RouteBranch { - routeSegment : union { - belPin @0 : PhysBelPin; - sitePin @1 : PhysSitePin; - pip @2 : PhysPIP; - sitePIP @3 : PhysSitePIP; - } - branches @4 : List(RouteBranch); - } - string_idx - function that returns the index of strList in PhysicalNetlist - """ - branch_obj.routeSegment.init('belPin') - branch_obj.routeSegment.belPin.site = string_idx(self.site_name) - branch_obj.routeSegment.belPin.bel = string_idx(self.bel_name) - branch_obj.routeSegment.belPin.pin = string_idx(self.pin_name) - - add_branch(branch_obj, self, string_idx) - - def get_device_resource(self, site_types, device_resources): - """ Get device resource that corresponds to this class. """ - return device_resources.bel_pin(self.site_name, site_types[self.site_name], - self.bel_name, self.pin_name) - - def to_tuple(self): - """ Create tuple suitable for sorting this object. - This tuple is used for sorting against other routing branch objects - to generate a canonical routing tree. - """ - return ('bel_pin', self.site_name, self.bel_name, self.pin_name) - - -class PhysicalSitePip(): - """ PhysicalSitepip class for intra-site routing. - struct PhysSitePIP { - site @0 : StringIdx $stringRef(); - bel @1 : StringIdx $stringRef(); - pin @2 : StringIdx $stringRef(); - isFixed @3 : Bool; - union { - isInverting @4 : Bool; - inverts @5 : Void; - } - } - """ - - def __init__(self, site_name, bel_name, pin_name): - self.site_name = site_name - self.bel_name = bel_name - self.pin_name = pin_name - self.is_inverting = False - - self.branches = [] - - def output_interchange(self, branch_obj, string_idx): - """ Add one route segment and all the branches under it. - """ - branch_obj.routeSegment.init('sitePIP') - branch_obj.routeSegment.sitePIP.site = string_idx(self.site_name) - branch_obj.routeSegment.sitePIP.bel = string_idx(self.bel_name) - branch_obj.routeSegment.sitePIP.pin = string_idx(self.pin_name) - branch_obj.routeSegment.sitePIP.isFixed = False - branch_obj.routeSegment.sitePIP.isInverting = False - - add_branch(branch_obj, self, string_idx) - - def get_device_resource(self, site_types, device_resources): - """ Get device resource that corresponds to this class. """ - return device_resources.site_pip(self.site_name, site_types[self.site_name], - self.bel_name, self.pin_name) - - def to_tuple(self): - """ Create tuple suitable for sorting this object. - This tuple is used for sorting against other routing branch objects - to generate a canonical routing tree. - """ - return ('site_pip', self.site_name, self.bel_name, self.pin_name, self.is_inverting) - - -def convert_tuple_to_object(site, tup): - """ Convert physical netlist tuple to object. - Physical netlist tuples are light weight ways to represent the physical - net tree. - site (Site) - Site object that tuple belongs too. - tup (tuple) - Tuple that is either a bel pin or site pip. - Returns - PhysicalBelPin or PhysicalSitePip based on - tuple. - """ - if tup[0] == 'bel_pin': - _, bel, pin = tup - return PhysicalBelPin(site.name, bel, pin) - elif tup[0] == 'site_pip': - _, bel, pin = tup - return PhysicalSitePip(site.name, bel, pin) - else: - return False - - -def add_site_routing_children(site, parent_obj, parent_key, site_routing): - """ Convert site_routing map into Physical* python objects. - site (Site) - Site object that contains site routing. - parent_obj (Physical* python object) - Parent Physical* object to add new - branches too. - parent_key (tuple) - Site routing tuple for current parent_obj. - site_routing (dict) - Map of parent site routing tuple to a set of - child site routing tuples. - inverted_root (list) - List of physical net sources for the inverted - signal (e.g. a constant 1 net inverts to the - constant 0 net) - """ - if parent_key in site_routing: - for child in site_routing[parent_key]: - - obj = convert_tuple_to_object(site, child) - parent_obj.branches.append(obj) - - add_site_routing_children(site, obj, child, site_routing) - - - -def create_site_routing(site, net_roots, site_routing): - """ Convert site_routing into map of nets to site local sources. - site (Site) - SiteInst object that contains site routing. - net_roots (dict) - Map of root site routing tuples to the net name for - this root. - site_routing (dict) - Map of parent site routing tuple to a set of - child site routing tuples. - Returns dict of nets to Physical* objects that represent the site local - sources for that net. - """ - nets = {} - - for root, net_name in net_roots.items(): - if net_name not in nets: - nets[net_name] = [] - - root_obj = convert_tuple_to_object(site, root) - add_site_routing_children(site, root_obj, root, site_routing) - - - nets[net_name].append(root_obj) - - return nets - -class SiteInst(): - """ - This class has the site router and site instances; - """ - def __init__(self, name): - self.name = name - self.cells = {} - self.net_belpins = {} - self.lut_map = {} - - def add_cells(self, node_name, cellplacement): - """ map from a node name in bookshelf to a list of cellplacement objetcs. """ - if node_name not in self.cells: - self.cells[node_name] = [] - - self.cells[node_name].append(cellplacement) - - def add_belpins(self, net_name, belpin_tup): - """ map from a net name to a list of belpin tuples """ - if net_name not in self.net_belpins: - self.net_belpins[net_name] = [] - - self.net_belpins[net_name].append(belpin_tup) - - def A6_LUT(self): - """ sorted luts """ - - vcc_tup = [] - A6_sitein = { - 'A': ('bel_pin', 'A6', 'A6'), - 'B': ('bel_pin', 'B6', 'B6'), - 'C': ('bel_pin', 'C6', 'C6'), - 'D': ('bel_pin', 'D6', 'D6'), - 'E': ('bel_pin', 'E6', 'E6'), - 'F': ('bel_pin', 'F6', 'F6'), - 'G': ('bel_pin', 'G6', 'G6'), - 'H': ('bel_pin', 'H6', 'H6') - } - self.lut_map = { - 'A': [], - 'B': [], - 'C': [], - 'D': [], - 'E': [], - 'F': [], - 'G': [], - 'H': [] - } - - for node, cellplacements in self.cells.items(): - for cell in cellplacements: - if cell.bel_name[-3:] == 'LUT': - self.lut_map[cell.bel_name[0]].append(cell) - - for key, cells in self.lut_map.items(): - if len(self.lut_map[key]) == 2 and '/' not in self.lut_map[key][0].cell_name: - vcc_tup.append(A6_sitein[key]) - - return vcc_tup - - - def site_router(self, routing_graph, site_in, site_out): - - nets = {} - net_roots = {} - site_routing = {} - net_source = {} - constant_nets = { - 0 : 'GLOBAL_LOGIC0', - 1 : 'GLOBAL_LOGIC1' - } - - clk_net = 'clk_BUFGP_net_top_wire' - - gnd_tup = ('bel_pin', 'HARD0GND', '0') - - # add the us+ rst - rst_tup = [('bel_pin', 'SRST_B1', 'SRST_B1'), ('bel_pin', 'SRST_B2', 'SRST_B2'), ('bel_pin', 'SRST1', 'SRST1'), ('bel_pin', 'SRST2', 'SRST2')] - - bufce_tup = [('bel_pin', 'CE_PRE_OPTINV', 'CE_PRE_OPTINV')] - - vcc_tup = self.A6_LUT() - - for net_name, belpins in self.net_belpins.items(): - - is_source = False - stubs = [] - skip_flag = False - - for belpin in belpins: - # the net has its source in this site - # belpin is source - if belpin in routing_graph: - net_roots[belpin] = net_name - site_routing[belpin] = [] - net_source[net_name] = belpin - is_source = True - - bel_name = belpin[1] - if bel_name[-4:] == '6LUT' and len(self.lut_map[bel_name[0]]) == 2: - skip_flag = True - - if skip_flag == False: - for child_tup in routing_graph[belpin]: - # through site pip - if child_tup[0] == 'site_pip': - sitepip_in = child_tup - sitepip_out = routing_graph[sitepip_in] - for out in routing_graph[sitepip_out]: - if out in site_out: - site_routing[belpin].append(sitepip_in) - site_routing[sitepip_in] = [] - site_routing[sitepip_in].append(sitepip_out) - site_routing[sitepip_out] = [] - - # belpin is stub - else: - stubs.append(belpin) - - for stub in stubs: - parent_found = False - - for parent in site_routing: - if stub in routing_graph[parent]: - site_routing[parent].append(stub) - parent_found = True - - if parent_found == True: - continue - - if is_source == True: - source = net_source[net_name] - # site wire - if stub in routing_graph[source]: - site_routing[source].append(stub) - parent_found = True - - # sitepip - else: - for child in routing_graph[source]: - if child[0] == 'site_pip': - sitepip_in = child - sitepip_out = routing_graph[sitepip_in] - if stub in routing_graph[sitepip_out]: - site_routing[source].append(sitepip_in) - site_routing[sitepip_in] = [] - site_routing[sitepip_in].append(sitepip_out) - site_routing[sitepip_out] = [] - site_routing[sitepip_out].append(stub) - parent_found = True - - if parent_found == True: - continue - - for site_pin in site_in: - site_in_found = False - # site wire - if stub in routing_graph[site_pin]: - net_roots[site_pin] = net_name - site_routing[site_pin] = [] - site_routing[site_pin].append(stub) - site_in_found = True - - # site pip - else: - for pin in routing_graph[site_pin]: - if pin[0] == 'site_pip': - sitepip_in = pin - sitepip_out = routing_graph[sitepip_in] - if stub in routing_graph[sitepip_out] : - net_roots[site_pin] = net_name - site_routing[site_pin] = [] - site_routing[site_pin].append(sitepip_in) - site_routing[sitepip_in] = [] - site_routing[sitepip_in].append(sitepip_out) - site_routing[sitepip_out] = [] - site_routing[sitepip_out].append(stub) - site_in_found = True - - if site_in_found == True: - break - - if self.name[:5] == 'SLICE': - # for rst_in in rst_tup: - # if rst_in not in site_routing and rst_in in site_in: - # net_roots[rst_in] = constant_nets[1] - # child = routing_graph[rst_in][0] - # site_routing[rst_in] = [] - # site_routing[rst_in].append(child) - # child2 = routing_graph[child] - # site_routing[child2] = [] - # net_roots[child2] = constant_nets[0] - - net_roots[gnd_tup] = constant_nets[0] - site_routing[gnd_tup] = [] - - - for vcc_in in vcc_tup: - net_roots[vcc_in] = constant_nets[1] - site_routing[vcc_in] = [] - - if self.name[:3] == 'DSP': - dsp_bel_map = {} - dsp_pin_map = {} - dsp_cell_pin = {} - for node_name in self.cells: - for cellplacement in self.cells[node_name]: - cell_name = cellplacement.cell_name - bel_name = cellplacement.bel_name - dsp_bel_map[bel_name] = node_name - for pin in cellplacement.pins: - pin_name = pin[0].replace('[', '<') - pin_name = pin_name.replace(']', '>') - dsp_pin_map[pin[2]] = pin_name - if pin[2][:7] == 'PATTERN': - cell_pin = pin[0].replace('_','') - dsp_cell_pin[pin[2]] = cell_pin - else: - dsp_cell_pin[pin[2]] = pin[0] - - - for bel_out in routing_graph: - if bel_out not in site_routing and bel_out[1] in dsp_bel_map: - site_routing[bel_out] = [] - for bel_in in routing_graph[bel_out]: - if bel_in not in site_out: - net_name = dsp_bel_map[bel_out[1]] + '/' + bel_out[1] + '.' + dsp_pin_map[bel_out[2]] - site_routing[bel_out].append(bel_in) - net_roots[bel_out] = net_name - else: - if bel_out[2] in dsp_cell_pin: - net_name = dsp_bel_map[bel_out[1]] + '/' + dsp_cell_pin[bel_out[2]] - net_roots[bel_out] = net_name - - - if self.name[:3] == 'RAM': - if gnd_tup in routing_graph: - net_roots[gnd_tup] = constant_nets[1] - site_routing[gnd_tup] = [] - for gnd_child in routing_graph[gnd_tup]: - site_routing[gnd_tup].append(gnd_child) - - - nets = create_site_routing(self, net_roots, site_routing) - - - return nets, net_source - - -class PhysicalNetType(enum.Enum): - # Net is just a signal, not a VCC or GND tied net. - Signal = 0 - # Net is tied to GND. - Gnd = 1 - # Net is tied to VCC. - Vcc = 2 - -class PhysicalNetlist: - """ Physical Netlist class for adding each field into an object - self.add_cellplacement() - self.add_site_instance() - self.add_physical_cell() - """ - def __init__(self, part): - self.part = part - self.placements = [] - self.nets = [] - self.physCells = {} - self.siteInsts = {} - self.null_nets = [] - - def add_cellplacement(self, cellplacement): - """ Add cellplacement into a list - cellplacement (object) - object of cellplacement - """ - self.placements.append(cellplacement) - - def add_site_instance(self, site_name, site_type): - """ Add site instance to a map """ - self.siteInsts[site_name] = site_type - - def add_physical_cell(self, cell_name, cell_type): - """ Add physical cell instance - cell_name (str) - Name of physical cell instance - cell_type (str) - Value of physical_netlist.PhysCellType - PhysicalSitePip - """ - self.physCells[cell_name] = cell_type - - def add_physical_net(self, - net_name, - sources, - stubs, - stubNodes, - net_type=PhysicalNetType.Signal): - """ Adds a physical net to the physical netlist. - net_name (str) - Name of net. - sources (list of - physical_netlist.PhysicalBelPin - or - - physical_netlist.PhysicalSitePin - or - - physical_netlist.PhysicalSitePip - or - - physical_netlist.PhysicalPip - ) - Sources of this net. - stubs (list of - physical_netlist.PhysicalBelPin - or - - physical_netlist.PhysicalSitePin - or - - physical_netlist.PhysicalSitePip - or - - physical_netlist.PhysicalPip - ) - Stubs of this net. - net_type (PhysicalNetType) - Type of net. - """ - self.nets.append( - PhysicalNet( - name=net_name, type=net_type, sources=sources, stubs=stubs, stubNodes=stubNodes)) - -class CellBel(): - """ Map cell into bel, mainly for the pin mapping. - strs - strList from DeviceResources - mapping - cellBelMap from DeviceResources - """ - def __init__(self, strs, mapping): - """ Build a pin map from cell pin to bel pin. - - Now only used the common pins. - - Find a cell's pin mapping by its [site_type, bel] - - """ - self.cell = strs[mapping.cell] - self.site_types_and_bels = set() - self.common_pins = {} - self.parameter_pins = {} - self.site_bel_map = {} - - # This is for common pin mapping - for common_pins in mapping.commonPins: - pin_map = {} - - for pin in common_pins.pins: - bel_pin = strs[pin.belPin] - pin_map[bel_pin] = strs[pin.cellPin] - - for site_type_and_bels in common_pins.siteTypes: - site_type = strs[site_type_and_bels.siteType] - for bel_idx in site_type_and_bels.bels: - bel = strs[bel_idx] - self.site_types_and_bels.add((site_type, bel)) - self.common_pins[site_type, bel] = pin_map - - for parameter_pins in mapping.parameterPins: - pin_map = {} - - for pin in parameter_pins.pins: - bel_pin = strs[pin.belPin] - - pin_map[bel_pin] = strs[pin.cellPin] - - for parameter_site_type_and_bel in parameter_pins.parametersSiteTypes: - site_type = strs[parameter_site_type_and_bel.siteType] - bel = strs[parameter_site_type_and_bel.bel] - - self.site_types_and_bels.add((site_type, bel)) - - parameter = parameter_site_type_and_bel.parameter - key = strs[parameter.key] - - parameter_which = parameter.which() - if parameter_which == 'textValue': - value = strs[parameter.textValue] - elif parameter_which == 'intValue': - value = str(parameter.intValue) - elif parameter_which == 'boolValue': - value = str(parameter.boolValue) - - - self.parameter_pins[site_type, bel, key, value] = pin_map - - - -class Cell(): - def __init__(self, name, capnp_index=0, property_map={}): - """ Create a new cell. """ - self.name = name - self.property_map = property_map - self.view = "netlist" - self.capnp_index = capnp_index - self.cell_instances = {} - -class Library(): - """ Library of cells. """ - - def __init__(self, name): - self.name = name - self.cells = {} - - def add_cell(self, cell): - assert cell.name not in self.cells, cell.name - self.cells[cell.name] = cell - -class Site( - namedtuple( - 'Site', - 'tile_index tile_name_index site_index tile_type_site_type_index site_type_index alt_index site_type_name' - )): - pass - - -class SiteWire( - namedtuple('SiteWire', 'tile_index site_index site_wire_index')): - def name(self, site_type): - """ - struct SiteWire { - name @0 : StringIdx $stringRef(); - pins @1 : List(BELPinIdx) $belPinRef(); - } - """ - return site_type.site_wire_names[self.site_wire_index] - - -class SitePinNames( - namedtuple('SitePinNames', - 'tile_name site_name site_type_name pin_name wire_name')): - pass - - -class Bel(): - def __init__(self, site_type, strs, bel): - self.site_type = site_type - self.name = strs[bel.name] - self.category = bel.category - self.type = strs[bel.type] - self.bel_pins = [bel_pin for bel_pin in bel.pins] - - def yield_pins(self, site, direction=None): - for bel_pin in self.bel_pins: - bel_name, bel_pin_name = self.site_type.bel_pin_index[bel_pin] - bel_pin = self.site_type.bel_pin(site, bel_name, bel_pin_name) - - if direction and bel_pin.direction == direction: - yield bel_pin - - -class SitePip(): - """ Site pip device resource object. """ - - def __init__(self, site, in_bel_pin_index, out_bel_pin_index, - in_site_wire_index, out_site_wire_index): - self.site = site - self.in_bel_pin_index = in_bel_pin_index - self.out_bel_pin_index = out_bel_pin_index - self.in_site_wire_index = in_site_wire_index - self.out_site_wire_index = out_site_wire_index - - -class BelPin(): - """ BEL Pin device resource object. """ - - def __init__(self, site, name, bel_pin_index, site_wire_index, direction, - is_site_pin): - self.site = site - self.site_wire_index = site_wire_index - self.name = name - self.bel_pin_index = bel_pin_index - self.direction = direction - self.is_site_pin = is_site_pin - - -class SitePin(): - """ Site pin device resource object. """ - - def __init__(self, site, site_pin_index, bel_pin_index, site_wire_index, direction): - self.site = site - self.site_pin_index = site_pin_index - self.bel_pin_index = bel_pin_index - self.site_wire_index = site_wire_index - self.direction = direction - - -class Direction(enum.Enum): - input = 0 - output = 1 - inout = 2 - - -class SiteType(): - """ Object for looking up device resources from a site type. - struct SiteType { - name @0 : StringIdx $stringRef(); - belPins @1 : List(BELPin); # All BEL Pins in site type - pins @2 : List(SitePin); - lastInput @3 : UInt32; # Index of the last input pin - bels @4 : List(BEL); - sitePIPs @5 : List(SitePIP); - siteWires @6 : List(SiteWire); - altSiteTypes @7 : List(SiteTypeIdx); - } - """ - def __init__(self, strs, site_type, site_type_index): - self.site_type = strs[site_type.name] - self.site_type_index = site_type_index - - - bel_pin_index_to_site_wire_index = {} - self.site_wire_names = [] - for site_wire_index, site_wire in enumerate(site_type.siteWires): - self.site_wire_names.append(strs[site_wire.name]) - for bel_pin_index in site_wire.pins: - bel_pin_index_to_site_wire_index[ - bel_pin_index] = site_wire_index - - self.bel_pin_index = [] - self.bel_pins = {} - for bel_pin_index, bel_pin in enumerate(site_type.belPins): - bel_name = strs[bel_pin.bel] - bel_pin_name = strs[bel_pin.name] - direction = Direction[bel_pin.dir] - if bel_pin_index in bel_pin_index_to_site_wire_index: - site_wire_index = bel_pin_index_to_site_wire_index[ - bel_pin_index] - else: - site_wire_index = None - - key = (bel_name, bel_pin_name) - self.bel_pins[key] = bel_pin_index, site_wire_index, direction - self.bel_pin_index.append(key) - - self.bel_pin_to_site_pins = {} - self.site_pins = {} - for site_pin_index, site_pin in enumerate(site_type.pins): - site_pin_name = strs[site_pin.name] - bel_pin_index = site_pin.belpin - - self.bel_pin_to_site_pins[bel_pin_index] = site_pin_index - - if bel_pin_index in bel_pin_index_to_site_wire_index: - site_wire_index = bel_pin_index_to_site_wire_index[ - bel_pin_index] - else: - site_wire_index = None - - self.site_pins[site_pin_name] = (site_pin_index, bel_pin_index, - site_wire_index, - Direction[site_pin.dir]) - - self.site_pips = {} - for site_pip in site_type.sitePIPs: - out_bel_pin = site_type.belPins[site_pip.outpin] - self.site_pips[site_pip.inpin] = strs[out_bel_pin.name] - - - - self.bels = [] - for bel in site_type.bels: - self.bels.append(Bel(self, strs, bel)) - - def bel_pin(self, site, bel, pin): - """ Return BelPin device resource for BEL pin in site. - site (Site) - Site tuple - bel (str) - BEL name - pin (str) - BEL pin name - """ - bel_pin_index, site_wire_index, direction = self.bel_pins[bel, pin] - - return BelPin( - site=site, - bel_pin_index=bel_pin_index, - name=pin, - site_wire_index=site_wire_index, - direction=direction, - is_site_pin=bel_pin_index in self.bel_pin_to_site_pins, - ) - - def site_pin(self, site, device_resources, pin): - """ Return SitePin device resource for site pin in site. - site (Site) - Site tuple - pin (str) - Site pin name - """ - - site_pin_index, bel_pin_index, site_wire_index, direction = self.site_pins[ - pin] - - site_pin_names = device_resources.get_site_pin(site, site_pin_index) - - return SitePin( - site=site, - site_pin_index=site_pin_index, - bel_pin_index=bel_pin_index, - site_wire_index=site_wire_index, - direction=direction) - - def site_pip(self, site, bel, pin): - """ Return SitePip device resource for site PIP in site. - site (Site) - Site tuple - bel (str) - BEL name containing site PIP. - pin (str) - BEL pin name for specific edge. - """ - - key = bel, pin - in_bel_pin_index, in_site_wire_index, direction = self.bel_pins[key] - - out_pin = self.site_pips[in_bel_pin_index] - out_bel_pin_index, out_site_wire_index, direction = self.bel_pins[ - bel, out_pin] - - return SitePip( - site=site, - in_bel_pin_index=in_bel_pin_index, - out_bel_pin_index=out_bel_pin_index, - in_site_wire_index=in_site_wire_index, - out_site_wire_index=out_site_wire_index) - - - def site_routing_graph(self): - """ Return routing graph for every site type - routing_graph (dict) - Map of parent site routing tuple to a set of - child site routing tuples. - - key - tuple of parent belpin - value - tuples of children belpin - 3 cases here: - 1. belpin -> belpin, through sitewire - 2. belpin -> sitepip, through sitewire - 3. sitepip -> belpin, through sitepip - """ - - # from one belpin to another belpins through either sitewire or sitepip - site_routing_graph = {} - site_in = [] - site_out = [] - - sitewire_parent = {} - children_sitewire = {} - - - for key, value in self.bel_pins.items(): - bel_name, bel_pin_name = key - bel_pin_index, site_wire_index, direction = value - - if bel_pin_index in self.site_pips and bel_name[2:] != 'LUT': - in_bel_pin_index = bel_pin_index - sitepip_in = 'site_pip', bel_name, bel_pin_name - - out_pin = self.site_pips[in_bel_pin_index] - sitepip_out = 'bel_pin', bel_name, out_pin - site_routing_graph[sitepip_in] = sitepip_out - - if direction in [Direction.output, Direction.inout]: - # parent tuple - parent_tup = 'bel_pin', bel_name, bel_pin_name - site_routing_graph[parent_tup] = [] - sitewire_parent[site_wire_index] = parent_tup - if bel_name in self.site_pins: - site_in.append(parent_tup) - - else: - child_key = bel_name, bel_pin_name - children_sitewire[child_key] = site_wire_index - - - - for child_key, site_wire_index in children_sitewire.items(): - bel_name, bel_pin_name = child_key - bel_pin_index, site_wire_index, direction = self.bel_pins[child_key] - - if site_wire_index in sitewire_parent: - parent_tup = sitewire_parent[site_wire_index] - - if bel_pin_index in self.site_pips and bel_name[2:] != 'LUT': - child_tup = 'site_pip', bel_name, bel_pin_name - site_routing_graph[parent_tup].append(child_tup) - - else: - child_tup = 'bel_pin', bel_name, bel_pin_name - site_routing_graph[parent_tup].append(child_tup) - if bel_name in self.site_pins: - site_out.append(child_tup) - - - return site_routing_graph, site_in, site_out - - -class LogicalNetlist: - """ - Parse Logical Netlist file. - This is for parameter pin mapping in BRAMs. - However, all the parameter pins have the same properties and value in ISPD16 benchmarks. - So the parameter pins have been hard-coded. - For example, - para_map['DOA_REG'] = '1' - para_map['WRITE_WIDTH_A'] = '1' - para_map['WRITE_WIDTH_B'] = '72' - para_map['DOB_REG'] = '1' - """ - def __init__(self, schema_dir): - """ Read and compile logical netlist for FPGA02-12 benchmarks - - """ - import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] - # add import path from the rapidwright java.capnp - # import_path.append(os.path.join(schema_dir, '../../schema')) - import_path.append('IFsupport') - self.logical_netlist_capnp = capnp.load(os.path.join(schema_dir, 'LogicalNetlist.capnp'), imports=import_path) - - with open('FPGA02_rapidwright.netlist', 'rb') as in_f: - f_comp = gzip.GzipFile(fileobj = in_f, mode='rb') - - with self.logical_netlist_capnp.Netlist.from_bytes(f_comp.read(), traversal_limit_in_words=NO_TRAVERSAL_LIMIT, nesting_limit=NESTING_LIMIT) as message: - self.logical_netlist = message - - self.strs = [s for s in self.logical_netlist.strList] - - self.string_index = {} - for idx, s in enumerate(self.strs): - self.string_index[s] = idx - - self.cell_list = self.logical_netlist.instList - - # for parameterPin mapping - self.cell_prop_map = {} - - for cell_instance in self.cell_list: - entries = cell_instance.propMap.entries - cell_name = self.strs[cell_instance.name] - if cell_name not in self.cell_prop_map: - self.cell_prop_map[cell_name] = [] - - for parameter in entries: - key = self.strs[parameter.key] - parameter_which = parameter.which() - if parameter_which == 'textValue': - value = self.strs[parameter.textValue] - elif parameter_which == 'intValue': - value = str(parameter.intValue) - elif parameter_which == 'boolValue': - value = str(parameter.boolValue) - - self.cell_prop_map[cell_name].append((key,value)) - - - -class DeviceResources: - """DeviceResources class to parse the part's placement resources. - yield_cell_bel_mappings(self) - get_library(self) - get_macro_instance(self) - get_site_type(self) - get_packages - """ - def __init__(self, schema_dir, part_name): - """ Read and compile device resources for part assigned by part_name - - """ - import_path = [os.path.dirname(os.path.dirname(capnp.__file__))] - # add import path from the rapidwright java.capnp - # import_path.append(os.path.join(schema_dir, '../../schema')) - import_path.append('IFsupport') - self.device_resources_capnp = capnp.load(os.path.join(schema_dir, 'DeviceResources.capnp'), imports=import_path) - - device_file = os.path.join('IFsupport', part_name) - with open(device_file + '.device', 'rb') as in_f: - f_comp = gzip.GzipFile(fileobj = in_f, mode='rb') - - with self.device_resources_capnp.Device.from_bytes(f_comp.read(), traversal_limit_in_words=NO_TRAVERSAL_LIMIT, nesting_limit=NESTING_LIMIT) as message: - self.device_resources = message - - self.strs = [s for s in self.device_resources.strList] - - self.string_index = {} - for idx, s in enumerate(self.strs): - self.string_index[s] = idx - - self.site_type_names = [] - self.site_type_name_to_index = {} - self.site_type_bels = {} - # generate site_type object - for site_type_index, site_type in enumerate(self.device_resources.siteTypeList): - site_obj = SiteType(self.strs, site_type, site_type_index) - site_type_name = self.strs[site_type.name] - self.site_type_names.append(site_type_name) - self.site_type_name_to_index[site_type_name] = site_type_index - self.site_type_bels[site_type_name] = [] - site_bels = site_obj.bels - for site_bel in site_bels: - site_bel_name = site_bel.name - self.site_type_bels[site_type_name].append(site_bel_name) - - tiletype_list = self.device_resources.tileTypeList - sitetype_list = self.device_resources.siteTypeList - - self.site_types = {} - self.site_type_map = {} - self.alt_site_type_map = {} - self.site_name_to_site = {} - self.altPinsToPrimPins = {} - - - for tile_idx, tile in enumerate(self.device_resources.tileList): - tile_name = self.strs[tile.name] - tile_name_index = self.string_index[tile_name] - - for site_idx, site in enumerate(tile.sites): - site_in_tile = self.strs[site.name] - self.site_name_to_site[site_in_tile] = {} - - tile_type_site_type_index = site.type - site_types = tiletype_list[tile.type].siteTypes - site_type_index = site_types[site.type].primaryType - site_type_name = self.strs[sitetype_list[site_type_index].name] - - self.site_type_map[site_in_tile] = site_type_name - - self.site_name_to_site[site_in_tile][site_type_name] = Site( - tile_index=tile_idx, - tile_name_index=tile_name_index, - site_index=site_idx, - tile_type_site_type_index=tile_type_site_type_index, - site_type_index=site_type_index, - alt_index=None, - site_type_name=site_type_name) - - for alt_index, alt_site_type_index in enumerate(sitetype_list[site_type_index].altSiteTypes): - site_type_name = self.site_type_names[alt_site_type_index] - self.alt_site_type_map[site_in_tile] = site_type_name - self.site_name_to_site[site_in_tile][site_type_name] = Site( - tile_index=tile_idx, - tile_name_index=tile_name_index, - site_index=site_idx, - tile_type_site_type_index=tile_type_site_type_index, - site_type_index=alt_site_type_index, - alt_index=alt_index, - site_type_name=site_type_name) - - # self.tile_types = {} - self.tile_wire_index_to_node_index = None - - - def get_site_type_index(self, site_type_name): - return self.site_type_name_to_index[site_type_name] - - def get_site_type(self, site_type_index): - """ Get SiteType object for specified site type index. """ - if site_type_index not in self.site_types: - self.site_types[site_type_index] = SiteType( - self.strs, - self.device_resources.siteTypeList[site_type_index], - site_type_index) - - return self.site_types[site_type_index] - - - def get_tile_name_at_site_name(self, site_name): - """ Get Tile name at site name. """ - - sites_dict = self.site_name_to_site[site_name] - - # Get the first site in the dict. Assume all alternative sites are at - # the same tile - site = list(sites_dict.values())[0] - return self.strs[site.tile_name_index] - - def bel_pin(self, site_name, site_type, bel, pin): - """ Return BelPin device resource for BEL pin in site. - site_name (str) - Name of site - site_type (str) - Name of specific site type being queried. - bel (str) - BEL name containing site PIP. - pin (str) - BEL pin name for specific edge. - """ - site = self.site_name_to_site[site_name][site_type] - return self.get_site_type(site.site_type_index).bel_pin(site, bel, pin) - - def site_pin(self, site_name, site_type, pin): - """ Return SitePin device resource for site pin in site. - site_name (str) - Name of site - site_type (str) - Name of specific site type being queried. - pin (str) - Site pin name - """ - site = self.site_name_to_site[site_name][site_type] - return self.get_site_type(site.site_type_index).site_pin( - site, self, pin) - - def site_pip(self, site_name, site_type, bel, pin): - """ Return SitePip device resource for site PIP in site. - site_name (str) - Name of site - site_type (str) - Name of specific site type being queried. - bel (str) - BEL name containing site PIP. - pin (str) - BEL pin name for specific edge. - """ - site = self.site_name_to_site[site_name][site_type] - return self.get_site_type(site.site_type_index).site_pip( - site, bel, pin) - - - def get_site_pin(self, site, site_pin_index): - """ Get SitePinNames for specified site pin. - site (Site) - Site tuple - site_pin_index (int) - Index into SiteType.pins list. - Site pin to tile relationships are estabilished through the site type - in tile type data. - If the site tuple indicates this is a primary site type, then the - tile wire can be returned directly. - If the site tuple indicates this is an alternate site type, then the - tile wire is found by first mapping the site pin from the alternate - site type to the primary site type. At that point, the tile wire can - be found. - """ - tile = self.device_resources.tileList[site.tile_index] - tile_type_index = tile.type - tile_type = self.device_resources.tileTypeList[tile_type_index] - site_type_in_tile_type = tile_type.siteTypes[site. - tile_type_site_type_index] - if site.alt_index is None: - # This site type is the primary site type, return the tile wire - # directly. - site_type = self.device_resources.siteTypeList[ - site_type_in_tile_type.primaryType] - site_type_name = self.strs[site_type.name] - pin_name = self.strs[site_type.pins[site_pin_index].name] - wire_name = self.strs[site_type_in_tile_type. - primaryPinsToTileWires[site_pin_index]] - else: - # This site type is an alternate site type. - prim_site_type = self.device_resources.siteTypeList[ - site_type_in_tile_type.primaryType] - site_type = self.device_resources.siteTypeList[ - prim_site_type.altSiteTypes[site.alt_index]] - site_type_name = self.strs[site_type.name] - pin_name = self.strs[site_type.pins[site_pin_index].name] - - # First translate the site_pin_index from the alternate site type - # To the primary site type pin index. - prim_site_pin_index = site_type_in_tile_type.altPinsToPrimaryPins[ - site.alt_index].pins[site_pin_index] - prim_site_pin_name = self.strs[site_type.pins[prim_site_pin_index].name] - self.altPinsToPrimPins[site_type_name][pin_name] = prim_site_pin_name - # Then lookup the tile wire using the primary site pin index. - wire_name = self.strs[site_type_in_tile_type. - primaryPinsToTileWires[prim_site_pin_index]] - - return SitePinNames( - tile_name=self.strs[tile.name], - site_name=self.strs[tile.sites[site.site_index].name], - site_type_name=site_type_name, - pin_name=pin_name, - wire_name=wire_name) - - - def yield_cell_bel_mappings(self): - """ yield cell bel mapping """ - for cell_bel_mapping in self.device_resources.cellBelMap: - yield CellBel(self.strs, cell_bel_mapping) - - def get_library(self): - """Build library for primitives and macros from device resources - Didn't fully parse the property map. - """ - netlist = self.device_resources.primLibs - - libraries = {} - for cell_capnp in netlist.cellList: - cell_decl = netlist.cellDecls[cell_capnp.index] - prop_map = {} - for prop in cell_decl.propMap.entries: - key = self.strs[prop.key] - if prop.which() == 'textValue': - value = self.strs[prop.textValue] - elif prop.which() == 'intValue': - value = prop.intValue - else: - assert prop.which() == 'boolValue' - value = prop.boolValue - prop_map[key] = value - cell = Cell( - name=self.strs[cell_decl.name], - capnp_index=cell_capnp.index, - property_map=prop_map, - ) - cell.view = self.strs[cell_decl.view] - for inst in cell_capnp.insts: - # struct CellInstance { - # name @0 : StringIdx $stringRef(); - # propMap @1 : PropertyMap; - # view @2 : StringIdx $stringRef(); - # cell @3 : CellIdx $cellRef(); - # } - cell_instance_name = self.strs[netlist.instList[inst].name] - cell_name = self.strs[netlist.cellDecls[netlist.instList[inst].cell].name] - cell.cell_instances[cell_instance_name] = (cell_instance_name, cell_name) - - library = self.strs[cell_decl.lib] - if library not in libraries: - libraries[library] = Library(name=library) - libraries[library].add_cell(cell) - return libraries - - def get_macro_instance(self): - """ Get macros from device resources - macro_inst - build a map for macros and their instances. - One macro consists of more than one primitives. - """ - macro_lib = self.get_library()['macros'] - macro_inst = {} - - for cell_name, cell in sorted( - macro_lib.cells.items(), key=lambda x: x[0]): - macro_name = cell_name - macro_inst[macro_name] = {} - - for inst_name, inst in sorted( - cell.cell_instances.items(), key=lambda x: x[0]): - macro_inst[macro_name][inst_name] = inst[1] - - return macro_inst - - def get_packages(self): - """ Get the device package for debugging. """ - package_list = self.device_resources.packages - - for package in package_list: - package_name = self.strs[package.name] - - -class db_to_physicalnetlist(): - def __init__(self, placedb, schema_dir, part_name): - self.part = part_name - self.schema_dir = schema_dir - self.sitemap = {} - # self.SLICE = {} - self.DSP = [] - self.BRAM = [] - self.IO = [] - bram_y_num = placedb. num_sites_y / 5 - dsp_y_num = placedb. num_sites_y / 2.5 - - - slice_index_x = 0 - # initialize sitemap - for i in range(placedb.num_sites_x): - slice_flag = False - # slice_index_y = 0 - for j in range(placedb.num_sites_y): - # LUT/FF - if placedb.site_type_map[i, j] == 1: - slice_flag = True - for k in range(0, 16): - self.sitemap[i, j, k] = "SLICE_X" + str(slice_index_x) + "Y" + str(j) - # slice_index_y += 1 - # DSP - elif placedb.site_type_map[i, j] == 2: - self.DSP.append((i, j)) - idx = len(self.DSP) - 1 - index_x = int(idx / dsp_y_num) - index_y = int(idx - index_x * dsp_y_num) - self.sitemap[i, j, 0] = "DSP48E2_X" + str(index_x) + "Y" + str(index_y) - # BRAM - elif placedb.site_type_map[i, j] == 3: - self.BRAM.append((i, j)) - idx = len(self.BRAM) - 1 - index_x = int(idx / bram_y_num) - index_y = int(idx - index_x * bram_y_num) - self.sitemap[i, j, 0] = "RAMB36_X" + str(index_x) + "Y" + str(index_y) - # IO - elif placedb.site_type_map[i, j] == 4: - self.IO.append((i, j)) - - if slice_flag: - slice_index_x += 1 - - # if slice_index_x == 90: - # print(i) - - IOB_col = [] - BUFGCE_col = [] - for loc in self.IO: - x, y = loc - if x != 0 and x != placedb.num_sites_x - 1: - if x not in IOB_col and x not in BUFGCE_col: - IOB_col.append(x) - BUFGCE_col.append(x+1) - - - self.Site_LUTs = {} - self.shared_LUT = [] - - # map from node name to cellplacement obj - self.node_placement = {} - # map from port name to cellplacement obj - self.port_placement = {} - - # map from site name to SiteInst object - self.site_instances = {} - # map from node name to site name - self.node_site_map = {} - - self.device_resource = DeviceResources(self.schema_dir, part_name) - - - - sitetypes_prefix = ['SLICEM', 'SLICEL', 'HPIOB', 'HRIO', 'BUFGCE', 'RAMB36', 'DSP48E2'] - sitetypes = [] - for site_type_name in self.device_resource.site_type_names: - for prefix in sitetypes_prefix: - if prefix in site_type_name: - sitetypes.append(site_type_name) - - # print(sitetypes) - - self.routing_graphs = {} - self.site_in = {} - self.site_out = {} - self.dsp_bel_pins = {} - - - for sitetype in sitetypes: - site_index = self.device_resource.get_site_type_index(sitetype) - sitetype_obj = self.device_resource.get_site_type(site_index) - routing_graph, site_in, site_out = sitetype_obj.site_routing_graph() - self.routing_graphs[sitetype] = routing_graph - self.site_in[sitetype] = site_in - self.site_out[sitetype] = site_out - if sitetype == 'DSP48E2': - for key in sitetype_obj.bel_pins: - bel_name, bel_pin_name = key - if bel_name not in self.dsp_bel_pins: - self.dsp_bel_pins[bel_name] = [] - self.dsp_bel_pins[bel_name].append(bel_pin_name) - - - def Map_bel(self, node_z, node_type): - if node_type[:3] == "LUT": - switcher = { - 0: "A5LUT", - 1: "A6LUT", - 2: "B5LUT", - 3: "B6LUT", - 4: "C5LUT", - 5: "C6LUT", - 6: "D5LUT", - 7: "D6LUT", - 8: "E5LUT", - 9: "E6LUT", - 10:"F5LUT", - 11:"F6LUT", - 12:"G5LUT", - 13:"G6LUT", - 14:"H5LUT", - 15:"H6LUT", - } - return switcher[node_z] - elif node_type[:4] == "FDRE": - switcher = { - 0: "AFF", - 1: "AFF2", - 2: "BFF", - 3: "BFF2", - 4: "CFF", - 5: "CFF2", - 6: "DFF", - 7: "DFF2", - 8: "EFF", - 9: "EFF2", - 10:"FFF", - 11:"FFF2", - 12:"GFF", - 13:"GFF2", - 14:"HFF", - 15:"HFF2", - } - return switcher[node_z] - elif node_type[:4] == "BUFG": - return "BUFCE" - elif node_type[:4] == "OBUF": - return "OUTBUF" - elif node_type[:3] == "RAM": - return "RAMB36E2" - else: - return "None" - - - def prevent_pin_overlap(self, placedb, phys_netlist): - """ For the cell bel pin mapping, especially for LUT packing - - The shared inputs of 5LUT and 6LUT must be connected to the same nets. - In addition, when 5LUT and 6LUT are both used, A6 pin must be connected to VCC. - The higher the pin number is, the less the delay. - e.g: A5 has higher pin number than A4, etc - - My pin mapping scheme: I map the shared inputs first and start from higher pin number, - and then move to the not shared ones. - - This is probably different from how vivado copes with pin mapping! - - """ - - bel_pins = ['A5', 'A4', 'A3', 'A2', 'A1'] - - for site in self.Site_LUTs: - LUT_map = { - 'A': [], - 'B': [], - 'C': [], - 'D': [], - 'E': [], - 'F': [], - 'G': [], - 'H': [], - } - for lut_placement in self.Site_LUTs[site]: - if lut_placement.bel_name[:1] == 'A': - LUT_map['A'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'B': - LUT_map['B'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'C': - LUT_map['C'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'D': - LUT_map['D'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'E': - LUT_map['E'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'F': - LUT_map['F'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'G': - LUT_map['G'].append(lut_placement) - elif lut_placement.bel_name[:1] == 'H': - LUT_map['H'].append(lut_placement) - else: - continue - - sharedluts_insite = [] - - for key in LUT_map: - if len(LUT_map[key]) == 2: - self.shared_LUT.append((LUT_map[key][0], LUT_map[key][1])) - sharedluts_insite.append((LUT_map[key][0], LUT_map[key][1])) - - #single 5LUT detection - elif len(LUT_map[key]) == 1 and LUT_map[key][0].bel_name.endswith('5LUT'): - LUT_map[key][0].bel_name = LUT_map[key][0].bel_name[:1] + '6LUT' - - # build new pinmap - new_pinmap = [] - for pin in LUT_map[key][0].pins: - if pin[0][:1] == 'O': - new_pin = (pin[0], 'O6') - elif pin[0][:1] == 'I': - new_pin = (pin[0], pin[2]) - new_pinmap.append(new_pin) - - LUT_map[key][0].pins.clear() - - for new_pin in new_pinmap: - LUT_map[key][0].add_pins(new_pin[0], new_pin[1]) - - else: - continue - - for lut_pair in sharedluts_insite: - lut_name_0 = lut_pair[0].cell_name - node_id_0 = placedb.node_name2id_map[lut_name_0] - - lut_name_1 = lut_pair[1].cell_name - node_id_1 = placedb.node_name2id_map[lut_name_1] - - pin2net_0 = {} - - for pin_id in placedb.node2pin_map[node_id_0]: - pin2net_0[placedb.pin_names[pin_id]] = placedb.pin2net_map[pin_id] - - pin2net_1 = {} - for pin_id in placedb.node2pin_map[node_id_1]: - pin2net_1[placedb.pin_names[pin_id]] = placedb.pin2net_map[pin_id] - - error_cnt = 0 - for pin_0 in lut_pair[0].pins: - cellpin_0 = pin_0[0] - belpin_0 = pin_0[2] - if belpin_0 == 'A6': - error_cnt += 1 - break - - for pin_1 in lut_pair[1].pins: - cellpin_1 = pin_1[0] - belpin_1 = pin_1[2] - if belpin_1 == 'A6': - error_cnt += 1 - break - - if belpin_0 == belpin_1: - if cellpin_0 in pin2net_0 and cellpin_1 in pin2net_1: - if pin2net_0[cellpin_0] == pin2net_1[cellpin_1]: - pass - else: - error_cnt += 1 - break - - - # fix the wrong mapping - - if error_cnt != 0: - - shared_pins_0 = [] - shared_pins_1 = [] - un_shared_pins_0 = [] - un_shared_pins_1 = [] - input_pins_0 = [] - output_pin_0 = None - input_pins_1 = [] - output_pin_1 = None - - for pin_0 in lut_pair[0].pins: - cellpin_0 = pin_0[0] - if cellpin_0[:1] == 'I': - input_pins_0.append(cellpin_0) - else: - output_pin_0 = pin_0 - - for pin_1 in lut_pair[1].pins: - cellpin_1 = pin_1[0] - if cellpin_1[:1] == 'I': - input_pins_1.append(cellpin_1) - else: - output_pin_1 = pin_1 - - lut_pair[0].pins.clear() - lut_pair[1].pins.clear() - - - for cellpin_0 in input_pins_0: - if cellpin_0 not in shared_pins_0: - for cellpin_1 in input_pins_1: - if cellpin_1 not in shared_pins_1: - if cellpin_0 in pin2net_0 and cellpin_1 in pin2net_1: - if pin2net_0[cellpin_0] == pin2net_1[cellpin_1]: - net_name = placedb.net_names[pin2net_0[cellpin_0]] - shared_pins_0.append(cellpin_0) - shared_pins_1.append(cellpin_1) - break - - - for cellpin_0 in input_pins_0: - if cellpin_0 not in shared_pins_0: - for cellpin_1 in input_pins_1: - if cellpin_1 not in shared_pins_1: - if cellpin_0 in pin2net_0 and cellpin_1 not in pin2net_1: - shared_pins_0.append(cellpin_0) - shared_pins_1.append(cellpin_1) - - elif cellpin_0 not in pin2net_0 and cellpin_1 in pin2net_1: - shared_pins_0.append(cellpin_0) - shared_pins_1.append(cellpin_1) - - - for cellpin_0 in input_pins_0: - if cellpin_0 not in shared_pins_0: - for cellpin_1 in input_pins_1: - if cellpin_1 not in shared_pins_1: - if cellpin_0 not in pin2net_0 and cellpin_1 not in pin2net_1: - shared_pins_0.append(cellpin_0) - shared_pins_1.append(cellpin_1) - - - # self.pins.append((cellpin, self.bel_name, belpin)) - for idx, pin in enumerate(shared_pins_0): - lut_pair[0].add_pins(shared_pins_0[idx], bel_pins[idx]) - lut_pair[1].add_pins(shared_pins_1[idx], bel_pins[idx]) - - - for pin_0 in input_pins_0: - if pin_0 not in shared_pins_0: - un_shared_pins_0.append(pin_0) - for pin_1 in input_pins_1: - if pin_1 not in shared_pins_1: - un_shared_pins_1.append(pin_1) - - for idx, un_shared_pin in enumerate(un_shared_pins_0): - lut_pair[0].add_pins(un_shared_pin, bel_pins[idx + len(shared_pins_0)]) - - for idx, un_shared_pin in enumerate(un_shared_pins_1): - lut_pair[1].add_pins(un_shared_pin, bel_pins[idx + len(un_shared_pins_0) + len(shared_pins_1)]) - - - lut_pair[0].add_pins(output_pin_0[0], output_pin_0[2]) - lut_pair[1].add_pins(output_pin_1[0], output_pin_1[2]) - - - def stitch_routing(self, placedb, phys_netlist): - """ - Do intra-site routing through site_routing graph. - call function site_router(), this is the clean way to do intra-site routing for each site. - """ - - nets = {} - net_source = {} - io_nets = {} - vcc_nets = [] - gnd_nets = [] - constant_nets = { - 0 : 'GLOBAL_LOGIC0', - 1 : 'GLOBAL_LOGIC1' - } - - ram_pin_pair = { - 'ADDRENAL' : 'ADDRENAU', - 'ADDRENBL' : 'ADDRENBU', - 'CLKARDCLKL' : 'CLKARDCLKU', - 'CLKBWRCLKL' : 'CLKBWRCLKU', - 'ECCPIPECEL': 'ECCPIPECEU', - 'ENARDENL' : 'ENARDENU', - 'ENBWRENL' : 'ENBWRENU', - 'REGCEAREGCEL' : 'REGCEAREGCEU', - 'REGCEBL' : 'REGCEBU', - 'REGCLKARDRCLKL' : 'REGCLKARDRCLKU', - 'REGCLKBL' : 'REGCLKBU', - 'RSTRAMARSTRAML' : 'RSTRAMARSTRAMU', - 'RSTRAMBL' : 'RSTRAMBU', - 'RSTREGARSTREGL' : 'RSTREGARSTREGU', - 'RSTREGBL' : 'RSTREGBU', - 'SLEEPL' : 'SLEEPU' - } - - - # sort nets - for net_id in range(len(placedb.net2pin_map)): - net_name = placedb.net_names[net_id] - sources = [] - stubs = [] - - for pin_id in placedb.net2pin_map[net_id]: - pin_name = placedb.pin_names[pin_id] - node_name = placedb.node_names[placedb.pin2node_map[pin_id]] - node_id = placedb.node_name2id_map[node_name] - node_type = placedb.node_types[node_id] - - # ignore the pseudo VCC, GND nodes - if node_type == 'LUT0': - if node_name == 'VCC': - vcc_nets.append(net_name) - else: - gnd_nets.append(net_name) - - continue - - site_name = self.node_site_map[node_name] - - - # net_roots - site_obj = self.site_instances[site_name] - cells = site_obj.cells - - # This pin is somehow not to be mapped - if pin_name == 'RSTREGB': - continue - - if node_type[:3] == 'DSP' and pin_name[0] == 'D': - pin_name = pin_name.replace('D', 'DIN') - - # primary cells - if len(cells[node_name]) == 1: - cell_placement = cells[node_name][0] - bel_name = cell_placement.bel_name - - for pin in cell_placement.pins: - if pin[0] == pin_name: - bel_pin = pin[2] - belpin_tup = 'bel_pin', bel_name, bel_pin - site_obj.add_belpins(net_name, belpin_tup) - if bel_pin in ram_pin_pair: - belup_tup = 'bel_pin', bel_name, ram_pin_pair[bel_pin] - site_obj.add_belpins(net_name, belup_tup) - - break - - else: - for cell in cells[node_name]: - cell_placement = cell - bel_name = cell_placement.bel_name - - belpin_tup = None - for pin in cell_placement.pins: - if pin[0] == pin_name: - bel_pin = pin[2] - belpin_tup = 'bel_pin', bel_name, bel_pin - break - elif bel_name.endswith('LUT') and pin_name.startswith('O'): - belpin_tup = 'bel_pin', bel_name, pin_name - - if bel_name == 'INBUF': - ctrl_tup = 'bel_pin', 'IBUFCTRL', 'I' - out_net = node_name + '/OUT' - site_obj.add_belpins(out_net, ctrl_tup) - site_obj.add_belpins(out_net, belpin_tup) - - elif bel_name == 'IBUFCTRL': - o_net = node_name + '/O' - io_nets[net_name] = o_net - site_obj.add_belpins(o_net, belpin_tup) - - - elif belpin_tup != None: - site_obj.add_belpins(net_name, belpin_tup) - - - # site router - for site_name, site_obj in self.site_instances.items(): - site_type = phys_netlist.siteInsts[site_name] - site_nets, site_net_source = site_obj.site_router(self.routing_graphs[site_type], self.site_in[site_type], self.site_out[site_type]) - - for net_name, root_list in site_nets.items(): - if net_name in io_nets: - net_name = io_nets[net_name] - if net_name not in nets: - nets[net_name] = root_list - else: - for root in root_list: - nets[net_name].append(root) - - for net_name, source in site_net_source.items(): - if net_name not in net_source: - net_source[net_name] = source - - - vcc_stubs = [] - gnd_stubs = [] - # Build physical nets for normal nets - # Extend the stub lists for vcc and gnd nets - for net_name, root_list in nets.items(): - sources = [] - stubs = [] - - for root in root_list: - if net_name in net_source and root.bel_name == net_source[net_name][1]: - sources.append(root) - else: - stubs.append(root) - - if net_name == constant_nets[0] or net_name in gnd_nets: - gnd_stubs.extend(stubs) - - elif net_name == constant_nets[1] or net_name in vcc_nets: - vcc_stubs.extend(stubs) - - else: - phys_netlist.add_physical_net(net_name=net_name, - sources=sources, - stubs=stubs, - stubNodes=[], - net_type=PhysicalNetType.Signal) - - # Finally build physical nets for vcc and gnd - # For gnd and vcc nets, they don't have sources - phys_netlist.add_physical_net(net_name=constant_nets[0], - sources=[], - stubs=gnd_stubs, - stubNodes=[], - net_type=PhysicalNetType.Gnd) - - phys_netlist.add_physical_net(net_name=constant_nets[1], - sources=[], - stubs=vcc_stubs, - stubNodes=[], - net_type=PhysicalNetType.Vcc) - - - - def build_physicalnetlist(self, placedb, out_file): - phys_netlist = PhysicalNetlist(self.part) - - # cell_prop_map = self.logical_netlist.cell_prop_map - - mappings = self.device_resource.yield_cell_bel_mappings() - site_type_map = self.device_resource.site_type_map - alt_site_type_map = self.device_resource.alt_site_type_map - site_type_bels = self.device_resource.site_type_bels - macro_inst = self.device_resource.get_macro_instance() - - pinmap = {} - parameter_pinmap = {} - for mapping in mappings: - cell = mapping.cell - pinmap[cell] = mapping.common_pins - parameter_pinmap[cell] = mapping.parameter_pins - - with open(out_file, 'r') as fin: - for line in fin: - node_name, x, y, z = line.split() - x = int(x) - y = int(y) - z = int(z) - node_id = placedb.node_name2id_map[node_name] - node_type = placedb.node_types[node_id] - - # ignore the pseudo VCC, GND nodes - if node_type == 'LUT0': - continue - - self.node_placement[node_name] = [] - - if node_type in macro_inst: - LUT6_2_flag = False - for inst in macro_inst[node_type]: - cell_type = macro_inst[node_type][inst] - cell_name = node_name + "/" + inst - site_name = self.sitemap[x, y, z] - - if site_type_map[site_name][:5] == 'SLICE' and LUT6_2_flag == False: - bel_name = self.Map_bel(z-1, node_type) - LUT6_2_flag = True - elif LUT6_2_flag == True: - bel_name = self.Map_bel(z, node_type) - else: - bel_name = cell_type - - self.node_site_map[node_name] = site_name - - # build siteinst obj - if site_name not in self.site_instances: - site_instance = SiteInst(site_name) - self.site_instances[site_name] = site_instance - - - # For site instances - if site_name not in phys_netlist.siteInsts: - site_type = site_type_map[site_name] - phys_netlist.add_site_instance(site_name, site_type) - - self.site_instances[site_name] = SiteInst(site_name) - - else: - site_type = phys_netlist.siteInsts[site_name] - - # add cell instance - cellplacement = Cellplacement(cell_name, cell_type, site_name, bel_name) - self.node_placement[node_name].append(cellplacement) - - # add pins for cell instance - for key, value in pinmap[cell_type][site_type, bel_name].items(): - belpin = key - cellpin = value - if cellpin == "GND": - continue - elif node_type == 'DSP48E2': - if belpin in self.dsp_bel_pins[bel_name]: - cellplacement.add_pins(cellpin, belpin) - else: - break - else: - cellplacement.add_pins(cellpin, belpin) - phys_netlist.add_cellplacement(cellplacement) - - self.site_instances[site_name].add_cells(node_name, cellplacement) - - # primary insts - else: - cell_type = node_type - cell_name = node_name - site_name = self.sitemap[x, y, z] - bel_name = self.Map_bel(z, node_type) - self.node_site_map[node_name] = site_name - - if site_name not in self.site_instances: - site_instance = SiteInst(site_name) - self.site_instances[site_name] = site_instance - - - # For site instances - if site_name not in phys_netlist.siteInsts: - if cell_type == "RAMB36E2": - site_type = alt_site_type_map[site_name] - else: - site_type = site_type_map[site_name] - phys_netlist.add_site_instance(site_name, site_type) - else: - site_type = phys_netlist.siteInsts[site_name] - - - # add cell instance - cellplacement = Cellplacement(cell_name, cell_type, site_name, bel_name) - self.node_placement[node_name].append(cellplacement) - self.site_instances[site_name].add_cells(node_name, cellplacement) - - # add pins for cell instance - for key, value in pinmap[cell_type][site_type, bel_name].items(): - belpin = key - cellpin = value - if cellpin == "GND": - continue - else: - cellplacement.add_pins(cellpin, belpin) - - - if cell_type == 'RAMB36E2': - para_belcell = {} - para_map = {} - para_map['DOA_REG'] = '1' - para_map['WRITE_WIDTH_A'] = '1' - para_map['WRITE_WIDTH_B'] = '72' - para_map['DOB_REG'] = '1' - - - for prop_key, prop_value in para_map.items(): - for key, value in parameter_pinmap[cell_type][site_type, bel_name, prop_key, prop_value].items(): - belpin = key - cellpin = value - - # hardcode this need to find a clean way - if belpin == 'DINBDIN1': - cellpin = 'DINBDIN[1]' - - if cellpin == "GND" or cellpin == "VCC": - continue - elif belpin not in para_belcell: - para_belcell[belpin] = cellpin - cellplacement.add_pins(cellpin, belpin) - - - phys_netlist.add_cellplacement(cellplacement) - - if site_name[:5] == 'SLICE': - if site_name not in self.Site_LUTs: - in_site_luts = [] - if cell_type[:3] == 'LUT': - in_site_luts.append(cellplacement) - self.Site_LUTs[site_name] = in_site_luts - else: - if cell_type[:3] == 'LUT': - self.Site_LUTs[site_name].append(cellplacement) - - - self.prevent_pin_overlap(placedb, phys_netlist) - - self.stitch_routing(placedb, phys_netlist) - - return phys_netlist - - -class tcl_generator(): - """ generate a tcl script for the golden reference IF file - - file_name - "place_cells.tcl" - place cells one by one by using vivado tcl command place_cell; - place each cell to its corresponding site. - - """ - def __init__(self): - self.file_name = 'place_cells.tcl' - - def write_tcl(self, phys_netlist): - """ write out the tcl script """ - with open(self.file_name, 'w') as tcl_file: - for placement in phys_netlist.placements: - if placement.cell_name.endswith('/LUT5'): - continue - elif placement.cell_name.endswith('/LUT6'): - placement.cell_name = placement.cell_name[:-5] - line = 'place_cell ' + placement.cell_name + ' ' + placement.site_name + '/' + placement.bel_name - tcl_file.write(line + os.linesep) - - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py deleted file mode 100644 index a5c9c353..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py +++ /dev/null @@ -1,152 +0,0 @@ -## -# @file NesterovAcceleratedGradientOptimizer.py -# @author Yibo Lin (DREAMPlace) -# @date Aug 2018 -# @brief Nesterov's accelerated gradient method proposed by e-place. -# - -import os -import sys -import time -import pickle -import numpy as np -import torch -from torch.optim.optimizer import Optimizer, required -import torch.nn as nn -import pdb - -class NesterovAcceleratedGradientOptimizer(Optimizer): - """ - @brief Follow the Nesterov's implementation of e-place algorithm 2 - http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf - """ - def __init__(self, params, lr=required, obj_and_grad_fn=required, constraint_fn=None): - """ - @brief initialization - @param params variable to optimize - @param lr learning rate - @param obj_and_grad_fn a callable function to get objective and gradient - @param constraint_fn a callable function to force variables to satisfy all the constraints - """ - if lr is not required and lr < 0.0: - raise ValueError("Invalid learning rate: {}".format(lr)) - - # u_k is major solution - # v_k is reference solution - # obj_k is the objective at v_k - # a_k is optimization parameter - # alpha_k is the step size - # v_k_1 is previous reference solution - # g_k_1 is gradient to v_k_1 - # obj_k_1 is the objective at v_k_1 - defaults = dict(lr=lr, - u_k=[], v_k=[], g_k=[], obj_k=[], a_k=[], alpha_k=[], - v_k_1=[], g_k_1=[], obj_k_1=[], - v_kp1 = [None], - obj_eval_count=0) - super(NesterovAcceleratedGradientOptimizer, self).__init__(params, defaults) - self.obj_and_grad_fn = obj_and_grad_fn - self.constraint_fn = constraint_fn - - # I do not know how to get generator's length - if len(self.param_groups) != 1: - raise ValueError("Only parameters with single tensor is supported") - - def __setstate__(self, state): - super(NesterovAcceleratedGradientOptimizer, self).__setstate__(state) - - def step(self, closure=None): - """ - @brief Performs a single optimization step. - @param closure A callable closure function that reevaluates the model and returns the loss. - """ - loss = None - if closure is not None: - loss = closure() - - for group in self.param_groups: - obj_and_grad_fn = self.obj_and_grad_fn - constraint_fn = self.constraint_fn - for i, prm in enumerate(group['params']): - if prm.grad is None: - continue - if not group['u_k']: - group['u_k'].append(prm.data.clone()) - # directly use prm as v_k to save memory - #group['v_k'].append(torch.autograd.Variable(prm.data, requires_grad=True)) - group['v_k'].append(prm) - obj, grad = obj_and_grad_fn(group['v_k'][i]) - group['g_k'].append(grad.data.clone()) # must clone - group['obj_k'].append(obj.data.clone()) - u_k = group['u_k'][i] - v_k = group['v_k'][i] - g_k = group['g_k'][i] - obj_k = group['obj_k'][i] - if not group['a_k']: - group['a_k'].append(torch.ones(1, dtype=g_k.dtype, device=g_k.device)) - group['v_k_1'].append(torch.autograd.Variable(torch.zeros_like(v_k), requires_grad=True)) - group['v_k_1'][i].data.copy_(group['v_k'][i]-group['lr']*g_k) - obj, grad = obj_and_grad_fn(group['v_k_1'][i]) - group['g_k_1'].append(grad.data) - group['obj_k_1'].append(obj.data.clone()) - a_k = group['a_k'][i] - v_k_1 = group['v_k_1'][i] - g_k_1 = group['g_k_1'][i] - obj_k_1 = group['obj_k_1'][i] - if not group['alpha_k']: - group['alpha_k'].append((v_k-v_k_1).norm(p=2) / (g_k-g_k_1).norm(p=2)) - alpha_k = group['alpha_k'][i] - - if group['v_kp1'][i] is None: - group['v_kp1'][i] = torch.autograd.Variable(torch.zeros_like(v_k), requires_grad=True) - v_kp1 = group['v_kp1'][i] - - # line search with alpha_k as hint - a_kp1 = (1 + (4*a_k.pow(2)+1).sqrt()) / 2 - coef = (a_k-1) / a_kp1 - alpha_kp1 = 0 - backtrack_cnt = 0 - max_backtrack_cnt = 10 - - while True: - #with torch.autograd.profiler.profile(use_cuda=True) as prof: - u_kp1 = v_k - alpha_k*g_k - #constraint_fn(u_kp1) - v_kp1.data.copy_(u_kp1 + coef*(u_kp1-u_k)) - # make sure v_kp1 subjects to constraints - # g_kp1 must correspond to v_kp1 - constraint_fn(v_kp1) - - f_kp1, g_kp1 = obj_and_grad_fn(v_kp1) - - #tt = time.time() - alpha_kp1 = torch.sqrt(torch.sum((v_kp1.data-v_k.data)**2) / torch.sum((g_kp1.data-g_k.data)**2)) - #alpha_kp1 = torch.dist(v_kp1.data, v_k.data, p=2) / torch.dist(g_kp1.data, g_k.data, p=2) - backtrack_cnt += 1 - group['obj_eval_count'] += 1 - - #logging.debug("|g_k| = %.6E, |g_kp1| = %.6E" % (g_k.norm(p=2), g_kp1.norm(p=2))) - if alpha_kp1 > 0.95*alpha_k or backtrack_cnt >= max_backtrack_cnt: - alpha_k.data.copy_(alpha_kp1.data) - break - else: - alpha_k.data.copy_(alpha_kp1.data) - - v_k_1.data.copy_(v_k.data) - g_k_1.data.copy_(g_k.data) - obj_k_1.data.copy_(obj_k.data) - - u_k.data.copy_(u_kp1.data) - v_k.data.copy_(v_kp1.data) - g_k.data.copy_(g_kp1.data) - obj_k.data.copy_(f_kp1.data) - a_k.data.copy_(a_kp1.data) - - # although the solution should be u_k - # we need the gradient of v_k - # the update of density weight also requires v_k - # I do not know how to copy u_k back to prm when exit yet - #prm.data.copy_(v_k.data) - - return loss - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py deleted file mode 100644 index afeb6f8a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py +++ /dev/null @@ -1,1044 +0,0 @@ -## -# @file NonLinearPlace.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief Nonlinear placement engine to be called with parameters and placement database -# - -import os -import sys -import time -import pickle -import numpy as np -import logging -import torch -import gzip -import copy -import matplotlib.pyplot as plt -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle -from BasicPlace import * -from PlaceObj import * -from EvalMetrics import * -import NesterovAcceleratedGradientOptimizer -import pdb -import dreamplacefpga.ops.dsp_ram_legalization.dsp_ram_legalization as dsp_ram_legalization - -class NonLinearPlaceFPGA (BasicPlaceFPGA): - """ - @brief Nonlinear placement engine. - It takes parameters and placement database and runs placement flow. - """ - def __init__(self, params, placedb): - """ - @brief initialization. - @param params parameters - @param placedb placement database - """ - super(NonLinearPlaceFPGA, self).__init__(params, placedb) - - def __call__(self, params, placedb): - """ - @brief Top API to solve placement. - @param params parameters - @param placedb placement database - """ - iteration = 0 - blockLegalIter = 0 - all_metrics = [] - - # global placement - if params.global_place_flag: - # global placement may run in multiple stages according to user specification - for global_place_params in params.global_place_stages: - - # we formulate each stage as a 3-nested optimization problem - # f_gamma(g_density(h(x) ; density weight) ; gamma) - # Lgamma Llambda Lsub - # When optimizing an inner problem, the outer parameters are fixed. - # This is a generalization to the eplace/RePlAce approach - - # As global placement may easily diverge, we record the position of best overflow - best_metric = [None] - best_pos = [None] - - if params.gpu: - torch.cuda.synchronize() - tt = time.time() - # construct model and optimizer - density_weight = 0.0 - # construct placement model - model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) - #print("Model constructed in %g ms"%((time.time()-tt)*1000)) - - optimizer_name = global_place_params["optimizer"] - - # determine optimizer - if optimizer_name.lower() == "adam": - optimizer = torch.optim.Adam(self.parameters(), lr=0) - elif optimizer_name.lower() == "sgd": - optimizer = torch.optim.SGD(self.parameters(), lr=0) - elif optimizer_name.lower() == "sgd_momentum": - optimizer = torch.optim.SGD(self.parameters(), lr=0, momentum=0.9, nesterov=False) - elif optimizer_name.lower() == "sgd_nesterov": - optimizer = torch.optim.SGD(self.parameters(), lr=0, momentum=0.9, nesterov=True) - elif optimizer_name.lower() == "nesterov": - optimizer = NesterovAcceleratedGradientOptimizer.NesterovAcceleratedGradientOptimizer(self.parameters(), - lr=0, - obj_and_grad_fn=model.obj_and_grad_fn, - constraint_fn=self.op_collections.move_boundary_op, - ) - else: - assert 0, "unknown optimizer %s" % (optimizer_name) - - logging.info("use %s optimizer" % (optimizer_name)) - - model.train() - # defining evaluation ops - eval_ops = { - "hpwl" : self.op_collections.hpwl_op, - "overflow" : self.op_collections.density_overflow_op - } - if params.routability_opt_flag: - eval_ops.update({ - 'clustering_compatibility_lut': - self.op_collections.clustering_compatibility_lut_area_op, - 'clustering_compatibility_ff': - self.op_collections.clustering_compatibility_ff_area_op, - 'route_utilization': - self.op_collections.route_utilization_map_op, - 'pin_utilization': - self.op_collections.pin_utilization_map_op - }) - #For fence regions - eval_ops.update({ - 'density': - self.op_collections.fence_region_density_merged_op, - "overflow": - self.op_collections.fence_region_density_overflow_merged_op, - }) - - #Initialization moved before printing metrics - if torch.eq(torch.mean(model.density_weight), 0.0): - model.initialize_density_weight(params, placedb) - #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) - - # a function to initialize learning rate - def initialize_learning_rate(pos): - learning_rate = model.estimate_initial_learning_rate(pos) - # update learning rate - for param_group in optimizer.param_groups: - param_group['lr'] = learning_rate.data - - if iteration == 0: - if params.gp_noise_ratio > 0.0: - #logging.info("add %g%% noise" % (params.gp_noise_ratio*100)) - model.op_collections.noise_op(model.data_collections.pos[0], params.gp_noise_ratio) - initialize_learning_rate(model.data_collections.pos[0]) - # the state must be saved after setting learning rate - initial_state = copy.deepcopy(optimizer.state_dict()) - - if params.gpu: - torch.cuda.synchronize() - #logging.info("%s initialization takes %g seconds" % (optimizer_name, (time.time()-tt))) - - # as nesterov requires line search, we cannot follow the convention of other solvers - if optimizer_name.lower() in {"sgd", "adam", "sgd_momentum", "sgd_nesterov"}: - model.obj_and_grad_fn(model.data_collections.pos[0]) - elif optimizer_name.lower() != "nesterov": - assert 0, "unsupported optimizer %s" % (optimizer_name) - - # stopping criteria - def Lgamma_stop_criterion(placedb, Lgamma_step, metrics, stop_mask=None): - with torch.no_grad(): - if len(metrics) > 1: - cur_metric = metrics[-1][-1][-1] - prev_metric = metrics[-2][-1][-1] - - if Lgamma_step > 100 and (((cur_metric.overflow.cpu().numpy() < placedb.targetOverflow).sum() == len(placedb.targetOverflow) and cur_metric.hpwl > prev_metric.hpwl) or - cur_metric.max_density.max() < 1.0) and (placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].sum() == 0 or blockLegalIter >= 5): - logInfo = "Lgamma stopping criteria: " + str(Lgamma_step) + " > 100 and (( OVFL: " - for el in range(placedb.targetOverflow.size): - logInfo += str(round(cur_metric.overflow[el].item(),4)) + " < " + str(placedb.targetOverflow[el]) + "; " - logInfo += " and HPWL " + '{:.4e}'.format(cur_metric.hpwl.item()) + " > " + '{:.4e}'.format(prev_metric.hpwl.item()) + " ) or " - logInfo += str(round(cur_metric.max_density.max().item(),4)) + " < 1.0) and DSP/RAM block legal iter " + str(blockLegalIter) + " >= 5" - logging.info(logInfo) - return True - return False - - def Llambda_stop_criterion(placedb, Lgamma_step, Llambda_density_weight_step, metrics): - with torch.no_grad(): - if len(metrics) > 1: - cur_metric = metrics[-1][-1] - prev_metric = metrics[-2][-1] - if ((cur_metric.overflow.cpu().numpy() < placedb.targetOverflow).sum() == len(placedb.targetOverflow) and - cur_metric.hpwl > prev_metric.hpwl and (placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].sum() == 0 or blockLegalIter >= 5)) or cur_metric.max_density[-1] < 1.0: - logInfo = "Llambda stopping criteria: " + str(Llambda_density_weight_step) + " and (( OVFL: " - for el in range(placedb.targetOverflow.size): - logInfo += str(round(cur_metric.overflow[el].item(),4)) + " < " + str(placedb.targetOverflow[el]) + "; " - logInfo += " and HPWL " + '{:.4e}'.format(cur_metric.hpwl.item()) + " > " + '{:.4e}'.format(prev_metric.hpwl.item()) + " ) or " - logInfo += str(round(cur_metric.max_density.max().item(),4)) + " < 1.0)" - return True - return False - - # use a moving average window for stopping criteria, for an example window of 3 - # 0, 1, 2, 3, 4, 5, 6 - # window2 - # window1 - moving_avg_window = max(min(model.Lsub_iteration // 2, 3), 1) - def Lsub_stop_criterion(Lgamma_step, Llambda_density_weight_step, Lsub_step, metrics): - with torch.no_grad(): - if len(metrics) >= moving_avg_window * 2: - cur_avg_obj = 0 - prev_avg_obj = 0 - for i in range(moving_avg_window): - cur_avg_obj += metrics[-1 - i].objective - prev_avg_obj += metrics[-1 - moving_avg_window - i].objective - cur_avg_obj /= moving_avg_window - prev_avg_obj /= moving_avg_window - threshold = 0.999 - if cur_avg_obj >= prev_avg_obj * threshold: - logging.info("Lsub stopping criteria: %d and %g > %g * %g" % (Lsub_step, cur_avg_obj, prev_avg_obj, threshold)) - return True - return False - - def one_descent_step(Lgamma_step, Llambda_density_weight_step, Lsub_step, iteration, metrics, stop_mask=None): - - # metric for this iteration - cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) - cur_metric.gamma = model.gamma.data - cur_metric.density_weight = model.density_weight.data - metrics.append(cur_metric) - pos = model.data_collections.pos[0] - - # move any out-of-bound cell back to placement region - self.op_collections.move_boundary_op(pos) - optimizer.zero_grad() - cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) - model.overflow = cur_metric.overflow.data.clone() - #logging.debug("evaluation %.3f ms" % ((time.time()-t1)*1000)) - #t2 = time.time() - - # as nesterov requires line search, we cannot follow the convention of other solvers - if optimizer_name.lower() in ["sgd", "adam", "sgd_momentum", "sgd_nesterov"]: - obj, grad = model.obj_and_grad_fn(pos) - cur_metric.objective = obj.data.clone() - elif optimizer_name.lower() != "nesterov": - assert 0, "unsupported optimizer %s" % (optimizer_name) - - # plot placement - if params.plot_flag and (iteration % 100 == 0): - cur_pos = self.pos[0].data.clone().cpu().numpy() - self.plot(params, placedb, iteration, cur_pos) - - logging.info(cur_metric) - - t3 = time.time() - if(model.update_mask is not None): - pos_bk = pos.data.clone() - optimizer.step() - # print(model.update_mask) - for region_id, fence_region_update_flag in enumerate(model.update_mask): - #If there are no elements of resource type, skip - if(fence_region_update_flag == 0 and placedb.num_movable_nodes_fence_region[region_id] > 0): - ### don't update cell location in that region - mask = self.op_collections.fence_region_density_ops[region_id].pos_mask - pos.data.masked_scatter_(mask, pos_bk[mask]) - else: - optimizer.step() - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - #Update locations of pseudo filler nodes - if placedb.num_mlab_nodes > 0: - mlab_locations_x = pos[:placedb.num_physical_nodes][placedb.is_mlab_node].data - mlab_locations_y = pos[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes][placedb.is_mlab_node].data - pos[:placedb.num_nodes][placedb.is_mlab_filler_node == 1].data.copy_(mlab_locations_x) - pos[placedb.num_nodes:][placedb.is_mlab_filler_node == 1].data.copy_(mlab_locations_y) - - # nesterov has already computed the objective of the next step - if optimizer_name.lower() == "nesterov": - cur_metric.objective = optimizer.param_groups[0]['obj_k_1'][0].data.clone() - #print("Nesterov objective %f \n"%(tobj.data.clone())) - #print("HPWL is %g; Obj is %g \n" %(cur_metric.hpwl, cur_metric.objective)) - - # actually reports the metric before step - #logging.info(cur_metric) - # record the best outer cell overflow - if best_metric[0] is None or (best_metric[0].overflow > cur_metric.overflow).sum().item() == cur_metric.overflow.size()[0]: - best_metric[0] = cur_metric - if best_pos[0] is None: - best_pos[0] = self.pos[0].data.clone() - else: - best_pos[0].data.copy_(self.pos[0].data) - - #logging.info("full step %.3f ms" % ((time.time()-t0)*1000)) - - def check_plateau(x, window=10, threshold=0.001): - if(len(x) < window): - return False - x = x[-window:] - return (np.max(x) - np.min(x)) / np.mean(x) < threshold - - def check_divergence(x, window=50, threshold=0.05): - if(len(x) < window): - return False - x = np.array(x[-window:]) - smooth = max(1,int(0.1*window)) - wl_beg, wl_end = np.mean(x[0:smooth,0]), np.mean(x[-smooth:,0]) - overflow_beg, overflow_end = np.mean(x[0:smooth,1]), np.mean(x[-smooth:,1]) - # wl_ratio, overflow_ratio = (wl_end - wl_beg)/wl_beg, (overflow_end - max(placedb.targetOverflow.max(), best_metric[0].overflow))/best_metric[0].overflow - overflow_mean = np.mean(x[:,1]) - overflow_diff = np.maximum(0,np.sign(x[1:,1] - x[:-1,1])).astype(np.float32) - overflow_diff = np.sum(overflow_diff) / overflow_diff.shape[0] - overflow_range = np.max(x[:,1]) - np.min(x[:,1]) - wl_mean = np.mean(x[:,0]) - wl_ratio, overflow_ratio = (wl_mean - best_metric[0].hpwl.item())/best_metric[0].hpwl.item(), (overflow_mean - max(placedb.targetOverflow.max(), best_metric[0].overflow.max().item()))/best_metric[0].overflow.max().item() - if(wl_ratio > threshold*1.2): - if(overflow_ratio > threshold): - print(f"[Warning] Divergence detected: overflow increases too much than best overflow ({overflow_ratio:.4f} > {threshold:.4f})") - return True - elif(overflow_range/overflow_mean < threshold): - print(f"[Warning] Divergence detected: overflow plateau ({overflow_range/overflow_mean:.4f} < {threshold:.4f})") - return True - elif(overflow_diff > 0.6): - print(f"[Warning] Divergence detected: overflow fluctuate too frequently ({overflow_diff:.2f} > 0.6)") - return True - else: - return False - else: - return False - - Lgamma_metrics = all_metrics - - if params.routability_opt_flag: - adjust_area_flag = True - adjust_resource_area_flag = params.adjust_resource_area_flag - adjust_route_area_flag = params.adjust_route_area_flag - adjust_pin_area_flag = params.adjust_pin_area_flag - num_area_adjust = 0 - - Llambda_flat_iteration = 0 - - ### self-adaptive divergence check - overflow_list = np.ones((len(placedb.region_boxes)), dtype=placedb.dtype) - divergence_list = [] - min_perturb_interval = 50 - stop_placement = 0 - last_perturb_iter = -min_perturb_interval - noise_injected_flag = 0 - perturb_counter = 0 - allow_update = 1 - - # Start to compute time for optimization without parsing and initialization - optimization_timer = time.time() - for Lgamma_step in range(model.Lgamma_iteration): - Lgamma_metrics.append([]) - Llambda_metrics = Lgamma_metrics[-1] - for Llambda_density_weight_step in range(model.Llambda_density_weight_iteration): - Llambda_metrics.append([]) - Lsub_metrics = Llambda_metrics[-1] - for Lsub_step in range(model.Lsub_iteration): - ## Divergence threshold should decrease as overflow decreases - ## Only detect divergence when overflow is relatively low but not too low - if(((placedb.targetOverflow * 1.1 < overflow_list).sum() == len(placedb.targetOverflow) and (overflow_list < placedb.targetOverflow).sum() == len(placedb.targetOverflow)) and check_divergence(divergence_list, window=3, threshold=0.01 * overflow_list)): - self.pos[0].data.copy_(best_pos[0].data) - stop_placement = 1 - allow_update = 0 - logging.error( - "possible DIVERGENCE detected, roll back to the best position recorded and switch to ZerothOrderSearch of overflow and hpwl" - ) - - ct0 = time.time() - one_descent_step(Lgamma_step, Llambda_density_weight_step, Lsub_step, iteration, Lsub_metrics) - #print("Time for one step: %g ms" %((time.time()-ct0)*1000)) - iteration += 1 - if model.lock_mask is not None and model.lock_mask[placedb.dsp_ram_compIds].sum() == len(placedb.dsp_ram_compIds): - blockLegalIter += 1 - # stopping criteria - if Lsub_stop_criterion(Lgamma_step, Llambda_density_weight_step, Lsub_step, Lsub_metrics): - break - Llambda_flat_iteration += 1 - # update density weight - if Llambda_flat_iteration > 1: - model.op_collections.update_density_weight_op(Llambda_metrics[-1][-1], Llambda_metrics[-2][-1] if len(Llambda_metrics) > 1 else Lgamma_metrics[-2][-1][-1], Llambda_flat_iteration) - #logging.debug("update density weight %.3f ms" % ((time.time()-t2)*1000)) - if Llambda_stop_criterion(placedb, Lgamma_step, Llambda_density_weight_step, Llambda_metrics): - break - - if (params.routability_opt_flag and num_area_adjust < params.max_num_area_adjust and - (Llambda_metrics[-1][-1].overflow[placedb.slice_compIds] < self.data_collections.node_area_adjust_overflow[placedb.slice_compIds]).sum().item() == len(placedb.slice_compIds)): - pos = model.data_collections.pos[0] - - route_utilization_map = None - pin_utilization_map = None - resource_areas = None - if adjust_route_area_flag: - #Use RUDY for FPGA - route_utilization_map = model.op_collections.route_utilization_map_op(pos) - if params.plot_flag: - path = "%s/%s" % (params.result_dir, params.design_name()) - figname = "%s/plot/rudy%d.png" % (path, num_area_adjust) - os.system("mkdir -p %s" % (os.path.dirname(figname))) - plt.imsave(figname, route_utilization_map.data.cpu().numpy().T, origin='lower') - if adjust_pin_area_flag: - pin_utilization_map = model.op_collections.pin_utilization_map_op(pos) - if params.plot_flag: - path = "%s/%s" % (params.result_dir, params.design_name()) - figname = "%s/plot/pin%d.png" % (path, num_area_adjust) - os.system("mkdir -p %s" % (os.path.dirname(figname))) - plt.imsave(figname, pin_utilization_map.data.cpu().numpy().T, origin='lower') - # Compute LUT/FF clustering compatibility optimized resource areas - if adjust_resource_area_flag: - lut_resource_areas = model.op_collections.clustering_compatibility_lut_area_op(pos) - ff_resource_areas = model.op_collections.clustering_compatibility_ff_area_op(pos) - resource_areas = lut_resource_areas + ff_resource_areas - - adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag = model.op_collections.adjust_node_area_op( - pos, - resource_areas, - route_utilization_map, - pin_utilization_map - ) - content = "routability optimization round %d: adjust area flags = (%d, %d, %d, %d)" % (num_area_adjust, adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag) - logging.info(content) - if adjust_area_flag: - num_area_adjust += 1 - - #Record position before instance area update - best_metric[0] = Llambda_metrics[-1][-1] - if best_pos[0] is None: - best_pos[0] = model.data_collections.pos[0].data.clone() - else: - best_pos[0].data.copy_(model.data_collections.pos[0].data) - - #Compute new node areas - for el in range(len(placedb.slice_compIds)): - rsrcId = placedb.comp2rsrcId_map[el] - mask = model.data_collections.node2fence_region_map == rsrcId - model.data_collections.total_movable_node_area_fence_region[el] = (model.data_collections.node_size_x[:model.data_collections.num_physical_nodes] * model.data_collections.node_size_y[:model.data_collections.num_physical_nodes] * mask).sum() - - #Update node areas - model.data_collections.node_areas = model.data_collections.node_size_x * model.data_collections.node_size_y - - # restart Llambda - model.op_collections.density_op.reset(model.data_collections) - model.op_collections.density_overflow_op.reset() - model.op_collections.pin_utilization_map_op.reset() - for fence_reg in model.op_collections.fence_region_density_ops: - fence_reg.reset(model.data_collections) - - cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) - cur_metric.gamma = model.gamma.data - cur_metric.density_weight = model.density_weight.data - cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) - model.overflow = cur_metric.overflow.data.clone() - - model.op_collections.update_gamma_op(Lgamma_step, model.overflow) - model.reset_density_weight(params, placedb, 0.1) - #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) - - # load state to restart the optimizer - optimizer.load_state_dict(initial_state) - # must after loading the state - initialize_learning_rate(pos) - # increase iterations of the sub problem to slow down the search - model.Lsub_iteration = model.routability_Lsub_iteration - - break - - ##DSP/RAM legalization condition check - if len(placedb.dsp_ram_compIds) > 0 and placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].max() > 0 and (Llambda_metrics[-1][-1].overflow < model.data_collections.targetOverflow).sum().item() == model.data_collections.targetOverflow.size()[0]: - pos = model.data_collections.pos[0] - if model.lock_mask is not None and model.lock_mask[placedb.dsp_ram_compIds].sum() == len(placedb.dsp_ram_compIds): - break - - ## plot placement - #if params.plot_flag: - # cur_pos = pos.data.clone().cpu().numpy() - # self.plot(params, placedb, iteration, cur_pos) - # iteration += 1 - - #Legalize DSP/RAM at the end of Global placement - for lgId in placedb.dsp_ram_rsrcIds: - if placedb.node_count[lgId] > 0: - movVal = dsp_ram_legalization.LegalizeDSPRAMFunction.legalize(pos, placedb, lgId, model) - logging.info("Legalized %d %s instances with maxMov = %g and avgMov = %g" % - (placedb.node_count[lgId], placedb.rsrcTypes[lgId], movVal[0], movVal[1])) - - ## plot placement - #if params.plot_flag: - # cur_pos = pos.data.clone().cpu().numpy() - # self.plot(params, placedb, iteration, cur_pos) - # iteration += 1 - - #Lock DSP/RAM locations - model.lock_mask[placedb.dsp_ram_compIds] = True - model.update_mask = ~model.lock_mask - pos.grad[0:placedb.num_physical_nodes].data.masked_fill_(model.data_collections.dsp_ram_mask, 0.0) - pos.grad[placedb.num_nodes:placedb.num_nodes+placedb.num_physical_nodes].data.masked_fill_(model.data_collections.dsp_ram_mask, 0.0) - - # restart Llambda - for fence_reg in model.op_collections.fence_region_density_ops: - fence_reg.setLockDSPRAM() - - #Restart place params - cur_metric = EvalMetricsFPGA(iteration, (Lgamma_step, Llambda_density_weight_step, Lsub_step)) - cur_metric.gamma = model.gamma.data - cur_metric.density_weight = model.density_weight.data - cur_metric.evaluate(placedb, eval_ops, pos, model.data_collections) - model.overflow = cur_metric.overflow.data.clone() - - model.op_collections.update_gamma_op(Lgamma_step, model.overflow) - - model.reset_density_weight(params, placedb, 1.0) - #logging.info("density_weight = [%s]" % ", ".join(["%.3E" % i for i in model.density_weight.cpu().numpy().tolist()])) - - # load state to restart the optimizer - optimizer.load_state_dict(initial_state) - # must after loading the state - initialize_learning_rate(pos) - # increase iterations of the sub problem to slow down the search - model.Lsub_iteration = model.routability_Lsub_iteration - - break - - # gradually reduce gamma to tradeoff smoothness and accuracy - model.op_collections.update_gamma_op(Lgamma_step, Llambda_metrics[-1][-1].overflow) - - if Lgamma_stop_criterion(placedb, Lgamma_step, Lgamma_metrics) or stop_placement == 1: - break - - # update learning rate - if optimizer_name.lower() in ["sgd", "adam", "sgd_momentum", "sgd_nesterov", "cg"]: - if 'learning_rate_decay' in global_place_params: - for param_group in optimizer.param_groups: - param_group['lr'] *= global_place_params['learning_rate_decay'] - -######################################## -######################################## - - def solve_problem_2(pos_w, admm_multiplier, non_fence_regions_ex, non_fence_regions, iteration): - def check_valid(regions, pos_x, pos_y, pos_xh, pos_yh, valid_margin_x=0, valid_margin_y=0): - if(type(regions) == list): - regions = np.concatenate(regions,0) - valid_mask = torch.ones_like(pos_x, dtype=torch.bool) - for sub_region in regions: - xll, yll, xhh, yhh = sub_region - valid_margin_x = min((xhh-xll)/2, valid_margin_x) - valid_margin_y = min((yhh-yll)/2, valid_margin_y) - valid_mask.masked_fill_((pos_x < xhh-valid_margin_x) & (pos_xh > xll+valid_margin_x) & (pos_y < yhh-valid_margin_y) & (pos_yh > yll+valid_margin_y), 0) - return valid_mask - - num_nodes = placedb.num_nodes - num_movable_nodes = placedb.num_movable_nodes - - pos_g = pos_w + admm_multiplier # minimize the L2 norm - # node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map[:num_movable_nodes]).to(pos_g.device) - node2fence_region_map = self.data_collections.node2fence_region_map - - pos_x, pos_y = pos_g[:num_movable_nodes], pos_g[num_nodes:num_nodes + num_movable_nodes] - node_size_x, node_size_y = model.data_collections.node_size_x[:num_movable_nodes], model.data_collections.node_size_y[:num_movable_nodes] - num_regions = len(placedb.region_boxes) - - regions = placedb.region_boxes - # margin = 20 * 0.997**iteration - margin_x = placedb.bin_size_x * min(1,4*0.997**iteration) - margin_y = placedb.bin_size_y * min(1,4*0.997**iteration) - - # valid_margin = 1000 * 0.995**iteration - valid_margin_x = placedb.bin_size_x * 200*0.996**iteration - valid_margin_y = placedb.bin_size_y * 200*0.996**iteration - # valid_margin = 0 if valid_margin < 5 else valid_margin - ### move cells into fence regions - for i in range(num_regions): - if i in placedb.fixed_rsrcIds: - continue - mask = (node2fence_region_map == i) - pos_x_i, pos_y_i = pos_x[mask], pos_y[mask] - num_movable_nodes_i = pos_x_i.numel() - node_size_x_i, node_size_y_i = node_size_x[mask], node_size_y[mask] - pos_xh_i = pos_x_i + node_size_x_i - pos_yh_i = pos_y_i + node_size_y_i - regions_i = regions[i] # [n_regions, 4] - delta_min = torch.empty(num_movable_nodes_i, device=pos_x.device).fill_(((placedb.xh-placedb.xl)**2+(placedb.yh-placedb.yl)**2)) - delta_x_min = torch.zeros_like(delta_min) - delta_y_min = torch.zeros_like(delta_min) - - valid_mask = check_valid(non_fence_regions[i], pos_x_i, pos_y_i, pos_xh_i, pos_yh_i, valid_margin_x, valid_margin_y) - - for sub_region in regions_i: - delta_x = torch.zeros_like(delta_min) - delta_y = torch.zeros_like(delta_min) - xl, yl, xh, yh = sub_region - - # on the left - mask_l = (pos_x_i < xl + margin_x).masked_fill_(valid_mask, 0) - # on the right - mask_r = (pos_xh_i > xh - margin_x).masked_fill_(valid_mask, 0) - # on the top - mask_t = (pos_yh_i > yh - margin_y).masked_fill_(valid_mask, 0) - # on the bottom - mask_b = (pos_y_i < yl + margin_y).masked_fill_(valid_mask, 0) - - # x replacement for left cell - delta_x.masked_scatter_(mask_l, xl + margin_x - pos_x_i[mask_l]) - # x replacement for right cell - delta_x.masked_scatter_(mask_r, xh - margin_x - pos_xh_i[mask_r]) - # delta_x.masked_fill_(~(mask_l | mask_r), 0) - # y replacement for top cell - delta_y.masked_scatter_(mask_t, yh - margin_y - pos_yh_i[mask_t]) - # y replacement for bottom cell - delta_y.masked_scatter_(mask_b, yl + margin_y - pos_y_i[mask_b]) - # delta_y.masked_fill_(~(mask_t | mask_b), 0) - # update minimum replacement - delta_i = (delta_x ** 2 + delta_y ** 2) - update_mask = delta_i < delta_min - - delta_x_min.masked_scatter_(update_mask, delta_x[update_mask]) - delta_y_min.masked_scatter_(update_mask, delta_y[update_mask]) - delta_min.masked_scatter_(update_mask, delta_i[update_mask]) - - # update the minimum replacement for subregions - pos_x.masked_scatter_(mask, pos_x_i + delta_x_min) - pos_y.masked_scatter_(mask, pos_y_i + delta_y_min) - - ### move cells out of fence regions - # margin = 0 - # valid_margin = 100 * 0.99**iteration - exclude_mask = (node2fence_region_map == placedb.rIOIdx) | (node2fence_region_map == placedb.rPLLIdx) - pos_x_ex, pos_y_ex = pos_x[exclude_mask], pos_y[exclude_mask] - node_size_x_ex, node_size_y_ex = node_size_x[exclude_mask], node_size_y[exclude_mask] - pos_xh_ex = pos_x_ex + node_size_x_ex - pos_yh_ex = pos_y_ex + node_size_y_ex - - delta_min = torch.empty(pos_x_ex.numel(), device=pos_x.device).fill_(((placedb.xh-placedb.xl)**2+(placedb.yh-placedb.yl)**2)) - delta_x_min = torch.zeros_like(delta_min) - delta_y_min = torch.zeros_like(delta_min) - ### don't move valid cells - valid_mask = check_valid(regions, pos_x_ex, pos_y_ex, pos_xh_ex, pos_yh_ex, valid_margin_x, valid_margin_y) - - for sub_region in non_fence_regions_ex: - delta_x = torch.zeros_like(delta_min) - delta_y = torch.zeros_like(delta_min) - xl, yl, xh, yh = sub_region - - # on the left - mask_l = (pos_x_ex < xl).masked_fill_(valid_mask, 0) - # on the right - mask_r = (pos_xh_ex > xh).masked_fill_(valid_mask, 0) - # on the top - mask_t = (pos_yh_ex > yh).masked_fill_(valid_mask, 0) - # on the bottom - mask_b = (pos_y_ex < yl).masked_fill_(valid_mask, 0) - - # x replacement for left cell - delta_x.masked_scatter_(mask_l, xl + margin_x - pos_x_ex[mask_l]) - # x replacement for right cell - delta_x.masked_scatter_(mask_r, xh - margin_x - pos_xh_ex[mask_r]) - # delta_x.masked_fill_(~(mask_l | mask_r), 0) - # y replacement for top cell - delta_y.masked_scatter_(mask_t, yh - margin_y - pos_yh_ex[mask_t]) - # y replacement for bottom cell - delta_y.masked_scatter_(mask_b, yl + margin_y - pos_y_ex[mask_b]) - # delta_y.masked_fill_(~(mask_t | mask_b), 0) - # update minimum replacement - delta_i = (delta_x ** 2 + delta_y ** 2) - update_mask = delta_i < delta_min - - delta_x_min.masked_scatter_(update_mask, delta_x[update_mask]) - delta_y_min.masked_scatter_(update_mask, delta_y[update_mask]) - delta_min.masked_scatter_(update_mask, delta_i[update_mask]) - - # update the minimum replacement for subregions - pos_x.masked_scatter_(exclude_mask, pos_x_ex + delta_x_min) - pos_y.masked_scatter_(exclude_mask, pos_y_ex + delta_y_min) - - ### write back solution - fillers = np.zeros(self.num_filler_nodes, dtype=placedb.dtype) - fmask = placedb.io_mask - fillMask = np.concatenate((fmask,fillers.astype(bool),fmask,fillers.astype(bool)),axis=0) - allLoc = np.concatenate((placedb.node_x, fillers, placedb.node_y, fillers),axis=0) - omask = ~fmask - allMask = ~fillMask - res = pos_g.data.clone() - res.data[:num_movable_nodes].copy_(pos_x) - res.data[num_nodes:num_nodes + num_movable_nodes].copy_(pos_y) - return res - - # in case of divergence, use the best metric - ### always rollback to best outer cell overflow - last_metric = all_metrics[-1][-1][-1] - self.targetOverflow = torch.tensor(placedb.targetOverflow, dtype=torch.float, device=self.device) - if last_metric.overflow.max() > max(self.targetOverflow.max(), best_metric[0].overflow.max()) and last_metric.hpwl > best_metric[0].hpwl: - self.pos[0].data.copy_(best_pos[0].data) - logging.error("possible DIVERGENCE detected, roll back to the best position recorded") - all_metrics.append([best_metric]) - logging.info(best_metric[0]) - - #Legalize DSP/RAMs if any - if ((len(placedb.dsp_ram_compIds) > 0 and - placedb.num_movable_nodes_fence_region[placedb.dsp_ram_compIds].max() > 0) and - (model.lock_mask is not None and - model.lock_mask[placedb.dsp_ram_compIds].sum() != len(placedb.dsp_ram_compIds))): - - for lgId in placedb.dsp_ram_rsrcIds: - if placedb.node_count[lgId] > 0: - movVal = dsp_ram_legalization.LegalizeDSPRAMFunction.legalize(pos, placedb, lgId, model) - logging.info("Legalized %s with maxMov = %g and avgMov = %g" % (placedb.rsrcTypes[lgId], movVal[0], movVal[1])) - model.lock_mask[placedb.dsp_ram_compIds] = True - model.update_mask = ~model.lock_mask - - #logging.info("optimizer %s takes %.3f seconds" % (optimizer_name, time.time()-tt)) - # recover node size and pin offset for legalization, since node size is adjusted in global placement - if params.routability_opt_flag: - with torch.no_grad(): - # convert lower left to centers - # convert lower left to centers - #self.pos[0][:placedb.num_movable_nodes].add_( - # self.data_collections. - # node_size_x[:placedb.num_movable_nodes] / 2) - #self.pos[0][placedb.num_nodes:placedb.num_nodes + - # placedb.num_movable_nodes].add_( - # self.data_collections. - # node_size_y[:placedb.num_movable_nodes] / - # 2) - self.data_collections.node_size_x.copy_( - self.data_collections.original_node_size_x) - self.data_collections.node_size_y.copy_( - self.data_collections.original_node_size_y) - ## use fixed centers as the anchor - #self.pos[0][:placedb.num_movable_nodes].sub_( - # self.data_collections. - # node_size_x[:placedb.num_movable_nodes] / 2) - #self.pos[0][placedb.num_nodes:placedb.num_nodes + - # placedb.num_movable_nodes].sub_( - # self.data_collections. - # node_size_y[:placedb.num_movable_nodes] / - # 2) - self.data_collections.pin_offset_x.copy_( - self.data_collections.original_pin_offset_x) - self.data_collections.pin_offset_y.copy_( - self.data_collections.original_pin_offset_y) - #else: - # cur_metric = EvalMetricsFPGA(iteration) - # all_metrics.append(cur_metric) - # cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) - # logging.info(cur_metric) - - # dump global placement solution for legalization - if params.dump_global_place_solution_flag: - self.dump(params, placedb, self.pos[0].cpu(), "%s.lg.pklz" %(params.design_name())) - - half_pos = self.pos[0].shape[0]//2 - - ## plot placement - #if params.plot_flag: - # cur_pos = self.pos[0].data.clone().cpu().numpy() - # self.plot(params, placedb, 12345, cur_pos) - - if params.global_place_flag == 1 and placedb.num_ccNodes > 0: - #Update sizes & GP location of cc nodes - model.data_collections.org_node_size_x[placedb.new2org_node_map[:placedb.num_movable_nodes]] = model.data_collections.node_size_x[:placedb.num_movable_nodes] - model.data_collections.org_node_size_y[placedb.new2org_node_map[:placedb.num_movable_nodes]] = model.data_collections.node_size_y[:placedb.num_movable_nodes] - model.data_collections.org_node_size_x[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = model.data_collections.node_size_x[placedb.num_movable_nodes:placedb.num_physical_nodes] - model.data_collections.org_node_size_y[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = model.data_collections.node_size_y[placedb.num_movable_nodes:placedb.num_physical_nodes] - - model.data_collections.org_node_x[placedb.new2org_node_map[:placedb.num_movable_nodes]] = self.pos[0][:placedb.num_movable_nodes].data - model.data_collections.org_node_y[placedb.new2org_node_map[:placedb.num_movable_nodes]] = self.pos[0][half_pos:half_pos+placedb.num_movable_nodes].data - model.data_collections.org_node_x[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = self.pos[0][placedb.num_movable_nodes:placedb.num_physical_nodes].data - model.data_collections.org_node_size_y[placedb.org_num_movable_nodes:placedb.org_num_physical_nodes] = self.pos[0][half_pos+placedb.num_movable_nodes:half_pos+placedb.num_physical_nodes] - - ccYLocIncr = 1/placedb.SLICE_CAPACITY - for ccId in range(placedb.num_carry_chains): - org_cc_indices = np.where(placedb.org_node2ccId_map == ccId)[0] - curr_cc_index = placedb.cc2nodeId_map[ccId] - elCount = placedb.cc_element_count[ccId] - model.data_collections.org_node_size_x[org_cc_indices] = model.data_collections.node_size_x[curr_cc_index] - model.data_collections.org_node_size_y[org_cc_indices] = model.data_collections.node_size_y[curr_cc_index]/elCount - model.data_collections.org_node_x[org_cc_indices] = self.pos[0][curr_cc_index].data - yoffset = torch.arange(elCount-1, -1, -1, dtype=self.data_collections.dtype, device=self.device)*ccYLocIncr - model.data_collections.org_node_y[org_cc_indices] = self.pos[0][half_pos:][curr_cc_index].data + yoffset - #print("Updated Carry chain %d at (%.2f, %.2f) => org (%.2f, %.2f)"% - # (ccId, self.pos[0][curr_cc_index].data, self.pos[0][half_pos:][curr_cc_index].data, - # model.data_collections.org_node_x[org_cc_indices[0]], - # model.data_collections.org_node_y[org_cc_indices[0]])) - - model.data_collections.org_node_areas = model.data_collections.org_node_size_x * model.data_collections.org_node_size_y - placedb.num_movable_nodes = placedb.org_num_movable_nodes - placedb.num_physical_nodes = placedb.org_num_physical_nodes - self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_x) - self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_y) - - ## plot placement - #if params.plot_flag: - # cur_pos = self.pos[0].data.clone().cpu().numpy() - # self.plot(params, placedb, 5678, cur_pos) - - # legalization - if params.legalize_flag: - if params.global_place_flag == 0: - #Load from GP results - for global_place_params in params.global_place_stages: - - if params.gpu: - torch.cuda.synchronize() - tt = time.time() - # construct model and optimizer - density_weight = 0.0 - # construct placement model - model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) - print("Model constructed in %g ms"%((time.time()-tt)*1000)) - - place_file=params.global_place_sol - #logging.info("Reading %s" % (place_file)) - with open (place_file, "r") as f: - for line in f: - tokens = line.split() - if len(tokens) > 0: - if tokens[0] in placedb.node_name2id_map: - nodeId = placedb.node_name2id_map[tokens[0]] - self.data_collections.node_x[nodeId].data.fill_(placedb.dtype(tokens[1])) - self.data_collections.node_y[nodeId].data.fill_(placedb.dtype(tokens[2])) - self.data_collections.node_z[nodeId].data.fill_(int(tokens[3])) - if placedb.num_ccNodes: - nodeId = placedb.org_node_name2id_map[tokens[0]] - self.data_collections.org_node_x[nodeId].data.fill_(placedb.dtype(tokens[1])) - self.data_collections.org_node_y[nodeId].data.fill_(placedb.dtype(tokens[2])) - self.data_collections.org_node_z[nodeId].data.fill_(int(tokens[3])) - self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.node_x) - self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.node_y) - logging.info("Read Global Placement solution from %s" % (place_file)) - cur_metric = EvalMetricsFPGA(iteration) - all_metrics.append(cur_metric) - cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) - logging.info(cur_metric) - iteration += 1 - - #Break carry chain nodes as single entity - if placedb.num_ccNodes > 0: - placedb.num_movable_nodes = placedb.org_num_movable_nodes - placedb.num_physical_nodes = placedb.org_num_physical_nodes - self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_x) - self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.org_node_y) - node_areas = self.data_collections.org_node_areas - lut_mask = placedb.org_lut_mask - flop_mask = placedb.org_flop_mask - lut_flop_mask = placedb.org_lut_flop_mask - node_z = self.data_collections.org_node_z - else: - node_areas = self.data_collections.node_areas - lut_mask = placedb.lut_mask - flop_mask = placedb.flop_mask - lut_flop_mask = placedb.lut_flop_mask - node_z = self.data_collections.node_z - - #Perform sorting of pin, net, node - _, sortedNetIdx = torch.sort(self.data_collections.net2pincount_map) - sortedNetIdx = sortedNetIdx.to(torch.int32) - _, sortedNetMap = torch.sort(sortedNetIdx) - sortedNetMap = sortedNetMap.to(torch.int32) - - _, sortedPinIdx = torch.sort(sortedNetMap[self.data_collections.pin2net_map.to(torch.long)]) - sortedPinIdx = sortedPinIdx.to(torch.int32) - _, sortedPinMap = torch.sort(sortedPinIdx) - sortedPinMap = sortedPinMap.to(torch.int32) - - node2pinId0 = self.op_collections.sort_node2pin_op(sortedPinMap) - #node2pinId0 = torch.zeros(placedb.num_physical_nodes, dtype=torch.int32) - #for el in range(placedb.num_physical_nodes): - # startId = data_collections.flat_node2pin_start_map[el] - # endId = data_collections.flat_node2pin_start_map[el+1] - # _, sorted_node2pin_idx = torch.sort(sorted_pin_map[data_collections.flat_node2pin_map.to(torch.long)[startId:endId]]) - # data_collections.flat_node2pin_map[startId:endId].data.copy_(data_collections.flat_node2pin_map[startId:endId][sorted_node2pin_idx].data) - # node2pinId0[el] = sorted_pin_map[data_collections.flat_node2pin_map[startId]] - - _, sortedNodeIdx = torch.sort(node2pinId0) - sortedNodeIdx = sortedNodeIdx.to(torch.int32) - - _, sortedNodeMap = torch.sort(sortedNodeIdx) - sortedNodeMap = sortedNodeMap.to(torch.int32) - - tt = time.time() - - if placedb.num_ccNodes == 0: - preconditioner = model.precondWL[:placedb.num_physical_nodes] - else: - preconditioner = model.lg_precondWL[:placedb.num_physical_nodes] - - self.op_collections.lut_ff_legalization_op.initialize(self.pos[0], preconditioner, sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap) - - DLStatus = 1 - dlIter = 0 - - #For runDLIter stopping criteria - MAX_DL_ITERS=100 - MIN_DL_ITERS=50 - ITERS_INCREASE=50 - STOP_ITERS=150 - STABLE_ITER_COUNT=5 - REM_INSTANCE_RATIO=0.09 - activeStatus = torch.zeros(placedb.num_sites_x*placedb.num_sites_y, dtype=torch.int, device=self.device) - illegalStatus = torch.zeros(placedb.num_physical_nodes, dtype=torch.int, device=self.device) - - iter_stable = 0 - prevAct = 0 - - while (DLStatus == 1): - self.op_collections.lut_ff_legalization_op.runDLIter(self.pos[0], preconditioner, sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap, activeStatus, illegalStatus, dlIter) - - if prevAct == illegalStatus.sum().item() + activeStatus.sum().item(): - iter_stable = iter_stable + 1 - else: - iter_stable = 0 - - dlIter = dlIter+1 - if activeStatus.sum().item() > 0: - DLStatus = 1 - elif illegalStatus.sum().item() > 0: - DLStatus = -1 - else: - DLStatus = 0 - - prevAct=illegalStatus.sum().item() + activeStatus.sum().item() - - if dlIter > STOP_ITERS or (dlIter > MIN_DL_ITERS and iter_stable > STABLE_ITER_COUNT): - DLStatus = 0 - - if dlIter > MAX_DL_ITERS and iter_stable < STABLE_ITER_COUNT: - if illegalStatus.sum().item() < REM_INSTANCE_RATIO*placedb.num_physical_nodes: - DLStatus = 0 - else: - MAX_DL_ITERS += ITERS_INCREASE - - #Use inflated instance areas for ripUP & greedy LG - avgLUTArea = node_areas[:placedb.num_physical_nodes][lut_mask].sum() - avgLUTArea /= placedb.node_count[placedb.rLUTIdx] - avgFFArea = node_areas[:placedb.num_physical_nodes][flop_mask].sum() - avgFFArea /= placedb.node_count[placedb.rFFIdx] - #Inst Areas - inst_areas = node_areas[:placedb.num_physical_nodes].detach().clone() - inst_areas[~lut_flop_mask] = 0.0 #Area of non SLICE nodes set to 0.0 - inst_areas[lut_mask] /= avgLUTArea - inst_areas[flop_mask] /= avgFFArea - - self.pos[0].data.copy_(self.op_collections.lut_ff_legalization_op.ripUP_Greedy_slotAssign(self.pos[0], preconditioner, node_z[:placedb.num_movable_nodes], sortedNodeMap, sortedNodeIdx, sortedNetMap, sortedNetIdx, sortedPinMap, inst_areas)) - - #Terminate if legalization has errors - if (self.pos[0] == -1).sum().item() > 0: - sys.exit("[ERROR] " + str((self.pos[0] == -1).sum().item()) + " instances were not legalized - Please ensure there is sufficient space in sitemap and/or revisit LG algorithm") - - logging.info("legalization takes %.3f seconds" % (time.time()-tt)) - cur_metric = EvalMetricsFPGA(iteration) - all_metrics.append(cur_metric) - cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) - logging.info(cur_metric) - iteration += 1 - - # recover node size and pin offset for plot, since node size is adjusted in global placement - if params.routability_opt_flag: - with torch.no_grad(): - # convert lower left to centers - # convert lower left to centers - #self.pos[0][:placedb.num_movable_nodes].add_( - # self.data_collections. - # node_size_x[:placedb.num_movable_nodes] / 2) - #self.pos[0][placedb.num_nodes:placedb.num_nodes + - # placedb.num_movable_nodes].add_( - # self.data_collections. - # node_size_y[:placedb.num_movable_nodes] / - # 2) - self.data_collections.node_size_x.copy_( - self.data_collections.original_node_size_x) - self.data_collections.node_size_y.copy_( - self.data_collections.original_node_size_y) - ## use fixed centers as the anchor - #self.pos[0][:placedb.num_movable_nodes].sub_( - # self.data_collections. - # node_size_x[:placedb.num_movable_nodes] / 2) - #self.pos[0][placedb.num_nodes:placedb.num_nodes + - # placedb.num_movable_nodes].sub_( - # self.data_collections. - # node_size_y[:placedb.num_movable_nodes] / - # 2) - self.data_collections.pin_offset_x.copy_( - self.data_collections.original_pin_offset_x) - self.data_collections.pin_offset_y.copy_( - self.data_collections.original_pin_offset_y) - - # plot placement - #if params.plot_flag: - # self.plot(params, placedb, iteration, self.pos[0].data.clone().cpu().numpy()) - - # dump legalization solution for detailed placement - if params.dump_legalize_solution_flag: - self.dump(params, placedb, self.pos[0].cpu(), "%s.dp.pklz" %(params.design_name())) - - # detailed placement - if params.detailed_place_flag: - place_file=params.lg_place_sol - if params.global_place_flag == 0 and params.legalize_flag == 0 and place_file != "": - #Load legal placement results from file - for global_place_params in params.global_place_stages: - - if params.gpu: - torch.cuda.synchronize() - tt = time.time() - # construct model and optimizer - density_weight = 0.0 - # construct placement model - model = PlaceObjFPGA(density_weight, params, placedb, self.data_collections, self.op_collections, global_place_params).to(self.data_collections.pos[0].device) - print("Model constructed in %g ms"%((time.time()-tt)*1000)) - - with open (place_file, "r") as f: - for line in f: - tokens = line.split() - if len(tokens) > 0: - nodeId = placedb.node_name2id_map[tokens[0]] - self.data_collections.node_x[nodeId].data.fill_(float(tokens[1])) - self.data_collections.node_y[nodeId].data.fill_(float(tokens[2])) - self.data_collections.node_z[nodeId].data.fill_(int(tokens[3])) - self.pos[0][:placedb.num_physical_nodes].data.copy_(self.data_collections.node_x) - self.pos[0][half_pos:half_pos+placedb.num_physical_nodes].data.copy_(self.data_collections.node_y) - - ##Update locations for all instances from placement solution - logging.info("Read Legalized Placement solution from %s" % (place_file)) - cur_metric = EvalMetricsFPGA(iteration) - all_metrics.append(cur_metric) - cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) - logging.info(cur_metric) - iteration += 1 - - ## detailed placement - #if params.detailed_place_flag: - # tt = time.time() - # self.pos[0].data.copy_(self.op_collections.detailed_place_op(self.pos[0])) - # logging.info("detailed placement takes %.3f seconds" % (time.time()-tt)) - # cur_metric = EvalMetricsFPGA(iteration) - # all_metrics.append(cur_metric) - # cur_metric.evaluate(placedb, {"hpwl" : self.op_collections.hpwl_op}, self.pos[0]) - # logging.info(cur_metric) - # iteration += 1 - - if placedb.num_ccNodes: - node_z = self.data_collections.org_node_z - else: - node_z = self.data_collections.node_z - half_pos = self.pos[0].shape[0]//2 - - # save results - cur_pos = self.pos[0].data.clone().cpu().numpy() - node_z = node_z[:placedb.num_movable_nodes].data.clone().cpu().numpy() - # apply solution - placedb.apply( - cur_pos[0:placedb.num_movable_nodes], - cur_pos[half_pos:half_pos + placedb.num_movable_nodes], - node_z) - - # plot placement - if params.plot_flag: - self.plot(params, placedb, iteration, cur_pos) - - return all_metrics - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py deleted file mode 100644 index 7750a1a0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py +++ /dev/null @@ -1,179 +0,0 @@ -## -# @file Params.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief User parameters -# - -import os -import sys -import json -import math -from collections import OrderedDict -import pdb - -class Params: - """ - @brief Parameter class - """ - def __init__(self): - """ - @brief initialization - """ - filename = os.path.join(os.path.dirname(__file__), 'params.json') - self.__dict__ = {} - params_dict = {} - with open(filename, "r") as f: - params_dict = json.load(f, object_pairs_hook=OrderedDict) - for key, value in params_dict.items(): - if 'default' in value: - self.__dict__[key] = value['default'] - else: - self.__dict__[key] = None - self.__dict__['params_dict'] = params_dict - - def printWelcome(self): - """ - @brief print welcome message - """ - content = """\ -======================================================== - DREAMPlaceFPGA -========================================================""" - print(content) - - def printHelp(self): - """ - @brief print help message for JSON parameters - """ - content = self.toMarkdownTable() - print(content) - - def toMarkdownTable(self): - """ - @brief convert to markdown table - """ - key_length = len('JSON Parameter') - key_length_map = [] - default_length = len('Default') - default_length_map = [] - description_length = len('Description') - description_length_map = [] - - def getDefaultColumn(key, value): - if sys.version_info.major < 3: # python 2 - flag = isinstance(value['default'], unicode) - else: #python 3 - flag = isinstance(value['default'], str) - if flag and not value['default'] and 'required' in value: - return value['required'] - else: - return value['default'] - - for key, value in self.params_dict.items(): - key_length_map.append(len(key)) - default_length_map.append(len(str(getDefaultColumn(key, value)))) - description_length_map.append(len(value['descripton'])) - key_length = max(key_length, key_length_map[-1]) - default_length = max(default_length, default_length_map[-1]) - description_length = max(description_length, description_length_map[-1]) - - content = "| %s %s| %s %s| %s %s|\n" % ( - 'JSON Parameter', - " " * (key_length - len('JSON Parameter') + 1), - 'Default', - " " * (default_length - len('Default') + 1), - 'Description', - " " * (description_length - len('Description') + 1) - ) - content += "| %s | %s | %s |\n" % ( - "-" * (key_length + 1), - "-" * (default_length + 1), - "-" * (description_length + 1) - ) - count = 0 - for key, value in self.params_dict.items(): - content += "| %s %s| %s %s| %s %s|\n" % ( - key, - " " * (key_length - key_length_map[count] + 1), - str(getDefaultColumn(key, value)), - " " * (default_length - default_length_map[count] + 1), - value['descripton'], - " " * (description_length - description_length_map[count] + 1) - ) - count += 1 - return content - - def toJson(self): - """ - @brief convert to json - """ - data = {} - for key, value in self.__dict__.items(): - if key != 'params_dict': - data[key] = value - return data - - def fromJson(self, data): - """ - @brief load form json - """ - for key, value in data.items(): - self.__dict__[key] = value - - def dump(self, filename): - """ - @brief dump to json file - """ - with open(filename, 'w') as f: - json.dump(self.toJson(), f) - - def load(self, filename): - """ - @brief load from json file - """ - with open(filename, 'r') as f: - self.fromJson(json.load(f)) - - def __str__(self): - """ - @brief string - """ - return str(self.toJson()) - - def __repr__(self): - """ - @brief print - """ - return self.__str__() - - def design_name(self): - """ - @brief speculate the design name for dumping out intermediate solutions - """ - design_name = os.path.basename(self.aux_input).replace(".aux", "").replace(".AUX", "") - return design_name - - def solution_file_suffix(self): - """ - @brief speculate placement solution file suffix - """ - return "pl" - -#Rac: Create child class for FPGA -class ParamsFPGA(Params): - """ - initialization - """ - def __init__(self): - #Same as Params to read in all the specified variables - filename = os.path.join(os.path.dirname(__file__), 'paramsFPGA.json') - self.__dict__ = {} - params_dict = {} - with open(filename, "r") as f: - params_dict = json.load(f, object_pairs_hook=OrderedDict) - for key, value in params_dict.items(): - if 'default' in value: - self.__dict__[key] = value['default'] - else: - self.__dict__[key] = None diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py deleted file mode 100644 index a10bf33b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py +++ /dev/null @@ -1,1156 +0,0 @@ -## -# @file PlaceDB.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Oct 2020 -# @brief FPGA placement database -# - -import sys -import os -import re -import math -import time -import numpy as np -import logging -import Params -import dreamplacefpga -import dreamplacefpga.ops.place_io.place_io as place_io -import pdb -from enum import IntEnum - -datatypes = { - 'float32' : np.float32, - 'float64' : np.float64 - } - -class PlaceDBFPGA (object): - """ - initialization - To avoid the usage of list, flatten everything. - """ - def __init__(self): - self.rawdb = None # raw placement database, a C++ object - self.num_physical_nodes = 0 # number of real nodes, including movable nodes, terminals, and terminal_NIs - self.node_names = [] # name of instances - self.node_name2id_map = {} # map instance name to instance id - self.node_types = [] # instance types - self.node_x = [] # site location - self.node_y = [] # site location - self.node_z = [] # site specific location - self.ctrlSets = [] #Used for Flops - self.flat_ctrlSets = [] #Used for Flops - self.flop2ctrlSetId_map = [] #Used for Flop to ctrlset Id map - self.node_size_x = []# 1D array, cell width - self.node_size_y = []# 1D array, cell height - self.resource_size_x = None# 1D array, resource type-based cell width - self.resource_size_y = None# 1D array, resource type-based cell height - #Legalization - self.spiral_accessor = [] - - self.pin_names = [] # pin names - self.pin_types = [] # pin types - self.pin_offset_x = []# 1D array, pin offset x to its node - self.pin_offset_y = []# 1D array, pin offset y to its node - self.lg_pin_offset_x = []# 1D array, pin offset x to its node - self.lg_pin_offset_y = []# 1D array, pin offset y to its node - self.pin2nodeType_map = [] # 1D array, pin to node type map - self.node2pin_map = [] # nested array of array to record pins in each instance - self.flat_node2pin_map = [] #Flattened array of node2pin_map - self.flat_node2pin_start_map = [] #Contains start index for flat_node2pin_map - self.pin2node_map = [] # map pin to node - - self.net_names = [] # net names - self.net2pin_map = [] # nested array of array to record pins in each net - self.flat_net2pin_map = [] # flattend version of net2pin_map - self.flat_net2pin_start_map = [] # starting point for flat_net2pin_map - self.pin2net_map = None # map pin to net - - self.num_bins_x = None# number of bins in horizontal direction - self.num_bins_y = None# number of bins in vertical direction - self.bin_size_x = None# bin width, currently 1 site - self.bin_size_y = None# bin height, currently 1 site - - self.num_sites_x = None # number of sites in horizontal direction - self.num_sites_y = None # number of sites in vertical direction - self.site_type_map = None # site type of each site - self.lg_siteXYs = None # site type of each site - self.dspSiteXYs = [] #Sites for DSP instances - self.ramSite0XYs = [] #Sites for RAM instances - self.ramSite1XYs = [] #Sites for RAM instances - - self.xWirelenWt = None #X-directed wirelength weight - self.yWirelenWt = None #Y-directed wirelength weight - self.baseWirelenGammaBinRatio = None # The base wirelenGamma is * average bin size - self.instDemStddevTrunc = None # We truncate Gaussian distribution outside the instDemStddevTrunc * instDemStddev - # Resource Area Parameters - self.gpInstStddev = None - self.gpInstStddevTrunc = None - self.instDemStddevX = None - self.instDemStddevY = None - # Routability and pin density optimization parameters - self.unitHoriRouteCap = 0 - self.unitVertRouteCap = 0 - self.unitPinCap = 0 - - #Area type parameters - self.filler_size_x = [] #Filler size X for each resourceType - self.filler_size_y = [] #Filler size Y for each resourceType - self.targetOverflow = [] #Target overflow - self.overflowInstDensityStretchRatio = [] #OVFL density stretch ratio - - self.rawdb = None # raw placement database, a C++ object - - self.num_movable_nodes = 0# number of movable nodes - self.num_terminals = 0# number of IOs, essentially fixed instances - self.num_ccNodes= 0# number of carry chains - self.net_weights = None # weights for each net - - self.xl = None - self.yl = None - self.xh = None - self.yh = None - - self.num_movable_pins = None - - self.total_movable_node_area = None # total movable cell area - self.total_fixed_node_area = None # total fixed cell area - self.total_space_area = None # total placeable space area excluding fixed cells - - # enable filler cells - # the Idea from e-place and RePlace - self.total_filler_node_area = None - self.num_filler_nodes = 0 - - self.routing_grid_xl = None - self.routing_grid_yl = None - self.routing_grid_xh = None - self.routing_grid_yh = None - self.num_routing_grids_x = None - self.num_routing_grids_y = None - self.num_routing_layers = None - self.unit_horizontal_capacity = None # per unit distance, projected to one layer - self.unit_vertical_capacity = None # per unit distance, projected to one layer - self.unit_horizontal_capacities = None # per unit distance, layer by layer - self.unit_vertical_capacities = None # per unit distance, layer by layer - self.initial_horizontal_demand_map = None # routing demand map from fixed cells, indexed by (grid x, grid y), projected to one layer - self.initial_vertical_demand_map = None # routing demand map from fixed cells, indexed by (grid x, grid y), projected to one layer - self.dtype = None - #Use Fence region structure for different resource type placement - self.regions = 0 #FF, LUT, DSP, RAM & IO - self.flat_region_boxes = []# flat version of regionsLimits - self.flat_region_boxes_start = []# start indices of regionsLimits, length of num regions + 1 - self.node2fence_region_map = []# map cell to a region, maximum integer if no fence region - self.node_count = [] #Count of nodes based on resource type - #Introduce masks - self.flop_mask = None - self.lut_mask = None - self.lut_type = None - self.cluster_lut_type = None - self.ram0_mask = None - self.ram1_mask = None - self.dsp_mask = None - - self.fixed_rsrcIds = [] - self.slice_rsrcIds = [] - self.slice_compIds = [] - self.dsp_ram_rsrcIds = [] - self.dsp_ram_compIds= [] - - """ - @return number of nodes - """ - @property - def num_nodes_nofiller(self): - return self.num_physical_nodes - """ - @return number of nodes - """ - @property - def num_nodes(self): - return self.num_physical_nodes + self.num_filler_nodes - """ - @return number of nets - """ - @property - def num_nets(self): - return len(self.net2pin_map) - """ - @return number of pins - """ - @property - def num_pins(self): - return len(self.pin2node_map) - - @property - def width(self): - """ - @return width of layout - """ - return int(self.xh-self.xl) - - @property - def height(self): - """ - @return height of layout - """ - return int(self.yh-self.yl) - - @property - def area(self): - """ - @return area of layout - """ - return self.width*self.height - - @property - def routing_grid_size_x(self): - return (self.routing_grid_xh - self.routing_grid_xl) / self.num_routing_grids_x - - @property - def routing_grid_size_y(self): - return (self.routing_grid_yh - self.routing_grid_yl) / self.num_routing_grids_y - - def num_bins(self, l, h, bin_size): - """ - @brief compute number of bins - @param l lower bound - @param h upper bound - @param bin_size bin size - @return number of bins - """ - return int(np.ceil((h-l)/bin_size)) - - """ - read all files including .inst, .pin, .net, .routingUtil files - """ - def read(self, params): - self.dtype = datatypes[params.dtype] - - self.rawdb = place_io.PlaceIOFunction.read(params) - - self.initialize_from_rawdb(params) - - self.lut_mask = self.node2fence_region_map == self.rLUTIdx - self.flop_mask = self.node2fence_region_map == self.rFFIdx - self.lut_flop_mask = self.lut_mask | self.flop_mask - - if self.num_ccNodes > 0: - self.org_lut_mask = self.org_node2fence_region_map == self.rLUTIdx - self.org_flop_mask = self.org_node2fence_region_map == self.rFFIdx - self.org_lut_flop_mask = self.org_lut_mask | self.org_flop_mask - - if self.rLUTIdx != -1: - self.slice_rsrcIds.append(self.rLUTIdx) - if self.rFFIdx != -1: - self.slice_rsrcIds.append(self.rFFIdx) - - self.dsp_ram_mask = self.node2fence_region_map == -1 - if self.rDSPIdx != -1: - self.dsp_mask = self.node2fence_region_map == self.rDSPIdx - self.dsp_ram_mask |= self.dsp_mask - self.dsp_ram_rsrcIds.append(self.rDSPIdx) - if self.rBRAMIdx != -1: - self.ram0_mask = self.node2fence_region_map == self.rBRAMIdx - self.dsp_ram_mask |= self.ram0_mask - self.dsp_ram_rsrcIds.append(self.rBRAMIdx) - if self.rM9KIdx != -1: - self.ram0_mask = self.node2fence_region_map == self.rM9KIdx - self.dsp_ram_mask |= self.ram0_mask - self.dsp_ram_rsrcIds.append(self.rM9KIdx) - if self.rM144KIdx != -1: - self.ram1_mask = self.node2fence_region_map == self.rM144KIdx - self.dsp_ram_mask |= self.ram1_mask - self.dsp_ram_rsrcIds.append(self.rM144KIdx) - self.dsp_ram_rsrcIds = np.array(self.dsp_ram_rsrcIds, dtype=np.int32) - - self.io_mask = self.node2fence_region_map == -1 - if self.rIOIdx!= -1: - self.fixed_rsrcIds.append(self.rIOIdx) - self.io_mask |= self.node2fence_region_map == self.rIOIdx - if self.rPLLIdx != -1: - self.fixed_rsrcIds.append(self.rPLLIdx) - self.io_mask |= self.node2fence_region_map == self.rPLLIdx - self.fixed_rsrcIds = np.array(self.fixed_rsrcIds, dtype=np.int32) - - def initialize_from_rawdb(self, params): - """ - @brief initialize data members from raw database - @param params parameters - """ - pydb = place_io.PlaceIOFunction.pydb(self.rawdb) - - self.num_terminals = pydb.num_terminals - self.num_movable_nodes = pydb.num_movable_nodes - self.num_physical_nodes = pydb.num_physical_nodes - self.node_count = np.array(pydb.node_count, dtype=np.int32) - - #Do not use numpy array for names as it could result in - # large memory usage for large designs with long names - self.node_names = pydb.node_names - self.node_name2id_map = pydb.node_name2id_map - self.node_size_x = np.array(pydb.node_size_x, dtype=self.dtype) - self.node_size_y = np.array(pydb.node_size_y, dtype=self.dtype) - self.node_types = np.array(pydb.node_types, dtype=np.str_) - self.node2fence_region_map = np.array(pydb.node2fence_region_map, dtype=np.int32) - self.node_x = np.array(pydb.node_x, dtype=self.dtype) - self.node_y = np.array(pydb.node_y, dtype=self.dtype) - self.node_z = np.array(pydb.node_z, dtype=np.int32) - - self.node2pin_map = pydb.node2pin_map - self.flat_node2pin_map = np.array(pydb.flat_node2pin_map, dtype=np.int32) - self.flat_node2pin_start_map = np.array(pydb.flat_node2pin_start_map, dtype=np.int32) - self.node2pincount_map = np.array(pydb.node2pincount_map, dtype=np.int32) - self.net2pincount_map = np.array(pydb.net2pincount_map, dtype=np.int32) - self.node2outpinIdx_map = np.array(pydb.node2outpinIdx_map, dtype=np.int32) - self.flop_indices = np.array(pydb.flop_indices) - self.lut_type = np.array(pydb.lut_type).astype(np.int32) - #Use for clustering aware instance area update. LUT0 is ignored and other types have N-1 for type LUTN - self.cluster_lut_type = np.array(pydb.cluster_lut_type).astype(np.int32) - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - self.is_mlab_node = self.lut_type > 9 - self.num_mlab_nodes = self.is_mlab_node.sum() - - self.pin_offset_x = np.array(pydb.pin_offset_x, dtype=self.dtype) - self.pin_offset_y = np.array(pydb.pin_offset_y, dtype=self.dtype) - self.pin2nodeType_map = np.array(pydb.pin2nodeType_map, dtype=np.int32) - - self.pin_names = pydb.pin_names - self.pin_types = np.array(pydb.pin_types, dtype=np.str_) - self.pin_typeIds = np.array(pydb.pin_typeIds, dtype=np.int32) - self.pin2node_map = np.array(pydb.pin2node_map, dtype=np.int32) - self.pin2net_map = np.array(pydb.pin2net_map, dtype=np.int32) - self.spiral_accessor = np.array(pydb.spiral_accessor, dtype=np.int32) - self.spiral_maxVal = pydb.spiral_maxVal - - self.net_names = pydb.net_names - self.net2pin_map = pydb.net2pin_map - - self.flat_net2pin_map = np.array(pydb.flat_net2pin_map, dtype=np.int32) - self.flat_net2pin_start_map = np.array(pydb.flat_net2pin_start_map, dtype=np.int32) - self.net_name2id_map = pydb.net_name2id_map - self.net_weights = np.array(np.ones(len(self.net_names)), dtype=self.dtype) - - self.num_sites_x = pydb.num_sites_x - self.num_sites_y = pydb.num_sites_y - self.siteTypes = np.array(pydb.siteTypes, dtype=np.str_) - self.siteWidths = np.array(pydb.siteWidths, dtype=self.dtype) - self.siteHeights = np.array(pydb.siteHeights, dtype=self.dtype) - self.rsrcTypes = np.array(pydb.rsrcTypes, dtype=np.str_) - self.rsrcInstWidths = np.array(pydb.rsrcInstWidths, dtype=self.dtype) - self.rsrcInstHeights = np.array(pydb.rsrcInstHeights, dtype=self.dtype) - #TODO: Cell dimensions read in for FF/LUT are square values - self.rsrcInstWidths[self.rsrcInstWidths < 1.0] = np.sqrt(self.rsrcInstWidths[self.rsrcInstWidths < 1.0]) - self.rsrcInstHeights[self.rsrcInstHeights < 1.0] = np.sqrt(self.rsrcInstHeights[self.rsrcInstHeights < 1.0]) - self.siteResources = pydb.siteResources - self.rsrcInsts = pydb.rsrcInsts - self.rsrcInstTypes = np.array(pydb.rsrcInstTypes, dtype=np.str_) - self.rsrc2siteMap = pydb.rsrc2siteMap - self.inst2rsrcMap = pydb.inst2rsrcMap - self.siteRsrc2CountMap = pydb.siteRsrc2CountMap - self.siteType2indexMap = pydb.siteType2indexMap - self.rsrcType2indexMap = pydb.rsrcType2indexMap - self.rsrcInstType2indexMap = pydb.rsrcInstType2indexMap - self.sliceElements = pydb.sliceElements - self.lut_maxShared = pydb.lut_maxShared - self.lutTypeInSliceUnit = pydb.lut_type_in_sliceUnit - self.lutFracturesMap = pydb.lutFracturesMap - self.sliceFF_ctrl_mode = pydb.sliceFF_ctrl_mode - self.sliceFFCtrls = pydb.sliceFFCtrls - self.sliceUnitFFCtrls = pydb.sliceUnitFFCtrls - self.siteOutCoordinates = np.array(pydb.siteOutCoordinates, dtype=np.str_) - self.siteOutValues = np.array(pydb.siteOutValues, dtype=np.int32) - self.site_type_map = np.array(pydb.site_type_map, dtype=np.int32) - self.lg_siteXYs = np.array(pydb.lg_siteXYs, dtype=self.dtype) - - #Compute Indices for rsrcTypes - self.rFFIdx = -1 - self.rLUTIdx = -1 - self.rMlabIdx = -1 - self.rADDIdx = -1 - self.rIOIdx = -1 - self.rPLLIdx = -1 - self.rBRAMIdx = -1 - self.rM9KIdx = -1 - self.rM144KIdx = -1 - self.rDSPIdx = -1 - self.rEMPTYIdx = -1 - - if 'FF' in self.rsrcType2indexMap: - self.rFFIdx = self.rsrcType2indexMap['FF'] - elif 'dffeas' in self.rsrcType2indexMap: - self.rFFIdx = self.rsrcType2indexMap['dffeas'] - if 'LUT' in self.rsrcType2indexMap: - self.rLUTIdx = self.rsrcType2indexMap['LUT'] - elif 'lcell_comb' in self.rsrcType2indexMap: - self.rLUTIdx = self.rsrcType2indexMap['lcell_comb'] - elif 'stratixiv_lcell_comb' in self.rsrcType2indexMap: - self.rLUTIdx = self.rsrcType2indexMap['stratixiv_lcell_comb'] - - fVal = [value for key, value in self.rsrcType2indexMap.items() if 'mlab' in key.lower()] - if len(fVal) > 0: - self.rMlabIdx = fVal[0] - - if 'ADD' in self.rsrcType2indexMap: - self.rADDIdx = self.rsrcType2indexMap['ADD'] - if 'IO' in self.rsrcType2indexMap: - self.rIOIdx = self.rsrcType2indexMap['IO'] - elif 'io' in self.rsrcType2indexMap: - self.rIOIdx = self.rsrcType2indexMap['io'] - if 'PLL' in self.rsrcType2indexMap: - self.rPLLIdx= self.rsrcType2indexMap['PLL'] - if 'DSP' in self.rsrcType2indexMap: - self.rDSPIdx = self.rsrcType2indexMap['DSP'] - elif 'DSP48E2' in self.rsrcType2indexMap: - self.rDSPIdx = self.rsrcType2indexMap['DSP48E2'] - if 'BRAM' in self.rsrcType2indexMap: - self.rBRAMIdx = self.rsrcType2indexMap['BRAM'] - elif 'RAMB36E2' in self.rsrcType2indexMap: - self.rBRAMIdx = self.rsrcType2indexMap['RAMB36E2'] - if 'M9K' in self.rsrcType2indexMap: - self.rM9KIdx = self.rsrcType2indexMap['M9K'] - if 'M144K' in self.rsrcType2indexMap: - self.rM144KIdx = self.rsrcType2indexMap['M144K'] - if 'EMPTY' in self.rsrcType2indexMap: - self.rEMPTYIdx = self.rsrcType2indexMap['EMPTY'] - elif 'empty' in self.rsrcType2indexMap: - self.rEMPTYIdx = self.rsrcType2indexMap['empty'] - - self.lg_pin_offset_x = self.pin_offset_x.copy() - self.lg_pin_offset_y = self.pin_offset_y.copy() - #Initialize pin offsets for LUT/FF/IO to 0.0 during legalization - dsp_ram_pin_mask = (self.pin2nodeType_map == self.rDSPIdx) | (self.pin2nodeType_map == self.rBRAMIdx) | (self.pin2nodeType_map == self.rM9KIdx) | (self.pin2nodeType_map == self.rM144KIdx) - self.lg_pin_offset_x[~dsp_ram_pin_mask] = 0.0 - self.lg_pin_offset_y[~dsp_ram_pin_mask] = 0.0 - - #Indices for siteTypes - self.sSLICEIdx = pydb.sliceIdx - self.sDSPIdx = pydb.dspIdx - self.sBRAMIdx = pydb.bramIdx - self.sM9KIdx = pydb.m9kIdx - self.sM144KIdx = pydb.m144kIdx - self.sIOIdx = pydb.ioIdx - self.sPLLIdx = pydb.pllIdx - - self.sliceSiteXYs = np.array(pydb.sliceSiteXYs, dtype=self.dtype) - self.slice_x_min = np.min(self.sliceSiteXYs[:,0]) - self.slice_y_min = np.min(self.sliceSiteXYs[:,1]) - self.slice_x_max = np.max(self.sliceSiteXYs[:,0]) - self.slice_y_max = np.max(self.sliceSiteXYs[:,1]) - - self.dspSiteXYs = np.array(pydb.dspSiteXYs, dtype=self.dtype) - if self.sBRAMIdx != -1 or self.sM9KIdx != -1: - self.ramSite0XYs = np.array(pydb.ramSite0XYs, dtype=self.dtype) - if self.sM144KIdx != -1: - self.ramSite1XYs = np.array(pydb.ramSite1XYs, dtype=self.dtype) - - self.lutName = [key for key, val in self.rsrcType2indexMap.items() if val==self.rLUTIdx][0] - self.slice_lut_capacity=self.siteRsrc2CountMap[self.lutName] - self.SLICE_CAPACITY = self.slice_lut_capacity - self.HALF_SLICE_CAPACITY = self.SLICE_CAPACITY//2 - - self.regions = self.rsrcTypes.shape[0] - self.flat_region_boxes = np.array(pydb.flat_region_boxes, dtype=self.dtype) - self.flat_region_boxes_start = np.array(pydb.flat_region_boxes_start, dtype=np.int32) - self.ctrlSets = np.array(pydb.ctrlSets, dtype=np.int32) - self.flat_ctrlSets = self.ctrlSets.flatten() - self.flop2ctrlSetId_map = np.zeros(self.num_physical_nodes, dtype=np.int32) - self.flop2ctrlSetId_map[self.node2fence_region_map == self.rFFIdx] = np.arange(self.node_count[self.rFFIdx]) - #For 'SHARED' flop ctrls - self.extended_ctrlSets = np.array(pydb.extended_ctrlSets, dtype=np.int32) - self.ext_ctrlSet_start_map = np.array(pydb.ext_ctrlSet_start_map, dtype=np.int32) - - ##Carry chains - self.num_ccNodes = pydb.num_ccNodes - self.num_carry_chains = 0 - #self.flat_cc2node_map = np.array(pydb.flat_cc2node_map, dtype=np.int32) - #self.flat_cc2node_start_map = np.array(pydb.flat_cc2node_start_map, dtype=np.int32) - - if self.num_ccNodes > 0: - self.node2ccId_map = np.array(pydb.node2ccId_map, dtype=np.int32) - self.cc2nodeId_map = np.array(pydb.cc2nodeId_map, dtype=np.int32) - self.cc_element_count= np.array(pydb.cc_element_count, dtype=np.int32) - self.is_cc_node = np.array(pydb.is_cc_node, dtype=np.int32) - self.cc_site_height = np.ceil(self.node_size_y[self.is_cc_node == 1]).astype(np.int32) - - self.org_num_movable_nodes = pydb.org_num_movable_nodes - self.org_num_physical_nodes = pydb.org_num_movable_nodes + pydb.num_terminals - self.org_node_name2id_map = pydb.org_node_name2id_map - self.org_node_names = pydb.org_node_names - self.org_node_types = np.array(pydb.org_node_types, dtype=np.str_) - self.org_node_size_x = np.array(pydb.org_node_size_x, dtype=self.dtype) - self.org_node_size_y = np.array(pydb.org_node_size_y, dtype=self.dtype) - self.org_node_x = np.array(pydb.org_node_x, dtype=self.dtype) - self.org_node_y = np.array(pydb.org_node_y, dtype=self.dtype) - self.org_node_z = np.array(pydb.org_node_z, dtype=np.int32) - self.org_node2fence_region_map = np.array(pydb.org_node2fence_region_map, dtype=np.int32) - self.org_node_count = np.array(pydb.org_node_count, dtype=np.int32) - self.org_is_cc_node = np.array(pydb.org_is_cc_node, dtype=np.int32) - self.org_flop_indices = np.array(pydb.org_flop_indices) - self.org_lut_type = np.array(pydb.org_lut_type).astype(np.int32) - self.org_node2ccId_map = np.array(pydb.org_node2ccId_map, dtype=np.int32) - self.org_pin_offset_x = np.array(pydb.org_pin_offset_x, dtype=self.dtype) - self.org_pin_offset_y = np.array(pydb.org_pin_offset_y, dtype=self.dtype) - self.org_pin2nodeType_map = np.array(pydb.org_pin2nodeType_map, dtype=np.int32) - self.org_node2pincount_map = np.array(pydb.org_node2pincount_map, dtype=np.int32) - self.org_pin2node_map = np.array(pydb.org_pin2node_map, dtype=np.int32) - self.org_node2outpinIdx_map = np.array(pydb.org_node2outpinIdx_map, dtype=np.int32) - self.org_flat_node2pin_map = np.array(pydb.org_flat_node2pin_map, dtype=np.int32) - self.org_flat_node2pin_start_map = np.array(pydb.org_flat_node2pin_start_map, dtype=np.int32) - self.org_flat_cc2node_map = np.array(pydb.org_flat_cc2node_map, dtype=np.int32) - self.org_flat_cc2node_start_map = np.array(pydb.org_flat_cc2node_start_map, dtype=np.int32) - self.org_node2ccId_map = np.array(pydb.org_node2ccId_map, dtype=np.int32) - self.new2org_node_map = np.array(pydb.new2org_node_map, dtype=np.int32) - ## FF Ctrl - self.org_ctrlSets = np.array(pydb.org_ctrlSets, dtype=np.int32) - self.flat_org_ctrlSets = self.org_ctrlSets.flatten() - self.org_flop2ctrlSetId_map = np.zeros(self.org_num_physical_nodes, dtype=np.int32) - self.org_flop2ctrlSetId_map[self.org_node2fence_region_map == self.rFFIdx] = np.arange(self.org_node_count[self.rFFIdx]) - #For 'SHARED' flop ctrls - self.org_extended_ctrlSets = np.array(pydb.org_extended_ctrlSets, dtype=np.int32) - self.org_ext_ctrlSet_start_map = np.array(pydb.org_ext_ctrlSet_start_map, dtype=np.int32) - self.org_is_mlab_node = self.org_lut_type > 9 - - self.org_lg_pin_offset_x = self.org_pin_offset_x.copy() - self.org_lg_pin_offset_y = self.org_pin_offset_y.copy() - #Initialize pin offsets for LUT/FF/IO to 0.0 during legalization - dsp_ram_pin_mask = (self.org_pin2nodeType_map == self.rDSPIdx) | (self.org_pin2nodeType_map == self.rBRAMIdx) | (self.org_pin2nodeType_map == self.rM9KIdx) | (self.org_pin2nodeType_map == self.rM144KIdx) - self.org_lg_pin_offset_x[~dsp_ram_pin_mask] = 0.0 - self.org_lg_pin_offset_y[~dsp_ram_pin_mask] = 0.0 - - - else: - self.is_cc_node = np.zeros(self.num_nodes, dtype=np.int32) - self.org_is_cc_node = np.zeros_like(self.is_cc_node) - - self.num_routing_grids_x = pydb.xh - self.num_routing_grids_y = pydb.yh - self.routing_grid_xl = self.dtype(pydb.routing_grid_xl) - self.routing_grid_yl = self.dtype(pydb.routing_grid_yl) - self.routing_grid_xh = self.dtype(pydb.routing_grid_xh) - self.routing_grid_yh = self.dtype(pydb.routing_grid_yh) - - self.xl = self.dtype(pydb.xl) - self.yl = self.dtype(pydb.yl) - self.xh = self.dtype(pydb.xh) - self.yh = self.dtype(pydb.yh) - - self.ff_ctrl_type = pydb.ff_ctrl_type - self.num_routing_layers = 1 - self.xWirelenWt = pydb.wl_weightX - self.yWirelenWt = pydb.wl_weightY - self.unitPinCap = pydb.pinRouteCap - self.unit_horizontal_capacity = 0.95 * pydb.routeCapH - self.unit_vertical_capacity = 0.95 * pydb.routeCapV - - #Use for debug when node and net names are long - if params.name_map_file_dump == 1: - tt = time.time() - #Dump out design.nodes - content="" - - for nodeId in range(len(self.node_names)): - upd_node_name = "inst_"+str(nodeId) - content += "%s %s\n" % (upd_node_name, self.node_types[nodeId]) - - mNames_file = "mapped_design.nodes" - with open(mNames_file, "w") as f: - f.write(content) - logging.info("write out node name mapping to %s took %.3f seconds" % (mNames_file, time.time()-tt)) - - #Dump out design.nets - content="" - - for netId in range(len(self.net_names)): - upd_net_name = "net_"+str(netId) - content += "net %s %d\n" % (upd_net_name, self.net2pincount_map[netId]) - - n2pStart = self.flat_net2pin_start_map[netId] - n2pEnd = self.flat_net2pin_start_map[netId+1] - - for pId in range(n2pStart, n2pEnd): - pinId = self.flat_net2pin_map[pId] - nodeId = self.pin2node_map[pinId] - upd_node_name = "inst_"+str(nodeId) - content += "\t%s %s\n" % (upd_node_name, self.pin_names[pinId]) - - content += "endnet\n" - - mNets_file = "mapped_design.nets" - with open(mNets_file, "w") as f: - f.write(content) - logging.info("write out node net mapping to %s took %.3f seconds" % (mNets_file, time.time()-tt)) - - - def print_node(self, node_id): - """ - @brief print node information - @param node_id cell index - """ - logging.debug("node %s(%d), size (%g, %g), pos (%g, %g)" % (self.node_names[node_id], node_id, self.node_size_x[node_id], self.node_size_y[node_id], self.node_x[node_id], self.node_y[node_id])) - pins = "pins " - for pin_id in self.node2pin_map[node_id]: - pins += "%s(%s, %d) " % (self.node_names[self.pin2node_map[pin_id]], self.net_names[self.pin2net_map[pin_id]], pin_id) - logging.debug(pins) - - def print_net(self, net_id): - """ - @brief print net information - @param net_id net index - """ - logging.debug("net %s(%d)" % (self.net_names[net_id], net_id)) - pins = "pins " - for pin_id in self.net2pin_map[net_id]: - pins += "%s(%s, %d) " % (self.node_names[self.pin2node_map[pin_id]], self.net_names[self.pin2net_map[pin_id]], pin_id) - logging.debug(pins) - - def flatten_nested_map(self, net2pin_map): - """ - @brief flatten an array of array to two arrays like CSV format - @param net2pin_map array of array - @return a pair of (elements, cumulative column indices of the beginning element of each row) - """ - # flat netpin map, length of #pins - flat_net2pin_map = np.zeros(len(pin2net_map), dtype=np.int32) - # starting index in netpin map for each net, length of #nets+1, the last entry is #pins - flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(net2pin_map)): - flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] - flat_net2pin_start_map[i] = count - count += len(net2pin_map[i]) - assert flat_net2pin_map[-1] != 0 - flat_net2pin_start_map[len(net2pin_map)] = len(pin2net_map) - - return flat_net2pin_map, flat_net2pin_start_map - - def __call__(self, params): - """ - @brief top API to read placement files - @param params parameters - """ - tt = time.time() - - self.read(params) - self.initialize(params) - - logging.info("reading benchmark takes %g seconds" % (time.time()-tt)) - - def calc_num_filler_for_fence_region(self, region_id, node2fence_region_map, filler_size_x, filler_size_y): - ''' - @description: calculate number of fillers for each fence region - @param fence_regions{type} - @return: - ''' - fence_region_mask = (node2fence_region_map == region_id) - - if region_id in self.fixed_rsrcIds: - return 0, 0, self.num_terminals - - #If no cells of particular resourceType - if np.sum(fence_region_mask) == 0: - return 0, 0, 0.0 - - movable_node_size_x = self.node_size_x[fence_region_mask] - movable_node_size_y = self.node_size_y[fence_region_mask] - - #Calcuation based on region size - region = self.flat_region_boxes[self.flat_region_boxes_start[region_id]:self.flat_region_boxes_start[region_id+1]] - placeable_area = np.sum((region[:, 2]-region[:, 0])*(region[:, 3]-region[:, 1])) - - total_movable_node_area = np.sum(movable_node_size_x*movable_node_size_y) - - total_filler_node_area = max(placeable_area-total_movable_node_area, 0.0) - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - #Add pseudo filler nodes for FFs - if region_id == self.rFFIdx and self.num_mlab_nodes > 0: - total_filler_node_area -= self.num_mlab_nodes - num_filler = int(math.floor(total_filler_node_area/(filler_size_x*filler_size_y))) + self.num_mlab_nodes - else: - num_filler = int(math.floor(total_filler_node_area/(filler_size_x*filler_size_y))) - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - if region_id == self.rLUTIdx and self.num_mlab_nodes > 0: - logging.info("Region: %d [%s] #movable_nodes = %d (%s) + %d (mlab) = %d, movable_node_area = %.1f, placeable_area = %.1f, filler_node_area = %.1f, #fillers = %d, filler size = %.4g x %g\n" - % (region_id, self.rsrcTypes[region_id], fence_region_mask.sum()-self.num_mlab_nodes, self.rsrcTypes[region_id], self.num_mlab_nodes, fence_region_mask.sum(), total_movable_node_area, placeable_area, total_filler_node_area, num_filler, filler_size_x, filler_size_y)) - else: - logging.info("Region: %d [%s] #movable_nodes = %d movable_node_area = %.1f, placeable_area = %.1f, filler_node_area = %.1f, #fillers = %d, filler size = %.4g x %g\n" - % (region_id, self.rsrcTypes[region_id], fence_region_mask.sum(), total_movable_node_area, placeable_area, total_filler_node_area, num_filler, filler_size_x, filler_size_y)) - - #Ensure there is sufficient space available for placement - if total_movable_node_area > placeable_area: - logging.error("Provided %d x %d site_map is not large enough to accomodate all %s instances. Use a larger site_map.\n" % (self.num_sites_x, self.num_sites_y, self.rsrcTypes[region_id])) - sys.exit(0) - - return num_filler, total_movable_node_area, np.sum(fence_region_mask) - - - def initialize(self, params): - """ - @brief initialize data members after reading - @param params parameters - """ - self.resource_size_x = self.siteWidths - self.resource_size_y = self.siteHeights - - #Parameter initialization - Can be changed later through params - if self.xWirelenWt == None or self.xWirelenWt == 0: - self.xWirelenWt = 1.0 - if self.yWirelenWt == None or self.yWirelenWt == 0: - self.yWirelenWt = 1.0 - self.instDemStddevTrunc = 2.5 - - #Resource area parameter - self.gpInstStddev = math.sqrt(2.5e-4 * self.num_nodes) / (2.0 * self.instDemStddevTrunc) - self.gpInstStddevTrunc = self.instDemStddevTrunc - - self.instDemStddevX = self.gpInstStddev - self.instDemStddevY = self.gpInstStddev - - #Parameter for Direct Legalization - self.nbrDistEnd = 1.2 * self.gpInstStddev * self.gpInstStddevTrunc - - # Routability and pin density optimization parameters - self.unitPinCap = 0 - - #Area type parameters - Consider default fillerstrategy of FIXED_SHAPE - self.filler_size_x = np.zeros(self.regions - self.fixed_rsrcIds.size) - self.filler_size_y = np.zeros(self.regions - self.fixed_rsrcIds.size) - self.targetOverflow = np.zeros(self.regions - self.fixed_rsrcIds.size) - self.overflowInstDensityStretchRatio = np.zeros(self.regions - self.fixed_rsrcIds.size) - self.node_area_adjust_overflow = np.ones_like(self.targetOverflow) - self.node_area_adjust_overflow *= params.node_area_adjust_overflow - - self.rsrc2compId_map = np.ones(self.regions, dtype=np.int32) - self.rsrc2compId_map *= -1 - self.comp2rsrcId_map = np.ones_like(self.rsrc2compId_map) - self.comp2rsrcId_map *= -1 - - tId = 0 - for rId in range(self.regions): - if rId not in self.fixed_rsrcIds: - if self.node_count[rId] > 0: - #Do not consider large instances such as carry chains while determining filler sizes - largeNodes = self.is_cc_node[self.node2fence_region_map == rId] - if rId == self.rLUTIdx: - largeNodes |= self.is_mlab_node[self.node2fence_region_map == rId] - max_x = np.max(self.node_size_x[self.node2fence_region_map == rId][largeNodes == 0]) - max_y = np.max(self.node_size_y[self.node2fence_region_map == rId][largeNodes == 0]) - self.filler_size_x[tId] = math.sqrt(round(max_x*max_x,4)) - self.filler_size_y[tId] = math.sqrt(round(max_y*max_y,4)) - if max_x < 1.0 and max_y < 1.0: - self.targetOverflow[tId] = 0.1 - self.overflowInstDensityStretchRatio[tId] = math.sqrt(2.0) - else: - self.targetOverflow[tId] = 0.2 - self.node_area_adjust_overflow[tId] = 0.25 - self.rsrc2compId_map[rId] = tId - self.comp2rsrcId_map[tId] = rId - tId = tId + 1 - - ##Set FF filler size to be same as LUT - self.filler_size_x[self.rFFIdx] = self.filler_size_x[self.rLUTIdx] - self.filler_size_y[self.rFFIdx] = self.filler_size_y[self.rLUTIdx] - - #Resize based on available resources in the design - maxVal = self.rsrc2compId_map.max()+1 - self.filler_size_x = self.filler_size_x[:maxVal] - self.filler_size_y = self.filler_size_y[:maxVal] - self.targetOverflow = self.targetOverflow[:maxVal] - self.overflowInstDensityStretchRatio = self.overflowInstDensityStretchRatio[:maxVal] - - if self.rLUTIdx != -1 and self.rsrc2compId_map[self.rLUTIdx] != -1: - self.slice_compIds.append(self.rsrc2compId_map[self.rLUTIdx]) - if self.rFFIdx != -1 and self.rsrc2compId_map[self.rFFIdx] != -1: - self.slice_compIds.append(self.rsrc2compId_map[self.rFFIdx]) - - if self.rDSPIdx != -1 and self.rsrc2compId_map[self.rDSPIdx] != -1: - self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rDSPIdx]) - if self.rBRAMIdx != -1 and self.rsrc2compId_map[self.rBRAMIdx] != -1: - self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rBRAMIdx]) - if self.rM9KIdx != -1 and self.rsrc2compId_map[self.rM9KIdx] != -1: - self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rM9KIdx]) - if self.rM144KIdx != -1 and self.rsrc2compId_map[self.rM144KIdx] != -1: - self.dsp_ram_compIds.append(self.rsrc2compId_map[self.rM144KIdx]) - - #set number of bins - self.num_bins_x = params.num_bins_x - self.num_bins_y = params.num_bins_y - self.bin_size_x = self.width/self.num_bins_x - self.bin_size_y = self.height/self.num_bins_y - - # set total cell area - self.total_movable_node_area = self.dtype(np.sum(self.lut_flop_mask)*self.filler_size_x[self.rLUTIdx]*self.filler_size_y[self.rLUTIdx]) - if self.dsp_ram_mask.sum() > 0: - self.total_movable_node_area += self.dtype(np.sum(self.node_size_x[self.dsp_ram_mask]*self.node_size_y[self.dsp_ram_mask])) - - # total fixed node area should exclude the area outside the layout and the area of terminal_NIs - self.total_fixed_node_area = self.dtype(self.num_terminals) - self.total_space_area = self.width * self.height - - self.region_boxes = [] - - #For FPGA, the regions are fixed for each resourceType - for region_id in range(self.regions): - idx = self.rsrc2compId_map[region_id] - if idx != -1: - region = self.flat_region_boxes[self.flat_region_boxes_start[region_id]:self.flat_region_boxes_start[region_id+1]] - self.region_boxes.append(region) - - # insert filler nodes - ### calculate fillers for different resourceTypes - self.filler_size_x_fence_region = [] - self.filler_size_y_fence_region = [] - self.num_filler_nodes = 0 - self.num_filler_nodes_fence_region = [] - self.num_movable_nodes_fence_region = [] - self.total_movable_node_area_fence_region = [] - self.target_density_fence_region = [] - self.filler_start_map = None - filler_node_size_x_list = [] - filler_node_size_y_list = [] - self.total_filler_node_area = 0 - - for idx in range(self.regions): - i = self.rsrc2compId_map[idx] - if i != -1: - num_filler_i, total_movable_node_area_i, num_movable_nodes_i = self.calc_num_filler_for_fence_region(idx, self.node2fence_region_map, - self.filler_size_x[i], self.filler_size_y[i]) - self.num_movable_nodes_fence_region.append(num_movable_nodes_i) - self.num_filler_nodes_fence_region.append(num_filler_i) - self.total_movable_node_area_fence_region.append(total_movable_node_area_i) - self.target_density_fence_region.append(self.targetOverflow[i]) - self.filler_size_x_fence_region.append(self.filler_size_x[i]) - self.filler_size_y_fence_region.append(self.filler_size_y[i]) - self.num_filler_nodes += num_filler_i - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - #Add pseudo filler nodes for FFs - if i == self.rFFIdx and self.num_mlab_nodes > 0: - filler_count = num_filler_i - self.num_mlab_nodes - tmp_filler_size_x = np.full(self.num_mlab_nodes, fill_value=self.rsrcInstWidths[self.rMlabIdx], dtype=self.node_size_x.dtype) - tmp_filler_size_x = np.concatenate((tmp_filler_size_x, np.full(filler_count, fill_value=self.filler_size_x[i], dtype=self.node_size_x.dtype))) - filler_node_size_x_list.append(tmp_filler_size_x) - tmp_filler_size_y = np.full(self.num_mlab_nodes, fill_value=self.rsrcInstHeights[self.rMlabIdx], dtype=self.node_size_y.dtype) - tmp_filler_size_y = np.concatenate((tmp_filler_size_y, np.full(filler_count, fill_value=self.filler_size_y[i], dtype=self.node_size_y.dtype))) - filler_node_size_y_list.append(tmp_filler_size_y) - filler_node_area_i = filler_count * (self.filler_size_x[i]*self.filler_size_y[i]) + self.num_mlab_nodes - else: - filler_node_size_x_list.append(np.full(num_filler_i, fill_value=self.filler_size_x[i], dtype=self.node_size_x.dtype)) - filler_node_size_y_list.append(np.full(num_filler_i, fill_value=self.filler_size_y[i], dtype=self.node_size_y.dtype)) - filler_node_area_i = num_filler_i * (self.filler_size_x[i]*self.filler_size_y[i]) - self.total_filler_node_area += filler_node_area_i - - for rId in self.fixed_rsrcIds: - if self.node_count[rId] > 0: - logging.info("Region: %d [%s] #fixed_nodes = %d \n" - % (rId, self.rsrcTypes[rId], self.node_count[rId])) - - self.total_movable_node_area_fence_region = np.array(self.total_movable_node_area_fence_region, dtype=self.dtype) - self.num_movable_nodes_fence_region = np.array(self.num_movable_nodes_fence_region, dtype=np.int32) - - if params.enable_fillers: - # the way to compute this is still tricky; we need to consider place_io together on how to - # summarize the area of fixed cells, which may overlap with each other. - self.filler_start_map = np.cumsum([0]+self.num_filler_nodes_fence_region) - self.num_filler_nodes_fence_region = np.array(self.num_filler_nodes_fence_region, dtype=np.int32) - self.node_size_x = np.concatenate([self.node_size_x] + filler_node_size_x_list) - self.node_size_y = np.concatenate([self.node_size_y] + filler_node_size_y_list) - else: - self.total_filler_node_area = 0 - self.num_filler_nodes = 0 - filler_size_x, filler_size_y = 0, 0 - if(len(self.region_boxes) > 0): - self.filler_start_map = np.zeros(len(self.region_boxes)+1, dtype=np.int32) - self.num_filler_nodes_fence_region = np.zeros(len(self.num_filler_nodes_fence_region), dtype=np.int32) - - #TODO - For Stratix-IV: mlab is treated as lut type as sites for MLAB/LAB are not distinguished - if self.num_mlab_nodes > 0: - self.is_mlab_filler_node = np.zeros(self.num_nodes, dtype=np.int32) - self.is_mlab_filler_node[self.filler_start_map[self.rFFIdx]:self.filler_start_map[self.rFFIdx]+self.num_mlab_nodes] = 1 - - def write(self, pl_file): - """ - @brief write placement solution as .pl file - @Use as intermediate - does not contain VPR output format - @param pl_file .pl file - """ - tt = time.time() - #logging.info("writing to %s" % (pl_file)) - - if self.num_ccNodes == 0: - node_x = self.node_x - node_y = self.node_y - node_z = self.node_z - str_node_names = self.node_names - node_area = self.node_size_x*self.node_size_y - else: - node_x = self.org_node_x - node_y = self.org_node_y - node_z = self.org_node_z - str_node_names = self.org_node_names - node_area = self.org_node_size_x*self.org_node_size_y - - #cc_length = self.cc_site_height - #cc_node_x = node_x[self.is_cc_node == 1].astype(np.int32) - #cc_node_y = node_y[self.is_cc_node == 1].astype(np.int32) - - content = "" - - for i in range(self.num_physical_nodes): - #if self.is_cc_node[i] == 1: - # ccId = self.node2ccId_map[i] - # ccXloc = cc_node_x[ccId] - # #solution is the starting Slice - # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 - # ccZloc = 0 - # ccElArea = round(self.node_size_y[i]/self.cc_element_count[ccId], 4) - # rStart = self.org_flat_cc2node_start_map[ccId] - # rEnd = self.org_flat_cc2node_start_map[ccId+1] - # for mId in range(rStart, rEnd): - # orgNodeId = self.org_flat_cc2node_map[mId] - # content += "%s %.6E %.6E %g %.6E" % ( - # self.org_node_names[orgNodeId], - # ccXloc, - # currY, - # ccZloc, - # ccElArea - # ) - # if mId < rEnd-1: - # content += "\n" - # ccZloc = ccZloc+1 - # if ccZloc == self.slice_lut_capacity: - # ccZloc = 0; - # currY = currY-1 - #else: - content += "%s %.6E %.6E %g %.6E" % ( - str_node_names[i], - node_x[i], - node_y[i], - node_z[i], - node_area[i] - ) - if i < self.num_physical_nodes-1: - content += "\n" - - with open(pl_file, "w") as f: - f.write(content) - logging.info("write placement solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) - - def writeFinalSolution(self, pl_file): - """ - @brief write placement solution as .pl file - @param pl_file .pl file - """ - tt = time.time() - #logging.info("writing to %s" % (pl_file)) - - if self.num_ccNodes == 0: - node_x = self.node_x - node_y = self.node_y - node_z = self.node_z - node_types = self.node_types - str_node_names = self.node_names - node2fence_region_map = self.node2fence_region_map - else: - node_x = self.org_node_x - node_y = self.org_node_y - node_z = self.org_node_z - node_types = self.org_node_types - str_node_names = self.org_node_names - node2fence_region_map = self.org_node2fence_region_map - - #cc_length = self.cc_site_height - #cc_node_x = node_x[self.is_cc_node == 1].astype(np.int32) - #cc_node_y = node_y[self.is_cc_node == 1].astype(np.int32) - - content = "" - #node_area = self.node_size_x*self.node_size_y - - if self.siteOutValues.shape[0] == 0: - for i in range(self.num_physical_nodes): - #if self.is_cc_node[i] == 1: - # ccId = self.node2ccId_map[i] - # ccXloc = cc_node_x[ccId] - # #solution is the starting Slice - # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 - # ccZloc = 0 - # rStart = self.org_flat_cc2node_start_map[ccId] - # rEnd = self.org_flat_cc2node_start_map[ccId+1] - # for mId in range(rStart, rEnd): - # orgNodeId = self.org_flat_cc2node_map[mId] - # content += "%s %d %d %g" % ( - # self.org_node_names[orgNodeId], - # ccXloc, - # currY, - # ccZloc - # ) - # if mId < rEnd-1: - # content += "\n" - # ccZloc = ccZloc+1 - # if ccZloc == self.slice_lut_capacity: - # ccZloc = 0; - # currY = currY-1 - #else: - content += "%s %d %d %g" % ( - str_node_names[i], - node_x[i], - node_y[i], - node_z[i] - ) - if i < self.num_physical_nodes-1: - content += "\n" - else: - for i in range(self.num_physical_nodes): - #if self.is_cc_node[i] == 1: - # siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[node2fence_region_map[i]]]] - # zVal = 0 - # sVal = 0 - # ccId = self.node2ccId_map[i] - # ccXloc = cc_node_x[ccId] - # #solution is the starting Slice - # currY = cc_node_y[ccId] + self.cc_site_height[ccId] -1 - # ccZloc = 0 - # ccElArea = round(self.node_size_y[i]/self.cc_element_count[ccId], 4) - # rStart = self.org_flat_cc2node_start_map[ccId] - # rEnd = self.org_flat_cc2node_start_map[ccId+1] - # for mId in range(rStart, rEnd): - # orgNodeId = self.org_flat_cc2node_map[mId] - # if self.siteOutCoordinates[siteId] == 'z': - # sVal = ccZloc - # zVal = self.siteOutValues[siteId] - # elif self.siteOutCoordinates[siteId] == 's': - # zVal = ccZloc - # sVal = self.siteOutValues[siteId] - # content += "%s %d %d %g %g %s" % ( - # self.org_node_names[orgNodeId], - # ccXloc, - # currY, - # zVal, - # sVal, - # self.org_node_types[orgNodeId] - # ) - # if mId < rEnd-1: - # content += "\n" - # ccZloc = ccZloc+1 - # if ccZloc == self.slice_lut_capacity: - # ccZloc = 0; - # currY = currY-1 - #else: - siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[node2fence_region_map[i]]]] - zVal = 0 - sVal = 0 - if self.siteOutCoordinates[siteId] == 'z': - sVal = node_z[i] - zVal = self.siteOutValues[siteId] - elif self.siteOutCoordinates[siteId] == 's': - zVal = node_z[i] - sVal = self.siteOutValues[siteId] - content += "%s %d %d %g %g %s" % ( - str_node_names[i], - node_x[i], - node_y[i], - zVal, - sVal, - node_types[i] - ) - if i < self.num_physical_nodes-1: - content += "\n" - - with open(pl_file, "w") as f: - f.write(content) - logging.info("write placement solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) - - #Use for debug - Does not support macro mode - def writeMapSolution(self): - """ - @brief write mapped placement solution as .pl file - """ - tt = time.time() - #logging.info("writing to %s" % (pl_file)) - - node_x = self.node_x - node_y = self.node_y - node_z = self.node_z - - pl_file = "mapped_design_final.pl" - - content = "" - - if self.siteOutValues.shape[0] == 0: - for i in range(self.num_physical_nodes): - content += "%s %d %d %g" % ( - "inst_"+str(i), - node_x[i], - node_y[i], - node_z[i] - ) - if i < self.num_physical_nodes-1: - content += "\n" - else: - for i in range(self.num_physical_nodes): - siteId = self.siteType2indexMap[self.rsrc2siteMap[self.rsrcTypes[self.node2fence_region_map[i]]]] - zVal = 0 - sVal = 0 - if self.siteOutCoordinates[siteId] == 'z': - sVal = node_z[i] - zVal = self.siteOutValues[siteId] - elif self.siteOutCoordinates[siteId] == 's': - zVal = node_z[i] - sVal = self.siteOutValues[siteId] - content += "%s %d %d %g %g %s" % ( - "inst_"+str(i), - node_x[i], - node_y[i], - zVal, - sVal, - self.node_types[i] - ) - if i < self.num_physical_nodes-1: - content += "\n" - - with open(pl_file, "w") as f: - f.write(content) - logging.info("write out mapped solution to %s took %.3f seconds" % (pl_file, time.time()-tt)) - - def apply(self, node_x, node_y, node_z): - """ - @brief apply placement solution and update database - """ - - if self.num_ccNodes == 0: - # assign solution - self.node_x[:self.num_movable_nodes] = node_x[:self.num_movable_nodes] - self.node_y[:self.num_movable_nodes] = node_y[:self.num_movable_nodes] - self.node_z[:self.num_movable_nodes] = node_z[:self.num_movable_nodes] - node_x = self.node_x - node_y = self.node_y - node_z = self.node_z - else: - # assign solution - self.org_node_x[:self.num_movable_nodes] = node_x[:self.num_movable_nodes] - self.org_node_y[:self.num_movable_nodes] = node_y[:self.num_movable_nodes] - self.org_node_z[:self.num_movable_nodes] = node_z[:self.num_movable_nodes] - node_x = self.org_node_x - node_y = self.org_node_y - node_z = self.org_node_z - - # update raw database - place_io.PlaceIOFunction.apply(self.rawdb, node_x.astype(self.dtype), node_y.astype(self.dtype), node_z.astype(np.int32)) - - -if __name__ == "__main__": - if len(sys.argv) != 2: - logging.error("One input parameters in json format in required") - - params = Params.Params() - params.load(sys.argv[sys.argv[1]]) - logging.info("parameters = %s" % (params)) - - db = PlaceDB() - db(params) - - db.print_node(1) - db.print_net(1) - db.print_row(1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py deleted file mode 100644 index acd13916..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py +++ /dev/null @@ -1,876 +0,0 @@ -## -# @file PlaceObj.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief Placement model class defining the placement objective. -# - -import os -import sys -import time -import numpy as np -import itertools -import logging -import torch -import torch.autograd as autograd -import torch.nn as nn -import torch.nn.functional as F -import pdb -import gzip -import math -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle -import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength as weighted_average_wirelength -#import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength as logsumexp_wirelength -import dreamplacefpga.ops.electric_potential.electric_potential as electric_potential -import dreamplacefpga.ops.rudy.rudy as rudy -import dreamplacefpga.ops.pin_utilization.pin_utilization as pin_utilization -#FPGA clustering compatibility resource area computation -import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility as clustering_compatibility -import dreamplacefpga.ops.adjust_node_area.adjust_node_area as adjust_node_area - -# For FPGA -class PreconditionOpFPGA: - """Preconditioning engine is critical for convergence. - Need to be carefully designed. - """ - def __init__(self, placedb, data_collections): - self.placedb = placedb - self.data_collections = data_collections - self.iteration = 0 - self.filler2fence_region_map = torch.zeros(placedb.num_filler_nodes, device=data_collections.pos[0].device, dtype=torch.long) - self.movablenode2fence_region_map_clamp = data_collections.node2fence_region_map[:placedb.num_movable_nodes].long() - for idx in range(placedb.regions): - i = placedb.rsrc2compId_map[idx] - if i != -1: - filler_beg, filler_end = placedb.filler_start_map[i:i+2] - self.filler2fence_region_map[filler_beg:filler_end] = i - #Adjust movablenode2fence_region_map from rsrcId to compId - if idx != i: - self.movablenode2fence_region_map_clamp[data_collections.node2fence_region_map[:placedb.num_movable_nodes] == idx] = i - - def __call__(self, grad, density_weight, precondWL, update_mask=None): - """Introduce alpha parameter to avoid divergence. - It is tricky for this parameter to increase. - """ - with torch.no_grad(): - #FPGA Preconditioning - node_areas = self.data_collections.node_areas.clone() - - for imk in range(self.placedb.regions): - mk = self.placedb.rsrc2compId_map[imk] - if mk != -1: - mask = self.data_collections.node2fence_region_map[:self.placedb.num_movable_nodes] == imk - node_areas[:self.placedb.num_movable_nodes].masked_scatter_(mask, node_areas[:self.placedb.num_movable_nodes][mask]*density_weight[mk]) - filler_beg, filler_end = self.placedb.filler_start_map[mk:mk+2] - node_areas[self.placedb.num_nodes-self.placedb.num_filler_nodes+filler_beg:self.placedb.num_nodes-self.placedb.num_filler_nodes+filler_end] *= density_weight[mk] - - precond = precondWL + node_areas - #Use alpha to avoid divergence - #precond = precondWL + self.alpha * node_areas - - precond.clamp_(min=1.0) - grad[0:self.placedb.num_nodes].div_(precond) - grad[self.placedb.num_nodes:self.placedb.num_nodes * - 2].div_(precond) - - #print("Overall preconditioned grad norm1: %g" %(grad.norm(p=1))) - ### stop gradients for terminated electric field - if(update_mask is not None): - grad = grad.view(2, -1) - update_mask = ~update_mask - if update_mask.sum() < len(update_mask): - movable_mask = update_mask[self.movablenode2fence_region_map_clamp] - filler_mask = update_mask[self.filler2fence_region_map] - grad[0, :self.placedb.num_movable_nodes].masked_fill_(movable_mask, 0) - grad[1, :self.placedb.num_movable_nodes].masked_fill_(movable_mask, 0) - grad[0, self.placedb.num_nodes-self.placedb.num_filler_nodes:].masked_fill_(filler_mask, 0) - grad[1, self.placedb.num_nodes-self.placedb.num_filler_nodes:].masked_fill_(filler_mask, 0) - grad = grad.view(-1) - self.iteration += 1 - - return grad - -class PlaceObjFPGA(nn.Module): - """ - @brief Define placement objective: - wirelength + density_weight * density penalty - It includes various ops related to global placement as well. - """ - def __init__(self, density_weight, params, placedb, data_collections, op_collections, global_place_params): - """ - @brief initialize ops for placement - @param density_weight density weight in the objective - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - @param op_collections a collection of all ops - @param global_place_params global placement parameters for current global placement stage - """ - super(PlaceObjFPGA, self).__init__() - - ### quadratic penalty - self.density_quad_coeff = 1000 #corresponds to beta/2 in obj function - self.quad_penalty_coeff = None - self.init_density = None - ### increase density penalty if slow convergence - self.density_factor = 1 - - ### fence region will enable quadratic penalty by default - self.quad_penalty = True - - ### fence region - ### update mask controls whether stop gradient/updating, 1 represents allow grad/update - self.update_mask = None - self.lock_mask = None - ### for subregion rough legalization, once stop updating, perform immediate greedy legalization once - ### this is to avoid repeated legalization - ### 1 represents already legal - self.legal_mask = torch.zeros(placedb.regions) - self.legal_mask[placedb.fixed_rsrcIds] = 1 #IOs are fixed - - self.params = params - self.placedb = placedb - self.data_collections = data_collections - self.op_collections = op_collections - self.global_place_params = global_place_params - - self.fixedDemMaps = [] - - self.gpu = params.gpu - self.precondWL = self.op_collections.precondwl_op() - if placedb.num_ccNodes > 0: - self.lg_precondWL = self.op_collections.lg_precondition_op() - self.fixedDemMaps = self.op_collections.demandMap_op() - - ### different fence region needs different density weights in multi-electric field algorithm - self.density_weight = torch.tensor( - [density_weight]*(placedb.targetOverflow.size), - dtype=self.data_collections.dtype, - device=self.data_collections.device) - ### Note: even for multi-electric fields, they use the same gamma - self.gamma = torch.tensor(self.base_gamma(params, placedb)[0], - dtype=self.data_collections.dtype, - device=self.data_collections.device) - initOverflow = torch.ones(placedb.targetOverflow.size, dtype=self.gamma.dtype, device=self.gamma.device) - self.update_gamma(0, initOverflow, self.base_gamma(params, placedb)) - - # compute weighted average wirelength from position - self.num_bins_x = placedb.num_bins_x - self.num_bins_y = placedb.num_bins_y - - self.name = "%dx%d bins" % (self.num_bins_x, self.num_bins_y) - - self.op_collections.wirelength_op, self.op_collections.update_gamma_op = self.build_weighted_average_wl( - params, placedb, self.data_collections, self.op_collections.pin_pos_op) - - self.op_collections.density_op = self.build_electric_potential( - params, - placedb, - self.data_collections, - self.num_bins_x, - self.num_bins_y, - name=self.name) - - ### build multiple density op for multi-electric field - self.op_collections.fence_region_density_ops, self.op_collections.fence_region_density_merged_op, self.op_collections.fence_region_density_overflow_merged_op = self.build_multi_fence_region_density_op() - - self.op_collections.update_density_weight_op = self.build_update_density_weight(params, placedb) - - self.op_collections.precondition_op = self.build_precondition(params, placedb, self.data_collections) - - self.op_collections.noise_op = self.build_noise(params, placedb, self.data_collections) - - if params.routability_opt_flag: - # compute congestion map, RISA/RUDY congestion map - self.op_collections.route_utilization_map_op = self.build_route_utilization_map(params, placedb, self.data_collections) - self.op_collections.pin_utilization_map_op = self.build_pin_utilization_map(params, placedb, self.data_collections) - #FPGA clustering compatibility resource area computation - self.op_collections.clustering_compatibility_lut_area_op = self.build_clustering_compatibility_lut_map(params, placedb, self.data_collections) - self.op_collections.clustering_compatibility_ff_area_op = self.build_clustering_compatibility_ff_map(params, placedb, self.data_collections) - # adjust instance area with congestion map - self.op_collections.adjust_node_area_op = self.build_adjust_node_area(params, placedb, self.data_collections) - - self.Lgamma_iteration = global_place_params["iteration"] - if 'Llambda_density_weight_iteration' in global_place_params: - self.Llambda_density_weight_iteration = global_place_params['Llambda_density_weight_iteration'] - else: - self.Llambda_density_weight_iteration = 1 - if 'Lsub_iteration' in global_place_params: - self.Lsub_iteration = global_place_params['Lsub_iteration'] - else: - self.Lsub_iteration = 1 - if 'routability_Lsub_iteration' in global_place_params: - self.routability_Lsub_iteration = global_place_params['routability_Lsub_iteration'] - else: - self.routability_Lsub_iteration = self.Lsub_iteration - self.start_fence_region_density = False - - def obj_fn(self, pos): - """ - @brief Compute objective. - wirelength + density_weight * density penalty - @param pos locations of cells - @return objective value - """ - wirelength = self.op_collections.wirelength_op(pos) - - density = self.op_collections.fence_region_density_merged_op(pos) - - if self.init_density is None: - ### record initial density - self.init_density = density.data.clone() - ### density weight subgradient preconditioner - self.density_weight_grad_precond = self.init_density.masked_scatter(self.init_density != 0, 1/self.init_density[self.init_density != 0]) - ### quadratic density penalty - if self.quad_penalty_coeff is None: - self.quad_penalty_coeff = self.density_quad_coeff/2 * self.density_weight_grad_precond - - density = density*(1+self.quad_penalty_coeff * density) - - result = wirelength + self.density_weight_u.dot(density) - #logging.info("result: %g" %(result)) - - return result - - def obj_and_grad_fn(self, pos): - """ - @brief compute objective and gradient. - wirelength + density_weight * density penalty - @param pos locations of cells - @return objective value - """ - #self.check_gradient(pos) - if pos.grad is not None: - pos.grad.zero_() - - obj = self.obj_fn(pos) - obj.backward() - - self.op_collections.precondition_op(pos.grad, self.density_weight, self.precondWL, self.update_mask) - - return obj, pos.grad - - def forward(self): - """ - @brief Compute objective with current locations of cells. - """ - return self.obj_fn(self.data_collections.pos[0]) - - def check_gradient(self, pos): - """ - @brief check gradient for debug - @param pos locations of cells - """ - wirelength = self.op_collections.wirelength_op(pos) - - if pos.grad is not None: - pos.grad.zero_() - wirelength.backward() - wirelength_grad = pos.grad.clone() - - pos.grad.zero_() - density = self.density_weight * self.op_collections.density_op(pos) - density.backward() - density_grad = pos.grad.clone() - - wirelength_grad_norm = wirelength_grad.norm(p=1) - density_grad_norm = density_grad.norm(p=1) - - pos.grad.zero_() - - def estimate_initial_learning_rate(self, x_k): - """ - @brief Estimate initial learning rate by moving a small step. - Computed as | x_k - x_k_1 |_2 / | g_k - g_k_1 |_2. - @param x_k current solution - """ - obj_k, g_k = self.obj_and_grad_fn(x_k) - lr = 0.001 * min((self.placedb.xh - self.placedb.xl), (self.placedb.yh-self.placedb.yl)) * (self.placedb.num_nodes - self.placedb.num_terminals) - lr /= g_k.norm(p=1) - x_k_1 = torch.autograd.Variable(x_k - lr * g_k, requires_grad=True) - obj_k_1, g_k_1 = self.obj_and_grad_fn(x_k_1) - #print("alpha = %g"%(lr)) - #print("learning rate = %g"%((x_k - x_k_1).norm(p=2) / (g_k - g_k_1).norm(p=2))) - - return (x_k - x_k_1).norm(p=2) / (g_k - g_k_1).norm(p=2) - - def build_weighted_average_wl(self, params, placedb, data_collections, pin_pos_op): - """ - @brief build the op to compute weighted average wirelength - @param params parameters - @param placedb placement database - @param data_collections a collection of data and variables required for constructing ops - @param pin_pos_op the op to compute pin locations according to cell locations - """ - # use WeightedAverageWirelength atomic - wirelength_for_pin_op = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=data_collections.flat_net2pin_map, - netpin_start=data_collections.flat_net2pin_start_map, - pin2net_map=data_collections.pin2net_map, - net_weights=data_collections.net_weights, - num_carry_chains=placedb.num_carry_chains, - cc_net_weight=placedb.carry_chain_net_weight, - dir_net_weight=params.dir_net_weight, - net_mask=data_collections.net_mask_ignore_large_degrees, - pin_mask=data_collections.pin_mask_ignore_fixed_macros, - gamma=self.gamma, - net_bounding_box_min=data_collections.net_bounding_box_min, - net_bounding_box_max=data_collections.net_bounding_box_max, - num_threads=params.num_threads, - algorithm='merged') - #algorithm='net-by-net') - - # wirelength for position - def build_wirelength_op(pos): - return wirelength_for_pin_op(pin_pos_op(pos)) - - # update gamma - base_gamma = self.base_gamma(params, placedb) - - def build_update_gamma_op(iteration, overflow): - self.update_gamma(iteration, overflow, base_gamma) - #logging.debug("update gamma to %g" % (wirelength_for_pin_op.gamma.data)) - - return build_wirelength_op, build_update_gamma_op - - def build_electric_potential(self, params, placedb, data_collections, - num_bins_x, num_bins_y, name, region_id=None, fence_regions=None): - """ - @brief e-place electrostatic potential - @param params parameters - @param placedb placement database - @param data_collections a collection of data and variables required for constructing ops - @param num_bins_x number of bins in horizontal direction - @param num_bins_y number of bins in vertical direction - @param padding number of padding bins to left, right, bottom, top of the placement region - @param name string for printing - @param fence_regions a [n_subregions, 4] tensor for fence regions potential penalty - """ - bin_size_x = (placedb.xh - placedb.xl) / num_bins_x - bin_size_y = (placedb.yh - placedb.yl) / num_bins_y - - return electric_potential.ElectricPotential( - node_size_x=data_collections.node_size_x, - node_size_y=data_collections.node_size_y, - xl=placedb.xl, - yl=placedb.yl, - xh=placedb.xh, - yh=placedb.yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=placedb.num_filler_nodes, - deterministic_flag=params.deterministic_flag, - sorted_node_map=data_collections.sorted_node_map, - region_id=region_id, - fence_regions=fence_regions, - node2fence_region_map=data_collections.node2fence_region_map, - placedb=placedb) - - def initialize_density_weight(self, params, placedb): - """ - @brief compute initial density weight - @param params parameters - @param placedb placement database - """ - #Updated to elfPlace - wirelength = self.op_collections.wirelength_op(self.data_collections.pos[0]) - - if self.data_collections.pos[0].grad is not None: - self.data_collections.pos[0].grad.zero_() - wirelength.backward() - wirelength_grad_norm = self.data_collections.pos[0].grad.norm(p=1) - #content = "Initial WL grad norm = %.3E" % (wirelength_grad_norm) - - self.data_collections.pos[0].grad.zero_() - density_weight = [] - density_list = [] - density_grad_list = [] - for density_op in self.op_collections.fence_region_density_ops: - density_i = density_op(self.data_collections.pos[0]) - density_list.append(density_i.data.clone()) - density_i.backward() - density_grad_list.append(self.data_collections.pos[0].grad.data.clone()) - self.data_collections.pos[0].grad.zero_() - - ## density = self.op_collections.fence_region_density_merged_op(self.data_collections.pos[0]) - #### record initial density - self.init_density = torch.stack(density_list) - - #### density weight subgradient preconditioner - self.density_weight_grad_precond = self.init_density.masked_scatter(self.init_density != 0, 1/self.init_density[self.init_density != 0]) - #content += ", Density weight gradient preconditioner = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight_grad_precond.cpu().numpy().tolist()]) - #### compute u - self.density_weight_u = self.init_density * self.density_weight_grad_precond - self.density_weight_u += 0.5 * self.density_quad_coeff * self.density_weight_u**2 - #### compute s - density_weight_s = 1 + self.density_quad_coeff * self.init_density * self.density_weight_grad_precond - - #### compute density grad L1 norm - density_grad_norm = sum(self.density_weight_u[i]*density_weight_s[i]*density_grad_list[i].norm(p=1) for i in range(density_weight_s.size(0))) - #content += ", Initial Density grad Norm = %.3E" % (density_grad_norm) - - self.density_weight_u *= params.density_weight * wirelength_grad_norm / density_grad_norm - - #### set initial step size for density weight update - self.density_weight_step_size_inc_low = 1.05 - self.density_weight_step_size_inc_high = 1.06 - - self.density_weight_step_size = (self.density_weight_step_size_inc_low - 1) * self.density_weight_u.norm(p=2) - ### commit initial density weight - self.density_weight = self.density_weight_u * density_weight_s - - return self.density_weight - - def reset_density_weight(self, params, placedb, ratio): - """ - @brief compute initial density weight - @param params parameters - @param placedb placement database - @param ratio to weight density weight - """ - wirelength = self.op_collections.wirelength_op(self.data_collections.pos[0]) - - if self.data_collections.pos[0].grad is not None: - self.data_collections.pos[0].grad.zero_() - wirelength.backward() - wirelength_grad_norm = self.data_collections.pos[0].grad.norm(p=1) - - self.data_collections.pos[0].grad.zero_() - density_list = [] - density_grad_list = [] - for density_op in self.op_collections.fence_region_density_ops: - density_i = density_op(self.data_collections.pos[0]) - density_list.append(density_i.data.clone()) - # if(self.quad_penalty): - # density_i = density_i + self.density_quad_coeff / 2 / density_i * density_i **2 - density_i.backward() - density_grad_list.append(self.data_collections.pos[0].grad.data.clone()) - self.data_collections.pos[0].grad.zero_() - - ### record updated density - self.upd_density = torch.stack(density_list) - - #### Reset lambda - density_weight_u - self.density_weight_u = self.upd_density * self.density_weight_grad_precond - self.density_weight_u += 0.5 * self.density_quad_coeff * self.density_weight_u**2 - #### compute s - density_weight_s = 1 + self.density_quad_coeff * self.upd_density * self.density_weight_grad_precond - - #### compute density grad L1 norm - density_grad_norm = sum(self.density_weight_u[i]*density_weight_s[i]*density_grad_list[i].norm(p=1) for i in range(density_weight_s.size(0))) - - self.density_weight_u *= ratio * wirelength_grad_norm / density_grad_norm - - self.density_weight_step_size = (self.density_weight_step_size_inc_low - 1) * self.density_weight_u.norm(p=2) - # ### commit the density weight - self.density_weight = self.density_weight_u * density_weight_s - - return self.density_weight - - - def build_update_density_weight(self, params, placedb, algo="overflow"): - """ - @brief update density weight - @param params parameters - @param placedb placement database - """ - #Updated to elfPlace - ### params for hpwl mode from RePlAce - ref_hpwl = params.RePlAce_ref_hpwl - LOWER_PCOF = params.RePlAce_LOWER_PCOF - UPPER_PCOF = params.RePlAce_UPPER_PCOF - ### params for overflow mode from elfPlace - # alpha_h = 1.06 - # alpha_l = 1.05 - # self.density_step_size = alpha_h-1 - assert algo in {"hpwl", "overflow"}, logging.error("density weight update not supports hpwl mode or overflow mode") - - def update_density_weight_op_hpwl(cur_metric, prev_metric, iteration): - ### based on hpwl - with torch.no_grad(): - delta_hpwl = cur_metric.hpwl - prev_metric.hpwl - if delta_hpwl < 0: - mu = UPPER_PCOF * np.maximum( - np.power(0.9999, float(iteration)), 0.98) - #mu = UPPER_PCOF*np.maximum(np.power(0.9999, float(iteration)), 1.03) - else: - mu = UPPER_PCOF * torch.pow( - UPPER_PCOF, -delta_hpwl / ref_hpwl).clamp( - min=LOWER_PCOF, max=UPPER_PCOF) - self.density_weight *= mu - - def update_density_weight_op_overflow(cur_metric, prev_metric, iteration): - assert self.quad_penalty == True, "[Error] density weight update based on overflow only works for quadratic density penalty" - ### based on overflow - ### stop updating if a region has lower overflow than stop overflow - with torch.no_grad(): - density_norm = cur_metric.density * self.density_weight_grad_precond - density_weight_grad = density_norm + self.density_quad_coeff/2*density_norm**2 - - #content = "Density Norm = [%s]" % ", ".join(["%.3E" % i for i in density_norm.cpu().numpy().tolist()]) - ##Rachel: Possibility of zero in density_norm for some resource types could result in INF in density weight grad computation - if density_weight_grad.isinf().any(): - density_weight_grad[density_weight_grad == float("Inf")] = 0 - - density_weight_grad /= density_weight_grad.norm(p=2) - - #content += ", Density Weight Grad = [%s]" % ", ".join(["%.3E" % i for i in density_weight_grad.cpu().numpy().tolist()]) - - ### self.density_weight += self.density_weight_step_size * density_weight_grad# * 1e-7 - self.density_weight_u += self.density_weight_step_size * density_weight_grad - density_weight_s = 1 + self.density_quad_coeff * density_norm - - #content += ", Density Weight Step Size = %.3E, " % (self.density_weight_step_size) - #content += "density_weight_s = [%s]" % ", ".join(["%.3E" % i for i in density_weight_s.cpu().numpy().tolist()]) - #content += ", density_weight_u = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight_u.cpu().numpy().tolist()]) - - #### update density weight step size - rate = torch.log(self.density_quad_coeff * density_norm.norm(p=2)).clamp(min=0) - rate = rate / (1 + rate) - rate = rate * (self.density_weight_step_size_inc_high - self.density_weight_step_size_inc_low) + self.density_weight_step_size_inc_low - self.density_weight_step_size *= rate - - #content += ", Rate = %g" % (rate) - #### conditional update if this region's overflow is higher than stop overflow - - density_weight_new = self.density_weight_u * density_weight_s - - self.targetOverflow = torch.tensor(self.placedb.targetOverflow, dtype=torch.float, device=self.data_collections.pos[0].device) - if(self.update_mask is None): - self.update_mask = cur_metric.overflow >= self.targetOverflow - self.lock_mask = cur_metric.overflow < self.targetOverflow - - self.density_weight.masked_scatter_(self.update_mask, density_weight_new[self.update_mask]) - #content += ", density_weight = [%s]" % ", ".join(["%.3E" % i for i in self.density_weight.cpu().numpy().tolist()]) - #logging.info(content) - - - if(not self.quad_penalty and algo == "overflow"): - logging.warn("quadratic density penalty is disabled, density weight update is forced to be based on HPWL") - algo = "hpwl" - if(len(self.placedb.region_boxes) == 0 and algo == "overflow"): - logging.warn("for benchmark without fence region, density weight update is forced to be based on HPWL") - algo = "hpwl" - - update_density_weight_op = {"hpwl":update_density_weight_op_hpwl, "overflow": update_density_weight_op_overflow}[algo] - - return update_density_weight_op - - def base_gamma(self, params, placedb): - """ - @brief compute base gamma - @param params parameters - @param placedb placement database - """ - ## Updated to elfPlace - self.baseWLGamma = [] - self.WLGammaK = [] - self.WLGammaB = [] - self.WLGammaWt = [] - - for idx in range(placedb.regions): - i = placedb.rsrc2compId_map[idx] - if i != -1: - self.baseWLGamma.append(0.5 * params.gamma * (placedb.bin_size_x + placedb.bin_size_y)) - # Compute coeffcient for wirelength gamma updating - # The basic idea is that we want to achieve - # gamma = 10 * base_gamma, if overflow = 1.0 - # gamma = 0.1 * base_gamma, if overflow = target_overflow - # We use function f(ovfl) = 10^(k * ovfl + b) to achieve the two above two points - # So we want - # k + b = 1 - # k * target_overflow + b = -1 - # Then we have - # k = 2.0 / (1 - target_overflow) - # b = 1.0 - k - self.WLGammaK.append(2.0/(1.0 - placedb.targetOverflow[i])) - self.WLGammaB.append(1.0 - self.WLGammaK[i]) - # Compare the wirelength gamma weight to balance gamma updating for different area types - self.WLGammaWt.append(self.precondWL[:placedb.num_physical_nodes][self.data_collections.node2fence_region_map == idx].sum()) - - return self.baseWLGamma - - def update_gamma(self, iteration, overflow, base_gamma): - """ - @brief update gamma in wirelength model - @param iteration optimization step - @param overflow evaluated in current step - @param base_gamma base gamma - """ - ## Updated to elfPlace - # Compute the gamma for each area type and use the pin count-averaged value as the final gamma - totalGamma = 0.0 - totalWt = 0.0 - for idx in range(self.placedb.regions): - i = self.placedb.rsrc2compId_map[idx] - if i != -1: - gma = base_gamma[i] * pow(10.0, overflow[i] * self.WLGammaK[i] + self.WLGammaB[i]) - totalGamma += gma * self.WLGammaWt[i] - totalWt += self.WLGammaWt[i] - - self.gamma.data.fill_(totalGamma / totalWt) - return True - - def build_noise(self, params, placedb, data_collections): - """ - @brief add noise to cell locations - @param params parameters - @param placedb placement database - @param data_collections a collection of data and variables required for constructing ops - """ - node_size = torch.cat([data_collections.node_size_x, data_collections.node_size_y], - dim=0).to(data_collections.device) - - def noise_op(pos, noise_ratio): - with torch.no_grad(): - noise = torch.rand_like(pos) - noise.sub_(0.5).mul_(node_size).mul_(noise_ratio) - # no noise to fixed cells - noise[placedb.num_movable_nodes:placedb.num_nodes - - placedb.num_filler_nodes].zero_() - noise[placedb.num_nodes + - placedb.num_movable_nodes:2 * placedb.num_nodes - - placedb.num_filler_nodes].zero_() - return pos.add_(noise) - - return noise_op - - def build_precondition(self, params, placedb, data_collections): - """ - @brief preconditioning to gradient - @param params parameters - @param placedb placement database - @param data_collections a collection of data and variables required for constructing ops - """ - - #def precondition_op(grad): - # with torch.no_grad(): - # # preconditioning - # node_areas = data_collections.node_size_x * data_collections.node_size_y - # precond = self.density_weight * node_areas - # precond[:placedb.num_physical_nodes].add_(data_collections.pin_weights) - # precond.clamp_(min=1.0) - # grad[0:placedb.num_nodes].div_(precond) - # grad[placedb.num_nodes:placedb.num_nodes*2].div_(precond) - # #for p in pos: - # # grad_norm = p.grad.norm(p=2) - # # logging.debug("grad_norm = %g" % (grad_norm.data)) - # # p.grad.div_(grad_norm.data) - # # logging.debug("grad_norm = %g" % (p.grad.norm(p=2).data)) - # #grad.data[0:placedb.num_movable_nodes].div_(grad[0:placedb.num_movable_nodes].norm(p=2)) - # #grad.data[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes].div_(grad[placedb.num_nodes:placedb.num_nodes+placedb.num_movable_nodes].norm(p=2)) - # return grad - - #return precondition_op - - return PreconditionOpFPGA(placedb, data_collections) - - def build_route_utilization_map(self, params, placedb, data_collections): - """ - @brief routing congestion map based on current cell locations - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - """ - congestion_op = rudy.Rudy( - netpin_start=data_collections.flat_net2pin_start_map, - flat_netpin=data_collections.flat_net2pin_map, - net_weights=data_collections.net_weights, - xl=placedb.routing_grid_xl, - yl=placedb.routing_grid_yl, - xh=placedb.routing_grid_xh, - yh=placedb.routing_grid_yh, - num_bins_x=placedb.num_routing_grids_x, - num_bins_y=placedb.num_routing_grids_y, - unit_horizontal_capacity=placedb.unit_horizontal_capacity, - unit_vertical_capacity=placedb.unit_vertical_capacity, - deterministic_flag=params.deterministic_flag, - initial_horizontal_utilization_map=data_collections. - initial_horizontal_utilization_map, - initial_vertical_utilization_map=data_collections. - initial_vertical_utilization_map, - num_threads=params.num_threads) - - def route_utilization_map_op(pos): - pin_pos = self.op_collections.pin_pos_op(pos) - return congestion_op(pin_pos) - - return route_utilization_map_op - - def build_pin_utilization_map(self, params, placedb, data_collections): - """ - @brief pin density map based on current cell locations - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - """ - return pin_utilization.PinUtilization( - pin_weights=data_collections.pin_weights, - flat_node2pin_start_map=data_collections.flat_node2pin_start_map, - node_size_x=data_collections.node_size_x, - node_size_y=data_collections.node_size_y, - xl=placedb.routing_grid_xl, - yl=placedb.routing_grid_yl, - xh=placedb.routing_grid_xh, - yh=placedb.routing_grid_yh, - num_movable_nodes=placedb.num_movable_nodes, - num_filler_nodes=placedb.num_filler_nodes, - num_bins_x=placedb.num_routing_grids_x, - num_bins_y=placedb.num_routing_grids_y, - unit_pin_capacity=data_collections.unit_pin_capacity, - pin_stretch_ratio=params.pin_stretch_ratio, - deterministic_flag=params.deterministic_flag, - num_threads=params.num_threads) - - #Update for LUT - def build_clustering_compatibility_lut_map(self, params, placedb, data_collections): - """ - @brief clustering compatibility lut map based on current cell locations to ensure maximum input pin constraint is met - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - """ - bins_x = math.ceil((placedb.xh - placedb.xl)/placedb.instDemStddevX) - bins_y = math.ceil((placedb.yh - placedb.yl)/placedb.instDemStddevY) - - return clustering_compatibility.LUTCompatibility( - lut_indices=data_collections.lut_indices, - lut_type=data_collections.cluster_lut_type, - node_size_x=data_collections.node_size_x, - node_size_y=data_collections.node_size_y, - num_bins_x=bins_x, - num_bins_y=bins_y, - num_bins_l=placedb.lutTypeInSliceUnit, - placedb=placedb, - deterministic_flag=params.deterministic_flag, - num_threads=params.num_threads) - - #Update for FF - def build_clustering_compatibility_ff_map(self, params, placedb, data_collections): - """ - @brief clustering compatibility flop map based on current cell locations to ensure control set constraint is met - @param params parameters - @param placedb placement database - @param data_collections a collection of all data and variables required for constructing the ops - """ - bins_x = math.ceil((placedb.xh - placedb.xl)/placedb.instDemStddevX) - bins_y = math.ceil((placedb.yh - placedb.yl)/placedb.instDemStddevY) - return clustering_compatibility.FFCompatibility( - flop_indices=data_collections.flop_indices, - flop_ctrlSets=data_collections.flop_ctrlSets, - node_size_x=data_collections.node_size_x, - node_size_y=data_collections.node_size_y, - num_bins_x=bins_x, - num_bins_y=bins_y, - num_bins_ck=placedb.ctrlSets[:,1].max()+1, - num_bins_ce=placedb.ctrlSets[:,2].max()+1, - placedb=placedb, - deterministic_flag=params.deterministic_flag, - num_threads=params.num_threads) - - def build_adjust_node_area(self, params, placedb, data_collections): - """ - @brief adjust cell area according to routing congestion and pin utilization map - """ - #Include total area only for LUT/FF - total_movable_area = ( - data_collections.node_size_x[:placedb.num_movable_nodes] * - data_collections.node_size_y[:placedb.num_movable_nodes] * - data_collections.flop_lut_mask[:placedb.num_movable_nodes]).sum().item() - lut_compId = placedb.rsrc2compId_map[placedb.rLUTIdx] - lut_filler_start = placedb.filler_start_map[lut_compId] - lut_filler_end = placedb.filler_start_map[lut_compId+1] - lut_fillers = lut_filler_start + lut_filler_end - lut_filler_area = ( - data_collections.node_size_x[placedb.num_physical_nodes + lut_filler_start:placedb.num_physical_nodes + lut_filler_end] * - data_collections.node_size_y[placedb.num_physical_nodes + lut_filler_start:placedb.num_physical_nodes + lut_filler_end]).sum().item() - - flop_compId = placedb.rsrc2compId_map[placedb.rFFIdx] - flop_filler_start = placedb.filler_start_map[flop_compId] - flop_filler_end = placedb.filler_start_map[flop_compId+1] - flop_fillers = flop_filler_start + flop_filler_end - flop_filler_area = ( - data_collections.node_size_x[placedb.num_physical_nodes + flop_filler_start:placedb.num_physical_nodes + flop_filler_end] * - data_collections.node_size_y[placedb.num_physical_nodes + flop_filler_start:placedb.num_physical_nodes + flop_filler_end]).sum().item() - - flop_lut_fillers = lut_fillers + flop_fillers - total_filler_area = lut_filler_area + flop_filler_area - total_place_area = total_movable_area + total_filler_area - - adjust_node_area_op = adjust_node_area.AdjustNodeArea( - placedb=placedb, - flat_node2pin_map=data_collections.flat_node2pin_map, - flat_node2pin_start_map=data_collections.flat_node2pin_start_map, - pin_weights=data_collections.pin_weights, - flop_lut_indices=data_collections.flop_lut_indices, - flop_lut_mask=data_collections.flop_lut_mask, - flop_mask=data_collections.flop_mask, - lut_mask=data_collections.lut_mask, - total_place_area=total_place_area, - total_whitespace_area=total_place_area - total_movable_area, - max_route_opt_adjust_rate=params.max_route_opt_adjust_rate, - route_opt_adjust_exponent=params.route_opt_adjust_exponent, - max_pin_opt_adjust_rate=params.max_pin_opt_adjust_rate, - area_adjust_stop_ratio=params.area_adjust_stop_ratio, - route_area_adjust_stop_ratio=params.route_area_adjust_stop_ratio, - pin_area_adjust_stop_ratio=params.pin_area_adjust_stop_ratio, - unit_pin_capacity=data_collections.unit_pin_capacity) - - def build_adjust_node_area_op(pos, resource_areas, route_utilization_map, pin_utilization_map): - return adjust_node_area_op( - pos, data_collections.node_size_x, - data_collections.node_size_y, data_collections.pin_offset_x, - data_collections.pin_offset_y, 1.0, - resource_areas, route_utilization_map, pin_utilization_map) - - return build_adjust_node_area_op - - def build_multi_fence_region_density_op(self): - # region 0, ..., region n, non_fence_region - self.op_collections.fence_region_density_ops = [] - - for idx in range(self.placedb.rsrc2compId_map.size): - i = self.placedb.rsrc2compId_map[idx] - if i != -1: - #Create for all resource types but do not update fixed types - self.op_collections.fence_region_density_ops.append(self.build_electric_potential( - self.params, - self.placedb, - self.data_collections, - self.num_bins_x, - self.num_bins_y, - name=self.name, - region_id=idx, - fence_regions=self.fixedDemMaps[i]) - ) - - def merged_density_op(pos): - #### stop mask is to stop forward of density - #### 1 represents stop flag - - resdb = torch.stack([densityOp(pos, mode="density") for densityOp in self.op_collections.fence_region_density_ops]) - - return resdb - - def merged_density_overflow_op(pos): - ### stop mask is to stop forward of density - ### 1 represents stop flag - overflow_list, max_density_list = [], [] - for density_op in self.op_collections.fence_region_density_ops: - overflow, max_density = density_op(pos, mode="overflow") - overflow_list.append(overflow) - max_density_list.append(max_density) - overflow_list, max_density_list = torch.stack(overflow_list), torch.stack(max_density_list) - return overflow_list, max_density_list - - self.op_collections.fence_region_density_merged_op = merged_density_op - - self.op_collections.fence_region_density_overflow_merged_op = merged_density_overflow_op - return self.op_collections.fence_region_density_ops, self.op_collections.fence_region_density_merged_op, self.op_collections.fence_region_density_overflow_merged_op - - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py deleted file mode 100644 index e334e200..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py +++ /dev/null @@ -1,130 +0,0 @@ -## -# @file Placer.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Sep 2020 -# @brief Main file to run the entire placement flow. -# - -import matplotlib -matplotlib.use('Agg') -import os -import sys -import time -import numpy as np -import logging -# for consistency between python2 and python3 -root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -if root_dir not in sys.path: - sys.path.append(root_dir) -import dreamplacefpga.configure as configure -from Params import * -from PlaceDB import * -from NonLinearPlace import * -from IFWriter import * -import pdb - -def placeFPGA(params): - """ - @brief Top API to run the entire placement flow. - @param params parameters - """ - assert (not params.gpu) or configure.compile_configurations["CUDA_FOUND"] == 'TRUE', \ - "CANNOT enable GPU without CUDA compiled" - - np.random.seed(params.random_seed) - # Read Database - start = time.time() - placedb = PlaceDBFPGA() - placedb(params) #Call function - - # Placement - placer = NonLinearPlaceFPGA(params, placedb) - metrics = placer(params, placedb) - logging.info("Non-Linear Placement completed in %.2f seconds" % (time.time()-start)) - - # write placement solution - path = "%s/%s" % (params.result_dir, params.design_name()) - if not os.path.exists(path): - os.system("mkdir -p %s" % (path)) - if params.global_place_flag and params.legalize_flag == 0 and placedb.sliceFF_ctrl_mode == "HALF": ##Only global placement is run - gp_out_file = os.path.join(path, "%s.gp.pl" % (params.design_name())) - placedb.write(gp_out_file) - - ##Use elfPlace binary to run legalization and detatiled placement - #elfPlace binary picks file named gp.pl in the current directory - if os.path.exists("thirdparty/elfPlace_LG_DP"): - cp_cmd = "cp %s gp.pl" %(gp_out_file) - os.system(cp_cmd) - out_file = os.path.join(path, "%s_final.%s" % (params.design_name(), params.solution_file_suffix())) - cmd = "./thirdparty/elfPlace_LG_DP --aux %s --numThreads %s --pl %s" % (params.aux_input, params.num_threads, out_file) - logging.info("Legalization and Detailed Placement run using elfPlace (CPU): %s" % (cmd)) - tt = time.time() - os.system(cmd) - logging.info("Legalization and detailed placement completed in %.3f seconds" % (time.time()-tt)) - else: - logging.warning("External legalization & detailed placement engine NOT found at thirdparty/elfPlace_LG_DP") - - elif params.global_place_flag: - if params.legalize_flag: ## Run both global placement and legalization - logging.info("Detailed Placement not run") - else: - gp_out_file = os.path.join(path, "%s.gp.pl" % (params.design_name())) - placedb.write(gp_out_file) - logging.info("Legalization & detailed placement not run") - - final_out_file = os.path.join(path, "%s.final.%s" % (params.design_name(), params.solution_file_suffix())) - placedb.writeFinalSolution(final_out_file) - - logging.info("Total Runtime: %.3f seconds" % (time.time()-start)) - - ##For debug - if params.name_map_file_dump == 1: - placedb.writeMapSolution() - - if params.enable_if == 1: - tt = time.time() - logging.info("Start writing solution to Interchange Format(IF)") - part_name = params.part_name - schema_dir = os.path.join(os.path.dirname(__file__), '../thirdparty/fpga-interchange-schema/interchange') - db2phys = db_to_physicalnetlist(placedb, schema_dir, part_name) - phys_netlist = db2phys.build_physicalnetlist(placedb, final_out_file) - # tcl_generator().write_tcl(phys_netlist) - if_writer = IFWriter(schema_dir) - physical_netlist = if_writer.build_IF(phys_netlist) - if_file = os.path.join(path, "%s.phys" % (params.design_name())) - if_writer.write_IF(physical_netlist, if_file) - logging.info("Interchange Format(IF) Writer completed in %.3f seconds" % (time.time()-tt)) - - -if __name__ == "__main__": - """ - @brief main function to invoke the entire placement flow. - """ - logging.root.name = 'DREAMPlaceFPGA' - logging.basicConfig(level=logging.INFO, format='[%(levelname)-7s] %(name)s - %(message)s', stream=sys.stdout) - - if len(sys.argv) < 2: - logging.error("Input parameters required in json format") - paramsArray = [] - for i in range(1, len(sys.argv)): - params = ParamsFPGA() - params.load(sys.argv[i]) - paramsArray.append(params) - logging.info("Parameters[%d] = %s" % (len(paramsArray), paramsArray)) - - #Settings to minimze non-determinism - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - torch.backends.cudnn.enabled = False - torch.manual_seed(params.random_seed) - np.random.seed(params.random_seed) - #random.seed(params.random_seed) - if params.gpu: - torch.cuda.manual_seed_all(params.random_seed) - torch.cuda.manual_seed(params.random_seed) - - # tt = time.time() - for params in paramsArray: - placeFPGA(params) - # logging.info("Completed Placement in %.3f seconds" % (time.time()-tt)) - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py deleted file mode 100644 index 5882ca67..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jul 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py deleted file mode 100644 index e216a291..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py +++ /dev/null @@ -1,26 +0,0 @@ -## -# @file configure.py -# @author Yibo Lin (DREAMPlace) -# @date Feb 2020 -# @brief Record all configurations including compilation -# - -compile_configurations = { - "CMAKE_CXX_COMPILER" : "/usr/bin/g++-9", - "CMAKE_CC_COMPILER" : "", - "CMAKE_BUILD_TYPE" : "Release", - "CMAKE_CXX_ABI" : "0", - "CMAKE_CXX_STANDARD" : "14", - "PYTHON" : "", - "Boost_DIR" : "", - "Boost_INCLUDE_DIRS" : "/usr/include", - "ZLIB_INCLUDE_DIRS" : "/usr/include", - "ZLIB_LIBRARIES" : "/usr/lib/x86_64-linux-gnu/libz.so", - "CUDA_FOUND" : "", - "CUDA_TOOLKIT_ROOT_DIR" : "", - "CMAKE_CUDA_FLAGS" : "", - "CAIRO_FOUND" : "TRUE", - "CAIRO_INCLUDE_DIRS" : "/usr/include/cairo", - "CAIRO_LIBRARIES" : "/usr/lib/x86_64-linux-gnu/libcairo.so", - } - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in deleted file mode 100644 index cf999a86..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in +++ /dev/null @@ -1,26 +0,0 @@ -## -# @file configure.py -# @author Yibo Lin (DREAMPlace) -# @date Feb 2020 -# @brief Record all configurations including compilation -# - -compile_configurations = { - "CMAKE_CXX_COMPILER" : "${CMAKE_CXX_COMPILER}", - "CMAKE_CC_COMPILER" : "${CMAKE_CC_COMPILER}", - "CMAKE_BUILD_TYPE" : "${CMAKE_BUILD_TYPE}", - "CMAKE_CXX_ABI" : "${CMAKE_CXX_ABI}", - "CMAKE_CXX_STANDARD" : "${CMAKE_CXX_STANDARD}", - "PYTHON" : "${PYTHON}", - "Boost_DIR" : "${Boost_DIR}", - "Boost_INCLUDE_DIRS" : "${Boost_INCLUDE_DIRS}", - "ZLIB_INCLUDE_DIRS" : "${ZLIB_INCLUDE_DIRS}", - "ZLIB_LIBRARIES" : "${ZLIB_LIBRARIES}", - "CUDA_FOUND" : "${CUDA_FOUND}", - "CUDA_TOOLKIT_ROOT_DIR" : "${CUDA_TOOLKIT_ROOT_DIR}", - "CMAKE_CUDA_FLAGS" : "${CMAKE_CUDA_FLAGS}", - "CAIRO_FOUND" : "${CAIRO_FOUND}", - "CAIRO_INCLUDE_DIRS" : "${CAIRO_INCLUDE_DIRS}", - "CAIRO_LIBRARIES" : "${CAIRO_LIBRARIES}", - } - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt deleted file mode 100644 index 5196183c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -add_subdirectory(utility) -add_subdirectory(dct) -add_subdirectory(pin_pos) -add_subdirectory(density_map) -#add_subdirectory(density_overflow) -add_subdirectory(density_potential) -add_subdirectory(logsumexp_wirelength) -add_subdirectory(draw_place) -add_subdirectory(electric_potential) -add_subdirectory(hpwl) -add_subdirectory(move_boundary) -add_subdirectory(weighted_average_wirelength) -#add_subdirectory(rmst_wl) -add_subdirectory(place_io) -add_subdirectory(precondWL) -add_subdirectory(sortNode2Pin) -add_subdirectory(demandMap) -# legalization operators -add_subdirectory(dsp_ram_legalization) -add_subdirectory(lut_ff_legalization) -# routing congestion operator -add_subdirectory(pin_utilization) -add_subdirectory(rudy) -add_subdirectory(adjust_node_area) -add_subdirectory(clustering_compatibility) - -file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") -install( - FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py deleted file mode 100644 index 2fe6334d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin -# @date Jun 2018 -# @brief ops -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt deleted file mode 100644 index 907eb82a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -set(OP_NAME adjust_node_area) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(adjust_node_area_cpp - src/adjust_node_area.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(update_pin_offset_cpp - src/update_pin_offset.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - adjust_node_area_cpp - update_pin_offset_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(adjust_node_area_cuda - src/adjust_node_area_cuda.cpp - src/adjust_node_area_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(update_pin_offset_cuda - src/update_pin_offset_cuda.cpp - src/update_pin_offset_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - adjust_node_area_cuda - update_pin_offset_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py deleted file mode 100644 index a28e9763..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py +++ /dev/null @@ -1,407 +0,0 @@ -## -# @file adjust_node_area.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Nov 2020 -# @brief Nonlinear placement engine to be called with parameters and placement database -# -import math -import torch -from torch import nn -import torch.nn.functional as F -import logging -import pdb - -import dreamplacefpga.ops.adjust_node_area.adjust_node_area_cpp as adjust_node_area_cpp -import dreamplacefpga.ops.adjust_node_area.update_pin_offset_cpp as update_pin_offset_cpp - -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.adjust_node_area.adjust_node_area_cuda as adjust_node_area_cuda - import dreamplacefpga.ops.adjust_node_area.update_pin_offset_cuda as update_pin_offset_cuda - -logger = logging.getLogger(__name__) - -class ComputeNodeAreaFromRouteMap(nn.Module): - def __init__(self, xl, yl, xh, yh, flop_lut_indices, num_movable_nodes, num_bins_x, - num_bins_y): - super(ComputeNodeAreaFromRouteMap, self).__init__() - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.flop_lut_indices = flop_lut_indices - self.num_movable_nodes = num_movable_nodes - self.num_bins_x = num_bins_x - self.num_bins_y = num_bins_y - self.bin_size_x = (xh - xl) / num_bins_x - self.bin_size_y = (yh - yl) / num_bins_y - - def forward(self, pos, node_size_x, node_size_y, utilization_map): - if pos.is_cuda: - func = adjust_node_area_cuda.forward - else: - func = adjust_node_area_cpp.forward - output = func(pos, node_size_x, node_size_y, utilization_map, - self.bin_size_x, self.bin_size_y, self.xl, self.yl, - self.xh, self.yh, self.flop_lut_indices, self.num_movable_nodes, - self.num_bins_x, self.num_bins_y) - return output - - -class ComputeNodeAreaFromPinMap(ComputeNodeAreaFromRouteMap): - def __init__(self, pin_weights, flat_node2pin_start_map, xl, yl, xh, yh, - flop_lut_indices, num_movable_nodes, num_bins_x, num_bins_y, unit_pin_capacity): - super(ComputeNodeAreaFromPinMap, - self).__init__(xl, yl, xh, yh, flop_lut_indices, num_movable_nodes, num_bins_x, - num_bins_y) - bin_area = (xh - xl) / num_bins_x * (yh - yl) / num_bins_y - self.unit_pin_capacity = unit_pin_capacity - # for each physical node, we use the pin counts as the weights - if pin_weights is not None: - self.pin_weights = pin_weights - elif flat_node2pin_start_map is not None: - self.pin_weights = flat_node2pin_start_map[ - 1:self.num_movable_nodes + - 1] - flat_node2pin_start_map[:self.num_movable_nodes] - else: - assert "either pin_weights or flat_node2pin_start_map is required" - - def forward(self, pos, node_size_x, node_size_y, utilization_map): - output = super(ComputeNodeAreaFromPinMap, - self).forward(pos, node_size_x, node_size_y, - utilization_map) - output.mul_(self.pin_weights[:self.num_movable_nodes].to(node_size_x.dtype) / (node_size_x[:self.num_movable_nodes] * node_size_y[:self.num_movable_nodes] * self.unit_pin_capacity)) - return output - - -class AdjustNodeArea(nn.Module): - def __init__( - self, - placedb, - flat_node2pin_map, - flat_node2pin_start_map, - pin_weights, # only one of them needed - flop_lut_indices, - flop_lut_mask, - flop_mask, - lut_mask, - total_place_area, # total placement area excluding fixed cells - total_whitespace_area, # total white space area excluding movable and fixed cells - max_route_opt_adjust_rate, - route_opt_adjust_exponent=2.5, - max_pin_opt_adjust_rate=2.5, - area_adjust_stop_ratio=0.01, - route_area_adjust_stop_ratio=0.01, - pin_area_adjust_stop_ratio=0.05, - unit_pin_capacity=0.0): - - super(AdjustNodeArea, self).__init__() - self.flat_node2pin_start_map = flat_node2pin_start_map - self.flat_node2pin_map = flat_node2pin_map - self.pin_weights = pin_weights - - self.flop_lut_indices = flop_lut_indices - self.flop_lut_mask = flop_lut_mask - self.flop_mask = flop_mask - self.lut_mask = lut_mask - - self.filler_start_map = placedb.filler_start_map - self.xl = placedb.xl - self.xh = placedb.xh - self.yl = placedb.yl - self.yh = placedb.yh - self.num_movable_nodes = placedb.num_movable_nodes - self.num_filler_nodes = placedb.num_filler_nodes - - #mlab - self.num_mlab_nodes = placedb.num_mlab_nodes - if self.num_mlab_nodes > 0: - self.is_mlab_node = placedb.is_mlab_node - self.is_mlab_filler_node = placedb.is_mlab_filler_node - - #large carry chain nodes - self.num_ccNodes = placedb.num_ccNodes - if self.num_ccNodes > 0: - self.is_cc_node = torch.from_numpy(placedb.is_cc_node).to(self.lut_mask.device) - # maximum and minimum instance area adjustment rate for routability optimization - self.max_route_opt_adjust_rate = max_route_opt_adjust_rate - self.min_route_opt_adjust_rate = 1.0 / max_route_opt_adjust_rate - # exponent for adjusting the utilization map - self.route_opt_adjust_exponent = route_opt_adjust_exponent - # maximum and minimum instance area adjustment rate for routability optimization - self.max_pin_opt_adjust_rate = max_pin_opt_adjust_rate - self.min_pin_opt_adjust_rate = 1.0 / max_pin_opt_adjust_rate - - # stop ratio - self.area_adjust_stop_ratio = area_adjust_stop_ratio - self.route_area_adjust_stop_ratio = route_area_adjust_stop_ratio - self.pin_area_adjust_stop_ratio = pin_area_adjust_stop_ratio - - self.compute_node_area_route = ComputeNodeAreaFromRouteMap( - xl=self.xl, - yl=self.yl, - xh=self.xh, - yh=self.yh, - flop_lut_indices=self.flop_lut_indices, - num_movable_nodes=self.num_movable_nodes, - num_bins_x=placedb.num_routing_grids_x, - num_bins_y=placedb.num_routing_grids_y) - self.compute_node_area_pin = ComputeNodeAreaFromPinMap( - pin_weights=self.pin_weights, - flat_node2pin_start_map=self.flat_node2pin_start_map, - xl=self.xl, - yl=self.yl, - xh=self.xh, - yh=self.yh, - flop_lut_indices=self.flop_lut_indices, - num_movable_nodes=self.num_movable_nodes, - num_bins_x=placedb.num_routing_grids_x, - num_bins_y=placedb.num_routing_grids_y, - unit_pin_capacity=unit_pin_capacity) - - # placement area excluding fixed cells - self.total_place_area = total_place_area - # placement area excluding movable and fixed cells - self.total_whitespace_area = total_whitespace_area - - def forward(self, pos, node_size_x, node_size_y, pin_offset_x, - pin_offset_y, target_density, resource_areas, - route_utilization_map, pin_utilization_map): - - with torch.no_grad(): - adjust_area_flag = True - adjust_resource_area_flag = resource_areas is not None - adjust_route_area_flag = route_utilization_map is not None - adjust_pin_area_flag = pin_utilization_map is not None - - if not (adjust_resource_area_flag or adjust_pin_area_flag or adjust_route_area_flag): - return False, False, False, False - - num_physical_nodes = node_size_x.numel() - self.num_filler_nodes - num_flop_lut_fillers = self.filler_start_map[2] - # compute old areas of movable nodes - Ignore DSP/RAM instances - node_size_x_movable = node_size_x[:self.num_movable_nodes] - node_size_y_movable = node_size_y[:self.num_movable_nodes] - - node_size_x_filler = node_size_x[num_physical_nodes:num_physical_nodes+num_flop_lut_fillers] - node_size_y_filler = node_size_y[num_physical_nodes:num_physical_nodes+num_flop_lut_fillers] - old_movable_area = node_size_x_movable * node_size_y_movable - old_filler_area = node_size_x_filler * node_size_y_filler - - #Update for LUT - old_movable_area_lut_sum = (node_size_x_movable * node_size_y_movable * self.lut_mask[:self.num_movable_nodes]).sum() - num_lut_fillers = self.filler_start_map[1] - old_filler_area_lut_sum = (node_size_x_filler[:num_lut_fillers] * node_size_y_filler[:num_lut_fillers]).sum() - - #Update for FF - old_movable_area_flop_sum = (node_size_x_movable * node_size_y_movable * self.flop_mask[:self.num_movable_nodes]).sum() - num_flop_fillers = self.filler_start_map[2] - self.filler_start_map[1] - old_filler_area_flop_sum = (node_size_x_filler[num_lut_fillers:] * node_size_y_filler[num_lut_fillers:]).sum() - old_filler_area_sum = old_filler_area_lut_sum + old_filler_area_flop_sum - - #No filler space available for nodes to inflate - if old_filler_area_sum == 0: - return False, False, False, False - - # compute routability optimized area - if adjust_route_area_flag: - # clamp the routing square of routing utilization map - route_utilization_map_clamp = route_utilization_map.pow(self.route_opt_adjust_exponent).clamp_( - min=self.min_route_opt_adjust_rate, - max=self.max_route_opt_adjust_rate) - route_opt_area = self.compute_node_area_route(pos, node_size_x, node_size_y, route_utilization_map_clamp) - # compute pin density optimized area - if adjust_pin_area_flag: - pin_opt_area = self.compute_node_area_pin(pos, node_size_x, node_size_y, - # clamp the pin utilization map - pin_utilization_map.clamp(min=self.min_pin_opt_adjust_rate, max=self.max_pin_opt_adjust_rate)) - - # compute the extra area max(route_opt_area, pin_opt_area) over the base area for each movable node - # Include all possible conditions - if adjust_resource_area_flag and adjust_route_area_flag and adjust_pin_area_flag: - area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], torch.max(route_opt_area, pin_opt_area)) - old_movable_area) - elif adjust_resource_area_flag and adjust_route_area_flag: - area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], route_opt_area) - old_movable_area) - elif adjust_resource_area_flag and adjust_pin_area_flag: - area_increment = F.relu(torch.max(resource_areas[:self.num_movable_nodes], pin_opt_area) - old_movable_area) - elif adjust_route_area_flag and adjust_pin_area_flag: - area_increment = F.relu(torch.max(route_opt_area, pin_opt_area) - old_movable_area) - elif adjust_resource_area_flag: - area_increment = F.relu(resource_areas[:self.num_movable_nodes] - old_movable_area) - elif adjust_route_area_flag: - area_increment = F.relu(route_opt_area - old_movable_area) - elif adjust_pin_area_flag: - area_increment = F.relu(pin_opt_area - old_movable_area) - else: - area_increment = torch.zeros(old_movable_area.numel(), dtype=old_movable_area.dtype, device=old_movable_area.device) - - #Restrict inflation of large carry chain nodes - if self.num_ccNodes > 0: - cc_mask = self.is_cc_node[:self.num_movable_nodes] == 1 - area_increment[cc_mask] = area_increment[cc_mask].clamp_(max=3.0) - - area_increment_lut_sum = (area_increment * self.lut_mask[:self.num_movable_nodes]).sum() - area_increment_flop_sum = (area_increment * self.flop_mask[:self.num_movable_nodes]).sum() - ## check whether the total area is larger than the max area requirement - scale_factor_lut = max(((self.total_place_area/2.0 - old_movable_area_lut_sum) / area_increment_lut_sum).clamp_(max=1.0), 0.0) - scale_factor_flop = max(((self.total_place_area/2.0 - old_movable_area_flop_sum) / area_increment_flop_sum).clamp_(max=1.0), 0.0) - #Incorporate area of placeholder MLAB fillers for FF - if self.num_mlab_nodes > 0: - mlab_size_x = node_size_x_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] - mlab_size_y = node_size_y_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] - mlab_area = mlab_size_x * mlab_size_y - scale_factor_flop = max(((self.total_place_area/2.0 - old_movable_area_flop_sum - mlab_area.sum()) / area_increment_flop_sum).clamp_(max=1.0), 0.0) - - # set the new_movable_area as base_area + scaled area increment - new_movable_area = old_movable_area + (area_increment * scale_factor_lut * self.lut_mask[:self.num_movable_nodes]) + (area_increment * scale_factor_flop * self.flop_mask[:self.num_movable_nodes]) - - area_increment_sum = area_increment_lut_sum * scale_factor_lut + area_increment_flop_sum * scale_factor_flop - old_movable_area_sum = old_movable_area_lut_sum + old_movable_area_flop_sum - new_movable_area_sum = old_movable_area_sum + area_increment_sum - area_increment_ratio = area_increment_sum / old_movable_area_sum - if area_increment_sum > 0: - logger.info( - "area_increment = %E, area_increment / movable = %g, area_adjust_stop_ratio = %g" - % (area_increment_sum, area_increment_ratio, - self.area_adjust_stop_ratio)) - logger.info( - "area_increment / total_place_area = %g, area_increment / filler = %g, area_increment / total_whitespace_area = %g" - % (area_increment_sum / self.total_place_area, - area_increment_sum / old_filler_area_sum, - area_increment_sum / self.total_whitespace_area)) - - # compute the adjusted area increase ratio - # disable some of the area adjustment if the condition holds - if adjust_resource_area_flag: - resource_area_increment_ratio = F.relu((resource_areas[:self.num_movable_nodes] - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum - adjust_resource_area_flag = resource_area_increment_ratio.item() > self.route_area_adjust_stop_ratio - logger.info( - "resource_area_increment_ratio = %g, resource_area_adjust_stop_ratio = %g" - % (resource_area_increment_ratio, self.route_area_adjust_stop_ratio)) - if adjust_route_area_flag: - route_area_increment_ratio = F.relu((route_opt_area - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum - adjust_route_area_flag = route_area_increment_ratio.data.item() > self.route_area_adjust_stop_ratio - logger.info( - "route_area_increment_ratio = %g, route_area_adjust_stop_ratio = %g" - % (route_area_increment_ratio, self.route_area_adjust_stop_ratio)) - if adjust_pin_area_flag: - pin_area_increment_ratio = F.relu((pin_opt_area - old_movable_area) * self.flop_lut_mask[:self.num_movable_nodes]).sum() / old_movable_area_sum - adjust_pin_area_flag = pin_area_increment_ratio.data.item() > self.pin_area_adjust_stop_ratio - logger.info( - "pin_area_increment_ratio = %g, pin_area_adjust_stop_ratio = %g" - % (pin_area_increment_ratio, self.pin_area_adjust_stop_ratio)) - adjust_area_flag = ( - area_increment_ratio.data.item() > self.area_adjust_stop_ratio - ) and (adjust_resource_area_flag or adjust_route_area_flag or adjust_pin_area_flag) - - if not adjust_area_flag: - return adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag - - num_nodes = pos.numel() // 2 - # adjust the size and positions of movable nodes - # each movable node have its own inflation ratio, the shape of movable_nodes_ratio is (num_movable_nodes) - # we keep the centers the same - movable_nodes_ratio = new_movable_area / old_movable_area - logger.info( - "inflation ratio for movable nodes: avg/max %g/%g" % - (movable_nodes_ratio.mean(), movable_nodes_ratio.max())) - movable_nodes_ratio.sqrt_() - ## convert positions to centers - # scale size - node_size_x_movable *= movable_nodes_ratio - node_size_y_movable *= movable_nodes_ratio - ## convert back to lower left corners - - # finally scale the filler instance areas to let the total area be self.total_place_area - # all the filler nodes share the same deflation ratio, filler_nodes_ratio is a scalar - if new_movable_area_sum + old_filler_area_sum > self.total_place_area: - ##Use common filler size for both LUT/FF - - #Update for LUT fillers - new_movable_area_lut_sum = old_movable_area_lut_sum + area_increment_lut_sum * scale_factor_lut - new_lut_filler_area = F.relu(self.total_place_area/2 - new_movable_area_lut_sum)/num_lut_fillers - new_lut_filler_length = new_lut_filler_area.sqrt() - - node_size_x_filler[:num_lut_fillers] = new_lut_filler_length - node_size_y_filler[:num_lut_fillers] = new_lut_filler_length - - #Update for Flop fillers - new_movable_area_flop_sum = old_movable_area_flop_sum + area_increment_flop_sum * scale_factor_flop - new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum)/num_flop_fillers - new_flop_filler_length = new_flop_filler_area.sqrt() - - node_size_x_filler[num_lut_fillers:] = new_flop_filler_length - node_size_y_filler[num_lut_fillers:] = new_flop_filler_length - - #Old movable area for flops includes mlab placeholder filler area - if self.num_mlab_nodes > 0: - mlab_ff_mask = self.is_mlab_filler_node[num_lut_fillers:num_lut_fillers+num_flop_fillers] == 1 - mlab_size_x = node_size_x_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] - mlab_size_y = node_size_y_movable[self.is_mlab_node[:self.num_movable_nodes] == 1] - mlab_area = mlab_size_x * mlab_size_y - #assign mlab area to placeholder fillers in FF - node_size_x_filler[num_lut_fillers:][mlab_ff_mask] = mlab_size_x - node_size_y_filler[num_lut_fillers:][mlab_ff_mask] = mlab_size_y - new_movable_area_flop_sum = (node_size_x_movable * node_size_y_movable * self.flop_mask[:self.num_movable_nodes]).sum() - if new_movable_area_flop_sum + mlab_area.sum() > self.total_place_area/2: - logger.info( - "FLOP: new movable area %.3E + mlab placeholder filler area %.3E > total_place_area %.3E - CHECK!" - % (new_movable_area_flop_sum, mlab_area.sum(),self.total_place_area/2)) - new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum - mlab_area.sum()) - new_flop_filler_area /= (num_flop_fillers - self.num_mlab_nodes) - new_flop_filler_length = new_flop_filler_area.sqrt() - node_size_x_filler[num_lut_fillers:] = new_flop_filler_length - node_size_y_filler[num_lut_fillers:] = new_flop_filler_length - #Update new_flop_filler_area to correct value - new_flop_filler_area = F.relu(self.total_place_area/2 - new_movable_area_flop_sum) - new_flop_filler_area /= num_flop_fillers - - new_filler_area_sum = F.relu(self.total_place_area - new_movable_area_sum) - else: - new_filler_area_sum = old_filler_area_sum - new_movable_area_lut_sum = old_movable_area_lut_sum - new_lut_filler_area = old_filler_area_lut_sum/num_lut_fillers - new_movable_area_flop_sum = old_movable_area_flop_sum - new_flop_filler_area = old_filler_area_flop_sum/num_flop_fillers - - logger.info( - "old total movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (old_movable_area_sum, old_filler_area_sum, - old_movable_area_sum + old_filler_area_sum, - self.total_place_area)) - logger.info( - "LUT old movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (old_movable_area_lut_sum, old_filler_area_lut_sum, - old_movable_area_lut_sum+old_filler_area_lut_sum, - self.total_place_area/2)) - logger.info( - "FLOP old movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (old_movable_area_flop_sum, old_filler_area_flop_sum, - old_movable_area_flop_sum+old_filler_area_flop_sum, - self.total_place_area/2)) - - - logger.info( - "new total movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (new_movable_area_sum, new_filler_area_sum, - new_movable_area_sum + new_filler_area_sum, - self.total_place_area)) - logger.info( - "LUT new movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (new_movable_area_lut_sum, new_lut_filler_area * num_lut_fillers, - new_movable_area_lut_sum + new_lut_filler_area * num_lut_fillers, - self.total_place_area/2)) - logger.info( - "FLOP new movable nodes area %.3E, filler area %.3E, total movable + filler area %.3E, total_place_area %.3E" - % (new_movable_area_flop_sum, new_flop_filler_area * num_flop_fillers, - new_movable_area_flop_sum + new_flop_filler_area * num_flop_fillers, - self.total_place_area/2)) - - if pos.is_cuda: - func = update_pin_offset_cuda.forward - else: - func = update_pin_offset_cpp.forward - func(node_size_x, node_size_y, self.flat_node2pin_start_map, - self.flat_node2pin_map, movable_nodes_ratio, - self.num_movable_nodes, pin_offset_x, pin_offset_y) - return adjust_area_flag, adjust_resource_area_flag, adjust_route_area_flag, adjust_pin_area_flag diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp deleted file mode 100644 index fafdad39..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file adjust_node_area.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Adjust cell area according to congestion map. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "adjust_node_area/src/scaling_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -DEFINE_AVERAGE_SCALING_FUNCTION(T); - -template -DEFINE_MAX_SCALING_FUNCTION(T); - -#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -int computeInstanceRoutabilityOptimizationMapLauncher( - const T *pos_x, const T *pos_y, const int *indices, const T *node_size_x, const T *node_size_y, - const T *routing_utilization_map, T xl, T yl, T bin_size_x, T bin_size_y, - int num_bins_x, int num_bins_y, int num_movable_nodes, int num_threads, - T *instance_route_area) { - const T inv_bin_size_x = 1.0 / bin_size_x; - const T inv_bin_size_y = 1.0 / bin_size_y; - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max( - int(num_movable_nodes / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int idx = 0; idx < num_movable_nodes; ++idx) { - const int i = indices[idx]; - const T x_min = pos_x[i]; - const T x_max = x_min + node_size_x[i]; - const T y_min = pos_y[i]; - const T y_max = y_min + node_size_y[i]; - - // compute the bin box that this net will affect - // We do NOT follow Wuxi's implementation. Instead, we clamp the bounding - // box. - int bin_index_xl = int((x_min - xl) * inv_bin_size_x); - int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - int bin_index_yl = int((y_min - yl) * inv_bin_size_y); - int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - instance_route_area[i] = - SCALING_OP(routing_utilization_map, xl, yl, bin_size_x, bin_size_y, - num_bins_x, num_bins_y, bin_index_xl, bin_index_yl, - bin_index_xh, bin_index_yh, x_min, y_min, x_max, y_max); - } - - return 0; -} - -at::Tensor adjust_node_area_forward(at::Tensor pos, at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor routing_utilization_map, - double bin_size_x, double bin_size_y, - double xl, double yl, double xh, double yh, - at::Tensor flop_lut_indices, - int num_movable_nodes, int num_bins_x, - int num_bins_y) { - CHECK_FLAT_CPU(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT_CPU(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT_CPU(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - CHECK_FLAT_CPU(flop_lut_indices); - CHECK_CONTIGUOUS(flop_lut_indices); - - int num_nodes = pos.numel() / 2; - at::Tensor instance_route_area = - at::zeros({num_movable_nodes}, pos.options()); - - // compute routability and density optimziation instance area - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeInstanceRoutabilityOptimizationMapLauncher", [&] { - computeInstanceRoutabilityOptimizationMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(flop_lut_indices, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(routing_utilization_map, scalar_t), xl, - yl, bin_size_x, bin_size_y, num_bins_x, num_bins_y, - flop_lut_indices.numel(), at::get_num_threads(), - DREAMPLACE_TENSOR_DATA_PTR(instance_route_area, scalar_t)); - }); - - return instance_route_area; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::adjust_node_area_forward, - "Compute adjusted area for routability optimization"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp deleted file mode 100644 index 274c6e4b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file adjust_node_area_cuda.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Adjust cell area according to congestion map. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -int computeInstanceRoutabilityOptimizationMapCudaLauncher( - const T *pos_x, const T *pos_y, const int *indices, const T *node_size_x, const T *node_size_y, - const T *routing_utilization_map, T xl, T yl, T bin_size_x, T bin_size_y, - int num_bins_x, int num_bins_y, int num_movable_nodes, - T *instance_route_area); - -at::Tensor adjust_node_area_forward(at::Tensor pos, at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor routing_utilization_map, - double bin_size_x, double bin_size_y, - double xl, double yl, double xh, double yh, - at::Tensor flop_lut_indices, - int num_movable_nodes, int num_bins_x, - int num_bins_y) { - CHECK_FLAT_CUDA(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT_CUDA(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT_CUDA(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - CHECK_FLAT_CUDA(flop_lut_indices); - CHECK_CONTIGUOUS(flop_lut_indices); - - int num_nodes = pos.numel() / 2; - at::Tensor instance_route_area = - at::zeros({num_movable_nodes}, pos.options()); - - // compute routability and density optimziation instance area - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeInstanceRoutabilityOptimizationMapCudaLauncher", [&] { - computeInstanceRoutabilityOptimizationMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(flop_lut_indices, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(routing_utilization_map, scalar_t), xl, - yl, bin_size_x, bin_size_y, num_bins_x, num_bins_y, - flop_lut_indices.numel(), - DREAMPLACE_TENSOR_DATA_PTR(instance_route_area, scalar_t)); - }); - - return instance_route_area; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::adjust_node_area_forward, - "Compute adjusted area for routability optimization (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu deleted file mode 100644 index b041e551..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @file adjust_node_area_cuda_kernel.cu - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Adjust cell area according to congestion map. - */ - -#include "utility/src/utils.cuh" -// local dependency -#include "adjust_node_area/src/scaling_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__device__ DEFINE_AVERAGE_SCALING_FUNCTION(T); - -template -__device__ DEFINE_MAX_SCALING_FUNCTION(T); - -template -__global__ void computeInstanceRoutabilityOptimizationMap( - const T *pos_x, const T *pos_y, const int *indices, - const T *node_size_x, const T *node_size_y, - const T *routing_utilization_map, - T xl, T yl, - T bin_size_x, T bin_size_y, - int num_bins_x, int num_bins_y, - int num_movable_nodes, - T *instance_route_area) -{ - const int idx = threadIdx.x + blockDim.x * blockIdx.x; - if (idx < num_movable_nodes) - { - const int i = indices[idx]; - const T x_min = pos_x[i]; - const T x_max = x_min + node_size_x[i]; - const T y_min = pos_y[i]; - const T y_max = y_min + node_size_y[i]; - - // compute the bin box that this net will affect - // We do NOT follow Wuxi's implementation. Instead, we clamp the bounding box. - int bin_index_xl = int((x_min - xl) / bin_size_x); - int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - int bin_index_yl = int((y_min - yl) / bin_size_y); - int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - instance_route_area[i] = SCALING_OP( - routing_utilization_map, - xl, yl, - bin_size_x, bin_size_y, - num_bins_x, num_bins_y, - bin_index_xl, - bin_index_yl, - bin_index_xh, - bin_index_yh, - x_min, y_min, x_max, y_max - ); - } -} - -template -int computeInstanceRoutabilityOptimizationMapCudaLauncher( - const T *pos_x, const T *pos_y, const int *indices, - const T *node_size_x, const T *node_size_y, - const T *routing_utilization_map, - T xl, T yl, - T bin_size_x, T bin_size_y, - int num_bins_x, int num_bins_y, - int num_movable_nodes, - T *instance_route_area) -{ - int thread_count = 512; - int block_count = ceilDiv(num_movable_nodes, thread_count); - computeInstanceRoutabilityOptimizationMap<<>>( - pos_x, pos_y, indices, - node_size_x, node_size_y, - routing_utilization_map, - xl, yl, - bin_size_x, bin_size_y, - num_bins_x, num_bins_y, - num_movable_nodes, - instance_route_area); - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeInstanceRoutabilityOptimizationMapCudaLauncher( \ - const T *pos_x, const T *pos_y, const int *indices, \ - const T *node_size_x, const T *node_size_y, \ - const T *routing_utilization_map, \ - T xl, T yl, \ - T bin_size_x, T bin_size_y, \ - int num_bins_x, int num_bins_y, \ - int num_movable_nodes, \ - T *instance_route_area); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h deleted file mode 100644 index 9c36e856..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file scaling_function.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2020 - */ - -DREAMPLACE_BEGIN_NAMESPACE - -//#define SCALING_OP maxScaling -#define SCALING_OP averageScaling - -#define DEFINE_AVERAGE_SCALING_FUNCTION(T) \ - T averageScaling( \ - const T* routing_utilization_map, \ - T xl, T yl, \ - T bin_size_x, T bin_size_y, \ - int num_bins_x, int num_bins_y, \ - int bin_index_xl, \ - int bin_index_yl, \ - int bin_index_xh, \ - int bin_index_yh, \ - T x_min, T y_min, T x_max, T y_max \ - ) \ - { \ - T area = 0; \ - for (int x = bin_index_xl; x < bin_index_xh; ++x) \ - { \ - for (int y = bin_index_yl; y < bin_index_yh; ++y) \ - { \ - T bin_xl = xl + x * bin_size_x; \ - T bin_yl = yl + y * bin_size_y; \ - T bin_xh = bin_xl + bin_size_x; \ - T bin_yh = bin_yl + bin_size_y; \ - T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * \ - DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); \ - area += overlap * routing_utilization_map[x * num_bins_y + y]; \ - } \ - } \ - return area; \ - } - -#define DEFINE_MAX_SCALING_FUNCTION(T) \ - T maxScaling( \ - const T* routing_utilization_map, \ - T xl, T yl, \ - T bin_size_x, T bin_size_y, \ - int num_bins_x, int num_bins_y, \ - int bin_index_xl, \ - int bin_index_yl, \ - int bin_index_xh, \ - int bin_index_yh, \ - T x_min, T y_min, T x_max, T y_max \ - ) \ - { \ - T util = 0; \ - for (int x = bin_index_xl; x < bin_index_xh; ++x) \ - { \ - for (int y = bin_index_yl; y < bin_index_yh; ++y) \ - { \ - util = DREAMPLACE_STD_NAMESPACE::max(util, routing_utilization_map[x * num_bins_y + y]); \ - } \ - } \ - T area = (x_max - x_min) * (y_max - y_min); \ - return area * util; \ - } - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp deleted file mode 100644 index d97fa038..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -void updatePinOffset(const T *node_size_x, const T *node_size_y, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, const T *node_ratios, - const int num_nodes, T *pin_offset_x, T *pin_offset_y, - const int num_threads) { -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) { - T ratio = (node_ratios[i] - 1) / 2; - T sx = node_size_x[i]; - T sy = node_size_y[i]; - - int start = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i + 1]; - for (int j = start; j < end; ++j) { - int pin_id = flat_node2pin_map[j]; - pin_offset_x[pin_id] += ratio * sx; - pin_offset_y[pin_id] += ratio * sy; - } - } -} - -void update_pin_offset_forward(at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor node_ratios, int num_movable_nodes, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y) { - CHECK_FLAT_CPU(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - - CHECK_FLAT_CPU(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - - CHECK_FLAT_CPU(node_ratios); - CHECK_CONTIGUOUS(node_ratios); - - CHECK_FLAT_CPU(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - - CHECK_FLAT_CPU(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pin_offset_x, "updatePinOffset", [&] { - updatePinOffset( - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node_ratios, scalar_t), - num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - at::get_num_threads()); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::update_pin_offset_forward, - "Update pin offset with cell scaling"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp deleted file mode 100644 index 8ad4692f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -void updatePinOffsetCudaLauncher(const T *node_size_x, const T *node_size_y, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const T *node_ratios, const int num_nodes, - T *pin_offset_x, T *pin_offset_y); - -void update_pin_offset_forward(at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor node_ratios, int num_movable_nodes, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y) { - CHECK_FLAT_CUDA(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - - CHECK_FLAT_CUDA(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - - CHECK_FLAT_CUDA(node_ratios); - CHECK_CONTIGUOUS(node_ratios); - - CHECK_FLAT_CUDA(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - - CHECK_FLAT_CUDA(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pin_offset_x, "updatePinOffsetCudaLauncher", [&] { - updatePinOffsetCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node_ratios, scalar_t), - num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t)); - }); -} - -DREAMPLACE_END_NAMESPACE -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::update_pin_offset_forward, - "Update pin offset with cell scaling"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu deleted file mode 100644 index c3b1e9d8..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu +++ /dev/null @@ -1,67 +0,0 @@ -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void updatePinOffset( - const T* node_size_x, const T* node_size_y, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const T *node_ratios, - const int num_nodes, - T *pin_offset_x, T *pin_offset_y - ) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nodes) - { - T ratio = (node_ratios[i] - 1) / 2; - T sx = node_size_x[i]; - T sy = node_size_y[i]; - - int start = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i + 1]; - for (int j = start; j < end; ++j) - { - int pin_id = flat_node2pin_map[j]; - pin_offset_x[pin_id] += ratio * sx; - pin_offset_y[pin_id] += ratio * sy; - } - } -} - -template -void updatePinOffsetCudaLauncher( - const T* node_size_x, const T* node_size_y, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const T *node_ratios, - const int num_nodes, - T *pin_offset_x, T *pin_offset_y - ) -{ - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - updatePinOffset<<>>( - node_size_x, node_size_y, - flat_node2pin_start_map, - flat_node2pin_map, - node_ratios, - num_nodes, - pin_offset_x, pin_offset_y - ); -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template void updatePinOffsetCudaLauncher( \ - const T* node_size_x, const T* node_size_y, \ - const int *flat_node2pin_start_map, \ - const int *flat_node2pin_map, \ - const T *node_ratios, \ - const int num_nodes, \ - T *pin_offset_x, T *pin_offset_y); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt deleted file mode 100644 index b9ba8e5d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -set(OP_NAME clustering_compatibility) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(clustering_compatibility_cpp - src/lut_compatibility.cpp - src/flop_compatibility.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - clustering_compatibility_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(clustering_compatibility_cuda - src/lut_compatibility_cuda.cpp - src/lut_compatibility_cuda_kernel.cu - src/flop_compatibility_cuda.cpp - src/flop_compatibility_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - clustering_compatibility_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py deleted file mode 100644 index da346be1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Oct 2020 -# - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py deleted file mode 100644 index 37f7a0c8..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py +++ /dev/null @@ -1,237 +0,0 @@ -''' -@File: clustering_compatibility.py -@Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) -@Date: June 2023 -''' -import math -import torch -from torch import nn -from torch.autograd import Function -import matplotlib.pyplot as plt -import pdb - -import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility_cpp as clustering_compatibility_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.clustering_compatibility.clustering_compatibility_cuda as clustering_compatibility_cuda - -class LUTCompatibility(nn.Module): - def __init__(self, - lut_indices, lut_type, node_size_x, node_size_y, - num_bins_x, num_bins_y, num_bins_l, - placedb, deterministic_flag, - num_threads - ): - super(LUTCompatibility, self).__init__() - self.lut_indices = lut_indices - self.lut_type = lut_type - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.num_threads = num_threads - self.num_bins_x = num_bins_x - self.num_bins_y = num_bins_y - self.num_bins_l = num_bins_l - self.xl = placedb.xl - self.yl = placedb.yl - self.xh = placedb.xh - self.yh = placedb.yh - self.inst_stddev_x = placedb.instDemStddevX - self.inst_stddev_y = placedb.instDemStddevY - self.inst_stddev_trunc = placedb.instDemStddevTrunc - self.deterministic_flag = deterministic_flag - self.lutFracturesMap = placedb.lutFracturesMap - self.SLICE_CAPACITY = placedb.SLICE_CAPACITY - self.maxLUTSize = placedb.lutTypeInSliceUnit - self.subSlice_area = 1/placedb.HALF_SLICE_CAPACITY - - self.half_ctrl_mode = 0 - if placedb.sliceFF_ctrl_mode == "HALF": - self.half_ctrl_mode = 1 - - - def forward(self, pos): - lutType_DemMap = torch.zeros((self.num_bins_x, self.num_bins_y, self.num_bins_l), dtype=pos.dtype, device=pos.device) - resource_areas = torch.zeros(len(self.node_size_x), dtype=pos.dtype, device=pos.device) - - ext_bin = max(round(self.inst_stddev_trunc - 0.5), 0) - demandX = torch.zeros((2 * ext_bin + 1), dtype=pos.dtype, device=pos.device) - demandY = torch.zeros_like(demandX) - - lut_fracture = torch.zeros((self.num_bins_l, self.num_bins_l), dtype=torch.int, device=pos.device) - - if self.half_ctrl_mode == 0: - resource_areas[self.lut_indices.long()] = (self.node_size_x*self.node_size_y)[self.lut_indices.long()] - #Set the LUTs one sizes smaller than maxLUTSize to also be subSlice_area - val = self.maxLUTSize - 2 - large_lut_indices = torch.where(self.lut_type == val)[0] - if (large_lut_indices.shape[0] > 0): - resource_areas[large_lut_indices] = self.subSlice_area - #Set the LUTs two sizes smaller than maxLUTSize to be 0.75xsubSlice_area - val = self.maxLUTSize - 3 - large_lut_indices = torch.where(self.lut_type == val)[0] - if (large_lut_indices.shape[0] > 0): - resource_areas[large_lut_indices] = 0.75*self.subSlice_area - return resource_areas - - for i in range(self.num_bins_l): - lut_fracture[i][self.lutFracturesMap[i]] = 1 - - if pos.is_cuda: - areaMap = clustering_compatibility_cuda.lut_compatibility( - pos.view(pos.numel()), - self.lut_indices, - self.lut_type, - self.node_size_x, - self.node_size_y, - lut_fracture, - self.num_bins_x, - self.num_bins_y, - self.num_bins_l, - self.xl, - self.yl, - self.xh, - self.yh, - self.inst_stddev_x, - self.inst_stddev_y, - 1.0/self.inst_stddev_x, - 1.0/self.inst_stddev_y, - ext_bin, - self.inst_stddev_x * self.inst_stddev_y, - 1/math.sqrt(2.0), - self.deterministic_flag, - lutType_DemMap, - resource_areas - ) - else: - areaMap = clustering_compatibility_cpp.lut_compatibility( - pos.view(pos.numel()), - self.lut_indices, - self.lut_type, - self.node_size_x, - self.node_size_y, - lut_fracture, - self.num_bins_x, - self.num_bins_y, - self.num_bins_l, - self.num_threads, - self.inst_stddev_x, - self.inst_stddev_y, - 1.0/self.inst_stddev_x, - 1.0/self.inst_stddev_y, - ext_bin, - self.inst_stddev_x * self.inst_stddev_y, - 1/math.sqrt(2.0), - demandX, - demandY, - lutType_DemMap, - resource_areas - ) - - # Include post-processing if any here - resource_areas /= self.SLICE_CAPACITY - - return resource_areas - - -class FFCompatibility(nn.Module): - def __init__(self, - flop_indices, flop_ctrlSets, node_size_x, node_size_y, - num_bins_x, num_bins_y, num_bins_ck, num_bins_ce, - placedb, deterministic_flag, - num_threads - ): - super(FFCompatibility, self).__init__() - self.flop_indices = flop_indices - self.flop_ctrlSets = flop_ctrlSets - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.num_bins_x = num_bins_x - self.num_bins_y = num_bins_y - self.num_bins_ck = num_bins_ck - self.num_bins_ce = num_bins_ce - self.num_threads = num_threads - self.deterministic_flag = deterministic_flag - self.xl = placedb.xl - self.yl = placedb.yl - self.xh = placedb.xh - self.yh = placedb.yh - self.inst_stddev_x = placedb.instDemStddevX - self.inst_stddev_y = placedb.instDemStddevY - self.inst_stddev_trunc = placedb.instDemStddevTrunc - self.SLICE_CAPACITY = placedb.SLICE_CAPACITY - - self.half_ctrl_mode = 0 - if placedb.sliceFF_ctrl_mode == "HALF": - self.half_ctrl_mode = 1 - - def forward(self, pos): - resource_areas = torch.zeros(len(self.node_size_x), dtype=pos.dtype, device=pos.device) - - if self.half_ctrl_mode == 0 and self.num_bins_ck == 1: - resource_areas[self.flop_indices.long()] = (self.node_size_x*self.node_size_y)[self.flop_indices.long()] - return resource_areas - - flopType_DemMap = torch.zeros((self.num_bins_x, self.num_bins_y, self.num_bins_ck, self.num_bins_ce), dtype=pos.dtype, device=pos.device) - - ext_bin = max(round(self.inst_stddev_trunc - 0.5), 0) - demandX = torch.zeros((2 * ext_bin + 1), dtype=pos.dtype, device=pos.device) - demandY = torch.zeros_like(demandX) - - if pos.is_cuda: - areaMap = clustering_compatibility_cuda.flop_compatibility( - pos.view(pos.numel()), - self.flop_indices, - self.flop_ctrlSets, - self.node_size_x, - self.node_size_y, - self.num_bins_x, - self.num_bins_y, - self.num_bins_ck, - self.num_bins_ce, - self.xl, - self.yl, - self.xh, - self.yh, - self.inst_stddev_x, - self.inst_stddev_y, - 1.0/self.inst_stddev_x, - 1.0/self.inst_stddev_y, - ext_bin, - self.inst_stddev_x * self.inst_stddev_y, - 1/math.sqrt(2.0), - self.SLICE_CAPACITY, - self.deterministic_flag, - flopType_DemMap, - resource_areas - ) - else: - areaMap = clustering_compatibility_cpp.flop_compatibility( - pos.view(pos.numel()), - self.flop_indices, - self.flop_ctrlSets, - self.node_size_x, - self.node_size_y, - self.num_bins_x, - self.num_bins_y, - self.num_bins_ck, - self.num_bins_ce, - self.num_threads, - self.inst_stddev_x, - self.inst_stddev_y, - 1.0/self.inst_stddev_x, - 1.0/self.inst_stddev_y, - ext_bin, - self.inst_stddev_x * self.inst_stddev_y, - 1/math.sqrt(2.0), - self.SLICE_CAPACITY, - demandX, - demandY, - flopType_DemMap, - resource_areas - ) - - # Include post-processing if any here - resource_areas /= self.SLICE_CAPACITY - - return resource_areas - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp deleted file mode 100644 index 3941a2e4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/** - * @file flop_compatibility.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for FLOP based on elfPlace. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "clustering_compatibility/src/functions.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define gaussian_auc_function -template -DEFINE_GAUSSIAN_AUC_FUNCTION(T); -/// define smooth_ceil_function -template -DEFINE_SMOOTH_CEIL_FUNCTION(T); -/// define flop_aggregate_demand_function -template -DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(T); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map node by node -template -int fillDemandMapFF(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - int num_threads, int num_nodes, - T stddev_x, T stddev_y, - T inv_stddev_x, T inv_stddev_y, - int ext_bin, T inv_sqrt, - T *demandX, T *demandY, T *demMap) -{ - - //unsigned int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - unsigned int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - unsigned int o_p = num_bins_ck * num_bins_ce; - for (unsigned int i = 0; i < num_nodes; ++i) - { - const unsigned int nIdx = indices[i]; - T node_x = pos_x[nIdx] + 0.5 * node_size_x[nIdx]; - T node_y = pos_y[nIdx] + 0.5 * node_size_y[nIdx]; - //Ctrl set values - int cksr = ctrlSets[i*3 + 1]; - int ce = ctrlSets[i*3 + 2]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - // compute the bin box that this net will affect - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); - T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); - - T sf = 1.0 / (gaussianX * gaussianY); - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - demandX[x - bin_index_xl] = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); - } - - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - demandY[y - bin_index_yl] = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); - } - - ////DBG - //std::cout << "x limit: " << bin_index_xl << " to " << bin_index_xh << std::endl; - //std::cout << "y limit: " << bin_index_yl << " to " << bin_index_yh << std::endl; - ////DBG - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - //#pragma omp atomic update - unsigned int idx = x * n_o_p + y * o_p + cksr * num_bins_ce + ce; - T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; - ////DBG - //std::cout << "idx = " << x << "(x) * " << n_o_p << "(n_o_p) + " - // << y << "(y) * " << o_p << "(o_p) + " - // << cksr << "(cksr) * " << num_bins_ce << "(num_bins_ce) + " - // << ce << "(ce)" << std::endl; - //std::cout << "FillDemandMapFF: Add " << dem << " to demMap at " << idx << std::endl; - ////DBG - demMap[idx] += dem; - } - } - } - return 0; -} - - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaFF(const T *demMap, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - int num_threads, T stddev_x, T stddev_y, - int ext_bin, T bin_area, T half_slice, T *areaMap) -{ - - int total_bins = num_bins_x*num_bins_y; - int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - int o_p = num_bins_ck * num_bins_ce; - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(total_bins / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < total_bins; ++i) - { - ////DBG - //std::cout << "Start for Bin " << i << " out of " << total_bins << std::endl; - ////DBG - - int binX = int(i/num_bins_y); - int binY = int(i%num_bins_y); - - // compute the bin box - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - int index = binX * n_o_p + binY * o_p; - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - if (x != binX && y != binY) - { - unsigned int idx = x * n_o_p + y * o_p; - flop_aggregate_demand_function(demMap, idx, areaMap, index, num_bins_ck, num_bins_ce); - } - } - } - ////DBG - //std::cout << "For Bin " << i << " completed flop_aggregate_demand_function for x: " << bin_index_xl << " -> " << bin_index_xh - // << " and y : " << bin_index_yl << " -> " << bin_index_yh << std::endl; - ////DBG - - //Flop compute areas - for (int ck = 0; ck < num_bins_ck; ++ck) - { - T totalQ = 0.0; - for (int ce = 0; ce < num_bins_ce; ++ce) - { - unsigned int updIdx = index + ck*num_bins_ce + ce; - if (areaMap[updIdx] > 0.0) - { - totalQ += smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); - } - } - - T sf = half_slice * smooth_ceil_function(totalQ*0.5, 0.5) / totalQ; - - for (int cE = 0; cE < num_bins_ce; ++cE) - { - unsigned int updIdx = index + ck*num_bins_ce + cE; - if (areaMap[updIdx] > 0.0) - { - T qrt = smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); - areaMap[updIdx] = sf * qrt / areaMap[updIdx]; - } - } - } - ////DBG - //std::cout << "For Bin " << i << " completed flop area compute for num_bins_ck: " << num_bins_ck - // << " and num_bins_ce: " << num_bins_ce << std::endl; - ////DBG - - } - return 0; -} - - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasFF(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T *areaMap, - int num_threads, int num_nodes, - T inv_stddev_x, T inv_stddev_y, - T *instAreas) -{ - - int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - int o_p = num_bins_ck * num_bins_ce; - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (unsigned int i = 0; i < num_nodes; ++i) - { - const unsigned int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - //Ctrl set values - int cksr = ctrlSets[i*3 + 1]; - int ce = ctrlSets[i*3 + 2]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - unsigned int index = binX * n_o_p + binY * o_p + cksr * num_bins_ce + ce; - instAreas[idx] = areaMap[index]; - } - return 0; -} - -at::Tensor flop_compatibility( - at::Tensor pos, - at::Tensor indices, - at::Tensor ctrlSets, - at::Tensor node_size_x, - at::Tensor node_size_y, - int num_bins_x, - int num_bins_y, - int num_bins_ck, - int num_bins_ce, - int num_threads, - double stddev_x, - double stddev_y, - double inv_stddev_x, - double inv_stddev_y, - int ext_bin, - double bin_area, - double inv_sqrt, - int slice_capacity, - at::Tensor demandX, - at::Tensor demandY, - at::Tensor demMap, - at::Tensor rsrcAreas - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(indices); - CHECK_CONTIGUOUS(indices); - - CHECK_FLAT(ctrlSets); - CHECK_CONTIGUOUS(ctrlSets); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - int num_nodes = pos.numel() / 2; - double half_slice = slice_capacity / 2.0; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapFF", [&] { - fillDemandMapFF( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - num_threads, indices.numel(), - stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt, - DREAMPLACE_TENSOR_DATA_PTR(demandX, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(demandY, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); - }); - - //std::cout << "Completed fillDemandMapFF" << std::endl; - - at::Tensor areaMap = demMap.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaFF", [&] { - computeInstanceAreaFF( - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), - num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - num_threads, - stddev_x, stddev_y, ext_bin, bin_area, half_slice, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); - }); - //std::cout << "Completed computeInstanceAreaFF" << std::endl; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasFF", [&] { - collectInstanceAreasFF( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_y, num_bins_ck, num_bins_ce, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), - num_threads, indices.numel(), - inv_stddev_x, inv_stddev_y, - DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); - }); - //std::cout << "Completed collectInstanceAreasFF" << std::endl; - - return areaMap; -} - -DREAMPLACE_END_NAMESPACE - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp deleted file mode 100644 index bdbcdd4a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @file flop_compatibility_cuda.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for Flop based on elfPlace. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map node by node -template -int fillDemandMapFFCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T xl, const T yl, - const T xh, const T yh, - const int num_nodes, - const T stddev_x, const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt, - const int deterministic_flag, - T *demMap); - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaFFCuda(const T *demMap, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T stddev_x, const T stddev_y, - const int ext_bin, const T bin_area, - const T half_slice, T *areaMap); - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasFFCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T *areaMap, - const int num_nodes, - const T inv_stddev_x, - const T inv_stddev_y, - T *instAreas); - -at::Tensor flop_compatibility( - at::Tensor pos, - at::Tensor indices, - at::Tensor ctrlSets, - at::Tensor node_size_x, - at::Tensor node_size_y, - int num_bins_x, - int num_bins_y, - int num_bins_ck, - int num_bins_ce, - double xl, - double yl, - double xh, - double yh, - double stddev_x, - double stddev_y, - double inv_stddev_x, - double inv_stddev_y, - int ext_bin, - double bin_area, - double inv_sqrt, - int slice_capacity, - int deterministic_flag, - at::Tensor demMap, - at::Tensor rsrcAreas - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(indices); - CHECK_CONTIGUOUS(indices); - - CHECK_FLAT(ctrlSets); - CHECK_CONTIGUOUS(ctrlSets); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - int num_nodes = pos.numel() / 2; - double half_slice = slice_capacity / 2.0; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapFFCuda", [&] { - fillDemandMapFFCuda( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - xl, yl, xh, yh, indices.numel(), - stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt, - deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); - }); - - at::Tensor areaMap = demMap.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaFFCuda", [&] { - computeInstanceAreaFFCuda( - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), - num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - stddev_x, stddev_y, ext_bin, bin_area, half_slice, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); - }); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasFFCuda", [&] { - collectInstanceAreasFFCuda( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_y, num_bins_ck, num_bins_ce, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), - indices.numel(), inv_stddev_x, inv_stddev_y, - DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); - }); - - return areaMap; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu deleted file mode 100644 index cdaf70a1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu +++ /dev/null @@ -1,330 +0,0 @@ -/** - * @file lut_compatibility_cuda_kernel.cu - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for LUT based on elfPlace. - */ - -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" -// local dependency -#include "clustering_compatibility/src/functions.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define gaussian_auc_function -template -inline __device__ DEFINE_GAUSSIAN_AUC_FUNCTION(T); -/// define smooth_ceil_function -template -inline __device__ DEFINE_SMOOTH_CEIL_FUNCTION(T); -/// define flop_aggregate_demand_function -template -inline __device__ DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(T); - -template -__global__ void fillDemandMapFF(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const int num_nodes, - const T stddev_x, const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt, - AtomicOp atomic_add_op, - typename AtomicOp::type* demMap) -{ - const unsigned int i = threadIdx.x + blockDim.x * blockIdx.x; - int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - int o_p = num_bins_ck * num_bins_ce; - if (i < num_nodes) - { - const unsigned int nIdx = indices[i]; - T node_x = pos_x[nIdx] + 0.5 * node_size_x[nIdx]; - T node_y = pos_y[nIdx] + 0.5 * node_size_y[nIdx]; - //Ctrl set values - int cksr = ctrlSets[i*3 + 1]; - int ce = ctrlSets[i*3 + 2]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - // compute the bin box that this net will affect - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); - T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); - - T sf = 1.0 / (gaussianX * gaussianY); - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - T dem_xmbin_index_xl = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - unsigned int idx = x * n_o_p + y * o_p + cksr * num_bins_ce + ce; - T dem_ymbin_index_yl = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); - //T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; - T dem = sf * dem_xmbin_index_xl * dem_ymbin_index_yl; - //demMap[idx] += dem; - atomic_add_op(&demMap[idx], dem); - } - } - } -} - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -__global__ void computeInstanceAreaFF(const T *demMap, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T stddev_x, const T stddev_y, const int ext_bin, - const T bin_area, const T half_slice, T *areaMap) -{ - const unsigned int i = threadIdx.x + blockDim.x * blockIdx.x; - int total_bins = num_bins_x*num_bins_y; - int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - int o_p = num_bins_ck * num_bins_ce; - if (i < total_bins) - { - int binX = int(i/num_bins_y); - int binY = int(i%num_bins_y); - - // compute the bin box - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - int index = binX * n_o_p + binY * o_p; - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - if (x != binX && y != binY) - { - unsigned int idx = x * n_o_p + y * o_p; - flop_aggregate_demand_function(demMap, idx, areaMap, index, num_bins_ck, num_bins_ce); - } - } - } - - //Flop compute areas - for (int ck = 0; ck < num_bins_ck; ++ck) - { - T totalQ = 0.0; - for (int ce = 0; ce < num_bins_ce; ++ce) - { - unsigned int updIdx = index + ck*num_bins_ce + ce; - if (areaMap[updIdx] > 0.0) - { - totalQ += smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); - } - } - - T sf = half_slice * smooth_ceil_function(totalQ*0.5, 0.5) / totalQ; - - for (int cE = 0; cE < num_bins_ce; ++cE) - { - unsigned int updIdx = index + ck*num_bins_ce + cE; - if (areaMap[updIdx] > 0.0) - { - T qrt = smooth_ceil_function(areaMap[updIdx]* 0.25, 0.25); - areaMap[updIdx] = sf * qrt / areaMap[updIdx]; - } - } - } - } -} - - -// Set a set of instance area in a area vector based on the given area map -template -__global__ void collectInstanceAreasFF(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T *areaMap, - const int num_nodes, const T inv_stddev_x, - const T inv_stddev_y, - T *instAreas) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - int n_o_p = num_bins_y * num_bins_ck * num_bins_ce; - int o_p = num_bins_ck * num_bins_ce; - if (i < num_nodes) - { - const unsigned int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - //Ctrl set values - int cksr = ctrlSets[i*3 + 1]; - int ce = ctrlSets[i*3 + 2]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - unsigned int index = binX * n_o_p + binY * o_p + cksr * num_bins_ce + ce; - instAreas[idx] = areaMap[index]; - } -} - -// fill the demand map net by net -template -int fillDemandMapFFCuda(const T *pos_x, const T *pos_y, - const int *indices, const int *ctrlSets, - const T *node_size_x, const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const T stddev_x, const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt, - const int deterministic_flag, - T *demMap) -{ - if (deterministic_flag == 1) - { - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_bins_x * num_bins_y * num_bins_ck * num_bins_ce; - unsigned long long int *buf_map = NULL; - allocateCUDA(buf_map, num_bins, unsigned long long int); - - AtomicAddCUDA atomic_add_op(scale_factor); - - int thread_count = 512; - int block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - buf_map, demMap, scale_factor, num_bins); - - block_count = ceilDiv(num_nodes, thread_count); - fillDemandMapFF<<>>( - pos_x, pos_y, indices, ctrlSets, node_size_x, - node_size_y, num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - num_nodes, stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, - ext_bin, inv_sqrt, - atomic_add_op, buf_map - ); - - block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - demMap, buf_map, T(1.0 / scale_factor), num_bins); - - destroyCUDA(buf_map); - - } else - { - AtomicAddCUDA atomic_add_op; - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - fillDemandMapFF<<>>( - pos_x, pos_y, indices, ctrlSets, node_size_x, - node_size_y, num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - num_nodes, stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, - ext_bin, inv_sqrt, - atomic_add_op, demMap - ); - } - return 0; -} - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaFFCuda(const T *demMap, - const int num_bins_x, const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T stddev_x, const T stddev_y, const int ext_bin, - const T bin_area, const T half_slice, T *areaMap) -{ - int thread_count = 512; - int block_count = ceilDiv(num_bins_x*num_bins_y, thread_count); - computeInstanceAreaFF<<>>( - demMap, - num_bins_x, num_bins_y, - num_bins_ck, num_bins_ce, - stddev_x, stddev_y, - ext_bin, bin_area, - half_slice, areaMap - ); - return 0; -} - - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasFFCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *ctrlSets, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_ck, const int num_bins_ce, - const T *areaMap, - const int num_nodes, - const T inv_stddev_x, const T inv_stddev_y, - T *instAreas) -{ - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - collectInstanceAreasFF<<>>( - pos_x, pos_y, - indices, ctrlSets, - node_size_x, node_size_y, - num_bins_y, num_bins_ck, - num_bins_ce, - areaMap, num_nodes, - inv_stddev_x, inv_stddev_y, - instAreas - ); - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int fillDemandMapFFCuda( \ - const T *pos_x, const T *pos_y, const int *indices, const int *ctrlSets, \ - const T *node_size_x, const T *node_size_y, const int num_bins_x, \ - const int num_bins_y, const int num_bins_ck, const int num_bins_ce, \ - const T xl, const T yl, const T xh, const T yh, const int num_nodes, \ - const T stddev_x, const T stddev_y, const T inv_stddev_x, const T inv_stddev_y, \ - const int ext_bin, const T inv_sqrt, const int deterministic_flag, T *demMap); \ - \ - template int computeInstanceAreaFFCuda( \ - const T *demMap, const int num_bins_x, const int num_bins_y, \ - const int num_bins_ck, const int num_bins_ce, const T stddev_x, \ - const T stddev_y, const int ext_bin, const T bin_area, const T half_slice, \ - T *areaMap); \ - \ - template int collectInstanceAreasFFCuda( \ - const T *pos_x, const T *pos_y, const int *indices, const int *ctrlSets, \ - const T *node_size_x, const T *node_size_y, const int num_bins_y, \ - const int num_bins_ck, const int num_bins_ce, const T *areaMap, \ - const int num_nodes, const T inv_stddev_x, const T inv_stddev_y, T *instAreas); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h deleted file mode 100644 index e6c8fede..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file functions.h - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - */ - -#ifndef DREAMPLACE_CLUSTERING_COMPATIBILITY_FUNCTIONS_H -#define DREAMPLACE_CLUSTERING_COMPATIBILITY_FUNCTIONS_H - -DREAMPLACE_BEGIN_NAMESPACE - -#define DEFINE_GAUSSIAN_AUC_FUNCTION(type) \ - T gaussian_auc_function(T mu, T sigma, T xLo, T xHi, T invSqrt) \ - { \ - T a = invSqrt / sigma; \ - T hiVal = DREAMPLACE_STD_NAMESPACE::erfc((mu - xHi) * a); \ - T loVal = DREAMPLACE_STD_NAMESPACE::erfc((mu - xLo) * a); \ - return T(0.5) * (hiVal - loVal); \ - } - -#define DEFINE_SMOOTH_CEIL_FUNCTION(type) \ - T smooth_ceil_function(T val, T threshold) \ - { \ - T r = DREAMPLACE_STD_NAMESPACE::fmod(val, T(1.0)); \ - T interm = DREAMPLACE_STD_NAMESPACE::min(r/threshold, T(1.0)); \ - return val - r + interm; \ - } - -//LUT -// Reuse version -//// Note the following mergings are feasible -//// LUT1 and {LUT1, LUT2, LUT3, LUT4} -//// LUT2 and {LUT2, LUT3} -// -//// Compute white space area in the window -//RealType totalDem = std::accumulate(dem.begin(), dem.end(), 0.0); -//RealType space = std::max(winArea - totalDem, 0.0); -//RealType totalArea = totalDem + space; -// -//area[0] = (dem[0] + dem[1] + dem[2] + dem[3] + 2.0 * (dem[4] + dem[5] + space)) / totalArea; -//area[1] = (dem[0] + dem[1] + dem[2] + 2.0 * (dem[3] + dem[4] + dem[5] + space)) / totalArea; -//area[2] = (dem[0] + dem[1] + 2.0 * (dem[2] + dem[3] + dem[4] + dem[5] + space)) / totalArea; -//area[3] = (dem[0] + 2.0 * (dem[1] + dem[2] + dem[3] + dem[4] + dem[5] + space)) / totalArea; -//area[4] = 2.0; -//area[5] = 2.0; - -//Hard-coded version for Ultrascale -#define DEFINE_LUT_COMPUTE_AREAS_FUNCTION(type) \ - void lut_compute_areas_function(const T winArea, T* area, const int idx, const int lBins) \ - { \ - T totalDem = T(0.0); \ - for (int x = 0; x < lBins; ++x) \ - { \ - totalDem += area[idx + x]; \ - } \ - T space = DREAMPLACE_STD_NAMESPACE::max(winArea - totalDem, T(0.0)); \ - T totalArea = totalDem + space; \ - space += space; \ - \ - T sum23 = area[idx+2] + area[idx+3]; \ - T sum45 = area[idx+4] + area[idx+5]; \ - T sum3 = area[idx+3]; \ - T sum0 = area[idx]; \ - area[idx] = (totalDem + sum45 + space) / totalArea; \ - area[idx+1] = (totalDem + sum3 + sum45 + space) / totalArea; \ - area[idx+2] = (totalDem + sum23 + sum45 + space) / totalArea; \ - area[idx+3] = (T(2.0) * totalDem - sum0 + space) / totalArea; \ - area[idx+4] = T(2.0); \ - area[idx+5] = area[idx+4]; \ - } - -//Generic version based on lut_fracture -#define DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(type) \ - void lut_compute_areas_function_generic(const int* lut_fracture, const T winArea, T* area, const int idx, const int lBins) \ - { \ - T totalDem = T(0.0); \ - T dem[16]; \ - for (int x = 0; x < lBins; ++x) \ - { \ - totalDem += area[idx + x]; \ - dem[x] = area[idx + x]; \ - } \ - T space = DREAMPLACE_STD_NAMESPACE::max(winArea - totalDem, T(0.0)); \ - T totalArea = totalDem + space; \ - space += space; \ - \ - for (int lt = 0; lt < lBins; ++lt) \ - {\ - char fracture(0);\ - T val = T(0.0);\ - for (int lf = 0; lf < lBins; ++lf)\ - {\ - int lfId = lt*lBins + lf;\ - if (lut_fracture[lfId] == 1)\ - {\ - val += dem[lf];\ - fracture = 1;\ - } else\ - {\ - val += T(2.0)*dem[lf]; \ - }\ - }\ - if (fracture == 1)\ - {\ - area[idx+lt] = (val + space)/totalArea;\ - } else\ - {\ - area[idx+lt] = T(2.0);\ - }\ - }\ - } - -//Flop - -#define DEFINE_FLOP_AGGREGATE_DEMAND_FUNCTION(type) \ - void flop_aggregate_demand_function(const T* demMap, unsigned int dIdx, T* resMap, int rIdx, const int ckSize, const int ceSize) \ - { \ - for (int ck = 0; ck < ckSize; ++ck) \ - { \ - for (int ce = 0; ce < ceSize; ++ce) \ - { \ - int cIdx = ck*ceSize + ce; \ - resMap[rIdx+cIdx] += demMap[dIdx+cIdx]; \ - } \ - } \ - } - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp deleted file mode 100644 index ca5fdb35..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/** - * @file lut_compatibility.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for LUT based on elfPlace. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "clustering_compatibility/src/functions.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define gaussian_auc_function -template -DEFINE_GAUSSIAN_AUC_FUNCTION(T); -/// define lut_compute_areas_function -template -DEFINE_LUT_COMPUTE_AREAS_FUNCTION(T); -template -DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(T); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - - -// fill the demand map node by node -template -int fillDemandMapLUT(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_bins_l, - int num_threads, int num_nodes, - T stddev_x, T stddev_y, - T inv_stddev_x, T inv_stddev_y, - int ext_bin, T inv_sqrt2, - T *demandX, T *demandY, T *demMap) -{ - - //int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - const int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - int lutType = type[idx]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - // compute the bin box that this net will affect - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt2); - T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt2); - - T sf = 1.0 / (gaussianX * gaussianY); - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - demandX[x - bin_index_xl] = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt2); - } - - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - demandY[y - bin_index_yl] = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt2); - } - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - //#pragma omp atomic update - int index = x * (num_bins_y * num_bins_l) + y * num_bins_l + lutType; - T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; - demMap[index] += dem; - } - } - } - return 0; -} - - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaLUT(const T *demMap, const int* lut_fracture, - const int num_bins_x, const int num_bins_y, - const int num_bins_l, - int num_threads, T stddev_x, T stddev_y, - int ext_bin, T bin_area, T *areaMap) -{ - - int total_bins = num_bins_x*num_bins_y; - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(total_bins / num_threads / 16), 1); - //#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < total_bins; ++i) - { - int binX = int(i/num_bins_y); - int binY = int(i%num_bins_y); - - // compute the bin box - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - int idx = binX * (num_bins_y * num_bins_l) + binY * num_bins_l; - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - int index = x * (num_bins_y * num_bins_l) + y * num_bins_l; - if (x != binX && y != binY) - { - for (int l = 0; l < num_bins_l; ++l) - { - areaMap[idx + l] += demMap[index + l]; - //tempMap[idx + l] += demMap[index + l]; - } - } - } - } - - // Compute instance areas based on the window demand distribution - T winArea = (bin_index_xh - bin_index_xl) * (bin_index_yh - bin_index_yl) * bin_area; - - //lut_compute_areas_function(winArea, tempMap, idx, num_bins_l); - lut_compute_areas_function_generic(lut_fracture, winArea, areaMap, idx, num_bins_l); - } - return 0; -} - - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasLUT(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_l, - const T *areaMap, - int num_threads, int num_nodes, - T inv_stddev_x, T inv_stddev_y, - T *instAreas) -{ - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - const int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - int index = binX * (num_bins_y * num_bins_l) + binY * num_bins_l + type[idx]; - instAreas[idx] = areaMap[index]; - } - return 0; -} - -at::Tensor lut_compatibility( - at::Tensor pos, - at::Tensor indices, - at::Tensor type, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor lut_fracture, - int num_bins_x, - int num_bins_y, - int num_bins_l, - int num_threads, - double stddev_x, - double stddev_y, - double inv_stddev_x, - double inv_stddev_y, - int ext_bin, - double bin_area, - double inv_sqrt2, - at::Tensor demandX, - at::Tensor demandY, - at::Tensor demMap, - at::Tensor rsrcAreas - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(indices); - CHECK_CONTIGUOUS(indices); - - CHECK_FLAT(type); - CHECK_CONTIGUOUS(type); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - int num_nodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapLUT", [&] { - fillDemandMapLUT( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(type, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_x, num_bins_y, num_bins_l, - num_threads, indices.numel(), - stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt2, - DREAMPLACE_TENSOR_DATA_PTR(demandX, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(demandY, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); - }); - - at::Tensor areaMap = demMap.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaLUT", [&] { - computeInstanceAreaLUT( - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(lut_fracture, int), - num_bins_x, num_bins_y, num_bins_l, num_threads, - stddev_x, stddev_y, ext_bin, bin_area, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); - }); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasLUT", [&] { - collectInstanceAreasLUT( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(type, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_y, num_bins_l, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), - num_threads, indices.numel(), - inv_stddev_x, inv_stddev_y, - DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); - }); - - return areaMap; -} - -at::Tensor flop_compatibility( - at::Tensor pos, at::Tensor indices, at::Tensor ctrlSets, - at::Tensor node_size_x, at::Tensor node_size_y, int num_bins_x, - int num_bins_y, int num_bins_ck, int num_bins_ce, - int num_threads, double stddev_x, double stddev_y, - double inv_stddev_x, double inv_stddev_y, int ext_bin, - double bin_area, double inv_sqrt, int slice_capacity, - at::Tensor demandX, at::Tensor demandY, at::Tensor demMap, - at::Tensor rsrcAreas); - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("lut_compatibility", &DREAMPLACE_NAMESPACE::lut_compatibility, "compute LUT compatibility instance areas"); - m.def("flop_compatibility", &DREAMPLACE_NAMESPACE::flop_compatibility, "compute Flop compatibility instance areas"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp deleted file mode 100644 index 1c9405ea..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @file lut_compatibility_cuda.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for LUT based on elfPlace. - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map node by node -template -int fillDemandMapLUTCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_l, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const T stddev_x, const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt2, - const int deterministic_flag, - T *demMap); - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaLUTCuda(const T *demMap, - const int *lut_fracture, - const int num_bins_x, - const int num_bins_y, - const int num_bins_l, - const T stddev_x, const T stddev_y, - const int ext_bin, const T bin_area, - T *areaMap); - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasLUTCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_l, - const T *areaMap, - const int num_nodes, - const T inv_stddev_x, - const T inv_stddev_y, - T *instAreas); - -at::Tensor lut_compatibility( - at::Tensor pos, - at::Tensor indices, - at::Tensor type, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor lut_fracture, - int num_bins_x, - int num_bins_y, - int num_bins_l, - double xl, - double yl, - double xh, - double yh, - double stddev_x, - double stddev_y, - double inv_stddev_x, - double inv_stddev_y, - int ext_bin, - double bin_area, - double inv_sqrt2, - int deterministic_flag, - at::Tensor demMap, - at::Tensor rsrcAreas - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(indices); - CHECK_CONTIGUOUS(indices); - - CHECK_FLAT(type); - CHECK_CONTIGUOUS(type); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - int num_nodes = pos.numel() / 2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "fillDemandMapLUTCuda", [&] { - fillDemandMapLUTCuda( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(type, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_x, num_bins_y, num_bins_l, - xl, yl, xh, yh, indices.numel(), - stddev_x, stddev_y, - inv_stddev_x, inv_stddev_y, ext_bin, inv_sqrt2, - deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t)); - }); - - at::Tensor areaMap = demMap.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeInstanceAreaLUTCuda", [&] { - computeInstanceAreaLUTCuda( - DREAMPLACE_TENSOR_DATA_PTR(demMap, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(lut_fracture, int), - num_bins_x, num_bins_y, num_bins_l, - stddev_x, stddev_y, ext_bin, bin_area, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t)); - }); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "collectInstanceAreasLUTCuda", [&] { - collectInstanceAreasLUTCuda( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(indices, int), - DREAMPLACE_TENSOR_DATA_PTR(type, int), - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - num_bins_y, num_bins_l, - DREAMPLACE_TENSOR_DATA_PTR(areaMap, scalar_t), - indices.numel(), inv_stddev_x, inv_stddev_y, - DREAMPLACE_TENSOR_DATA_PTR(rsrcAreas, scalar_t)); - }); - - return areaMap; -} - -at::Tensor flop_compatibility( - at::Tensor pos, at::Tensor indices, at::Tensor ctrlSets, - at::Tensor node_size_x, at::Tensor node_size_y, - int num_bins_x, int num_bins_y, int num_bins_ck, - int num_bins_ce, double xl, double yl, double xh, - double yh, double stddev_x, double stddev_y, - double inv_stddev_x, double inv_stddev_y, int ext_bin, - double bin_area, double inv_sqrt, int slice_capacity, - int deterministic_flag, at::Tensor demMap, at::Tensor rsrcAreas); - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("lut_compatibility", &DREAMPLACE_NAMESPACE::lut_compatibility, "compute LUT compatibility instance areas (CUDA)"); - m.def("flop_compatibility", &DREAMPLACE_NAMESPACE::flop_compatibility, "compute Flop compatibility instance areas (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu deleted file mode 100644 index f35274bd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu +++ /dev/null @@ -1,309 +0,0 @@ -/** - * @file lut_compatibility_cuda_kernel.cu - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute the Clustering compatibility map for LUT based on elfPlace. - */ - -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" -// local dependency -#include "clustering_compatibility/src/functions.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define gaussian_auc_function -template -inline __device__ DEFINE_GAUSSIAN_AUC_FUNCTION(T); -/// define lut_compute_areas_function -template -inline __device__ DEFINE_LUT_COMPUTE_AREAS_FUNCTION(T); -template -inline __device__ DEFINE_LUT_COMPUTE_AREAS_FUNCTION_GENERIC(T); - -template -__global__ void fillDemandMapLUT(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_bins_l, - const int num_nodes, const T stddev_x, - const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt, - AtomicOp atomic_add_op, - typename AtomicOp::type* demMap) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nodes) - { - const int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - int lutType = type[idx]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - // compute the bin box that this net will affect - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - T gaussianX = gaussian_auc_function(node_x, stddev_x, bin_index_xl*stddev_x, bin_index_xh* stddev_x, inv_sqrt); - T gaussianY = gaussian_auc_function(node_y, stddev_y, bin_index_yl*stddev_y, bin_index_yh* stddev_y, inv_sqrt); - - T sf = 1.0 / (gaussianX * gaussianY); - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - T dem_xmbin_index_xl = gaussian_auc_function(node_x, stddev_x, x*stddev_x, (x+1)*stddev_x, inv_sqrt); - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - //#pragma omp atomic update - int index = x * (num_bins_y * num_bins_l) + y * num_bins_l + lutType; - T dem_ymbin_index_yl = gaussian_auc_function(node_y, stddev_y, y*stddev_y, (y+1)*stddev_y, inv_sqrt); - //T dem = sf * demandX[x - bin_index_xl] * demandY[y - bin_index_yl]; - T dem = sf * dem_xmbin_index_xl * dem_ymbin_index_yl; - //demMap[index] += dem; - atomic_add_op(&demMap[index], dem); - } - } - } -} - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -__global__ void computeInstanceAreaLUT(const T *demMap, - const int* lut_fracture, - const int num_bins_x, - const int num_bins_y, - const int num_bins_l, - const T stddev_x, const T stddev_y, - const int ext_bin, const T bin_area, - T *areaMap) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - int total_bins = num_bins_x*num_bins_y; - if (i < total_bins) - { - int binX = int(i/num_bins_y); - int binY = int(i%num_bins_y); - - // compute the bin box - int bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(binX - ext_bin, 0); - int bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(binX + ext_bin + 1, num_bins_x); - - int bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(binY - ext_bin, 0); - int bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(binY + ext_bin + 1, num_bins_y); - - int idx = binX * (num_bins_y * num_bins_l) + binY * num_bins_l; - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - int index = x * (num_bins_y * num_bins_l) + y * num_bins_l; - if (x != binX && y != binY) - { - for (int l = 0; l < num_bins_l; ++l) - { - areaMap[idx + l] += demMap[index + l]; - } - } - } - } - - // Compute instance areas based on the window demand distribution - T winArea = (bin_index_xh - bin_index_xl) * (bin_index_yh - bin_index_yl) * bin_area; - //lut_compute_areas_function(winArea, areaMap, idx, num_bins_l); - lut_compute_areas_function_generic(lut_fracture, winArea, areaMap, idx, num_bins_l); - } -} - - -// Set a set of instance area in a area vector based on the given area map -template -__global__ void collectInstanceAreasLUT(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_l, - const T *areaMap, - const int num_nodes, - const T inv_stddev_x, - const T inv_stddev_y, - T *instAreas) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nodes) - { - const int idx = indices[i]; - T node_x = pos_x[idx] + 0.5 * node_size_x[idx]; - T node_y = pos_y[idx] + 0.5 * node_size_y[idx]; - - int binX = int(node_x * inv_stddev_x); - int binY = int(node_y * inv_stddev_y); - - int index = binX * (num_bins_y * num_bins_l) + binY * num_bins_l + type[idx]; - instAreas[idx] = areaMap[index]; - } -} - -// fill the demand map net by net -template -int fillDemandMapLUTCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_x, const int num_bins_y, - const int num_bins_l, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const T stddev_x, const T stddev_y, - const T inv_stddev_x, const T inv_stddev_y, - const int ext_bin, const T inv_sqrt, - const int deterministic_flag, - T *demMap) -{ - if (deterministic_flag == 1) - { - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_bins_x * num_bins_y * num_bins_l; - unsigned long long int *buf_map = NULL; - allocateCUDA(buf_map, num_bins, unsigned long long int); - - AtomicAddCUDA atomic_add_op(scale_factor); - - int thread_count = 512; - int block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - buf_map, demMap, scale_factor, num_bins); - - block_count = ceilDiv(num_nodes, thread_count); - fillDemandMapLUT<<>>( - pos_x, pos_y, indices, type, node_size_x, - node_size_y, num_bins_x, num_bins_y, num_bins_l, - num_nodes, stddev_x, stddev_y, inv_stddev_x, - inv_stddev_y, ext_bin, inv_sqrt, - atomic_add_op, buf_map - ); - - block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - demMap, buf_map, T(1.0 / scale_factor), num_bins); - - destroyCUDA(buf_map); - - } else - { - AtomicAddCUDA atomic_add_op; - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - fillDemandMapLUT<<>>( - pos_x, pos_y, indices, type, node_size_x, - node_size_y, num_bins_x, num_bins_y, num_bins_l, - num_nodes, stddev_x, stddev_y, inv_stddev_x, - inv_stddev_y, ext_bin, inv_sqrt, - atomic_add_op, demMap - ); - } - return 0; -} - -// Given a Gaussian demand map, compute demand of each instance type based on local window demand distribution -template -int computeInstanceAreaLUTCuda(const T *demMap, - const int *lut_fracture, - const int num_bins_x, - const int num_bins_y, - const int num_bins_l, - const T stddev_x, const T stddev_y, - const int ext_bin, const T bin_area, - T *areaMap) -{ - int thread_count = 512; - int block_count = ceilDiv(num_bins_x*num_bins_y, thread_count); - computeInstanceAreaLUT<<>>( - demMap, lut_fracture, - num_bins_x, num_bins_y, - num_bins_l, - stddev_x, stddev_y, - ext_bin, bin_area, - areaMap - ); - return 0; -} - - -// Set a set of instance area in a area vector based on the given area map -template -int collectInstanceAreasLUTCuda(const T *pos_x, - const T *pos_y, - const int *indices, - const int *type, - const T *node_size_x, - const T *node_size_y, - const int num_bins_y, - const int num_bins_l, - const T *areaMap, - const int num_nodes, - const T inv_stddev_x, - const T inv_stddev_y, - T *instAreas) -{ - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - collectInstanceAreasLUT<<>>( - pos_x, pos_y, - indices, type, - node_size_x, node_size_y, - num_bins_y, num_bins_l, - areaMap, num_nodes, - inv_stddev_x, inv_stddev_y, - instAreas - ); - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int fillDemandMapLUTCuda( \ - const T *pos_x, const T *pos_y, const int *indices, const int *type, \ - const T *node_size_x, const T *node_size_y, const int num_bins_x, \ - const int num_bins_y, const int num_bins_l, const T xl, const T yl, \ - const T xh, const T yh, const int num_nodes, \ - const T stddev_x, const T stddev_y, \ - const T inv_stddev_x, const T inv_stddev_y, const int ext_bin, \ - const T inv_sqrt, const int deterministic_flag, \ - T *demMap); \ - \ - template int computeInstanceAreaLUTCuda( \ - const T *demMap, const int* lut_fracture, const int num_bins_x, const int num_bins_y, \ - const int num_bins_l, const T stddev_x, const T stddev_y, \ - const int ext_bin, const T bin_area, T *areaMap); \ - \ - template int collectInstanceAreasLUTCuda( \ - const T *pos_x, const T *pos_y, const int *indices, const int *type, \ - const T *node_size_x, const T *node_size_y, const int num_bins_y, \ - const int num_bins_l, const T *areaMap, const int num_nodes, \ - const T inv_stddev_x, const T inv_stddev_y, T *instAreas); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt deleted file mode 100644 index 275789bb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -set(OP_NAME dct) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(dct_cpp - src/dct.cpp - src/dst.cpp - src/dxt.cpp - src/dct_2N.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(dct_lee_cpp - src/dct_lee.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(dct2_fft2_cpp - src/dct2_fft2.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - dct_cpp dct_lee_cpp dct2_fft2_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) - -# shared by dct_cuda and dct_lee_cuda -add_torch_extension(dct_dst_kernel_cuda - src/dct_cuda_kernel.cu - src/dst_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(dct_cuda - src/dct_cuda.cpp - src/dst_cuda.cpp - src/dxt_cuda.cpp - src/dct_2N_cuda.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES dct_dst_kernel_cuda ${LINK_LIBS}) - -add_pytorch_extension(dct_lee_cuda - src/dct_lee_cuda.cpp - src/dct_lee_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES dct_dst_kernel_cuda ${LINK_LIBS}) - -add_pytorch_extension(dct2_fft2_cuda - src/dct2_fft2_cuda.cpp - src/dct2_fft2_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - dct_cuda dct_lee_cuda dct2_fft2_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py deleted file mode 100644 index 58eefdd2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Mar 2019 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py deleted file mode 100644 index 3fc374e5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py +++ /dev/null @@ -1,399 +0,0 @@ -## -# @file dct.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# - -import os -import sys -import numpy as np -import torch -from torch.autograd import Function -from torch import nn - -import dreamplacefpga.ops.dct.dct_cpp as dct_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.dct.dct_cuda as dct_cuda - -import dreamplacefpga.ops.dct.discrete_spectral_transform as discrete_spectral_transform - -def dct(x, expk, algorithm): - """compute discrete cosine transformation, DCT II, using N-FFT or 2N-FFT - yk = \sum_{n=0}^{N-1} x_n cos(pi/N*n*(k+1/2)) - - @param x sequence - @param expk coefficients for post-processing - @param algorithm algorithm type N | 2N - """ - if x.is_cuda: - if algorithm == 'N': - output = dct_cuda.dct(x.view([-1, x.size(-1)]), expk) - elif algorithm == '2N': - output = dct_cuda.dct_2N(x.view([-1, x.size(-1)]), expk) - else: - if algorithm == 'N': - output = dct_cpp.dct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - elif algorithm == '2N': - output = dct_cpp.dct_2N(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - return output.view(x.size()) - -class DCTFunction(Function): - @staticmethod - def forward(ctx, x, expk, algorithm): - return dct(x, expk, algorithm) - -class DCT(nn.Module): - def __init__(self, expk=None, algorithm='N'): - super(DCT, self).__init__() - self.expk = expk - self.algorithm = algorithm - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return DCTFunction.apply(x, self.expk, self.algorithm) - -def idct(x, expk, algorithm): - """Compute inverse discrete cosine transformation, which is also the DCT III, using N-FFT or 2N-FFT - yk = Re { 1/2*x0 + \sum_{n=1}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - The actual yk will be scaled by 2 to match other python implementation - - @param x sequence - @param expk coefficients for pre-processing - @param algorithm algorithm type N | 2N - """ - if x.is_cuda: - if algorithm == 'N': - output = dct_cuda.idct(x.view([-1, x.size(-1)]), expk) - elif algorithm == '2N': - output = dct_cuda.idct_2N(x.view([-1, x.size(-1)]), expk) - else: - if algorithm == 'N': - output = dct_cpp.idct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - elif algorithm == '2N': - output = dct_cpp.idct_2N(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - return output.view(x.size()) - -class IDCTFunction(Function): - @staticmethod - def forward(ctx, x, expk, algorithm): - return idct(x, expk, algorithm) - -class IDCT(nn.Module): - def __init__(self, expk=None, algorithm='N'): - super(IDCT, self).__init__() - self.expk = expk - self.algorithm = algorithm - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDCTFunction.apply(x, self.expk, self.algorithm) - -def dct2(x, expk0, expk1, algorithm='N'): - """compute 2D discrete cosine transformation, using N-FFT or 2N-FFT - """ - if x.is_cuda: - if algorithm == 'N': - output = dct_cuda.dct2(x, expk0, expk1) - #output = dct_cuda.dct(dct_cuda.dct(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() - elif algorithm == '2N': - output = dct_cuda.dct2_2N(x, expk0, expk1) - #output = dct_cuda.dct_2N(dct_cuda.dct_2N(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() - else: - if algorithm == 'N': - output = dct_cpp.dct2(x, expk0, expk1, torch.get_num_threads()) - #output = dct_cpp.dct(dct_cpp.dct(x, expk1, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous(), expk0, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous() - elif algorithm == '2N': - output = dct_cpp.dct2_2N(x, expk0, expk1, torch.get_num_threads()) - return output - -class DCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, algorithm): - return dct2(x, expk0, expk1, algorithm) - -class DCT2(nn.Module): - def __init__(self, expk0=None, expk1=None, algorithm='N'): - super(DCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.algorithm = algorithm - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return DCT2Function.apply(x, self.expk0, self.expk1, self.algorithm) - -def idct2(x, expk0, expk1, algorithm='N'): - """compute 2D inverse discrete cosine transformation, using N-FFT or 2N-FFT - """ - if x.is_cuda: - if algorithm == 'N': - output = dct_cuda.idct2(x, expk0, expk1) - #output = dct_cuda.idct(dct_cuda.idct(x, expk1).transpose_(dim0=-2, dim1=-1).contiguous(), expk0).transpose_(dim0=-2, dim1=-1).contiguous() - elif algorithm == '2N': - output = dct_cuda.idct2_2N(x, expk0, expk1) - else: - if algorithm == 'N': - output = dct_cpp.idct2(x, expk0, expk1, torch.get_num_threads()) - #output = dct_cpp.idct(dct_cpp.idct(x, expk1, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous(), expk0, torch.get_num_threads()).transpose_(dim0=-2, dim1=-1).contiguous() - elif algorithm == '2N': - output = dct_cpp.idct2_2N(x, expk0, expk1, torch.get_num_threads()) - return output - -class IDCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, algorithm): - return idct2(x, expk0, expk1, algorithm) - -class IDCT2(nn.Module): - def __init__(self, expk0=None, expk1=None, algorithm='N'): - super(IDCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.algorithm = algorithm - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDCT2Function.apply(x, self.expk0, self.expk1, self.algorithm) - -def dst(x, expk): - """compute discrete sine transformation - yk = \sum_{n=0}^{N-1} x_n cos(pi/N*(n+1/2)*(k+1)) - """ - if x.is_cuda: - output = dct_cuda.dst(x.view([-1, x.size(-1)]), expk) - else: - output = dct_cpp.dst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - return output.view(x.size()) - -class DSTFunction(Function): - @staticmethod - def forward(ctx, x, expk): - return dst(x, expk) - -class DST(nn.Module): - def __init__(self, expk=None): - super(DST, self).__init__() - self.expk = expk - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return DSTFunction.apply(x, self.expk) - -def idst(x, expk): - """Compute inverse discrete sine transformation, which is also the DST III - yk = Im { (-1)^k*x_{N-1}/2 + \sum_{n=0}^{N-2} xn exp(j*pi/(2N)*(n+1)*(2k+1)) } - The actual yk will be scaled by 2 to match other python implementation - """ - if x.is_cuda: - output = dct_cuda.idst(x.view([-1, x.size(-1)]), expk) - else: - output = dct_cpp.idst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - return output.view(x.size()) - -class IDSTFunction(Function): - @staticmethod - def forward(ctx, x, expk): - return idst(x, expk) - -class IDST(nn.Module): - def __init__(self, expk=None): - super(IDST, self).__init__() - self.expk = expk - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDSTFunction.apply(x, self.expk) - -def idxct(x, expk): - """compute inverse discrete cosine transformation - This is different from ordinary formulation for IDCT III - yk = Re { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - """ - if x.is_cuda: - output = dct_cuda.idxct(x.view([-1, x.size(-1)]), expk) - else: - output = dct_cpp.idxct(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - #output = IDCTFunction.forward(ctx, x, expk) - #output.add_(x[..., 0].unsqueeze(-1)).mul_(0.5) - ##output.mul_(0.5).add_(x[..., 0].unsqueeze(-1).mul(0.5)) - return output.view(x.size()) - -class IDXCTFunction(Function): - @staticmethod - def forward(ctx, x, expk): - return idxct(x, expk) - -class IDXCT(nn.Module): - def __init__(self, expk=None): - super(IDXCT, self).__init__() - self.expk = expk - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDXCTFunction.apply(x, self.expk) - -def idxst(x, expk): - """compute inverse discrete sine transformation - This is different from ordinary formulation for IDCT III - yk = Im { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - """ - if x.is_cuda: - output = dct_cuda.idxst(x.view([-1, x.size(-1)]), expk) - else: - output = dct_cpp.idxst(x.view([-1, x.size(-1)]), expk, torch.get_num_threads()) - return output.view(x.size()) - -class IDXSTFunction(Function): - @staticmethod - def forward(ctx, x, expk): - return idxst(x, expk) - -class IDXST(nn.Module): - def __init__(self, expk=None): - super(IDXST, self).__init__() - self.expk = expk - def forward(self, x): - if self.expk is None or self.expk.size(-2) != x.size(-1): - self.expk = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDXSTFunction.apply(x, self.expk) - -def idcct2(x, expk0, expk1): - """compute inverse discrete cosine-sine transformation - This is equivalent to idcct(idcct(x)^T)^T - """ - if x.is_cuda: - output = dct_cuda.idcct2(x.view([-1, x.size(-1)]), expk0, expk1) - else: - output = dct_cpp.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) - return output.view(x.size()) - -class IDCCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1): - return idcct2(x, expk0, expk1) - -class IDCCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDCCT2Function.apply(x, self.expk0, self.expk1) - -def idcst2(x, expk0, expk1): - """compute inverse discrete cosine-sine transformation - This is equivalent to idxct(idxst(x)^T)^T - """ - if x.is_cuda: - output = dct_cuda.idcst2(x.view([-1, x.size(-1)]), expk0, expk1) - else: - output = dct_cpp.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) - return output.view(x.size()) - -class IDCST2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1): - return idcst2(x, expk0, expk1) - -class IDCST2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCST2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDCST2Function.apply(x, self.expk0, self.expk1) - -def idsct2(x, expk0, expk1): - """compute inverse discrete cosine-sine transformation - This is equivalent to idxst(idxct(x)^T)^T - """ - if x.is_cuda: - output = dct_cuda.idsct2(x.view([-1, x.size(-1)]), expk0, expk1) - else: - output = dct_cpp.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) - return output.view(x.size()) - -class IDSCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1): - return idsct2(x, expk0, expk1) - -class IDSCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDSCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDSCT2Function.apply(x, self.expk0, self.expk1) - -def idct_idxst(x, expk0, expk1): - """compute inverse discrete cosine-sine transformation - This is equivalent to idct(idxst(x)^T)^T - """ - if x.is_cuda: - output = dct_cuda.idct_idxst(x.view([-1, x.size(-1)]), expk0, expk1) - else: - output = dct_cpp.idct_idxst(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) - return output.view(x.size()) - -class IDCT_IDXSTFunction(Function): - @staticmethod - def forward(ctx, x, expk0, expk1): - return idct_idxst(x, expk0, expk1) - -class IDCT_IDXST(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCT_IDXST, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDCT_IDXSTFunction.apply(x, self.expk0, self.expk1) - -def idxst_idct(x, expk0, expk1): - """compute inverse discrete cosine-sine transformation - This is equivalent to idxst(idct(x)^T)^T - """ - if x.is_cuda: - output = dct_cuda.idxst_idct(x.view([-1, x.size(-1)]), expk0, expk1) - else: - output = dct_cpp.idxst_idct(x.view([-1, x.size(-1)]), expk0, expk1, torch.get_num_threads()) - return output.view(x.size()) - -class IDXST_IDCTFunction(Function): - @staticmethod - def forward(ctx, x, expk0, expk1): - return idxst_idct(x, expk0, expk1) - - -class IDXST_IDCT(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDXST_IDCT, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - def forward(self, x): - if self.expk0 is None or self.expk0.size(-2) != x.size(-2): - self.expk0 = discrete_spectral_transform.get_expk(x.size(-2), dtype=x.dtype, device=x.device) - if self.expk1 is None or self.expk1.size(-2) != x.size(-1): - self.expk1 = discrete_spectral_transform.get_expk(x.size(-1), dtype=x.dtype, device=x.device) - return IDXST_IDCTFunction.apply(x, self.expk0, self.expk1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py deleted file mode 100644 index 9b2dfc4b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py +++ /dev/null @@ -1,150 +0,0 @@ -## -# @file dct2_fft2.py -# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) -# @date Jun 2018 -# @brief Implement 2d dct, 2d idct, idxst(idct(x)), idct(idxst(x)) based on 2d fft -# - -import numpy as np -import torch -from torch.autograd import Function -from torch import nn - -from dreamplacefpga.ops.dct.discrete_spectral_transform import get_exact_expk as precompute_expk - -import dreamplacefpga.ops.dct.dct2_fft2_cpp as dct2_fft2_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.dct.dct2_fft2_cuda as dct2_fft2_cuda - - -class DCT2Function(Function): - @staticmethod - def forward(ctx, x, expkM, expkN, out, buf): - if x.is_cuda: - dct2_fft2_cuda.dct2_fft2(x, expkM, expkN, out, buf) - else: - dct2_fft2_cpp.dct2_fft2(x, expkM, expkN, out, buf, torch.get_num_threads()) - return out - - -class DCT2(nn.Module): - def __init__(self, expkM=None, expkN=None): - super(DCT2, self).__init__() - - self.expkM = expkM - self.expkN = expkN - self.out = None - self.buf = None - - def forward(self, x): - M = x.size(-2) - N = x.size(-1) - if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: - self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) - if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: - self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) - if self.out is None: - self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) - self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) - - return DCT2Function.apply(x, self.expkM, self.expkN, self.out, self.buf) - - -class IDCT2Function(Function): - @staticmethod - def forward(ctx, x, expkM, expkN, out, buf): - if x.is_cuda: - dct2_fft2_cuda.idct2_fft2(x, expkM, expkN, out, buf) - else: - dct2_fft2_cpp.idct2_fft2(x, expkM, expkN, out, buf, torch.get_num_threads()) - return out - - -class IDCT2(nn.Module): - def __init__(self, expkM=None, expkN=None): - super(IDCT2, self).__init__() - - self.expkM = expkM - self.expkN = expkN - self.out = None - self.buf = None - - def forward(self, x): - M = x.size(-2) - N = x.size(-1) - if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: - self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) - if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: - self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) - if self.out is None: - self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) - self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) - - return IDCT2Function.apply(x, self.expkM, self.expkN, self.out, self.buf) - - -class IDCT_IDXSTFunction(Function): - @staticmethod - def forward(ctx, x, expkM, expkN, out, buf): - if x.is_cuda: - dct2_fft2_cuda.idct_idxst(x, expkM, expkN, out, buf) - else: - dct2_fft2_cpp.idct_idxst(x, expkM, expkN, out, buf, torch.get_num_threads()) - return out - - -class IDCT_IDXST(nn.Module): - def __init__(self, expkM=None, expkN=None): - super(IDCT_IDXST, self).__init__() - - self.expkM = expkM - self.expkN = expkN - self.out = None - self.buf = None - - def forward(self, x): - M = x.size(-2) - N = x.size(-1) - if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: - self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) - if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: - self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) - if self.out is None: - self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) - self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) - - return IDCT_IDXSTFunction.apply(x, self.expkM, self.expkN, self.out, self.buf) - - -class IDXST_IDCTFunction(Function): - @staticmethod - def forward(ctx, x, expkM, expkN, out, buf): - if x.is_cuda: - dct2_fft2_cuda.idxst_idct(x, expkM, expkN, out, buf) - else: - dct2_fft2_cpp.idxst_idct(x, expkM, expkN, out, buf, torch.get_num_threads()) - return out - - -class IDXST_IDCT(nn.Module): - def __init__(self, expkM=None, expkN=None): - super(IDXST_IDCT, self).__init__() - - self.expkM = expkM - self.expkN = expkN - self.out = None - self.buf = None - - def forward(self, x): - M = x.size(-2) - N = x.size(-1) - if self.expkM is None or self.expkM.size(-2) != M or self.expkM.dtype != x.dtype: - self.expkM = precompute_expk(M, dtype=x.dtype, device=x.device) - if self.expkN is None or self.expkN.size(-2) != N or self.expkN.dtype != x.dtype: - self.expkN = precompute_expk(N, dtype=x.dtype, device=x.device) - if self.out is None: - self.out = torch.empty(M, N, dtype=x.dtype, device=x.device) - self.buf = torch.empty(M, N // 2 + 1, 2, dtype=x.dtype, device=x.device) - - return IDXST_IDCTFunction.apply(x, self.expkM, self.expkN, self.out, self.buf) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py deleted file mode 100644 index bec5ce63..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py +++ /dev/null @@ -1,426 +0,0 @@ -## -# @file dct_lee.py -# @author Yibo Lin (DREAMPlace) -# @date Oct 2018 -# - -import numpy as np -import torch -from torch.autograd import Function -from torch import nn -import pdb - -import dreamplacefpga.ops.dct.dct_lee_cpp as dct_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.dct.dct_lee_cuda as dct_cuda - -def dct(x, expk, buf, out): - """compute discrete cosine transformation, DCT II - yk = \sum_{n=0}^{N-1} x_n cos(pi/N*n*(k+1/2)) - """ - if x.is_cuda: - dct_cuda.dct(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.dct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - return out.view(x.size()) - -class DCTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return dct(x, expk, buf, out) - -class DCT(nn.Module): - def __init__(self, expk=None): - super(DCT, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_dct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_dct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return DCTFunction.apply(x, self.expk, self.buf, self.out) - -def idct(x, expk, buf, out): - """Compute inverse discrete cosine transformation, which is also the DCT III - yk = Re { 1/2*x0 + \sum_{n=1}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - The actual yk will be scaled by 2 to match other python implementation - """ - if x.is_cuda: - dct_cuda.idct(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.idct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDCTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return idct(x, expk, buf, out) - -class IDCT(nn.Module): - def __init__(self, expk=None): - super(IDCT, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDCTFunction.apply(x, self.expk, self.buf, self.out) - -def dct2(x, expk0, expk1, buf, out): - """compute 2D discrete cosine transformation - """ - if x.is_cuda: - dct_cuda.dct2(x, expk0, expk1, buf, out) - else: - dct_cpp.dct2(x, expk0, expk1, buf, out, torch.get_num_threads()) - return out - -class DCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, buf, out): - return dct2(x, expk0, expk1, buf, out) - -class DCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(DCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.buf = None - self.out = None - def forward(self, x): - if self.expk0 is None or self.expk0.size(-1) != x.size(-2): - self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_dct_cos(x.size(-2), self.expk0) - else: - dct_cpp.precompute_dct_cos(x.size(-2), self.expk0) - if self.expk1 is None or self.expk1.size(-1) != x.size(-1): - self.expk1 = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_dct_cos(x.size(-1), self.expk1) - else: - dct_cpp.precompute_dct_cos(x.size(-1), self.expk1) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return DCT2Function.apply(x, self.expk0, self.expk1, self.buf, self.out) - -def idct2(x, expk0, expk1, buf, out): - """compute 2D inverse discrete cosine transformation - """ - if x.is_cuda: - dct_cuda.idct2(x, expk0, expk1, buf, out) - else: - dct_cpp.idct2(x, expk0, expk1, buf, out, torch.get_num_threads()) - return out - -class IDCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, buf, out): - return idct2(x, expk0, expk1, buf, out) - -class IDCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.buf = None - self.out = None - def forward(self, x): - if self.expk0 is None or self.expk0.size(-1) != x.size(-2): - self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) - else: - dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) - if self.expk1 is None or self.expk1.size(-1) != x.size(-1): - self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDCT2Function.apply(x, self.expk0, self.expk1, self.buf, self.out) - -def dst(x, expk, buf, out): - """compute discrete sine transformation - yk = \sum_{n=0}^{N-1} x_n cos(pi/N*(n+1/2)*(k+1)) - """ - if x.is_cuda: - dct_cuda.dst(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.dst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - return out.view(x.size()) - -class DSTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return dst(x, expk, buf, out) - -class DST(nn.Module): - def __init__(self, expk=None): - super(DST, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_dct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_dct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return DSTFunction.apply(x, self.expk, self.buf, self.out) - -def idst(x, expk, buf, out): - """Compute inverse discrete sine transformation, which is also the DST III - yk = Im { (-1)^k*x_{N-1}/2 + \sum_{n=0}^{N-2} xn exp(j*pi/(2N)*(n+1)*(2k+1)) } - The actual yk will be scaled by 2 to match other python implementation - """ - if x.is_cuda: - dct_cuda.idst(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.idst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDSTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return idst(x, expk, buf, out) - -class IDST(nn.Module): - def __init__(self, expk=None): - super(IDST, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDSTFunction.apply(x, self.expk, self.buf, self.out) - -def idxct(x, expk, buf, out): - """compute inverse discrete cosine transformation - This is different from ordinary formulation for IDCT III - yk = Re { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - """ - if x.is_cuda: - dct_cuda.idxct(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.idxct(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - #output = IDCTFunction.forward(ctx, x, expk) - #output.add_(x[..., 0].unsqueeze(-1)).mul_(0.5) - ##output.mul_(0.5).add_(x[..., 0].unsqueeze(-1).mul(0.5)) - return out.view(x.size()) - -class IDXCTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return idxct(x, expk, buf, out) - -class IDXCT(nn.Module): - def __init__(self, expk=None): - super(IDXCT, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDXCTFunction.apply(x, self.expk, self.buf, self.out) - -def idxst(x, expk, buf, out): - """compute inverse discrete sine transformation - This is different from ordinary formulation for IDCT III - yk = Im { \sum_{n=0}^{N-1} xn exp(j*pi/(2N)*n*(2k+1)) } - """ - if x.is_cuda: - dct_cuda.idxst(x.view([-1, x.size(-1)]), expk, buf, out) - else: - dct_cpp.idxst(x.view([-1, x.size(-1)]), expk, buf, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDXSTFunction(Function): - @staticmethod - def forward(ctx, x, expk, buf, out): - return idxst(x, expk, buf, out) - -class IDXST(nn.Module): - def __init__(self, expk=None): - super(IDXST, self).__init__() - self.expk = expk - self.buf = None - self.out = None - def forward(self, x): - if self.expk is None or self.expk.size(-1) != x.size(-1): - self.expk = torch.empty(x.size(-1), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk) - if self.out is None or self.out.size() != x.size(): - self.buf = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDXSTFunction.apply(x, self.expk, self.buf, self.out) - -def idcct2(x, expk0, expk1, buf0, buf1, out): - """compute inverse discrete cosine-sine transformation - This is equivalent to idcct(idcct(x)^T)^T - """ - if x.is_cuda: - dct_cuda.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) - else: - dct_cpp.idcct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDCCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, buf0, buf1, out): - return idcct2(x, expk0, expk1, buf0, buf1, out) - -class IDCCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.buf0 = None - self.buf1 = None - self.out = None - def forward(self, x): - if self.expk0 is None or self.expk0.size(-1) != x.size(-2): - self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) - else: - dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) - if self.expk1 is None or self.expk1.size(-1) != x.size(-1): - self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) - if self.out is None or self.out.size() != x.size(): - self.buf0 = torch.empty_like(x) - self.buf1 = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDCCT2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) - -def idcst2(x, expk0, expk1, buf0, buf1, out): - """compute inverse discrete cosine-sine transformation - This is equivalent to idxct(idxst(x)^T)^T - """ - if x.is_cuda: - dct_cuda.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) - else: - dct_cpp.idcst2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDCST2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, buf0, buf1, out): - return idcst2(x, expk0, expk1, buf0, buf1, out) - -class IDCST2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDCST2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.buf0 = None - self.buf1 = None - self.out = None - def forward(self, x): - if self.expk0 is None or self.expk0.size(-1) != x.size(-2): - self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) - else: - dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) - if self.expk1 is None or self.expk1.size(-1) != x.size(-1): - self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) - if self.out is None or self.out.size() != x.size(): - self.buf0 = torch.empty_like(x) - self.buf1 = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDCST2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) - -def idsct2(x, expk0, expk1, buf0, buf1, out): - """compute inverse discrete cosine-sine transformation - This is equivalent to idxst(idxct(x)^T)^T - """ - if x.is_cuda: - dct_cuda.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out) - else: - dct_cpp.idsct2(x.view([-1, x.size(-1)]), expk0, expk1, buf0, buf1, out, torch.get_num_threads()) - return out.view(x.size()) - -class IDSCT2Function(Function): - @staticmethod - def forward(ctx, x, expk0, expk1, buf0, buf1, out): - return idsct2(x, expk0, expk1, buf0, buf1, out) - -class IDSCT2(nn.Module): - def __init__(self, expk0=None, expk1=None): - super(IDSCT2, self).__init__() - self.expk0 = expk0 - self.expk1 = expk1 - self.buf0 = None - self.buf1 = None - self.out = None - def forward(self, x): - if self.expk0 is None or self.expk0.size(-1) != x.size(-2): - self.expk0 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-2), self.expk0) - else: - dct_cpp.precompute_idct_cos(x.size(-2), self.expk0) - if self.expk1 is None or self.expk1.size(-1) != x.size(-1): - self.expk1 = torch.empty(x.size(-2), dtype=x.dtype, device=x.device) - if x.is_cuda: - dct_cuda.precompute_idct_cos(x.size(-1), self.expk1) - else: - dct_cpp.precompute_idct_cos(x.size(-1), self.expk1) - if self.out is None or self.out.size() != x.size(): - self.buf0 = torch.empty_like(x) - self.buf1 = torch.empty_like(x) - self.out = torch.empty_like(x) - return IDSCT2Function.apply(x, self.expk0, self.expk1, self.buf0, self.buf1, self.out) - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py deleted file mode 100644 index 5351ecfb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py +++ /dev/null @@ -1,430 +0,0 @@ -## -# @file discrete_spectral_transform.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# - -import os -import sys -import numpy as np -import torch -import torch.nn.functional as F -import pdb -import dreamplacefpga.ops.dct.torch_fft_api as torch_fft_api - -""" Discrete spectral transformation leveraging fast fourier transform engine. -The math here mainly uses Prosthaphaeresis properties. -The trigonometric identities exploited by prosthaphaeresis relate products of trigonometric functions to sums. -sin(a) sin(b) = 1/2 * (cos(a-b) - cos(a+b)) -cos(a) cos(b) = 1/2 * (cos(a-b) + cos(a+b)) -sin(a) cos(b) = 1/2 * (sin(a+b) + sin(a-b)) -cos(a) sin(b) = 1/2 * (sin(a-b) - sin(a+b)) - -A 2D FFT performs -y_{u, v} = \sum_i \sum_j x_{i, j} exp(-j*2*pi*u*i/M) exp(-j*2*pi*v*j/N) - = \sum_i \sum_j x_{i, j} exp(-j*2*pi*(u*i/M + v*j/N)) - = \sum_i \sum_j x_{i, j} (cos(-2*pi*(u*i/M + v*j/N)) + j sin(-2*pi*(u*i/M + v*j/N))). - -By mapping the original image from (i, j) to (i, N-j), we can have (u*i/M - v*j/N) inside exp. -This will enable us to derive various cos/sin transformation by computing FFT twice. -""" - -def get_expk(N, dtype, device): - """ Compute 2*exp(-1j*pi*u/(2N)), but not exactly the same. - The actual return is 2*cos(pi*u/(2N)), 2*sin(pi*u/(2N)). - This will make later multiplication easier. - """ - pik_by_2N = torch.arange(N, dtype=dtype, device=device) - pik_by_2N.mul_(np.pi/(2*N)) - # cos, sin - # I use sin because the real part requires subtraction - # this will be easier for multiplication - expk = torch.stack([pik_by_2N.cos(), pik_by_2N.sin()], dim=-1) - expk.mul_(2) - - return expk.contiguous() - - -def get_expkp1(N, dtype, device): - """ Compute 2*exp(-1j*pi*(u+1)/(2N)), but not exactly the same. - The actual return is 2*cos(pi*(u+1)/(2N)), 2*sin(pi*(u+1)/(2N)) - """ - neg_pik_by_2N = torch.arange(1, N+1, dtype=dtype, device=device) - neg_pik_by_2N.mul_(np.pi/(2*N)) - # sin, -cos - # I swap -cos and sin because we need the imag part - # this will be easier for multiplication - expk = torch.stack([neg_pik_by_2N.cos(), neg_pik_by_2N.sin()], dim=-1) - expk.mul_(2) - - return expk.contiguous() - - -def get_exact_expk(N, dtype, device): - # Compute exp(-j*pi*u/(2N)) = cos(pi*u/(2N)) - j * sin(pi*u/(2N)) - pik_by_2N = torch.arange(N, dtype=dtype, device=device) - pik_by_2N.mul_(np.pi/(2*N)) - # cos, -sin - expk = torch.stack([pik_by_2N.cos(), -pik_by_2N.sin()], dim=-1) - return expk.contiguous() - - -def get_perm(N, dtype, device): - """ Compute permutation to generate following array - 0, 2, 4, ..., 2*(N//2)-2, 2*(N//2)-1, 2*(N//2)-3, ..., 3, 1 - """ - perm = torch.zeros(N, dtype=dtype, device=device) - perm[0:(N-1)//2+1] = torch.arange(0, N, 2, dtype=dtype, device=device) - perm[(N-1)//2+1:] = torch.arange(2*(N//2)-1, 0, -2, dtype=dtype, device=device) - - return perm - -def dct_2N(x, expk=None): - """ Batch Discrete Cosine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i cos(pi*(2i+1)*u/(2N)), - Impelements the 2N padding trick to solve DCT with FFT in the following link, - https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft - - 1. Pad x by zeros - 2. Perform FFT - 3. Multiply by 2*exp(-1j*pi*u/(2N)) - 4. Extract the real part - """ - # last dimension - N = x.size(-1) - # pad last dimension - x_pad = F.pad(x, (0, N), 'constant', 0) - - # the last dimension here becomes -2 because complex numbers introduce a new dimension - y = torch_fft_api.rfft(x_pad, signal_ndim=1, normalized=False, onesided=True)[..., 0:N, :] - y.mul_(1.0/N) - - if expk is None: - expk = get_expk(N, dtype=x.dtype, device=x.device) - - # get real part - y.mul_(expk) - - # I found add is much faster than sum - #y = y.sum(dim=-1) - return y[..., 0]+y[..., 1] - - -def dct_N(x, perm=None, expk=None): - """ Batch Discrete Cosine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i cos(pi*(2i+1)*u/(2N)), - Impelements the N permuting trick to solve DCT with FFT in the following link, - https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft - - 1. permute x such that [a, b, c, d, e, f] becomes [a, c, e, f, d, b] - 2. Perform FFT - 3. Multiply by 2*exp(-1j*pi*u/(2N)) - 4. Extract the real part - """ - # last dimension - N = x.size(-1) - - if perm is None: - perm = get_perm(N, dtype=torch.int64, device=x.device) - if x.ndimension() <= 1: - x_reorder = x.view([1, N]) - else: - x_reorder = x.clone() - # switch from row-major to column-major for speedup - x_reorder.transpose_(dim0=-2, dim1=-1) - #x_reorder = x_reorder[..., perm, :] - x_reorder = x_reorder.index_select(dim=-2, index=perm) - # switch back - x_reorder.transpose_(dim0=-2, dim1=-1) - - y = torch_fft_api.rfft(x_reorder, signal_ndim=1, normalized=False, onesided=False)[..., 0:N, :] - y.mul_(1.0/N) - - if expk is None: - expk = get_expk(N, dtype=x.dtype, device=x.device) - - # get real part - y.mul_(expk) - # I found add is much faster than sum - #y = y.sum(dim=-1) - return y[..., 0]+y[..., 1] - - -def idct_2N(x, expk=None): - """ Batch Inverse Discrete Cosine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), - Impelements the 2N padding trick to solve IDCT with IFFT in the following link, - https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py - - 1. Multiply by 2*exp(1j*pi*u/(2N)) - 2. Pad x by zeros - 3. Perform IFFT - 4. Extract the real part - """ - # last dimension - N = x.size(-1) - - if expk is None: - expk = get_expk(N, dtype=x.dtype, device=x.device) - - # multiply by 2*exp(1j*pi*u/(2N)) - x_pad = x.unsqueeze(-1).mul(expk) - # pad second last dimension, excluding the complex number dimension - x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) - - if len(x.size()) == 1: - x_pad.unsqueeze_(0) - - # the last dimension here becomes -2 because complex numbers introduce a new dimension - y = torch_fft_api.irfft(x_pad, signal_ndim=1, normalized=False, onesided=False, signal_sizes=[2*N])[..., 0:N] - y.mul_(N) - - if len(x.size()) == 1: - y.squeeze_(0) - - return y - - -def idct_N(x, expk=None): - N = x.size(-1) - - if expk is None: - expk = get_expk(N, dtype=x.dtype, device=x.device) - - size = list(x.size()) - size.append(2) - x_reorder = torch.zeros(size, dtype=x.dtype, device=x.device) - x_reorder[..., 0] = x - x_reorder[..., 1:, 1] = x.flip([x.ndimension()-1])[..., :N-1].mul_(-1) - - x_reorder[..., 0] = x.mul(expk[..., 0]).sub_(x_reorder[..., 1].mul(expk[..., 1])) - x_reorder[..., 1].mul_(expk[..., 0]) - x_reorder[..., 1].add_(x.mul(expk[..., 1])) - # this is to match idct_2N - # normal way should multiply 0.25 - x_reorder.mul_(0.5) - - y = torch_fft_api.ifft(x_reorder, signal_ndim=1, normalized=False) - y.mul_(N) - - z = torch.empty_like(x) - z[..., 0:N:2] = y[..., :(N+1)//2, 0] - z[..., 1:N:2] = y[..., (N+1)//2:, 0].flip([x.ndimension()-1]) - - return z - - -def dst(x, expkp1=None): - """ Batch Discrete Sine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i sin(pi*(2i+1)*(u+1)/(2N)), - Impelements the 2N padding trick to solve DCT with FFT in the following link, - https://dsp.stackexchange.com/questions/2807/fast-cosine-transform-via-fft - - 1. Pad x by zeros - 2. Perform FFT - 3. Multiply by 2*exp(-1j*pi*u/(2N)) - 4. Extract the real part - """ - # last dimension - N = x.size(-1) - # pad last dimension - x_pad = F.pad(x, (0, N), 'constant', 0) - - # the last dimension here becomes -2 because complex numbers introduce a new dimension - y = torch_fft_api.rfft(x_pad, signal_ndim=1, normalized=False, onesided=True)[..., 1:N+1, :] - - if expkp1 is None: - expkp1 = get_expkp1(N, dtype=x.dtype, device=x.device) - - # get imag part - y = y[..., 1].mul(expkp1[:, 0]) - y[..., 0].mul(expkp1[:, 1]) - - return y - - -def idst(x, expkp1=None): - """ Batch Inverse Discrete Sine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), - Impelements the 2N padding trick to solve IDCT with IFFT in the following link, - https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py - - 1. Multiply by 2*exp(1j*pi*u/(2N)) - 2. Pad x by zeros - 3. Perform IFFT - 4. Extract the real part - """ - # last dimension - N = x.size(-1) - - if expkp1 is None: - expkp1 = get_expkp1(N, dtype=x.dtype, device=x.device) - - # multiply by 2*exp(1j*pi*u/(2N)) - x_pad = x.unsqueeze(-1).mul(expkp1) - # pad second last dimension, excluding the complex number dimension - x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) - - if len(x.size()) == 1: - x_pad.unsqueeze_(0) - - # the last dimension here becomes -2 because complex numbers introduce a new dimension - y = torch_fft_api.irfft(x_pad, signal_ndim=1, normalized=False, onesided=False, signal_sizes=[2*N])[..., 1:N+1] - y.mul_(N) - - if len(x.size()) == 1: - y.squeeze_(0) - - return y - - -def idxt(x, cos_or_sin_flag, expk=None): - """ Batch Inverse Discrete Cosine Transformation without normalization to coefficients. - Compute y_u = \sum_i x_i cos(pi*(2u+1)*i/(2N)), - Impelements the 2N padding trick to solve IDCT with IFFT in the following link, - https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/spectral_ops.py - - 1. Multiply by 2*exp(1j*pi*u/(2N)) - 2. Pad x by zeros - 3. Perform IFFT - 4. Extract the real part - - @param x batch 1D tensor for conversion - @param cos_or_sin_flag 0 for cosine tranformation and 1 or sine transformation - @param expk 2*exp(j*pi*k/(2N)) - """ - # last dimension - N = x.size(-1) - - if expk is None: - expk = get_expk(N, dtype=x.dtype, device=x.device) - - # multiply by 2*exp(1j*pi*u/(2N)) - x_pad = x.unsqueeze(-1).mul(expk) - # pad second last dimension, excluding the complex number dimension - x_pad = F.pad(x_pad, (0, 0, 0, N), 'constant', 0) - - if len(x.size()) == 1: - x_pad.unsqueeze_(0) - - # the last dimension here becomes -2 because complex numbers introduce a new dimension - # Must use IFFT here - y = torch_fft_api.ifft(x_pad, signal_ndim=1, normalized=False)[..., 0:N, cos_or_sin_flag] - y.mul_(N) - - if len(x.size()) == 1: - y.squeeze_(0) - - return y - - -def dct2_2N(x, expk0=None, expk1=None): - """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. - Compute 1D DCT twice. - @param x batch tensor, the 2D part is MxN - @param expk0 with length M - @param expk1 with length N - """ - return dct_2N(dct_2N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) - - -def dct2_N(x, perm0=None, expk0=None, perm1=None, expk1=None): - """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. - Compute 1D DCT twice. - @param x batch tensor, the 2D part is MxN - @param perm0 with length M - @param expk0 with length M - @param perm1 with length N - @param expk1 with length N - """ - return dct_N(dct_N(x.transpose(dim0=-2, dim1=-1), perm=perm0, expk=expk0).transpose_(dim0=-2, dim1=-1), perm=perm1, expk=expk1) - - -def idct2_2N(x, expk0=None, expk1=None): - """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. - Compute 1D DCT twice. - @param x batch tensor, the 2D part is MxN - @param expk0 with length M - @param expk1 with length N - """ - return idct_2N(idct_2N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) - - -def idct2_N(x, expk0=None, expk1=None): - """ Batch 2D Discrete Cosine Transformation without normalization to coefficients. - Compute 1D DCT twice. - @param x batch tensor, the 2D part is MxN - @param expk0 with length M - @param expk1 with length N - """ - return idct_N(idct_N(x.transpose(dim0=-2, dim1=-1), expk0).transpose_(dim0=-2, dim1=-1), expk1) - - -def dst2(x, expkp1_0=None, expkp1_1=None): - """ Batch 2D Discrete Sine Transformation without normalization to coefficients. - Compute 1D DST twice. - @param x batch tensor, the 2D part is MxN - @param expkp1_0 with length M - @param expkp1_1 with length N - """ - return dst(dst(x.transpose(dim0=-2, dim1=-1), expkp1_0).transpose_(dim0=-2, dim1=-1), expkp1_1) - - -def idcct2(x, expk_0=None, expk_1=None): - """ Batch 2D Inverse Discrete Cosine-Cosine Transformation without normalization to coefficients. - It computes following equation, which is slightly different from standard DCT formulation. - y_{u, v} = \sum_p \sum_q x_{p, q} cos(pi/M*p*(u+0.5)) cos(pi/N*q*(v+0.5)) - Compute 1D DCT twice. - @param x batch tensor, the 2D part is MxN - @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) - @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) - """ - return idxt(idxt(x, 0, expk_1).transpose_(dim0=-2, dim1=-1), 0, expk_0).transpose(dim0=-2, dim1=-1) - # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1), 0, expk_1) - - -def idsct2(x, expk_0=None, expk_1=None): - """ Batch 2D Inverse Discrete Sine-Cosine Transformation without normalization to coefficients. - It computes following equation, which is slightly different from standard DCT formulation. - y_{u, v} = \sum_p \sum_q x_{p, q} sin(pi/M*p*(u+0.5)) cos(pi/N*q*(v+0.5)) - Compute 1D DST and then 1D DCT. - @param x batch tensor, the 2D part is MxN - @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) - @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) - """ - return idxt(idxt(x, 0, expk_1).transpose_(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1) - # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1), 0, expk_1) - - -def idcst2(x, expk_0=None, expk_1=None): - """ Batch 2D Inverse Discrete Cosine-Sine Transformation without normalization to coefficients. - It computes following equation, which is slightly different from standard DCT formulation. - y_{u, v} = \sum_p \sum_q x_{p, q} cos(pi/M*p*(u+0.5)) sin(pi/N*q*(v+0.5)) - Compute 1D DCT and then 1D DST. - @param x batch tensor, the 2D part is MxN - @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) - @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) - """ - return idxt(idxt(x, 1, expk_1).transpose_(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1) - # return idxt(idxt(x.transpose(dim0=-2, dim1=-1), 0, expk_0).transpose_(dim0=-2, dim1=-1), 1, expk_1) - - -def idxst_idct(x, expk_0=None, expk_1=None): - ''' - Batch 2D Inverse Discrete Sine-Cosine Transformation without normalization to coefficients. - Compute idxst(idct(x)) - @param x batch tensor, the 2D part is MxN - @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) - @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) - ''' - return idxt(idct_N(x, expk_1).transpose_(dim0=-2, dim1=-1), 1, expk_0).transpose_(dim0=-2, dim1=-1) - - -def idct_idxst(x, expk_0=None, expk_1=None): - ''' - Batch 2D Inverse Discrete Cosine-Sine Transformation without normalization to coefficients. - Compute idct(idxst(x)). - @param x batch tensor, the 2D part is MxN - @param expk_0 with length M, 2*exp(-1j*pi*k/(2M)) - @param expk_1 with length N, 2*exp(-1j*pi*k/(2N)) - ''' - return idct_N(idxt(x, 1, expk_1).transpose_(dim0=-2, dim1=-1), expk_0).transpose_(dim0=-2, dim1=-1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py deleted file mode 100644 index 1ba9440e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py +++ /dev/null @@ -1,106 +0,0 @@ -## -# @file naive.py -# @author Yibo Lin (DREAMPlace) -# @date Sep 2018 -# - -import numpy as np -import scipy -from scipy import fftpack -import torch -import pdb - -def myidst(x): - N = x.shape[-1] - z = np.zeros_like(x).astype(np.complex128) - for k in range(len(z)): - for i in range(len(x)): - #content = str(x[i]) + " " + str(i) - #print("[%d] %s" % (k, content)) - z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0))) - #for k in range(len(z)): - # for i in range(len(x)): - # if i == N-1: - # z[k] -= x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0)))/2.0 - return z - -def myidst_ext(x): - N = x.shape[-1] - z = np.zeros_like(x).astype(np.complex128) - for k in range(len(z)): - for i in range(len(x)): - #content = str(x[i]) + " " + str(i+1) - #print("[%d] %s" % (k, content)) - z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1))) - #for k in range(len(z)): - # for i in range(len(x)): - # if i == N-1: - # z[k] -= x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+0)))/2.0 - return z - -def dst_type2(x): - N = x.shape[-1] - z = np.zeros_like(x).astype(np.complex128) - for k in range(len(z)): - for i in range(len(x)): - z[k] += x[i]*np.sin(np.pi/N*((k+1)*(i+0.5))) - #z[k] += x[i]*np.exp(1j*np.pi/N*((k+1)*(i+0.5))) - return z - -def dst_type3(x): - N = x.shape[-1] - z = np.zeros_like(x).astype(np.complex128) - for k in range(len(z)): - for i in range(len(x)): - if i == N-1: - #z[k] += ((-1)**k)/2.0*x[i] - z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1)))/2.0 - else: - #z[k] += x[i]*np.sin(np.pi/N*((k+0.5)*(i+1))) - z[k] += x[i]*np.exp(1j*np.pi/N*((k+0.5)*(i+1))) - return z*2 - -if __name__ == "__main__": - x = np.array([1, 23, 5, 6, 7, 4]).astype(np.float64) - xflip = np.flip(x, 0) - N = len(x) - print("scipy dst") - print(fftpack.dst(x, 2)/2) - - ydst2 = dst_type2(x) - print("dst_type2") - print(ydst2) - - pdb.set_trace() - - print("scipy idst") - print(fftpack.idst(ydst2, 2)/len(x)) - - print("scipy dst III") - print(fftpack.dst(ydst2, 3)/len(x)) - - zidst2 = dst_type3(ydst2)/len(x) - print("idst_type2") - print(zidst2) - - zmyidst = myidst(ydst2) - print("myidst") - print(zmyidst) - - ydst2_ext = np.concatenate([ydst2[1:], [0]]) - zmyidst_ext = myidst_ext(ydst2_ext) - print("myidst_ext") - print(zmyidst_ext) - - print(fftpack.idct(np.flip(ydst2_ext, 0), 2)/2) - - #expk = 0.5*np.exp(np.arange(N)*1j*np.pi*2/(4*N)) - #v = np.zeros_like(expk) - #for k in range(N): - # if k == 0: - # v[k] = expk[k] * (-0 + 1j*ydst2_ext[k]) - # else: - # v[k] = expk[k] * (-ydst2_ext[N-k] + 1j*ydst2_ext[k]) - #print(np.fft.ifft(v)) - - pdb.set_trace() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp deleted file mode 100644 index 9079eb0b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/** - * @file dct.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - */ -#include "dct.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dct_forward(at::Tensor x, at::Tensor expk, int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // auto x_reorder = at::empty_like(x); - auto x_reorder = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_forward", [&] { - computeReorder(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - auto y = at::rfft(x_reorder, 1, false, true); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - // std::cout << "x_reorder\n" << x_reorder << "\n"; - // std::cout << "expk\n" << expk << "\n"; - computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - // std::cout << "z\n" << x_reorder << "\n"; - x_reorder.mul_(1.0 / N); - }); - - return x_reorder; -} - -at::Tensor idct_forward(at::Tensor x, at::Tensor expk, int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_forward", [&] { - computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << __func__ << " v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << __func__ << " y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // reuse v - v.resize_({M, N}); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - // this is to match python implementation - // normal way should be multiply by 0.25*N - v.mul_(0.5 * N); - }); - - return v; -} - -at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - auto x_reorder = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_forward", [&] { - computeReorder(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - auto y = at::rfft(x_reorder, 1, false, true); - // y.mul_(1.0/N); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - // std::cout << "expk1\n" << expk1 << "\n"; - computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - // std::cout << "z\n" << x_reorder << "\n"; - - // 1D DCT to rows - auto xt = x_reorder.transpose(-2, -1).contiguous(); - // std::cout << "xt\n" << xt << "\n"; - // I do not want to allocate memory another time - // x_reorder = at::empty_like(xt); - x_reorder = x_reorder.view_as(xt); - computeReorder(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - y = at::rfft(x_reorder, 1, false, true); - // y.mul_(1.0/M); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - // std::cout << "expk0\n" << expk0 << "\n"; - computeMulExpk(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads); - - x_reorder.mul_(1.0 / (M * N)); - x_reorder.transpose_(-2, -1); - }); - - return x_reorder.contiguous(); -} - -at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_forward", [&] { - computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "expk1\n" << expk1 << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - // y.mul_(0.25*N); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - // auto z = at::empty(x.options(), {M, N}); - /// reuse v - v.resize_({M, N}); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - // 1D DCT to rows - auto xt = v.transpose(-2, -1).contiguous(); - // std::cout << "xt\n" << xt << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "expk0\n" << expk0 << "\n"; - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - // y.mul_(0.25*M); - - // std::cout << "y\n" << y << "\n"; - - // I do not want to allocate memory another time - // reuse v - v.resize_({N, M}); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - // this is to match python implementation - // normal way should be multiply by 0.25*0.25*M*N - v.mul_(0.25 * M * N); - v.transpose_(-2, -1); - }); - - return v.contiguous(); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("dct", &DREAMPLACE_NAMESPACE::dct_forward, "DCT forward"); - m.def("idct", &DREAMPLACE_NAMESPACE::idct_forward, "IDCT forward"); - m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_forward, "IDXCT forward"); - m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_forward, "DCT2 forward"); - - m.def("dst", &DREAMPLACE_NAMESPACE::dst_forward, "DST forward"); - m.def("idst", &DREAMPLACE_NAMESPACE::idst_forward, "IDST forward"); - - m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_forward, "IDCT2 forward"); - m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_forward, "IDXST forward"); - - // use idxst and idxct as kernels - m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_forward, "IDCCT2 forward"); - m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_forward, "IDCST2 forward"); - m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_forward, "IDSCT2 forward"); - - // use idxst and idct as kernels - m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST(IDCT(x)) forward"); - m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT(IDXST(x)) forward"); - - m.def("dct_2N", &DREAMPLACE_NAMESPACE::dct_2N_forward, "DCT forward"); - m.def("idct_2N", &DREAMPLACE_NAMESPACE::idct_2N_forward, "IDCT forward"); - m.def("dct2_2N", &DREAMPLACE_NAMESPACE::dct2_2N_forward, "DCT2 forward"); - m.def("idct2_2N", &DREAMPLACE_NAMESPACE::idct2_2N_forward, "IDCT2 forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h deleted file mode 100644 index 64911e9d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h +++ /dev/null @@ -1,427 +0,0 @@ -/** - * @file dct.h - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#ifndef DREAMPLACE_DCT_H -#define DREAMPLACE_DCT_H - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_CPU(x) AT_ASSERTM(!x.is_cuda(), #x "must be a tensor on CPU") -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -at::Tensor dct_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idct_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor dct2_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idct2_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor dst_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idst_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idxct_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idxst_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idcct2_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idcst2_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idsct2_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idxst_idct_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idct_idxst_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -template -void computeReorder( - const T* x, - const int M, - const int N, - T* y, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int ii = i%N; - - if (ii < (N>>1)) - { - // i*2 - //printf("x[%d] = y[%d]\n", i+ii, i); - y[i] = x[i+ii]; - } - else - { - // (N-i)*2-1 - //printf("x[%d] = y[%d]\n", i+N*2-ii*3-1, i); - y[i] = x[i+N*2-ii*3-1]; - } - } -} - -template -void computeMulExpk( - const T* x, - const T* expk, - const int M, - const int N, - T* z, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int fft_onesided_size = (N>>1)+1; - int fft_onesided_size_2x = fft_onesided_size<<1; - - if (col_2x <= N) - { - int j = row*fft_onesided_size_2x + col_2x; - //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); - z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; - } - else - { - int j = row*fft_onesided_size_2x + (N<<1) - col_2x; - //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); - z[i] = x[j]*expk[col_2x] - x[j+1]*expk[col_2x+1]; - } - } -} - -template -void computeVk( - const T* x, - const T* expk, - const int M, - const int N, - T* v, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*(N/2+1); ++i) - { - int ncol = N/2+1; - int row = i/ncol; // row - int col = i-row*ncol; // column - int col_2x = (col<<1); - - // real - T real = x[row*N+col]; - T imag = (col == 0)? 0 : -x[row*N+N-col]; - - v[2*i] = real*expk[col_2x] - imag*expk[col_2x+1]; - // imag, x[N-i] - v[2*i+1] = real*expk[col_2x+1] + imag*expk[col_2x]; - } -} - -template -void computeReorderReverse( - const T* y, - const int M, - const int N, - T* z, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - - //assert((i-col*2+N-1)*2 < M*N*2); - //printf("z[%d] = y[%d]\n", i, (col&1)? (i-col*3/2+N-1) : (i-col/2)); - //z[i] = (col&1)? y[(i-col*3/2+N-1)] : y[(i-col/2)]; - // according to the paper, it should be N - (col+1)/2 for col is odd - // but it seems previous implementation accidentally matches this as well - z[i] = (col&1)? y[(i-col) + N - (col+1)/2] : y[(i-col/2)]; - } -} - -template -void addX0AndScale( - const T* x, - const int M, - const int N, - T* y, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int i0 = int(i/N)*N; - y[i] = (y[i]+x[i0])*0.5; - } -} - -/// extends from addX0AndScale to merge scaling -template -void addX0AndScaleN( - const T* x, - const int M, - const int N, - T* y, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int i0 = int(i/N)*N; - // this is to match python implementation - // normal way should be multiply by 0.25*N - y[i] = y[i]*0.25*N+x[i0]*0.5; - } -} - -/// given an array -/// x_0, x_1, ..., x_{N-1} -/// convert to -/// 0, x_{N-1}, ..., x_2, x_1 -/// drop x_0 -template -void computeFlipAndShift( - const T* x, - const int M, - const int N, - T* y, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int ii = i%N; - y[i] = (ii)? x[i+N-ii*2] : 0; - } -} - -/// flip sign of odd entries -/// index starts from 0 -template -void negateOddEntries( - T* x, - const int M, - const int N, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*(N/2); ++i) - { - x[i*2+1] = -x[i*2+1]; - } -} - -/// given an array -/// x_0, x_1, ..., x_{N-1} -/// convert to -/// x_{N-1}, ..., x_2, x_1, x_0 -template -void computeFlip( - const T* x, - const int M, - const int N, - T* y, - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int ii = i%N; - y[i] = x[i+N-ii*2-1]; - } -} - -at::Tensor dct_2N_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor idct_2N_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ); - -at::Tensor dct2_2N_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -at::Tensor idct2_2N_forward( - at::Tensor x, - at::Tensor expk0, - at::Tensor expk1, - int num_threads - ); - -template -void computePad( - const T* x, // M*N - const int M, - const int N, - T* z, // M*2N - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - int j = row*(N<<1) + col; - z[j] = x[i]; - } -} - -template -void computeMulExpk_2N( - const T* x, // M*(N+1)*2 - const T* expk, - const int M, - const int N, - T* z, // M*N - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int j = row*((N+1)<<1) + col_2x; - z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; - } -} - -template -void computeMulExpkAndPad_2N( - const T* x, // M*N - const T* expk, - const int M, - const int N, - T* z, // M*2N*2 - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int j = row*(N<<2) + col_2x; - z[j] = x[i]*expk[col_2x]; - z[j+1] = x[i]*expk[col_2x+1]; - } -} - - -/// remove last N entries in each column -template -void computeTruncation( - const T* x, // M*2N - const int M, - const int N, - T* z, // M*N - int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < M*N; ++i) - { - int row = i/N; // row - int col = i-row*N; // column - int j = row*(N<<1) + col; - z[i] = x[j]; - } -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp deleted file mode 100644 index b33a139d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @file dct2_fft2.cpp - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Aug 2019 - * @brief All the transforms in this file are implemented based on 2D FFT. - * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. - */ - -#include "dct/src/dct2_fft2.h" - -DREAMPLACE_BEGIN_NAMESPACE - -void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads) { - CHECK_CPU(x); - CHECK_CPU(expkM); - CHECK_CPU(expkN); - CHECK_CPU(out); - CHECK_CPU(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_fft2_forward", [&] { - dct2dPreprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); - - buf = at::rfft(out, 2, false, true); - - dct2dPostprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); - }); -} - -void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads) { - CHECK_CPU(x); - CHECK_CPU(expkM); - CHECK_CPU(expkN); - CHECK_CPU(out); - CHECK_CPU(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_fft2_forward", [&] { - idct2_fft2PreprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idct2_fft2PostprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); - }); -} - -void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads) { - CHECK_CPU(x); - CHECK_CPU(expkM); - CHECK_CPU(expkN); - CHECK_CPU(out); - CHECK_CPU(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_idxst_forward", [&] { - idct_idxstPreprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idct_idxstPostprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); - }); -} - -void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads) { - CHECK_CPU(x); - CHECK_CPU(expkM); - CHECK_CPU(expkN); - CHECK_CPU(out); - CHECK_CPU(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_idct_forward", [&] { - idxst_idctPreprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t), num_threads); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idxst_idctPostprocessCpuLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, num_threads); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("dct2_fft2", &DREAMPLACE_NAMESPACE::dct2_fft2_forward, "DCT2 FFT2D (CPU)"); - m.def("idct2_fft2", &DREAMPLACE_NAMESPACE::idct2_fft2_forward, "IDCT2 FFT2D (CPU)"); - m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT IDXST FFT2D (CPU)"); - m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST IDCT FFT2D (CPU)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h deleted file mode 100644 index d0e51bcf..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h +++ /dev/null @@ -1,613 +0,0 @@ -/** - * @file dct2_fft2.h - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Aug 2019 - * @brief All the transforms in this file are implemented based on 2D FFT. - * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. - */ -#ifndef DREAMPLACE_DCT2_FFT2_H -#define DREAMPLACE_DCT2_FFT2_H - -#include -#include -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads); - -void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads); - -void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads); - -void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf, int num_threads); - -inline int INDEX(const int hid, const int wid, const int N) { - return (hid * N + wid); -} - -template -void dct2dPreprocessCpu(const T* x, T* y, const int M, const int N, - int num_threads) { - int halfN = N / 2; -#pragma omp parallel for num_threads(num_threads) - for(int hid = 0; hid < M; ++hid) { - for(int wid = 0; wid < N; ++wid) { - int index; - int cond = (((hid & 1) == 0) << 1) | ((wid & 1) == 0); - switch (cond) { - case 0: - index = INDEX(2 * M - (hid + 1), N - (wid + 1) / 2, halfN); - break; - case 1: - index = INDEX(2 * M - (hid + 1), wid / 2, halfN); - break; - case 2: - index = INDEX(hid, N - (wid + 1) / 2, halfN); - break; - case 3: - index = INDEX(hid, wid / 2, halfN); - break; - default: - break; - } - y[index] = x[INDEX(hid, wid, N)]; - } - } -} - -template -void dct2dPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, - int num_threads) { - dct2dPreprocessCpu(x, y, M, N, num_threads); -} - -template -void dct2dPostprocessCpu(const TComplex* V, T* y, const int M, const int N, - const TComplex* expkM, const TComplex* expkN, - int num_threads) { - int halfM = M / 2; - int halfN = N / 2; - T four_over_MN =(T)(4. / (M * N)); - T two_over_MN =(T)(2. / (M * N)); - -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < halfM; ++hid) { - for (int wid = 0; wid < halfN; ++wid) { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) { - case 0: - { - y[0] = V[0].x * four_over_MN; - y[halfN] = RealPartOfMul(expkN[halfN], V[halfN]) * four_over_MN; - y[INDEX(halfM, 0, N)] = expkM[halfM].x * V[INDEX(halfM, 0, halfN + 1)].x * four_over_MN; - y[INDEX(halfM, halfN, N)] = expkM[halfM].x * RealPartOfMul(expkN[halfN], V[INDEX(halfM, halfN, halfN + 1)]) * four_over_MN; - break; - } - - case 1: - { - ComplexType tmp; - - tmp = V[wid]; - y[wid] = RealPartOfMul(expkN[wid], tmp) * four_over_MN; - y[N - wid] = -ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; - - tmp = V[INDEX(halfM, wid, halfN + 1)]; - y[INDEX(halfM, wid, N)] = expkM[halfM].x * RealPartOfMul(expkN[wid], tmp) * four_over_MN; - y[INDEX(halfM, N - wid, N)] = -expkM[halfM].x * ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; - break; - } - - case 2: - { - ComplexType tmp1, tmp2, tmp_up, tmp_down; - tmp1 = V[INDEX(hid, 0, halfN + 1)]; - tmp2 = V[INDEX(M - hid, 0, halfN + 1)]; - tmp_up.x = expkM[hid].x * (tmp1.x + tmp2.x) + expkM[hid].y * (tmp2.y - tmp1.y); - tmp_down.x = -expkM[hid].y * (tmp1.x + tmp2.x) + expkM[hid].x * (tmp2.y - tmp1.y); - y[INDEX(hid, 0, N)] = tmp_up.x * two_over_MN; - y[INDEX(M - hid, 0, N)] = tmp_down.x * two_over_MN; - - tmp1 = complexAdd(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); - tmp2 = complexSubtract(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); - tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; - tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; - tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; - tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; - y[INDEX(hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_up) * two_over_MN; - y[INDEX(M - hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_down) * two_over_MN; - break; - } - - case 3: - { - ComplexType tmp1, tmp2, tmp_up, tmp_down; - tmp1 = complexAdd(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); - tmp2 = complexSubtract(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); - tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; - tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; - tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; - tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; - y[INDEX(hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_up) * two_over_MN; - y[INDEX(M - hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_down) * two_over_MN; - y[INDEX(hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_up) * two_over_MN; - y[INDEX(M - hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_down) * two_over_MN; - break; - } - - default: - assert(0); - break; - } - } - } -} - -template -void dct2dPostprocessCpuLauncher(const T* x, T* y, const int M, const int N, - const T* expkM, const T* expkN, - int num_threads) { - dct2dPostprocessCpu>((ComplexType *)x, y, M, N, (ComplexType *)expkM, (ComplexType *)expkN, num_threads); -} - -template -void idct2_fft2PreprocessCpu(const T* input, TComplex* output, const int M, - const int N, const TComplex* expkM, - const TComplex* expkN, int num_threads) { - const int halfM = M / 2; - const int halfN = N / 2; -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < halfM; ++hid) { - for (int wid = 0; wid < halfN; ++wid) { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = input[0]; - output[0].y = 0; - - tmp1 = input[halfN]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); - - tmp1 = input[INDEX(halfM, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - TComplex tmp_up; - tmp_up.x = input[wid]; - tmp_up.y = input[N - wid]; - output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); - - T tmp1 = input[INDEX(halfM, wid, N)]; - T tmp2 = input[INDEX(halfM, N - wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - tmp1 = input[INDEX(hid, 0, N)]; - tmp3 = input[INDEX(M - hid, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp3; - tmp_down.x = tmp3; - tmp_down.y = tmp1; - - output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); - output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); - - tmp1 = input[INDEX(hid, halfN, N)]; - tmp3 = input[INDEX(M - hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(hid, wid, N)]; - T tmp2 = input[INDEX(hid, N - wid, N)]; - T tmp3 = input[INDEX(M - hid, wid, N)]; - T tmp4 = input[INDEX(M - hid, N - wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } - } -} - -template -void idct2_fft2PreprocessCpuLauncher(const T* x, T* y, const int M, const int N, - const T* expkM, const T* expkN, - int num_threads) { - idct2_fft2PreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); -} - -template -void idct2_fft2PostprocessCpu(const T* x, T* y, const int M, const int N, - int num_threads) { - int MN = M * N; - #pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < M; ++hid) { - for (int wid = 0; wid < N; ++wid) { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - break; - default: - assert(0); - break; - } - y[index] = x[INDEX(hid, wid, N)] * MN; - } - } -} - -template -void idct2_fft2PostprocessCpuLauncher(const T* x, T* y, const int M, - const int N, int num_threads) { - idct2_fft2PostprocessCpu(x, y, M, N, num_threads); -} - -template -void idct_idxstPreprocessCpu(const T* input, TComplex* output, const int M, - const int N, const TComplex* expkM, - const TComplex* expkN, int num_threads) -{ - int halfM = M / 2; - int halfN = N / 2; -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < halfM; ++hid) - { - for (int wid = 0; wid < halfN; ++wid) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = 0; - output[0].y = 0; - - tmp1 = input[halfN]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); - - output[INDEX(halfM, 0, halfN + 1)].x = 0; - output[INDEX(halfM, 0, halfN + 1)].y = 0; - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - TComplex tmp_up; - tmp_up.x = input[N - wid]; - tmp_up.y = input[wid]; - output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); - - T tmp1 = input[INDEX(halfM, N - wid, N)]; - T tmp2 = input[INDEX(halfM, wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - output[INDEX(hid, 0, halfN + 1)].x = 0; - output[INDEX(hid, 0, halfN + 1)].y = 0; - output[INDEX(M - hid, 0, halfN + 1)].x = 0; - output[INDEX(M - hid, 0, halfN + 1)].y = 0; - - tmp1 = input[INDEX(hid, halfN, N)]; - tmp3 = input[INDEX(M - hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(hid, N - wid, N)]; - T tmp2 = input[INDEX(hid, wid, N)]; - T tmp3 = input[INDEX(M - hid, N - wid, N)]; - T tmp4 = input[INDEX(M - hid, wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } - } -} - -template -void idct_idxstPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, - const T* expkM, const T* expkN, - int num_threads) { - idct_idxstPreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); -} - -template -void idct_idxstPostprocessCpu(const T* x, T* y, const int M, const int N, - int num_threads) { - //const int halfN = N / 2; - const int MN = M * N; -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < M; ++hid) - { - for (int wid = 0; wid < N; ++wid) - { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) - { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - default: - assert(0); - break; - } - } - } -} - -template -void idct_idxstPostprocessCpuLauncher(const T* x, T* y, const int M, - const int N, int num_threads) { - idct_idxstPostprocessCpu(x, y, M, N, num_threads); -} - -template -void idxst_idctPreprocessCpu(const T* input, TComplex* output, const int M, - const int N, const TComplex* expkM, - const TComplex* expkN, int num_threads) { - const int halfM = M / 2; - const int halfN = N / 2; -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < halfM; ++hid) - { - for (int wid = 0; wid < halfN; ++wid) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = 0; - output[0].y = 0; - - output[halfN].x = 0; - output[halfN].y = 0; - - tmp1 = input[INDEX(halfM, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - output[wid].x = 0; - output[wid].y = 0; - - TComplex tmp_up; - T tmp1 = input[INDEX(halfM, wid, N)]; - T tmp2 = input[INDEX(halfM, N - wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - tmp1 = input[INDEX(M - hid, 0, N)]; - tmp3 = input[INDEX(hid, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp3; - tmp_down.x = tmp3; - tmp_down.y = tmp1; - - output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); - output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); - - tmp1 = input[INDEX(M - hid, halfN, N)]; - tmp3 = input[INDEX(hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(M - hid, wid, N)]; - T tmp2 = input[INDEX(M - hid, N - wid, N)]; - T tmp3 = input[INDEX(hid, wid, N)]; - T tmp4 = input[INDEX(hid, N - wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } - } -} - -template -void idxst_idctPreprocessCpuLauncher(const T* x, T* y, const int M, const int N, - const T* expkM, const T* expkN, - int num_threads) { - idxst_idctPreprocessCpu>(x, (ComplexType*)y, M, N, (ComplexType*)expkM, (ComplexType*)expkN, num_threads); -} - -template -void idxst_idctPostprocessCpu(const T* x, T* y, const int M, const int N, - int num_threads) { - //const int halfN = N / 2; - const int MN = M * N; -#pragma omp parallel for num_threads(num_threads) - for (int hid = 0; hid < M; ++hid) - { - for (int wid = 0; wid < N; ++wid) - { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) - { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - default: - assert(0); - break; - } - } - } -} - -template -void idxst_idctPostprocessCpuLauncher(const T* x, T* y, const int M, - const int N, int num_threads) { - idxst_idctPostprocessCpu(x, y, M, N, num_threads); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp deleted file mode 100644 index 8ab45a9c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @file dct2_fft2_cuda.cpp - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Apr 2019 - * @brief All the transforms in this file are implemented based on 2D FFT. - * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. - */ - -#include "dct2_fft2_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf) { - CHECK_CUDA(x); - CHECK_CUDA(expkM); - CHECK_CUDA(expkN); - CHECK_CUDA(out); - CHECK_CUDA(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_fft2_forward", [&] { - dct2dPreprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - - buf = at::rfft(out, 2, false, true); - - dct2dPostprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); - }); -} - -void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf) { - CHECK_CUDA(x); - CHECK_CUDA(expkM); - CHECK_CUDA(expkN); - CHECK_CUDA(out); - CHECK_CUDA(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_fft2_forward", [&] { - idct2_fft2PreprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idct2_fft2PostprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - }); -} - -void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf) { - CHECK_CUDA(x); - CHECK_CUDA(expkM); - CHECK_CUDA(expkN); - CHECK_CUDA(out); - CHECK_CUDA(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_idxst_forward", [&] { - idct_idxstPreprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idct_idxstPostprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - }); -} - -void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf) { - CHECK_CUDA(x); - CHECK_CUDA(expkM); - CHECK_CUDA(expkN); - CHECK_CUDA(out); - CHECK_CUDA(buf); - - CHECK_CONTIGUOUS(x); - CHECK_CONTIGUOUS(expkM); - CHECK_CONTIGUOUS(expkN); - CHECK_CONTIGUOUS(out); - CHECK_CONTIGUOUS(buf); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_idct_forward", [&] { - idxst_idctPreprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(expkM, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expkN, scalar_t)); - - auto y = at::irfft(buf, 2, false, true, {{M, N}}); - - idxst_idctPostprocessCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("dct2_fft2", &DREAMPLACE_NAMESPACE::dct2_fft2_forward, "DCT2 FFT2D (CUDA)"); - m.def("idct2_fft2", &DREAMPLACE_NAMESPACE::idct2_fft2_forward, "IDCT2 FFT2D (CUDA)"); - m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, "IDCT IDXST FFT2D (CUDA)"); - m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, "IDXST IDCT FFT2D (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h deleted file mode 100644 index c8a82c33..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file dct2_fft2_cuda.h - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Apr 2019 - * @brief All the transforms in this file are implemented based on 2D FFT. - * Each transfrom has three steps, 1) preprocess, 2) 2d fft or 2d ifft, 3) postprocess. - */ - -#ifndef DREAMPLACE_DCT2_FFT2_CUDA_H -#define DREAMPLACE_DCT2_FFT2_CUDA_H - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -// dct2_fft2 -void dct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf); - -template -void dct2dPreprocessCudaLauncher(const T *x, T *y, const int M, const int N); - -template -void dct2dPostprocessCudaLauncher(const T *x, T *y, const int M, const int N, - const T *__restrict__ expkM, - const T *__restrict__ expkN); - -// idct2_fft2 -void idct2_fft2_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf); - -template -void idct2_fft2PreprocessCudaLauncher(const T *x, T *y, const int M, - const int N, const T *__restrict__ expkM, - const T *__restrict__ expkN); - -template -void idct2_fft2PostprocessCudaLauncher(const T *x, T *y, const int M, - const int N); - -// idct_idxst -void idct_idxst_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf); - -template -void idct_idxstPreprocessCudaLauncher(const T *x, T *y, const int M, - const int N, const T *__restrict__ expkM, - const T *__restrict__ expkN); - -template -void idct_idxstPostprocessCudaLauncher(const T *x, T *y, const int M, - const int N); - -// idxst_idct -void idxst_idct_forward(at::Tensor x, at::Tensor expkM, at::Tensor expkN, - at::Tensor out, at::Tensor buf); - -template -void idxst_idctPreprocessCudaLauncher(const T *x, T *y, const int M, - const int N, const T *__restrict__ expkM, - const T *__restrict__ expkN); - -template -void idxst_idctPostprocessCudaLauncher(const T *x, T *y, const int M, - const int N); - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu deleted file mode 100644 index 21a1dedc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu +++ /dev/null @@ -1,728 +0,0 @@ -/** - * @file dct2_fft2_cuda_kernel.cu - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Apr 2019 - * @brief Refernece: Byeong Lee, "A new algorithm to compute the discrete cosine Transform," - * in IEEE Transactions on Acoustics, Speech, and Signal Processing, vol. 32, no. 6, pp. 1243-1245, December 1984. - * The preprocess and postprocess of 2d dct and 2d idct are discussed in the original paper. - * idct(idxst(x)) and idxst(idct(x)) are similar to the idct2d(x), - * except tiny modifications on preprocessing and postprocessing - */ - -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -#define TPB (16) - -DREAMPLACE_BEGIN_NAMESPACE - -inline __device__ int INDEX(const int hid, const int wid, const int N) -{ - return (hid * N + wid); -} - -// dct2_fft2 -template -__global__ void dct2dPreprocess(const T *x, T *y, const int M, const int N, const int halfN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < M && wid < N) - { - int index; - int cond = (((hid & 1) == 0) << 1) | ((wid & 1) == 0); - switch (cond) - { - case 0: - index = INDEX(2 * M - (hid + 1), N - (wid + 1) / 2, halfN); - break; - case 1: - index = INDEX(2 * M - (hid + 1), wid / 2, halfN); - break; - case 2: - index = INDEX(hid, N - (wid + 1) / 2, halfN); - break; - case 3: - index = INDEX(hid, wid / 2, halfN); - break; - default: - break; - } - y[index] = x[INDEX(hid, wid, N)]; - } -} - -template -void dct2dPreprocessCudaLauncher(const T *x, T *y, const int M, const int N) -{ - dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - dct2dPreprocess<<>>(x, y, M, N, N / 2); -} - -template -__global__ void __launch_bounds__(TPB * TPB, 8) dct2dPostprocess(const TComplex *V, T *y, const int M, const int N, - const int halfM, const int halfN, const T two_over_MN, const T four_over_MN, - const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) -{ - - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < halfM && wid < halfN) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - y[0] = V[0].x * four_over_MN; - y[halfN] = RealPartOfMul(expkN[halfN], V[halfN]) * four_over_MN; - y[INDEX(halfM, 0, N)] = expkM[halfM].x * V[INDEX(halfM, 0, halfN + 1)].x * four_over_MN; - y[INDEX(halfM, halfN, N)] = expkM[halfM].x * RealPartOfMul(expkN[halfN], V[INDEX(halfM, halfN, halfN + 1)]) * four_over_MN; - break; - } - - case 1: - { - ComplexType tmp; - - tmp = V[wid]; - y[wid] = RealPartOfMul(expkN[wid], tmp) * four_over_MN; - y[N - wid] = -ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; - - tmp = V[INDEX(halfM, wid, halfN + 1)]; - y[INDEX(halfM, wid, N)] = expkM[halfM].x * RealPartOfMul(expkN[wid], tmp) * four_over_MN; - y[INDEX(halfM, N - wid, N)] = -expkM[halfM].x * ImaginaryPartOfMul(expkN[wid], tmp) * four_over_MN; - break; - } - - case 2: - { - ComplexType tmp1, tmp2, tmp_up, tmp_down; - tmp1 = V[INDEX(hid, 0, halfN + 1)]; - tmp2 = V[INDEX(M - hid, 0, halfN + 1)]; - tmp_up.x = expkM[hid].x * (tmp1.x + tmp2.x) + expkM[hid].y * (tmp2.y - tmp1.y); - tmp_down.x = -expkM[hid].y * (tmp1.x + tmp2.x) + expkM[hid].x * (tmp2.y - tmp1.y); - y[INDEX(hid, 0, N)] = tmp_up.x * two_over_MN; - y[INDEX(M - hid, 0, N)] = tmp_down.x * two_over_MN; - - tmp1 = complexAdd(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); - tmp2 = complexSubtract(V[INDEX(hid, halfN, halfN + 1)], V[INDEX(M - hid, halfN, halfN + 1)]); - tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; - tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; - tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; - tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; - y[INDEX(hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_up) * two_over_MN; - y[INDEX(M - hid, halfN, N)] = RealPartOfMul(expkN[halfN], tmp_down) * two_over_MN; - break; - } - - case 3: - { - ComplexType tmp1, tmp2, tmp_up, tmp_down; - tmp1 = complexAdd(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); - tmp2 = complexSubtract(V[INDEX(hid, wid, halfN + 1)], V[INDEX(M - hid, wid, halfN + 1)]); - tmp_up.x = expkM[hid].x * tmp1.x - expkM[hid].y * tmp2.y; - tmp_up.y = expkM[hid].x * tmp1.y + expkM[hid].y * tmp2.x; - tmp_down.x = -expkM[hid].y * tmp1.x - expkM[hid].x * tmp2.y; - tmp_down.y = -expkM[hid].y * tmp1.y + expkM[hid].x * tmp2.x; - y[INDEX(hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_up) * two_over_MN; - y[INDEX(M - hid, wid, N)] = RealPartOfMul(expkN[wid], tmp_down) * two_over_MN; - y[INDEX(hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_up) * two_over_MN; - y[INDEX(M - hid, N - wid, N)] = -ImaginaryPartOfMul(expkN[wid], tmp_down) * two_over_MN; - break; - } - - default: - assert(0); - break; - } - } -} - -template -void dct2dPostprocessCudaLauncher(const T *x, T *y, const int M, const int N, - const T *__restrict__ expkM, const T *__restrict__ expkN) -{ - dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - dct2dPostprocess><<>>((ComplexType *)x, y, M, N, M / 2, N / 2, (T)(2. / (M * N)), (T)(4. / (M * N)), (ComplexType *)expkM, (ComplexType *)expkN); -} - -// idct2_fft2 -template -__global__ void __launch_bounds__(TPB * TPB, 8) idct2_fft2Preprocess(const T *input, TComplex *output, const int M, const int N, - const int halfM, const int halfN, - const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < halfM && wid < halfN) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = input[0]; - output[0].y = 0; - - tmp1 = input[halfN]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); - - tmp1 = input[INDEX(halfM, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - TComplex tmp_up; - tmp_up.x = input[wid]; - tmp_up.y = input[N - wid]; - output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); - - T tmp1 = input[INDEX(halfM, wid, N)]; - T tmp2 = input[INDEX(halfM, N - wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - tmp1 = input[INDEX(hid, 0, N)]; - tmp3 = input[INDEX(M - hid, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp3; - tmp_down.x = tmp3; - tmp_down.y = tmp1; - - output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); - output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); - - tmp1 = input[INDEX(hid, halfN, N)]; - tmp3 = input[INDEX(M - hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(hid, wid, N)]; - T tmp2 = input[INDEX(hid, N - wid, N)]; - T tmp3 = input[INDEX(M - hid, wid, N)]; - T tmp4 = input[INDEX(M - hid, N - wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } -} - -template -void idct2_fft2PreprocessCudaLauncher( - const T *x, - T *y, - const int M, - const int N, - const T *__restrict__ expkM, - const T *__restrict__ expkN) -{ - dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idct2_fft2Preprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); -} - -template -__global__ void idct2_fft2Postprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < M && wid < N) - { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) - { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - break; - default: - assert(0); - break; - } - y[index] = x[INDEX(hid, wid, N)] * MN; - } -} - -template -void idct2_fft2PostprocessCudaLauncher(const T *x, T *y, const int M, const int N) -{ - dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idct2_fft2Postprocess<<>>(x, y, M, N, N / 2, M * N); -} - -// idct_idxst -// Adpated from idct2d_preprocess(). The only change is the reordered input -// if (wid != 0) -// new_input[hid][wid] = input[hid][N - wid]; -// else -// new_input[hid][0] = 0 -template -__global__ void __launch_bounds__(TPB * TPB, 8) idct_idxstPreprocess(const T *input, TComplex *output, const int M, const int N, - const int halfM, const int halfN, - const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < halfM && wid < halfN) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = 0; - output[0].y = 0; - - tmp1 = input[halfN]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[halfN] = complexConj(complexMul(expkN[halfN], tmp_up)); - - output[INDEX(halfM, 0, halfN + 1)].x = 0; - output[INDEX(halfM, 0, halfN + 1)].y = 0; - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - TComplex tmp_up; - tmp_up.x = input[N - wid]; - tmp_up.y = input[wid]; - output[wid] = complexConj(complexMul(expkN[wid], tmp_up)); - - T tmp1 = input[INDEX(halfM, N - wid, N)]; - T tmp2 = input[INDEX(halfM, wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - output[INDEX(hid, 0, halfN + 1)].x = 0; - output[INDEX(hid, 0, halfN + 1)].y = 0; - output[INDEX(M - hid, 0, halfN + 1)].x = 0; - output[INDEX(M - hid, 0, halfN + 1)].y = 0; - - tmp1 = input[INDEX(hid, halfN, N)]; - tmp3 = input[INDEX(M - hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(hid, N - wid, N)]; - T tmp2 = input[INDEX(hid, wid, N)]; - T tmp3 = input[INDEX(M - hid, N - wid, N)]; - T tmp4 = input[INDEX(M - hid, wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } -} - -template -void idct_idxstPreprocessCudaLauncher(const T *x, T *y, const int M, const int N, - const T *__restrict__ expkM, const T *__restrict__ expkN) -{ - dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idct_idxstPreprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); -} - -// Adpated from idct2d_postprocess() with changes on sign and scale -// if (wid % 2 == 1) -// new_output[hid][wid] = -output[hid][wid]; -// else -// new_output[hid][wid] = output[hid][wid]; -template -__global__ void idct_idxstPostprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < M && wid < N) - { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) - { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - default: - assert(0); - break; - } - } -} - -template -void idct_idxstPostprocessCudaLauncher(const T *x, T *y, const int M, const int N) -{ - dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idct_idxstPostprocess<<>>(x, y, M, N, N / 2, M * N); -} - -// idxst_idct -// Adpated from idct2d_preprocess(). The only change is the reordered input -// if (hid != 0) -// new_input[hid][wid] = input[M - hid][wid]; -// else -// new_input[0][wid] = 0 -template -__global__ void __launch_bounds__(TPB * TPB, 8) idxst_idctPreprocess(const T *input, TComplex *output, const int M, const int N, - const int halfM, const int halfN, - const TComplex *__restrict__ expkM, const TComplex *__restrict__ expkN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < halfM && wid < halfN) - { - int cond = ((hid != 0) << 1) | (wid != 0); - switch (cond) - { - case 0: - { - T tmp1; - TComplex tmp_up; - - output[0].x = 0; - output[0].y = 0; - - output[halfN].x = 0; - output[halfN].y = 0; - - tmp1 = input[INDEX(halfM, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp1; - output[INDEX(halfM, 0, halfN + 1)] = complexConj(complexMul(expkM[halfM], tmp_up)); - - tmp1 = input[INDEX(halfM, halfN, N)]; - tmp_up.x = 0; - tmp_up.y = 2 * tmp1; - output[INDEX(halfM, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[halfN]), tmp_up)); - break; - } - - case 1: - { - output[wid].x = 0; - output[wid].y = 0; - - TComplex tmp_up; - T tmp1 = input[INDEX(halfM, wid, N)]; - T tmp2 = input[INDEX(halfM, N - wid, N)]; - tmp_up.x = tmp1 - tmp2; - tmp_up.y = tmp1 + tmp2; - output[INDEX(halfM, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[halfM], expkN[wid]), tmp_up)); - break; - } - - case 2: - { - T tmp1, tmp3; - TComplex tmp_up, tmp_down; - - tmp1 = input[INDEX(M - hid, 0, N)]; - tmp3 = input[INDEX(hid, 0, N)]; - tmp_up.x = tmp1; - tmp_up.y = tmp3; - tmp_down.x = tmp3; - tmp_down.y = tmp1; - - output[INDEX(hid, 0, halfN + 1)] = complexConj(complexMul(expkM[hid], tmp_up)); - output[INDEX(M - hid, 0, halfN + 1)] = complexConj(complexMul(expkM[M - hid], tmp_down)); - - tmp1 = input[INDEX(M - hid, halfN, N)]; - tmp3 = input[INDEX(hid, halfN, N)]; - tmp_up.x = tmp1 - tmp3; - tmp_up.y = tmp3 + tmp1; - tmp_down.x = tmp3 - tmp1; - tmp_down.y = tmp1 + tmp3; - - output[INDEX(hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[halfN]), tmp_up)); - output[INDEX(M - hid, halfN, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[halfN]), tmp_down)); - break; - } - - case 3: - { - T tmp1 = input[INDEX(M - hid, wid, N)]; - T tmp2 = input[INDEX(M - hid, N - wid, N)]; - T tmp3 = input[INDEX(hid, wid, N)]; - T tmp4 = input[INDEX(hid, N - wid, N)]; - TComplex tmp_up, tmp_down; - tmp_up.x = tmp1 - tmp4; - tmp_up.y = tmp3 + tmp2; - tmp_down.x = tmp3 - tmp2; - tmp_down.y = tmp1 + tmp4; - - output[INDEX(hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[hid], expkN[wid]), tmp_up)); - output[INDEX(M - hid, wid, halfN + 1)] = complexConj(complexMul(complexMul(expkM[M - hid], expkN[wid]), tmp_down)); - break; - } - - default: - assert(0); - break; - } - } -} - -template -void idxst_idctPreprocessCudaLauncher( - const T *x, - T *y, - const int M, - const int N, - const T *__restrict__ expkM, - const T *__restrict__ expkN) -{ - dim3 gridSize((N / 2 + TPB - 1) / TPB, (M / 2 + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idxst_idctPreprocess><<>>(x, (ComplexType *)y, M, N, M / 2, N / 2, (ComplexType *)expkM, (ComplexType *)expkN); -} - -// Adpated from idct2d_postprocess() with changes on sign and scale -// if (hid % 2 == 1) -// new_output[hid][wid] = -output[hid][wid]; -// else -// new_output[hid][wid] = output[hid][wid]; -template -__global__ void idxst_idctPostprocess(const T *x, T *y, const int M, const int N, const int halfN, const int MN) -{ - const int wid = blockDim.x * blockIdx.x + threadIdx.x; - const int hid = blockDim.y * blockIdx.y + threadIdx.y; - if (hid < M && wid < N) - { - int cond = ((hid < M / 2) << 1) | (wid < N / 2); - int index; - switch (cond) - { - case 0: - index = INDEX(((M - hid) << 1) - 1, ((N - wid) << 1) - 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 1: - index = INDEX(((M - hid) << 1) - 1, wid << 1, N); - y[index] = -x[INDEX(hid, wid, N)] * MN; - break; - case 2: - index = INDEX(hid << 1, ((N - wid) << 1) - 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - case 3: - index = INDEX(hid << 1, wid << 1, N); - y[index] = x[INDEX(hid, wid, N)] * MN; - break; - default: - assert(0); - break; - } - } -} - -template -void idxst_idctPostprocessCudaLauncher(const T *x, T *y, const int M, const int N) -{ - dim3 gridSize((N + TPB - 1) / TPB, (M + TPB - 1) / TPB, 1); - dim3 blockSize(TPB, TPB, 1); - idxst_idctPostprocess<<>>(x, y, M, N, N / 2, M * N); -} - -// dct2_fft2 -#define REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(type) \ - template void dct2dPreprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N); - -REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(float); -REGISTER_DCT2DPREPROCESS_KERNEL_LAUNCHER(double); - -#define REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(type) \ - template void dct2dPostprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N, \ - const type *__restrict__ expkM, \ - const type *__restrict__ expkN); - -REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(float); -REGISTER_DCT2DPOSTPROCESS_KERNEL_LAUNCHER(double); - -//idct_idxst -#define REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(type) \ - template void idct_idxstPreprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N, \ - const type *__restrict__ expkM, \ - const type *__restrict__ expkN); - -REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDCT_IDXSTPREPROCESS_KERNEL_LAUNCHER(double); - -#define REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(type) \ - template void idct_idxstPostprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N); - -REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDCT_IDXSTPOSTPROCESS_KERNEL_LAUNCHER(double); - -//idxst_idct -#define REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(type) \ - template void idxst_idctPreprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N, \ - const type *__restrict__ expkM, \ - const type *__restrict__ expkN); - -REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDXST_IDCTPREPROCESS_KERNEL_LAUNCHER(double); - -#define REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(type) \ - template void idxst_idctPostprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N); - -REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDXST_IDCTPOSTPROCESS_KERNEL_LAUNCHER(double); - -//idct2_fft2 -#define REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(type) \ - template void idct2_fft2PreprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N, \ - const type *__restrict__ expkM, \ - const type *__restrict__ expkN); - -REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDCT2_FFT2PREPROCESS_KERNEL_LAUNCHER(double); - -#define REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(type) \ - template void idct2_fft2PostprocessCudaLauncher( \ - const type *x, \ - type *y, \ - const int M, \ - const int N); - -REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(float); -REGISTER_IDCT2_FFT2POSTPROCESS_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp deleted file mode 100644 index df9a9697..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @file dct_2N.cpp - * @author Yibo Lin (DREAMPlace) - * @date Nov 2018 - */ -#include "dct.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk, int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel()/N; - - auto x_pad = at::zeros({M, 2*N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_2N_forward", [&] { - computePad(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), - num_threads); - - auto y = at::rfft(x_pad, 1, false, true); - - // re-use x_pad as output - x_pad.resize_({{M, N}}); - computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - x_pad.mul_(1.0/N); - }); - - return x_pad; -} - -at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk, int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel()/N; - - auto x_pad = at::zeros({M, 2*N, 2}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_2N_forward", [&] { - computeMulExpkAndPad_2N( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - // y is real now - auto y = at::irfft(x_pad, 1, false, false, {2*N}); - - // reuse x_pad - x_pad.resize_({{M, N}}); - computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - //std::cout << "z\n" << z << "\n"; - - // this is to match python implementation - // normal way should be multiply by 0.25*N - x_pad.mul_(N); - }); - - return x_pad; -} - -at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - // 1D DCT to columns - - //std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel()/N; - auto x_pad = at::zeros({M, 2*N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_2N_forward", [&] { - computePad(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), - num_threads); - - auto y = at::rfft(x_pad, 1, false, true); - - // re-use x_pad as output - x_pad.resize_({{M, N}}); - computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - //x_pad.mul_(1.0/N); - - // 1D DCT to rows - auto xt = x_pad.transpose(-2, -1).contiguous(); - // I do not want to allocate memory another time - // must zero-out x_pad - x_pad.resize_({N, 2*M}).zero_(); - computePad(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), - num_threads); - - y = at::rfft(x_pad, 1, false, true); - //y.mul_(1.0/M); - - // re-use x_reorder as output - x_pad.resize_({N, M}); - computeMulExpk_2N(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - x_pad.mul_(1.0/(M*N)); - x_pad.transpose_(-2, -1); - }); - - return x_pad.contiguous(); -} - -at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // 1D DCT to columns - - auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_2N_forward", [&] { - computeMulExpkAndPad_2N( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - // y is real now - auto y = at::irfft(x_pad, 1, false, false, {2 * N}); - - // reuse x_pad - x_pad.resize_({M, N}); - computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - // this is to match python implementation - // normal way should be multiply by 0.25*N - // x_pad.mul_(N); - - // 1D DCT to rows - auto xt = x_pad.transpose(-2, -1).contiguous(); - x_pad.resize_({N, 2 * M, 2}).zero_(); - computeMulExpkAndPad_2N( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - // y is real now - y = at::irfft(x_pad, 1, false, false, {2 * M}); - - // reuse x_pad - x_pad.resize_({N, M}); - computeTruncation(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t), num_threads); - - // this is to match python implementation - // normal way should be multiply by 0.25*0.25*M*N - x_pad.mul_(M * N); - x_pad.transpose_(-2, -1); - }); - - return x_pad.contiguous(); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp deleted file mode 100644 index b8e10e44..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/** - * @file dct_2N_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Nov 2018 - */ -#include "dct_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - auto x_pad = at::zeros({M, 2 * N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_2N_forward", [&] { - computePadCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - auto y = at::rfft(x_pad, 1, false, true); - - // re-use x_pad as output - x_pad.resize_({M, N}); - computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - x_pad.mul_(1.0 / N); - }); - - return x_pad; -} - -at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_2N_forward", [&] { - computeMulExpkAndPad_2N_CudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - // y is real now - auto y = at::irfft(x_pad, 1, false, false, {2 * N}); - - // reuse x_pad - x_pad.resize_({M, N}); - computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - // this is to match python implementation - // normal way should be multiply by 0.25*N - x_pad.mul_(N); - }); - - return x_pad; -} - -at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - auto x_pad = at::zeros({M, 2 * N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_2N_forward", [&] { - computePadCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - auto y = at::rfft(x_pad, 1, false, true); - - // re-use x_pad as output - x_pad.resize_({M, N}); - computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - // x_pad.mul_(1.0/N); - - // 1D DCT to rows - auto xt = x_pad.transpose(-2, -1).contiguous(); - // I do not want to allocate memory another time - // must zero-out x_pad - x_pad.resize_({N, 2 * M}).zero_(); - computePadCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - y = at::rfft(x_pad, 1, false, true); - // y.mul_(1.0/M); - - // re-use x_reorder as output - x_pad.resize_({N, M}); - computeMulExpk_2N_CudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - x_pad.mul_(1.0 / (M * N)); - x_pad.transpose_(-2, -1); - }); - - return x_pad.contiguous(); -} - -at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // 1D DCT to columns - - auto x_pad = at::zeros({M, 2 * N, 2}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_2N_forward", [&] { - computeMulExpkAndPad_2N_CudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - // y is real now - auto y = at::irfft(x_pad, 1, false, false, {2 * N}); - - // reuse x_pad - x_pad.resize_({M, N}); - computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - // this is to match python implementation - // normal way should be multiply by 0.25*N - // x_pad.mul_(N); - - // 1D DCT to rows - auto xt = x_pad.transpose(-2, -1).contiguous(); - x_pad.resize_({N, 2 * M, 2}).zero_(); - computeMulExpkAndPad_2N_CudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - // y is real now - y = at::irfft(x_pad, 1, false, false, {2 * M}); - - // reuse x_pad - x_pad.resize_({N, M}); - computeTruncationCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_pad, scalar_t)); - - // this is to match python implementation - // normal way should be multiply by 0.25*0.25*M*N - x_pad.mul_(M * N); - x_pad.transpose_(-2, -1); - }); - - return x_pad.contiguous(); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp deleted file mode 100644 index 531df58a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/** - * @file dct_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - */ -#include "dct_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dct_forward(at::Tensor x, at::Tensor expk) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // auto x_reorder = at::empty_like(x); - auto x_reorder = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_forward", [&] { - computeReorderCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - auto y = at::rfft(x_reorder, 1, false, true); - y.mul_(1.0 / N); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - computeMulExpkCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - // std::cout << "z\n" << z << "\n"; - }); - - return x_reorder; -} - -at::Tensor idct_forward(at::Tensor x, at::Tensor expk) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk); - CHECK_CONTIGUOUS(expk); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_forward", [&] { - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - // auto z = at::empty({M, N}, x.options()); - // reuse v - v.resize_({M, N}); - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - // std::cout << "z\n" << z << "\n"; - // this is to match python implementation - // normal way should be multiply by 0.25*N - v.mul_(0.5 * N); - }); - - return v; -} - -at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - auto x_reorder = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct2_forward", [&] { - computeReorderCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - auto y = at::rfft(x_reorder, 1, false, true); - // y.mul_(1.0/N); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - // std::cout << "expk1\n" << expk1 << "\n"; - computeMulExpkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, - N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - // std::cout << "z\n" << x_reorder << "\n"; - - // 1D DCT to rows - auto xt = x_reorder.transpose(-2, -1).contiguous(); - // std::cout << "xt\n" << xt << "\n"; - // I do not want to allocate memory another time - // x_reorder = at::empty_like(xt); - x_reorder = x_reorder.view_as(xt); - computeReorderCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - - // std::cout << "x_reorder\n" << x_reorder << "\n"; - - y = at::rfft(x_reorder, 1, false, true); - // y.mul_(1.0/M); - // std::cout << "y\n" << y << "\n"; - - // re-use x_reorder as output - // std::cout << "expk0\n" << expk0 << "\n"; - computeMulExpkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, - M, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - - x_reorder.mul_(1.0 / (M * N)); - x_reorder.transpose_(-2, -1); - }); - - return x_reorder.contiguous(); -} - -at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct2_forward", [&] { - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "expk1\n" << expk1 << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - // y.mul_(0.25*N); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - // auto z = at::empty({M, N}, x.options()); - // reuse v - v.resize_({M, N}); - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - // 1D DCT to rows - auto xt = v.transpose(-2, -1).contiguous(); - // std::cout << "xt\n" << xt << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // reuse v - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "expk0\n" << expk0 << "\n"; - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - // y.mul_(0.25*M); - - // std::cout << "y\n" << y << "\n"; - - // I do not want to allocate memory another time - v.resize_({N, M}); - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - // this is to match python implementation - // normal way should be multiply by 0.25*0.25*M*N - v.mul_(0.25 * M * N); - v.transpose_(-2, -1); - }); - - return v.contiguous(); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("dct", &DREAMPLACE_NAMESPACE::dct_forward, "DCT forward (CUDA)"); - m.def("idct", &DREAMPLACE_NAMESPACE::idct_forward, "IDCT forward (CUDA)"); - m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_forward, "DCT2 forward (CUDA)"); - m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_forward, "IDCT2 forward (CUDA)"); - - m.def("dst", &DREAMPLACE_NAMESPACE::dst_forward, "DST forward (CUDA)"); - m.def("idst", &DREAMPLACE_NAMESPACE::idst_forward, "IDST forward (CUDA)"); - - m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_forward, "IDXCT forward (CUDA)"); - m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_forward, "IDXST forward (CUDA)"); - - // use idxst and idxct as kernels - m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_forward, - "IDCCT2 forward (CUDA)"); - m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_forward, - "IDCST2 forward (CUDA)"); - m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_forward, - "IDSCT2 forward (CUDA)"); - - // use idxst and idct as kernels - m.def("idxst_idct", &DREAMPLACE_NAMESPACE::idxst_idct_forward, - "IDXST(IDCT(x)) forward (CUDA)"); - m.def("idct_idxst", &DREAMPLACE_NAMESPACE::idct_idxst_forward, - "IDCT(IDXST(x)) forward (CUDA)"); - - m.def("dct_2N", &DREAMPLACE_NAMESPACE::dct_2N_forward, "DCT forward (CUDA)"); - m.def("idct_2N", &DREAMPLACE_NAMESPACE::idct_2N_forward, - "IDCT forward (CUDA)"); - m.def("dct2_2N", &DREAMPLACE_NAMESPACE::dct2_2N_forward, - "DCT2 forward (CUDA)"); - m.def("idct2_2N", &DREAMPLACE_NAMESPACE::idct2_2N_forward, - "IDCT2 forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h deleted file mode 100644 index 990e3d2f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file dct_cuda.h - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#ifndef DREAMPLACE_DCT_CUDA_H -#define DREAMPLACE_DCT_CUDA_H - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dct_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idct_forward(at::Tensor x, at::Tensor expk); - -at::Tensor dct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor dst_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idst_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idxct_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idxst_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -template -void computeReorderCudaLauncher(const T* x, const int M, const int N, T* y); - -template -void computeMulExpkCudaLauncher(const T* x, const T* expk, const int M, - const int N, T* z); - -template -void computeVkCudaLauncher(const T* x, const T* expk, const int M, const int N, - T* v); - -template -void computeReorderReverseCudaLauncher(const T* y, const int M, const int N, - T* z); - -template -void addX0AndScaleCudaLauncher(const T* x, const int M, const int N, T* y); - -/// extends from addX0AndScale to merge scaling -template -void addX0AndScaleNCudaLauncher(const T* x, const int M, const int N, T* y); - -/// given an array -/// x_0, x_1, ..., x_{N-1} -/// convert to -/// x_{N-1}, ..., x_2, x_1, x_0 -template -void computeFlipCudaLauncher(const T* x, const int M, const int N, T* y); - -/// given an array -/// x_0, x_1, ..., x_{N-1} -/// convert to -/// 0, x_{N-1}, ..., x_2, x_1 -/// drop x_0 -template -void computeFlipAndShiftCudaLauncher(const T* x, const int M, const int N, - T* y); - -/// flip sign of odd entries -/// index starts from 0 -template -void negateOddEntriesCudaLauncher(T* x, const int M, const int N); - -at::Tensor dct_2N_forward(at::Tensor x, at::Tensor expk); - -at::Tensor idct_2N_forward(at::Tensor x, at::Tensor expk); - -at::Tensor dct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -at::Tensor idct2_2N_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1); - -template -void computePadCudaLauncher(const T* x, // M*N - const int M, const int N, - T* z // M*2N -); - -template -void computeMulExpk_2N_CudaLauncher(const T* x, // M*(N+1)*2 - const T* expk, const int M, const int N, - T* z // M*N -); - -template -void computeMulExpkAndPad_2N_CudaLauncher(const T* x, // M*N - const T* expk, const int M, - const int N, - T* z // M*2N*2 -); - -/// remove last N entries in each column -template -void computeTruncationCudaLauncher(const T* x, // M*2N - const int M, const int N, - T* z // M*N -); - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu deleted file mode 100644 index c8dc2df1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu +++ /dev/null @@ -1,523 +0,0 @@ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeMulExpk( - const T* x, - const T* expk, - const int M, - const int N, - T* z - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < M*N) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int fft_onesided_size = (N>>1)+1; - int fft_onesided_size_2x = fft_onesided_size<<1; - - if (col_2x <= N) - { - int j = row*fft_onesided_size_2x + col_2x; - //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); - z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; - } - else - { - int j = row*fft_onesided_size_2x + (N<<1) - col_2x; - //printf("x[%d]*expk[%d] + x[%d]*expk[%d] = z[%d]\n", j, col_2x, j+1, col_2x+1, i); - z[i] = x[j]*expk[col_2x] - x[j+1]*expk[col_2x+1]; - } - } -} - -template -void computeMulExpkCudaLauncher( - const T* x, - const T* expk, - const int M, - const int N, - T* z - ) -{ - const int thread_count = 1024; - const int block_count = (M * N - 1 + thread_count) / thread_count; - - computeMulExpk<<>>( - x, - expk, - M, - N, - z - ); -} - -template -__global__ void computeReorder( - const T* x, - const int M, - const int N, - T* y - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < M*N) - { - int ii = i%N; - - if (ii < (N>>1)) - { - // i*2 - //printf("x[%d] = y[%d]\n", i+ii, i); - y[i] = x[i+ii]; - } - else - { - // (N-i)*2-1 - //printf("x[%d] = y[%d]\n", i+N*2-ii*3-1, i); - y[i] = x[i+N*2-ii*3-1]; - } - } -} - -template -void computeReorderCudaLauncher( - const T* x, - const int M, - const int N, - T* y - ) -{ - const int thread_count = 1024; - const int block_count = (M * N - 1 + thread_count) / thread_count; - - computeReorder<<>>( - x, - M, - N, - y - ); -} - -template -__global__ void computeVk( - const T* x, - const T* expk, - const int M, - const int N, - T* v - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < M*(N/2+1)) - { - int ncol = N/2+1; - int row = i/ncol; // row - int col = i-row*ncol; // column - int col_2x = (col<<1); - - // real - T real = x[row*N+col]; - T imag = (col == 0)? 0 : -x[row*N+N-col]; - - v[2*i] = real*expk[col_2x] - imag*expk[col_2x+1]; - // imag, x[N-i] - v[2*i+1] = real*expk[col_2x+1] + imag*expk[col_2x]; - } - -} - -template -void computeVkCudaLauncher( - const T* x, - const T* expk, - const int M, - const int N, - T* v - ) -{ - const int thread_count = 512; - const int block_count = (M*(N/2+1) - 1 + thread_count) / thread_count; - - computeVk<<>>( - x, - expk, - M, - N, - v - ); -} - - -template -__global__ void computeReorderReverse( - const T* y, - const int M, - const int N, - T* z - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < M*N) - { - int row = i/N; // row - int col = i-row*N; // column - - //printf("z[%d] = y[%d]\n", i, (col&1)? (i-col*3/2+N-1) : (i-col/2)); - //z[i] = (col&1)? y[(i-col*3/2+N-1)] : y[(i-col/2)]; - // according to the paper, it should be N - (col+1)/2 for col is odd - // but it seems previous implementation accidentally matches this as well - z[i] = (col&1)? y[(i-col) + N - (col+1)/2] : y[(i-col/2)]; - } -} - -template -void computeReorderReverseCudaLauncher( - const T* y, - const int M, - const int N, - T* z - ) -{ - const int thread_count = 512; - const int block_count = (M * N - 1 + thread_count) / thread_count; - - computeReorderReverse<<>>( - y, - M, - N, - z - ); -} - -template -__global__ void addX0AndScale( - const T* x, - const int M, - const int N, - T* y - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int i0 = int(i/N)*N; - y[i] = (y[i]+x[i0])*0.5; - } -} - -template -void addX0AndScaleCudaLauncher( - const T* x, - const int M, - const int N, - T* y - ) -{ - addX0AndScale<<<32, 1024>>>( - x, - M, - N, - y - ); -} - -/// extends from addX0AndScale to merge scaling -template -__global__ void addX0AndScaleN( - const T* x, - const int M, - const int N, - T* y - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int i0 = int(i/N)*N; - // this is to match python implementation - // normal way should be multiply by 0.25*N - y[i] = y[i]*0.25*N+x[i0]*0.5; - } -} - -template -void addX0AndScaleNCudaLauncher( - const T* x, - const int M, - const int N, - T* y - ) -{ - addX0AndScaleN<<<32, 1024>>>( - x, - M, - N, - y - ); -} - -template -__global__ void computePad( - const T* x, // M*N - const int M, - const int N, - T* z // M*2N - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int row = i/N; // row - int col = i-row*N; // column - int j = row*(N<<1) + col; - z[j] = x[i]; - } -} - -template -void computePadCudaLauncher( - const T* x, // M*N - const int M, - const int N, - T* z // M*2N - ) -{ - computePad<<<32, 1024>>>( - x, - M, - N, - z - ); -} - -template -__global__ void computeMulExpk_2N( - const T* x, // M*(N+1)*2 - const T* expk, - const int M, - const int N, - T* z // M*N - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int j = row*((N+1)<<1) + col_2x; - z[i] = x[j]*expk[col_2x] + x[j+1]*expk[col_2x+1]; - } -} - -template -void computeMulExpk_2N_CudaLauncher( - const T* x, // M*(N+1)*2 - const T* expk, - const int M, - const int N, - T* z // M*N - ) -{ - computeMulExpk_2N<<<32, 1024>>>( - x, - expk, - M, - N, - z - ); -} - -template -__global__ void computeMulExpkAndPad_2N( - const T* x, // M*N - const T* expk, - const int M, - const int N, - T* z // M*2N*2 - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int row = i/N; // row - int col = i-row*N; // column - int col_2x = (col<<1); - int j = row*(N<<2) + col_2x; - z[j] = x[i]*expk[col_2x]; - z[j+1] = x[i]*expk[col_2x+1]; - } -} - -template -void computeMulExpkAndPad_2N_CudaLauncher( - const T* x, // M*N - const T* expk, - const int M, - const int N, - T* z // M*2N*2 - ) -{ - computeMulExpkAndPad_2N<<<32, 1024>>>( - x, - expk, - M, - N, - z - ); -} - -/// remove last N entries in each column -template -__global__ void computeTruncation( - const T* x, // M*2N - const int M, - const int N, - T* z // M*N - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int row = i/N; // row - int col = i-row*N; // column - int j = row*(N<<1) + col; - z[i] = x[j]; - } -} - -template -void computeTruncationCudaLauncher( - const T* x, // M*2N - const int M, - const int N, - T* z // M*N - ) -{ - computeTruncation<<<32, 1024>>>( - x, - M, - N, - z - ); -} - -// manually instantiate the template function -#define REGISTER_MULPEXPK_KERNEL_LAUNCHER(type) \ - template void computeMulExpkCudaLauncher(\ - const type* x, \ - const type* expk, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_MULPEXPK_KERNEL_LAUNCHER(float); -REGISTER_MULPEXPK_KERNEL_LAUNCHER(double); - -#define REGISTER_REORDER_KERNEL_LAUNCHER(type) \ - template void computeReorderCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* y \ - ); - -REGISTER_REORDER_KERNEL_LAUNCHER(float); -REGISTER_REORDER_KERNEL_LAUNCHER(double); - -#define REGISTER_VK_KERNEL_LAUNCHER(type) \ - template void computeVkCudaLauncher(\ - const type* x, \ - const type* expk, \ - const int M, \ - const int N, \ - type* v \ - ); - -REGISTER_VK_KERNEL_LAUNCHER(float); -REGISTER_VK_KERNEL_LAUNCHER(double); - -#define REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(type) \ - template void computeReorderReverseCudaLauncher(\ - const type* y, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(float); -REGISTER_REORDERREVERSE_KERNEL_LAUNCHER(double); - -#define REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(type) \ - template void addX0AndScaleCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* y \ - ); - -REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(float); -REGISTER_ADDX0ANDSCALE_KERNEL_LAUNCHER(double); - -#define REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(type) \ - template void addX0AndScaleNCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* y \ - ); - -REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(float); -REGISTER_ADDX0ANDSCALEN_KERNEL_LAUNCHER(double); - -#define REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(type) \ - template void computePadCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(float); -REGISTER_COMPUTEPAD_KERNEL_LAUNCHER(double); - -#define REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(type) \ - template void computeMulExpk_2N_CudaLauncher(\ - const type* x, \ - const type* expk, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(float); -REGISTER_COMPUTEMULEXPK_2N_KERNEL_LAUNCHER(double); - -#define REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(type) \ - template void computeMulExpkAndPad_2N_CudaLauncher(\ - const type* x, \ - const type* expk, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(float); -REGISTER_COMPUTEMULEXPKANDPAD_2N_KERNEL_LAUNCHER(double); - -#define REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(type) \ - template void computeTruncationCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* z \ - ); - -REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(float); -REGISTER_COMPUTETRUNCATION_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp deleted file mode 100644 index b9def3ef..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @file dct_lee.cpp - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -#include "dct.h" -#include "dct_lee_cpu.h" - -DREAMPLACE_BEGIN_NAMESPACE - -void dct_lee_precompute_dct_cos(int N, at::Tensor out) { - out.resize_(N); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - out, "dct_lee_precompute_dct_cos", [&] { - lee::precompute_dct_cos( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); - }); -} - -void dct_lee_precompute_idct_cos(int N, at::Tensor out) { - out.resize_(N); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - out, "dct_lee_precompute_idct_cos", [&] { - lee::precompute_idct_cos( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); - }); -} - -void dct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out, int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos); - CHECK_CONTIGUOUS(cos); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_lee_forward", [&] { - lee::dct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N, num_threads); - }); - - out.mul_(2.0 / N); -} - -void idct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out, int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos); - CHECK_CONTIGUOUS(cos); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_lee_forward", [&] { - lee::idct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N, num_threads); - }); - - out.mul_(2); -} - -void dst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, - at::Tensor out, int num_threads = at::get_num_threads()) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - buf.copy_(x); - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N, - num_threads); - - dct_lee_forward(buf, expk, buf, out, num_threads); - // std::cout << "y\n" << y << "\n"; - - computeFlip(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - num_threads); - }); - - out.copy_(buf); -} - -void idst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, - at::Tensor out, int num_threads = at::get_num_threads()) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - computeFlip(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - num_threads); - - idct_lee_forward(buf, expk, buf, out, num_threads); - // std::cout << "y\n" << y << "\n"; - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - num_threads); - // std::cout << "z\n" << y << "\n"; - }); -} - -void dct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf, at::Tensor out, - int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CPU(cos1); - CHECK_CONTIGUOUS(cos1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - - out.resize_({M, N}); - buf.resize_({M, N}); - - dct_lee_forward(x, cos1, out, buf, num_threads); - - // 1D DCT to rows - out.resize_({N, M}); - out.copy_(buf.transpose(-2, -1)); - buf.resize_({N, M}); - - dct_lee_forward(out, cos0, out, buf, num_threads); - - out.resize_({M, N}); - out.copy_(buf.transpose_(-2, -1)); -} - -void idct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf, at::Tensor out, - int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CPU(cos1); - CHECK_CONTIGUOUS(cos1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - - out.resize_({M, N}); - buf.resize_({M, N}); - - idct_lee_forward(x, cos1, out, buf, num_threads); - - // 1D DCT to rows - out.resize_({N, M}); - out.copy_(buf.transpose(-2, -1)); - buf.resize_({N, M}); - - idct_lee_forward(out, cos0, out, buf, num_threads); - - out.resize_({M, N}); - out.copy_(buf.transpose(-2, -1)); -} - -void idxct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out, - int num_threads = at::get_num_threads()) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_lee_forward", [&] { - idct_lee_forward(x, cos, buf, out, num_threads); - - // std::cout << __func__ << " z\n" << z << "\n"; - - addX0AndScale(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), - num_threads); - }); -} - -void idxst_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out, - int num_threads = at::get_num_threads()) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - num_threads); - - idct_lee_forward(buf, cos, buf, out, num_threads); - out.mul_(0.5); - // std::cout << "y\n" << y << "\n"; - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - num_threads); - // std::cout << "z\n" << y << "\n"; - }); -} - -void idcct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out, - int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CPU(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxct for rows - - idxct_lee_forward(x, cos1, buf0, out, num_threads); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxct_lee_forward(buf0, cos0, out, buf1, num_threads); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -void idcst2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out, - int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CPU(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxst for rows - - idxst_lee_forward(x, cos1, buf0, out, num_threads); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxct_lee_forward(buf0, cos0, out, buf1, num_threads); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -void idsct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out, - int num_threads = at::get_num_threads()) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CPU(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxst for rows - - idxct_lee_forward(x, cos1, buf0, out, num_threads); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxst_lee_forward(buf0, cos0, out, buf1, num_threads); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("precompute_dct_cos", &DREAMPLACE_NAMESPACE::dct_lee_precompute_dct_cos, - "Precompute DCT cosine"); - m.def("precompute_idct_cos", - &DREAMPLACE_NAMESPACE::dct_lee_precompute_idct_cos, - "Precompute IDCT cosine"); - m.def("dct", &DREAMPLACE_NAMESPACE::dct_lee_forward, "DCT forward"); - m.def("idct", &DREAMPLACE_NAMESPACE::idct_lee_forward, "IDCT forward"); - m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_lee_forward, "IDXCT forward"); - m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_lee_forward, "IDXST forward"); - - m.def("dst", &DREAMPLACE_NAMESPACE::dst_lee_forward, "DST forward"); - m.def("idst", &DREAMPLACE_NAMESPACE::idst_lee_forward, "IDST forward"); - - m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_lee_forward, "DCT2 forward"); - m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_lee_forward, "IDCT2 forward"); - m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_lee_forward, "IDCCT2 forward"); - m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_lee_forward, "IDCST2 forward"); - m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_lee_forward, "IDSCT2 forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h deleted file mode 100644 index b5545da1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h +++ /dev/null @@ -1,309 +0,0 @@ -/** - * @file dct_lee_cpu.h - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -#ifndef DREAMPLACE_DCT_LEE_CPU_H -#define DREAMPLACE_DCT_LEE_CPU_H - -#include -#include -#include -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -namespace lee { - -constexpr double PI = 3.14159265358979323846; - -/// Return true if a number is power of 2 -template -inline bool isPowerOf2(T val) { - return val && (val & (val - 1)) == 0; -} - -/// Transpose a row-major matrix with M rows and N columns using block transpose -/// method -template -inline void transpose(const TValue *in, TValue *out, TIndex M, TIndex N, - TIndex blockSize = 16) { - //#pragma omp parallel for collapse(2) schedule(static) - for (TIndex j = 0; j < N; j += blockSize) { - for (TIndex i = 0; i < M; i += blockSize) { - // Transpose the block beginning at [i, j] - TIndex xend = std::min(M, i + blockSize); - TIndex yend = std::min(N, j + blockSize); - for (TIndex y = j; y < yend; ++y) { - for (TIndex x = i; x < xend; ++x) { - out[x + y * M] = in[y + x * N]; - } - } - } - } -} - -/// Negate values in odd position of a vector -template -inline void negateOddEntries(TValue *vec, TIndex N, int num_threads) { -#pragma omp parallel for num_threads(num_threads) - for (TIndex i = 1; i < N; i += 2) { - vec[i] = -vec[i]; - } -} - -/// Precompute cosine values needed for N-point dct -/// @param cos size N - 1 buffer, contains the result after function call -/// @param N the length of target dct, must be power of 2 -template -void precompute_dct_cos(TValue *cos, TIndex N) { - // The input length must be power of 2 - if (!isPowerOf2(N)) { - throw std::domain_error("Input length is not power of 2."); - } - - TIndex offset = 0; - TIndex halfLen = N / 2; - while (halfLen) { - TValue phaseStep = 0.5 * PI / halfLen; - TValue phase = 0.5 * phaseStep; - for (TIndex i = 0; i < halfLen; ++i) { - cos[offset + i] = 0.5 / std::cos(phase); - phase += phaseStep; - } - offset += halfLen; - halfLen /= 2; - } -} - -/// Precompute cosine values needed for N-point idct -/// @param cos size N - 1 buffer, contains the result after function call -/// @param N the length of target idct, must be power of 2 -template -void precompute_idct_cos(TValue *cos, TIndex N) { - // The input length must be power of 2 - if (!isPowerOf2(N)) { - throw std::domain_error("Input length is not power of 2."); - } - - TIndex offset = 0; - TIndex halfLen = 1; - while (halfLen < N) { - TValue phaseStep = 0.5 * PI / halfLen; - TValue phase = 0.5 * phaseStep; - for (TIndex i = 0; i < halfLen; ++i) { - cos[offset + i] = 0.5 / std::cos(phase); - phase += phaseStep; - } - offset += halfLen; - halfLen *= 2; - } -} - -/// The implementation of fast Discrete Cosine Transform (DCT) algorithm and its -/// inverse (IDCT) are Lee's algorithms Algorithm reference: A New Algorithm to -/// Compute the Discrete Cosine Transform, by Byeong Gi Lee, 1984 -/// -/// Lee's algorithm has a recursive structure in nature. -/// Here is a sample recursive implementation: -/// https://www.nayuki.io/page/fast-discrete-cosine-transform-algorithms -/// -/// My implementation here is iterative, which is more efficient than the -/// recursive version. Here is a sample iterative implementation: -/// https://www.codeproject.com/Articles/151043/Iterative-Fast-1D-Forvard-DCT - -/// Compute y[k] = sum_n=0..N-1 (x[n] * cos((n + 0.5) * k * PI / N)), for k = -/// 0..N-1 -/// -/// @param vec length N sequence to be transformed -/// @param temp length 2 * N helping buffer -/// @param cos length N - 1, stores cosine values precomputed by function -/// 'precompute_dct_cos' -/// @param N length of vec, must be power of 2 -template -inline void dct(TValue *vec, TValue *out, TValue *buf, const TValue *cos, - TIndex N) { - // The input length must be power of 2 - if (!isPowerOf2(N)) { - throw std::domain_error("Input length is not power of 2."); - } - - // Pointers point to the beginning indices of two adjacent iterations - TValue *curr = out; - TValue *next = buf; - - // 'temp' is used to store data of two adjacent iterations - // Copy 'vec' to the first N element in 'temp' - std::copy(vec, vec + N, curr); - - // Current bufferfly length and half length - TIndex len = N; - TIndex halfLen = len / 2; - - // Iteratively bi-partition sequences into sub-sequences - TIndex cosOffset = 0; - while (halfLen) { - TIndex offset = 0; - TIndex steps = N / len; - for (TIndex k = 0; k < steps; ++k) { - for (TIndex i = 0; i < halfLen; ++i) { - next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; - next[offset + halfLen + i] = - (curr[offset + i] - curr[offset + len - i - 1]) * - cos[cosOffset + i]; - } - offset += len; - } - std::swap(curr, next); - cosOffset += halfLen; - len = halfLen; - halfLen /= 2; - } - - // Bottom-up form the final DCT solution - // Note that the case len = 2 will do nothing, so we start from len = 4 - len = 4; - halfLen = 2; - while (halfLen < N) { - TIndex offset = 0; - TIndex steps = N / len; - for (TIndex k = 0; k < steps; ++k) { - for (TIndex i = 0; i < halfLen - 1; ++i) { - next[offset + i * 2] = curr[offset + i]; - next[offset + i * 2 + 1] = - curr[offset + halfLen + i] + curr[offset + halfLen + i + 1]; - } - next[offset + len - 2] = curr[offset + halfLen - 1]; - next[offset + len - 1] = curr[offset + len - 1]; - offset += len; - } - std::swap(curr, next); - halfLen = len; - len *= 2; - } - - // Populate the final results into 'out' - if (curr != out) { - std::copy(curr, curr + N, out); - } -} - -/// Compute y[k] = 0.5 * x[0] + sum_n=1..N-1 (x[n] * cos(n * (k + 0.5) * PI / -/// N)), for k = 0..N-1 -/// @param vec length N sequence to be transformed -/// @param temp length 2 * N helping buffer -/// @param cos length N - 1, stores cosine values precomputed by function -/// 'precompute_idct_cos' -/// @param N length of vec, must be power of 2 -template -inline void idct(TValue *vec, TValue *out, TValue *buf, const TValue *cos, - TIndex N) { - // The input length must be power of 2 - if (!isPowerOf2(N)) { - throw std::domain_error("Input length is not power of 2."); - } - - // Pointers point to the beginning indices of two adjacent iterations - TValue *curr = out; - TValue *next = buf; - - // This array is used to store date of two adjacent iterations - // Copy 'vec' to the first N element in 'temp' - std::copy(vec, vec + N, curr); - curr[0] /= 2; - - // Current bufferfly length and half length - TIndex len = N; - TIndex halfLen = len / 2; - - // Iteratively bi-partition sequences into sub-sequences - while (halfLen) { - TIndex offset = 0; - TIndex steps = N / len; - for (TIndex k = 0; k < steps; ++k) { - next[offset] = curr[offset]; - next[offset + halfLen] = curr[offset + 1]; - for (TIndex i = 1; i < halfLen; ++i) { - next[offset + i] = curr[offset + i * 2]; - next[offset + halfLen + i] = - curr[offset + i * 2 - 1] + curr[offset + i * 2 + 1]; - } - offset += len; - } - std::swap(curr, next); - len = halfLen; - halfLen /= 2; - } - - // Bottom-up form the final IDCT solution - len = 2; - halfLen = 1; - TIndex cosOffset = 0; - while (halfLen < N) { - TIndex offset = 0; - TIndex steps = N / len; - for (TIndex k = 0; k < steps; ++k) { - for (TIndex i = 0; i < halfLen; ++i) { - TValue g = curr[offset + i]; - TValue h = curr[offset + halfLen + i] * cos[cosOffset + i]; - next[offset + i] = g + h; - next[offset + len - 1 - i] = g - h; - } - offset += len; - } - std::swap(curr, next); - cosOffset += halfLen; - halfLen = len; - len *= 2; - } - - // Populate the final results into 'out' - if (curr != out) { - std::copy(curr, curr + N, out); - } -} - -/// Compute batch dct -/// @param mtx size M * N row-major matrix to be transformed -/// @param temp length 3 * M * N helping buffer, first 2 * M * N is for dct, -/// the last M * N is for matrix transpose -/// @param cosM length M - 1, stores cosine values precomputed by function -/// 'precompute_dct_cos' for M-point dct -/// @param cosN length N - 1, stores cosine values precomputed by function -/// 'precompute_dct_cos' for N-point dct -/// @param M number of rows -/// @param N number of columns -template -inline void dct(TValue *mtx, TValue *out, TValue *buf, const TValue *cos, - TIndex M, TIndex N, int num_threads) { -#pragma omp parallel for num_threads(num_threads) schedule(static) - for (TIndex i = 0; i < M; ++i) { - dct(mtx + i * N, out + i * N, buf + i * N, cos, N); - } -} - -/// Compute batch idct -/// @param mtx size M * N row-major matrix to be transformed -/// @param temp length 3 * M * N helping buffer, first 2 * M * N is for dct, -/// the last M * N is for matrix transpose -/// @param cosM length M - 1, stores cosine values precomputed by function -/// 'precompute_dct_cos' for M-point dct -/// @param cosN length N - 1, stores cosine values precomputed by function -/// 'precompute_dct_cos' for N-point dct -/// @param M number of rows -/// @param N number of columns -template -inline void idct(TValue *mtx, TValue *out, TValue *buf, const TValue *cos, - TIndex M, TIndex N, int num_threads) { -#pragma omp parallel for num_threads(num_threads) schedule(static) - for (TIndex i = 0; i < M; ++i) { - idct(mtx + i * N, out + i * N, buf + i * N, cos, N); - } -} - -} // End of namespace lee - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp deleted file mode 100644 index 4081115b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/** - * @file dct_lee_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -#include "dct_cuda.h" -#include "dct_lee_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -void dct_lee_precompute_dct_cos(int N, at::Tensor out) { - out.resize_(N); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - out, "dct_lee_precompute_dct_cos", [&] { - lee::precompute_dct_cos( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); - }); -} - -void dct_lee_precompute_idct_cos(int N, at::Tensor out) { - out.resize_(N); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - out, "dct_lee_precompute_idct_cos", [&] { - lee::precompute_idct_cos( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), N); - }); -} - -inline void dct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos); - CHECK_CONTIGUOUS(cos); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dct_lee_forward", [&] { - lee::dct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N); - }); - - out.mul_(2.0 / N); -} - -inline void idct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos); - CHECK_CONTIGUOUS(cos); - - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idct_lee_forward", [&] { - lee::idct(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(cos, scalar_t), M, N); - }); - - out.mul_(2); -} - -void dst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, - at::Tensor out) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - buf.copy_(x); - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t), M, N); - - dct_lee_forward(buf, expk, buf, out); - // std::cout << "y\n" << y << "\n"; - - computeFlipCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); - }); - - out.copy_(buf); -} - -void idst_lee_forward(at::Tensor x, at::Tensor expk, at::Tensor buf, - at::Tensor out) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - computeFlipCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); - - idct_lee_forward(buf, expk, buf, out); - // std::cout << "y\n" << y << "\n"; - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - // std::cout << "z\n" << y << "\n"; - }); -} - -void dct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf, at::Tensor out) { - // CHECK_CUDA(x); - // CHECK_CONTIGUOUS(x); - // CHECK_CUDA(cos0); - // CHECK_CONTIGUOUS(cos0); - // CHECK_CUDA(cos1); - // CHECK_CONTIGUOUS(cos1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - - out.resize_({M, N}); - buf.resize_({M, N}); - - dct_lee_forward(x, cos1, out, buf); - - // 1D DCT to rows - out.resize_({N, M}); - out.copy_(buf.transpose(-2, -1)); - buf.resize_({N, M}); - - dct_lee_forward(out, cos0, out, buf); - - out.resize_({M, N}); - out.copy_(buf.transpose(-2, -1)); -} - -void idct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf, at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CUDA(cos1); - CHECK_CONTIGUOUS(cos1); - - // 1D DCT to columns - - // std::cout << "x\n" << x << "\n"; - auto N = x.size(-1); - auto M = x.numel() / N; - - out.resize_({M, N}); - buf.resize_({M, N}); - - idct_lee_forward(x, cos1, out, buf); - - // 1D DCT to rows - out.resize_({N, M}); - out.copy_(buf.transpose(-2, -1)); - buf.resize_({N, M}); - - idct_lee_forward(out, cos0, out, buf); - - out.resize_({M, N}); - out.copy_(buf.transpose(-2, -1)); -} - -void idxct_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_lee_forward", [&] { - idct_lee_forward(x, cos, buf, out); - - // std::cout << __func__ << " z\n" << z << "\n"; - - addX0AndScaleCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)); - }); -} - -void idxst_lee_forward(at::Tensor x, at::Tensor cos, at::Tensor buf, - at::Tensor out) { - auto N = x.size(-1); - auto M = x.numel() / N; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_lee_forward", [&] { - // std::cout << "x\n" << x << "\n"; - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(buf, scalar_t)); - - idct_lee_forward(buf, cos, buf, out); - out.mul_(0.5); - // std::cout << "y\n" << y << "\n"; - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), M, N); - // std::cout << "z\n" << y << "\n"; - }); -} - -void idcct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CUDA(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxct for rows - - idxct_lee_forward(x, cos1, buf0, out); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxct_lee_forward(buf0, cos0, out, buf1); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -void idcst2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CUDA(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxst for rows - - idxst_lee_forward(x, cos1, buf0, out); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxct_lee_forward(buf0, cos0, out, buf1); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -void idsct2_lee_forward(at::Tensor x, at::Tensor cos0, at::Tensor cos1, - at::Tensor buf0, at::Tensor buf1, at::Tensor out) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(cos0); - CHECK_CONTIGUOUS(cos0); - CHECK_CUDA(cos1); - CHECK_CONTIGUOUS(cos1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // two buffers are required to keep make sure no additional allocation of - // memory - - // idxst for rows - - idxct_lee_forward(x, cos1, buf0, out); - - // idxct for columns - - buf0.resize_({N, M}); - buf0.copy_(out.transpose(-2, -1)); - buf1.resize_({N, M}); - out.resize_({N, M}); - - idxst_lee_forward(buf0, cos0, out, buf1); - - out.resize_({M, N}); - out.copy_(buf1.transpose(-2, -1)); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("precompute_dct_cos", &DREAMPLACE_NAMESPACE::dct_lee_precompute_dct_cos, - "Precompute DCT cosine"); - m.def("precompute_idct_cos", - &DREAMPLACE_NAMESPACE::dct_lee_precompute_idct_cos, - "Precompute IDCT cosine"); - m.def("dct", &DREAMPLACE_NAMESPACE::dct_lee_forward, "DCT forward"); - m.def("idct", &DREAMPLACE_NAMESPACE::idct_lee_forward, "IDCT forward"); - m.def("idxct", &DREAMPLACE_NAMESPACE::idxct_lee_forward, "IDXCT forward"); - m.def("idxst", &DREAMPLACE_NAMESPACE::idxst_lee_forward, "IDXST forward"); - - m.def("dst", &DREAMPLACE_NAMESPACE::dst_lee_forward, "DST forward"); - m.def("idst", &DREAMPLACE_NAMESPACE::idst_lee_forward, "IDST forward"); - - m.def("dct2", &DREAMPLACE_NAMESPACE::dct2_lee_forward, "DCT2 forward"); - m.def("idct2", &DREAMPLACE_NAMESPACE::idct2_lee_forward, "IDCT2 forward"); - m.def("idcct2", &DREAMPLACE_NAMESPACE::idcct2_lee_forward, "IDCCT2 forward"); - m.def("idcst2", &DREAMPLACE_NAMESPACE::idcst2_lee_forward, "IDCST2 forward"); - m.def("idsct2", &DREAMPLACE_NAMESPACE::idsct2_lee_forward, "IDSCT2 forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h deleted file mode 100644 index c3f80d25..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file dct_lee_cuda.h - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -#ifndef DREAMPLACE_DCT_LEE_CUDA_H -#define DREAMPLACE_DCT_LEE_CUDA_H - -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -namespace lee { - -template -void precompute_dct_cos(TValue *cos, int N); - -template -void precompute_idct_cos(TValue *cos, int N); - -template -void dct(const TValue *vec, TValue *curr, TValue* next, const TValue *cos, int M, int N); - -template -void idct(const TValue *vec, TValue *curr, TValue *next, const TValue *cos, int M, int N); - -} // End of namespace lee - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu deleted file mode 100644 index 4996e9de..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu +++ /dev/null @@ -1,294 +0,0 @@ -/** - * @file dct_lee_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -//#include -//#include -#include -#include -#include -#include -#include "cuda_runtime.h" - -//#include "dct_lee_cuda.h" -#include "dct_lee_cuda_kernel.h" - -DREAMPLACE_BEGIN_NAMESPACE - -namespace lee -{ - -constexpr double PI = 3.14159265358979323846; - -/// Return true if a number is power of 2 -template -inline bool isPowerOf2(T val) -{ - return val && (val & (val - 1)) == 0; -} - -template -inline void swap(T& x, T& y) -{ - T tmp = x; - x = y; - y = tmp; -} - -/// Precompute cosine values needed for N-point dct -/// @param cos size N - 1 buffer on GPU, contains the result after function call -/// @param N the length of target dct, must be power of 2 -template -void precompute_dct_cos(TValue *cos, int N) -{ - // The input length must be power of 2 - if (! isPowerOf2(N)) - { - printf("Input length is not power of 2.\n"); - assert(0); - } - - // create the array on host - TValue* cos_host = new TValue [N]; - - int offset = 0; - int halfLen = N / 2; - while (halfLen) - { - TValue phaseStep = 0.5 * PI / halfLen; - TValue phase = 0.5 * phaseStep; - for (int i = 0; i < halfLen; ++i) - { - cos_host[offset + i] = 0.5 / std::cos(phase); - phase += phaseStep; - } - offset += halfLen; - halfLen /= 2; - } - - // copy to GPU - cudaMemcpy(cos, cos_host, N*sizeof(TValue), cudaMemcpyHostToDevice); - - delete [] cos_host; -} - -/// Precompute cosine values needed for N-point idct -/// @param cos size N - 1 buffer on GPU, contains the result after function call -/// @param N the length of target idct, must be power of 2 -template -void precompute_idct_cos(TValue *cos, int N) -{ - // The input length must be power of 2 - if (! isPowerOf2(N)) - { - printf("Input length is not power of 2.\n"); - assert(0); - } - - // create the array on host - TValue* cos_host = new TValue [N]; - - int offset = 0; - int halfLen = 1; - while(halfLen < N) - { - TValue phaseStep = 0.5 * PI / halfLen; - TValue phase = 0.5 * phaseStep; - for (int i = 0; i < halfLen; ++i) - { - cos_host[offset + i] = 0.5 / std::cos(phase); - phase += phaseStep; - } - offset += halfLen; - halfLen *= 2; - } - - // copy to GPU - cudaMemcpy(cos, cos_host, N*sizeof(TValue), cudaMemcpyHostToDevice); - - delete [] cos_host; -} - -/// The implementation of fast Discrete Cosine Transform (DCT) algorithm and its inverse (IDCT) are Lee's algorithms -/// Algorithm reference: A New Algorithm to Compute the Discrete Cosine Transform, by Byeong Gi Lee, 1984 -/// -/// Lee's algorithm has a recursive structure in nature. -/// Here is a sample recursive implementation: https://www.nayuki.io/page/fast-discrete-cosine-transform-algorithms -/// -/// My implementation here is iterative, which is more efficient than the recursive version. -/// Here is a sample iterative implementation: https://www.codeproject.com/Articles/151043/Iterative-Fast-1D-Forvard-DCT - -/// Compute y[k] = sum_n=0..N-1 (x[n] * cos((n + 0.5) * k * PI / N)), for k = 0..N-1 -/// -/// @param vec length M * N sequence to be transformed in last dimension -/// @param out length M * N helping buffer, which is also the output -/// @param buf length M * N helping buffer -/// @param cos length N - 1, stores cosine values precomputed by function 'precompute_dct_cos' -/// @param M length of dimension 0 of vec -/// @param N length of dimension 1 of vec, must be power of 2 -template -void dct(const TValue *vec, TValue *out, TValue* buf, const TValue *cos, int M, int N) -{ - int block_count = 2048; - int thread_count = 512; - - // The input length must be power of 2 - if (! isPowerOf2(N)) - { - printf("Input length is not power of 2.\n"); - assert(0); - } - - // Pointers point to the beginning indices of two adjacent iterations - TValue *curr = buf; - TValue *next = out; - - // 'temp' used to store date of two adjacent iterations - // Copy 'vec' to the first N element in 'temp' - cudaMemcpy(curr, vec, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); - - // Current bufferfly length and half length - int len = N; - int halfLen = len / 2; - - // Iteratively bi-partition sequences into sub-sequences - int cosOffset = 0; - while (halfLen) - { - computeDctForward<<>>(curr, next, cos, M, N, len, halfLen, cosOffset); - swap(curr, next); - cosOffset += halfLen; - len = halfLen; - halfLen /= 2; - } - - // Bottom-up form the final DCT solution - // Note that the case len = 2 will do nothing, so we start from len = 4 - len = 4; - halfLen = 2; - while (halfLen < N) - { - computeDctBackward<<>>(curr, next, M, N, len, halfLen); - swap(curr, next); - halfLen = len; - len *= 2; - } - - // Populate the final results into 'out' - if (curr != out) - { - cudaMemcpy(out, curr, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); - } -} - -/// Compute y[k] = 0.5 * x[0] + sum_n=1..N-1 (x[n] * cos(n * (k + 0.5) * PI / N)), for k = 0..N-1 -/// @param vec length M * N sequence to be transformed -/// @param out length M * N helping buffer, which is also the output -/// @param buf length M * N helping buffer -/// @param cos length N - 1, stores cosine values precomputed by function 'precompute_idct_cos' -/// @param M length of dimension 0 of vec -/// @param N length of dimension 1 of vec, must be power of 2 -template -void idct(const TValue *vec, TValue *out, TValue *buf, const TValue *cos, int M, int N) -{ - int block_count = 32; - int thread_count = 1024; - - // The input length must be power of 2 - if (! isPowerOf2(N)) - { - printf("Input length is not power of 2.\n"); - assert(0); - } - - // Pointers point to the beginning indices of two adjacent iterations - TValue *curr = buf; - TValue *next = out; - - // This array is used to store date of two adjacent iterations - // Copy 'vec' to the first N element in 'temp' - cudaMemcpy(curr, vec, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); - computeIdctScale0<<>>(curr, M, N); - - // Current bufferfly length and half length - int len = N; - int halfLen = len / 2; - - // Iteratively bi-partition sequences into sub-sequences - while (halfLen) - { - computeIdctForward<<>>(curr, next, M, N, len, halfLen); - swap(curr, next); - len = halfLen; - halfLen /= 2; - } - - // Bottom-up form the final IDCT solution - len = 2; - halfLen = 1; - int cosOffset = 0; - while(halfLen < N) - { - ComputeIdctBackward<<>>(curr, next, cos, M, N, len, halfLen, cosOffset); - swap(curr, next); - cosOffset += halfLen; - halfLen = len; - len *= 2; - } - - // Populate the final results into 'out' - if (curr != out) - { - cudaMemcpy(out, curr, M*N*sizeof(TValue), cudaMemcpyDeviceToDevice); - } -} - -} // End of namespace lee - -#define REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(type) \ - template void lee::precompute_dct_cos(\ - type* cos, \ - int N \ - ); - -REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(float); -REGISTER_DCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(double); - -#define REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(type) \ - template void lee::precompute_idct_cos(\ - type* cos, \ - int N \ - ); - -REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(float); -REGISTER_IDCT_PRECOMPUTE_COS_KERNEL_LAUNCHER(double); - -#define REGISTER_DCT_KERNEL_LAUNCHER(type) \ - template void lee::dct(\ - const type* vec, \ - type* curr, \ - type* next, \ - const type* cos, \ - int M, \ - int N \ - ); - -REGISTER_DCT_KERNEL_LAUNCHER(float); -REGISTER_DCT_KERNEL_LAUNCHER(double); - -#define REGISTER_IDCT_KERNEL_LAUNCHER(type) \ - template void lee::idct(\ - const type* vec, \ - type* curr, \ - type* next, \ - const type* cos, \ - int M, \ - int N \ - ); - -REGISTER_IDCT_KERNEL_LAUNCHER(float); -REGISTER_IDCT_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h deleted file mode 100644 index f218d5b9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file dct_lee_cuda_kernel.h - * @author Yibo Lin (DREAMPlace) - * @date Oct 2018 - */ - -#ifndef DREAMPLACE_DCT_LEE_CUDA_KERNEL_H -#define DREAMPLACE_DCT_LEE_CUDA_KERNEL_H - -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -namespace lee { - -template -__global__ void computeDctForward(const TValue* curr, TValue* next, - const TValue* cos, TIndex M, TIndex N, - TIndex len, TIndex halfLen, - TIndex cosOffset) { - // for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < - // M*N/2; thread_id += blockDim.x * gridDim.x) - //{ - // TIndex halfN = N/2; - // TIndex batch_id = thread_id / halfN; - // TIndex rest = thread_id - batch_id*halfN; - // TIndex k = rest / halfLen; - // TIndex i = rest - k*halfLen; - // TIndex batch_offset = batch_id*N; - // TIndex offset = batch_offset + k*len; - - // next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; - // next[offset + halfLen + i] = (curr[offset + i] - curr[offset + len -i - - // 1]) * cos[cosOffset + i]; - //} - TIndex halfN = (N >> 1); - TIndex halfMN = M * halfN; - // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; - // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; - thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { - TIndex rest = thread_id & (halfN - 1); - TIndex i = rest & (halfLen - 1); - TIndex offset = (thread_id - i) * 2; - - next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; - // next[offset + i + halfLen] = (curr[offset + i] - curr[offset + len - i - - // 1]) * cos[cosOffset + i]; - } - // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; - // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; - thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { - TIndex rest = thread_id & (halfN - 1); - TIndex i = rest & (halfLen - 1); - TIndex offset = (thread_id - i) * 2; - - // next[offset + i] = curr[offset + i] + curr[offset + len - i - 1]; - next[offset + i + halfLen] = - (curr[offset + i] - curr[offset + len - i - 1]) * cos[cosOffset + i]; - } -} - -template -__global__ void computeDctBackward(const TValue* curr, TValue* next, TIndex M, - TIndex N, TIndex len, TIndex halfLen) { - // for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < - // M*N/2; thread_id += blockDim.x * gridDim.x) - //{ - // TIndex halfN = N/2; - // TIndex batch_id = thread_id / halfN; - // TIndex rest = thread_id - batch_id*halfN; - // TIndex k = rest / halfLen; - // TIndex i = rest - k*halfLen; - // TIndex batch_offset = batch_id*N; - // TIndex offset = batch_offset + k*len; - - // if (i+1 == halfLen) - // { - // next[offset + len - 2] = curr[offset + halfLen - 1]; - // next[offset + len - 1] = curr[offset + len - 1]; - // } - // else - // { - // next[offset + i * 2] = curr[offset + i]; - // next[offset + i * 2 + 1] = curr[offset + halfLen + i] + curr[offset - // + halfLen + i + 1]; - // } - // //next[offset + i] = (i&1)? curr[offset + halfLen + i/2] + curr[offset + - // halfLen + i/2 + 1*(i+1 < len)]*(i+1 < len) : curr[offset + i/2]; - //} - TIndex halfN = (N >> 1); - TIndex halfMN = M * halfN; - // TIndex halfMN_by_gridDim = halfMN/gridDim.x; - // for (TIndex thread_id = halfMN_by_gridDim*blockIdx.x + threadIdx.x; - // thread_id < halfMN_by_gridDim*(blockIdx.x+1); thread_id += blockDim.x) - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; - thread_id < halfMN; thread_id += blockDim.x * gridDim.x) { - TIndex rest = thread_id & (halfN - 1); - TIndex i = rest & (halfLen - 1); - TIndex offset = (thread_id - i) * 2; - - next[offset + i * 2] = curr[offset + i]; - next[offset + i * 2 + 1] = - (i + 1 == halfLen) - ? curr[offset + len - 1] - : curr[offset + halfLen + i] + curr[offset + halfLen + i + 1]; - } -} - -template -__global__ void computeIdctScale0(TValue* curr, TIndex M, TIndex N) { - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; thread_id < M; - thread_id += blockDim.x * gridDim.x) { - curr[thread_id * N] *= 0.5; - } -} - -template -__global__ void computeIdctForward(const TValue* curr, TValue* next, TIndex M, - TIndex N, TIndex len, TIndex halfLen) { - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; - thread_id < M * N / 2; thread_id += blockDim.x * gridDim.x) { - TIndex halfN = N / 2; - TIndex batch_id = thread_id / halfN; - TIndex rest = thread_id - batch_id * halfN; - TIndex k = rest / halfLen; - TIndex i = rest - k * halfLen; - TIndex batch_offset = batch_id * N; - TIndex offset = batch_offset + k * len; - - if (i == 0) { - next[offset] = curr[offset]; - next[offset + halfLen] = curr[offset + 1]; - } else { - next[offset + i] = curr[offset + i * 2]; - next[offset + halfLen + i] = - curr[offset + i * 2 - 1] + curr[offset + i * 2 + 1]; - } - } -} - -template -__global__ void ComputeIdctBackward(const TValue* curr, TValue* next, - const TValue* cos, TIndex M, TIndex N, - TIndex len, TIndex halfLen, - TIndex cosOffset) { - for (TIndex thread_id = blockIdx.x * blockDim.x + threadIdx.x; - thread_id < M * N / 2; thread_id += blockDim.x * gridDim.x) { - TIndex halfN = N / 2; - TIndex batch_id = thread_id / halfN; - TIndex rest = thread_id - batch_id * halfN; - TIndex k = rest / halfLen; - TIndex i = rest - k * halfLen; - TIndex batch_offset = batch_id * N; - TIndex offset = batch_offset + k * len; - - TValue g = curr[offset + i]; - TValue h = curr[offset + halfLen + i] * cos[cosOffset + i]; - next[offset + i] = g + h; - next[offset + len - 1 - i] = g - h; - } -} - -} // End of namespace lee - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp deleted file mode 100644 index 2ed7a0a1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file dst.cpp - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#include "dct.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dst_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ) -{ - auto N = x.size(-1); - auto M = x.numel()/N; - - //std::cout << "x\n" << x << "\n"; - auto x_reorder = x.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_forward", [&] { - negateOddEntries( - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - M, - N, - num_threads - ); - - auto y = dct_forward(x_reorder, expk, num_threads); - //std::cout << "y\n" << y << "\n"; - - computeFlip( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, - N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads - ); - //std::cout << "z\n" << y << "\n"; - }); - - return x_reorder; -} - -at::Tensor idst_forward( - at::Tensor x, - at::Tensor expk, - int num_threads - ) -{ - auto N = x.size(-1); - auto M = x.numel()/N; - - //std::cout << "x\n" << x << "\n"; - auto x_reorder = at::empty_like(x); - auto y = at::empty_like(x); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_forward", [&] { - computeFlip( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - M, - N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - num_threads - ); - - y = idct_forward(x_reorder, expk, num_threads); - //std::cout << "y\n" << y << "\n"; - - negateOddEntries( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, - N, - num_threads - ); - //std::cout << "z\n" << y << "\n"; - }); - - return y; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp deleted file mode 100644 index 80a3c195..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @file dst_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#include "dct_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor dst_forward( - at::Tensor x, - at::Tensor expk) -{ - auto N = x.size(-1); - auto M = x.numel()/N; - - //std::cout << "x\n" << x << "\n"; - auto x_reorder = x.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "dst_forward", [&] { - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), - M, - N - ); - - auto y = dct_forward(x_reorder, expk); - //std::cout << "y\n" << y << "\n"; - - computeFlipCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, - N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t) - ); - //std::cout << "z\n" << y << "\n"; - }); - - return x_reorder; -} - -at::Tensor idst_forward( - at::Tensor x, - at::Tensor expk) -{ - auto N = x.size(-1); - auto M = x.numel()/N; - - //std::cout << "x\n" << x << "\n"; - auto x_reorder = at::empty_like(x); - auto y = at::empty_like(x); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idst_forward", [&] { - computeFlipCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - M, - N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t) - ); - - y = idct_forward(x_reorder, expk); - //std::cout << "y\n" << y << "\n"; - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, - N - ); - //std::cout << "z\n" << y << "\n"; - }); - - return y; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu deleted file mode 100644 index b6ef72ee..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - - -template -__global__ void computeFlip( - const T* x, - const int M, - const int N, - T* y - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int ii = i%N; - y[i] = x[i+N-ii*2-1]; - } -} - -template -void computeFlipCudaLauncher( - const T* x, - const int M, - const int N, - T* y - ) -{ - computeFlip<<<32, 1024>>>( - x, - M, - N, - y - ); -} - -template -__global__ void computeFlipAndShift( - const T* x, - const int M, - const int N, - T* y - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*N; i += blockDim.x * gridDim.x) - { - int ii = i%N; - y[i] = (ii)? x[i+N-ii*2] : 0; - } -} - -template -void computeFlipAndShiftCudaLauncher( - const T* x, - const int M, - const int N, - T* y - ) -{ - computeFlipAndShift<<<32, 1024>>>( - x, - M, - N, - y - ); -} - -template -__global__ void negateOddEntries( - T* x, - const int M, - const int N - ) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M*(N>>1); i += blockDim.x * gridDim.x) - { - x[i*2+1] = -x[i*2+1]; - } -} - -template -void negateOddEntriesCudaLauncher( - T* x, - const int M, - const int N - ) -{ - negateOddEntries<<<32, 1024>>>( - x, - M, - N - ); -} - -#define REGISTER_FLIP_KERNEL_LAUNCHER(type) \ - template void computeFlipCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* y \ - ); - -REGISTER_FLIP_KERNEL_LAUNCHER(float); -REGISTER_FLIP_KERNEL_LAUNCHER(double); - -#define REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(type) \ - template void computeFlipAndShiftCudaLauncher(\ - const type* x, \ - const int M, \ - const int N, \ - type* y \ - ); - -REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(float); -REGISTER_FLIPANDSHIFT_KERNEL_LAUNCHER(double); - -#define REGISTER_NEGATE_KERNEL_LAUNCHER(type) \ - template void negateOddEntriesCudaLauncher(\ - type* x, \ - const int M, \ - const int N \ - ); - -REGISTER_NEGATE_KERNEL_LAUNCHER(float); -REGISTER_NEGATE_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp deleted file mode 100644 index 413bbef4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/** - * @file dxt.cpp - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#include "dct.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor idxct_forward(at::Tensor x, at::Tensor expk, int num_threads) { - auto N = x.size(-1); - auto M = x.numel() / N; - - auto z = idct_forward(x, expk, num_threads); - - // std::cout << __func__ << " z\n" << z << "\n"; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_forward", [&] { - addX0AndScale(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - }); - - return z; -} - -at::Tensor idxst_forward(at::Tensor x, at::Tensor expk, int num_threads) { - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // auto x_reorder = at::empty_like(x); - auto x_reorder = at::empty({M, N}, x.options()); - auto y = at::empty_like(x); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_forward", [&] { - computeFlipAndShift( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t), num_threads); - - y = idct_forward(x_reorder, expk, num_threads); - y.mul_(0.5); - // std::cout << "y\n" << y << "\n"; - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - num_threads); - // std::cout << "z\n" << y << "\n"; - }); - - return y; -} - -at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcct2_forward", [&] { - computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - v.resize_({M, N}); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), - num_threads); - - // std::cout << "z\n" << z << "\n"; - // idxct for columns - - auto xt = v.transpose(-2, -1).contiguous(); - - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << __func__ << " v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << __func__ << " y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - v.resize_({N, M}); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - // std::cout << __func__ << " z\n" << z << "\n"; - - addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), - num_threads); - - v.transpose_(-2, -1); - }); - - return v.contiguous(); -} - -at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = - at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { - computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxst for columns - - auto xt = z.transpose(-2, -1).contiguous(); - // std::cout << "x\n" << x << "\n"; - z = z.view_as(xt); - computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, - M, DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - // this is to match python implementation - // normal way should be multiply by 0.25*N - z.mul_(0.25 * M); - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M, - num_threads); - // std::cout << "z\n" << y << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxst for rows - // std::cout << "x\n" << x << "\n"; - // auto z = at::empty_like(x); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { - computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - // this is to match python implementation - // normal way should be multiply by 0.25*N - z.mul_(0.25 * N); - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N, - num_threads); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxct for columns - - auto xt = z.transpose(-2, -1).contiguous(); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - z = z.view_as(xt); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - addX0AndScaleN(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = - at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { - computeVk(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - // idxst for columns - - auto xt = z.transpose(-2, -1).contiguous(); - // std::cout << "x\n" << x << "\n"; - z = z.view_as(xt); - computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, - M, DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - // normalized to match dct2_fft2 implementation - z.mul_(0.25 * M * N); - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M, - num_threads); - // std::cout << "z\n" << y << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1, - int num_threads) { - CHECK_CPU(x); - CHECK_CONTIGUOUS(x); - CHECK_CPU(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CPU(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxst for rows - // std::cout << "x\n" << x << "\n"; - // auto z = at::empty_like(x); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { - computeFlipAndShift(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - num_threads); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - // normalized to match dct2_fft2 implementation - z.mul_(0.25 * N * M); - - negateOddEntries(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N, - num_threads); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxct for columns - - auto xt = z.transpose(-2, -1).contiguous(); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVk(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t), num_threads); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - z = z.view_as(xt); - computeReorderReverse(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), num_threads); - // std::cout << "z\n" << z << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp deleted file mode 100644 index 08736c7e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/** - * @file dxt_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Sep 2018 - */ -#include "dct_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -at::Tensor idxct_forward(at::Tensor x, at::Tensor expk) { - auto N = x.size(-1); - auto M = x.numel() / N; - - auto z = idct_forward(x, expk); - - // std::cout << "z\n" << z << "\n"; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxct_forward", [&] { - addX0AndScaleCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - }); - - return z; -} - -at::Tensor idxst_forward(at::Tensor x, at::Tensor expk) { - auto N = x.size(-1); - auto M = x.numel() / N; - - // std::cout << "x\n" << x << "\n"; - // auto x_reorder = at::empty_like(x); - auto x_reorder = at::empty({M, N}, x.options()); - auto y = at::empty_like(x); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idxst_forward", [&] { - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(x_reorder, scalar_t)); - - y = idct_forward(x_reorder, expk); - y.mul_(0.5); - // std::cout << "y\n" << y << "\n"; - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N); - // std::cout << "z\n" << y << "\n"; - }); - - return y; -} - -at::Tensor idcct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcct2_forward", [&] { - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - v.resize_({M, N}); - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - addX0AndScaleNCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "z\n" << z << "\n"; - // idxct for columns - - auto xt = v.transpose(-2, -1).contiguous(); - - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << __func__ << " v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << __func__ << " y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - v.resize_({N, M}); - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - // std::cout << __func__ << " z\n" << z << "\n"; - - addX0AndScaleNCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - v.transpose_(-2, -1); - }); - - return v.contiguous(); -} - -at::Tensor idsct2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - addX0AndScaleNCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxst for columns - - auto xt = z.transpose(-2, -1).contiguous(); - // std::cout << "x\n" << x << "\n"; - z = z.view_as(xt); - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - // this is to match python implementation - // normal way should be multiply by 0.25*N - z.mul_(0.25 * M); - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M); - // std::cout << "z\n" << y << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idcst2_forward(at::Tensor x, at::Tensor expk0, at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxst for rows - // std::cout << "x\n" << x << "\n"; - // auto z = at::empty_like(x); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - // this is to match python implementation - // normal way should be multiply by 0.25*N - z.mul_(0.25 * N); - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxct for columns - - auto xt = z.transpose(-2, -1).contiguous(); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - z = z.view_as(xt); - computeReorderReverseCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - addX0AndScaleNCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idxst_idct_forward(at::Tensor x, at::Tensor expk0, - at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxct for rows - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk is hermitian symmetric, only fill in half - auto v = - at::empty({M * N + std::max(M, N)}, x.options()).resize_({M, N / 2 + 1, 2}); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idsct2_forward", [&] { - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - // y is real now - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - // auto z = at::empty_like(x); - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - // idxst for columns - - auto xt = z.transpose(-2, -1).contiguous(); - // std::cout << "x\n" << x << "\n"; - z = z.view_as(xt); - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - // normalized to match dct2_fft2 implementation - z.mul_(0.25 * M * N); - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), N, M); - // std::cout << "z\n" << y << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -at::Tensor idct_idxst_forward(at::Tensor x, at::Tensor expk0, - at::Tensor expk1) { - CHECK_CUDA(x); - CHECK_CONTIGUOUS(x); - CHECK_CUDA(expk0); - CHECK_CONTIGUOUS(expk0); - CHECK_CUDA(expk1); - CHECK_CONTIGUOUS(expk1); - - auto N = x.size(-1); - auto M = x.numel() / N; - - // idxst for rows - // std::cout << "x\n" << x << "\n"; - // auto z = at::empty_like(x); - auto z = at::empty({M, N}, x.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(x, "idcst2_forward", [&] { - computeFlipAndShiftCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(x, scalar_t), M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - auto v = at::empty({M * N + std::max(M, N)}, x.options()) - .resize_({M, N / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk1, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - auto y = at::irfft(v, 1, false, true, {N}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - M, N, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - // normalized to match dct2_fft2 implementation - z.mul_(0.25 * N * M); - - negateOddEntriesCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t), M, N); - // std::cout << __func__ << " z\n" << z << "\n"; - - // idxct for columns - - auto xt = z.transpose(-2, -1).contiguous(); - - // std::cout << "x\n" << x << "\n"; - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - // vk = 0.5*W_{4N}^{k} (c[k] - c[N-k]) - v.resize_({N, M / 2 + 1, 2}); - computeVkCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(xt, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(expk0, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(v, scalar_t)); - - // std::cout << "v\n" << v << "\n"; - - y = at::irfft(v, 1, false, true, {M}); - - // std::cout << "y\n" << y << "\n"; - - // std::cout << "expk\n" << expk << "\n"; - z = z.view_as(xt); - computeReorderReverseCudaLauncher(DREAMPLACE_TENSOR_DATA_PTR(y, scalar_t), - N, M, - DREAMPLACE_TENSOR_DATA_PTR(z, scalar_t)); - // std::cout << "z\n" << z << "\n"; - - z.transpose_(-2, -1); - }); - - return z.contiguous(); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py deleted file mode 100644 index 4d4bf5a0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py +++ /dev/null @@ -1,148 +0,0 @@ -#* """ -#* Author: Yibo Lin (DREAMPlace) -#* Date: June 15, 2021 -#* """ - -import torch -from distutils.version import LooseVersion, StrictVersion - -def rfft(input, signal_ndim, normalized=False, onesided=True): - if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): - return torch.rfft(input, signal_ndim, normalized, onesided) - else: - if onesided: - if normalized: - if signal_ndim == 1: - y = torch.fft.rfft(input, None, -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.rfft2(input, None, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.rfftn(input, None, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized rfft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.rfft(input, None, -1, "backward") - elif signal_ndim == 2: - y = torch.fft.rfft2(input, None, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.rfftn(input, None, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized rfft() has illegal number of dimensions %s" % (signal_ndim) - else: - if normalized: - if signal_ndim == 1: - y = torch.fft.fft(input, None, -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.fft2(input, None, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.fftn(input, None, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized fft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.fft(input, None, -1, "backward") - elif signal_ndim == 2: - y = torch.fft.fft2(input, None, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.fftn(input, None, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized fft() has illegal number of dimensions %s" % (signal_ndim) - - return torch.view_as_real(y).contiguous() - -def irfft(input, signal_ndim, normalized=False, onesided=True, signal_sizes=None): - if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): - return torch.irfft(input, signal_ndim, normalized, onesided, signal_sizes) - else: - assert signal_sizes, "Parameter signal_sizes is required" - if onesided: - if normalized: - if signal_ndim == 1: - y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized irfft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "backward") - elif signal_ndim == 2: - y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized irfft() has illegal number of dimensions %s" % (signal_ndim) - else: - if normalized: - if signal_ndim == 1: - y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.irfft(torch.view_as_complex(input), signal_sizes[-1], -1, "backward") - elif signal_ndim == 2: - y = torch.fft.irfft2(torch.view_as_complex(input), signal_sizes, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.irfftn(torch.view_as_complex(input), signal_sizes, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) - assert not y.is_complex() - return y.contiguous() - -def fft(input, signal_ndim, normalized=False): - if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): - return torch.fft(input, signal_ndim, normalized) - else: - if normalized: - if signal_ndim == 1: - y = torch.fft.fft(torch.view_as_complex(input), None, -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.fft2(torch.view_as_complex(input), None, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.fftn(torch.view_as_complex(input), None, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized fft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.fft(torch.view_as_complex(input), None, -1, "backward") - elif signal_ndim == 2: - y = torch.fft.fft2(torch.view_as_complex(input), None, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.fftn(torch.view_as_complex(input), None, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized fft() has illegal number of dimensions %s" % (signal_ndim) - - return torch.view_as_real(y).contiguous() - -def ifft(input, signal_ndim, normalized=False): - if LooseVersion(torch.__version__) < LooseVersion("1.8.0"): - return torch.ifft(input, signal_ndim, normalized) - else: - if normalized: - if signal_ndim == 1: - y = torch.fft.ifft(torch.view_as_complex(input), None, -1, "ortho") - elif signal_ndim == 2: - y = torch.fft.ifft2(torch.view_as_complex(input), None, (-2, -1), "ortho") - elif signal_ndim == 3: - y = torch.fft.ifftn(torch.view_as_complex(input), None, (-3, -2, -1), "ortho") - else: - assert False, "Ortho-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) - else: - if signal_ndim == 1: - y = torch.fft.ifft(torch.view_as_complex(input), None, -1, "backward") - elif signal_ndim == 2: - y = torch.fft.ifft2(torch.view_as_complex(input), None, (-2, -1), "backward") - elif signal_ndim == 3: - y = torch.fft.ifftn(torch.view_as_complex(input), None, (-3, -2, -1), "backward") - else: - assert False, "Backward-normalized ifft() has illegal number of dimensions %s" % (signal_ndim) - - return torch.view_as_real(y).contiguous() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt deleted file mode 100644 index be394aa9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -set(OP_NAME demandMap) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(demandMap_cpp - src/demandMap.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - demandMap_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) - -add_pytorch_extension(demandMap_cuda - src/demandMap_cuda.cpp - src/demandMap_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - demandMap_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py deleted file mode 100644 index 384de405..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Nov 2020 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py deleted file mode 100644 index e3d1d0a5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py +++ /dev/null @@ -1,105 +0,0 @@ -## -# @file demandMap.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Nov 2020 -# - -import torch -from torch.autograd import Function -from torch import nn -import numpy as np -import pdb -import time - -import dreamplacefpga.ops.demandMap.demandMap_cpp as demandMap_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.demandMap.demandMap_cuda as demandMap_cuda - -class DemandMap(nn.Module): - """ - @brief Build binCapMap and fixedDemandMap - """ - def __init__(self, placedb, site_type_map, site_size_x, site_size_y, - deterministic_flag, device, num_threads): - """ - @brief initialization - @param placedb - @param site_type_map - @param site_size_x - @param site_size_y - @param deterministic_flag - @param device - @param num_threads - """ - super(DemandMap, self).__init__() - self.num_bins_x=placedb.num_bins_x - self.num_bins_y=placedb.num_bins_y - self.width=placedb.xh - placedb.xl - self.height=placedb.yh - placedb.yl - self.rsrc2compId_map=placedb.rsrc2compId_map - self.comp2rsrcId_map=placedb.comp2rsrcId_map - self.rsrc2siteMap = placedb.rsrc2siteMap - self.rsrcType2IndexMap = placedb.rsrcType2indexMap - self.siteType2IndexMap = placedb.siteType2indexMap - self.node_count=placedb.node_count - self.site_type_map=site_type_map - self.site_size_x=site_size_x - self.site_size_y=site_size_y - self.deterministic_flag = deterministic_flag - self.device=device - self.num_threads = num_threads - - def forward(self): - numSiteTypes = len(self.siteType2IndexMap)+1 - binCapMap = torch.zeros((numSiteTypes, self.num_bins_x, self.num_bins_y), dtype=self.site_size_x.dtype, device=self.device) - - binW = self.width/self.num_bins_x - binH = self.height/self.num_bins_y - - if binCapMap.is_cuda: - demandMap_cuda.forward( - self.site_type_map.flatten(), - self.site_size_x, - self.site_size_y, - self.num_bins_x, - self.num_bins_y, - self.width, - self.height, - binW, binH, - numSiteTypes, - self.num_bins_x*self.num_bins_y, - binCapMap, - self.deterministic_flag) - else: - demandMap_cpp.forward( - self.site_type_map.flatten(), - self.site_size_x, - self.site_size_y, - self.num_bins_x, - self.num_bins_y, - self.width, - self.height, - numSiteTypes, - binCapMap, - self.num_threads, - self.deterministic_flag) - - binArea = binW * binH - binCapMap = binArea - binCapMap - - rsrcDemMap = torch.zeros((len(self.rsrcType2IndexMap),self.num_bins_x,self.num_bins_y), dtype=self.site_size_x.dtype, device=self.device) - - for rsrc, rsrcId in self.rsrcType2IndexMap.items(): - compId = self.rsrc2compId_map[rsrcId] - if compId != -1: - sId = self.siteType2IndexMap[self.rsrc2siteMap[rsrc]] - rsrcDemMap[compId] = binCapMap[sId] - - out = [] - - for idx in self.rsrc2compId_map: - if idx != -1: - out.append(rsrcDemMap[idx]) - - return out diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp deleted file mode 100644 index d98088d4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/** - * @file demandMap.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Nov 2020 - * @brief Compute binCapMap - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "demandMap/src/demand_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define compute_demand_function -template -DEFINE_COMPUTE_DEMAND_FUNCTION(T); - -template -int computeDemandMapLauncher( - const int *site_type_map, - const T *site_size_x, - const T *site_size_y, - const int num_bins_x, - const int num_bins_y, - const int width, - const int height, - const int num_threads, - AtomicOp atomic_add_op, - typename AtomicOp::type* buf_map - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -#define CALL_FPGA_LAUNCHER(atomic_add_op, map_ptr) \ - computeDemandMapLauncher( \ - DREAMPLACE_TENSOR_DATA_PTR(site_type_map, int), \ - DREAMPLACE_TENSOR_DATA_PTR(site_size_x, scalar_t), \ - DREAMPLACE_TENSOR_DATA_PTR(site_size_y, scalar_t), \ - num_bins_x, num_bins_y, width, height, \ - num_threads, atomic_add_op, map_ptr) - -/// @brief Compute wirelength preconditioner -int forward( - at::Tensor site_type_map, - at::Tensor site_size_x, - at::Tensor site_size_y, - int num_bins_x, - int num_bins_y, - int width, - int height, - int num_site_types, - at::Tensor binCapMap, - int num_threads, - int deterministic_flag) -{ - CHECK_FLAT(site_type_map); - CHECK_CONTIGUOUS(site_type_map); - - CHECK_FLAT(site_size_x); - CHECK_CONTIGUOUS(site_size_x); - CHECK_FLAT(site_size_y); - CHECK_CONTIGUOUS(site_size_y); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(site_size_x, "computeDemandMapLauncher", [&] { - if (deterministic_flag == 1) - { - double diearea = width * height; - int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); - long scale_factor = (1L << fraction_bits); - int num_bins = num_site_types * num_bins_x * num_bins_y; - - std::vector buf(num_bins, 0); - AtomicAdd atomic_add_op(scale_factor); - - CALL_FPGA_LAUNCHER(atomic_add_op, buf.data()); - - scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t), - buf.data(), 1.0 / scale_factor, num_bins, num_threads); - } else - { - auto buf = DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t); - AtomicAdd atomic_add_op; - CALL_FPGA_LAUNCHER(atomic_add_op, buf); - } - - }); - return 0; -} - -template -int computeDemandMapLauncher( - const int *site_type_map, - const T *site_size_x, - const T *site_size_y, - const int num_bins_x, - const int num_bins_y, - const int width, - const int height, - const int num_threads, - AtomicOp atomic_add_op, - typename AtomicOp::type* buf_map - ) -{ - int bins_xy = num_bins_x * num_bins_y; - int num_sites = width * height; - T binW = T(width)/T(num_bins_x); - T binH = T(height)/T(num_bins_y); -#pragma omp parallel for num_threads(num_threads) - for (int s = 0; s < num_sites; ++s) - { - int site_type = site_type_map[s]; - int site_typeId = site_type*bins_xy; - int rw = int(s/height); - int cl = int(s%height); - - if (site_type > 0) - { - T nodeX = site_size_x[site_type]; - T nodeY = site_size_y[site_type]; - T col = DREAMPLACE_STD_NAMESPACE::round(cl/nodeY)*nodeY; - int iLo = int(rw/binW); - int jLo = int(col/binH); - int iHi = DREAMPLACE_STD_NAMESPACE::min(int((rw + nodeX)/binW), num_bins_x-1); - int jHi = DREAMPLACE_STD_NAMESPACE::min(int((col + nodeY)/binH), num_bins_y-1); - for (int i = iLo; i <= iHi; ++i) - { - T w = compute_demand_function(i, binW, T(rw), nodeX); - for (int j = jLo; j <= jHi; ++j) - { - T h = compute_demand_function(j, binH, col, nodeY); - T area = w * h; - int index = site_typeId + i*num_bins_y + j; - atomic_add_op(&buf_map[index], area); - } - } - } - } - return 0; -} - -#undef CALL_FPGA_LAUNCHER - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "DemandMap forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp deleted file mode 100644 index c73198f5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @file demandMap_cuda.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Nov 2020 - * @brief Compute binCapMap - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" -using namespace torch::indexing; - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeDemandMapCudaLauncher( - const int *site_type_map, - const T *site_size_x, - const T *site_size_y, - const T binW, - const T binH, - const int num_site_types, - const int num_bins_x, - const int num_bins_y, - const int width, - const int height, - const int bins_xy, - const int deterministic_flag, - T *binCapMap); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute bin capacity map -void forward( - at::Tensor site_type_map, - at::Tensor site_size_x, - at::Tensor site_size_y, - int num_bins_x, - int num_bins_y, - int width, int height, - double binW, double binH, - int num_site_types, - int bins_xy, - at::Tensor binCapMap, - int deterministic_flag) -{ - CHECK_FLAT(site_type_map); - CHECK_CONTIGUOUS(site_type_map); - - CHECK_FLAT(site_size_x); - CHECK_CONTIGUOUS(site_size_x); - CHECK_FLAT(site_size_y); - CHECK_CONTIGUOUS(site_size_y); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(site_size_x, "computeDemandMapCudaLauncher", [&] { - computeDemandMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(site_type_map, int), - DREAMPLACE_TENSOR_DATA_PTR(site_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_size_y, scalar_t), - binW, binH, num_site_types, num_bins_x, num_bins_y, - width, height, bins_xy, - deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(binCapMap, scalar_t) - ); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "DemandMap forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu deleted file mode 100644 index 2fefe592..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu +++ /dev/null @@ -1,148 +0,0 @@ -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" -// local dependency -#include "demandMap/src/demand_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define compute_demand_function -template -inline __device__ DEFINE_COMPUTE_DEMAND_FUNCTION(T); - -template -__global__ void __launch_bounds__(1024, 8) computeDemandMap( - const int *site_type_map, const T *site_size_x, const T *site_size_y, - const T binW, const T binH, const int num_bins_x, const int num_bins_y, - const int width, const int height, const int bins_xy, - AtomicOp atomicAddOp, typename AtomicOp::type *binCapMap) -{ - __shared__ int num_sites; - num_sites = width*height; - - int idx = blockIdx.x * blockDim.z + threadIdx.z; - if (idx < num_sites) - { - int site_type = site_type_map[idx]; - int site_typeId = site_type*bins_xy; - int rw = int(idx/height); - int cl = int(idx%height); - - if (site_type > 0) - { - T nodeX = site_size_x[site_type]; - T nodeY = site_size_y[site_type]; - T col = DREAMPLACE_STD_NAMESPACE::round(cl/nodeY)*nodeY; - int iLo = int(rw/binW); - int jLo = int(col/binH); - int iHi = DREAMPLACE_STD_NAMESPACE::min(int((rw + nodeX)/binW), num_bins_x-1); - int jHi = DREAMPLACE_STD_NAMESPACE::min(int((col + nodeY)/binH), num_bins_y-1); - - for (int i = iLo + threadIdx.y; i <= iHi; i += blockDim.y) - { - T w = compute_demand_function(i, binW, T(rw), nodeX); - for (int j = jLo + threadIdx.x; j <= jHi; j += blockDim.x) - { - T h = compute_demand_function(j, binH, col, nodeY); - T area = w * h; - int index = site_typeId + i*num_bins_y + j; - atomicAddOp(&binCapMap[index], area); - } - } - } - } -} - -template -int computeDemandMapCallKernel( - const int *site_type_map, const T *site_size_x, - const T *site_size_y, const T binW, const T binH, - const int num_bins_x, const int num_bins_y, - const int width, const int height, - const int bins_xy, - AtomicOp atomicAddOp, - typename AtomicOp::type *binCapMap) -{ - int thread_count = 64; - dim3 blockSize(2, 2, thread_count); - - int block_count = (width*height - 1 + thread_count) / thread_count; - - computeDemandMap<<>>( - site_type_map, site_size_x, site_size_y, binW, binH, - num_bins_x, num_bins_y, width, height, bins_xy, - atomicAddOp, binCapMap); - - return 0; -} - - -template -int computeDemandMapCudaLauncher( - const int *site_type_map, - const T *site_size_x, - const T *site_size_y, - const T binW, - const T binH, - const int num_site_types, - const int num_bins_x, - const int num_bins_y, - const int width, - const int height, - const int bins_xy, - const int deterministic_flag, - T *binCapMap) -{ - if (deterministic_flag == 1) - { - // total die area - double diearea = width * height; - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_site_types * num_bins_x * num_bins_y; - - unsigned long long int *bin_cap_map = NULL; - allocateCUDA(bin_cap_map, num_bins, unsigned long long int); - - AtomicAddCUDA atomicAddOp(scale_factor); - int thread_count = 512; - - copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, - thread_count>>>( - bin_cap_map, binCapMap, scale_factor, num_bins); - - computeDemandMapCallKernel( - site_type_map, site_size_x, site_size_y, binW, binH, - num_bins_x, num_bins_y, width, height, bins_xy, - atomicAddOp, bin_cap_map); - - copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, - thread_count>>>(binCapMap, - bin_cap_map, T(1.0 / scale_factor), num_bins); - - destroyCUDA(bin_cap_map); - } else - { - AtomicAddCUDA atomicAddOp; - - computeDemandMapCallKernel( - site_type_map, site_size_x, site_size_y, binW, binH, - num_bins_x, num_bins_y, width, height, bins_xy, - atomicAddOp, binCapMap); - } - return 0; -} - -// manually instantiate the template function -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeDemandMapCudaLauncher( \ - const int *site_type_map, const T *site_size_x, \ - const T *site_size_y, const T binW, const T binH, \ - const int num_site_types, const int num_bins_x, \ - const int num_bins_y, const int width, const int height, \ - const int bins_xy, const int deterministic_flag, T *binCapMap); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h deleted file mode 100644 index c97cdaa2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file demand_function.h - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Nov 2020 - */ - -#ifndef DREAMPLACE_DEMANDMAP_DEMAND_FUNCTION_H -#define DREAMPLACE_DEMANDMAP_DEMAND_FUNCTION_H - -DREAMPLACE_BEGIN_NAMESPACE - -// return non-negative value -#define DEFINE_COMPUTE_DEMAND_FUNCTION(type) \ - T compute_demand_function(int k, T bin_size, T siteL, T siteW) \ - { \ - T bin_k = k * bin_size; \ - T bin_kp1 = bin_k + bin_size; \ - return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(siteL + siteW, bin_kp1) - DREAMPLACE_STD_NAMESPACE::max(siteL, bin_k)); \ - } - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt deleted file mode 100644 index b87c0a0d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(OP_NAME density_map) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/density_map.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/density_map_cuda.cpp - src/density_map_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py deleted file mode 100644 index 1441687d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py +++ /dev/null @@ -1,211 +0,0 @@ -## -# @file density_map.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# @brief Compute density map -# - -import math -import torch -from torch import nn -from torch.autograd import Function - -import dreamplacefpga.ops.density_map.density_map_cpp as density_map_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.density_map.density_map_cuda as density_map_cuda - -import numpy as np -import matplotlib -matplotlib.use('Agg') -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt - -import pdb - -class DensityMapFunction(Function): - """ - @brief compute density map. - """ - @staticmethod - def forward( - pos, - node_size_x, - node_size_y, - bin_center_x, - bin_center_y, - initial_density_map, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_filler_nodes, - num_threads - ): - if pos.is_cuda: - output = density_map_cuda.forward( - pos.view(pos.numel()), - node_size_x, - node_size_y, - bin_center_x, - bin_center_y, - initial_density_map, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_filler_nodes) - else: - output = density_map_cpp.forward( - pos.view(pos.numel()), - node_size_x, - node_size_y, - bin_center_x, - bin_center_y, - initial_density_map, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_filler_nodes, - num_threads - ) - return output - -class DensityMap(object): - """ - @brief Compute density map for both movable and fixed cells. - The density map for fixed cells is pre-computed. - Each call will only compute the density map for movable cells. - """ - def __init__(self, node_size_x, node_size_y, bin_center_x, bin_center_y, xl, yl, xh, yh, bin_size_x, bin_size_y, num_movable_nodes, num_terminals, num_filler_nodes, num_threads=8): - """ - @brief initialization - @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order - @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order - @param bin_center_x bin center x locations - @param bin_center_y bin center y locations - @param xl left boundary - @param yl bottom boundary - @param xh right boundary - @param yh top boundary - @param bin_size_x bin width - @param bin_size_y bin height - @param num_movable_nodes number of movable cells - @param num_terminals number of fixed cells - @param num_filler_nodes number of filler cells - """ - super(DensityMap, self).__init__() - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.bin_center_x = bin_center_x - self.bin_center_y = bin_center_y - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.bin_size_x = bin_size_x - self.bin_size_y = bin_size_y - self.num_movable_nodes = num_movable_nodes - self.num_terminals = num_terminals - self.num_filler_nodes = num_filler_nodes - self.initial_density_map = None - self.num_threads = num_threads - def forward(self, pos): - """ - @brief API - @param pos cell locations. The array consists of x locations of movable cells, fixed cells, and filler cells, then y locations of them - """ - if self.initial_density_map is None: - if pos.is_cuda: - self.initial_density_map = density_map_cuda.fixed_density_map( - pos, - self.node_size_x, - self.node_size_y, - self.bin_center_x, - self.bin_center_y, - self.xl, - self.yl, - self.xh, - self.yh, - self.bin_size_x, - self.bin_size_y, - self.num_movable_nodes, - self.num_terminals - ) - else: - self.initial_density_map = density_map_cpp.fixed_density_map( - pos, - self.node_size_x, - self.node_size_y, - self.bin_center_x, - self.bin_center_y, - self.xl, - self.yl, - self.xh, - self.yh, - self.bin_size_x, - self.bin_size_y, - self.num_movable_nodes, - self.num_terminals, - self.num_threads - ) - - density_map = DensityMapFunction.forward( - pos=pos, - node_size_x=self.node_size_x, - node_size_y=self.node_size_y, - bin_center_x=self.bin_center_x, - bin_center_y=self.bin_center_y, - initial_density_map=self.initial_density_map, - xl=self.xl, - yl=self.yl, - xh=self.xh, - yh=self.yh, - bin_size_x=self.bin_size_x, - bin_size_y=self.bin_size_y, - num_movable_nodes=self.num_movable_nodes, - num_filler_nodes=self.num_filler_nodes, - num_threads=self.num_threads - ) - - return density_map - -def plot(density_map, name): - """ - @brief density map contour and heat map - """ - print(np.amax(density_map)) - print(np.mean(density_map)) - fig = plt.figure(figsize=(4, 3)) - ax = fig.gca(projection='3d') - - x = np.arange(density_map.shape[0]) - y = np.arange(density_map.shape[1]) - - x, y = np.meshgrid(x, y) - ax.plot_surface(x, y, density_map, alpha=0.8) - - ax.set_xlabel('x') - ax.set_ylabel('y') - ax.set_zlabel('density') - - plt.savefig(name+".3d.png") - - plt.clf() - - fig, ax = plt.subplots() - - ax.pcolor(density_map) - - fig.tight_layout() - plt.savefig(name+".2d.png") diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp deleted file mode 100644 index afb1e6a5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/** - * @file density_map.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density map on CPU - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -void distributeBox2Bin( - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T bxl, T byl, T bxh, T byh, - T* buf_map - ) -{ - // density overflow function - auto computeDensityFunc = [](T x, T node_size, T bin_center, T bin_size){ - return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(x+node_size, bin_center+bin_size/2) - DREAMPLACE_STD_NAMESPACE::max(x, bin_center-bin_size/2)); - }; - // x direction - int bin_index_xl = int((bxl-xl)/bin_size_x); - int bin_index_xh = int(ceil((bxh-xl)/bin_size_x))+1; // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - // y direction - int bin_index_yl = int((byl-yl-2*bin_size_y)/bin_size_y); - int bin_index_yh = int(ceil((byh-yl+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityFunc(bxl, bxh - bxl, bin_center_x_tensor[k], bin_size_x); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityFunc(byl, byh - byl, bin_center_y_tensor[h], bin_size_y); - - // still area - T& density = buf_map[k*num_bins_y+h]; -#pragma omp atomic - density += px*py; - } - } -}; - -/// @brief compute density map -/// @param x_tensor cell x locations -/// @param y_tensor cell y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param num_nodes number of cells -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_threads number of threads -/// @param density_map_tensor 2D density map in column-major to write -template -int computeDensityMapLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - int num_threads, - T* density_map_tensor - ) -{ - // density_map_tensor should be initialized outside - -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - T bxl = x_tensor[i]; - T byl = y_tensor[i]; - T bxh = bxl + node_size_x_tensor[i]; - T byh = byl + node_size_y_tensor[i]; - distributeBox2Bin( - bin_center_x_tensor, bin_center_y_tensor, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - bxl, byl, bxh, byh, - density_map_tensor - ); - } - - return 0; -} - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute density map. -/// @param pos cell locations, array of x locations and then y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param initial_density_map initial density map -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @return density map -at::Tensor density_map_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - at::Tensor initial_density_map, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - int num_bins_x = int(ceil((xh-xl)/bin_size_x)); - int num_bins_y = int(ceil((yh-yl)/bin_size_y)); - at::Tensor density_map = initial_density_map.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { - computeDensityMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_movable_nodes, // only compute that for movable cells - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { - computeDensityMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_filler_nodes, // only compute that for movable cells - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - } - - return density_map; -} - -/// @brief Compute the density overflow for fixed cells. -/// This map can be used as the initial density map since it only needs to be computed once. -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_terminals number of fixed cells -/// @return a density map for fixed cells -at::Tensor fixed_density_map( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_terminals, - int num_threads - ) -{ - int num_bins_x = int(ceil((xh-xl)/bin_size_x)); - int num_bins_y = int(ceil((yh-yl)/bin_size_y)); - int num_nodes = pos.numel() / 2; - at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); - - if (num_terminals) - { - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapLauncher", [&] { - computeDensityMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t) + num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_terminals, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - } - - return density_map; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::density_map_forward, "DensityMap forward"); - m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_map, "DensityMap Map for Fixed Cells"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp deleted file mode 100644 index 65d0398f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/** - * @file density_map.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density map on GPU - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief compute density map -/// @param x_tensor cell x locations -/// @param y_tensor cell y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param num_nodes number of cells -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param density_map_tensor 2D density map in column-major to write -template -int computeDensityMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T* density_map_tensor - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute density map. -/// @param pos cell locations, array of x locations and then y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param initial_density_map initial density map -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @return density map -at::Tensor density_map_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - at::Tensor initial_density_map, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - int num_bins_x = int(ceil((xh-xl)/bin_size_x)); - int num_bins_y = int(ceil((yh-yl)/bin_size_y)); - at::Tensor density_map = initial_density_map.clone(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { - computeDensityMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_movable_nodes, // only compute that for movable cells - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { - computeDensityMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_filler_nodes, // only compute that for movable cells - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - } - - return density_map; -} - -/// @brief Compute the density overflow for fixed cells. -/// This map can be used as the initial density map since it only needs to be computed once. -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_terminals number of fixed cells -/// @return a density map for fixed cells -at::Tensor fixed_density_map( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_terminals - ) -{ - int num_bins_x = int(ceil((xh-xl)/bin_size_x)); - int num_bins_y = int(ceil((yh-yl)/bin_size_y)); - at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); - - if (num_terminals) - { - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityMapCudaLauncher", [&] { - computeDensityMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel()/2 + num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t) + num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t) + num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_terminals, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - } - - return density_map; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::density_map_forward, "DensityMap forward (CUDA)"); - m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_map, "DensityMap Map for Fixed Cells (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu deleted file mode 100644 index 523309d9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @file density_map_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Dec 2019 - */ - -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/print.cuh" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -inline __device__ void distributeBox2Bin( - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T bxl, T byl, T bxh, T byh, - T* buf_map - ) -{ - // density overflow function - auto computeDensityFunc = [](T x, T node_size, T bin_center, T bin_size){ - return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(x+node_size, bin_center+bin_size/2) - DREAMPLACE_STD_NAMESPACE::max(x, bin_center-bin_size/2)); - }; - // x direction - int bin_index_xl = int((bxl-xl)/bin_size_x); - int bin_index_xh = int(ceil((bxh-xl)/bin_size_x))+1; // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - // y direction - int bin_index_yl = int((byl-yl-2*bin_size_y)/bin_size_y); - int bin_index_yh = int(ceil((byh-yl+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityFunc(bxl, bxh - bxl, bin_center_x_tensor[k], bin_size_x); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityFunc(byl, byh - byl, bin_center_y_tensor[h], bin_size_y); - - // still area - atomicAdd(&buf_map[k*num_bins_y+h], px * py); - } - } -}; - -template -__global__ void computeDensityMap( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T* density_map_tensor - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nodes) - { - T bxl = x_tensor[i]; - T byl = y_tensor[i]; - T bxh = bxl + node_size_x_tensor[i]; - T byh = byl + node_size_y_tensor[i]; - distributeBox2Bin( - bin_center_x_tensor, bin_center_y_tensor, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - bxl, byl, bxh, byh, - density_map_tensor - ); - } -} - -/// @brief compute density map -/// @param x_tensor cell x locations -/// @param y_tensor cell y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param num_nodes number of cells -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param density_map_tensor 2D density map in column-major to write -template -int computeDensityMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T* density_map_tensor - ) -{ - int thread_count = 256; - int block_count = ceilDiv(num_nodes, thread_count); - - computeDensityMap<<>>( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - bin_center_x_tensor, bin_center_y_tensor, - num_nodes, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - density_map_tensor - ); - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeDensityMapCudaLauncher(\ - const T* x_tensor, const T* y_tensor, \ - const T* node_size_x_tensor, const T* node_size_y_tensor, \ - const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ - const int num_nodes, \ - const int num_bins_x, const int num_bins_y, \ - const T xl, const T yl, const T xh, const T yh, \ - const T bin_size_x, const T bin_size_y, \ - T* density_map_tensor\ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt deleted file mode 100644 index f7e8f60d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -set(OP_NAME density_potential) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/density_potential.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/density_potential_cuda.cpp - src/density_potential_cuda_kernel.cu - src/density_overflow_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py deleted file mode 100644 index efdada13..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py +++ /dev/null @@ -1,408 +0,0 @@ -## -# @file density_potential.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# @brief Compute density potential according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) -# - -import math -import numpy as np -import torch -from torch import nn -from torch.autograd import Function -from torch.nn import functional as F - -import dreamplacefpga.ops.density_potential.density_potential_cpp as density_potential_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.density_potential.density_potential_cuda as density_potential_cuda - -import pdb -import matplotlib -matplotlib.use('Agg') -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt - -# global variable for plot -#plot_count = 0 - -class DensityPotentialFunction(Function): - """ - @brief compute density potential. - """ - - @staticmethod - def forward( - ctx, - pos, - node_size_x, node_size_y, - ax, bx, cx, - ay, by, cy, - bin_center_x, bin_center_y, - initial_density_map, - target_density, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_movable_nodes, - num_filler_nodes, - padding, - padding_mask, # same dimensions as density map, with padding regions to be 1 - num_bins_x, - num_bins_y, - num_impacted_bins_x, - num_impacted_bins_y, - num_threads - ): - if pos.is_cuda: - output = density_potential_cuda.forward( - pos.view(pos.numel()), - node_size_x, node_size_y, - ax, bx, cx, - ay, by, cy, - bin_center_x, bin_center_y, - initial_density_map, - target_density, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_movable_nodes, - num_filler_nodes, - padding, - num_bins_x, - num_bins_y, - num_impacted_bins_x, - num_impacted_bins_y - ) - else: - output = density_potential_cpp.forward( - pos.view(pos.numel()), - node_size_x, node_size_y, - ax, bx, cx, - ay, by, cy, - bin_center_x, bin_center_y, - initial_density_map, - target_density, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_movable_nodes, - num_filler_nodes, - padding, - num_bins_x, - num_bins_y, - num_impacted_bins_x, - num_impacted_bins_y, - num_threads - ) - - # output consists of (density_cost, density_map, max_density) - ctx.node_size_x = node_size_x - ctx.node_size_y = node_size_y - ctx.ax = ax - ctx.bx = bx - ctx.cx = cx - ctx.ay = ay - ctx.by = by - ctx.cy = cy - ctx.bin_center_x = bin_center_x - ctx.bin_center_y = bin_center_y - ctx.target_density = target_density - ctx.xl = xl - ctx.yl = yl - ctx.xh = xh - ctx.yh = yh - ctx.bin_size_x = bin_size_x - ctx.bin_size_y = bin_size_y - ctx.num_movable_nodes = num_movable_nodes - ctx.num_filler_nodes = num_filler_nodes - ctx.padding = padding - ctx.num_bins_x = num_bins_x - ctx.num_bins_y = num_bins_y - ctx.num_impacted_bins_x = num_impacted_bins_x - ctx.num_impacted_bins_y = num_impacted_bins_y - ctx.pos = pos - ctx.num_threads = num_threads - ctx.density_map = output[1] - - # set padding density - if padding > 0: - ctx.density_map.masked_fill_(padding_mask, target_density * bin_size_x * bin_size_y) - - #global plot_count - #if plot_count % 100 == 0: - # plot(plot_count, output[1].clone().div(bin_size_x*bin_size_y).cpu().numpy(), padding, 'summary/potential_map') - #plot_count += 1 - - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - if grad_pos.is_cuda: - output = density_potential_cuda.backward( - grad_pos, - ctx.num_bins_x, ctx.num_bins_y, - ctx.num_impacted_bins_x, ctx.num_impacted_bins_y, - ctx.density_map, - ctx.pos, - ctx.node_size_x, ctx.node_size_y, - ctx.ax, ctx.bx, ctx.cx, - ctx.ay, ctx.by, ctx.cy, - ctx.bin_center_x, ctx.bin_center_y, - ctx.target_density, - ctx.xl, ctx.yl, ctx.xh, ctx.yh, - ctx.bin_size_x, ctx.bin_size_y, - ctx.num_movable_nodes, - ctx.num_filler_nodes, - ctx.padding - ) - else: - output = density_potential_cpp.backward( - grad_pos, - ctx.num_bins_x, ctx.num_bins_y, - ctx.num_impacted_bins_x, ctx.num_impacted_bins_y, - ctx.density_map, - ctx.pos, - ctx.node_size_x, ctx.node_size_y, - ctx.ax, ctx.bx, ctx.cx, - ctx.ay, ctx.by, ctx.cy, - ctx.bin_center_x, ctx.bin_center_y, - ctx.target_density, - ctx.xl, ctx.yl, ctx.xh, ctx.yh, - ctx.bin_size_x, ctx.bin_size_y, - ctx.num_movable_nodes, - ctx.num_filler_nodes, - ctx.padding, - ctx.num_threads - ) - return output, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None - -class DensityPotential(nn.Module): - """ - @brief Compute density potential according to NTUPlace3 - """ - def __init__(self, - node_size_x, node_size_y, - ax, bx, cx, - ay, by, cy, - bin_center_x, bin_center_y, - target_density, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_movable_nodes, - num_terminals, - num_filler_nodes, - padding, - sigma, - delta, - num_threads - ): - """ - @brief initialization - @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order - @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order - @param ax - @param bx - @param cx - @param ay - @param by - @param cy see the a, b, c defined in NTUPlace3 - @param bin_center_x bin center x locations - @param bin_center_y bin center y locations - @param target_density target density - @param xl left boundary - @param yl bottom boundary - @param xh right boundary - @param yh top boundary - @param bin_size_x bin width - @param bin_size_y bin height - @param num_movable_nodes number of movable cells - @param num_terminals number of fixed cells - @param num_filler_nodes number of filler cells - @param padding bin padding to boundary of placement region - @param sigma parameter for density map of fixed cells according to NTUPlace3 - @param delta parameter for density map of fixed cells according to NTUPlace3 - """ - super(DensityPotential, self).__init__() - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.ax = ax - self.bx = bx - self.cx = cx - self.ay = ay - self.by = by - self.cy = cy - self.bin_center_x = bin_center_x - self.bin_center_y = bin_center_y - self.target_density = target_density - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.bin_size_x = bin_size_x - self.bin_size_y = bin_size_y - self.num_movable_nodes = num_movable_nodes - self.num_terminals = num_terminals - self.num_filler_nodes = num_filler_nodes - self.padding = padding - # compute maximum impacted bins - self.num_bins_x = int(math.ceil((xh-xl)/bin_size_x)) - self.num_bins_y = int(math.ceil((yh-yl)/bin_size_y)) - self.num_impacted_bins_x = ((node_size_x[:num_movable_nodes].max()+4*self.bin_size_x)/self.bin_size_x).ceil().clamp(max=self.num_bins_x); - self.num_impacted_bins_y = ((node_size_y[:num_movable_nodes].max()+4*self.bin_size_y)/self.bin_size_y).ceil().clamp(max=self.num_bins_y); - if self.padding > 0: - self.padding_mask = torch.ones(self.num_bins_x, self.num_bins_y, dtype=torch.uint8, device=node_size_x.device) - self.padding_mask[self.padding:self.num_bins_x-self.padding, self.padding:self.num_bins_y-self.padding].fill_(0) - else: - self.padding_mask = torch.zeros(self.num_bins_x, self.num_bins_y, dtype=torch.uint8, device=node_size_x.device) - - # parameters for initial density map - self.sigma = sigma - self.delta = delta - self.num_threads = num_threads - # initial density_map due to fixed cells - self.initial_density_map = None - - def forward(self, pos): - if self.initial_density_map is None: - if self.num_terminals == 0: - num_impacted_bins_x = 0 - num_impacted_bins_y = 0 - else: - num_impacted_bins_x = ((self.node_size_x[self.num_movable_nodes:self.num_movable_nodes+self.num_terminals].max()+self.bin_size_x)/self.bin_size_x).ceil().clamp(max=self.num_bins_x) - num_impacted_bins_y = ((self.node_size_y[self.num_movable_nodes:self.num_movable_nodes+self.num_terminals].max()+self.bin_size_y)/self.bin_size_y).ceil().clamp(max=self.num_bins_y) - if pos.is_cuda: - self.initial_density_map = density_potential_cuda.fixed_density_map( - pos.view(pos.numel()), - self.node_size_x, self.node_size_y, - self.ax, self.bx, self.cx, - self.ay, self.by, self.cy, - self.bin_center_x, self.bin_center_y, - self.xl, self.yl, self.xh, self.yh, - self.bin_size_x, self.bin_size_y, - self.num_movable_nodes, - self.num_terminals, - self.num_bins_x, - self.num_bins_y, - num_impacted_bins_x, - num_impacted_bins_y, - self.sigma, self.delta - ) - else: - self.initial_density_map = density_potential_cpp.fixed_density_map( - pos.view(pos.numel()), - self.node_size_x, self.node_size_y, - self.ax, self.bx, self.cx, - self.ay, self.by, self.cy, - self.bin_center_x, self.bin_center_y, - self.xl, self.yl, self.xh, self.yh, - self.bin_size_x, self.bin_size_y, - self.num_movable_nodes, - self.num_terminals, - self.num_bins_x, - self.num_bins_y, - num_impacted_bins_x, - num_impacted_bins_y, - self.sigma, self.delta, - self.num_threads - ) - # there exist fixed cells - if (self.num_movable_nodes+self.num_filler_nodes) < pos.numel()/2: - # convert area to density - bin_area = self.bin_size_x*self.bin_size_y - self.initial_density_map.div_(bin_area) - # gaussian filter - gaussian_weights = torch.tensor(gaussian_kernel(self.sigma)).to(pos.device) - self.initial_density_map = F.conv2d( - self.initial_density_map.view([1, 1, self.num_bins_x, self.num_bins_y]), - gaussian_weights.view([1, 1, gaussian_weights.size(0), gaussian_weights.size(1)]), - padding=[gaussian_weights.size(0)/2, gaussian_weights.size(1)/2] - ).view([self.num_bins_x, self.num_bins_y]) - ## level smoothing - #self.initial_density_map.div_(self.initial_density_map.max()) - #density_mean = self.initial_density_map.mean() - #delta_map = self.initial_density_map - density_mean - #self.initial_density_map = density_mean + delta_map.sign().mul_(delta_map.abs().pow_(self.delta)) - # convert density to area - self.initial_density_map.mul_(bin_area) - - #plot(self.initial_density_map.clone().div(self.bin_size_x*self.bin_size_y).cpu().numpy(), self.padding, 'initial_potential_map') - - return DensityPotentialFunction.apply( - pos, - self.node_size_x, self.node_size_y, - self.ax, self.bx, self.cx, - self.ay, self.by, self.cy, - self.bin_center_x, - self.bin_center_y, - self.initial_density_map, - self.target_density, - self.xl, - self.yl, - self.xh, - self.yh, - self.bin_size_x, - self.bin_size_y, - self.num_movable_nodes, - self.num_filler_nodes, - self.padding, - self.padding_mask, - self.num_bins_x, - self.num_bins_y, - self.num_impacted_bins_x, - self.num_impacted_bins_y, - self.num_threads - ) - -def gaussian_kernel(sigma, truncate=4.0): - """ - Return Gaussian that truncates at the given number of standard deviations. - """ - - sigma = float(sigma) - radius = int(truncate * sigma + 0.5) - - x, y = np.mgrid[-radius:radius+1, -radius:radius+1] - sigma = sigma**2 - - k = 2*np.exp(-0.5 * (x**2 + y**2) / sigma) - k = k / np.sum(k) - - return k - -def plot(plot_count, density_map, padding, name): - """ - density map contour and heat map - """ - density_map = density_map[padding:-1-padding, padding:-1-padding] - print("max density = %g" % (np.amax(density_map))) - print("mean density = %g" % (np.mean(density_map))) - - fig = plt.figure() - ax = fig.gca(projection='3d') - - x = np.arange(density_map.shape[0]) - y = np.arange(density_map.shape[1]) - - x, y = np.meshgrid(x, y) - ax.plot_surface(x, y, density_map, alpha=0.8) - - ax.set_xlabel('x') - ax.set_ylabel('y') - ax.set_zlabel('density') - - #plt.tight_layout() - plt.savefig(name+".3d.%d.png" % (plot_count)) - plt.close() - - #plt.clf() - - #fig, ax = plt.subplots() - - #ax.pcolor(density_map) - - ## Loop over data dimensions and create text annotations. - ##for i in range(density_map.shape[0]): - ## for j in range(density_map.shape[1]): - ## text = ax.text(j, i, density_map[i, j], - ## ha="center", va="center", color="w") - #fig.tight_layout() - #plt.savefig(name+".2d.%d.png" % (plot_count)) - #plt.close() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu deleted file mode 100644 index 3624f1f1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @file density_overflow_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density overflow according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063). - * This is for fixed cells. - */ -#include -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeDensityMap( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const int num_impacted_bins_x, const int num_impacted_bins_y, - T* density_map_tensor) -{ - int64_t i = blockIdx.x * blockDim.x + threadIdx.x; - int64_t bound = int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y; - // rank-one update density map - if (i < bound) - { - // density overflow function - auto computeDensityOverflowFunc = [](T x, T node_size, T bin_center, T bin_size){ - return max(T(0.0), min(x+node_size, bin_center+bin_size/2) - max(x, bin_center-bin_size/2)); - }; - int node_id = i/(num_impacted_bins_x*num_impacted_bins_y); - int residual_index = i-node_id*num_impacted_bins_x*num_impacted_bins_y; - // x direction - int bin_index_xl = int((x_tensor[node_id]-xl)/bin_size_x); - bin_index_xl = max(bin_index_xl, 0); - int k = bin_index_xl+int(residual_index / num_impacted_bins_y); - if (k+1 > num_bins_x) - { - return; - } - // y direction - int bin_index_yl = int((y_tensor[node_id]-yl)/bin_size_y); - bin_index_yl = max(bin_index_yl, 0); - int h = bin_index_yl+(residual_index % num_impacted_bins_y); - if (h+1 > num_bins_y) - { - return; - } - - T px = computeDensityOverflowFunc(x_tensor[node_id], node_size_x_tensor[node_id], bin_center_x_tensor[k], bin_size_x); - T py = computeDensityOverflowFunc(y_tensor[node_id], node_size_y_tensor[node_id], bin_center_y_tensor[h], bin_size_y); - // still area - atomicAdd(&density_map_tensor[k*num_bins_y+h], px*py); - } -} - -template -int computeDensityOverflowMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const int num_impacted_bins_x, const int num_impacted_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T* density_map_tensor - ) -{ - int64_t thread_count = 512; - int64_t block_count = (int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y - 1 + thread_count) /thread_count; - dreamplaceAssert(block_count >= 0); // avoid numerical overflow - - computeDensityMap<<>>( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - bin_center_x_tensor, bin_center_y_tensor, - num_nodes, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_impacted_bins_x, num_impacted_bins_y, - density_map_tensor); - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeDensityOverflowMapCudaLauncher(\ - const T* x_tensor, const T* y_tensor, \ - const T* node_size_x_tensor, const T* node_size_y_tensor, \ - const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ - const int num_nodes, \ - const int num_bins_x, const int num_bins_y, \ - const int num_impacted_bins_x, const int num_impacted_bins_y, \ - const T xl, const T yl, const T xh, const T yh, \ - const T bin_size_x, const T bin_size_y, \ - T* density_map_tensor\ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -template -__global__ void computeGaussianFilterWeights( - const int num_bins_x, const int num_bins_y, - const T sigma, - T* gaussian_filter_tensor - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_bins_x*num_bins_y) - { - int x = i/num_bins_y; - int y = i-x*num_bins_y; - T sigma_square = sigma*sigma; - T x2_y2 = (x-num_bins_x/2)*(x-num_bins_x) + (y-num_bins_y/2)*(y-num_bins_y); - //gaussian_filter_tensor[i] = 1.0/(2*M_PI*sigma_square) * exp(-x2_y2/(2*sigma_square)); - gaussian_filter_tensor[i] = 2 * exp(-x2_y2/(2*sigma_square)); - } -} - -template -int computeGaussianFilterLauncher( - const int num_bins_x, const int num_bins_y, - const T sigma, - T* gaussian_filter_tensor - ) -{ - int thread_count = 512; - int block_count = (num_bins_x*num_bins_y - 1 + thread_count) / thread_count; - - computeGaussianFilterWeights<<>>( - num_bins_x, num_bins_y, - sigma, - gaussian_filter_tensor - ); - - return 0; -} - -#define REGISTER_GAUSSIAN_FILTER_LAUNCHER(T) \ - template int computeGaussianFilterLauncher(\ - const int num_bins_x, const int num_bins_y, \ - const T sigma, \ - T* gaussian_filter_tensor\ - ); - -REGISTER_GAUSSIAN_FILTER_LAUNCHER(float); -REGISTER_GAUSSIAN_FILTER_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp deleted file mode 100644 index c5876155..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/** - * @file density_potential.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density potential on CPU according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief compute density map, density cost, and gradient -/// @param x_tensor cell x locations -/// @param y_tensor cell y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param ax_tensor ax tensor according to NTUPlace3 paper, for x direction -/// @param bx_tensor bx tensor according to NTUPlace3 paper, for x direction -/// @param cx_tensor cx tensor according to NTUPlace3 paper, for x direction -/// @param ay_tensor ay tensor according to NTUPlace3 paper, for y direction -/// @param by_tensor by tensor according to NTUPlace3 paper, for y direction -/// @param cy_tensor cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -/// @param num_nodes number of cells -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param target_area target area computed from target density -/// @param density_map_tensor 2D density map in column-major to write -/// @param grad_tensor input gradient from backward propagation -/// @param grad_x_tensor density gradient of cell in x direction -/// @param grad_y_tensor density gradient of cell in y direction -template -int computeDensityPotentialMapLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - int num_impacted_bins_x, int num_impacted_bins_y, - const int num_nodes, - const int num_bins_x, const int num_bins_y, const int padding, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_area, - T* density_map_tensor, - const T* grad_tensor, - int num_threads, - T* grad_x_tensor, T* grad_y_tensor - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief compute density map, density cost, and gradient -/// @param pos cell locations. The array consists of all x locations and then y locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param ax ax tensor according to NTUPlace3 paper, for x direction -/// @param bx bx tensor according to NTUPlace3 paper, for x direction -/// @param cx cx tensor according to NTUPlace3 paper, for x direction -/// @param ay ay tensor according to NTUPlace3 paper, for y direction -/// @param by by tensor according to NTUPlace3 paper, for y direction -/// @param cy cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param initial_density_map initial density map for fixed cells -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -std::vector density_potential_forward( - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - at::Tensor initial_density_map, // initial density map from fixed cells - double target_density, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - int padding, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = initial_density_map.clone(); - double target_area = target_density*bin_size_x*bin_size_y; - - //int num_nodes = pos.numel()/2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { - computeDensityPotentialMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - num_movable_nodes, - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - nullptr, - num_threads, - nullptr, nullptr - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { - computeDensityPotentialMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - num_filler_nodes, - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - nullptr, - num_threads, - nullptr, nullptr - ); - }); - } - - auto max_density = density_map.max(); - // (max(0, density-target_area))^2 - //auto delta = (density_map-target_area).clamp_min(0).pow(2); - auto delta = (density_map-target_area).pow(2); - auto density_cost = at::sum(delta); - - return {density_cost, density_map, max_density}; -} - -/// @brief Compute density potential gradient -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -/// @param density_map current density map -/// @param pos cell locations. The array consists of all x locations and then y locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param ax ax tensor according to NTUPlace3 paper, for x direction -/// @param bx bx tensor according to NTUPlace3 paper, for x direction -/// @param cx cx tensor according to NTUPlace3 paper, for x direction -/// @param ay ay tensor according to NTUPlace3 paper, for y direction -/// @param by by tensor according to NTUPlace3 paper, for y direction -/// @param cy cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -at::Tensor density_potential_backward( - at::Tensor grad_pos, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y, - at::Tensor density_map, - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double target_density, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - int padding, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - AT_ASSERTM(!density_map.is_cuda() && density_map.ndimension() == 2 && density_map.size(0) == num_bins_x && density_map.size(1) == num_bins_y, "density_map must be a 2D tensor on CPU"); - double target_area = target_density*bin_size_x*bin_size_y; - at::Tensor grad_out = at::zeros_like(pos); - - //int num_nodes = pos.numel()/2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { - computeDensityPotentialMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - num_movable_nodes, - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()/2 - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapLauncher", [&] { - computeDensityPotentialMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+pos.numel()/2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - num_filler_nodes, - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()/2-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+pos.numel()-num_filler_nodes - ); - }); - } - - return grad_out; -} - -template -int computeDensityOverflowMapLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_density, - const int num_threads, - T* density_map_tensor - ); - -template -int computeGaussianFilterLauncher( - const int num_bins_x, const int num_bins_y, - const T sigma, - const int num_threads, - T* gaussian_filter_tensor - ); - -/// @brief compute density map for fixed cells -at::Tensor fixed_density_potential_map( - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_terminals, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y, - double sigma, double delta, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.type()); - - int num_nodes = pos.numel()/2; - - // Call the cuda kernel launcher - if (num_terminals && num_impacted_bins_x && num_impacted_bins_y) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityOverflowMapLauncher", [&] { - computeDensityOverflowMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_terminals, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - 0, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - }); - } - - return density_map; -} - -template -int computeDensityPotentialMapLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - int num_impacted_bins_x, int num_impacted_bins_y, - const int num_nodes, - const int num_bins_x, const int num_bins_y, const int padding, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_area, - T* density_map_tensor, - const T* grad_tensor, - int num_threads, - T* grad_x_tensor, T* grad_y_tensor - ) -{ - // density_map_tensor should be initialized outside - - // density potential function - auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ - // from origin to center - x += node_size/2; - //printf("x = %g, bin_center = %g\n", x, bin_center); - T dist = fabs(x-bin_center); - //printf("dist = %g\n", dist); - T partition1 = node_size/2+bin_size; - //printf("partition1 = %g\n", partition1); - T partition2 = partition1+bin_size; - //printf("partition2 = %g\n", partition2); - //printf("a = %g, b = %g, c = %g\n", a, b, c); - if (dist < partition1) - { - return c*(1-a*dist*dist); - } - else if (dist < partition2) - { - return c*(b*(dist-partition2)*(dist-partition2)); - } - else - { - return T(0.0); - } - }; - // density potential gradient function - auto computeDensityPotentialGradFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ - // from origin to center - x += node_size/2; - T dist = fabs(x-bin_center); - T partition1 = node_size/2+bin_size; - T partition2 = partition1+bin_size; - if (dist < partition1) - { - return -2*c*a*(x-bin_center); - } - else if (dist < partition2) - { - T sign = (x < bin_center)? -1.0 : 1.0; - return 2*c*b*(dist-partition2)*sign; - } - else - { - return T(0.0); - } - }; - -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - // x direction - int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); - int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive - bin_index_xl = std::max(bin_index_xl, 0); - bin_index_xh = std::min(bin_index_xh, num_bins_x); - - // y direction - int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); - int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = std::max(bin_index_yl, 0); - bin_index_yh = std::min(bin_index_yh, num_bins_y); - - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); - //printf("px[%d, %d] = %g\n", i, k, px); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); - //printf("py[%d, %d] = %g\n", i, h, py); - //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); - - // still area - T& density = density_map_tensor[k*num_bins_y+h]; -#pragma omp atomic - density += px*py; - } - } - } - - if (grad_tensor) // compute density gradient - { -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); - //int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive - bin_index_xl = std::max(bin_index_xl, 0); - // be careful about the bin_index_xl and bin_index_xh here - // the assumption is that num_bins_x >= num_impacted_bins_x - // each row of the px matrix should be filled with num_impacted_bins_x columns - bin_index_xl = std::min(bin_index_xl, num_bins_x-num_impacted_bins_x); - //bin_index_xh = std::min(bin_index_xh, num_bins_x); - int bin_index_xh = bin_index_xl+num_impacted_bins_x; - - int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); - //int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = std::max(bin_index_yl, 0); - // be careful about the bin_index_yl and bin_index_yh here - // the assumption is that num_bins_y >= num_impacted_bins_y - // each row of the py matrix should be filled with num_impacted_bins_y columns - bin_index_yl = std::min(bin_index_yl, num_bins_y-num_impacted_bins_y); - //bin_index_yh = std::min(bin_index_yh, num_bins_y); - int bin_index_yh = bin_index_yl+num_impacted_bins_y; - - grad_x_tensor[i] = 0; - grad_y_tensor[i] = 0; - // update density potential map - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); - T gradx = computeDensityPotentialGradFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); - T grady = computeDensityPotentialGradFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); - - T delta = density_map_tensor[k*num_bins_y+h]-target_area; - //delta = std::max(delta, (T)0); - - grad_x_tensor[i] += 2*delta*py*gradx; - grad_y_tensor[i] += 2*delta*px*grady; - - } - } - - grad_x_tensor[i] *= *grad_tensor; - grad_y_tensor[i] *= *grad_tensor; - } - } - else // compute density cost - { - // handle padding -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_bins_x; ++i) - { - for (int j = 0; j < num_bins_y; ++j) - { - if (!(i >= padding && i+padding < num_bins_x && j >= padding && j+padding < num_bins_y)) - { - density_map_tensor[i*num_bins_y+j] = target_area; - } - } - } - } - - return 0; -} - -template -int computeDensityOverflowMapLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_area, - const int num_threads, - T* density_map_tensor - ) -{ - // initialize - int num_bins = num_bins_x*num_bins_y; -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_bins; ++i) - { - density_map_tensor[i] = 0; - } - - // density overflow function - auto computeDensityOverflowFunc = [](T x, T node_size, T bin_center, T bin_size){ - return std::max(T(0.0), std::min(x+node_size, bin_center+bin_size/2) - std::max(x, bin_center-bin_size/2)); - }; -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - // x direction - int bin_index_xl = int((x_tensor[i]-xl)/bin_size_x); - int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i])/bin_size_x))+1; // exclusive - bin_index_xl = std::max(bin_index_xl, 0); - bin_index_xh = std::min(bin_index_xh, num_bins_x); - - // y direction - int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); - int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = std::max(bin_index_yl, 0); - bin_index_yh = std::min(bin_index_yh, num_bins_y); - - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityOverflowFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityOverflowFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y); - //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); - - // still area - T& density = density_map_tensor[k*num_bins_y+h]; -#pragma omp atomic - density += px*py; - } - } - } - - return 0; -} - -template -int computeGaussianFilterLauncher( - const int num_bins_x, const int num_bins_y, - const T sigma, - const int num_threads, - T* gaussian_filter_tensor - ) -{ - T sigma_square = sigma*sigma; -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_bins_x; ++i) - { - for (int j = 0; j < num_bins_y; ++j) - { - T x2_y2 = (i-num_bins_x/2)*(i-num_bins_x) + (j-num_bins_y/2)*(j-num_bins_y); - gaussian_filter_tensor[i*num_bins_y+j] = 1.0/(2*M_PI*sigma_square) * exp(-x2_y2/(2*sigma_square)); - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::density_potential_forward, "DensityPotential forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::density_potential_backward, "DensityPotential backward"); - m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_potential_map, "DensityPotential Map for Fixed Cells"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp deleted file mode 100644 index 183b3f53..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/** - * @file hpwl_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density potential on CUDA according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063) - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief compute density map, density cost, and gradient -/// @param x_tensor cell x locations -/// @param y_tensor cell y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param ax_tensor ax tensor according to NTUPlace3 paper, for x direction -/// @param bx_tensor bx tensor according to NTUPlace3 paper, for x direction -/// @param cx_tensor cx tensor according to NTUPlace3 paper, for x direction -/// @param ay_tensor ay tensor according to NTUPlace3 paper, for y direction -/// @param by_tensor by tensor according to NTUPlace3 paper, for y direction -/// @param cy_tensor cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x_tensor bin center x locations -/// @param bin_center_y_tensor bin center y locations -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -/// @param num_nodes number of cells -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param target_area target area computed from target density -/// @param density_map_tensor 2D density map in column-major to write -/// @param density_cost_tensor overall density overflow -/// @param grad_tensor input gradient from backward propagation -/// @param grad_x_tensor density gradient of cell in x direction -/// @param grad_y_tensor density gradient of cell in y direction -template -int computeDensityPotentialMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_impacted_bins_x, const int num_impacted_bins_y, - const int mat_size_x, const int mat_size_y, - const int num_nodes, - const int num_bins_x, const int num_bins_y, const int padding, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_area, - T* density_map_tensor, - const T* grad_tensor, - T* grad_x_tensor, T* grad_y_tensor - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -typedef double T; - -/// @brief compute density map, density cost, and gradient -/// @param pos cell locations. The array consists of all x locations and then y locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param ax ax tensor according to NTUPlace3 paper, for x direction -/// @param bx bx tensor according to NTUPlace3 paper, for x direction -/// @param cx cx tensor according to NTUPlace3 paper, for x direction -/// @param ay ay tensor according to NTUPlace3 paper, for y direction -/// @param by by tensor according to NTUPlace3 paper, for y direction -/// @param cy cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -std::vector density_potential_forward( - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - at::Tensor initial_density_map, // initial density map from fixed cells - double target_density, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - int padding, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = initial_density_map.clone(); - double target_area = target_density*bin_size_x*bin_size_y; - - int num_nodes = pos.numel()/2; - int mat_size_x = (num_movable_nodes*num_impacted_bins_x); // only need to compute for movable nodes - int mat_size_y = (num_movable_nodes*num_impacted_bins_y); - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { - computeDensityPotentialMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - mat_size_x, mat_size_y, - num_movable_nodes, // only need to compute for movable nodes - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - nullptr, - nullptr, nullptr - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { - computeDensityPotentialMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes*2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - mat_size_x, mat_size_y, - num_filler_nodes, // only need to compute for movable nodes - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - nullptr, - nullptr, nullptr - ); - }); - } - - auto max_density = density_map.max(); - - // (max(0, density-target_area))^2 - //auto delta = (density_map-target_area).clamp_min(0).pow(2); - auto delta = (density_map-target_area).pow(2); - auto density_cost = at::sum(delta); - - return {density_cost, - density_map, - max_density - }; -} - -/// @brief Compute density potential gradient -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_impacted_bins_x number of impacted bins for any cell in x direction -/// @param num_impacted_bins_y number of impacted bins for any cell in y direction -/// @param density_map current density map -/// @param pos cell locations. The array consists of all x locations and then y locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param ax ax tensor according to NTUPlace3 paper, for x direction -/// @param bx bx tensor according to NTUPlace3 paper, for x direction -/// @param cx cx tensor according to NTUPlace3 paper, for x direction -/// @param ay ay tensor according to NTUPlace3 paper, for y direction -/// @param by by tensor according to NTUPlace3 paper, for y direction -/// @param cy cy tensor according to NTUPlace3 paper, for y direction -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -at::Tensor density_potential_backward( - at::Tensor grad_pos, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y, - at::Tensor density_map, - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double target_density, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - int padding) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - AT_ASSERTM(density_map.is_cuda() && density_map.ndimension() == 2 && density_map.size(0) == num_bins_x && density_map.size(1) == num_bins_y, "density_map must be a 2D tensor on GPU"); - double target_area = target_density*bin_size_x*bin_size_y; - at::Tensor grad_out = at::zeros_like(pos); - - int num_nodes = pos.numel()/2; - int mat_size_x = (num_movable_nodes*num_impacted_bins_x); - int mat_size_y = (num_movable_nodes*num_impacted_bins_y); - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { - computeDensityPotentialMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - mat_size_x, mat_size_y, - num_movable_nodes, // only need to compute for movable nodes - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes - ); - }); - if (num_filler_nodes) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityPotentialMapCudaLauncher", [&] { - computeDensityPotentialMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes*2-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t)+num_nodes-num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_impacted_bins_x, num_impacted_bins_y, - mat_size_x, mat_size_y, - num_filler_nodes, // only need to compute for movable nodes - num_bins_x, num_bins_y, padding, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - target_area, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes-num_filler_nodes, DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_nodes*2-num_filler_nodes - ); - }); - } - - return grad_out; -} - -template -int computeDensityOverflowMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const int num_impacted_bins_x, const int num_impacted_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - T* density_map_tensor - ); - -template -int computeGaussianFilterLauncher( - const int num_bins_x, const int num_bins_y, - const T sigma, - T* gaussian_filter_tensor - ); - -/// @brief compute density map for fixed cells -at::Tensor fixed_density_potential_map( - at::Tensor pos, - at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor ax, at::Tensor bx, at::Tensor cx, - at::Tensor ay, at::Tensor by, at::Tensor cy, - at::Tensor bin_center_x, - at::Tensor bin_center_y, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_terminals, - int num_bins_x, int num_bins_y, - int num_impacted_bins_x, int num_impacted_bins_y, - double sigma, double delta - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = at::zeros({num_bins_x, num_bins_y}, pos.type()); - - int num_nodes = pos.numel()/2; - - // Call the cuda kernel launcher - if (num_terminals && num_impacted_bins_x && num_impacted_bins_y) - { - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeDensityOverflowMapCudaLauncher", [&] { - //int mat_size_x = ((num_nodes-num_movable_nodes)*num_impacted_bins_x); - //int mat_size_y = ((num_nodes-num_movable_nodes)*num_impacted_bins_y); - //computeDensityPotentialMapCudaLauncher( - // DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, - // DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, - // DREAMPLACE_TENSOR_DATA_PTR(ax, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bx, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cx, scalar_t), - // DREAMPLACE_TENSOR_DATA_PTR(ay, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(by, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(cy, scalar_t), - // DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - // num_impacted_bins_x, num_impacted_bins_y, - // mat_size_x, mat_size_y, - // num_nodes-num_movable_nodes, - // num_bins_x, num_bins_y, 0, - // xl, yl, xh, yh, - // bin_size_x, bin_size_y, - // 0, - // DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - // nullptr, - // nullptr, nullptr - // ); - computeDensityOverflowMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes+num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t)+num_movable_nodes, DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t)+num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(bin_center_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(bin_center_y, scalar_t), - num_terminals, - num_bins_x, num_bins_y, - num_impacted_bins_x, num_impacted_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t) - ); - -#if 0 - density_map.div_(bin_size_x*bin_size_y); - - // smooth with Gaussian filter - T truncate = 4.0; - int radius = std::round(truncate*sigma); - int kernel_size = 2*radius+1; - at::Tensor gaussian_filter = at::zeros({kernel_size, kernel_size}, density_map.type()); - computeGaussianFilterLauncher( - gaussian_filter.size(0), gaussian_filter.size(1), - sigma, - DREAMPLACE_TENSOR_DATA_PTR(gaussian_filter, T) - ); - gaussian_filter.div_(gaussian_filter.sum()); - //std::cout << "density_map = " << density_map << "\n"; - //std::cout << "gaussian_filter = " << gaussian_filter << "\n"; - density_map = at::conv2d(density_map.view({1, 1, num_bins_x, num_bins_y}), gaussian_filter.view({1, 1, gaussian_filter.size(0), gaussian_filter.size(1)}), {}, 1, {{radius, radius}}).view({num_bins_x, num_bins_y}); - //std::cout << "density_map = " << density_map << "\n"; - // normalize to [0, 1] - //density_map.div_(density_map.max()); - - // level smoothing - at::Tensor density_mean = density_map.mean(); - at::Tensor delta_map = density_map-density_mean; - density_map = density_mean + delta_map.sign().mul_(delta_map.abs().pow_(delta)); - - density_map.mul_(bin_size_x*bin_size_y); -#endif - }); - } - - return density_map; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::density_potential_forward, "DensityPotential forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::density_potential_backward, "DensityPotential backward (CUDA)"); - m.def("fixed_density_map", &DREAMPLACE_NAMESPACE::fixed_density_potential_map, "DensityPotential Map for Fixed Cells (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu deleted file mode 100644 index d5b2bc40..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu +++ /dev/null @@ -1,317 +0,0 @@ -/** - * @file density_potential_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute density potential according to NTUPlace3 (https://doi.org/10.1109/TCAD.2008.923063). - * This is for movable and filler cells. - */ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -#if 0 -template -__global__ void computePaddingDensityMap( - const int num_bins_x, const int num_bins_y, - const int padding, - T* density_map_tensor) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < num_nodes*num_impacted_bins_x*num_impacted_bins_y; i += blockDim.x * gridDim.x) - { - int ix = i/num_bins_y; - int iy = i-ix*num_bins_y; - - if (ix < padding) - { - density_map_tensor[i] = density_map_tensor[padding*num_bins_y+iy]; - } - } -} -#endif - -template -__global__ void computeDensityMap( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const int num_impacted_bins_x, const int num_impacted_bins_y, - T* density_map_tensor) -{ - int64_t i = blockIdx.x * blockDim.x + threadIdx.x; - int64_t bound = int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y; - // rank-one update density map - if (i < bound) - { - // density potential function - auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ - // from origin to center - x += node_size/2; - //printf("x = %g, bin_center = %g\n", x, bin_center); - T dist = fabs(x-bin_center); - //printf("dist = %g\n", dist); - T partition1 = node_size/2+bin_size; - //printf("partition1 = %g\n", partition1); - T partition2 = partition1+bin_size; - //printf("partition2 = %g\n", partition2); - //printf("a = %g, b = %g, c = %g\n", a, b, c); - if (dist < partition1) - { - return c*(1-a*dist*dist); - } - else if (dist < partition2) - { - return c*(b*(dist-partition2)*(dist-partition2)); - } - else - { - return T(0.0); - } - }; - int node_id = i/(num_impacted_bins_x*num_impacted_bins_y); - int residual_index = i-node_id*num_impacted_bins_x*num_impacted_bins_y; - // x direction - int bin_index_xl = int((x_tensor[node_id]-xl-2*bin_size_x)/bin_size_x); - bin_index_xl = max(bin_index_xl, 0); - int k = bin_index_xl+int(residual_index / num_impacted_bins_y); - if (k+1 > num_bins_x) - { - return; - } - // y direction - int bin_index_yl = int((y_tensor[node_id]-yl-2*bin_size_y)/bin_size_y); - bin_index_yl = max(bin_index_yl, 0); - int h = bin_index_yl+(residual_index % num_impacted_bins_y); - if (h+1 > num_bins_y) - { - return; - } - - T px = computeDensityPotentialFunc(x_tensor[node_id], node_size_x_tensor[node_id], bin_center_x_tensor[k], bin_size_x, ax_tensor[node_id], bx_tensor[node_id], cx_tensor[node_id]); - T py = computeDensityPotentialFunc(y_tensor[node_id], node_size_y_tensor[node_id], bin_center_y_tensor[h], bin_size_y, ay_tensor[node_id], by_tensor[node_id], cy_tensor[node_id]); - //printf("px[%d, %d] = %g, py[%d, %d] = %g\n", k, h, px, k, h, py); - // still area - atomicAdd(&density_map_tensor[k*num_bins_y+h], px*py); - } -} - -template -__global__ void computeDensityGradient( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const int num_impacted_bins_x, const int num_impacted_bins_y, - const T* grad_tensor, const T target_area, - const T* density_map_tensor, - T* grad_x_tensor, T* grad_y_tensor - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - // rank-one update density map - if (i < num_nodes) - { - // density potential function - auto computeDensityPotentialFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ - // from origin to center - x += node_size/2; - //printf("x = %g, bin_center = %g\n", x, bin_center); - T dist = fabs(x-bin_center); - //printf("dist = %g\n", dist); - T partition1 = node_size/2+bin_size; - //printf("partition1 = %g\n", partition1); - T partition2 = partition1+bin_size; - //printf("partition2 = %g\n", partition2); - //printf("a = %g, b = %g, c = %g\n", a, b, c); - if (dist < partition1) - { - return c*(1-a*dist*dist); - } - else if (dist < partition2) - { - return c*(b*(dist-partition2)*(dist-partition2)); - } - else - { - return T(0.0); - } - }; - // density potential gradient function - auto computeDensityPotentialGradFunc = [](T x, T node_size, T bin_center, T bin_size, T a, T b, T c){ - // from origin to center - x += node_size/2; - T dist = fabs(x-bin_center); - T partition1 = node_size/2+bin_size; - T partition2 = partition1+bin_size; - if (dist < partition1) - { - return -2*c*a*(x-bin_center); - } - else if (dist < partition2) - { - T sign = (x < bin_center)? -1.0 : 1.0; - return 2*c*b*(dist-partition2)*sign; - } - else - { - return T(0.0); - } - }; - int bin_index_xl = int((x_tensor[i]-xl-2*bin_size_x)/bin_size_x); - int bin_index_xh = int(ceil((x_tensor[i]-xl+node_size_x_tensor[i]+2*bin_size_x)/bin_size_x))+1; // exclusive - bin_index_xl = max(bin_index_xl, 0); - // be careful about the bin_index_xl and bin_index_xh here - // the assumption is that num_bins_x >= num_impacted_bins_x - // each row of the px matrix should be filled with num_impacted_bins_x columns - bin_index_xl = min(bin_index_xl, num_bins_x-num_impacted_bins_x); - bin_index_xh = min(bin_index_xh, num_bins_x); - //int bin_index_xh = bin_index_xl+num_impacted_bins_x; - - int bin_index_yl = int((y_tensor[i]-yl-2*bin_size_y)/bin_size_y); - int bin_index_yh = int(ceil((y_tensor[i]-yl+node_size_y_tensor[i]+2*bin_size_y)/bin_size_y))+1; // exclusive - bin_index_yl = max(bin_index_yl, 0); - // be careful about the bin_index_yl and bin_index_yh here - // the assumption is that num_bins_y >= num_impacted_bins_y - // each row of the py matrix should be filled with num_impacted_bins_y columns - bin_index_yl = min(bin_index_yl, num_bins_y-num_impacted_bins_y); - bin_index_yh = min(bin_index_yh, num_bins_y); - //int bin_index_yh = bin_index_yl+num_impacted_bins_y; - - grad_x_tensor[i] = 0; - grad_y_tensor[i] = 0; - // update density potential map - for (int k = bin_index_xl; k < bin_index_xh; ++k) - { - T px = computeDensityPotentialFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); - T gradx = computeDensityPotentialGradFunc(x_tensor[i], node_size_x_tensor[i], bin_center_x_tensor[k], bin_size_x, ax_tensor[i], bx_tensor[i], cx_tensor[i]); - for (int h = bin_index_yl; h < bin_index_yh; ++h) - { - T py = computeDensityPotentialFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); - T grady = computeDensityPotentialGradFunc(y_tensor[i], node_size_y_tensor[i], bin_center_y_tensor[h], bin_size_y, ay_tensor[i], by_tensor[i], cy_tensor[i]); - - T delta = density_map_tensor[k*num_bins_y+h]-target_area; - //delta = max(delta, (T)0); - - grad_x_tensor[i] += 2*delta*py*gradx; - grad_y_tensor[i] += 2*delta*px*grady; - - } - } - - grad_x_tensor[i] *= *grad_tensor; - grad_y_tensor[i] *= *grad_tensor; - } -} - -template -int computeDensityPotentialMapCudaLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, - const T* bin_center_x_tensor, const T* bin_center_y_tensor, - const int num_impacted_bins_x, const int num_impacted_bins_y, - const int mat_size_x, const int mat_size_y, - const int num_nodes, - const int num_bins_x, const int num_bins_y, const int padding, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const T target_area, - T* density_map_tensor, - const T* grad_tensor, - T* grad_x_tensor, T* grad_y_tensor - ) -{ - int64_t block_count; - int64_t thread_count = 512; - - // compute gradient - if (grad_tensor) - { - block_count = (num_nodes - 1 + thread_count) / thread_count; - - computeDensityGradient<<>>( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - ax_tensor, bx_tensor, cx_tensor, - ay_tensor, by_tensor, cy_tensor, - bin_center_x_tensor, bin_center_y_tensor, - num_nodes, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_impacted_bins_x, num_impacted_bins_y, - grad_tensor, target_area, - density_map_tensor, - grad_x_tensor, grad_y_tensor - ); - - // print gradient - //printArray(grad_x_tensor, 10, "grad_x_tensor"); - //printArray(grad_y_tensor, 10, "grad_y_tensor"); - } - else - { - block_count = (int64_t(num_nodes)*num_impacted_bins_x*num_impacted_bins_y - 1 + thread_count) / thread_count; - - computeDensityMap<<>>( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - ax_tensor, bx_tensor, cx_tensor, - ay_tensor, by_tensor, cy_tensor, - bin_center_x_tensor, bin_center_y_tensor, - num_nodes, - num_bins_x, num_bins_y, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_impacted_bins_x, num_impacted_bins_y, - density_map_tensor); - - // print density map - //print2DArray(density_map_tensor, num_bins_x, num_bins_y, "potential density_map_tensor"); - //printScalar(bin_size_x, "bin_size_x"); - //printScalar(bin_size_y, "bin_size_y"); - //printScalar(target_area, "target_area"); - - } - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeDensityPotentialMapCudaLauncher(\ - const T* x_tensor, const T* y_tensor, \ - const T* node_size_x_tensor, const T* node_size_y_tensor, \ - const T* ax_tensor, const T* bx_tensor, const T* cx_tensor, \ - const T* ay_tensor, const T* by_tensor, const T* cy_tensor, \ - const T* bin_center_x_tensor, const T* bin_center_y_tensor, \ - const int num_impacted_bins_x, const int num_impacted_bins_y, \ - const int mat_size_x, const int mat_size_y, \ - const int num_nodes, \ - const int num_bins_x, const int num_bins_y, const int padding, \ - const T xl, const T yl, const T xh, const T yh, \ - const T bin_size_x, const T bin_size_y, \ - const T target_area, \ - T* density_map_tensor, \ - const T* grad_tensor, \ - T* grad_x_tensor, T* grad_y_tensor \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore deleted file mode 100644 index 5b6f5911..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*.pkl -*.dat -*.png -*.pklz -*.gds -__pycache__ diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt deleted file mode 100644 index c5192ce1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(OP_NAME draw_place) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${LIMBO_SOURCE_DIR} - ) - -set(LINK_LIBS utility_cxx - ${LIMBO_BINARY_DIR}/limbo/parsers/gdsii/stream/libgdsparser.a - ${LIMBO_BINARY_DIR}/limbo/thirdparty/gzstream/libgzstream.a - ${ZLIB_LIBRARIES} - ) - -if(CAIRO_FOUND) - set(INCLUDE_DIRS ${INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}) - set(LINK_LIBS ${LINK_LIBS} ${CAIRO_LIBRARIES}) - set(DRAWPLACE 1) -else() - set(DRAWPLACE 0) -endif() - -add_pytorch_extension(${TARGET_NAME}_cpp - src/draw_place.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS} - EXTRA_DEFINITIONS DRAWPLACE=${DRAWPLACE} - ) - -install(TARGETS ${TARGET_NAME}_cpp DESTINATION dreamplacefpga/ops/${OP_NAME}) -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py deleted file mode 100644 index 93dd6e6b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py +++ /dev/null @@ -1,218 +0,0 @@ -## -# @file PlaceDrawer.py -# @author Yibo Lin (DREAMPlace) -# @date Mar 2019 -# @brief A python implementation of placement drawer as an alternative when cairo C/C++ API is not available. -# - -import sys -import os -import time -import math -import cairocffi as cairo -import numpy as np - -class PlaceDrawer (object): - """ - @brief A python implementation of placement drawer as an alternative when cairo C/C++ API is not available. - """ - @staticmethod - def forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - filename, - iteration=None): - """ - @brief python implementation of placement drawer. - @param pos locations of cells - @param node_size_x array of cell width - @param node_size_y array of cell height - @param pin_offset_x pin offset to cell origin - @param pin_offset_y pin offset to cell origin - @param pin2node_map map pin to cell - @param xl left boundary - @param yl bottom boundary - @param xh right boundary - @param yh top boundary - @param site_width width of placement site - @param row_height height of placement row, equivalent to height of placement site - @param bin_size_x bin width - @param bin_size_y bin height - @param num_movable_nodes number of movable cells - @param num_filler_nodes number of filler cells - @param filename output filename - @param iteration current optimization step - """ - num_nodes = len(pos)//2 - num_movable_nodes = num_movable_nodes - num_filler_nodes = num_filler_nodes - num_physical_nodes = num_nodes-num_filler_nodes - num_bins_x = int(math.ceil((xh-xl)/bin_size_x)) - num_bins_y = int(math.ceil((yh-yl)/bin_size_y)) - x = np.array(pos[:num_nodes]) - y = np.array(pos[num_nodes:]) - node_size_x = np.array(node_size_x) - node_size_y = np.array(node_size_y) - pin_offset_x = np.array(pin_offset_x) - pin_offset_y = np.array(pin_offset_y) - pin2node_map = np.array(pin2node_map) - try: - tt = time.time() - width = 800 - height = 800 - line_width = 0.1 - padding = 0 - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - ctx = cairo.Context(surface) - # Do not use scale function. - # This is not compatible with show_text - - if num_movable_nodes < num_physical_nodes: - layout_xl = min(np.amin(x[num_movable_nodes:num_physical_nodes]), xl) - layout_yl = min(np.amin(y[num_movable_nodes:num_physical_nodes]), yl) - layout_xh = max(np.amax(x[num_movable_nodes:num_physical_nodes]+node_size_x[num_movable_nodes:num_physical_nodes]), xh) - layout_yh = max(np.amax(y[num_movable_nodes:num_physical_nodes]+node_size_y[num_movable_nodes:num_physical_nodes]), yh) - else: - layout_xl = xl - layout_yl = yl - layout_xh = xh - layout_yh = yh - - def bin_xl(id_x): - """ - @param id_x horizontal index - @return bin xl - """ - return xl+id_x*bin_size_x - - def bin_xh(id_x): - """ - @param id_x horizontal index - @return bin xh - """ - return min(bin_xl(id_x)+bin_size_x, xh) - - def bin_yl(id_y): - """ - @param id_y vertical index - @return bin yl - """ - return yl+id_y*bin_size_y - - def bin_yh(id_y): - """ - @param id_y vertical index - @return bin yh - """ - return min(bin_yl(id_y)+bin_size_y, yh) - - def normalize_x(xx): - return (xx - (layout_xl-padding*bin_size_x))/(layout_xh-layout_xl+padding*2*bin_size_x)*width - def normalize_y(xx): - return (xx - (layout_yl-padding*bin_size_y))/(layout_yh-layout_yl+padding*2*bin_size_y)*height - def draw_rect(x1, y1, x2, y2): - ctx.move_to(x1, y1) - ctx.line_to(x1, y2) - ctx.line_to(x2, y2) - ctx.line_to(x2, y1) - ctx.close_path() - ctx.stroke() - - # draw layout region - ctx.set_source_rgb(1, 1, 1) - draw_layout_xl = normalize_x(layout_xl-padding*bin_size_x) - draw_layout_yl = normalize_y(layout_yl-padding*bin_size_y) - draw_layout_xh = normalize_x(layout_xh+padding*bin_size_x) - draw_layout_yh = normalize_y(layout_yh+padding*bin_size_y) - ctx.rectangle(draw_layout_xl, draw_layout_yl, draw_layout_xh, draw_layout_yh) - ctx.fill() - ctx.set_line_width(line_width) - ctx.set_source_rgba(0.1, 0.1, 0.1, alpha=0.8) - ctx.move_to(normalize_x(xl), normalize_y(yl)) - ctx.line_to(normalize_x(xl), normalize_y(yh)) - ctx.line_to(normalize_x(xh), normalize_y(yh)) - ctx.line_to(normalize_x(xh), normalize_y(yl)) - ctx.close_path() - ctx.stroke() - ## draw bins - #for i in range(1, num_bins_x): - # ctx.move_to(normalize_x(bin_xl(i)), normalize_y(yl)) - # ctx.line_to(normalize_x(bin_xl(i)), normalize_y(yh)) - # ctx.close_path() - # ctx.stroke() - #for i in range(1, num_bins_y): - # ctx.move_to(normalize_x(xl), normalize_y(bin_yl(i))) - # ctx.line_to(normalize_x(xh), normalize_y(bin_yl(i))) - # ctx.close_path() - # ctx.stroke() - - # draw cells - ctx.set_font_size(16) - ctx.select_font_face("monospace", cairo.FONT_SLANT_NORMAL, - cairo.FONT_WEIGHT_NORMAL) - node_xl = x - node_yl = layout_yl+layout_yh-(y+node_size_y[0:len(y)]) # flip y - node_xh = node_xl+node_size_x[0:len(x)] - node_yh = layout_yl+layout_yh-y # flip y - node_xl = normalize_x(node_xl) - node_yl = normalize_y(node_yl) - node_xh = normalize_x(node_xh) - node_yh = normalize_y(node_yh) - ctx.set_line_width(line_width) - #print("plot layout") - # draw fixed macros - ctx.set_source_rgba(1, 0, 0, alpha=0.5) - for i in range(num_movable_nodes, num_physical_nodes): - ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) - ctx.fill() - ctx.set_source_rgba(0, 0, 0, alpha=1.0) # Solid color - for i in range(num_movable_nodes, num_physical_nodes): - draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) - # draw fillers - if len(node_xl) > num_physical_nodes: # filler is included - ctx.set_line_width(line_width) - ctx.set_source_rgba(115/255.0, 115/255.0, 125/255.0, alpha=0.5) # Solid color - for i in range(num_physical_nodes, num_nodes): - ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) - ctx.fill() - ctx.set_source_rgba(230/255.0, 230/255.0, 250/255.0, alpha=0.3) # Solid color - for i in range(num_physical_nodes, num_nodes): - draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) - # draw cells - ctx.set_line_width(line_width*2) - ctx.set_source_rgba(0, 0, 1, alpha=0.5) # Solid color - for i in range(num_movable_nodes): - ctx.rectangle(node_xl[i], node_yl[i], node_xh[i]-node_xl[i], node_yh[i]-node_yl[i]) # Rectangle(xl, yl, w, h) - ctx.fill() - ctx.set_source_rgba(0, 0, 0.8, alpha=0.8) # Solid color - for i in range(num_movable_nodes): - draw_rect(node_xl[i], node_yl[i], node_xh[i], node_yh[i]) - ## draw cell indices - #for i in range(num_nodes): - # ctx.move_to((node_xl[i]+node_xh[i])/2, (node_yl[i]+node_yh[i])/2) - # ctx.show_text("%d" % (i)) - - # show iteration - if iteration: - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(line_width*10) - ctx.select_font_face("monospace", cairo.FONT_SLANT_NORMAL, - cairo.FONT_WEIGHT_NORMAL) - ctx.set_font_size(32) - ctx.move_to(normalize_x((xl+xh)/2), normalize_y((yl+yh)/2)) - ctx.show_text('{:04}'.format(iteration)) - - surface.write_to_png(filename) # Output to PNG - print("[I] plotting to %s takes %.3f seconds" % (filename, time.time()-tt)) - except Exception as e: - print("[E] failed to plot") - print(str(e)) - return 0 - - return 1 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py deleted file mode 100644 index 2ca80a39..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py +++ /dev/null @@ -1,238 +0,0 @@ -## -# @file draw_place.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Jan 2021 -# @brief Plot placement to an image -# - -import os -import sys -import torch -from torch.autograd import Function - -import dreamplacefpga.ops.draw_place.draw_place_cpp as draw_place_cpp -import dreamplacefpga.ops.draw_place.PlaceDrawer as PlaceDrawer -import pdb -import numpy as np - -class DrawPlaceFunction(Function): - @staticmethod - def forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - filename - ): - ret = draw_place_cpp.forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - filename - ) - # if C/C++ API failed, try with python implementation - if not filename.endswith(".gds") and not ret: - ret = PlaceDrawer.PlaceDrawer.forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - filename - ) - return ret - -class DrawPlace(object): - """ - @brief Draw placement - """ - def __init__(self, placedb): - """ - @brief initialization - """ - self.node_size_x = torch.from_numpy(placedb.node_size_x).float() - self.node_size_y = torch.from_numpy(placedb.node_size_y).float() - self.pin_offset_x = torch.FloatTensor(placedb.pin_offset_x).float() - self.pin_offset_y = torch.FloatTensor(placedb.pin_offset_y).float() - self.pin2node_map = torch.from_numpy(placedb.pin2node_map) - self.xl = placedb.xl - self.yl = placedb.yl - self.xh = placedb.xh - self.yh = placedb.yh - self.site_width = placedb.width - self.row_height = placedb.height - self.bin_size_x = placedb.bin_size_x - self.bin_size_y = placedb.bin_size_y - self.num_movable_nodes = placedb.num_movable_nodes - self.num_filler_nodes = placedb.num_filler_nodes - - def forward(self, pos, filename): - """ - @param pos cell locations, array of x locations and then y locations - @param filename suffix specifies the format - """ - return DrawPlaceFunction.forward( - pos, - self.node_size_x, - self.node_size_y, - self.pin_offset_x, - self.pin_offset_y, - self.pin2node_map, - self.xl, - self.yl, - self.xh, - self.yh, - self.site_width, - self.row_height, - self.bin_size_x, - self.bin_size_y, - self.num_movable_nodes, - self.num_filler_nodes, - filename - ) - - def __call__(self, pos, filename): - """ - @brief top API - @param pos cell locations, array of x locations and then y locations - @param filename suffix specifies the format - """ - return self.forward(pos, filename) - -# FPGA version - Added by Rachel -class DrawPlaceFunctionFPGA(Function): - @staticmethod - def forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_physical_nodes, num_filler_nodes, - node2fence_region_map, - is_cc_node, - ffIdx, lutIdx, addIdx, - bramIdx, m9kIdx, m144kIdx, - dspIdx, - ioIdx,pllIdx, - filename - ): - ret = draw_place_cpp.fpga( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_physical_nodes, num_filler_nodes, - node2fence_region_map, - is_cc_node, - ffIdx, lutIdx, addIdx, - bramIdx, m9kIdx, m144kIdx, - dspIdx, - ioIdx,pllIdx, - filename - ) - # if C/C++ API failed, try with python implementation - if not filename.endswith(".gds") and not ret: - ret = PlaceDrawer.PlaceDrawer.forward( - pos, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - filename - ) - return ret - -class DrawPlaceFPGA(object): - """ - @brief Draw placement - """ - def __init__(self, placedb): - """ - @brief initialization - """ - if placedb.num_ccNodes > 0: - self.is_cc_node = torch.from_numpy(placedb.is_cc_node) - nodeSizeX = placedb.node_size_x - nodeSizeX[:placedb.num_physical_nodes][placedb.is_cc_node == 1] = 0.5 - self.node_size_x = torch.from_numpy(nodeSizeX) - else: - self.is_cc_node = torch.zeros(placedb.num_physical_nodes, dtype=torch.int32) - self.node_size_x = torch.from_numpy(placedb.node_size_x) - self.node_size_y = torch.from_numpy(placedb.node_size_y) - self.pin_offset_x = torch.from_numpy(placedb.pin_offset_x) - self.pin_offset_y = torch.from_numpy(placedb.pin_offset_y) - self.pin2node_map = torch.from_numpy(placedb.pin2node_map) - self.xl = placedb.xl - self.yl = placedb.yl - self.xh = placedb.xh - self.yh = placedb.yh - self.bin_size_x = placedb.bin_size_x - self.bin_size_y = placedb.bin_size_y - self.num_physical_nodes = placedb.num_physical_nodes - self.num_filler_nodes = placedb.num_filler_nodes - self.node2fence_region_map = torch.from_numpy(placedb.node2fence_region_map) - self.fmask = torch.from_numpy(placedb.io_mask) - self.node_x = torch.from_numpy(placedb.node_x) - self.node_y = torch.from_numpy(placedb.node_y) - self.num_ccNodes = placedb.num_ccNodes - - ##Use resource type identifier for color coding - self.ffIdx = placedb.rFFIdx - self.lutIdx = placedb.rLUTIdx - self.addIdx = placedb.rADDIdx - self.bramIdx = placedb.rBRAMIdx - self.m9kIdx = placedb.rM9KIdx - self.m144kIdx = placedb.rM144KIdx - self.dspIdx = placedb.rDSPIdx - self.ioIdx = placedb.rIOIdx - self.pllIdx = placedb.rPLLIdx - - def forward(self, pos, filename): - """ - @param pos cell locations, array of x locations and then y locations - @param filename suffix specifies the format - """ - fillers = torch.tensor(np.zeros(self.num_filler_nodes)).bool() - fmask = torch.cat((self.fmask,fillers,self.fmask, fillers),0) - allLoc = torch.cat((self.node_x, fillers.to(dtype=pos.dtype), self.node_y, fillers.to(dtype=pos.dtype)),0) - omask = ~fmask - newpos = pos*omask.to(dtype=pos.dtype) + allLoc*fmask.to(dtype=pos.dtype) - - return DrawPlaceFunctionFPGA.forward( - newpos, self.node_size_x, self.node_size_y, - self.pin_offset_x, self.pin_offset_y, - self.pin2node_map, self.xl, self.yl, - self.xh, self.yh, self.bin_size_x, - self.bin_size_y, self.num_physical_nodes, - self.num_filler_nodes, self.node2fence_region_map, - self.is_cc_node, self.ffIdx, self.lutIdx, - self.addIdx, self.bramIdx, self.m9kIdx, - self.m144kIdx, self.dspIdx, self.ioIdx, - self.pllIdx, filename) - - def __call__(self, pos, filename): - """ - @brief top API - @param pos cell locations, array of x locations and then y locations - @param filename suffix specifies the format - """ - return self.forward(pos, filename) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h deleted file mode 100644 index df22c208..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h +++ /dev/null @@ -1,437 +0,0 @@ -/** - * @file PlaceDrawFPGA.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef DREAMPLACE_PLACEDRAWFPGA_H -#define DREAMPLACE_PLACEDRAWFPGA_H - -#include -#include -#include -#include - -#if DRAWPLACE == 1 -#include -#include -#include -#include -#endif - -#include -#include -#include - -#include "utility/src/utils.h" - -typedef struct _cairo_surface cairo_surface_t; - -DREAMPLACE_BEGIN_NAMESPACE - -/// PlaceDrawFPGA write files in various formats -template -class PlaceDrawFPGA -{ - public: - typedef T coordinate_type; - typedef I index_type; - - enum FileFormat { - EPS = 0, // handle by cairo - PDF = 1, // handle by cairo - SVG = 2, // handle by cairo - PNG = 3 // handle by cairo - }; - enum DrawContent { - NONE = 0, - NODE = 1, - NODETEXT = 2, - PIN = 4, - NET = 8, - ALL = NODE|NODETEXT|PIN|NET - }; - /// constructor - PlaceDrawFPGA(const coordinate_type* x, const coordinate_type* y, - const coordinate_type* node_size_x, const coordinate_type* node_size_y, - const coordinate_type* pin_offset_x, const coordinate_type* pin_offset_y, - const index_type* pin2node_map, - const index_type num_nodes, - const index_type num_physical_nodes, - const index_type num_filler_nodes, - const index_type num_pins, - const coordinate_type xl, const coordinate_type yl, const coordinate_type xh, const coordinate_type yh, - const coordinate_type bin_size_x, const coordinate_type bin_size_y, - const index_type* node2fence_region_map, - const index_type* is_cc_node, - const int ffIdx, - const int lutIdx, - const int addIdx, - const int bramIdx, - const int m9kIdx, - const int m144kIdx, - const int dspIdx, - const int ioIdx, - const int pllIdx, - int content = ALL) - : m_x(x) - , m_y(y) - , m_node_size_x(node_size_x) - , m_node_size_y(node_size_y) - , m_pin_offset_x(pin_offset_x) - , m_pin_offset_y(pin_offset_y) - , m_pin2node_map(pin2node_map) - , m_num_nodes(num_nodes) - , m_num_physical_nodes(num_physical_nodes) - , m_num_filler_nodes(num_filler_nodes) - , m_num_pins(num_pins) - , m_xl(xl) - , m_yl(yl) - , m_xh(xh) - , m_yh(yh) - , m_bin_size_x(bin_size_x) - , m_bin_size_y(bin_size_y) - , m_node2fence_region_map(node2fence_region_map) - , m_is_cc_node(is_cc_node) - , m_ffId(ffIdx) - , m_lutId(lutIdx) - , m_addId(addIdx) - , m_bramId(bramIdx) - , m_m9kId(m9kIdx) - , m_m144kId(m144kIdx) - , m_dspId(dspIdx) - , m_ioId(ioIdx) - , m_pllId(pllIdx) - , m_content(content) - { - } - - bool run(std::string const& filename, FileFormat ff) const - { - dreamplacePrint(kINFO, "writing placement to %s\n", filename.c_str()); - bool flag = false; - - //PlaceDB const& placeDB = m_db.placeDB(); - - switch (ff) - { - case EPS: - case PDF: - case SVG: - case PNG: - flag = writeFig(filename.c_str(), m_xh*10, m_yh*10, ff); - break; - default: - dreamplacePrint(kERROR, "unknown writing format at line %u\n", __LINE__); - break; - } - - return flag; - } - - /// \param first and last mark nodes whose nets will be drawn - template - bool run(std::string const& filename, FileFormat ff, Iterator first, Iterator last) - { - m_sMarkNode.insert(first, last); - bool flag = run(filename, ff); - m_sMarkNode.clear(); - return flag; - } - protected: - /// write formats supported by cairo - /// \param width of screen - /// \param height of screen - void paintCairo(cairo_surface_t* cs, double width, double height) const - { -#if DRAWPLACE == 1 - double ratio[2] = { - width/(m_xh-m_xl), - height/(m_yh-m_yl) - }; - char buf[16]; - cairo_t *c; - cairo_text_extents_t extents; - - c=cairo_create(cs); - cairo_save(c); // save status - cairo_translate(c, 0-m_xl*ratio[0], height+m_yl*ratio[1]); // translate is additive - cairo_scale(c, ratio[0], -ratio[1]); // scale is additive - - // background - cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); - cairo_set_source_rgb(c, 1.0, 1.0, 1.0); - cairo_fill(c); - cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); - cairo_set_line_width(c, 0.001); - cairo_set_source_rgb(c, 0.1, 0.1, 0.1); - cairo_stroke(c); - - // bins - cairo_set_line_width(c, 0.001); - cairo_set_source_rgba(c, 0.1, 0.1, 0.1, 0.8); - for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) - { - cairo_move_to(c, bx, m_yl); - cairo_line_to(c, bx, m_yh); - cairo_stroke(c); - } - for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) - { - cairo_move_to(c, m_xl, by); - cairo_line_to(c, m_xh, by); - cairo_stroke(c); - } - - // nodes - cairo_set_line_width(c, 0.001); - cairo_select_font_face (c, "Sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - if (m_content&NODE) - { - // filler - for (int i = m_num_nodes-m_num_filler_nodes; i < m_num_nodes; ++i) - { - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 200/255.0, 200/255.0, 200/255.0, 0.5); - cairo_fill(c); - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 175/255.0, 175/255.0, 175/255.0, 0.8); - cairo_stroke(c); - if (m_content&NODETEXT) - { - sprintf(buf, "%u", i); - cairo_set_font_size (c, m_node_size_y[i]/20); - cairo_text_extents (c, buf, &extents); - cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); - cairo_show_text(c, buf); - } - } - // movable & fixed nodes - for (int i = 0; i < m_num_physical_nodes; ++i) - { - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - - if (m_is_cc_node[i] == 1) - { - cairo_set_source_rgba(c, 1, 50/255, 1, 0.7); - } - else if (m_node2fence_region_map[i] == m_lutId) - { - cairo_set_source_rgba(c, 0, 1, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_ffId) - { - cairo_set_source_rgba(c, 0, 0, 1, 0.7); - } - else if (m_node2fence_region_map[i] == m_dspId) - { - cairo_set_source_rgba(c, 1, 0.5, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_bramId || - m_node2fence_region_map[i] == m_m9kId) - { - cairo_set_source_rgba(c, 175/255.0, 0, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_m144kId) - { - cairo_set_source_rgba(c, 1, 107/255, 107/255, 0.7); - } - else if (m_node2fence_region_map[i] == m_ioId) - { - cairo_set_source_rgba(c, 0, 0, 100/255, 0.7); - } - else if (m_node2fence_region_map[i] == m_pllId) - { - cairo_set_source_rgba(c, 0, 100/255.0, 100/255.0, 0.7); - } - else if (m_node2fence_region_map[i] == m_addId) - { - cairo_set_source_rgba(c, 100/255.0, 0, 100/255.0, 0.7); - } else - { - cairo_set_source_rgba(c, 0, 0, 0, 1); - } - - cairo_fill(c); - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - - if (m_is_cc_node[i] == 1) - { - cairo_set_source_rgba(c, 1, 50/255, 1, 0.7); - } - else if (m_node2fence_region_map[i] == m_lutId) - { - cairo_set_source_rgba(c, 0, 1, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_ffId) - { - cairo_set_source_rgba(c, 0, 0, 1, 0.7); - } - else if (m_node2fence_region_map[i] == m_dspId) - { - cairo_set_source_rgba(c, 1, 0.5, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_bramId || - m_node2fence_region_map[i] == m_m9kId) - { - cairo_set_source_rgba(c, 175/255.0, 0, 0, 0.7); - } - else if (m_node2fence_region_map[i] == m_m144kId) - { - cairo_set_source_rgba(c, 1, 107/255, 107/255, 0.7); - } - else if (m_node2fence_region_map[i] == m_ioId) - { - cairo_set_source_rgba(c, 0, 0, 100/255, 0.7); - } - else if (m_node2fence_region_map[i] == m_pllId) - { - cairo_set_source_rgba(c, 0, 100/255.0, 100/255.0, 0.7); - } - else if (m_node2fence_region_map[i] == m_addId) - { - cairo_set_source_rgba(c, 100/255.0, 0, 100/255.0, 0.7); - } else - { - cairo_set_source_rgba(c, 0, 0, 0, 1); - } - - cairo_stroke(c); - - if (m_content&NODETEXT) - { - sprintf(buf, "%u", i); - cairo_set_font_size (c, m_node_size_y[i]/20); - cairo_text_extents (c, buf, &extents); - cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); - cairo_show_text(c, buf); - } - } - } - cairo_restore(c); - - cairo_show_page(c); - - cairo_destroy(c); -#else - dreamplacePrint(kWARN, "cs = %p, width = %g, height = %g are not used, as DRAWPLACE not enabled\n", cs, width, height); -#endif - } - bool writeFig(const char* fname, double width, double height, FileFormat ff) const - { -#if DRAWPLACE == 1 - cairo_surface_t *cs; - - switch (ff) - { - case PNG: - cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - break; - case PDF: - cs=cairo_pdf_surface_create(fname, width, height); - break; - case EPS: - cs=cairo_ps_surface_create(fname, width, height); - break; - case SVG: - cs=cairo_svg_surface_create(fname, width, height); - break; - default: - dreamplacePrint(kERROR, "unknown file format in %s\n", __func__); - return false; - } - - paintCairo(cs, width, height); - - cairo_surface_flush(cs); - // need additional writing call for PNG - if (ff == PNG) - cairo_surface_write_to_png(cs, fname); - cairo_surface_destroy(cs); - return true; -#else - dreamplacePrint(kWARN, "filename = %s, width = %g, height = %g, file format = %d not used, as DRAWPLACE not enabled\n", fname, width, height, (int)ff); - return false; -#endif - } - /// scale source coordinate to target screen - double scaleToScreen(double coord, double srcOffset, double srcSize, double tgtOffset, double tgtSize) const - { - double ratio = tgtSize/srcSize; - return tgtOffset + (coord-srcOffset)*ratio; - } - - /// automatically increment by 2 - /// \param reset controls whehter restart from 1 - unsigned getLayer(bool reset = false) const - { - static unsigned count = 0; - if (reset) - count = 0; - return (++count)<<1; - } - /// \param i node id - /// \return text to be shown on cell - std::string getTextOnNode(index_type i) const - { - return ""; - } - /// \param i pin id - /// \return text to be shown on pin - std::string getTextOnPin(index_type i) const - { - return "NA"; - } - /// \brief set pin bounding box - /// \param i pin id - void getPinBbox(index_type i, double scale_rato, coordinate_type& xl, coordinate_type& yl, coordinate_type& xh, coordinate_type& yh) const - { - index_type node_id = m_pin2node_map[i]; - coordinate_type x = m_x[node_id]; - coordinate_type y = m_y[node_id]; - coordinate_type offset_x = m_pin_offset_x[i]; - coordinate_type offset_y = m_pin_offset_y[i]; - coordinate_type pin_size = std::max(std::min(x, y)/10, (coordinate_type)(1.0/scale_rato)); - xl = x+offset_x-pin_size; - yl = y+offset_y-pin_size; - xh = x+offset_x+pin_size; - yh = y+offset_y+pin_size; - } - - const coordinate_type* m_x; - const coordinate_type* m_y; - const coordinate_type* m_node_size_x; - const coordinate_type* m_node_size_y; - const coordinate_type* m_pin_offset_x; - const coordinate_type* m_pin_offset_y; - const index_type* m_pin2node_map; - index_type m_num_nodes; - index_type m_num_physical_nodes; - index_type m_num_filler_nodes; - index_type m_num_pins; - coordinate_type m_xl; - coordinate_type m_yl; - coordinate_type m_xh; - coordinate_type m_yh; - coordinate_type m_bin_size_x; - coordinate_type m_bin_size_y; - const index_type* m_node2fence_region_map; - const index_type* m_is_cc_node; - const int m_ffId; - const int m_lutId; - const int m_addId; - const int m_bramId; - const int m_m9kId; - const int m_m144kId; - const int m_dspId; - const int m_ioId; - const int m_pllId; - std::set m_sMarkNode; ///< marked nodes whose net will be drawn - int m_content; ///< content for DrawContent -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h deleted file mode 100644 index 73b0bd02..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h +++ /dev/null @@ -1,462 +0,0 @@ -/** - * @file PlaceDrawer.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef DREAMPLACE_PLACEDRAWER_H -#define DREAMPLACE_PLACEDRAWER_H - -#include -#include -#include -#include - -#if DRAWPLACE == 1 -#include -#include -#include -#include -#endif - -#include -#include -#include - -#include "utility/src/utils.h" - -typedef struct _cairo_surface cairo_surface_t; - -DREAMPLACE_BEGIN_NAMESPACE - -/// PlaceDrawer write files in various formats -template -class PlaceDrawer -{ - public: - typedef T coordinate_type; - typedef I index_type; - - enum FileFormat { - EPS = 0, // handle by cairo - PDF = 1, // handle by cairo - SVG = 2, // handle by cairo - PNG = 3, // handle by cairo - GDSII = 4 - }; - enum DrawContent { - NONE = 0, - NODE = 1, - NODETEXT = 2, - PIN = 4, - NET = 8, - ALL = NODE|NODETEXT|PIN|NET - }; - /// constructor - PlaceDrawer(const coordinate_type* x, const coordinate_type* y, - const coordinate_type* node_size_x, const coordinate_type* node_size_y, - const coordinate_type* pin_offset_x, const coordinate_type* pin_offset_y, - const index_type* pin2node_map, - const index_type num_nodes, - const index_type num_movable_nodes, - const index_type num_filler_nodes, - const index_type num_pins, - const coordinate_type xl, const coordinate_type yl, const coordinate_type xh, const coordinate_type yh, - const coordinate_type site_width, const coordinate_type row_height, - const coordinate_type bin_size_x, const coordinate_type bin_size_y, - int content = ALL) - : m_x(x) - , m_y(y) - , m_node_size_x(node_size_x) - , m_node_size_y(node_size_y) - , m_pin_offset_x(pin_offset_x) - , m_pin_offset_y(pin_offset_y) - , m_pin2node_map(pin2node_map) - , m_num_nodes(num_nodes) - , m_num_movable_nodes(num_movable_nodes) - , m_num_filler_nodes(num_filler_nodes) - , m_num_pins(num_pins) - , m_xl(xl) - , m_yl(yl) - , m_xh(xh) - , m_yh(yh) - , m_site_width(site_width) - , m_row_height(row_height) - , m_bin_size_x(bin_size_x) - , m_bin_size_y(bin_size_y) - , m_content(content) - { - } - - bool run(std::string const& filename, FileFormat ff) const - { - dreamplacePrint(kINFO, "writing placement to %s\n", filename.c_str()); - bool flag = false; - - //PlaceDB const& placeDB = m_db.placeDB(); - - switch (ff) - { - case EPS: - case PDF: - case SVG: - case PNG: - flag = writeFig(filename.c_str(), 800, 800, ff); - break; - case GDSII: - flag = writeGdsii(filename); - break; - default: - dreamplacePrint(kERROR, "unknown writing format at line %u\n", __LINE__); - break; - } - - return flag; - } - - /// \param first and last mark nodes whose nets will be drawn - template - bool run(std::string const& filename, FileFormat ff, Iterator first, Iterator last) - { - m_sMarkNode.insert(first, last); - bool flag = run(filename, ff); - m_sMarkNode.clear(); - return flag; - } - protected: - /// write formats supported by cairo - /// \param width of screen - /// \param height of screen - void paintCairo(cairo_surface_t* cs, double width, double height) const - { -#if DRAWPLACE == 1 - double ratio[2] = { - width/(m_xh-m_xl), - height/(m_yh-m_yl) - }; - char buf[16]; - cairo_t *c; - cairo_text_extents_t extents; - - c=cairo_create(cs); - cairo_save(c); // save status - cairo_translate(c, 0-m_xl*ratio[0], height+m_yl*ratio[1]); // translate is additive - cairo_scale(c, ratio[0], -ratio[1]); // scale is additive - - // background - cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); - cairo_set_source_rgb(c, 1.0, 1.0, 1.0); - cairo_fill(c); - cairo_rectangle(c, m_xl, m_yl, (m_xh-m_xl), (m_yh-m_yl)); - cairo_set_line_width(c, 0.001); - cairo_set_source_rgb(c, 0.1, 0.1, 0.1); - cairo_stroke(c); - - // bins - cairo_set_line_width(c, 0.001); - cairo_set_source_rgba(c, 0.1, 0.1, 0.1, 0.8); - for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) - { - cairo_move_to(c, bx, m_yl); - cairo_line_to(c, bx, m_yh); - cairo_stroke(c); - } - for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) - { - cairo_move_to(c, m_xl, by); - cairo_line_to(c, m_xh, by); - cairo_stroke(c); - } - - // nodes - cairo_set_line_width(c, 0.001); - cairo_select_font_face (c, "Sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - if (m_content&NODE) - { - // fixed macro - for (int i = m_num_movable_nodes; i < m_num_nodes-m_num_filler_nodes; ++i) - { - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 1.0, 0.0, 0.0, 0.5); - cairo_fill(c); - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgb(c, 0.0, 0.0, 0.0); - cairo_stroke(c); - if (m_content&NODETEXT) - { - sprintf(buf, "%u", i); - cairo_set_font_size (c, m_node_size_y[i]/20); - cairo_text_extents (c, buf, &extents); - cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); - cairo_show_text(c, buf); - } - } - // filler - for (int i = m_num_nodes-m_num_filler_nodes; i < m_num_nodes; ++i) - { - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 115/255.0, 115/255.0, 125/255.0, 0.5); - cairo_fill(c); - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 100/255.0, 100/255.0, 100/255.0, 0.8); - cairo_stroke(c); - if (m_content&NODETEXT) - { - sprintf(buf, "%u", i); - cairo_set_font_size (c, m_node_size_y[i]/20); - cairo_text_extents (c, buf, &extents); - cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); - cairo_show_text(c, buf); - } - } - // movable - for (int i = 0; i < m_num_movable_nodes; ++i) - { - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 0, 0, 1, 0.5); - cairo_fill(c); - cairo_rectangle(c, m_x[i], m_y[i], m_node_size_x[i], m_node_size_y[i]); - cairo_set_source_rgba(c, 0, 0, 0.8, 0.8); - cairo_stroke(c); - if (m_content&NODETEXT) - { - sprintf(buf, "%u", i); - cairo_set_font_size (c, m_node_size_y[i]/20); - cairo_text_extents (c, buf, &extents); - cairo_move_to(c, (m_x[i]+m_node_size_x[i]/2)-(extents.width/2+extents.x_bearing), (m_y[i]+m_node_size_y[i]/2)-(extents.height/2+extents.y_bearing)); - cairo_show_text(c, buf); - } - } - } - cairo_restore(c); - - cairo_show_page(c); - - cairo_destroy(c); -#else - dreamplacePrint(kWARN, "cs = %p, width = %g, height = %g are not used, as DRAWPLACE not enabled\n", cs, width, height); -#endif - } - bool writeFig(const char* fname, double width, double height, FileFormat ff) const - { -#if DRAWPLACE == 1 - cairo_surface_t *cs; - - switch (ff) - { - case PNG: - cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - break; - case PDF: - cs=cairo_pdf_surface_create(fname, width, height); - break; - case EPS: - cs=cairo_ps_surface_create(fname, width, height); - break; - case SVG: - cs=cairo_svg_surface_create(fname, width, height); - break; - default: - dreamplacePrint(kERROR, "unknown file format in %s\n", __func__); - return false; - } - - paintCairo(cs, width, height); - - cairo_surface_flush(cs); - // need additional writing call for PNG - if (ff == PNG) - cairo_surface_write_to_png(cs, fname); - cairo_surface_destroy(cs); - return true; -#else - dreamplacePrint(kWARN, "filename = %s, width = %g, height = %g, file format = %d not used, as DRAWPLACE not enabled\n", fname, width, height, (int)ff); - return false; -#endif - } - /// scale source coordinate to target screen - double scaleToScreen(double coord, double srcOffset, double srcSize, double tgtOffset, double tgtSize) const - { - double ratio = tgtSize/srcSize; - return tgtOffset + (coord-srcOffset)*ratio; - } - - /// write gdsii format - virtual bool writeGdsii(std::string const& filename) const - { - double scale_rato = 1000; - GdsParser::GdsWriter gw (filename.c_str()); - gw.create_lib("TOP", 0.001, 1e-6/scale_rato); - gw.gds_write_bgnstr(); - gw.gds_write_strname("TOP"); - - // kernel function to fill in contents - writeGdsiiContent(gw, scale_rato); - - gw.gds_write_endstr(); - gw.gds_write_endlib(); - - return true; - } - /// write contents to GDSII - virtual void writeGdsiiContent(GdsParser::GdsWriter& gw, double scale_rato) const - { - // layer specification - // it is better to use even layers, because text appears on odd layers - const unsigned dieAreaLayer = getLayer(true); - const unsigned rowLayer = getLayer(false); - const unsigned subRowLayer = getLayer(false); - const unsigned binRowLayer = getLayer(false); - const unsigned binLayer = getLayer(false); - const unsigned sbinLayer = getLayer(false); - const unsigned movableCellBboxLayer = getLayer(false); - const unsigned fixedCellBboxLayer = getLayer(false); - const unsigned blockageBboxLayer = getLayer(false); - const unsigned fillerCellBboxLayer = getLayer(false); - const unsigned pinLayer = getLayer(false); - const unsigned multiRowCellBboxLayer = getLayer(false); - const unsigned movePathLayer = getLayer(false); - const unsigned markedNodeLayer = getLayer(false); // together with netLayer - const unsigned netLayer = getLayer(false); - - dreamplacePrint(kINFO, "Layer: dieArea:%u, row:%u, subRow:%u, binRow:%u, bin:%u, sbin:%u, movableCellBbox:%u, fixedCellBbox:%u, blockageBbox:%u, fillerCellBboxLayer:%u, pin:%u, multiRowCellBbox:%u, movePathLayer:%u, markedNodeLayer:%u, net:from %u\n", - dieAreaLayer, rowLayer, subRowLayer, binRowLayer, binLayer, sbinLayer, movableCellBboxLayer, fixedCellBboxLayer, blockageBboxLayer, fillerCellBboxLayer, pinLayer, multiRowCellBboxLayer, movePathLayer, markedNodeLayer, netLayer); - - char buf[1024]; - - // write dieArea - gw.write_box(dieAreaLayer, 0, m_xl*scale_rato, m_yl*scale_rato, m_xh*scale_rato, m_yh*scale_rato); - // write bins - for (coordinate_type bx = m_xl; bx < m_xh; bx += m_bin_size_x) - { - for (coordinate_type by = m_yl; by < m_yh; by += m_bin_size_y) - { - coordinate_type bxl = bx; - coordinate_type byl = by; - coordinate_type bxh = std::min(bxl+m_bin_size_x, m_xh); - coordinate_type byh = std::min(byl+m_bin_size_y, m_yh); - gw.write_box(binLayer, 0, bxl*scale_rato, byl*scale_rato, bxh*scale_rato, byh*scale_rato); - dreamplaceSPrint(kNONE, buf, "%u,%u", (unsigned int)round((bx-m_xl)/m_bin_size_x), (unsigned int)round((by-m_yl)/m_bin_size_y)); - gw.gds_create_text(buf, (bxl+bxh)/2*scale_rato, (byl+byh)/2*scale_rato, binLayer+1, 5); - } - } - // write cells - for (index_type i = 0; i < m_num_nodes; ++i) - { - // bounding box of cells and its name - coordinate_type node_xl = m_x[i]; - coordinate_type node_yl = m_y[i]; - coordinate_type node_xh = node_xl+m_node_size_x[i]; - coordinate_type node_yh = node_yl+m_node_size_y[i]; - unsigned layer; - if (i < m_num_movable_nodes) // movable cell - { - layer = movableCellBboxLayer; - } - else if (i >= m_num_nodes-m_num_filler_nodes) // filler cell - { - layer = fillerCellBboxLayer; - } - else // fixed cells - { - layer = fixedCellBboxLayer; - } - - if (layer == fixedCellBboxLayer || m_sMarkNode.empty()) // do not write cells if there are marked cells - { - gw.write_box(layer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); - dreamplaceSPrint(kNONE, buf, "(%u)%s", i, getTextOnNode(i).c_str()); - gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, layer+1, 5); - - if (i < m_num_movable_nodes && m_node_size_y[i] > m_row_height) // multi-row cell - { - gw.write_box(multiRowCellBboxLayer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); - gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, multiRowCellBboxLayer+1, 5); - } - } - if (m_sMarkNode.count(i)) // highlight marked nodes - { - gw.write_box(markedNodeLayer, 0, node_xl*scale_rato, node_yl*scale_rato, node_xh*scale_rato, node_yh*scale_rato); - dreamplaceSPrint(kNONE, buf, "(%u)%s", i, getTextOnNode(i).c_str()); - gw.gds_create_text(buf, (node_xl+node_xh)/2*scale_rato, (node_yl+node_yh)/2*scale_rato, markedNodeLayer+1, 5); - } - } - // write pins - for (index_type i = 0; i < m_num_pins; ++i) - { - coordinate_type pin_xl; - coordinate_type pin_yl; - coordinate_type pin_xh; - coordinate_type pin_yh; - getPinBbox(i, scale_rato, pin_xl, pin_yl, pin_xh, pin_yh); - // bounding box of pins and its macropin name - gw.write_box(pinLayer, 0, pin_xl*scale_rato, pin_yl*scale_rato, pin_xh*scale_rato, pin_yh*scale_rato); - gw.gds_create_text(getTextOnPin(i).c_str(), (pin_xl+pin_xh)/2*scale_rato, (pin_yl+pin_yh)/2*scale_rato, pinLayer+1, 5); - } - - } - /// automatically increment by 2 - /// \param reset controls whehter restart from 1 - unsigned getLayer(bool reset = false) const - { - static unsigned count = 0; - if (reset) - count = 0; - return (++count)<<1; - } - /// \param i node id - /// \return text to be shown on cell - std::string getTextOnNode(index_type i) const - { - return ""; - } - /// \param i pin id - /// \return text to be shown on pin - std::string getTextOnPin(index_type i) const - { - return "NA"; - } - /// \brief set pin bounding box - /// \param i pin id - void getPinBbox(index_type i, double scale_rato, coordinate_type& xl, coordinate_type& yl, coordinate_type& xh, coordinate_type& yh) const - { - index_type node_id = m_pin2node_map[i]; - coordinate_type x = m_x[node_id]; - coordinate_type y = m_y[node_id]; - coordinate_type offset_x = m_pin_offset_x[i]; - coordinate_type offset_y = m_pin_offset_y[i]; - coordinate_type pin_size = std::max(std::min(m_site_width, m_row_height)/10, (coordinate_type)(1.0/scale_rato)); - xl = x+offset_x-pin_size; - yl = y+offset_y-pin_size; - xh = x+offset_x+pin_size; - yh = y+offset_y+pin_size; - } - - const coordinate_type* m_x; - const coordinate_type* m_y; - const coordinate_type* m_node_size_x; - const coordinate_type* m_node_size_y; - const coordinate_type* m_pin_offset_x; - const coordinate_type* m_pin_offset_y; - const index_type* m_pin2node_map; - index_type m_num_nodes; - index_type m_num_movable_nodes; - index_type m_num_filler_nodes; - index_type m_num_pins; - coordinate_type m_xl; - coordinate_type m_yl; - coordinate_type m_xh; - coordinate_type m_yh; - coordinate_type m_site_width; - coordinate_type m_row_height; - coordinate_type m_bin_size_x; - coordinate_type m_bin_size_y; - std::set m_sMarkNode; ///< marked nodes whose net will be drawn - int m_content; ///< content for DrawContent -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp deleted file mode 100644 index a0a0d76d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/** - * @file src/draw_place.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Plot placement to an image - */ -#include -#include "utility/src/torch.h" -#include "draw_place/src/draw_place.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief plot placement solution to an image -/// @param pos cell locations, array of x locations and then y locations -/// @param node_size_x_tensor cell width array -/// @param node_size_y_tensor cell height array -/// @param pin_offset_x pin offset to its cell origin -/// @param pin_offset_y pin offset to its cell origin -/// @param pin2node_map map pin to cell -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param site_width width of a placement site -/// @param row_height height of a placement row, same as height of a placement site -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param filename output image file name -int draw_place_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - double xl, - double yl, - double xh, - double yh, - double site_width, - double row_height, - double bin_size_x, - double bin_size_y, - int num_movable_nodes, - int num_filler_nodes, - const std::string& filename - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - int num_nodes = pos.numel()/2; - - // Call the kernel launcher - int ret = 0; - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "drawPlaceLauncher", [&] { - ret = drawPlaceLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - num_nodes, - num_movable_nodes, - num_filler_nodes, - pin2node_map.numel(), - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - filename - ); - }); - - return ret; -} - -int draw_place_fpga( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_physical_nodes, - int num_filler_nodes, - at::Tensor node2fence_region_map, - at::Tensor is_cc_node, - int ffIdx, - int lutIdx, - int addIdx, - int bramIdx, - int m9kIdx, - int m144kIdx, - int dspIdx, - int ioIdx, - int pllIdx, - const std::string& filename - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - int num_nodes = pos.numel()/2; - - // Call the kernel launcher - int ret = 0; - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "drawPlaceLauncherFPGA", [&] { - ret = drawPlaceLauncherFPGA( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - num_nodes, - num_physical_nodes, - num_filler_nodes, - pin2node_map.numel(), - xl, yl, xh, yh, - bin_size_x, bin_size_y, - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(is_cc_node, int), - ffIdx, lutIdx, addIdx, - bramIdx, m9kIdx, m144kIdx, - dspIdx, - ioIdx, pllIdx, - filename - ); - }); - - return ret; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::draw_place_forward, "Draw place forward"); - m.def("fpga", &DREAMPLACE_NAMESPACE::draw_place_fpga, "Draw place FPGA"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h deleted file mode 100644 index 32c99929..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @file draw_place.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ -#ifndef DREAMPLACE_DRAW_PLACE_H -#define DREAMPLACE_DRAW_PLACE_H - -#include "draw_place/src/PlaceDrawer.h" -#include "draw_place/src/PlaceDrawFPGA.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int drawPlaceLauncher( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* pin_offset_x_tensor, const T* pin_offset_y_tensor, - const int* pin2node_map_tensor, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes, - const int num_pins, - const T xl, const T yl, const T xh, const T yh, - const T site_width, const T row_height, - const T bin_size_x, const T bin_size_y, - const std::string& filename - ) -{ - PlaceDrawer drawer ( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - pin_offset_x_tensor, pin_offset_y_tensor, - pin2node_map_tensor, - num_nodes, - num_movable_nodes, - num_filler_nodes, - num_pins, - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y - ); - typename PlaceDrawer::FileFormat ff; - if (filename.substr(filename.size()-4) == ".eps") - { - ff = PlaceDrawer::EPS; - } - else if (filename.substr(filename.size()-4) == ".pdf") - { - ff = PlaceDrawer::PDF; - } - else if (filename.substr(filename.size()-4) == ".svg") - { - ff = PlaceDrawer::SVG; - } - else if (filename.substr(filename.size()-4) == ".png") - { - ff = PlaceDrawer::PNG; - } - else - { - ff = PlaceDrawer::GDSII; - } - return drawer.run(filename, ff); -} - -template -int drawPlaceLauncherFPGA( - const T* x_tensor, const T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T* pin_offset_x_tensor, const T* pin_offset_y_tensor, - const int* pin2node_map_tensor, - const int num_nodes, - const int num_physical_nodes, - const int num_filler_nodes, - const int num_pins, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, - const int* node2fence_region_map, - const int* is_cc_node, - const int ffIdx, - const int lutIdx, - const int addIdx, - const int bramIdx, - const int m9kIdx, - const int m144kIdx, - const int dspIdx, - const int ioIdx, - const int pllIdx, - const std::string& filename - ) -{ - PlaceDrawFPGA drawer ( - x_tensor, y_tensor, - node_size_x_tensor, node_size_y_tensor, - pin_offset_x_tensor, pin_offset_y_tensor, - pin2node_map_tensor, - num_nodes, - num_physical_nodes, - num_filler_nodes, - num_pins, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - node2fence_region_map, - is_cc_node, - ffIdx, lutIdx, addIdx, - bramIdx, m9kIdx, m144kIdx, - dspIdx, - ioIdx, pllIdx - ); - typename PlaceDrawFPGA::FileFormat ff; - if (filename.substr(filename.size()-4) == ".eps") - { - ff = PlaceDrawFPGA::EPS; - } - else if (filename.substr(filename.size()-4) == ".pdf") - { - ff = PlaceDrawFPGA::PDF; - } - else if (filename.substr(filename.size()-4) == ".svg") - { - ff = PlaceDrawFPGA::SVG; - } - else { - ff = PlaceDrawFPGA::PNG; - } - return drawer.run(filename, ff); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt deleted file mode 100644 index 5643fb81..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(OP_NAME dsp_ram_legalization) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${LIMBO_SOURCE_DIR} - ${LEMON_INCLUDE_DIRS} - ) - -set(LINK_LIBS utility_cxx - lemon - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(legalize_cpp - src/legalize.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS} - ) - -install(TARGETS legalize_cpp DESTINATION dreamplacefpga/ops/${OP_NAME}) -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py deleted file mode 100644 index e3e3fd7a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @Date: Oct 2020 -# - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py deleted file mode 100644 index 2741f487..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py +++ /dev/null @@ -1,109 +0,0 @@ -''' -@File: dsp_ram_legalization.py -@Author: Rachel Selina Rajarathnam (DREAMPlaceFPGA) -@Date: May 2023 -''' -import math -import torch -from torch import nn -from torch.autograd import Function -import numpy as np -import pdb - -import dreamplacefpga.ops.dsp_ram_legalization.legalize_cpp as legalize_cpp -import dreamplacefpga.configure as configure - -import logging -logger = logging.getLogger(__name__) - -class LegalizeDSPRAMFunction(Function): - @staticmethod - def legalize(pos, placedb, region_id, model): - """ - @brief legalize DSP/RAM at the end of Global Placement - @param pos X/Y locations of all instances locX ndarray - @param placedb Placement Database - @param region_id Instance type identifier - @param model Use for region mask and wirelength preconditioner - @param num_nodes Instance count - @param num_sites Instance site count - @param sites Instance site ndarray - @param precondWL Instance wirelength preconditioner ndarray - @param dInit lg_max_dist_init - @param dIncr lg_max_dist_incr - @param fScale lg_flow_cost_scale - @param movVal Maximum & Average Instance movement (list) - @param outLoc Legalized Instance locations list - {x0, x1, ... xn, y0, y1, ... yn} - """ - lg_max_dist_init=10.0 - lg_max_dist_incr=10.0 - lg_flow_cost_scale=100.0 - numNodes = pos.numel()//2 - comp_id = placedb.rsrc2compId_map[region_id] - num_inst = placedb.num_movable_nodes_fence_region[comp_id] - outLoc = np.zeros(2*num_inst, dtype=np.float32).tolist() - - if region_id == placedb.rDSPIdx: - mask = model.data_collections.dsp_mask - sites = placedb.dspSiteXYs - else: - if region_id == placedb.rBRAMIdx or region_id == placedb.rM9KIdx: - mask = model.data_collections.ram0_mask - sites = placedb.ramSite0XYs - elif region_id == placedb.rM144KIdx: - mask = model.data_collections.ram1_mask - sites = placedb.ramSite1XYs - - locX = pos[:placedb.num_physical_nodes][mask].cpu().detach().numpy() - locY = pos[numNodes:numNodes+placedb.num_physical_nodes][mask].cpu().detach().numpy() - - num_sites = len(sites) - precondWL = model.precondWL[:placedb.num_physical_nodes][mask].cpu().detach().numpy() - movVal = np.zeros(2, dtype=np.float32).tolist() - - #Use auction algorithm - if placedb.sliceFF_ctrl_mode != "HALF" or num_inst > 0.8*num_sites: - # Assign num_sites as N for auction algorithm that employs an N->N mapping - cost = torch.ones(num_sites*num_sites, dtype=pos.dtype, device=pos.device) - cost *= -10000.0 - locations = torch.ones(num_sites, dtype=torch.int, device=pos.device) - locations *= -1 - displacement = torch.zeros(num_inst, dtype=pos.dtype, device=pos.device) - lg_sites = torch.from_numpy(sites.flatten()).to(pos.device) - posX = pos[:placedb.num_physical_nodes][mask].data - posY = pos[numNodes:numNodes+placedb.num_physical_nodes][mask].data - precond = model.precondWL[:placedb.num_physical_nodes][mask] - diff = num_sites - num_inst - if diff > 0: - tmp_diff = torch.zeros(diff, dtype=pos.dtype, device=pos.device) - posX = torch.cat((posX, tmp_diff), 0) - posY = torch.cat((posY, tmp_diff), 0) - tmp_diff += 1.0 - precond = torch.cat((precond, tmp_diff), 0) - - if pos.is_cuda: - cpu_locations = locations.cpu() - cpu_displacement = displacement.cpu() - legalize_cpp.legalize_auction(posX.cpu(), posY.cpu(), lg_sites.cpu(), - precond.cpu(), num_inst, num_sites, cost.cpu(), cpu_displacement, - cpu_locations) - locations.data.copy_(cpu_locations.data) - displacement.data.copy_(cpu_displacement.data) - else: - legalize_cpp.legalize_auction(posX, posY, lg_sites, precond, - num_inst, num_sites, cost, displacement, locations) - - outLoc[:num_inst] = sites[locations[:num_inst].cpu().detach().numpy()][:,0] - outLoc[num_inst:] = sites[locations[:num_inst].cpu().detach().numpy()][:,1] - outLoc = np.array(outLoc) - movVal[0] = displacement.max().item() - movVal[1] = displacement.mean().item() - else: - legalize_cpp.legalize(locX, locY, num_inst, num_sites, sites.flatten(), precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) - outLoc = np.array(outLoc) - - updLoc = torch.from_numpy(outLoc).to(dtype=pos.dtype, device=pos.device) - pos.data[:placedb.num_physical_nodes].masked_scatter_(mask, updLoc[:num_inst]) - pos.data[numNodes:numNodes+placedb.num_physical_nodes].masked_scatter_(mask, updLoc[num_inst:]) - - return movVal diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp deleted file mode 100644 index d75daab8..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/** - * @file dsp_ram_legalization.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Legalize DSP/RAM instances at the end of Global Placement. - */ -#include -#include -#include -#include - -#include -#include -#include -#include "utility/src/utils.h" -#include "utility/src/torch.h" -// Lemon for min cost flow -#include "lemon/list_graph.h" -#include "lemon/network_simplex.h" -#include "lemon/cost_scaling.h" -// local dependency -#include "dsp_ram_legalization/src/legalize_auction.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define INVALID -1 -#define AUCTION_MAX_EPS 5.0 // Larger values mean solution is more approximate -#define AUCTION_MIN_EPS 1.0 -#define AUCTION_FACTOR 0.1 -#define AUCTION_MAX_ITERS 9999 - -//Min-cost flow to legalize DSPs/RAMs -void legalize( - pybind11::array_t const& locX, - pybind11::array_t const& locY, - int const num_nodes, int const num_sites, - pybind11::array_t const& sites, - pybind11::array_t const& precond, - double const &lg_max_dist_init, double const &lg_max_dist_incr, - double const &lg_flow_cost_scale, pybind11::list &movVal, pybind11::list &out) -{ - typedef lemon::ListDigraph graphType; - graphType graph; - graphType::ArcMap capLo(graph); - graphType::ArcMap capHi(graph); - graphType::ArcMap cost(graph); - std::vector lNodes, rNodes; - std::vector lArcs, rArcs, mArcs; - std::vector> mArcPairs; - - //Source and target Nodes - graphType::Node s = graph.addNode(), t = graph.addNode(); - - //Add left nodes (blocks) and arcs between source node and left nodes - for (int i = 0; i < num_nodes; ++i) - { - lNodes.emplace_back(graph.addNode()); - lArcs.emplace_back(graph.addArc(s, lNodes.back())); - cost[lArcs.back()] = 0.0; - capLo[lArcs.back()] = 0.0; - capHi[lArcs.back()] = 1.0; - } - - //Add right nodes (sites) and arc between right nodes and target node - for (int j=0; j < num_sites; ++j) - { - rNodes.emplace_back(graph.addNode()); - rArcs.emplace_back(graph.addArc(rNodes.back(), t)); - cost[rArcs.back()] = 0.0; - capLo[rArcs.back()] = 0.0; - capHi[rArcs.back()] = 1.0; - } - - //To improve efficiency, we do not run matching for complete bipartite graph but incrementally add arcs when needed - double distMin = 0.0; - double distMax = lg_max_dist_init; - - while (true) - { - //Generate arcs between left (blocks) and right (sites) nodes, pruning based on distance - for (int blk = 0; blk < num_nodes; ++blk) - { - for (int st = 0; st < num_sites; ++st) - { - double dist = std::abs(locX.at(blk) - sites.at(st*2)) + std::abs(locY.at(blk) - sites.at(st*2+1)); - if (dist >= distMin && dist < distMax) - { - mArcs.emplace_back(graph.addArc(lNodes[blk], rNodes[st])); - mArcPairs.emplace_back(blk, st); - double mArcCost = dist * precond.at(blk) * lg_flow_cost_scale; - cost[mArcs.back()] = mArcCost; - capLo[mArcs.back()] = 0.0; - capHi[mArcs.back()] = 1.0; - } - } - } - - //Run min-cost flow - lemon::NetworkSimplex mcf(graph); - mcf.stSupply(s, t, num_nodes); - mcf.lowerMap(capLo).upperMap(capHi).costMap(cost); - mcf.run(); - - //A feasible solution must have flow size equal to the no of blocks - //If not, we need to increase the max distance constraint - double flowSize = 0.0; - for (const auto &arc : rArcs) - { - flowSize += mcf.flow(arc); - } - if (flowSize != num_nodes) - { - //Increase searching range - distMin = distMax; - distMax += lg_max_dist_incr; - continue; - } - ////DBG - //std::cout << "INFO: Search range min: 0 and max: " << distMax - // << " for " << num_nodes << " instances and " << num_sites - // << " sites with " << mArcs.size() << " arcs" < -int auctionAlgorithmLauncher( - const T* locX, - const T* locY, - const T* sites, - const T* precond, - const int num_nodes, - const int num_sites, - T* cost, - T* displacements, - int* outLoc) -{ - //Populate cost array - for (int blk = 0; blk < num_nodes; ++blk) - { - for (int st = 0; st < num_sites; ++st) - { - T dist = std::abs(locX[blk] - sites[st*2]) + std::abs(locY[blk] - sites[st*2+1]); - T mArcCost = dist * precond[blk]; - unsigned index = blk*num_sites+ st; - cost[index] = mArcCost; - } - } - - std::vector m_matrix; - std::vector m_item2person; - std::vector m_bids; - std::vector m_prices; - std::vector m_sbids; - - unsigned nn = num_sites*num_sites; - - m_matrix.resize(nn); - m_item2person.resize(num_sites); - m_bids.resize(nn); - m_prices.resize(num_sites); - m_sbids.resize(num_sites); - - std::copy(cost, cost+nn, m_matrix.data()); - - int ret = run_auction( - num_nodes, - num_sites, - m_matrix.data(), - outLoc, - AUCTION_MAX_EPS, - AUCTION_MIN_EPS, - AUCTION_FACTOR, - AUCTION_MAX_ITERS, - m_item2person.data(), - m_bids.data(), - m_prices.data(), - m_sbids.data() - ); - - ////Collect node displacements - for (int idx = 0; idx < num_nodes; ++idx) - { - int sId = outLoc[idx]; - if (sId != INVALID) - { - displacements[idx] = std::abs(locX[idx] - sites[sId*2]) + std::abs(locY[idx] - sites[sId*2+1]); - } - } - - return ret; -} - - -void legalize_auction( - at::Tensor posX, at::Tensor posY, at::Tensor sites, - at::Tensor precond, int num_nodes, int num_sites, - at::Tensor cost, at::Tensor displacements, - at::Tensor locations) -{ - CHECK_FLAT_CPU(posX); - CHECK_CONTIGUOUS(posX); - - CHECK_FLAT_CPU(posY); - CHECK_CONTIGUOUS(posY); - - CHECK_FLAT_CPU(sites); - CHECK_EVEN(sites); - CHECK_CONTIGUOUS(sites); - - CHECK_FLAT_CPU(precond); - CHECK_CONTIGUOUS(precond); - - CHECK_FLAT_CPU(cost); - CHECK_CONTIGUOUS(cost); - - ////DBG - //CPUTimer::hr_clock_rep timer_start, timer_stop; - //timer_start = CPUTimer::getGlobaltime(); - ////DBG - - DREAMPLACE_DISPATCH_FLOATING_TYPES(posX, "auctionAlgorithmLauncher", [&] { - auctionAlgorithmLauncher( - DREAMPLACE_TENSOR_DATA_PTR(posX, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(posY, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(sites, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(precond, scalar_t), - num_nodes, num_sites, - DREAMPLACE_TENSOR_DATA_PTR(cost, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(displacements, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(locations, int)); - }); - - ////DBG - //timer_stop = CPUTimer::getGlobaltime(); - //dreamplacePrint(kINFO, "Legalize using Auction Algorithm takes %g ms\n", - // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); - ////DBG -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("legalize", &DREAMPLACE_NAMESPACE::legalize, "Legalize DSP & RAM instances using Min-Cost Flow"); - m.def("legalize_auction", &DREAMPLACE_NAMESPACE::legalize_auction, "Legalize using Auction Algorithm"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h deleted file mode 100644 index f8541e8f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h +++ /dev/null @@ -1,164 +0,0 @@ -/** - * @file legalize_auction.h - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Jul 2023 - */ -#ifndef _DREAMPLACE_LEGALIZE_AUCTION_H -#define _DREAMPLACE_LEGALIZE_AUCTION_H - -#include -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -#define INVALID -1 -#define BIG_POSITIVE 9999999 - -/// Auction Algorithm -template -int run_auction( - int num_nodes, - int num_sites, - T* data_ptr, // data, num_sites*num_sites in row-major - int* person2item_ptr, // results - float auction_max_eps, - float auction_min_eps, - float auction_factor, - int auction_max_iters, - int* item2person_ptr=nullptr, - T* bids_ptr=nullptr, - T* prices_ptr=nullptr, - int* sbids_ptr=nullptr -) -{ - // Declare variables - bool allocate_flag = false; - if (!item2person_ptr) - { - item2person_ptr = (int*)malloc(num_sites * sizeof(int)); - bids_ptr = (T*)malloc(num_sites * num_sites * sizeof(T)); - prices_ptr = (T*)malloc(num_sites * sizeof(T)); - sbids_ptr = (int*)malloc(num_sites * sizeof(int)); - allocate_flag = true; - } - - T *data = data_ptr; - int *person2item = person2item_ptr; - int *item2person = item2person_ptr; - T *prices = prices_ptr; - int *sbids = sbids_ptr; - T *bids = bids_ptr; - int num_assigned = 0; - - for(int i = 0; i < num_sites; i++) { - prices[i] = 0.0; - person2item[i] = INVALID; - } - - float auction_eps = auction_max_eps; - int counter = 0; - while(auction_eps >= auction_min_eps && counter < auction_max_iters) { - for(int i = 0; i < num_sites; i++) { - person2item[i] = INVALID; - item2person[i] = INVALID; - } - num_assigned = 0; - - while(num_assigned < num_nodes && counter < auction_max_iters){ - counter += 1; - - std::memset(bids, BIG_POSITIVE, num_sites * num_sites * sizeof(T)); - std::memset(sbids, 0, num_sites * sizeof(int)); - - for(int i = 0; i < num_nodes; i++) { - if(person2item[i] == INVALID) { - T top1_val = BIG_POSITIVE; - T top2_val = BIG_POSITIVE; - int top1_col = BIG_POSITIVE; - T tmp_val = BIG_POSITIVE; - - for (int col = 0; col < num_sites; col++) - { - tmp_val = data[i * num_sites+ col]; - if (tmp_val < 0) - { - continue; - } - tmp_val = tmp_val + prices[col]; - if (tmp_val < top1_val) - { - top2_val = top1_val; - top1_col = col; - top1_val = tmp_val; - } - else if (tmp_val <= top2_val) - { - top2_val = tmp_val; - } - } - if (top2_val == BIG_POSITIVE) - { - top2_val = top1_val; - } - T bid = top1_val + auction_eps; - bids[i*num_sites + top1_col] = bid; - sbids[top1_col] = 1; - } - } - - for(int j = 0; j < num_sites; j++) { - if(sbids[j] != 0) { - T low_bid = BIG_POSITIVE; - int low_bidder = INVALID; - - T tmp_bid = BIG_POSITIVE; - for(int i = 0; i < num_nodes; i++){ - tmp_bid = bids[i*num_sites + j]; - if(tmp_bid < low_bid){ - low_bid = tmp_bid; - low_bidder = i; - } - } - - int current_person = item2person[j]; - if(current_person >= 0){ - person2item[current_person] = INVALID; - } else { - num_assigned++; - } - - prices[j] += low_bid; - person2item[low_bidder] = j; - item2person[j] = low_bidder; - } - } - } - - auction_eps *= auction_factor; - } - - ////DBG - ////Print results - //int score = 0; - //for (int i = 0; i < num_nodes; i++) { - // std::cout << i << " " << person2item[i] << std::endl; - // score += data[i * num_sites + person2item[i]]; - //} - //std::cout << "score=" <= num_nodes); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore deleted file mode 100644 index 80a85ad9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.csv -*.png diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt deleted file mode 100644 index 0ea7524f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -set(OP_NAME electric_potential) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/electric_density_map.cpp - src/electric_force.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/electric_density_map_cuda.cpp - src/electric_density_map_cuda_kernel.cu - src/electric_force_cuda.cpp - src/electric_force_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py deleted file mode 100644 index 28c21fad..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py +++ /dev/null @@ -1,210 +0,0 @@ -## -# @file electric_overflow.py -# @Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @Date: Oct 2020 -# - -import math -import numpy as np -import torch -from torch import nn -from torch.autograd import Function -from torch.nn import functional as F - -import dreamplacefpga.ops.electric_potential.electric_potential_cpp as electric_potential_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.electric_potential.electric_potential_cuda as electric_potential_cuda - -import pdb -import matplotlib -matplotlib.use('Agg') -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt - - -class ElectricDensityMapFunction(Function): - """ - @brief compute density overflow. - @param ctx pytorch API to store data for backward proporgation - @param pos location of cells, x and then y - @param node_size_x_clamped stretched size, max(bin_size*sqrt2, node_size) - @param node_size_y_clamped stretched size, max(bin_size*sqrt2, node_size) - @param offset_x (stretched size - node_size) / 2 - @param offset_y (stretched size - node_size) / 2 - @param ratio node_size_x * node_size_y for FPGA - @param initial_density_map density_map for fixed cells - @param target_density target density - @param xl left boundary - @param yl lower boundary - @param xh right boundary - @param yh upper boundary - @param bin_size_x bin width - @param bin_size_x bin height - @param num_movable_nodes number of movable cells - @param num_filler_nodes number of filler cells - @param padding bin padding to boundary of placement region - @param padding_mask padding mask with 0 and 1 to indicate padding bins with padding regions to be 1 - @param num_bins_x number of bins in horizontal direction - @param num_bins_y number of bins in vertical direction - @param num_movable_impacted_bins_x number of impacted bins for any movable cell in x direction - @param num_movable_impacted_bins_y number of impacted bins for any movable cell in y direction - @param num_filler_impacted_bins_x number of impacted bins for any filler cell in x direction - @param num_filler_impacted_bins_y number of impacted bins for any filler cell in y direction - @param sorted_node_map the indices of the movable node map - """ - @staticmethod - def forward( - pos, - node_size_x_clamped, - node_size_y_clamped, - offset_x, - offset_y, - ratio, - initial_density_map, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_filler_nodes, - num_bins_x, - num_bins_y, - deterministic_flag, - sorted_node_map, - stretchRatio): - - ##Rachel: Incorporate stretch ratio as a variable as it differs for Density/OVFL computation in FPGA - targetHalfSizeX = 0.5 * stretchRatio * bin_size_x - targetHalfSizeY = 0.5 * stretchRatio * bin_size_y - - if pos.is_cuda: - output = electric_potential_cuda.density_map_fpga( - pos.view(pos.numel()), node_size_x_clamped, - node_size_y_clamped, offset_x, offset_y, ratio.mul(0.25), - initial_density_map, xl, yl, xh, - yh, bin_size_x, bin_size_y, num_movable_nodes, - num_filler_nodes, num_bins_x, num_bins_y, - deterministic_flag, sorted_node_map, targetHalfSizeX, targetHalfSizeY) - else: - output = electric_potential_cpp.density_map_fpga( - pos.view(pos.numel()), node_size_x_clamped, - node_size_y_clamped, offset_x, offset_y, ratio.mul(0.25), - initial_density_map, xl, yl, xh, yh, bin_size_x, bin_size_y, - targetHalfSizeX, targetHalfSizeY, num_movable_nodes, - num_filler_nodes, num_bins_x, num_bins_y, - deterministic_flag) - - density_map = output.view([num_bins_x, num_bins_y]) - - return density_map - - -class ElectricOverflow(nn.Module): - def __init__( - self, - node_size_x, - node_size_y, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_terminals, - num_filler_nodes, - deterministic_flag, # control whether to use deterministic routine - sorted_node_map, - fence_regions=None, - stretchRatio=None): - super(ElectricOverflow, self).__init__() - self.node_size_x = node_size_x - self.node_size_y = node_size_y - - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.bin_size_x = bin_size_x - self.bin_size_y = bin_size_y - self.num_movable_nodes = num_movable_nodes - self.num_terminals = 0 - self.num_filler_nodes = num_filler_nodes - self.sorted_node_map = sorted_node_map - - self.deterministic_flag = deterministic_flag - #Rachel: Include explicit stretchRatio as it varies for DSP/RAM for Density and OVFL computation - self.stretchRatio = stretchRatio - - self.reset() - # initial density_map due to fixed cells - if fence_regions is not None: - self.initial_density_map = fence_regions - - def reset(self, data_collections=None): - sqrt2 = math.sqrt(2) - # clamped means stretch a cell to bin size - # clamped = max(bin_size*sqrt2, node_size) - # offset means half of the stretch size - # ratio means the original area over the stretched area - self.node_size_x_clamped = self.node_size_x - self.offset_x = self.node_size_x_clamped.mul(0.5) - self.node_size_y_clamped = self.node_size_y - self.offset_y = self.node_size_y_clamped.mul(0.5) - self.ratio = self.node_size_x_clamped * self.node_size_y_clamped - - # compute maximum impacted bins - self.num_bins_x = int(math.ceil((self.xh - self.xl) / self.bin_size_x)) - self.num_bins_y = int(math.ceil((self.yh - self.yl) / self.bin_size_y)) - - def forward(self, pos): - if self.initial_density_map is None: - pdb.set_trace() #Rachel: Should not reach here for FPGA - - density_map = ElectricDensityMapFunction.forward( - pos, self.node_size_x_clamped, self.node_size_y_clamped, - self.offset_x, self.offset_y, self.ratio, - self.initial_density_map, - self.xl, self.yl, self.xh, self.yh, self.bin_size_x, - self.bin_size_y, self.num_movable_nodes, self.num_filler_nodes, - self.num_bins_x, self.num_bins_y, - self.deterministic_flag, self.sorted_node_map, self.stretchRatio) - - bin_area = self.bin_size_x * self.bin_size_y - density_cost = (density_map - - self.target_density * bin_area).clamp_(min=0.0).sum().unsqueeze(0) - - return density_cost, density_map.max().unsqueeze(0) / bin_area - - -def plot(plot_count, density_map, padding, name): - """ - density map contour and heat map - """ - density_map = density_map[padding:density_map.shape[0] - padding, - padding:density_map.shape[1] - padding] - print("max density = %g @ %s" % - (np.amax(density_map), - np.unravel_index(np.argmax(density_map), density_map.shape))) - print("mean density = %g" % (np.mean(density_map))) - - fig = plt.figure() - ax = fig.gca(projection='3d') - - x = np.arange(density_map.shape[0]) - y = np.arange(density_map.shape[1]) - - x, y = np.meshgrid(x, y) - # looks like x and y should be swapped - ax.plot_surface(y, x, density_map, alpha=0.8) - - ax.set_xlabel('x') - ax.set_ylabel('y') - ax.set_zlabel('density') - - # plt.tight_layout() - plt.savefig(name + ".3d.png") - plt.close() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py deleted file mode 100644 index 5269cefe..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py +++ /dev/null @@ -1,430 +0,0 @@ -## -# @file electric_potential.py -# @Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @Date: Oct 2020 -# @brief electric potential according to e-place (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) -# - -import os -import sys -import math -import numpy as np -import time -import torch -from torch import nn -from torch.autograd import Function -from torch.nn import functional as F -import logging - -import dreamplacefpga.ops.dct.discrete_spectral_transform as discrete_spectral_transform - -import dreamplacefpga.ops.dct.dct2_fft2 as dct -from dreamplacefpga.ops.dct.discrete_spectral_transform import get_exact_expk as precompute_expk - -from dreamplacefpga.ops.electric_potential.electric_overflow import ElectricDensityMapFunction as ElectricDensityMapFunction -from dreamplacefpga.ops.electric_potential.electric_overflow import ElectricOverflow as ElectricOverflow - -import dreamplacefpga.ops.electric_potential.electric_potential_cpp as electric_potential_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.electric_potential.electric_potential_cuda as electric_potential_cuda - -import pdb -import matplotlib -matplotlib.use('Agg') - -logger = logging.getLogger(__name__) - -# global variable for plot -plot_count = 0 - - -class ElectricPotentialFunction(Function): - """ - @brief compute electric potential according to e-place. - """ - @staticmethod - def forward( - ctx, - pos, - node_size_x_clamped, - node_size_y_clamped, - offset_x, - offset_y, - ratio, - initial_density_map, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_filler_nodes, - num_bins_x, - num_bins_y, - deterministic_flag, - sorted_node_map, - exact_expkM=None, # exp(-j*pi*k/M) - exact_expkN=None, # exp(-j*pi*k/N) - inv_wu2_plus_wv2=None, # 1.0/(wu^2 + wv^2) - wu_by_wu2_plus_wv2_half=None, # wu/(wu^2 + wv^2)/2 - wv_by_wu2_plus_wv2_half=None, # wv/(wu^2 + wv^2)/2 - dct2=None, - idct2=None, - idct_idxst=None, - idxst_idct=None, - stretchRatio=None, - lock_flag=None - ): - - tt = time.time() - - # output consists of (density_cost, density_map, max_density) - ctx.node_size_x_clamped = node_size_x_clamped - ctx.node_size_y_clamped = node_size_y_clamped - ctx.offset_x = offset_x - ctx.offset_y = offset_y - ctx.ratio = ratio - ctx.xl = xl - ctx.yl = yl - ctx.xh = xh - ctx.yh = yh - ctx.bin_size_x = bin_size_x - ctx.bin_size_y = bin_size_y - ctx.num_movable_nodes = num_movable_nodes - ctx.num_filler_nodes = num_filler_nodes - ctx.num_bins_x = num_bins_x - ctx.num_bins_y = num_bins_y - ctx.pos = pos - ctx.sorted_node_map = sorted_node_map - ctx.stretchRatio = stretchRatio - - if lock_flag is None: - ctx.lock_flag = False - else: - ctx.lock_flag = lock_flag - #Return zero if there are no elements in this resourceType - if (num_movable_nodes == 0 and num_filler_nodes == 0) or ctx.lock_flag: - return torch.tensor(0, dtype=pos.dtype, device=pos.device) - - #If filler sizes become zero due to instance adjust area - if node_size_x_clamped[-1] == 0 or node_size_y_clamped[-1] == 0: - density_map = ElectricDensityMapFunction.forward( - pos, node_size_x_clamped[:num_movable_nodes], node_size_y_clamped[:num_movable_nodes], - offset_x[:num_movable_nodes], offset_y[:num_movable_nodes], - ratio[:num_movable_nodes], - initial_density_map, - xl, yl, xh, yh, bin_size_x, bin_size_y, - num_movable_nodes, 0, - num_bins_x, num_bins_y, - deterministic_flag, sorted_node_map, stretchRatio) - else: - density_map = ElectricDensityMapFunction.forward( - pos, node_size_x_clamped, node_size_y_clamped, offset_x, offset_y, - ratio, - initial_density_map, - xl, yl, xh, yh, bin_size_x, bin_size_y, - num_movable_nodes, num_filler_nodes, - num_bins_x, num_bins_y, - deterministic_flag, sorted_node_map, stretchRatio) - - # for DCT - M = num_bins_x - N = num_bins_y - - # wu and wv - if inv_wu2_plus_wv2 is None: - wu = torch.arange(M, - dtype=density_map.dtype, - device=density_map.device).mul(2 * np.pi / - M).view([M, 1]) - wv = torch.arange(N, - dtype=density_map.dtype, - device=density_map.device).mul(2 * np.pi / - N).view([1, N]) - wu2_plus_wv2 = wu.pow(2) + wv.pow(2) - wu2_plus_wv2[0, - 0] = 1.0 # avoid zero-division, it will be zeroed out - inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 - inv_wu2_plus_wv2[0, 0] = 0.0 - wu_by_wu2_plus_wv2_half = wu.mul(inv_wu2_plus_wv2).mul_(1. / 2) - wv_by_wu2_plus_wv2_half = wv.mul(inv_wu2_plus_wv2).mul_(1. / 2) - - # compute auv - density_map.mul_(1.0 / (ctx.bin_size_x * ctx.bin_size_y)) - - #auv = discrete_spectral_transform.dct2_2N(density_map, expk0=exact_expkM, expk1=exact_expkN) - auv = dct2.forward(density_map) - - # compute field xi - auv_by_wu2_plus_wv2_wu = auv.mul(wu_by_wu2_plus_wv2_half) - auv_by_wu2_plus_wv2_wv = auv.mul(wv_by_wu2_plus_wv2_half) - - ctx.field_map_x = idxst_idct.forward(auv_by_wu2_plus_wv2_wu) - - ctx.field_map_y = idct_idxst.forward(auv_by_wu2_plus_wv2_wv) - - auv_by_wu2_plus_wv2 = auv.mul(inv_wu2_plus_wv2) - - potential_map = idct2.forward(auv_by_wu2_plus_wv2) - - # compute energy - energy = potential_map.mul(density_map).sum() - return energy - - @staticmethod - def backward(ctx, grad_pos): - tt = time.time() - - #Return zero if there are no elements in this resourceType - if (ctx.num_movable_nodes == 0 and ctx.num_filler_nodes == 0) or ctx.lock_flag: - return None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None - - if grad_pos.is_cuda: - output = -electric_potential_cuda.electric_force_fpga( - grad_pos, ctx.num_bins_x, ctx.num_bins_y, - ctx.field_map_x.view([-1]), ctx.field_map_y.view([-1]), - ctx.pos, ctx.node_size_x_clamped, - ctx.node_size_y_clamped, - ctx.bin_size_x, ctx.bin_size_y, ctx.num_movable_nodes, - ctx.num_filler_nodes, ctx.sorted_node_map) - else: - output = -electric_potential_cpp.electric_force_fpga( - grad_pos, ctx.num_bins_x, ctx.num_bins_y, - ctx.field_map_x.view([-1]), ctx.field_map_y.view( - [-1]), ctx.pos, ctx.node_size_x_clamped, - ctx.node_size_y_clamped, - ctx.ratio, - ctx.bin_size_x, ctx.bin_size_y, ctx.num_movable_nodes, - ctx.num_filler_nodes) - return output, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None, None, \ - None, None, None - - -class ElectricPotential(ElectricOverflow): - """ - @brief Compute electric potential according to e-place - """ - def __init__( - self, - node_size_x, - node_size_y, - xl, - yl, - xh, - yh, - bin_size_x, - bin_size_y, - num_movable_nodes, - num_terminals, - num_filler_nodes, - deterministic_flag, # control whether to use deterministic routine - sorted_node_map, - region_id=None, - fence_regions=None, # [n_subregion, 4] as dummy macros added to initial density. (xl,yl,xh,yh) rectangles - node2fence_region_map=None, - placedb=None, - stretchRatio=None - ): - """ - @brief initialization - Be aware that all scalars must be python type instead of tensors. - Otherwise, GPU version can be weirdly slow. - @param node_size_x cell width array consisting of movable cells, fixed cells, and filler cells in order - @param node_size_y cell height array consisting of movable cells, fixed cells, and filler cells in order - @param movable_macro_mask some large movable macros need to be scaled to avoid halos - @param bin_center_x bin center x locations - @param bin_center_y bin center y locations - @param target_density target density - @param xl left boundary - @param yl bottom boundary - @param xh right boundary - @param yh top boundary - @param bin_size_x bin width - @param bin_size_y bin height - @param num_movable_nodes number of movable cells - @param num_terminals number of fixed cells - @param num_filler_nodes number of filler cells - @param padding bin padding to boundary of placement region - @param deterministic_flag control whether to use deterministic routine - @param fast_mode if true, only gradient is computed, while objective computation is skipped - @param region_id id for fence region, from 0 to N if there are N fence regions - @param fence_regions # [n_subregion, 4] as dummy macros added to initial density. (xl,yl,xh,yh) rectangles - @param node2fence_region_map node to region id map, non fence region is set to INT_MAX - @param placedb - """ - - if(region_id is not None): - ### reconstruct data structure - self.region_id = region_id - self.comp_id = placedb.rsrc2compId_map[region_id] - num_nodes = placedb.num_nodes - self.fence_region_mask = node2fence_region_map[:num_movable_nodes] == region_id - - node_size_x = torch.cat([node_size_x[:num_movable_nodes][self.fence_region_mask], - node_size_x[num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id]:num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id+1]]], 0) - node_size_y = torch.cat([node_size_y[:num_movable_nodes][self.fence_region_mask], - node_size_y[num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id]:num_nodes-num_filler_nodes+placedb.filler_start_map[self.comp_id+1]]], 0) - - num_movable_nodes = (self.fence_region_mask).long().sum().item() - num_filler_nodes = placedb.filler_start_map[self.comp_id+1]-placedb.filler_start_map[self.comp_id] - ## sorted cell is recomputed - sorted_node_map = torch.sort(node_size_x[:num_movable_nodes])[1].to(torch.int32) - ## make pos mask for fast forward - self.pos_mask = torch.zeros(2, placedb.num_nodes, dtype=torch.bool, device=node_size_x.device) - self.pos_mask[0,:placedb.num_movable_nodes].masked_fill_(self.fence_region_mask, 1) - self.pos_mask[1,:placedb.num_movable_nodes].masked_fill_(self.fence_region_mask, 1) - self.pos_mask[:,placedb.num_nodes-placedb.num_filler_nodes+placedb.filler_start_map[self.comp_id]:placedb.num_nodes-placedb.num_filler_nodes+placedb.filler_start_map[self.comp_id+1]] = 1 - self.pos_mask = self.pos_mask.view(-1) - - super(ElectricPotential, - self).__init__(node_size_x=node_size_x, - node_size_y=node_size_y, - xl=xl, - yl=yl, - xh=xh, - yh=yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=num_movable_nodes, - num_terminals=0, - num_filler_nodes=num_filler_nodes, - deterministic_flag=deterministic_flag, - sorted_node_map=sorted_node_map) - self.fence_regions = fence_regions - self.node2fence_region_map = node2fence_region_map - self.placedb = placedb - self.region_id = region_id - self.fence_region_mask = node2fence_region_map == region_id - ## set by build_density_op func - self.filler_start_map = None - self.filler_beg = None - self.filler_end = None - self.initial_density_map = None - self.lock_flag = False - - - def reset(self, data_collections=None): - """ Compute members derived from input - """ - if data_collections is not None and self.region_id is not None: - self.node_size_x = torch.cat([data_collections.node_size_x[:data_collections.num_movable_nodes][self.fence_region_mask[:data_collections.num_movable_nodes]], - data_collections.node_size_x[data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id]:data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id+1]]], 0) - self.node_size_y = torch.cat([data_collections.node_size_y[:data_collections.num_movable_nodes][self.fence_region_mask[:data_collections.num_movable_nodes]], - data_collections.node_size_y[data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id]:data_collections.num_nodes-data_collections.num_filler_nodes+data_collections.filler_start_map[self.comp_id+1]]], 0) - self.sorted_node_map = torch.sort(self.node_size_x[:(self.fence_region_mask).long().sum().item()])[1].to(torch.int32) - - super(ElectricPotential, self).reset() - - def setLockDSPRAM(self): - """ Set computation for DSP/RAM to zero after legalization - """ - if self.region_id is not None and self.region_id in self.placedb.dsp_ram_rsrcIds: - self.lock_flag = True - - def forward(self, pos, mode="density"): - assert mode in {"density", "overflow"}, "Only support density mode or overflow mode" - if(self.region_id is not None): - ### reconstruct pos, only extract cells in this electric field - pos = pos[self.pos_mask] - - if self.initial_density_map is None: - num_nodes = pos.size(0)//2 - if(self.fence_regions is not None): - self.initial_density_map = self.fence_regions - else: - pdb.set_trace() #Rachel: Should not reach here for FPGA - #logger.info("fixed density map: average %g, max %g, bin area %g" % - # (self.initial_density_map.mean(), - # self.initial_density_map.max(), - # self.bin_size_x * self.bin_size_y)) - - # expk - M = self.num_bins_x - N = self.num_bins_y - self.exact_expkM = precompute_expk(M, - dtype=pos.dtype, - device=pos.device) - self.exact_expkN = precompute_expk(N, - dtype=pos.dtype, - device=pos.device) - - # init dct2, idct2, idct_idxst, idxst_idct with expkM and expkN - self.dct2 = dct.DCT2(self.exact_expkM, self.exact_expkN) - self.idct2 = dct.IDCT2(self.exact_expkM, self.exact_expkN) - self.idct_idxst = dct.IDCT_IDXST(self.exact_expkM, - self.exact_expkN) - self.idxst_idct = dct.IDXST_IDCT(self.exact_expkM, - self.exact_expkN) - - ar = self.bin_size_x / self.bin_size_y * self.placedb.xWirelenWt / self.placedb.yWirelenWt - # wu and wv - wu = torch.arange(M, dtype=pos.dtype, device=pos.device).mul( - 2 * np.pi / M).view([M, 1]) - # scale wv because the aspect ratio of a bin may not be 1 - wv = torch.arange(N, dtype=pos.dtype, device=pos.device).mul(2*np.pi / N).view( - [1, N]).mul_(ar) - wu2_plus_wv2 = wu.pow(2) + wv.pow(2) - wu2_plus_wv2[0, - 0] = 1.0 # avoid zero-division, it will be zeroed out - self.inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 - self.inv_wu2_plus_wv2[0, 0] = 0.0 - self.wu_by_wu2_plus_wv2_half = wu.mul(self.inv_wu2_plus_wv2).mul_( - 1. / 2) - self.wv_by_wu2_plus_wv2_half = wv.mul(self.inv_wu2_plus_wv2).mul_( - 1. / 2) - - if(mode == "density"): - #print("Density computation for region: %d" %(self.region_id)) - return ElectricPotentialFunction.apply( - pos, self.node_size_x_clamped, self.node_size_y_clamped, - self.offset_x, self.offset_y, self.ratio, - self.initial_density_map, - self.xl, self.yl, self.xh, self.yh, self.bin_size_x, - self.bin_size_y, self.num_movable_nodes, self.num_filler_nodes, - self.num_bins_x, self.num_bins_y, - self.deterministic_flag, self.sorted_node_map, self.exact_expkM, - self.exact_expkN, self.inv_wu2_plus_wv2, - self.wu_by_wu2_plus_wv2_half, self.wv_by_wu2_plus_wv2_half, - self.dct2, self.idct2, self.idct_idxst, self.idxst_idct, - math.sqrt(2.0), self.lock_flag) - elif(mode == "overflow"): - ### num_filler_nodes is set 0 - #Return zero if there are no elements in this resourceType - if (self.num_movable_nodes == 0 and self.num_filler_nodes == 0) or self.lock_flag: - return torch.tensor(0, dtype=pos.dtype, device=pos.device), torch.tensor(0, dtype=pos.dtype, device=pos.device) - - density_map = ElectricDensityMapFunction.forward( - pos, self.node_size_x_clamped, self.node_size_y_clamped, - self.offset_x, self.offset_y, self.ratio, - self.initial_density_map, - self.xl, self.yl, self.xh, self.yh, self.bin_size_x, - self.bin_size_y, self.num_movable_nodes, 0, - self.num_bins_x, self.num_bins_y, - self.deterministic_flag, self.sorted_node_map, - self.placedb.overflowInstDensityStretchRatio[self.comp_id]) - - bin_area = self.bin_size_x * self.bin_size_y - density_cost = (density_map - bin_area).clamp_(min=0.0).sum() - - return density_cost, density_map.max() / bin_area - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h deleted file mode 100644 index 03977a3f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @file density_function.h - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - */ - -#ifndef DREAMPLACE_ELECTRIC_POTENTIAL_DENSITY_FUNCTION_H -#define DREAMPLACE_ELECTRIC_POTENTIAL_DENSITY_FUNCTION_H - -DREAMPLACE_BEGIN_NAMESPACE - -//Added by Rachel -// return non-negative value -#define DEFINE_FPGA_DENSITY_FUNCTION(type) \ - T fpga_density_function(T xh, T xl, int k, T bin_size) \ - { \ - T bin_k = k * bin_size; \ - T bin_kp1 = bin_k + bin_size; \ - return DREAMPLACE_STD_NAMESPACE::max(T(0.0), DREAMPLACE_STD_NAMESPACE::min(xh, bin_kp1) - DREAMPLACE_STD_NAMESPACE::max(xl, bin_k)); \ - } - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp deleted file mode 100644 index af7a4b4f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/** - * @file density_map.cpp - * @author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute density map according to e-place - * (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "electric_potential/src/density_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define fpga_density_function -template -DEFINE_FPGA_DENSITY_FUNCTION(T); - -/// @brief The fpga density model from elfPlace. -template -int computeFPGADensityMapLauncher( - const T* x_tensor, const T* y_tensor, const T* node_size_x_tensor, - const T* node_size_y_tensor, const T* offset_x_tensor, - const T* offset_y_tensor, const T* ratio_tensor, - const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, - const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, - const T targetHalfSizeX, const T targetHalfSizeY, - const int num_threads, AtomicOp atomic_add_op, - typename AtomicOp::type* buf_map); - - -#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - - -#define CALL_FPGA_LAUNCHER(begin, end, atomic_add_op, map_ptr) \ - computeFPGADensityMapLauncher( \ - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + begin, end - (begin),\ - num_bins_x, num_bins_y, xl, yl, xh, yh, bin_size_x, bin_size_y, \ - targetHalfSizeX, targetHalfSizeY, \ - at::get_num_threads(), atomic_add_op, map_ptr) - -/// @brief compute density map for movable and filler cells for FPGA -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param initial_density_map initial density map for fixed cells -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -at::Tensor density_map_fpga( - at::Tensor pos, at::Tensor node_size_x_clamped, - at::Tensor node_size_y_clamped, at::Tensor offset_x, at::Tensor offset_y, - at::Tensor ratio, at::Tensor initial_density_map, double xl, double yl, - double xh, double yh, double bin_size_x, double bin_size_y, - double targetHalfSizeX, double targetHalfSizeY, - int num_movable_nodes, int num_filler_nodes, int num_bins_x, - int num_bins_y, int deterministic_flag) { - - CHECK_FLAT_CPU(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = initial_density_map.clone(); - int num_nodes = pos.numel() / 2; - - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = - DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); - long scale_factor = (1L << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeFPGADensityMapLauncher", [&] { - if (deterministic_flag == 1) { - std::vector buf(num_bins, 0); - AtomicAdd atomic_add_op(scale_factor); - CALL_FPGA_LAUNCHER(0, num_movable_nodes, atomic_add_op, - buf.data()); - if (num_filler_nodes) { - CALL_FPGA_LAUNCHER(num_nodes - num_filler_nodes, num_nodes, - atomic_add_op, buf.data()); - } - scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - buf.data(), 1.0 / scale_factor, num_bins, - at::get_num_threads()); - } else { - auto buf = DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t); - AtomicAdd atomic_add_op; - CALL_FPGA_LAUNCHER(0, num_movable_nodes, atomic_add_op, buf); - if (num_filler_nodes) { - CALL_FPGA_LAUNCHER(num_nodes - num_filler_nodes, num_nodes, - atomic_add_op, buf); - } - } - }); - - return density_map; -} - -/// @brief Compute electric force for movable and filler cells -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -/// @param field_map_x electric field map in x direction -/// @param field_map_y electric field map in y direction -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -at::Tensor electric_force_fpga( - at::Tensor grad_pos, int num_bins_x, int num_bins_y, - at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, - at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, at::Tensor ratio, - double bin_size_x, double bin_size_y, - int num_movable_nodes, int num_filler_nodes); - -template -int computeFPGADensityMapLauncher( - const T* x_tensor, const T* y_tensor, const T* node_size_x_clamped_tensor, - const T* node_size_y_clamped_tensor, const T* offset_x_tensor, - const T* offset_y_tensor, const T* ratio_tensor, - const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, - const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, - const T targetHalfSizeX, const T targetHalfSizeY, - const int num_threads, AtomicOp atomic_add_op, - typename AtomicOp::type* buf_map) { - // density_map_tensor should be initialized outside - - T inv_bin_size_x = 1.0 / bin_size_x; - T inv_bin_size_y = 1.0 / bin_size_y; - // do not use dynamic scheduling for determinism - // int chunk_size = - // DREAMPLACE_STD_NAMESPACE::max(int(num_nodes/num_threads/16), 1); -#pragma omp parallel for num_threads( \ - num_threads) // schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) { - // use stretched node size - T node_size_x = node_size_x_clamped_tensor[i]; - T node_size_y = node_size_y_clamped_tensor[i]; - T node_x = x_tensor[i] + offset_x_tensor[i]; - T node_y = y_tensor[i] + offset_y_tensor[i]; - T offset_x = offset_x_tensor[i]; - T offset_y = offset_y_tensor[i]; - T ratio = ratio_tensor[i]; - - T regValX = DREAMPLACE_STD_NAMESPACE::min(node_x - xl, xh - node_x); - T halfSizeX = DREAMPLACE_STD_NAMESPACE::max(offset_x, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeX, regValX)); - - T bXLo = node_x - halfSizeX; - T bXHi = node_x + halfSizeX; - - int bin_index_xl = int(bXLo * inv_bin_size_x); - int bin_index_xh = - int((bXHi * inv_bin_size_x)) + 1; // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - T regValY = DREAMPLACE_STD_NAMESPACE::min(node_y - yl, yh - node_y); - T halfSizeY = DREAMPLACE_STD_NAMESPACE::max(offset_y, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeY, regValY)); - - T bYLo = node_y - halfSizeY; - T bYHi = node_y + halfSizeY; - - int bin_index_yl = int(bYLo * inv_bin_size_y); - int bin_index_yh = - int((bYHi * inv_bin_size_y)) + 1; // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - T inv_halfSizes = 1.0 / (halfSizeX * halfSizeY); - T instDensity = ratio * inv_halfSizes; - - // update density potential map - for (int k = bin_index_xl; k < bin_index_xh; ++k) { - T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); - T px_by_ratio = px * instDensity; - - for (int h = bin_index_yl; h < bin_index_yh; ++h) { - T py = - fpga_density_function(bYHi, bYLo, h, bin_size_y); - T area = px_by_ratio * py; - - atomic_add_op(&buf_map[k * num_bins_y + h], area); - } - } - } - - return 0; -} - -#undef CALL_FPGA_LAUNCHER - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("density_map_fpga", &DREAMPLACE_NAMESPACE::density_map_fpga, "ElectricPotential Density Map"); - m.def("electric_force_fpga", &DREAMPLACE_NAMESPACE::electric_force_fpga, "ElectricPotential Electric Force"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp deleted file mode 100644 index 38954f58..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file density_map_cuda.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute density map according to e-place - * (http://cseweb.ucsd.edu/~jlu/papers/eplace-todaes14/paper.pdf) - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - - -// The FPGA density model from elfPlace -template -int computeFPGADensityMapCudaLauncher( - const T* x_tensor, const T* y_tensor, const T* node_size_x_clamped_tensor, - const T* node_size_y_clamped_tensor, const T* offset_x_tensor, - const T* offset_y_tensor, const T* ratio_tensor, int num_nodes, - const int num_bins_x, const int num_bins_y, const T xl, const T yl, - const T xh, const T yh, const T bin_size_x, const T bin_size_y, - bool deterministic_flag, T* density_map_tensor, const int* sorted_node_map, - const T targetHalfSizeX, const T targetHalfSizeY); - -#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - - -/// @brief compute density map for movable and filler cells for FPGA -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) -/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) -/// @param offset_x (node_size_x - node_size_x_clamped)/2 -/// @param offset_y (node_size_y - node_size_y_clamped)/2 -/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * -/// node_size_y_clamped) -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param initial_density_map initial density map for fixed cells -/// @param target_density target density -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param padding bin padding to boundary of placement region -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -/// @param sorted_node_map the indices of the movable node map -at::Tensor density_map_fpga( - at::Tensor pos, at::Tensor node_size_x_clamped, - at::Tensor node_size_y_clamped, at::Tensor offset_x, at::Tensor offset_y, - at::Tensor ratio, at::Tensor initial_density_map, - double xl, double yl, double xh, double yh, double bin_size_x, double bin_size_y, - int num_movable_nodes, int num_filler_nodes, int num_bins_x, - int num_bins_y, int deterministic_flag, at::Tensor sorted_node_map, - double targetHalfSizeX, double targetHalfSizeY) { - CHECK_FLAT_CUDA(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor density_map = initial_density_map.clone(); - int num_nodes = pos.numel() / 2; - - // Added by Rachel - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeFPGADensityMapCudaLauncher", [&] { - computeFPGADensityMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t), - num_movable_nodes, num_bins_x, num_bins_y, xl, yl, - xh, yh, bin_size_x, bin_size_y, (bool)deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - targetHalfSizeX, targetHalfSizeY); - }); - - if (num_filler_nodes) { - int num_physical_nodes = num_nodes - num_filler_nodes; - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeFPGADensityMapCudaLauncher", [&] { - computeFPGADensityMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(offset_x, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(offset_y, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + num_physical_nodes, - num_filler_nodes, num_bins_x, num_bins_y, xl, yl, - xh, yh, bin_size_x, bin_size_y, (bool)deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(density_map, scalar_t), NULL, - targetHalfSizeX, targetHalfSizeY); - }); - } - - return density_map; -} - -/// @brief Compute electric force for movable and filler cells for FPGA -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -/// @param field_map_x electric field map in x direction -/// @param field_map_y electric field map in y direction -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) -/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) -/// @param offset_x (node_size_x - node_size_x_clamped)/2 -/// @param offset_y (node_size_y - node_size_y_clamped)/2 -/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * -/// node_size_y_clamped) -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -at::Tensor electric_force_fpga( - at::Tensor grad_pos, int num_bins_x, int num_bins_y, - at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, - at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, - double bin_size_x, double bin_size_y, - int num_movable_nodes, int num_filler_nodes, at::Tensor sorted_node_map); - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("density_map_fpga", &DREAMPLACE_NAMESPACE::density_map_fpga, - "ElectricPotential Density Map (CUDA)"); - m.def("electric_force_fpga", &DREAMPLACE_NAMESPACE::electric_force_fpga, - "ElectricPotential Electric Force (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu deleted file mode 100644 index d7fd543e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu +++ /dev/null @@ -1,182 +0,0 @@ -/** - * @file electric_density_map_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - */ -#include -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -// local dependency -#include "electric_potential/src/density_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define fpga_density_function -template -inline __device__ DEFINE_FPGA_DENSITY_FUNCTION(T); - -//Added by Rachel -template -__global__ void __launch_bounds__(1024, 8) computeFPGADensityMap( - const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, - const T *node_size_y_clamped_tensor, const T *offset_x_tensor, - const T *offset_y_tensor, const T *ratio_tensor, - const int num_nodes, const int num_bins_x, const int num_bins_y, const T xl, - const T yl, const T xh, const T yh, const T bin_size_x, const T bin_size_y, - const T inv_bin_size_x, const T inv_bin_size_y, AtomicOp atomicAddOp, - typename AtomicOp::type *density_map_tensor, - const int *sorted_node_map, ///< can be NULL if not sorted - const T targetHalfSizeX, const T targetHalfSizeY -) { - int index = blockIdx.x * blockDim.z + threadIdx.z; - if (index < num_nodes) { - int i = (sorted_node_map) ? sorted_node_map[index] : index; - - // use stretched node size - T node_size_x = node_size_x_clamped_tensor[i]; - T node_size_y = node_size_y_clamped_tensor[i]; - T node_x = x_tensor[i] + offset_x_tensor[i]; - T node_y = y_tensor[i] + offset_y_tensor[i]; - T offset_x = offset_x_tensor[i]; - T offset_y = offset_y_tensor[i]; - T ratio = ratio_tensor[i]; - - T regValX = DREAMPLACE_STD_NAMESPACE::min(node_x - xl, xh - node_x); - T halfSizeX = DREAMPLACE_STD_NAMESPACE::max(offset_x, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeX, regValX)); - - T bXLo = node_x - halfSizeX; - T bXHi = node_x + halfSizeX; - - int bin_index_xl = int(bXLo * inv_bin_size_x); - int bin_index_xh = int((bXHi * inv_bin_size_x)) + 1; // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - T regValY = DREAMPLACE_STD_NAMESPACE::min(node_y - yl, yh - node_y); - T halfSizeY = DREAMPLACE_STD_NAMESPACE::max(offset_y, DREAMPLACE_STD_NAMESPACE::min(targetHalfSizeY, regValY)); - - T bYLo = node_y - halfSizeY; - T bYHi = node_y + halfSizeY; - - int bin_index_yl = int(bYLo * inv_bin_size_y); - int bin_index_yh = int((bYHi * inv_bin_size_y)) + 1; // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh + 1, num_bins_y); - - T inv_halfSizes = 1.0 / (halfSizeX * halfSizeY); - T instDensity = ratio * inv_halfSizes; - - // update density potential map - for (int k = bin_index_xl + threadIdx.y; k < bin_index_xh; - k += blockDim.y) { - T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); - T px_by_ratio = px * instDensity; - - for (int h = bin_index_yl + threadIdx.x; h < bin_index_yh; - h += blockDim.x) { - T py = fpga_density_function(bYHi, bYLo, h, bin_size_y); - T area = px_by_ratio * py; - atomicAddOp(&density_map_tensor[k * num_bins_y + h], area); - - } - } - } -} - - -template -int computeFPGADensityMapCallKernel( - const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, - const T *node_size_y_clamped_tensor, const T *offset_x_tensor, - const T *offset_y_tensor, const T *ratio_tensor, int num_nodes, - const int num_bins_x, const int num_bins_y, - const T xl, const T yl, const T xh, const T yh, - const T bin_size_x, const T bin_size_y, AtomicOp atomicAddOp, - typename AtomicOp::type *density_map_tensor, const int *sorted_node_map, - const T targetHalfSizeX, const T targetHalfSizeY) { - int thread_count = 64; - dim3 blockSize(2, 2, thread_count); - - int block_count = (num_nodes - 1 + thread_count) / thread_count; - computeFPGADensityMap<<>>( - x_tensor, y_tensor, node_size_x_clamped_tensor, - node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, - ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, yl, xh, yh, - bin_size_x, bin_size_y, 1 / bin_size_x, 1 / bin_size_y, atomicAddOp, - density_map_tensor, sorted_node_map, targetHalfSizeX, targetHalfSizeY); - - return 0; -} - -template -int computeFPGADensityMapCudaLauncher( - const T *x_tensor, const T *y_tensor, const T *node_size_x_clamped_tensor, - const T *node_size_y_clamped_tensor, const T *offset_x_tensor, - const T *offset_y_tensor, const T *ratio_tensor, int num_nodes, - const int num_bins_x, const int num_bins_y, const T xl, const T yl, - const T xh, const T yh, const T bin_size_x, const T bin_size_y, - bool deterministic_flag, T *density_map_tensor, const int *sorted_node_map, - const T targetHalfSizeX, const T targetHalfSizeY) { - if (deterministic_flag) // deterministic implementation using unsigned long - // as fixed point number - { - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - unsigned long long int *scaled_density_map_tensor = NULL; - allocateCUDA(scaled_density_map_tensor, num_bins, unsigned long long int); - - AtomicAddCUDA atomicAddOp(scale_factor); - - int thread_count = 512; - copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, - thread_count>>>( - scaled_density_map_tensor, density_map_tensor, scale_factor, num_bins); - computeFPGADensityMapCallKernel( - x_tensor, y_tensor, node_size_x_clamped_tensor, - node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, - ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, - yl, xh, yh, bin_size_x, bin_size_y, atomicAddOp, - scaled_density_map_tensor, sorted_node_map, targetHalfSizeX, targetHalfSizeY); - copyScaleArray<<<(num_bins + thread_count - 1) / thread_count, - thread_count>>>(density_map_tensor, - scaled_density_map_tensor, - T(1.0 / scale_factor), num_bins); - - destroyCUDA(scaled_density_map_tensor); - } else { - AtomicAddCUDA atomicAddOp; - - computeFPGADensityMapCallKernel( - x_tensor, y_tensor, node_size_x_clamped_tensor, - node_size_y_clamped_tensor, offset_x_tensor, offset_y_tensor, - ratio_tensor, num_nodes, num_bins_x, num_bins_y, xl, - yl, xh, yh, bin_size_x, bin_size_y, atomicAddOp, density_map_tensor, - sorted_node_map, targetHalfSizeX, targetHalfSizeY); - } - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeFPGADensityMapCudaLauncher( \ - const T *x_tensor, const T *y_tensor, \ - const T *node_size_x_clamped_tensor, \ - const T *node_size_y_clamped_tensor, const T *offset_x_tensor, \ - const T *offset_y_tensor, const T *ratio_tensor, \ - const int num_nodes, const int num_bins_x, const int num_bins_y, \ - const T xl, const T yl, const T xh, const T yh, const T bin_size_x, \ - const T bin_size_y, bool deterministic_flag, T *density_map_tensor, \ - const int *sorted_node_map, const T targetHalfSizeX, \ - const T targetHalfSizeY); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp deleted file mode 100644 index 63b1dedb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file electric_force.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute electric force according to e-place - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" -// local dependency -#include "electric_potential/src/density_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define fpga_density_function -template -DEFINE_FPGA_DENSITY_FUNCTION(T); - -#define CHECK_FLAT_CPU(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -int computeElectricForceFPGALauncher( - int num_bins_x, int num_bins_y, - const T* field_map_x_tensor, const T* field_map_y_tensor, - const T* x_tensor, const T* y_tensor, - const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, - const T* ratio_tensor, - T bin_size_x, T bin_size_y, int num_nodes, int num_threads, - T* grad_x_tensor, T* grad_y_tensor); - -#define CALL_FPGA_LAUNCHER(begin, end) \ - computeElectricForceFPGALauncher( \ - num_bins_x, num_bins_y, \ - DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), \ - DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), \ - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(ratio, scalar_t) + begin, \ - bin_size_x, bin_size_y, end - (begin), at::get_num_threads(), \ - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + begin, \ - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes + begin) - - -/// @brief compute electric force for movable and filler cells -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -/// @param field_map_x electric field map in x direction -/// @param field_map_y electric field map in y direction -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x cell width array -/// @param node_size_y cell height array -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -at::Tensor electric_force_fpga( - at::Tensor grad_pos, int num_bins_x, int num_bins_y, - at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, - at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, at::Tensor ratio, - double bin_size_x, double bin_size_y, - int num_movable_nodes, int num_filler_nodes) { - CHECK_FLAT_CPU(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - at::Tensor grad_out = at::zeros_like(pos); - int num_nodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeElectricForceFPGALauncher", [&] { - CALL_FPGA_LAUNCHER(0, num_movable_nodes); - if (num_filler_nodes) { - int num_physical_nodes = num_nodes - num_filler_nodes; - CALL_FPGA_LAUNCHER(num_physical_nodes, num_nodes); - } - }); - - return grad_out.mul_(grad_pos); -} - -template -int computeElectricForceFPGALauncher( - int num_bins_x, int num_bins_y, - const T* field_map_x_tensor, const T* field_map_y_tensor, - const T* x_tensor, const T* y_tensor, - const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, - const T* ratio_tensor, - T bin_size_x, T bin_size_y, int num_nodes, int num_threads, - T* grad_x_tensor, T* grad_y_tensor) { - // density_map_tensor should be initialized outside - - T inv_bin_size_x = 1.0 / bin_size_x; - T inv_bin_size_y = 1.0 / bin_size_y; - int chunk_size = - DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) { - // use stretched node size - T node_size_x = node_size_x_clamped_tensor[i]; - T node_size_y = node_size_y_clamped_tensor[i]; - T node_x = x_tensor[i]; - T node_y = y_tensor[i]; - T ratio = ratio_tensor[i]; - - T bXLo = node_x; - T bXHi = node_x + node_size_x; - - // Yibo: looks very weird implementation, but this is how RePlAce implements - // it the common practice should be floor Zixuan and Jiaqi: use the common - // practice of floor - int bin_index_xl = int(bXLo * inv_bin_size_x); - int bin_index_xh = int(bXHi * inv_bin_size_x); // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x - 1); - - T bYLo = node_y; - T bYHi = node_y + node_size_y; - - // Yibo: looks very weird implementation, but this is how RePlAce implements - // it the common practice should be floor Zixuan and Jiaqi: use the common - // practice of floor - int bin_index_yl = int(bYLo * inv_bin_size_y); - int bin_index_yh = int(bYHi * inv_bin_size_y); // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y - 1); - - T& gx = grad_x_tensor[i]; - T& gy = grad_y_tensor[i]; - gx = 0.0; - gy = 0.0; - // update density potential map - for (int k = bin_index_xl; k <= bin_index_xh; ++k) { - T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); - for (int h = bin_index_yl; h <= bin_index_yh; ++h) { - T py = - fpga_density_function(bYHi, bYLo, h, bin_size_y); - T area = px * py; - - int idx = k * num_bins_y + h; - gx += area * field_map_x_tensor[idx]; - gy += area * field_map_y_tensor[idx]; - } - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp deleted file mode 100644 index 3ae9e4c6..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file electric_force_cuda.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - * @brief Compute electric force according to e-place - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT_CUDA(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -int computeElectricForceFPGACudaLauncher( - int num_bins_x, int num_bins_y, - const T* field_map_x_tensor, - const T* field_map_y_tensor, const T* x_tensor, const T* y_tensor, - const T* node_size_x_clamped_tensor, const T* node_size_y_clamped_tensor, - T bin_size_x, T bin_size_y, int num_nodes, T* grad_x_tensor, - T* grad_y_tensor, const int* sorted_node_map); - - -/// @brief compute electric force for movable and filler cells for FPGA -/// @param grad_pos input gradient from backward propagation -/// @param num_bins_x number of bins in horizontal bins -/// @param num_bins_y number of bins in vertical bins -/// @param num_movable_impacted_bins_x number of impacted bins for any movable -/// cell in x direction -/// @param num_movable_impacted_bins_y number of impacted bins for any movable -/// cell in y direction -/// @param num_filler_impacted_bins_x number of impacted bins for any filler -/// cell in x direction -/// @param num_filler_impacted_bins_y number of impacted bins for any filler -/// cell in y direction -/// @param field_map_x electric field map in x direction -/// @param field_map_y electric field map in y direction -/// @param pos cell locations. The array consists of all x locations and then y -/// locations. -/// @param node_size_x_clamped cell width array clamp(min = sqrt2 * bin_size_x) -/// @param node_size_y_clamped cell height array clamp(min = sqrt2 * bin_size_y) -/// @param offset_x (node_size_x - node_size_x_clamped)/2 -/// @param offset_y (node_size_y - node_size_y_clamped)/2 -/// @param ratio (node_size_x * node_size_y) / (node_size_x_clamped * -/// node_size_y_clamped) -/// @param bin_center_x bin center x locations -/// @param bin_center_y bin center y locations -/// @param xl left boundary -/// @param yl bottom boundary -/// @param xh right boundary -/// @param yh top boundary -/// @param bin_size_x bin width -/// @param bin_size_y bin height -/// @param num_movable_nodes number of movable cells -/// @param num_filler_nodes number of filler cells -/// @param sorted_node_map the indices of the movable node map -at::Tensor electric_force_fpga( - at::Tensor grad_pos, int num_bins_x, int num_bins_y, - at::Tensor field_map_x, at::Tensor field_map_y, at::Tensor pos, - at::Tensor node_size_x_clamped, at::Tensor node_size_y_clamped, - double bin_size_x, double bin_size_y, - int num_movable_nodes, int num_filler_nodes, at::Tensor sorted_node_map) { - CHECK_FLAT_CUDA(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT_CUDA(field_map_x); - CHECK_CONTIGUOUS(field_map_x); - CHECK_FLAT_CUDA(field_map_y); - CHECK_CONTIGUOUS(field_map_y); - - at::Tensor grad_out = at::zeros_like(pos); - int num_nodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeElectricForceFPGACudaLauncher", [&] { - computeElectricForceFPGACudaLauncher( - num_bins_x, num_bins_y, - DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t), - bin_size_x, bin_size_y, num_movable_nodes, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int)); - }); - - if (num_filler_nodes) { - int num_physical_nodes = num_nodes - num_filler_nodes; - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeElectricForceFPGACudaLauncher", [&] { - computeElectricForceFPGACudaLauncher( - num_bins_x, num_bins_y, - DREAMPLACE_TENSOR_DATA_PTR(field_map_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(field_map_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x_clamped, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_y_clamped, scalar_t) + - num_physical_nodes, - bin_size_x, bin_size_y, num_filler_nodes, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + - num_physical_nodes, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_nodes + - num_physical_nodes, - NULL); - }); - } - - return grad_out.mul_(grad_pos); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu deleted file mode 100644 index 2fffa566..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file electric_force_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Oct 2020 - */ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -// local dependency -#include "electric_potential/src/density_function.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// define fpga_density_function -template -inline __device__ DEFINE_FPGA_DENSITY_FUNCTION(T); - -template -__global__ void __launch_bounds__(1024, 8) computeElectricForceFPGA( - int num_bins_x, int num_bins_y, const T *field_map_x_tensor, - const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, - const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, - const T bin_size_x, const T bin_size_y, const T inv_bin_size_x, - const T inv_bin_size_y, int num_nodes, T *grad_x_tensor, T *grad_y_tensor, - const int *sorted_node_map ///< can be NULL if not sorted -) { - int index = blockIdx.x * blockDim.z + threadIdx.z; - if (index < num_nodes) { - int i = (sorted_node_map) ? sorted_node_map[index] : index; - - // use stretched node size - T node_size_x = node_size_x_clamped_tensor[i]; - T node_size_y = node_size_y_clamped_tensor[i]; - T node_x = x_tensor[i]; - T node_y = y_tensor[i]; - - T bXLo = node_x; - T bXHi = node_x + node_size_x; - - int bin_index_xl = int(bXLo * inv_bin_size_x); - int bin_index_xh = int((bXHi * inv_bin_size_x)); // exclusive - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x -1); - - T bYLo = node_y; - T bYHi = node_y + node_size_y; - - int bin_index_yl = int(bYLo * inv_bin_size_y); - int bin_index_yh = int((bYHi * inv_bin_size_y)); // exclusive - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y-1); - - // blockDim.x * blockDim.y threads will be used to update one node - // shared memory is used to privatize the atomic memory access to thread - // block - extern __shared__ unsigned char s_xy[]; - T *s_x = (T *)s_xy; - T *s_y = s_x + blockDim.z; - if (threadIdx.x == 0 && threadIdx.y == 0) { - s_x[threadIdx.z] = s_y[threadIdx.z] = 0; - } - __syncthreads(); - - T tmp_x, tmp_y; - tmp_x = 0; - tmp_y = 0; - - // update density potential map - for (int k = bin_index_xl + threadIdx.y; k <= bin_index_xh; - k += blockDim.y) { - T px = fpga_density_function(bXHi, bXLo, k, bin_size_x); - - for (int h = bin_index_yl + threadIdx.x; h <= bin_index_yh; - h += blockDim.x) { - T py = - fpga_density_function(bYHi, bYLo, h, bin_size_y); - T area = px * py; - - int idx = k * num_bins_y + h; - tmp_x += area * field_map_x_tensor[idx]; - tmp_y += area * field_map_y_tensor[idx]; - } - } - - atomicAdd(&s_x[threadIdx.z], tmp_x); - atomicAdd(&s_y[threadIdx.z], tmp_y); - __syncthreads(); - - if (threadIdx.x == 0 && threadIdx.y == 0) { - grad_x_tensor[i] = s_x[threadIdx.z]; - grad_y_tensor[i] = s_y[threadIdx.z]; - } - } -} - -template -int computeElectricForceFPGACudaLauncher( - int num_bins_x, int num_bins_y, - const T *field_map_x_tensor, - const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, - const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, - T bin_size_x, T bin_size_y, int num_nodes, T *grad_x_tensor, - T *grad_y_tensor, const int *sorted_node_map) { - int thread_count = 64; - dim3 blockSize(2, 2, thread_count); - size_t shared_mem_size = sizeof(T) * thread_count * 2; - - int block_count_nodes = (num_nodes + thread_count - 1) / thread_count; - computeElectricForceFPGA<<>>( - num_bins_x, num_bins_y, field_map_x_tensor, field_map_y_tensor, x_tensor, - y_tensor, node_size_x_clamped_tensor, node_size_y_clamped_tensor, - bin_size_x, bin_size_y, 1 / bin_size_x, 1 / bin_size_y, num_nodes, - grad_x_tensor, grad_y_tensor, sorted_node_map); - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeElectricForceFPGACudaLauncher( \ - int num_bins_x, int num_bins_y, const T *field_map_x_tensor, \ - const T *field_map_y_tensor, const T *x_tensor, const T *y_tensor, \ - const T *node_size_x_clamped_tensor, const T *node_size_y_clamped_tensor, \ - T bin_size_x, T bin_size_y, int num_nodes, T *grad_x_tensor, \ - T *grad_y_tensor, const int *sorted_node_map); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt deleted file mode 100644 index cd142a0e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -set(OP_NAME hpwl) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/hpwl.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cpp_atomic - src/hpwl_atomic.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - ${TARGET_NAME}_cpp_atomic - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/hpwl_cuda.cpp - src/hpwl_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda_atomic - src/hpwl_cuda_atomic.cpp - src/hpwl_cuda_atomic_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - ${TARGET_NAME}_cuda_atomic - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py deleted file mode 100644 index e372a75e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py +++ /dev/null @@ -1,128 +0,0 @@ -## -# @file hpwl.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date May 2023 -# - -import torch -from torch.autograd import Function -from torch import nn -import numpy as np -import pdb -import time - -import dreamplacefpga.ops.hpwl.hpwl_cpp as hpwl_cpp -import dreamplacefpga.ops.hpwl.hpwl_cpp_atomic as hpwl_cpp_atomic -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.hpwl.hpwl_cuda as hpwl_cuda - import dreamplacefpga.ops.hpwl.hpwl_cuda_atomic as hpwl_cuda_atomic - -class HPWLFunction(Function): - """compute half-perimeter wirelength. - @param pos pin location (x array, y array), not cell location - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param net_weights weight of nets - @param net_mask a boolean mask containing whether a net should be computed - @param pin2net_map pin2net map, second set of options - """ - @staticmethod - def forward(ctx, pos, flat_netpin, netpin_start, net_weights, - net_weights_x, net_mask, net_bounding_box_min, net_bounding_box_max, - xWeight, yWeight, num_threads): - - output = pos.new_empty(1) - if pos.is_cuda: - output = hpwl_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, net_mask) - return (net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight - else: - output = hpwl_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, - net_weights_x, net_mask, xWeight, yWeight, num_threads) - return output - -class HPWLAtomicFunction(Function): - """compute half-perimeter wirelength using atomic max/min. - @param pos pin location (x array, y array), not cell location - @param pin2net_map pin2net map, second set of options - @param net_weights weight of nets - @param net_mask a boolean mask containing whether a net should be computed - """ - @staticmethod - def forward(ctx, pos, pin2net_map, net_weights, net_weights_x, net_mask, xWeight, yWeight): - - output = pos.new_empty(1) - if pos.is_cuda: - output = hpwl_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask) - return ((net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight).sum().mul_(1.0/1000) - else: - output = hpwl_cpp_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask) - return ((net_weights_x*output[0]).sum()*xWeight + (net_weights*output[1]).sum()*yWeight).sum() - -class HPWL(nn.Module): - """ - @brief Compute half-perimeter wirelength. - Support two algoriths: net-by-net and atomic. - Different parameters are required for different algorithms. - """ - def __init__(self, placedb=None, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, - num_carry_chains=None, cc_net_weight=None, dir_net_weight=None, net_mask=None, net_bounding_box_min=None, - net_bounding_box_max=None, num_threads=None, algorithm='atomic'): - """ - @brief initialization - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore - @param algorithm must be net-by-net | atomic - """ - super(HPWL, self).__init__() - assert net_mask is not None, "net_mask is a requried parameter" - if algorithm == 'net-by-net': - assert flat_netpin is not None and netpin_start is not None, "flat_netpin, netpin_start are requried parameters for algorithm net-by-net" - elif algorithm == 'atomic': - assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" - self.xWeight = placedb.xWirelenWt - self.yWeight = placedb.yWirelenWt - self.flat_netpin = flat_netpin - self.netpin_start = netpin_start - self.pin2net_map = pin2net_map - self.net_weights = net_weights - self.net_weights_x = net_weights.detach().clone() - self.num_carry_chains = num_carry_chains - self.cc_net_weight = cc_net_weight - self.dir_net_weight = dir_net_weight - self.net_mask = net_mask - self.algorithm = algorithm - self.num_threads = num_threads - self.net_bounding_box_min = net_bounding_box_min - self.net_bounding_box_max = net_bounding_box_max - - #Weight x direction 50% more than y for carry chains - if self.num_carry_chains > 0 and self.cc_net_weight > 1.0 and self.dir_net_weight: - self.net_weights_x[self.net_weights_x == cc_net_weight] = 1.5*cc_net_weight - - def forward(self, pos): - if self.algorithm == 'net-by-net': - return HPWLFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.net_weights, - self.net_weights_x, - self.net_mask, - self.net_bounding_box_min, - self.net_bounding_box_max, - self.xWeight, - self.yWeight, - self.num_threads - ) - elif self.algorithm == 'atomic': - return HPWLAtomicFunction.apply(pos, - self.pin2net_map, - self.net_weights, - self.net_weights_x, - self.net_mask, - self.xWeight, - self.yWeight - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp deleted file mode 100644 index 25d26354..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file hpwl.cpp - * @author Yibo Lin (DREAMPlace) Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date May 2023 - * @brief Compute half-perimeter wirelength - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeHPWLLauncher( - const T* x, const T* y, - const T* net_weights, - const T* net_weights_x, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T xWeight, - T yWeight, - int num_threads, - T* hpwl - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute half-perimeter wirelength -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -at::Tensor hpwl_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - double xWeight, - double yWeight, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_weights_x); - CHECK_CONTIGUOUS(net_weights_x); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = netpin_start.numel()-1; - at::Tensor hpwl = at::zeros(num_nets, pos.options()); - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLLauncher", [&] { - computeHPWLLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, xWeight, yWeight, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(hpwl, scalar_t) - ); - }); - //if (net_weights.numel()) - //{ - // hpwl.mul_(net_weights); - //} - return hpwl.sum(); -} - -template -int computeHPWLLauncher( - const T* x, const T* y, - const T* net_weights, - const T* net_weights_x, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T xWeight, - T yWeight, - int num_threads, - T* hpwl - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nets; ++i) - { - T max_x = -std::numeric_limits::max(); - T min_x = std::numeric_limits::max(); - T max_y = -std::numeric_limits::max(); - T min_y = std::numeric_limits::max(); - - // ignore large degree nets - if (net_mask[i]) - { - for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) - { - min_x = std::min(min_x, x[flat_netpin[j]]); - max_x = std::max(max_x, x[flat_netpin[j]]); - min_y = std::min(min_y, y[flat_netpin[j]]); - max_y = std::max(max_y, y[flat_netpin[j]]); - } - hpwl[i] = ((max_x-min_x)*net_weights_x[i]*xWeight) + ((max_y-min_y)*net_weights[i]*yWeight); - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_forward, "HPWL forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp deleted file mode 100644 index c11c3670..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file hpwl_atomic.cpp - * @author Yibo Lin (DREAMPlace) - * @date Mar 2019 - * @brief Compute half-perimeter wirelength to mimic a parallel atomic implementation - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeHPWLAtomicLauncher( - const T* x, const T* y, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - T* partial_hpwl_max, - T* partial_hpwl_min - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute half-perimeter wirelength -/// @param pos cell locations, array of x locations and then y locations -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -at::Tensor hpwl_atomic_forward( - at::Tensor pos, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = net_mask.numel(); - // x then y - at::Tensor partial_hpwl_max = at::zeros({2, num_nets}, pos.type()); - at::Tensor partial_hpwl_min = at::zeros({2, num_nets}, pos.type()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLAtomicLauncher", [&] { - partial_hpwl_max[0].fill_(std::numeric_limits::min()); - partial_hpwl_max[1].fill_(std::numeric_limits::min()); - partial_hpwl_min[0].fill_(std::numeric_limits::max()); - partial_hpwl_min[1].fill_(std::numeric_limits::max()); - computeHPWLAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - pin2net_map.numel(), - DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_max, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_min, scalar_t) - ); - }); - - auto hpwl = (partial_hpwl_max-partial_hpwl_min); - - //if (net_weights.numel()) - //{ - // hpwl.mul_(net_weights.view({1, num_nets})); - //} - - //return hpwl.sum(); - return hpwl; -} - -template -int computeHPWLAtomicLauncher( - const T* x, const T* y, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - T* partial_hpwl_max, - T* partial_hpwl_min - ) -{ - T* partial_hpwl_x_max = partial_hpwl_max; - T* partial_hpwl_x_min = partial_hpwl_min; - T* partial_hpwl_y_max = partial_hpwl_max+num_nets; - T* partial_hpwl_y_min = partial_hpwl_min+num_nets; - for (int i = 0; i < num_pins; ++i) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - partial_hpwl_x_max[net_id] = std::max(partial_hpwl_x_max[net_id], x[i]); - partial_hpwl_x_min[net_id] = std::min(partial_hpwl_x_min[net_id], x[i]); - partial_hpwl_y_max[net_id] = std::max(partial_hpwl_y_max[net_id], y[i]); - partial_hpwl_y_min[net_id] = std::min(partial_hpwl_y_min[net_id], y[i]); - } - else - { - partial_hpwl_x_max[net_id] = 0; - partial_hpwl_x_min[net_id] = 0; - partial_hpwl_y_max[net_id] = 0; - partial_hpwl_y_min[net_id] = 0; - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_atomic_forward, "HPWL forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp deleted file mode 100644 index 9372b8b5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @file hpwl_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute half-perimeter wirelength - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" -using namespace torch::indexing; - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeHPWLCudaLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* partial_wl - ); - -template -int computeHPWLCudaLauncherFPGA( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* bbox_min_x, - T* bbox_max_x, - T* bbox_min_y, - T* bbox_max_y, - T* partial_wl - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute half-perimeter wirelength -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -at::Tensor hpwl_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_mask - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - // x then y - int num_nets = net_mask.numel(); - at::Tensor partial_wl = at::zeros({2, num_nets}, pos.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLCudaLauncher", [&] { - computeHPWLCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t) - ); - }); - //std::cout << "partial_hpwl = \n" << partial_wl << "\n"; - - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({1, num_nets})); - } - return partial_wl; -} - -/// @brief Compute half-perimeter wirelength along with net bbox -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -at::Tensor hpwl_forward_fpga( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor net_bounding_box_min, - at::Tensor net_bounding_box_max - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - // x then y - int num_nets = net_mask.numel(); - at::Tensor partial_wl = at::zeros({2, num_nets}, pos.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeHPWLCudaLauncherFPGA", [&] { - computeHPWLCudaLauncherFPGA( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t)+num_nets, DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t)+num_nets, - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t) - ); - }); - //std::cout << "partial_hpwl = \n" << partial_wl << "\n"; - - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({1, num_nets})); - } - return partial_wl; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_forward, "HPWL forward (CUDA)"); - m.def("forward_fpga", &DREAMPLACE_NAMESPACE::hpwl_forward_fpga, "HPWL forward to generate net bbox(CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp deleted file mode 100644 index 7b7e58f2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @file hpwl_cuda_atomic.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jul 2018 - * @brief Compute half-perimeter wirelength to mimic a parallel atomic implementation - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeHPWLCudaAtomicLauncher( - const T* x, const T* y, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - T* partial_hpwl_max, - T* partial_hpwl_min - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute half-perimeter wirelength -/// @param pos cell locations, array of x locations and then y locations -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -at::Tensor hpwl_atomic_forward( - at::Tensor pos, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask - ) -{ - typedef int T; - - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = net_mask.numel(); - // x then y - at::Tensor scaled_pos = at::_cast_Int(pos.mul(1000), false); - at::Tensor partial_hpwl_max = at::zeros({2, num_nets}, scaled_pos.type()); - at::Tensor partial_hpwl_min = at::zeros({2, num_nets}, scaled_pos.type()); - partial_hpwl_max[0].fill_(std::numeric_limits::min()); - partial_hpwl_max[1].fill_(std::numeric_limits::min()); - partial_hpwl_min[0].fill_(std::numeric_limits::max()); - partial_hpwl_min[1].fill_(std::numeric_limits::max()); - - computeHPWLCudaAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(scaled_pos, T), DREAMPLACE_TENSOR_DATA_PTR(scaled_pos, T)+scaled_pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - pin2net_map.numel(), - DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_max, T), - DREAMPLACE_TENSOR_DATA_PTR(partial_hpwl_min, T) - ); - - auto delta = partial_hpwl_max-partial_hpwl_min; - - at::Tensor hpwl; - switch (pos.scalar_type()) - { - case at::ScalarType::Double: - hpwl = at::_cast_Double(delta, false); - break; - case at::ScalarType::Float: - hpwl = at::_cast_Float(delta, false); - break; - default: - AT_ERROR("hpwl_atomic_forward", " not implemented for '", at::toString(pos.scalar_type()), "'"); - } - - //if (net_weights.numel()) - //{ - // hpwl.mul_(net_weights.view({1, num_nets})); - //} - //return hpwl.sum().mul_(1.0/1000); - return hpwl; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::hpwl_atomic_forward, "HPWL forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu deleted file mode 100644 index 648feed0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeHPWLMax( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - T *partial_hpwl_x_max) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - atomicMax(&partial_hpwl_x_max[net_id], (T)net_mask[net_id] * x[i]); - } -} - -template -__global__ void computeHPWLMin( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - T *partial_hpwl_x_min) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - atomicMin(&partial_hpwl_x_min[net_id], (T)net_mask[net_id] * x[i]); - } -} - -template -__global__ void computeHPWLMaxMin( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - T *partial_hpwl_x_max, T *partial_hpwl_x_min, - T *partial_hpwl_y_max, T *partial_hpwl_y_min) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - - T xx = (T)net_mask[net_id] * x[i]; - atomicMax(&partial_hpwl_x_max[net_id], xx); - atomicMin(&partial_hpwl_x_min[net_id], xx); - - T yy = (T)net_mask[net_id] * y[i]; - atomicMax(&partial_hpwl_y_max[net_id], yy); - atomicMin(&partial_hpwl_y_min[net_id], yy); - } -} - -template -int computeHPWLCudaAtomicLauncher( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - T *partial_hpwl_max, - T *partial_hpwl_min) -{ - const int thread_count = 64; - const int block_count_pins = (num_pins + thread_count - 1) / thread_count; - - computeHPWLMaxMin<<>>( - x, y, - pin2net_map, - net_mask, - num_pins, - partial_hpwl_max, partial_hpwl_min, - partial_hpwl_max + num_nets, partial_hpwl_min + num_nets); - - return 0; -} - -// manually instantiate the template function -#define REGISTER_KERNEL_LAUNCHER(type) \ - template int computeHPWLCudaAtomicLauncher( \ - const type *x, const type *y, \ - const int *pin2net_map, \ - const unsigned char *net_mask, \ - int num_nets, \ - int num_pins, \ - type *partial_hpwl_max, \ - type *partial_hpwl_min); - -REGISTER_KERNEL_LAUNCHER(int); -REGISTER_KERNEL_LAUNCHER(long long int); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu deleted file mode 100644 index 18c5a4c7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu +++ /dev/null @@ -1,238 +0,0 @@ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -//template -//__global__ void fillArray(T* x, const int n, const T v) -//{ -// //for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) -// int i = blockIdx.x * blockDim.x + threadIdx.x; -// if (i < n) -// { -// x[i] = v; -// } -//} - -template -__global__ void computeHPWL( - const T* x, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* partial_hpwl - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - T max_x = -FLT_MAX; - T min_x = FLT_MAX; - - if (net_mask[i]) - { - for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) - { - min_x = min(min_x, x[flat_netpin[j]]); - max_x = max(max_x, x[flat_netpin[j]]); - } - partial_hpwl[i] = max_x-min_x; - } - else - { - partial_hpwl[i] = 0; - } - } -} - -template -__global__ void computeHPWLFPGA( - const T* x, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* bbox_min_x, - T* bbox_max_x, - T* partial_hpwl - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - //T max_x = -FLT_MAX; - //T min_x = FLT_MAX; - bbox_max_x[i] = -FLT_MAX; - bbox_min_x[i] = FLT_MAX; - - if (net_mask[i]) - { - for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) - { - //min_x = min(min_x, x[flat_netpin[j]]); - //max_x = max(max_x, x[flat_netpin[j]]); - bbox_min_x[i] = min(bbox_min_x[i], x[flat_netpin[j]]); - bbox_max_x[i] = max(bbox_max_x[i], x[flat_netpin[j]]); - } - partial_hpwl[i] = bbox_max_x[i]-bbox_min_x[i]; - } - else - { - partial_hpwl[i] = 0; - } - } -} - -template -int computeHPWLCudaLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* partial_hpwl - ) -{ - const int thread_count = 512; - const int block_count_nets = (num_nets + thread_count - 1) / thread_count; - - cudaError_t status; - cudaStream_t stream_y; - status = cudaStreamCreate(&stream_y); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_y\n"); - fflush(stdout); - return 1; - } - - computeHPWL<<>>( - x, - flat_netpin, - netpin_start, - net_mask, - num_nets, - partial_hpwl - ); - - computeHPWL<<>>( - y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - partial_hpwl+num_nets - ); - - /* destroy stream */ - status = cudaStreamDestroy(stream_y); - if (status != cudaSuccess) - { - printf("stream_y destroy failed\n"); - fflush(stdout); - return 1; - } - - //printArray(partial_hpwl, num_nets, "partial_hpwl"); - - // I move out the summation to use ATen - // significant speedup is observed - //sumArray<<<1, 1>>>(partial_hpwl, num_nets, hpwl); - - return 0; -} - -template -int computeHPWLCudaLauncherFPGA( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - T* bbox_min_x, T* bbox_min_y, - T* bbox_max_x, T* bbox_max_y, - T* partial_hpwl - ) -{ - const int thread_count = 512; - const int block_count_nets = (num_nets + thread_count - 1) / thread_count; - - cudaError_t status; - cudaStream_t stream_y; - status = cudaStreamCreate(&stream_y); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_y\n"); - fflush(stdout); - return 1; - } - - computeHPWLFPGA<<>>( - x, - flat_netpin, - netpin_start, - net_mask, - num_nets, - bbox_min_x, - bbox_max_x, - partial_hpwl - ); - - computeHPWLFPGA<<>>( - y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - bbox_min_y, - bbox_max_y, - partial_hpwl+num_nets - ); - - /* destroy stream */ - status = cudaStreamDestroy(stream_y); - if (status != cudaSuccess) - { - printf("stream_y destroy failed\n"); - fflush(stdout); - return 1; - } - - //printArray(partial_hpwl, num_nets, "partial_hpwl"); - - // I move out the summation to use ATen - // significant speedup is observed - //sumArray<<<1, 1>>>(partial_hpwl, num_nets, hpwl); - - return 0; -} - -// manually instantiate the template function -#define REGISTER_KERNEL_LAUNCHER(type) \ - template int computeHPWLCudaLauncher(\ - const type* x, const type* y, \ - const int* flat_netpin, \ - const int* netpin_start, \ - const unsigned char* net_mask, \ - int num_nets, \ - type* partial_hpwl \ - ); \ - template int computeHPWLCudaLauncherFPGA( \ - const type* x, const type* y, \ - const int* flat_netpin, \ - const int* netpin_start, \ - const unsigned char* net_mask, \ - int num_nets, \ - type* bbox_min_x, type* bbox_min_y, \ - type* bbox_max_x, type* bbox_max_y, \ - type* partial_hpwl \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt deleted file mode 100644 index 60026fbb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -set(OP_NAME logsumexp_wirelength) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp_merged - src/logsumexp_wirelength_merged.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp_merged - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) - -add_pytorch_extension(${TARGET_NAME}_cuda_merged - src/logsumexp_wirelength_cuda_merged.cpp - src/logsumexp_wirelength_cuda_merged_kernel.cu - src/integrate_net_weights_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda_atomic - src/logsumexp_wirelength_cuda_atomic.cpp - src/logsumexp_wirelength_cuda_atomic_kernel.cu - src/integrate_net_weights_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda_merged - ${TARGET_NAME}_cuda_atomic - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py deleted file mode 100644 index cd043ea5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py +++ /dev/null @@ -1,200 +0,0 @@ -## -# @file logsumexp_wirelength.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 -# - -import time -import torch -from torch import nn -from torch.autograd import Function -import logging - -import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cpp_merged as logsumexp_wirelength_cpp_merged -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cuda_merged as logsumexp_wirelength_cuda_merged - import dreamplacefpga.ops.logsumexp_wirelength.logsumexp_wirelength_cuda_atomic as logsumexp_wirelength_cuda_atomic -import pdb - -logger = logging.getLogger(__name__) - - -class LogSumExpWirelengthAtomicFunction(Function): - """compute weighted average wirelength. - @param pos pin location (x array, y array), not cell location - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength - @param gamma the smaller, the closer to HPWL - """ - @staticmethod - def forward(ctx, pos, pin2net_map, net_weights, net_mask, gamma): - if pos.is_cuda: - output = logsumexp_wirelength_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, net_weights, net_mask, gamma) - else: - assert 0, "CPU version NOT IMPLEMENTED" - ctx.pin2net_map = pin2net_map - ctx.net_weights = net_weights - ctx.net_mask = net_mask - ctx.gamma = gamma - ctx.exp_xy = output[1] - ctx.exp_nxy = output[2] - ctx.exp_xy_sum = output[3] - ctx.exp_nxy_sum = output[4] - ctx.pos = pos - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - if grad_pos.is_cuda: - output = logsumexp_wirelength_cuda_atomic.backward( - grad_pos, - ctx.pos, - ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), - ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), - ctx.pin2net_map, - ctx.net_weights, - ctx.net_mask, - ctx.gamma - ) - else: - assert 0, "CPU version NOT IMPLEMENTED" - return output, None, None, None, None - -class LogSumExpWirelengthMergedFunction(Function): - """ - @brief compute weighted average wirelength. - """ - @staticmethod - def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, pin_mask, gamma, num_threads): - """ - @param pos pin location (x array, y array), not cell location - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength - @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute - @param gamma the larger, the closer to HPWL - """ - tt = time.time() - if pos.is_cuda: - output = logsumexp_wirelength_cuda_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, gamma) - else: - output = logsumexp_wirelength_cpp_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, gamma, num_threads) - ctx.num_threads = num_threads - ctx.pin2net_map = pin2net_map - ctx.flat_netpin = flat_netpin - ctx.netpin_start = netpin_start - ctx.net_weights = net_weights - ctx.net_mask = net_mask - ctx.pin_mask = pin_mask - ctx.gamma = gamma - ctx.grad_intermediate = output[1] - ctx.pos = pos - if pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - tt = time.time() - if grad_pos.is_cuda: - output = logsumexp_wirelength_cuda_merged.backward( - grad_pos, - ctx.pos, - ctx.grad_intermediate, - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_mask, - ctx.gamma - ) - else: - output = logsumexp_wirelength_cpp_merged.backward( - grad_pos, - ctx.pos, - ctx.grad_intermediate, - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_mask, - ctx.gamma, - ctx.num_threads - ) - output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) - output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) - if grad_pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) - return output, None, None, None, None, None, None, None, None - - -class LogSumExpWirelength(nn.Module): - """ Compute log-sum-exp wirelength. - CPU only supports net-by-net algorithm. - GPU supports two algorithms: atomic, sparse. - Different parameters are required for different algorithms. - - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore - @param gamma the smaller, the closer to HPWL - @param algorithm must be merged | atomic - """ - def __init__(self, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, net_mask=None, pin_mask=None, gamma=None, algorithm='merged', num_threads=8): - super(LogSumExpWirelength, self).__init__() - assert net_weights is not None \ - and net_mask is not None \ - and pin_mask is not None \ - and gamma is not None, "net_weights, net_mask, pin_mask, gamma are requried parameters" - if algorithm == 'merged': - assert flat_netpin is not None and netpin_start is not None and pin2net_map is not None, "flat_netpin, netpin_start, pin2net_map are requried parameters for algorithm %s" % (algorithm) - elif algorithm == 'atomic': - assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" - self.flat_netpin = flat_netpin - self.netpin_start = netpin_start - self.netpin_values = None - self.pin2net_map = pin2net_map - self.net_weights = net_weights - self.net_mask = net_mask - self.pin_mask = pin_mask - self.gamma = gamma - self.algorithm = algorithm - self.num_threads = num_threads - def forward(self, pos): - if pos.is_cuda: - if self.algorithm == 'atomic': - return LogSumExpWirelengthAtomicFunction.apply(pos, - self.pin2net_map, - self.net_weights, - self.net_mask, - self.gamma - ) - elif self.algorithm == 'merged': - return LogSumExpWirelengthMergedFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.pin2net_map, - self.net_weights, - self.net_mask, - self.pin_mask, - self.gamma, - self.num_threads - ) - else: # only merged for CPU - return LogSumExpWirelengthMergedFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.pin2net_map, - self.net_weights, - self.net_mask, - self.pin_mask, - self.gamma, - self.num_threads - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu deleted file mode 100644 index b7f0e077..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file integrate_net_weights_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Jul 2019 - */ - -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "weighted_average_wirelength/src/functional_cuda.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void integrateNetWeights( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - T weight = net_weights[net_id]; - if (net_id >= 0 && net_mask[net_id]) - { - grad_x_tensor[i] *= weight; - grad_y_tensor[i] *= weight; - } - } -} - -template -void integrateNetWeightsCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ) -{ - integrateNetWeights<<>>(pin2net_map, net_mask, net_weights, grad_x_tensor, grad_y_tensor, num_pins); -} - -template -__global__ void integrateNetWeightsforWL( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* partial_wl, ///< x,y interleave - int num_pins - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - T weight = net_weights[net_id]; - if (net_id >= 0 && net_mask[net_id]) - { - partial_wl[2*i] *= weight; - partial_wl[2*i+1] *= weight; - } - } -} - -template -void integrateNetWeightsforWLCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* partial_wl, - int num_pins - ) -{ - integrateNetWeightsforWL<<>>(pin2net_map, net_mask, net_weights, partial_wl, num_pins); -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template void integrateNetWeightsCudaLauncher(\ - const int* pin2net_map, \ - const unsigned char* net_mask, \ - const T* net_weights, \ - T* grad_x_tensor, T* grad_y_tensor, \ - int num_pins \ - ); \ - template void integrateNetWeightsforWLCudaLauncher(\ - const int* pin2net_map, \ - const unsigned char* net_mask, \ - const T* net_weights, \ - T* partial_wl, \ - int num_pins \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp deleted file mode 100644 index f63e0687..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @file logsumexp_wirelength_cuda_atomic.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jul 2018 - * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeLogSumExpWirelengthCudaAtomicLauncher( - const T* x, const T* y, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - const T* gamma, - T* exp_xy, T* exp_nxy, - T* exp_xy_sum, T* exp_nxy_sum, - V* xy_max, V* xy_min, - T* partial_wl, // wirelength of each net - const T* grad_tensor, - T* grad_x_tensor, T* grad_y_tensor // the gradient is partial total wirelength to partial pin position - ); - -/// @brief add net weights to gradient -template -void integrateNetWeightsCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -typedef int V; - -/// @brief Compute log-sum-exp wirelength according to NTUPlace3 -/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) -/// @param pos cell locations, array of x locations and then y locations -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -std::vector logsumexp_wirelength_atomic_forward( - at::Tensor pos, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma // a scalar tensor - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for -y - at::Tensor partial_wl = at::zeros({4, num_nets}, pos.type()); - at::Tensor exp_xy = at::zeros_like(pos); - at::Tensor exp_nxy = at::zeros_like(pos); - at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.type()); - at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.type()); - - // it is ok for xy_max and xy_min to be integer - // we do not really need accurate max/min, just some values to scale x/y - // therefore, there is no need to scale xy_max and xy_min to improve accuracy - at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); - at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaAtomicLauncher", [&] { - computeLogSumExpWirelengthCudaAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - nullptr, - nullptr, nullptr - ); - }); - - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({1, num_nets})); - } - // significant speedup is achieved by using summation in ATen - auto wl = partial_wl.sum(); - return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from back-propagation -/// @param pos locations of pins -/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) -/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) -/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) -/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -at::Tensor logsumexp_wirelength_atomic_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor exp_xy, at::Tensor exp_nxy, - at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma // a scalar tensor - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(exp_xy); - CHECK_EVEN(exp_xy); - CHECK_CONTIGUOUS(exp_xy); - CHECK_FLAT(exp_nxy); - CHECK_EVEN(exp_nxy); - CHECK_CONTIGUOUS(exp_nxy); - CHECK_FLAT(exp_xy_sum); - CHECK_EVEN(exp_xy_sum); - CHECK_CONTIGUOUS(exp_xy_sum); - CHECK_FLAT(exp_nxy_sum); - CHECK_EVEN(exp_nxy_sum); - CHECK_CONTIGUOUS(exp_nxy_sum); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - at::Tensor grad_out = at::zeros_like(pos); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaAtomicLauncher", [&] { - computeLogSumExpWirelengthCudaAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - nullptr, nullptr, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins - ); - if (net_weights.numel()) - { - integrateNetWeightsCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, - num_pins - ); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_atomic_forward, "LogSumExpWirelength forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_atomic_backward, "LogSumExpWirelength backward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu deleted file mode 100644 index 81c50e40..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu +++ /dev/null @@ -1,461 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -// V has to be int, or long long int -template -__global__ void computeMax( - const T* x, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - V* x_max - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicMax(&x_max[net_id], (V)(x[i])); - } - } -} - -// V has to be int, or long long int -template -__global__ void computeMin( - const T* x, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - V* x_min - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicMin(&x_min[net_id], (V)(x[i])); - } - } -} - -template -__global__ void computeExp( - const T* x, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - const T* gamma, - V* x_max, - T* exp_x - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_x[i] = exp((x[i]-x_max[net_id])/(*gamma)); - } - } -} - -template -__global__ void computeNegExp( - const T* x, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - const T* gamma, - V* x_min, - T* exp_nx - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_nx[i] = exp(-(x[i]-x_min[net_id])/(*gamma)); - } - } -} - -template -__global__ void computeExpSum( - const T* exp_x, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - T* exp_x_sum - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicAdd(&exp_x_sum[net_id], exp_x[i]); - } - } -} - -template -__global__ void computeLogSumExp( - const T* exp_x_sum, - const V* x_max, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - const T* gamma, - T* partial_wl - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - if (net_mask[i]) - { - partial_wl[i] = (*gamma)*log(exp_x_sum[i]) + (T)x_max[i]; - } - } -} - -template -__global__ void computeLogSumNegExp( - const T* exp_nx_sum, - const V* x_min, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - const T* gamma, - T* partial_wl - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - if (net_mask[i]) - { - partial_wl[i] = (*gamma)*log(exp_nx_sum[i]) - (T)x_min[i]; - } - } -} - -template -__global__ void computeLogSumExpWirelengthGrad( - const T* exp_x, const T* exp_nx, - const T* exp_x_sum, const T* exp_nx_sum, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - const T* gamma, - const T* grad_tensor, - T* grad_x_tensor - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - grad_x_tensor[i] = (exp_x[i]/exp_x_sum[net_id] - exp_nx[i]/exp_nx_sum[net_id])*(*grad_tensor); - } - } -} - -template -int computeLogSumExpWirelengthCudaAtomicLauncher( - const T* x, const T* y, - const int* pin2net_map, - const unsigned char* net_mask, - int num_nets, - int num_pins, - const T* gamma, - T* exp_xy, T* exp_nxy, - T* exp_xy_sum, T* exp_nxy_sum, - V* xy_max, V* xy_min, - T* partial_wl, // wirelength of each net - const T* grad_tensor, - T* grad_x_tensor, T* grad_y_tensor // the gradient is partial total wirelength to partial pin position - ) -{ - int thread_count = 512; - int block_count_pins = (num_pins + thread_count - 1) / thread_count; - int block_count_nets = (num_nets + thread_count - 1) / thread_count; - - cudaError_t status; - cudaStream_t stream_nx_exp; - cudaStream_t stream_y_exp; - cudaStream_t stream_ny_exp; - status = cudaStreamCreate(&stream_y_exp); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_y_exp\n"); - fflush(stdout); - return 1; - } - - if (grad_tensor) - { - computeLogSumExpWirelengthGrad<<>>( - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - grad_tensor, - grad_x_tensor - ); - computeLogSumExpWirelengthGrad<<>>( - exp_xy+num_pins, exp_nxy+num_pins, - exp_xy_sum+num_nets, exp_nxy_sum+num_nets, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - grad_tensor, - grad_y_tensor - ); - } - else - { - status = cudaStreamCreate(&stream_nx_exp); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_nx_exp\n"); - fflush(stdout); - return 1; - } - status = cudaStreamCreate(&stream_ny_exp); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_ny_exp\n"); - fflush(stdout); - return 1; - } - - // compute max/min - computeMax<<>>( - x, - pin2net_map, - net_mask, - num_nets, - num_pins, - xy_max - ); - computeMin<<>>( - x, - pin2net_map, - net_mask, - num_nets, - num_pins, - xy_min - ); - computeMax<<>>( - y, - pin2net_map, - net_mask, - num_nets, - num_pins, - xy_max+num_nets - ); - computeMin<<>>( - y, - pin2net_map, - net_mask, - num_nets, - num_pins, - xy_min+num_nets - ); - - // compute exp and negative exp - computeExp<<>>( - x, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - xy_max, - exp_xy - ); - computeNegExp<<>>( - x, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - xy_min, - exp_nxy - ); - computeExp<<>>( - y, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - xy_max+num_nets, - exp_xy+num_pins - ); - computeNegExp<<>>( - y, - pin2net_map, - net_mask, - num_nets, - num_pins, - gamma, - xy_min+num_nets, - exp_nxy+num_pins - ); - - // compute exp sum - computeExpSum<<>>( - exp_xy, - pin2net_map, - net_mask, - num_nets, - num_pins, - exp_xy_sum - ); - computeExpSum<<>>( - exp_nxy, - pin2net_map, - net_mask, - num_nets, - num_pins, - exp_nxy_sum - ); - computeExpSum<<>>( - exp_xy+num_pins, - pin2net_map, - net_mask, - num_nets, - num_pins, - exp_xy_sum+num_nets - ); - computeExpSum<<>>( - exp_nxy+num_pins, - pin2net_map, - net_mask, - num_nets, - num_pins, - exp_nxy_sum+num_nets - ); - - // compute log sum exp - computeLogSumExp<<>>( - exp_xy_sum, - xy_max, - pin2net_map, - net_mask, - num_nets, - gamma, - partial_wl - ); - computeLogSumNegExp<<>>( - exp_nxy_sum, - xy_min, - pin2net_map, - net_mask, - num_nets, - gamma, - partial_wl+num_nets - ); - - computeLogSumExp<<>>( - exp_xy_sum+num_nets, - xy_max+num_nets, - pin2net_map, - net_mask, - num_nets, - gamma, - partial_wl+2*num_nets - ); - computeLogSumNegExp<<>>( - exp_nxy_sum+num_nets, - xy_min+num_nets, - pin2net_map, - net_mask, - num_nets, - gamma, - partial_wl+3*num_nets - ); - - // I move out the summation to use ATen - // significant speedup is observed - //sumArray<<<1, 1>>>(partial_wl, 2*num_nets, wl); - - status = cudaStreamDestroy(stream_nx_exp); - if (status != cudaSuccess) - { - printf("stream_nx_exp destroy failed\n"); - fflush(stdout); - return 1; - } - status = cudaStreamDestroy(stream_ny_exp); - if (status != cudaSuccess) - { - printf("stream_ny_exp destroy failed\n"); - fflush(stdout); - return 1; - } - } - - /* destroy stream */ - status = cudaStreamDestroy(stream_y_exp); - if (status != cudaSuccess) - { - printf("stream_y_exp destroy failed\n"); - fflush(stdout); - return 1; - } - - return 0; -} - - -#define REGISTER_KERNEL_LAUNCHER(T, V) \ - template int computeLogSumExpWirelengthCudaAtomicLauncher(\ - const T* x, const T* y, \ - const int* pin2net_map, \ - const unsigned char* net_mask, \ - int num_nets, \ - int num_pins, \ - const T* gamma, \ - T* exp_xy, T* exp_nxy, \ - T* exp_xy_sum, T* exp_nxy_sum,\ - V* xy_max, V* xy_min, \ - T* partial_wl, \ - const T* grad_tensor, \ - T* grad_x_tensor, T* grad_y_tensor \ - ); - -REGISTER_KERNEL_LAUNCHER(float, int); -REGISTER_KERNEL_LAUNCHER(double, int); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp deleted file mode 100644 index 86c7389f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/** - * @file hpwl_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeLogSumExpWirelengthCudaLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* gamma, - const T* inv_gamma, - T* partial_wl, - T* grad_intermediate_x, T* grad_intermediate_y - ); - -/// @brief add net weights to gradient -template -void integrateNetWeightsCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute log-sum-exp wirelength according to NTUPlace3 -/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param netpin_values similar to the value array in CSR format, a dummy array of all ones -/// @param pin2net_map pin2net map -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -std::vector logsumexp_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma // a scalar tensor - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - // x, y interleave - at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); - // timed with grad_in yet - at::Tensor grad_intermediate = at::zeros_like(pos); - auto inv_gamma = 1.0 / gamma; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaLauncher", [&] { - computeLogSumExpWirelengthCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins - ); - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({num_nets, 1})); - } - }); - - auto wl = partial_wl.sum(); - return {wl, grad_intermediate}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from back-propagation -/// @param pos locations of pins -/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) -/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) -/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) -/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param pin2net_map pin2net map -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -at::Tensor logsumexp_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor grad_intermediate, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(grad_intermediate); - CHECK_EVEN(grad_intermediate); - CHECK_CONTIGUOUS(grad_intermediate); - - at::Tensor grad_out = grad_intermediate.mul_(grad_pos); - //int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthCudaLauncher", [&] { - if (net_weights.numel()) - { - integrateNetWeightsCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, - num_pins); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_forward, "LogSumExpWirelength forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_backward, "LogSumExpWirelength backward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu deleted file mode 100644 index dd7a2846..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeLogSumExpWirelength( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - const T* gamma, - const T *inv_gamma, - T *partial_wl, - T *grad_intermediate_x, T *grad_intermediate_y - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - int ii = i >> 1; - if (ii < num_nets && net_mask[ii]) - { - const T *values; - T *grads; - if (i & 1) - { - values = y; - grads = grad_intermediate_y; - } - else - { - values = x; - grads = grad_intermediate_x; - } - - // int degree = netpin_start[ii+1]-netpin_start[ii]; - T x_max = -FLT_MAX; - T x_min = FLT_MAX; - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - x_max = max(xx, x_max); - x_min = min(xx, x_min); - } - - T exp_x_sum = 0; - T exp_nx_sum = 0; - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - exp_x_sum += exp_x; - exp_nx_sum += exp_nx; - } - - partial_wl[i] = (log(exp_x_sum) + log(exp_nx_sum)) * (*gamma) + x_max - x_min; - - T reciprocal_exp_x_sum = 1.0 / exp_x_sum; - T reciprocal_exp_nx_sum = 1.0 / exp_nx_sum; - for (int j = netpin_start[ii]; j < netpin_start[ii+1]; ++j) - { - int jj = flat_netpin[j]; - T xx = values[jj]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - grads[jj] = (exp_x*reciprocal_exp_x_sum - exp_nx*reciprocal_exp_nx_sum); - } - } -} - -template -int computeLogSumExpWirelengthCudaLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* gamma, - const T* inv_gamma, - T* partial_wl, - T* grad_intermediate_x, T* grad_intermediate_y - ) -{ - int thread_count = 64; - int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y - - computeLogSumExpWirelength<<>>( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - gamma, - inv_gamma, - partial_wl, - grad_intermediate_x, grad_intermediate_y - ); - - return 0; -} - - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeLogSumExpWirelengthCudaLauncher(\ - const T* x, const T* y, \ - const int* flat_netpin, \ - const int* netpin_start, \ - const unsigned char* net_mask, \ - int num_nets, \ - const T* gamma, \ - const T* inv_gamma, \ - T* partial_wl, \ - T* grad_intermediate_x, T* grad_intermediate_y \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp deleted file mode 100644 index 46ea2b96..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/** - * @file logsumexp_wirelength.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute log-sum-exp wirelength and gradient according to NTUPlace3 - */ -#include -#include "utility/src/torch.h" -#include "utility/src/Msg.h" -#include "weighted_average_wirelength/src/functional.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -void computeLogSumExpWirelengthLauncher( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - const T* gamma, - const T *inv_gamma, - T *partial_wl, - T *grad_intermediate_x, T *grad_intermediate_y, - int num_threads - ) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - if (net_mask[i]) - { - T x_max = -std::numeric_limits::max(); - T x_min = std::numeric_limits::max(); - T y_max = -std::numeric_limits::max(); - T y_min = std::numeric_limits::max(); - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); - x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); - T yy = y[flat_netpin[j]]; - y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); - y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); - } - - T exp_x_sum = 0; - T exp_nx_sum = 0; - - T exp_y_sum = 0; - T exp_ny_sum = 0; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - exp_x_sum += exp_x; - exp_nx_sum += exp_nx; - - T yy = y[flat_netpin[j]]; - T exp_y = exp((yy - y_max) * (*inv_gamma)); - T exp_ny = exp((y_min - yy) * (*inv_gamma)); - - exp_y_sum += exp_y; - exp_ny_sum += exp_ny; - } - - partial_wl[i] = (log(exp_x_sum) + log(exp_nx_sum)) * (*gamma) + x_max - x_min - + (log(exp_y_sum) + log(exp_ny_sum)) * (*gamma) + y_max - y_min; - - T reciprocal_exp_x_sum = 1.0 / exp_x_sum; - T reciprocal_exp_nx_sum = 1.0 / exp_nx_sum; - T reciprocal_exp_y_sum = 1.0 / exp_y_sum; - T reciprocal_exp_ny_sum = 1.0 / exp_ny_sum; - for (int j = netpin_start[i]; j < netpin_start[i+1]; ++j) - { - int jj = flat_netpin[j]; - - T xx = x[jj]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - grad_intermediate_x[jj] = (exp_x*reciprocal_exp_x_sum - exp_nx*reciprocal_exp_nx_sum); - - T yy = y[jj]; - T exp_y = exp((yy - y_max) * (*inv_gamma)); - T exp_ny = exp((y_min - yy) * (*inv_gamma)); - grad_intermediate_y[jj] = (exp_y*reciprocal_exp_y_sum - exp_ny*reciprocal_exp_ny_sum); - } - } - } -} - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x " must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x " must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x " must be contiguous") - -/// @brief Compute log-sum-exp wirelength according to NTUPlace3 -/// gamma * (log(\sum exp(x_i/gamma)) + log(\sum exp(-x_i/gamma))) -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -std::vector logsumexp_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma, // a scalar tensor - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - // x, y interleave - at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); - // timed with grad_in yet - at::Tensor grad_intermediate = at::zeros_like(pos); - auto inv_gamma = 1.0 / gamma; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthLauncher", [&] { - computeLogSumExpWirelengthLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins, - num_threads - ); - if (net_weights.numel()) - { - partial_wl.mul_(net_weights); - } - }); - - auto wl = partial_wl.sum(); - return {wl, grad_intermediate}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from back-propagation -/// @param pos locations of pins -/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) -/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) -/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) -/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param gamma a scalar tensor for the parameter in the equation -at::Tensor logsumexp_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor grad_intermediate, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor gamma, // a scalar tensor - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(grad_intermediate); - CHECK_EVEN(grad_intermediate); - CHECK_CONTIGUOUS(grad_intermediate); - - at::Tensor grad_out = grad_intermediate.mul_(grad_pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeLogSumExpWirelengthLauncher", [&] { - if (net_weights.numel()) - { - integrateNetWeightsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t),//Use instead of net_weights_x - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, - netpin_start.numel() - 1, - num_threads); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_forward, "LogSumExpWirelength forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::logsumexp_wirelength_backward, "LogSumExpWirelength backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt deleted file mode 100644 index 3d02bf3c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -set(OP_NAME lut_ff_legalization) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${LEMON_INCLUDE_DIRS} - ) - -set(LINK_LIBS utility_cxx - lemon - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/lut_ff_legalization.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/lut_ff_legalization_cuda.cpp - src/lut_ff_legalization_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py deleted file mode 100644 index a1bfea3e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Rachel Selina (DREAMPlaceFPGA-PL) -# @date Apr 2022 -# - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py deleted file mode 100644 index 7eb3cda4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py +++ /dev/null @@ -1,946 +0,0 @@ -## -# @file lut_ff_legalization.py -# @author Rachel Selina (DREAMPlaceFPGA-PL) -# @date Apr 2022 -# - -import math -import torch -from torch import nn -from torch.autograd import Function -import pdb -import time -import logging -import numpy as np - -import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization_cpp as lut_ff_legalization_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.lut_ff_legalization.lut_ff_legalization_cuda as lut_ff_legalization_cuda - -def carry_chain_checker(flat_cc2node_start_map, flat_cc2node_map, inst_curr_detSite, - site2addr_map, site_det_impl_lut, num_sites_y, lutsInSlice, - num_carry_chains, device): - - carry_chain_ck = torch.zeros(num_carry_chains, dtype=torch.int, device=device) - - for idx in range(num_carry_chains): - instId = flat_cc2node_map[flat_cc2node_start_map[idx]].item() - siteId = inst_curr_detSite[instId].item() - columnX = math.floor(siteId/num_sites_y) - currY = math.floor(siteId%num_sites_y) - s_Id = site2addr_map[siteId].item() - - if instId in site_det_impl_lut[s_Id]: - instZ = (site_det_impl_lut[s_Id] == instId).nonzero(as_tuple=True)[0].item() - else: - print("ERROR: Instance: ", instId, " not found in site_det but assigned in inst_curr_detSite for site: ", siteId, " and sIdx: ", s_Id) - - if instZ != 0: - carry_chain_ck[idx] = carry_chain_ck[idx]+1 - print("ERROR: INCORRECT START: For idx: ", idx , " start inst: ", instId, " has loc: ", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item()) - - yOffset = 0 - cnt = 0 - if instZ == 0: - cnt = 1 - for ccIdx in range(flat_cc2node_start_map[idx]+1, flat_cc2node_start_map[idx+1]): - instId = flat_cc2node_map[ccIdx].item() - siteId = inst_curr_detSite[instId].item() - nextX = math.floor(siteId/num_sites_y) - nextY = math.floor(siteId%num_sites_y) - s_Id = site2addr_map[siteId].item() - - if instId in site_det_impl_lut[s_Id]: - nextZ = (site_det_impl_lut[s_Id] == instId).nonzero(as_tuple=True)[0].item() - else: - print("ERROR: Instance: ", instId, " not found in site_det but assigned in inst_curr_detSite for site: ", siteId, " and sIdx: ", s_Id) - - if columnX != nextX or nextY != currY + yOffset or nextZ != cnt: - carry_chain_ck[idx] = carry_chain_ck[idx]+1 - print("ERROR: INCORRECT ") - if columnX != nextX: - print(" X ") - if nextY != currY + yOffset: - print(" Y ") - if nextZ != cnt: - print(" Z ") - print(" Location for carry chain (", idx , ") with inst: ", instId, " at (", nextX, ", ", nextY, ", ", nextZ, ")") - - cnt = cnt + 1 - if cnt == lutsInSlice: - cnt = 0 - yOffset = yOffset - 1 - - if carry_chain_ck[idx] > 0: - print("ERROR: Check carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") - if carry_chain_ck.sum() > 0: - logging.info("ERROR: %d Carry-chains not correct" %(carry_chain_ck.sum())) - -def carry_chain_checker_loc(flat_cc2node_start_map, flat_cc2node_map, updXloc, updYloc, - updZloc, HALF_SLICE_CAPACITY, lutsInSlice, num_carry_chains, device): - - carry_chain_ck = torch.zeros(num_carry_chains, dtype=torch.int, device=device) - for idx in range(num_carry_chains): - instId = flat_cc2node_map[flat_cc2node_start_map[idx]].item() - columnX = updXloc[instId].item() - currY = updYloc[instId].item() - instZ = updZloc[instId].item() - - if instZ != 0 and instZ != HALF_SLICE_CAPACITY: - carry_chain_ck[idx] = carry_chain_ck[idx]+1 - print("ERROR: INCORRECT START: For idx: ", idx , " start inst: ", instId, " has loc: ", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item()) - - yOffset = 0 - cnt = 0 - if instZ == 0: - cnt = 1 - elif instZ == HALF_SLICE_CAPACITY: - cnt = 1 + HALF_SLICE_CAPACITY - for ccIdx in range(flat_cc2node_start_map[idx]+1, flat_cc2node_start_map[idx+1]): - instId = flat_cc2node_map[ccIdx].item() - - if columnX != updXloc[instId].item() or updYloc[instId].item() != currY + yOffset or updZloc[instId].item() != cnt: - carry_chain_ck[idx] = carry_chain_ck[idx]+1 - print("ERROR: INCORRECT ") - if columnX != updXloc[instId].item(): - print(" X ") - if updYloc[instId].item() != currY + yOffset: - print(" Y ") - if updZloc[instId].item() != cnt: - print(" Z ") - print(" Location for carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") - - cnt = cnt + 1 - if cnt == lutsInSlice: - cnt = 0 - yOffset = yOffset - 1 - - if carry_chain_ck[idx] > 0: - print("ERROR: Check carry chain (", idx , ") with inst: ", instId, " at (", updXloc[instId].to(torch.int).item(), ", ", updYloc[instId].to(torch.int).item(), ", ", updZloc[instId].item(), ")") - if carry_chain_ck.sum() > 0: - logging.info("ERROR: %d Carry-chains not correct" %(carry_chain_ck.sum())) - -def compute_remaining_slice_sites(slice_sites, site_det_sig_idx, addr2site_map): - - assigned_slice_site_mask = np.zeros(slice_sites.shape[0], dtype=bool) - assigned_sites = addr2site_map[torch.where(site_det_sig_idx > 0)[0].long()].cpu().detach().numpy() - - indices = np.where(np.in1d(slice_sites, assigned_sites))[0] - assigned_slice_site_mask[indices] = True - - return ~assigned_slice_site_mask - -class LegalizeCLB(nn.Module): - def __init__(self, data_collections, placedb, net_wts, #inst_areas, - site_types, num_threads, device): - - super(LegalizeCLB, self).__init__() - - if placedb.num_ccNodes == 0: - self.num_movable_nodes=placedb.num_movable_nodes - self.num_nodes=placedb.num_physical_nodes - self.lut_flop_indices=data_collections.flop_lut_indices - self.is_mlab_node = data_collections.is_mlab_node.int() - self.flop2ctrlSetId_map=data_collections.flop2ctrlSetId_map - self.flop_ctrlSets=data_collections.flop_ctrlSets - self.pin2node_map=data_collections.pin2node_map - self.flat_node2pin_map=data_collections.flat_node2pin_map - self.flat_node2pin_start_map=data_collections.flat_node2pin_start_map - self.node2fence_region_map=data_collections.node2fence_region_map - self.node2outpinIdx_map=data_collections.node2outpinIdx_map - self.node2pincount=data_collections.node2pincount_map - self.lut_type=data_collections.lut_type - self.pin_offset_x=data_collections.lg_pin_offset_x - self.pin_offset_y=data_collections.lg_pin_offset_y - self.node_size_x=data_collections.node_size_x[:self.num_nodes] - self.node_size_y=data_collections.node_size_y[:self.num_nodes] - self.flat_cc2node_map = torch.from_numpy(placedb.flat_cc2node_map).to(dtype=torch.int, device=device) - self.flat_cc2node_start_map = torch.from_numpy(placedb.flat_cc2node_start_map).to(dtype=torch.int, device=device) - else: - self.num_movable_nodes=placedb.org_num_movable_nodes - self.num_nodes=placedb.org_num_physical_nodes - self.lut_flop_indices=data_collections.org_flop_lut_indices - self.is_mlab_node = data_collections.org_is_mlab_node.int() - self.flop2ctrlSetId_map=data_collections.org_flop2ctrlSetId_map - self.flop_ctrlSets=data_collections.org_flop_ctrlSets - self.pin2node_map=data_collections.org_pin2node_map - self.flat_node2pin_map=data_collections.org_flat_node2pin_map - self.flat_node2pin_start_map=data_collections.org_flat_node2pin_start_map - self.node2fence_region_map=data_collections.org_node2fence_region_map - self.node2outpinIdx_map=data_collections.org_node2outpinIdx_map - self.node2pincount=data_collections.org_node2pincount_map - self.lut_type=data_collections.org_lut_type - self.pin_offset_x=data_collections.org_lg_pin_offset_x - self.pin_offset_y=data_collections.org_lg_pin_offset_y - self.node_size_x=data_collections.org_node_size_x[:self.num_nodes] - self.node_size_y=data_collections.org_node_size_y[:self.num_nodes] - self.flat_cc2node_map = torch.from_numpy(placedb.org_flat_cc2node_map).to(dtype=torch.int, device=device) - self.flat_cc2node_start_map = torch.from_numpy(placedb.org_flat_cc2node_start_map).to(dtype=torch.int, device=device) - - self.num_lutflops=self.lut_flop_indices.shape[0] - - self.pin_typeIds=data_collections.pin_typeIds - self.pin2net_map=data_collections.pin2net_map - self.flat_net2pin_map=data_collections.flat_net2pin_map - self.flat_net2pin_start_map=data_collections.flat_net2pin_start_map - - self.site_xy=data_collections.lg_siteXYs - self.net2pincount=data_collections.net2pincount_map - self.spiral_accessor=data_collections.spiral_accessor - - self.num_nets=placedb.num_nets - self.num_sites_x=placedb.num_sites_x - self.num_sites_y=placedb.num_sites_y - self.xWirelenWt=placedb.xWirelenWt - self.yWirelenWt=placedb.yWirelenWt - self.nbrDistEnd=placedb.nbrDistEnd - - self.xl = placedb.xl - self.yl = placedb.yl - self.xh = placedb.xh - self.yh = placedb.yh - - self.net_wts=net_wts - #self.inst_areas=inst_areas - self.site_types=site_types - self.num_threads=num_threads - self.device=device - self.dtype = self.node_size_x.dtype - - self.sliceId = placedb.sSLICEIdx - self.lutId = placedb.rLUTIdx - self.ffId = placedb.rFFIdx - - self.lut_flop_mask = torch.logical_or(self.node2fence_region_map == self.lutId,self.node2fence_region_map == self.ffId) - - lutName = [key for key, val in placedb.rsrcType2indexMap.items() if val==placedb.rLUTIdx][0] - ffName = [key for key, val in placedb.rsrcType2indexMap.items() if val==placedb.rFFIdx][0] - - self.lutsInSlice = placedb.siteRsrc2CountMap[lutName] - ffsInSlice = placedb.siteRsrc2CountMap[ffName] - - #mlabs - self.mlab_indices = torch.where(self.is_mlab_node == 1)[0].to(torch.int32) - self.num_mlab_nodes = self.mlab_indices.shape[0] - - #Carry chains - self.num_carry_chains = placedb.num_carry_chains - self.num_ccNodes = placedb.num_ccNodes - - self.slice_minX = int(placedb.slice_x_min) - self.slice_maxX = int(placedb.slice_x_max) - self.slice_minY = int(placedb.slice_y_min) - self.slice_maxY = int(placedb.slice_y_max) - self.sliceSiteXYs = placedb.sliceSiteXYs - self.slice_sites = (self.sliceSiteXYs[:,0]*self.num_sites_y + self.sliceSiteXYs[:,1]).astype(np.int32) - - #Architecture specific values - for el in placedb.sliceFFCtrls: - if 'clk'.casefold() in el[0].casefold() or 'ck'.casefold() in el[0].casefold(): - self.CKSR_IN_CLB = el[1] - else: - self.CE_IN_CLB = el[1] - - self.half_ctrl_mode = 0 #Use for FF ctrl signals - - if placedb.sliceFF_ctrl_mode == "HALF": - self.CKSR_IN_CLB *= 2 - self.CE_IN_CLB *= 2 - self.half_ctrl_mode = 1 - - for el in placedb.sliceElements: - if lutName in el[0]: - self.BLE_CAPACITY = el[1] - break - - self.ff_ctrl_type = placedb.ff_ctrl_type - self.netShareScoreMaxNetDegree = self.lutsInSlice - self.SLICE_CAPACITY = placedb.SLICE_CAPACITY - self.HALF_SLICE_CAPACITY = placedb.HALF_SLICE_CAPACITY - self.NUM_BLE_PER_SLICE = int(self.SLICE_CAPACITY/self.BLE_CAPACITY) - self.NUM_BLE_PER_HALF_SLICE = int(self.HALF_SLICE_CAPACITY/self.BLE_CAPACITY) - self.extended_ctrlSets = torch.from_numpy(placedb.extended_ctrlSets).to(dtype=torch.int, device=self.device) - self.ext_ctrlSet_start_map = torch.from_numpy(placedb.ext_ctrlSet_start_map).to(dtype=torch.int, device=self.device) - - self.PQ_IDX = 10 - self.SCL_IDX = 128 - - self.SIG_IDX = self.lutsInSlice + ffsInSlice - - #Initialize required constants - self.nbrDistBeg = 1.0 - self.nbrDistIncr = 1.0 - self.extNetCountWt = 0.3 - self.wirelenImprovWt = 0.1 - self.int_min_val = -2147483647 - self.WLscoreMaxNetDegree = 100 - self.maxList = max(128, math.ceil(0.005 * self.num_nodes)) #Based on empirical results from elfPlace - self.numGroups = math.ceil((self.nbrDistEnd-self.nbrDistBeg)/self.nbrDistIncr) + 1 - - #LUT specific entries - #lut type that occupies entire sliceunit - self.lutTypeInSliceUnit = placedb.lutTypeInSliceUnit - #max shared inputs to luts in a sliceunit - self.lut_maxShared = placedb.lut_maxShared - - #Initialize required tensors - self.net_bbox = torch.zeros(self.num_nets*4, dtype=self.dtype, device=device) - - self.net_pinIdArrayX = torch.zeros(len(self.flat_net2pin_map), dtype=torch.int, device=device) - self.net_pinIdArrayY = torch.zeros_like(self.net_pinIdArrayX) #len(flat_net2pin) - - self.flat_node2precluster_map = torch.ones((self.num_nodes,3), dtype=torch.int, device=device) - self.flat_node2precluster_map *= -1 - self.flat_node2precluster_map[:,0] = torch.arange(self.num_nodes, dtype=torch.int, device=device) - self.flat_node2prclstrCount = torch.ones(self.num_nodes, dtype=torch.int, device=device) - - #Instance Candidates - self.inst_curr_detSite = torch.zeros_like(self.flat_node2prclstrCount) #num_nodes - self.inst_curr_detSite[self.lut_flop_mask] = -1 - self.inst_curr_bestSite = torch.zeros_like(self.inst_curr_detSite) #num_nodes - self.inst_curr_bestSite[self.lut_flop_mask] = -1 - self.inst_curr_bestScoreImprov = torch.zeros(self.num_nodes, dtype=self.dtype, device=device) - self.inst_curr_bestScoreImprov[self.lut_flop_mask] = -10000.0 - - self.inst_next_detSite = torch.zeros_like(self.inst_curr_detSite) #num_nodes - self.inst_next_detSite[self.lut_flop_mask] = -1 - self.inst_next_bestSite = torch.zeros_like(self.inst_next_detSite) #num_nodes - self.inst_next_bestSite[self.lut_flop_mask] = -1 - self.inst_next_bestScoreImprov = torch.zeros_like(self.inst_curr_bestScoreImprov) #num_nodes - self.inst_next_bestScoreImprov[self.lut_flop_mask] = -10000.0 - - self.num_clb_sites = torch.bincount(self.site_types.flatten())[self.sliceId].item() - #Map from mem addr to CLB site - self.addr2site_map = self.site_types.flatten().nonzero(as_tuple=True)[0] - #Map from CLB site to mem addr - self.site2addr_map = torch.ones(self.num_sites_x*self.num_sites_y, dtype=torch.int, device=device) - self.site2addr_map *= -1 - self.site2addr_map[self.addr2site_map] = torch.arange(self.num_clb_sites, dtype=torch.int, device=device) - self.addr2site_map = self.addr2site_map.int() - - #Site Neighbors - self.site_nbrList = torch.zeros((self.num_clb_sites, self.maxList), dtype=torch.int, device=device) - self.site_nbr = torch.zeros_like(self.site_nbrList) #num_clb_sites * maxList - self.site_nbr_idx = torch.zeros(self.num_clb_sites, dtype=torch.int, device=device) - self.site_nbrRanges = torch.zeros((self.num_clb_sites, self.numGroups+1), dtype=torch.int, device=device) - self.site_nbrRanges_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_nbrGroup_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - ##Site Candidates - self.site_det_score = torch.zeros(self.num_clb_sites, dtype=self.dtype, device=device) - self.site_det_siteId = torch.ones_like(self.site_nbr_idx) #num_clb_sites - self.site_det_siteId *= -1 - self.site_det_impl_lut = torch.ones((self.num_clb_sites, self.SLICE_CAPACITY), dtype=torch.int, device=device) - self.site_det_impl_lut *= -1 - self.site_det_impl_ff = torch.ones_like(self.site_det_impl_lut) #num_clb_sites * SLICE_CAPACITY - self.site_det_impl_ff *= -1 - self.site_det_impl_cksr = torch.ones((self.num_clb_sites, self.CKSR_IN_CLB), dtype=torch.int, device=device) - self.site_det_impl_cksr *= -1 - self.site_det_impl_ce = torch.ones((self.num_clb_sites, self.CE_IN_CLB), dtype=torch.int, device=device) - self.site_det_impl_ce *= -1 - self.site_det_sig = torch.ones((self.num_clb_sites, self.SIG_IDX), dtype=torch.int, device=device) - self.site_det_sig *= -1 - self.site_det_sig_idx = torch.zeros_like(self.site_det_siteId) #num_clb_sites - - self.site_curr_stable = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_curr_scl_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_curr_scl_validIdx = torch.ones((self.num_clb_sites, self.SCL_IDX), dtype=torch.int, device=device) - self.site_curr_scl_validIdx *= -1 - self.site_curr_scl_siteId = torch.ones((self.num_clb_sites, self.SCL_IDX), dtype=torch.int, device=device) #num_clb_sites * SCL_IDX - self.site_curr_scl_siteId *= -1 - self.site_curr_scl_score = torch.zeros((self.num_clb_sites, self.SCL_IDX), dtype=self.dtype, device=device) - self.site_curr_scl_impl_lut = torch.ones((self.num_clb_sites, self.SCL_IDX, self.SLICE_CAPACITY), dtype=torch.int, device=device) - self.site_curr_scl_impl_lut *= -1 - self.site_curr_scl_impl_ff = torch.ones_like(self.site_curr_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY - self.site_curr_scl_impl_ff *= -1 - self.site_curr_scl_impl_cksr = torch.ones((self.num_clb_sites, self.SCL_IDX, self.CKSR_IN_CLB), dtype=torch.int, device=device) - self.site_curr_scl_impl_cksr *= -1 - self.site_curr_scl_impl_ce = torch.ones((self.num_clb_sites, self.SCL_IDX, self.CE_IN_CLB), dtype=torch.int, device=device) - self.site_curr_scl_impl_ce *= -1 - self.site_curr_scl_sig = torch.ones((self.num_clb_sites, self.SCL_IDX, self.SIG_IDX), dtype=torch.int, device=device) - self.site_curr_scl_sig *= -1 - self.site_curr_scl_sig_idx = torch.zeros_like(self.site_curr_scl_siteId) #num_clb_sites * SCL_IDX - - self.site_curr_pq_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_curr_pq_top_idx = torch.ones_like(self.site_nbr_idx) #num_clb_sites - self.site_curr_pq_top_idx *= -1 - self.site_curr_pq_score = torch.zeros((self.num_clb_sites, self.PQ_IDX), dtype=self.dtype, device=device) - self.site_curr_pq_validIdx = torch.ones((self.num_clb_sites, self.PQ_IDX), dtype=torch.int, device=device) - self.site_curr_pq_validIdx *= -1 - self.site_curr_pq_siteId = torch.ones((self.num_clb_sites, self.PQ_IDX), dtype=torch.int, device=device) #num_clb_sites * PQ_IDX - self.site_curr_pq_siteId *= -1 - self.site_curr_pq_sig = torch.ones((self.num_clb_sites, self.PQ_IDX, self.SIG_IDX), dtype=torch.int, device=device) - self.site_curr_pq_sig *= -1 - self.site_curr_pq_sig_idx = torch.zeros_like(self.site_curr_pq_siteId) #num_clb_sites * PQ_IDX - self.site_curr_pq_impl_lut = torch.ones((self.num_clb_sites, self.PQ_IDX, self.SLICE_CAPACITY), dtype=torch.int, device=device) - self.site_curr_pq_impl_lut *= -1 - self.site_curr_pq_impl_ff = torch.ones_like(self.site_curr_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY. - self.site_curr_pq_impl_ff *= -1 - self.site_curr_pq_impl_cksr = torch.ones((self.num_clb_sites, self.PQ_IDX, self.CKSR_IN_CLB), dtype=torch.int, device=device) - self.site_curr_pq_impl_cksr *= -1 - self.site_curr_pq_impl_ce = torch.ones((self.num_clb_sites, self.PQ_IDX, self.CE_IN_CLB), dtype=torch.int, device=device) - self.site_curr_pq_impl_ce *= -1 - - self.site_next_stable = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_next_scl_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_next_scl_validIdx = torch.ones_like(self.site_curr_scl_validIdx) #num_clb_sites * SCL_IDX - self.site_next_scl_validIdx *= -1 - self.site_next_scl_siteId = torch.ones_like(self.site_curr_scl_siteId) #num_clb_sites * SCL_IDX - self.site_next_scl_siteId *= -1 - self.site_next_scl_score = torch.zeros_like(self.site_curr_scl_score) #num_clb_sites * SCL_IDX - self.site_next_scl_impl_lut = torch.ones_like(self.site_curr_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY - self.site_next_scl_impl_lut *= -1 - self.site_next_scl_impl_ff = torch.ones_like(self.site_next_scl_impl_lut) #num_clb_sites * SCL_IDX * SLICE_CAPACITY - self.site_next_scl_impl_ff *= -1 - self.site_next_scl_impl_cksr = torch.ones_like(self.site_curr_scl_impl_cksr) #num_clb_sites * SCL_IDX * CKSR_IN_CLB - self.site_next_scl_impl_cksr *= -1 - self.site_next_scl_impl_ce = torch.ones_like(self.site_curr_scl_impl_ce) #num_clb_sites * SCL_IDX * CE_IN_CLB - self.site_next_scl_impl_ce *= -1 - self.site_next_scl_sig = torch.ones_like(self.site_curr_scl_sig) #num_clb_sites * SCL_IDX * SIG_IDX - self.site_next_scl_sig *= -1 - self.site_next_scl_sig_idx = torch.zeros_like(self.site_next_scl_siteId) #num_clb_sites * SCL_IDX - - self.site_next_pq_idx = torch.zeros_like(self.site_nbr_idx) #num_clb_sites - self.site_next_pq_top_idx = torch.ones_like(self.site_nbr_idx) #num_clb_sites - self.site_next_pq_top_idx *= -1 - self.site_next_pq_score = torch.zeros_like(self.site_curr_pq_score) #num_clb_sites * PQ_IDX - self.site_next_pq_validIdx = torch.ones_like(self.site_curr_pq_validIdx) #num_clb_sites * PQ_IDX - self.site_next_pq_validIdx *= -1 - self.site_next_pq_siteId = torch.ones_like(self.site_curr_pq_siteId) #num_clb_sites * PQ_IDX - self.site_next_pq_siteId *= -1 - self.site_next_pq_sig = torch.ones_like(self.site_curr_pq_sig) #num_clb_sites * PQ_IDX * SIG_IDX - self.site_next_pq_sig *= -1 - self.site_next_pq_sig_idx = torch.zeros_like(self.site_curr_pq_validIdx) #num_clb_sites * PQ_IDX - self.site_next_pq_impl_lut = torch.ones_like(self.site_curr_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY - self.site_next_pq_impl_lut *= -1 - self.site_next_pq_impl_ff = torch.ones_like(self.site_next_pq_impl_lut) #num_clb_sites * PQ_IDX * SLICE_CAPACITY - self.site_next_pq_impl_ff *= -1 - self.site_next_pq_impl_cksr = torch.ones_like(self.site_curr_pq_impl_cksr) #num_clb_sites * PQ_IDX * CKSR_IN_CLB - self.site_next_pq_impl_cksr *= -1 - self.site_next_pq_impl_ce = torch.ones_like(self.site_curr_pq_impl_ce) #num_clb_sites * PQ_IDX * CE_IN_CLB - self.site_next_pq_impl_ce *= -1 - - self.inst_score_improv = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) - self.inst_score_improv[self.lut_flop_mask] = self.int_min_val - self.site_score_improv = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) - self.site_score_improv *= self.int_min_val - - self.special_nodes = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) - self.special_nodes[self.flat_cc2node_map.long()] = 1 - - def initialize(self, pos, wlPrecond, sorted_node_map, sorted_node_idx, - sorted_net_map, sorted_net_idx, sorted_pin_map): - - tt = time.time() - - preClusteringMaxDist = 4.0 - maxD = math.ceil(self.nbrDistEnd) + 1 - spiralBegin = 0 - spiralEnd_maxD = 2 * (maxD + 1) * maxD + 1 - spiralEnd = self.spiral_accessor.shape[0] #Entire chip! - - #Handling carry chains - self.sites_with_special_nodes = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) - self.is_mlab_site = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) - - carry_chain_displacements = torch.zeros(self.num_nodes, dtype=pos.dtype, device=self.device) - - if pos.is_cuda: - - lut_ff_legalization_cuda.initLegalization(pos, self.pin_offset_x, self.pin_offset_y, - sorted_net_idx, sorted_node_map, sorted_node_idx, self.flat_net2pin_map, - self.flat_net2pin_start_map, self.flop2ctrlSetId_map, self.flop_ctrlSets, - self.node2fence_region_map, self.node2outpinIdx_map, self.pin2net_map, - self.pin2node_map, self.pin_typeIds, self.net2pincount, self.is_mlab_node, - preClusteringMaxDist, self.num_nets, self.num_nodes, self.lutId, - self.ffId, self.WLscoreMaxNetDegree, - self.net_bbox, self.net_pinIdArrayX, self.net_pinIdArrayY, - self.flat_node2precluster_map, self.flat_node2prclstrCount) - - - if self.num_carry_chains > 0: - - #Handle carry-chains and arithmetic share chains - cpu_carry_chain_displacements = carry_chain_displacements.cpu() - cpu_site_det_score = self.site_det_score.cpu() - cpu_inst_curr_bestScoreImprov = self.inst_curr_bestScoreImprov.cpu() - cpu_inst_next_bestScoreImprov = self.inst_next_bestScoreImprov.cpu() - cpu_sites_with_carry_chain = self.sites_with_special_nodes.cpu() - cpu_inst_curr_detSite = self.inst_curr_detSite.cpu() - cpu_inst_curr_bestSite = self.inst_curr_bestSite.cpu() - cpu_inst_next_detSite = self.inst_next_detSite.cpu() - cpu_inst_next_bestSite = self.inst_next_bestSite.cpu() - cpu_site_det_siteId = self.site_det_siteId.cpu() - cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() - cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() - cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() - - - #Legalize carry chains - lut_ff_legalization_cpp.legalizeCarryChain( - pos.cpu(), torch.flatten(self.site_xy).cpu(), wlPrecond.cpu(), - torch.flatten(self.spiral_accessor).cpu(), torch.flatten(self.site_types).cpu(), - self.site2addr_map.cpu(), self.flat_cc2node_start_map.cpu(), - self.flat_cc2node_map.cpu(), spiralBegin, spiralEnd, self.num_sites_x, - self.num_sites_y, self.sliceId, self.SIG_IDX, self.SLICE_CAPACITY, - self.num_carry_chains, self.lutsInSlice, self.slice_minX, self.slice_maxX, - self.slice_minY, self.slice_maxY, cpu_carry_chain_displacements, - cpu_site_det_score, cpu_inst_curr_bestScoreImprov, cpu_inst_next_bestScoreImprov, - cpu_sites_with_carry_chain, cpu_inst_curr_detSite, cpu_inst_curr_bestSite, - cpu_inst_next_detSite, cpu_inst_next_bestSite, cpu_site_det_siteId, - cpu_site_det_sig, cpu_site_det_sig_idx, cpu_site_det_impl_lut, self.num_threads) - - carry_chain_displacements.data.copy_(cpu_carry_chain_displacements) - self.site_det_score.data.copy_(cpu_site_det_score) - self.inst_curr_bestScoreImprov.data.copy_(cpu_inst_curr_bestScoreImprov) - self.inst_next_bestScoreImprov.data.copy_(cpu_inst_next_bestScoreImprov) - self.sites_with_special_nodes.data.copy_(cpu_sites_with_carry_chain) - self.inst_curr_detSite.data.copy_(cpu_inst_curr_detSite) - self.inst_curr_bestSite.data.copy_(cpu_inst_curr_bestSite) - self.inst_next_detSite.data.copy_(cpu_inst_next_detSite) - self.inst_next_bestSite.data.copy_(cpu_inst_next_bestSite) - self.site_det_siteId.data.copy_(cpu_site_det_siteId) - torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig) - self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx) - torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut) - - logging.info("%d carry-chains legalized with max and avg displacements: (%f, %f)" - % (self.num_carry_chains, carry_chain_displacements.max(), carry_chain_displacements.sum()/self.num_carry_chains)) - - #TODO - When mlabs are treated as a type of LUT - ##Legalize mlabs if any - if self.num_mlab_nodes > 0: - - lg_max_dist_init=self.nbrDistEnd - lg_max_dist_incr=self.nbrDistIncr - lg_flow_cost_scale=100.0 - - #Remove already assigned slice sites if any - rem_slice_sites_mask = compute_remaining_slice_sites(self.slice_sites, self.site_det_sig_idx, self.addr2site_map) - num_sites = rem_slice_sites_mask.sum() - num_total_nodes = pos.numel()//2 - - locX = pos[:self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() - locY = pos[num_total_nodes:num_total_nodes+self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() - precondWL = wlPrecond[self.is_mlab_node.bool()].cpu().detach().numpy() - - movVal = np.zeros(2, dtype=np.float32).tolist() - outLoc = np.zeros(2*self.num_mlab_nodes, dtype=np.float32).tolist() - - lut_ff_legalization_cpp.minCostFlow(locX, locY, num_sites, self.num_mlab_nodes, self.sliceSiteXYs[rem_slice_sites_mask].flatten(), - precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) - - outLoc=np.array(outLoc) - mlab_locX = torch.from_numpy(outLoc[:self.num_mlab_nodes]).to(dtype=pos.dtype, device=self.device) - mlab_locY = torch.from_numpy(outLoc[self.num_mlab_nodes:]).to(dtype=pos.dtype, device=self.device) - - mlab_displacements = torch.zeros(self.num_mlab_nodes, dtype=pos.dtype, device=self.device) - - lut_ff_legalization_cuda.legalizeMlab(pos, torch.flatten(self.site_xy), - mlab_locX, mlab_locY, self.mlab_indices, self.site2addr_map, - self.num_mlab_nodes, self.num_sites_y, self.SIG_IDX, self.SLICE_CAPACITY, - mlab_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, - self.inst_next_bestScoreImprov, self.site_det_siteId, self.site_det_sig_idx, - self.site_det_sig, self.site_det_impl_lut, self.inst_curr_detSite, - self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, - self.sites_with_special_nodes) - - logging.info("%d mlabs legalized with max and avg displacements: (%f, %f)" - % (self.num_mlab_nodes, mlab_displacements.max(), mlab_displacements.sum()/self.num_mlab_nodes)) - - mlab_sites = self.site2addr_map[self.inst_curr_detSite[torch.where(self.is_mlab_node == 1)[0]].long()] - self.is_mlab_site[mlab_sites.long()] = 1 - - ## Initialize Site Neighbors ## - cpu_site_curr_scl_score = torch.flatten(self.site_curr_scl_score).cpu() - cpu_site_curr_scl_siteId = torch.flatten(self.site_curr_scl_siteId).cpu() - cpu_site_curr_scl_validIdx = torch.flatten(self.site_curr_scl_validIdx).cpu() - cpu_site_curr_scl_idx = self.site_curr_scl_idx.cpu() - cpu_site_curr_scl_sig = torch.flatten(self.site_curr_scl_sig).cpu() - cpu_site_curr_scl_sig_idx = torch.flatten(self.site_curr_scl_sig_idx).cpu() - cpu_site_curr_scl_impl_lut = torch.flatten(self.site_curr_scl_impl_lut).cpu() - cpu_site_nbrRanges = torch.flatten(self.site_nbrRanges).cpu() - cpu_site_nbrRanges_idx = self.site_nbrRanges_idx.cpu() - cpu_site_nbrList = torch.flatten(self.site_nbrList).cpu() - cpu_site_nbr = torch.flatten(self.site_nbr).cpu() - cpu_site_nbr_idx = self.site_nbr_idx.cpu() - cpu_site_nbrGroup_idx = self.site_nbrGroup_idx.cpu() - cpu_site_det_siteId = self.site_det_siteId.cpu() - cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() - cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() - cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() - - lut_ff_legalization_cpp.initSiteNbrs( - pos.cpu(), wlPrecond.cpu(), torch.flatten(self.site_xy).cpu(), self.site_det_score.cpu(), - sorted_node_idx.cpu(), self.node2fence_region_map.cpu(), torch.flatten(self.site_types).cpu(), - torch.flatten(self.spiral_accessor).cpu(), self.site2addr_map.cpu(), self.addr2site_map.cpu(), - torch.flatten(self.flat_node2precluster_map).cpu(), self.flat_node2prclstrCount.cpu(), - self.is_mlab_node.cpu(), self.is_mlab_site.cpu(), self.sites_with_special_nodes.cpu(), self.nbrDistEnd, - self.nbrDistBeg, self.nbrDistIncr, self.lutId, self.ffId, self.sliceId, self.num_nodes, - self.num_sites_x, self.num_sites_y, self.num_clb_sites, self.SCL_IDX, self.SIG_IDX, - self.SLICE_CAPACITY, self.numGroups, self.maxList, spiralBegin, spiralEnd_maxD, - cpu_site_curr_scl_score, cpu_site_curr_scl_siteId, cpu_site_curr_scl_validIdx, - cpu_site_curr_scl_idx, cpu_site_curr_scl_sig, cpu_site_curr_scl_sig_idx, - cpu_site_curr_scl_impl_lut, cpu_site_nbrRanges, cpu_site_nbrRanges_idx, - cpu_site_nbrList, cpu_site_nbr, cpu_site_nbr_idx, cpu_site_nbrGroup_idx, cpu_site_det_siteId, - cpu_site_det_sig, cpu_site_det_sig_idx, cpu_site_det_impl_lut, self.num_threads) - - torch.flatten(self.site_curr_scl_score).data.copy_(cpu_site_curr_scl_score) - torch.flatten(self.site_curr_scl_siteId).data.copy_(cpu_site_curr_scl_siteId) - torch.flatten(self.site_curr_scl_validIdx).data.copy_(cpu_site_curr_scl_validIdx) - self.site_curr_scl_idx.data.copy_(cpu_site_curr_scl_idx) - torch.flatten(self.site_curr_scl_sig).data.copy_(cpu_site_curr_scl_sig) - torch.flatten(self.site_curr_scl_sig_idx).data.copy_(cpu_site_curr_scl_sig_idx) - torch.flatten(self.site_curr_scl_impl_lut).data.copy_(cpu_site_curr_scl_impl_lut) - torch.flatten(self.site_nbrRanges).data.copy_(cpu_site_nbrRanges) - self.site_nbrRanges_idx.data.copy_(cpu_site_nbrRanges_idx) - torch.flatten(self.site_nbrList).data.copy_(cpu_site_nbrList.data) - torch.flatten(self.site_nbr).data.copy_(cpu_site_nbr) - self.site_nbr_idx.data.copy_(cpu_site_nbr_idx) - self.site_nbrGroup_idx.data.copy_(cpu_site_nbrGroup_idx) - self.site_det_siteId.data.copy_(cpu_site_det_siteId) - torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig) - self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx) - torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut) - - else: - lut_ff_legalization_cpp.initializeLG( - pos, self.pin_offset_x, self.pin_offset_y, sorted_net_idx, sorted_node_map, sorted_node_idx, - self.flat_net2pin_map, self.flat_net2pin_start_map, self.flop2ctrlSetId_map, self.flop_ctrlSets, - self.node2fence_region_map, self.node2outpinIdx_map, self.pin2net_map, self.pin2node_map, - self.pin_typeIds, self.net2pincount, self.is_mlab_node, preClusteringMaxDist, self.lutId, - self.ffId, self.num_nets, self.num_nodes, self.num_threads, - self.WLscoreMaxNetDegree, self.net_bbox, self.net_pinIdArrayX, self.net_pinIdArrayY, - self.flat_node2precluster_map, self.flat_node2prclstrCount) - - if self.num_carry_chains > 0: - - #Legalize carry chains and initialize site neighbors accordingly - lut_ff_legalization_cpp.legalizeCarryChain( - pos, torch.flatten(self.site_xy), wlPrecond, torch.flatten(self.spiral_accessor), - torch.flatten(self.site_types), self.site2addr_map, self.flat_cc2node_start_map, - self.flat_cc2node_map, spiralBegin, spiralEnd, self.num_sites_x, self.num_sites_y, - self.sliceId, self.SIG_IDX, self.SLICE_CAPACITY, self.num_carry_chains, self.lutsInSlice, - self.slice_minX, self.slice_maxX, self.slice_minY, self.slice_maxY, - carry_chain_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, - self.inst_next_bestScoreImprov, self.sites_with_special_nodes, self.inst_curr_detSite, - self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, - self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, - self.site_det_impl_lut, self.num_threads) - - logging.info("%d carry-chains legalized with max and avg displacements: (%f, %f)" % - (self.num_carry_chains, carry_chain_displacements.max(), carry_chain_displacements.sum()/self.num_carry_chains)) - - #TODO - When mlabs are treated as a type of LUT - ##Legalize mlabs if any - if self.num_mlab_nodes > 0: - - lg_max_dist_init=self.nbrDistEnd - lg_max_dist_incr=self.nbrDistIncr - lg_flow_cost_scale=100.0 - - #Remove already assigned slice sites if any - rem_slice_sites_mask = compute_remaining_slice_sites(self.slice_sites, self.site_det_sig_idx, self.addr2site_map) - num_sites = rem_slice_sites_mask.sum() - num_total_nodes = pos.numel()//2 - - locX = pos[:self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() - locY = pos[num_total_nodes:num_total_nodes+self.num_nodes][self.is_mlab_node.bool()].cpu().detach().numpy() - precondWL = wlPrecond[self.is_mlab_node.bool()].cpu().detach().numpy() - - movVal = np.zeros(2, dtype=np.float32).tolist() - outLoc = np.zeros(2*self.num_mlab_nodes, dtype=np.float32).tolist() - - lut_ff_legalization_cpp.minCostFlow(locX, locY, num_sites, self.num_mlab_nodes, self.sliceSiteXYs[rem_slice_sites_mask].flatten(), - precondWL, lg_max_dist_init, lg_max_dist_incr, lg_flow_cost_scale, movVal, outLoc) - - outLoc=np.array(outLoc) - mlab_locX = torch.from_numpy(outLoc[:self.num_mlab_nodes]).to(dtype=pos.dtype, device=self.device) - mlab_locY = torch.from_numpy(outLoc[self.num_mlab_nodes:]).to(dtype=pos.dtype, device=self.device) - - mlab_displacements = torch.zeros(self.num_mlab_nodes, dtype=pos.dtype, device=self.device) - - lut_ff_legalization_cpp.legalizeMlab(pos, torch.flatten(self.site_xy), - mlab_locX, mlab_locY, self.mlab_indices, self.site2addr_map, - self.num_mlab_nodes, self.num_sites_y, self.SIG_IDX, self.SLICE_CAPACITY, - mlab_displacements, self.site_det_score, self.inst_curr_bestScoreImprov, - self.inst_next_bestScoreImprov, self.sites_with_special_nodes, self.inst_curr_detSite, - self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestSite, - self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, - self.site_det_impl_lut, self.num_threads) - - logging.info("%d mlabs legalized with max and avg displacements: (%f, %f)" - % (self.num_mlab_nodes, mlab_displacements.max(), mlab_displacements.sum()/self.num_mlab_nodes)) - - mlab_sites = self.site2addr_map[self.inst_curr_detSite[torch.where(self.is_mlab_node == 1)[0]].long()] - self.is_mlab_site[mlab_sites.long()] = 1 - - ## Initialize Site Neighbors ## - lut_ff_legalization_cpp.initSiteNbrs(pos, wlPrecond, torch.flatten(self.site_xy), self.site_det_score, - sorted_node_idx, self.node2fence_region_map, torch.flatten(self.site_types), - torch.flatten(self.spiral_accessor), self.site2addr_map, self.addr2site_map, - torch.flatten(self.flat_node2precluster_map), self.flat_node2prclstrCount, self.is_mlab_node, self.is_mlab_site, - self.sites_with_special_nodes, self.nbrDistEnd, self.nbrDistBeg, self.nbrDistIncr, self.lutId, - self.ffId, self.sliceId, self.num_nodes, self.num_sites_x, self.num_sites_y, self.num_clb_sites, - self.SCL_IDX, self.SIG_IDX, self.SLICE_CAPACITY, self.numGroups, self.maxList, spiralBegin, - spiralEnd_maxD, self.site_curr_scl_score, self.site_curr_scl_siteId, self.site_curr_scl_validIdx, - self.site_curr_scl_idx, self.site_curr_scl_sig, self.site_curr_scl_sig_idx, self.site_curr_scl_impl_lut, - self.site_nbrRanges, self.site_nbrRanges_idx, self.site_nbrList, self.site_nbr, self.site_nbr_idx, - self.site_nbrGroup_idx, self.site_det_siteId, self.site_det_sig, self.site_det_sig_idx, - self.site_det_impl_lut, self.num_threads) - - #DBG - #Preclustering Info - preAll = (self.flat_node2prclstrCount[self.node2fence_region_map==self.lutId] > 1).sum().item() - pre3 = (self.flat_node2prclstrCount[self.node2fence_region_map==self.lutId] > 2).sum().item() - pre2 = preAll - pre3 - #print("# Precluster: ", preAll, " (", pre2, " + ", pre3, ")") - #DBG - print("Preclusters: %d (%d + %d) Initialization completed in %.3f seconds" % (preAll, pre2, pre3, time.time()-tt)) - - #DBG - spl_mask = self.special_nodes == 1 - if -1 in self.inst_curr_detSite[spl_mask]: - print("ERROR: INCORRECT locations for special nodes after legalization - CHECK") - - #Carry-Chain Checker - if self.num_carry_chains > 0: - carry_chain_checker(self.flat_cc2node_start_map, self.flat_cc2node_map, self.inst_curr_detSite, - self.site2addr_map, self.site_det_impl_lut, self.num_sites_y, - self.lutsInSlice, self.num_carry_chains, self.device) - #DBG - - def runDLIter(self, pos, wlPrecond, sorted_node_map, sorted_node_idx, sorted_net_map, sorted_net_idx, sorted_pin_map, - activeStatus, illegalStatus, dlIter): - maxDist = 5.0 - spiralBegin = 0 - spiralEnd = 2 * (int(maxDist) + 1) * int(maxDist) + 1 - minStableIter = 3 - minNeighbors = 10 - cumsum_curr_scl = torch.zeros(self.num_clb_sites, dtype=torch.int, device=self.device) - sorted_clb_siteIds = torch.zeros_like(cumsum_curr_scl) - validIndices_curr_scl = torch.ones_like(self.site_curr_scl_validIdx) - validIndices_curr_scl *= -1 - - if pos.is_cuda: - lut_ff_legalization_cuda.runDLIter(pos, self.pin_offset_x, self.pin_offset_y, - self.net_bbox, torch.flatten(self.site_xy), self.net_wts, self.net_pinIdArrayX, - self.net_pinIdArrayY, torch.flatten(self.site_types), torch.flatten(self.spiral_accessor), - self.node2fence_region_map, self.lut_flop_indices, self.flop2ctrlSetId_map, - self.flop_ctrlSets, self.extended_ctrlSets, self.ext_ctrlSet_start_map, - self.lut_type, self.flat_node2pin_start_map, self.flat_node2pin_map, self.node2outpinIdx_map, - self.node2pincount, self.net2pincount, self.pin2net_map, self.pin_typeIds, self.flat_net2pin_start_map, - self.pin2node_map, sorted_net_map, sorted_node_map, self.flat_node2prclstrCount, - torch.flatten(self.flat_node2precluster_map), self.is_mlab_node, torch.flatten(self.site_nbrList), - torch.flatten(self.site_nbrRanges), self.site_nbrRanges_idx, self.addr2site_map, - self.site2addr_map, self.special_nodes, maxDist, self.xWirelenWt, self.yWirelenWt, - self.wirelenImprovWt, self.extNetCountWt, self.num_sites_x, self.num_sites_y, - self.num_clb_sites, self.num_lutflops, minStableIter, self.maxList, self.half_ctrl_mode, - self.SLICE_CAPACITY, self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, - minNeighbors, spiralBegin, spiralEnd, self.int_min_val, self.numGroups, - self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, - self.lutTypeInSliceUnit, self.lut_maxShared, self.CKSR_IN_CLB, self.CE_IN_CLB, - self.SCL_IDX, self.PQ_IDX, self.SIG_IDX, self.lutId, self.ffId, self.sliceId, - self.site_nbr_idx, self.site_nbr, self.site_nbrGroup_idx, self.site_curr_pq_top_idx, - self.site_curr_pq_sig_idx, self.site_curr_pq_sig, self.site_curr_pq_idx, self.site_curr_stable, - self.site_curr_pq_siteId, self.site_curr_pq_validIdx, self.site_curr_pq_score, - self.site_curr_pq_impl_lut, self.site_curr_pq_impl_ff, self.site_curr_pq_impl_cksr, - self.site_curr_pq_impl_ce, self.site_curr_scl_score, self.site_curr_scl_siteId, - self.site_curr_scl_idx, cumsum_curr_scl, self.site_curr_scl_validIdx, validIndices_curr_scl, - self.site_curr_scl_sig_idx, self.site_curr_scl_sig, self.site_curr_scl_impl_lut, - self.site_curr_scl_impl_ff, self.site_curr_scl_impl_cksr, self.site_curr_scl_impl_ce, - self.site_next_pq_idx, self.site_next_pq_validIdx, self.site_next_pq_top_idx, self.site_next_pq_score, - self.site_next_pq_siteId, self.site_next_pq_sig_idx, self.site_next_pq_sig, self.site_next_pq_impl_lut, - self.site_next_pq_impl_ff, self.site_next_pq_impl_cksr, self.site_next_pq_impl_ce, self.site_next_scl_score, - self.site_next_scl_siteId, self.site_next_scl_idx, self.site_next_scl_validIdx, self.site_next_scl_sig_idx, - self.site_next_scl_sig, self.site_next_scl_impl_lut, self.site_next_scl_impl_ff, self.site_next_scl_impl_cksr, - self.site_next_scl_impl_ce, self.site_next_stable, self.site_det_score, self.site_det_siteId, self.site_det_sig_idx, - self.site_det_sig, self.site_det_impl_lut, self.site_det_impl_ff, self.site_det_impl_cksr, self.site_det_impl_ce, - self.inst_curr_detSite, self.inst_curr_bestScoreImprov, self.inst_curr_bestSite, self.inst_next_detSite, - self.inst_next_bestScoreImprov, self.inst_next_bestSite, activeStatus, illegalStatus, self.inst_score_improv, - self.site_score_improv, sorted_clb_siteIds) - - else: - lut_ff_legalization_cpp.runDLIter(pos, self.pin_offset_x, self.pin_offset_y, self.net_bbox, self.net_pinIdArrayX, - self.net_pinIdArrayY, torch.flatten(self.site_xy), self.node2fence_region_map, self.flop_ctrlSets, - self.extended_ctrlSets, self.ext_ctrlSet_start_map, self.flop2ctrlSetId_map, self.lut_type, - self.flat_node2pin_start_map, self.flat_node2pin_map, self.node2outpinIdx_map, self.node2pincount, self.net2pincount, - self.pin2net_map, self.pin_typeIds, self.flat_net2pin_start_map, self.pin2node_map, self.flat_node2prclstrCount, - torch.flatten(self.flat_node2precluster_map), self.is_mlab_node, self.is_mlab_site, torch.flatten(self.site_nbrList), - torch.flatten(self.site_nbrRanges), self.site_nbrRanges_idx, sorted_node_map, sorted_net_map, self.net_wts, - self.addr2site_map, self.special_nodes, self.num_sites_x, self.num_sites_y, self.num_clb_sites, minStableIter, - self.maxList, self.half_ctrl_mode, self.SLICE_CAPACITY, self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, - self.NUM_BLE_PER_SLICE, minNeighbors, self.numGroups, self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, - self.lutTypeInSliceUnit, self.lut_maxShared, self.xWirelenWt, self.yWirelenWt, self.wirelenImprovWt, - self.extNetCountWt, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SCL_IDX, self.PQ_IDX, self.SIG_IDX, self.lutId, - self.ffId, self.num_nodes, self.num_threads, self.site_nbr_idx, self.site_nbr, self.site_nbrGroup_idx, - self.site_curr_pq_top_idx, self.site_curr_pq_sig_idx, self.site_curr_pq_sig, self.site_curr_pq_idx, - self.site_curr_pq_validIdx, self.site_curr_stable, self.site_curr_pq_siteId, self.site_curr_pq_score, - self.site_curr_pq_impl_lut, self.site_curr_pq_impl_ff, self.site_curr_pq_impl_cksr, self.site_curr_pq_impl_ce, - self.site_curr_scl_score, self.site_curr_scl_siteId, self.site_curr_scl_idx, self.site_curr_scl_validIdx, - self.site_curr_scl_sig_idx, self.site_curr_scl_sig, self.site_curr_scl_impl_lut, self.site_curr_scl_impl_ff, - self.site_curr_scl_impl_cksr, self.site_curr_scl_impl_ce, self.site_next_pq_idx, self.site_next_pq_validIdx, - self.site_next_pq_top_idx, self.site_next_pq_score, self.site_next_pq_siteId, self.site_next_pq_sig_idx, - self.site_next_pq_sig, self.site_next_pq_impl_lut, self.site_next_pq_impl_ff, self.site_next_pq_impl_cksr, - self.site_next_pq_impl_ce, self.site_next_scl_score, self.site_next_scl_siteId, self.site_next_scl_idx, - self.site_next_scl_validIdx, self.site_next_scl_sig_idx, self.site_next_scl_sig, self.site_next_scl_impl_lut, - self.site_next_scl_impl_ff, self.site_next_scl_impl_cksr, self.site_next_scl_impl_ce, self.site_next_stable, - self.site_det_score, self.site_det_siteId, self.site_det_sig_idx, self.site_det_sig, self.site_det_impl_lut, - self.site_det_impl_ff, self.site_det_impl_cksr, self.site_det_impl_ce, self.inst_curr_detSite, - self.inst_curr_bestScoreImprov, self.inst_curr_bestSite, self.inst_next_detSite, self.inst_next_bestScoreImprov, - self.inst_next_bestSite, activeStatus, illegalStatus) - - ####DBG - print(dlIter,": ", (self.inst_curr_detSite[self.node2fence_region_map==self.lutId] > -1).sum().item()+(self.inst_curr_detSite[self.node2fence_region_map==self.ffId] > -1).sum().item(), "/", self.num_nodes) - print("\tactive Status : ", activeStatus.sum().item()) - print("\tillegal Status : ", illegalStatus.sum().item()) - ##DBG - - def ripUP_Greedy_slotAssign(self, pos, wlPrecond, node_z, sorted_node_map, sorted_node_idx, sorted_net_map, - sorted_net_idx, sorted_pin_map, inst_areas): - - tt = time.time() - spiralBegin = 0 - spiralEnd = self.spiral_accessor.shape[0] #Entire chip! - ripupExpansion = 1 - greedyExpansion = 5 - slotAssignFlowWeightScale = 1000.0 - slotAssignFlowWeightIncr = 0.5 - - updXloc = torch.ones(self.num_nodes, dtype=self.dtype, device=self.device) - updXloc *= -1 - updYloc = torch.ones_like(updXloc) - updYloc *= -1 - updZloc = torch.zeros(self.num_movable_nodes, dtype=torch.int, device=self.device) - - #Re-initialize preclustering Update first element as itself - self.flat_node2precluster_map = torch.ones((self.num_nodes,3), dtype=torch.int, device=self.device) - self.flat_node2precluster_map *= -1 - self.flat_node2precluster_map[:,0] = torch.arange(self.num_nodes, dtype=torch.int, device=self.device) - self.flat_node2prclstrCount = torch.zeros(self.num_nodes, dtype=torch.int, device=self.device) - self.flat_node2prclstrCount[self.lut_flop_mask] = 1 - - #RipUp + Greedy Legalization - rem_insts_mask = (self.inst_curr_detSite == -1) - num_remInsts = rem_insts_mask.sum().item() - rem_inst_areas = inst_areas[rem_insts_mask] - rem_inst_ids = torch.arange(self.num_nodes, dtype=torch.int, device=self.device)[rem_insts_mask] - - if self.num_mlab_nodes > 0: - self.sites_with_special_nodes = torch.logical_or(self.sites_with_special_nodes, self.is_mlab_site).to(torch.int32) - - if self.half_ctrl_mode == 1: - #sorted node ids only comprise of remaining instances - _, sorted_ids = torch.sort(rem_inst_areas, descending=True) - sorted_remNode_idx = rem_inst_ids[sorted_ids] - sorted_remNode_idx = sorted_remNode_idx.to(torch.int32) - - #sorted node map will consist of all instances sorted based on decreasing area - _, sort_all_ids = torch.sort(inst_areas, descending=True) - _, sorted_remNode_map = torch.sort(sort_all_ids) - sorted_remNode_map = sorted_remNode_map.to(torch.int32) - else: - #Prioritize based on lut type and inst area - inst_scores = 2*self.lut_type/self.lut_type.max() - inst_scores += (inst_areas/inst_areas.max()) - rem_inst_scores = inst_scores[rem_insts_mask] - #sorted node ids only comprise of remaining instances - _, sorted_ids = torch.sort(rem_inst_scores, descending=True) - sorted_remNode_idx = rem_inst_ids[sorted_ids] - sorted_remNode_idx = sorted_remNode_idx.to(torch.int32) - - #sorted node map will consist of all instances sorted based on decreasing area - _, sort_all_ids = torch.sort(inst_scores, descending=True) - _, sorted_remNode_map = torch.sort(sort_all_ids) - sorted_remNode_map = sorted_remNode_map.to(torch.int32) - - #DBG - #print("RipUp & Greedy LG on ", num_remInsts, "insts (neighbors within", self.nbrDistEnd, "distance)") - numFFs = (self.node2fence_region_map[rem_inst_ids.long()] == self.ffId).sum().item() - numLUTs = rem_inst_ids.shape[0] - numFFs - #print("RipUP & Greedy LG on ", num_remInsts, " insts (", numLUTs, " LUTs + ", numFFs, " FFs)") - #DBG - - if pos.is_cuda: - cpu_inst_curr_detSite = self.inst_curr_detSite.cpu() - cpu_site_det_sig_idx = self.site_det_sig_idx.cpu() - cpu_site_det_sig = torch.flatten(self.site_det_sig).cpu() - cpu_site_det_impl_lut = torch.flatten(self.site_det_impl_lut).cpu() - cpu_site_det_impl_ff = torch.flatten(self.site_det_impl_ff).cpu() - cpu_site_det_impl_cksr = torch.flatten(self.site_det_impl_cksr).cpu() - cpu_site_det_impl_ce = torch.flatten(self.site_det_impl_ce).cpu() - cpu_site_det_siteId = self.site_det_siteId.cpu() - cpu_site_det_score = self.site_det_score.cpu() - cpu_node_x = updXloc.cpu() - cpu_node_y = updYloc.cpu() - cpu_node_z = updZloc.cpu() - - lut_ff_legalization_cpp.ripUp_SlotAssign(pos.cpu(), self.pin_offset_x.cpu(), self.pin_offset_y.cpu(), - self.net_wts.cpu(), self.net_bbox.cpu(), inst_areas.cpu(), wlPrecond.cpu(), - torch.flatten(self.site_xy).cpu(), self.net_pinIdArrayX.cpu(), self.net_pinIdArrayY.cpu(), - torch.flatten(self.spiral_accessor).cpu(), self.node2fence_region_map.cpu(), self.lut_type.cpu(), - torch.flatten(self.site_types).cpu(), self.node2pincount.cpu(), self.net2pincount.cpu(), - self.pin2net_map.cpu(), self.pin2node_map.cpu(), self.pin_typeIds.cpu(), - self.flop2ctrlSetId_map.cpu(), self.flop_ctrlSets.cpu(), self.extended_ctrlSets.cpu(), - self.ext_ctrlSet_start_map.cpu(), self.flat_node2pin_start_map.cpu(), - self.flat_node2pin_map.cpu(), self.flat_net2pin_start_map.cpu(), self.flat_node2prclstrCount.cpu(), - torch.flatten(self.flat_node2precluster_map).cpu(), sorted_remNode_map.cpu(), sorted_remNode_idx.cpu(), - sorted_net_map.cpu(), self.node2outpinIdx_map.cpu(), self.flat_net2pin_map.cpu(), - self.addr2site_map.cpu(), self.site2addr_map.cpu(), self.sites_with_special_nodes.cpu(), - self.special_nodes.cpu(), self.nbrDistEnd, self.xWirelenWt, self.yWirelenWt, self.extNetCountWt, - self.wirelenImprovWt, slotAssignFlowWeightScale, slotAssignFlowWeightIncr, self.lutTypeInSliceUnit, - self.lut_maxShared, num_remInsts, self.num_sites_x, self.num_sites_y, self.num_clb_sites, spiralBegin, - spiralEnd, self.half_ctrl_mode, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SLICE_CAPACITY, - self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, self.NUM_BLE_PER_HALF_SLICE, - self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, ripupExpansion, greedyExpansion, self.SIG_IDX, - self.lutId, self.ffId, self.sliceId, self.num_threads, - cpu_inst_curr_detSite, cpu_site_det_sig_idx, cpu_site_det_sig, cpu_site_det_impl_lut, cpu_site_det_impl_ff, - cpu_site_det_impl_cksr, cpu_site_det_impl_ce, cpu_site_det_siteId, cpu_site_det_score, - cpu_node_x, cpu_node_y, cpu_node_z) - - self.inst_curr_detSite.data.copy_(cpu_inst_curr_detSite.data) - self.site_det_sig_idx.data.copy_(cpu_site_det_sig_idx.data) - torch.flatten(self.site_det_sig).data.copy_(cpu_site_det_sig.data) - torch.flatten(self.site_det_impl_lut).data.copy_(cpu_site_det_impl_lut.data) - torch.flatten(self.site_det_impl_ff).data.copy_(cpu_site_det_impl_ff.data) - torch.flatten(self.site_det_impl_cksr).data.copy_(cpu_site_det_impl_cksr.data) - torch.flatten(self.site_det_impl_ce).data.copy_(cpu_site_det_impl_ce.data) - self.site_det_siteId.data.copy_(cpu_site_det_siteId.data) - self.site_det_score.data.copy_(cpu_site_det_score.data) - updXloc.data.copy_(cpu_node_x.data) - updYloc.data.copy_(cpu_node_y.data) - updZloc.data.copy_(cpu_node_z.data) - else: - lut_ff_legalization_cpp.ripUp_SlotAssign(pos, self.pin_offset_x, self.pin_offset_y, self.net_wts, self.net_bbox, inst_areas, wlPrecond, - torch.flatten(self.site_xy), self.net_pinIdArrayX, self.net_pinIdArrayY, torch.flatten(self.spiral_accessor), - self.node2fence_region_map, self.lut_type, torch.flatten(self.site_types), self.node2pincount, self.net2pincount, - self.pin2net_map, self.pin2node_map, self.pin_typeIds, self.flop2ctrlSetId_map, - self.flop_ctrlSets, self.extended_ctrlSets, self.ext_ctrlSet_start_map, self.flat_node2pin_start_map, - self.flat_node2pin_map, self.flat_net2pin_start_map, self.flat_node2prclstrCount, torch.flatten(self.flat_node2precluster_map), - sorted_remNode_map, sorted_remNode_idx, sorted_net_map, self.node2outpinIdx_map, self.flat_net2pin_map, - self.addr2site_map, self.site2addr_map, self.sites_with_special_nodes, self.special_nodes, - self.nbrDistEnd, self.xWirelenWt, self.yWirelenWt, self.extNetCountWt, self.wirelenImprovWt, slotAssignFlowWeightScale, - slotAssignFlowWeightIncr, self.lutTypeInSliceUnit, self.lut_maxShared, num_remInsts, self.num_sites_x, self.num_sites_y, - self.num_clb_sites, spiralBegin, spiralEnd, self.half_ctrl_mode, self.CKSR_IN_CLB, self.CE_IN_CLB, self.SLICE_CAPACITY, - self.HALF_SLICE_CAPACITY, self.BLE_CAPACITY, self.NUM_BLE_PER_SLICE, self.NUM_BLE_PER_HALF_SLICE, - self.netShareScoreMaxNetDegree, self.WLscoreMaxNetDegree, ripupExpansion, greedyExpansion, self.SIG_IDX, - self.lutId, self.ffId, self.sliceId, self.num_threads, - self.inst_curr_detSite, self.site_det_sig_idx, self.site_det_sig, self.site_det_impl_lut, self.site_det_impl_ff, - self.site_det_impl_cksr, self.site_det_impl_ce, self.site_det_siteId, self.site_det_score, updXloc, updYloc, updZloc) - - #Carry-Chain Checker - if self.num_carry_chains > 0: - carry_chain_checker_loc(self.flat_cc2node_start_map, self.flat_cc2node_map, updXloc, updYloc, - updZloc, self.HALF_SLICE_CAPACITY, self.lutsInSlice, self.num_carry_chains, self.device) - - totalNodes = int(len(pos)/2) - node_z.data.copy_(updZloc) - pos[:self.num_nodes].data.masked_scatter_(self.lut_flop_mask, updXloc[self.lut_flop_mask]) - pos[totalNodes:totalNodes+self.num_nodes].data.masked_scatter_(self.lut_flop_mask, updYloc[self.lut_flop_mask]) - - ###Logic Utilization - logic_util = 100 * (self.site_det_sig_idx > 0 ).sum().item() /self.num_clb_sites - logging.info("Occupied Slices = %d, Total slices = %d and LOGIC UTILIZATION %.4f%%" % ((self.site_det_sig_idx > 0 ).sum().item(), self.num_clb_sites, logic_util)) - print("RipUP & Greedy LG on %d insts (%d LUTs + %d FFs) takes %.3f seconds" % (num_remInsts, numLUTs, numFFs, time.time()-tt)) - - return pos diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh deleted file mode 100644 index 791acf51..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh +++ /dev/null @@ -1,1297 +0,0 @@ -/** - * @file graph_matching.cuh - * @author Rachel Selina Rajarathnam, Zixuan Jiang (DREAMPlaceFPGA-PL) - * @date Oct 2022 - */ -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -#define INVALID -1 -#define INPUT_PIN 1 -#define MAX_SLICE_INPUTS 100 -//Reset below values if required: -//Below values are for max vertex count of N=20 -#define SLICE_MAX_CAP 20 -#define N 20 -#define N2 400 -#define NN 40 -#define M 30 -#define M2 900 - -///helper functions for Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 -inline __device__ void queue_insert( - const int Q_size, int &Q_front, int &Q_back, int* Q, const int element) -{ - if (Q_back == Q_size-1) - { - printf("ERROR: QUEUE OVERFLOW - INCREASE SIZE\n"); - } else - { - if (Q_front == INVALID) - { - Q_front = 0; - } - if (Q_back == INVALID) - { - Q_back = 0; - } else - { - Q_back += 1; - } - Q[Q_back] = element; - } -} - -inline __device__ void queue_pop( - int &Q_front, const int Q_back) -{ - if (Q_front == INVALID || Q_front > Q_back) - { - printf("WARN: QUEUE UNDERFLOW\n"); - } else - { - ++Q_front; - } - -} - -inline __device__ void add_edge( - const int u, const int v, int* g) -{ - g[u*M+v] = u; - g[v*M+u] = v; -} - -inline __device__ void match( - const int u, const int v, int* g, int* mate) -{ - g[u*M+v] = INVALID; - g[v*M+u] = INVALID; - mate[u] = v; - mate[v] = u; -} - -//Note: x should not be changed outside the function! -inline __device__ void trace( - int x, const int* bl, const int* p, int* vx, int &vx_length) -{ - while(true) - { - while(bl[x] != x) x = bl[x]; - if(vx_length > 0 && vx[vx_length - 1] == x) break; - vx[vx_length] = x; - ++vx_length; - x = p[x]; - } -} - -__device__ void contract( - const int c, int x, int y, int* vx, int &vx_length, int* vy, - int &vy_length, int* b, int* bIndex, int* bl, int* g) -{ - bIndex[c] = 0; - int r = vx[vx_length - 1]; - while(vx_length > 0 && vy_length > 0 && vx[vx_length - 1] == vy[vy_length - 1]) - { - r = vx[vx_length - 1]; - --vx_length; - --vy_length; - } - // b[c].push_back(r); - b[c * M + bIndex[c]] = r; - ++bIndex[c]; - - // b[c].insert(b[c].end(), vx.rbegin(), vx.rend()); - for (int i = vx_length - 1; i >= 0; --i) { - b[c * M + bIndex[c]] = vx[i]; - ++bIndex[c]; - } - - // b[c].insert(b[c].end(), vy.begin(), vy.end()); - for (int i = 0; i < vy_length; ++i) - { - b[c * M + bIndex[c]] = vy[i]; - ++bIndex[c]; - } - - for(int i = 0; i <= c; ++i) - { - g[c*M+i] = INVALID; - g[i*M+c] = INVALID; - } - - for (int j = 0; j < bIndex[c]; ++j) - { - int z = b[c * M + j]; - bl[z] = c; - for(int i = 0; i < c; ++i) - { - if(g[z*M+i] != INVALID) - { - g[c*M+i] = z; - g[i*M+c] = g[i*M+z]; - } - } - } -} - -__device__ void lift( - const int n, const int* g, const int* b, const int* bIndex, int* vx, - int &vx_length, int* A, int &A_length) -{ - while (vx_length >= 2) - { - int z = vx[vx_length-1]; - --vx_length; - if (z < n) - { - A[A_length] = z; - ++A_length; - continue; - } - int w = vx[vx_length-1]; - int i = 0; - if (A_length % 2 == 0) - { - //Find index of g[z][w] within b[z] - int val = g[z*M+w]; - for (int bId = 0; bId < bIndex[z]; ++bId) - { - if (b[z*M+bId] == val) - { - i = bId; - break; - } - } - } - int j = 0; - if (A_length % 2 == 1) - { - //Find index of g[z][A.back()] within b[z] - int val = g[z*M+A[A_length-1]]; - for (int bId = 0; bId < bIndex[z]; ++bId) - { - if (b[z*M+bId] == val) - { - j = bId; - break; - } - } - } - int k = bIndex[z]; - int dif = (A_length % 2 == 0 ? i%2 == 1 : j%2 == 0) ? 1 : k-1; - - while(i != j) - { - vx[vx_length] = b[z*M+i]; - ++vx_length; - i = (i + dif) % k; - } - vx[vx_length] = b[z*M+i]; - ++vx_length; - } -} - -///End of helper functions for Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 - -//Sort -inline __device__ void sort_array(int* input_array, int& num_elements) -{ - if (num_elements > 1) - { - for (int ix = 1; ix < num_elements; ++ix) - { - for (int jx = 0; jx < num_elements-1; ++jx) - { - if (input_array[jx] > input_array[jx+1]) - { - int val = input_array[jx]; - input_array[jx] = input_array[jx+1]; - input_array[jx+1] = val; - } - } - } - } -} - -//remove duplicates from a sorted array -inline __device__ void remove_duplicates(int* input_array, int& num_elements) -{ - if (num_elements > 1) - { - for (int i = 0; i < num_elements; ++i) - { - for (int j=0; j < i; ++j) - { - if (input_array[i] == input_array[j]) - { - --num_elements; - for (int k=i; k < num_elements; ++k) - { - input_array[k] = input_array[k+1]; - } - --i; - } - } - } - } -} - -//Ensure flops in subSlice share the same set of ctrl signals -inline __device__ bool ffs_ctrl_match( - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* pin_typeIds, const int* pin2net_map, - const int ffInst, const int offInst) -{ - if (ffInst == INVALID || offInst == INVALID) - { - return true; - } - - int ff_ctrl[10], off_ctrl[10]; - //Initialize ctrls to INVALID - for (int sg = 0; sg < 10; ++sg) - { - ff_ctrl[sg] = INVALID; - off_ctrl[sg] = INVALID; - } - - int ff_pins[10], off_pins[10]; - int ffpIdx(0), offpIdx(0); - - int elIt = flat_node2pin_start_map[ffInst]; - int elEnd = flat_node2pin_start_map[ffInst+1]; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - if (pinType < 3 || pinType > 9) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - ff_ctrl[pinType] = netId; - ff_pins[ffpIdx] = pinType; - ++ffpIdx; - } - - if (ffpIdx == 0) - { - return true; - } - - elIt = flat_node2pin_start_map[offInst]; - elEnd = flat_node2pin_start_map[offInst+1]; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - if (pinType < 3 || pinType > 9) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - off_ctrl[pinType] = netId; - off_pins[offpIdx] = pinType; - ++offpIdx; - } - - if (offpIdx == 0) - { - return true; - } - - //Sort contents - sort_array(ff_pins, ffpIdx); - sort_array(off_pins, offpIdx); - - int idxA = 0, idxB = 0; - int pinTypeA = ff_pins[idxA]; - int pinTypeB = off_pins[idxB]; - - //Only compare if pinType matches - while (idxA < ffpIdx && idxB < offpIdx) - { - if (pinTypeA < pinTypeB) - { - ++idxA; - - if (idxA < ffpIdx) - { - pinTypeA = ff_pins[idxA]; - } else - { - break; - } - } - else if (pinTypeA > pinTypeB) - { - ++idxB; - - if (idxB < offpIdx) - { - pinTypeB = off_pins[idxB]; - } else - { - break; - } - } else - { - if (ff_ctrl[pinTypeA] != off_ctrl[pinTypeB]) - { - return false; - } - - ++idxA; - ++idxB; - - if (idxA < ffpIdx && idxB < offpIdx) - { - pinTypeA = ff_pins[idxA]; - pinTypeB = off_pins[idxB]; - } else - { - break; - } - } - } - - return true; -} - -//Ensure unique inputs and loopbacks of subSlice are within limits -inline __device__ bool subSlice_compatibility( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, - const int* pin_typeIds, const int* node2fence_region_map, - const int* res_ff, const int* res_lut, const int lutId, - const int SLICE_CAPACITY, const int BLE_CAPACITY, - const int MAX_INPUTS_IN_SUBSLICE, const int MAX_LOOPBACK_IN_SUBSLICE, - const int k, const int ssInstId) -{ - ////DBG - //char printMsg = 0; - ////DBG - - int ssId = int(k/2)*2; - int subSliceElements[8]; - int numSSEls = 0; - - if (res_lut[ssId] != INVALID) - { - subSliceElements[numSSEls] = res_lut[ssId]; - ++numSSEls; - - ////DBG - //if (printMsg == 0 && (res_lut[ssId] == 11846 || res_lut[ssId] == 11848)) - //{ - // printMsg = 1; - //} - ////DBG - } - - if (res_ff[ssId] != INVALID) - { - subSliceElements[numSSEls] = res_ff[ssId]; - ++numSSEls; - - ////DBG - //if (res_ff[ssId] == 73994 || res_ff[ssId] == 73995) - //{ - // printMsg = 1; - //} - ////DBG - } - - if (res_lut[ssId+1] != INVALID) - { - subSliceElements[numSSEls] = res_lut[ssId+1]; - ++numSSEls; - - ////DBG - //if (printMsg == 0 && (res_lut[ssId+1] == 11846 || res_lut[ssId+1] == 11848)) - //{ - // printMsg = 1; - //} - ////DBG - } - - if (res_ff[ssId+1] != INVALID) - { - subSliceElements[numSSEls] = res_ff[ssId+1]; - ++numSSEls; - - ////DBG - //if (printMsg == 0 && (res_ff[ssId+1] == 73994 || res_ff[ssId+1] == 73995)) - //{ - // printMsg = 1; - //} - ////DBG - } - - //Check the new inst for compatibility - if (ssInstId != INVALID) - { - subSliceElements[numSSEls] = ssInstId; - ++numSSEls; - - int lut_type = (node2fence_region_map[ssInstId] == lutId); - int loc_avail = lut_type ? res_lut[k] == INVALID : res_ff[k] == INVALID; - if (loc_avail == 0) - { - return false; - } - - if (lut_type == 0 && - (res_ff[ssId] != INVALID || res_ff[ssId+1] != INVALID)) - { - int ffA = (res_ff[ssId] != INVALID) ? res_ff[ssId] : res_ff[ssId + 1]; - if(!ffs_ctrl_match(flat_node2pin_start_map, flat_node2pin_map, - pin_typeIds, pin2net_map, ffA, ssInstId)) - { - return false; - } - } - } - - if (numSSEls > 2*BLE_CAPACITY) - { - return false; - } - - ////DBG - //if (printMsg == 0 && (ssInstId == 73994 || ssInstId == 73995 || - // ssInstId == 11846 || ssInstId == 11848)) - //{ - // printMsg = 1; - //} - ////DBG - - int all_inNets[MAX_SLICE_INPUTS]; - int num_all_inNets(0); - //Check if LUT is driving FF in same subSlice - int lut_ff_conns = 0; - int lut_outNets[4]; - int num_lut_outNets = 0; - int ff_inNets[8]; - int num_ff_inNets = 0; - - for (int elId = 0; elId < numSSEls; ++elId) - { - int instId = subSliceElements[elId]; - - int lut_inst = (node2fence_region_map[instId] == lutId); - - int elIt = flat_node2pin_start_map[instId]; - int elEnd = flat_node2pin_start_map[instId+1]; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - - if (lut_inst == 1 && pinType == 0) - { - lut_outNets[num_lut_outNets] = pin2net_map[flat_node2pin_map[el]]; - ++num_lut_outNets; - } - if (pinType != INPUT_PIN) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - - all_inNets[num_all_inNets] = netId; - ++num_all_inNets; - - if (lut_inst == 0) - { - ff_inNets[num_ff_inNets] = netId; - ++num_ff_inNets; - } - } - } - - if (num_lut_outNets > 0 && num_ff_inNets > 0) - { - sort_array(lut_outNets, num_lut_outNets); - remove_duplicates(lut_outNets, num_lut_outNets); - - sort_array(ff_inNets, num_ff_inNets); - remove_duplicates(ff_inNets, num_ff_inNets); - - int idxIn = 0, idxOut = 0; - int netIn = ff_inNets[idxIn]; - int netOut = lut_outNets[idxOut]; - - while (true) - { - if (netIn < netOut) - { - ++idxIn; - if (idxIn < num_ff_inNets) - { - netIn = ff_inNets[idxIn]; - } else - { - break; - } - } else if (netIn > netOut) - { - ++idxOut; - if (idxOut < num_lut_outNets) - { - netOut = lut_outNets[idxOut]; - } else - { - break; - } - } else - { - ++lut_ff_conns; - break; - } - } - } - - sort_array(all_inNets, num_all_inNets); - remove_duplicates(all_inNets, num_all_inNets); - - ////DBG - //if (printMsg == 1) - //{ - // printf("%d insts in ALM: ", numSSEls); - // for (int elId = 0; elId < numSSEls; ++elId) - // { - // printf("%d ",subSliceElements[elId]); - // } - - // printf(" have %d unique inputs\n", num_all_inNets); - //} - ////DBG - - if (num_all_inNets > MAX_INPUTS_IN_SUBSLICE) - { - return false; - } - - //Loopbacks from subSlice driving Slice instances - //Get input nets from other subSlices - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - //Skip already visited subSlice - if (sg == ssId) continue; - - int subSlice_insts[8]; - int num_subSlice_insts = 0; - - //FFs - if (res_ff[sg] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_ff[sg]; - ++num_subSlice_insts; - } - if (res_ff[sg+1] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_ff[sg+1]; - ++num_subSlice_insts; - } - - //LUTs - if (res_lut[sg] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_lut[sg]; - ++num_subSlice_insts; - } - if (res_lut[sg+1] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_lut[sg+1]; - ++num_subSlice_insts; - } - - //Get input nets - for (int el = 0; el < num_subSlice_insts; ++el) - { - int instId = subSlice_insts[el]; - int pStart = flat_node2pin_start_map[instId]; - int pEnd = flat_node2pin_start_map[instId+1]; - - for (int pId = pStart; pId < pEnd; ++pId) - { - //Skip if not an input pin - int pinType = pin_typeIds[flat_node2pin_map[pId]]; - if (pinType != INPUT_PIN) continue; - - all_inNets[num_all_inNets] = pin2net_map[flat_node2pin_map[pId]]; - ++num_all_inNets; - } - } - } - - sort_array(all_inNets, num_all_inNets); - remove_duplicates(all_inNets, num_all_inNets); - - if (num_all_inNets == 0) - { - return true; - } - - //Get nets from all subSlices - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - int subSlice_insts[8]; - int num_subSlice_insts(0); - - int subSlice_outNets[8]; - int num_subSlice_outNets(0); - - if (sg == ssId) - { - for (int elId = 0; elId < numSSEls; ++elId) - { - subSlice_insts[num_subSlice_insts] = subSliceElements[elId]; - ++num_subSlice_insts; - } - } else - { - //FFs - if (res_ff[sg] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_ff[sg]; - ++num_subSlice_insts; - } - if (res_ff[sg+1] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_ff[sg+1]; - ++num_subSlice_insts; - } - - //LUTs - if (res_lut[sg] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_lut[sg]; - ++num_subSlice_insts; - } - if (res_lut[sg+1] != INVALID) - { - subSlice_insts[num_subSlice_insts] = res_lut[sg+1]; - ++num_subSlice_insts; - } - } - - for (int idx = 0; idx < num_subSlice_insts; ++idx) - { - int instId = subSlice_insts[idx]; - - int ndOutId = 4*instId; - int ndOutPins = ndOutId + 4; - for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - subSlice_outNets[num_subSlice_outNets] = outNetId; - ++num_subSlice_outNets; - } - } - if (num_subSlice_outNets <= MAX_LOOPBACK_IN_SUBSLICE) continue; - - sort_array(subSlice_outNets, num_subSlice_outNets); - remove_duplicates(subSlice_outNets, num_subSlice_outNets); - - int num_loopbacks = 0; - //Compare sorted subSlice outNets with Slice input nets - - int idxIn = 0, idxOut = 0; - int netIn = all_inNets[idxIn]; - int netOut = subSlice_outNets[idxOut]; - - while (num_loopbacks <= MAX_LOOPBACK_IN_SUBSLICE) - { - if (netIn < netOut) - { - ++idxIn; - if (idxIn < num_all_inNets) - { - netIn = all_inNets[idxIn]; - } else - { - break; - } - } else if (netIn > netOut) - { - ++idxOut; - if (idxOut < num_subSlice_outNets) - { - netOut = subSlice_outNets[idxOut]; - } else - { - break; - } - } else - { - ++num_loopbacks; - ++idxIn; - ++idxOut; - if (idxIn < num_all_inNets && - idxOut < num_subSlice_outNets) - { - netIn = all_inNets[idxIn]; - netOut = subSlice_outNets[idxOut]; - } else - { - break; - } - } - } - - if (sg == ssId && lut_ff_conns > 0) - { - --num_loopbacks; - } - - if (num_loopbacks > MAX_LOOPBACK_IN_SUBSLICE) - { - return false; - } - } - - return true; -} - -//two lut compatibility -inline __device__ bool two_lut_compatibility_check( - const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int lutTypeInSliceUnit, const int lut_maxShared, const int lutAId, - const int lutBId) -{ - if (lut_type[lutAId] == lutTypeInSliceUnit || - lut_type[lutBId] == lutTypeInSliceUnit) - { - return false; - } - - int numInputs = lut_type[lutAId] + lut_type[lutBId]; - - if (numInputs <= lut_maxShared) - { - return true; - } - - //Include condition for LUT0 - if (lut_type[lutAId] == 0 || lut_type[lutBId] == 0) - { - return false; - } - - int lutANets[SLICE_MAX_CAP], lutBNets[SLICE_MAX_CAP]; - int lutAIdx(0), lutBIdx(0); - - int lutAIt = flat_node2pin_start_map[lutAId]; - int lutBIt = flat_node2pin_start_map[lutBId]; - int lutAEnd = flat_node2pin_start_map[lutAId+1]; - int lutBEnd = flat_node2pin_start_map[lutBId+1]; - - for (int el = lutAIt; el < lutAEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != INPUT_PIN) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutANets[lutAIdx] = netId; - ++lutAIdx; - } - - if (lutAIdx > 1) - { - //Sort contents of lutANets - for (int ix = 1; ix < lutAIdx; ++ix) - { - for (int jx = 0; jx < lutAIdx-1; ++jx) - { - if (lutANets[jx] > lutANets[jx+1]) - { - int val = lutANets[jx]; - lutANets[jx] = lutANets[jx+1]; - lutANets[jx+1] = val; - } - } - } - } - - for (int el = lutBIt; el < lutBEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != INPUT_PIN) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutBNets[lutBIdx] = netId; - ++lutBIdx; - } - - if (lutBIdx > 1) - { - //Sort contents of lutBNets - for (int ix = 1; ix < lutBIdx; ++ix) - { - for (int jx = 0; jx < lutBIdx-1; ++jx) - { - if (lutBNets[jx] > lutBNets[jx+1]) - { - int val = lutBNets[jx]; - lutBNets[jx] = lutBNets[jx+1]; - lutBNets[jx+1] = val; - } - } - } - } - - int idxA = 0, idxB = 0; - int netIdA = lutANets[idxA]; - int netIdB = lutBNets[idxB]; - - while(numInputs > lut_maxShared) - { - if (netIdA < netIdB) - { - ++idxA; - if (idxA < lutAIdx) - { - netIdA = lutANets[idxA]; - } else - { - break; - } - } else if (netIdA > netIdB) - { - ++idxB; - if (idxB < lutBIdx) - { - netIdB = lutBNets[idxB]; - } else - { - break; - } - - } else - { - --numInputs; - ++idxA; - ++idxB; - - if (idxA < lutAIdx && idxB < lutBIdx) - { - netIdA = lutANets[idxA]; - netIdB = lutBNets[idxB]; - } else - { - break; - } - } - } - - return numInputs <= lut_maxShared; -} - -//Given lut arrangement, fit FFs -inline __device__ bool fit_ffs( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, - const int* pin_typeIds, const int* node2fence_region_map, - const int* res_lut, const int lutId, const int lut_maxShared, - const int SLICE_CAPACITY, const int BLE_CAPACITY, int* res_ff) -{ - //Rearrange all FFs based on compatibility - int temp_ff[SLICE_MAX_CAP]; - int rem_ffs[SLICE_MAX_CAP]; - int num_rem_ffs(0); - - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - temp_ff[sg] = res_ff[sg]; - temp_ff[sg+1] = res_ff[sg+1]; - - if (res_ff[sg] != INVALID || res_ff[sg+1] != INVALID) - { - if (!subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, - node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, sg, INVALID)) - { - if (res_ff[sg] != INVALID) - { - rem_ffs[num_rem_ffs] = res_ff[sg]; - ++num_rem_ffs; - res_ff[sg] = INVALID; - } - if (res_ff[sg+1] != INVALID) - { - rem_ffs[num_rem_ffs] = res_ff[sg+1]; - ++num_rem_ffs; - res_ff[sg+1] = INVALID; - } - } - } - } - - if (num_rem_ffs == 0) return true; - - //Greedily assign remaining FFs - char ffLocFound = 0; - for (int el = 0; el < num_rem_ffs; ++el) - { - int ffId = rem_ffs[el]; - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - if (res_ff[sg] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, - node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, sg, ffId)) - { - res_ff[sg] = ffId; - ++ffLocFound; - break; - } - } else if (res_ff[sg+1] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, - node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, sg+1, ffId)) - { - res_ff[sg+1] = ffId; - ++ffLocFound; - break; - } - } - } - } - - if (ffLocFound == num_rem_ffs) - { - return true; - } - - //Revert - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_ff[sg] = temp_ff[sg]; - } - - return false; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -inline __device__ bool fit_luts_to_candidate_impl( - const int* node2outpinIdx_map, const int* lut_type, const int* pin2net_map, - const int* pin_typeIds, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* flat_node2precluster_map, - const int* node2fence_region_map, const int* special_nodes, - const int half_ctrl_mode, const int lutTypeInSliceUnit, const int lut_maxShared, - const int instPcl, const int node2prclstrCount, const int NUM_BLE_PER_SLICE, - const int SLICE_CAPACITY, const int BLE_CAPACITY, const int lutId, - int* res_lut, int* res_ff) -{ - int luts[N], lut6s[N], splNodes[N]; - int lutIdx(0), lut6Idx(0), splIdx(0); - - //Ensure subSlice-level shared input count is met - int temp_lut[SLICE_MAX_CAP]; - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - if (res_lut[sg] != INVALID) - { - if (special_nodes[res_lut[sg]] == 1) - { - splNodes[splIdx] = res_lut[sg]; - ++splIdx; - } else - { - if (lut_type[res_lut[sg]] < lutTypeInSliceUnit) - { - luts[lutIdx] = res_lut[sg]; - ++lutIdx; - } else - { - lut6s[lut6Idx] = res_lut[sg]; - ++lut6Idx; - } - } - } - if (half_ctrl_mode == 0) - { - temp_lut[sg] = res_lut[sg]; - } - } - - //int lutSize = lutIdx + lut6Idx; - for (int idx = 0; idx < node2prclstrCount; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - if (node2fence_region_map[clInstId] == lutId) - { - if (lut_type[clInstId] < lutTypeInSliceUnit) - { - luts[lutIdx] = clInstId; - ++lutIdx; - - sort_array(luts, lutIdx); - remove_duplicates(luts, lutIdx); - } else - { - lut6s[lut6Idx] = clInstId; - ++lut6Idx; - - sort_array(lut6s, lut6Idx); - remove_duplicates(lut6s, lut6Idx); - } - } - } - - int splNodeSpace = splIdx; - splNodeSpace += (splIdx & 1) ? 1 : 0; - - //graph matching can be called even if res_lut if full! - //Guard band for graph matching implementation with fixed memory - if (lutIdx + 2*lut6Idx + splNodeSpace > SLICE_CAPACITY) - { - return false; - } - - ///Edmonds Blossom Implementation based on https://codeforces.com/blog/entry/92339 - int n = lutIdx; //n - #vertices - //int m = (n%2 == 0) ? 3*n/2: 3*(n+1)/2; //m = 3n/2 - - int mate[N]; //array of length n; For each vertex u, if exposed mate[u] = -1 or mate[u] = u - int b[M2]; //For each blossom u, b[u] is list of all vertices contracted from u - int bIndex[M]; - int p[M]; //array of length m; For each vertex/blossom u, p[u] is parent in the search forest - int d[M]; //array of length m; For each vertex u, d[u] is status in search forest. d[u] = 0 if unvisited, d[u] = 1 is even depth from root and d[u] = 2 is odd depth from root - int bl[M]; //array of length m; For each vertex/blossom u, bl[u] is the blossom containing u. If not contracted, bl[u] = u. - int g[M2]; //table of size mxm with information of unmatched edges.g[u][v] = -1 if no unmatched vertices; g[u][v] = u, if u is a vertex. - - //Initialize mate - for (int mId = 0; mId < n; ++mId) - { - mate[mId] = INVALID; - } - for (int gId = 0; gId < M2; ++gId) - { - g[gId] = INVALID; - } - - //Create graph with luts - for(int ll = 0; ll < lutIdx; ++ll) - { - for(int rl = ll+1; rl < lutIdx; ++rl) - { - if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, - lutTypeInSliceUnit, lut_maxShared, luts[ll], luts[rl])) - { - add_edge(ll, rl, g); - } - } - } - - int totalPairs(0); - - for (int ans = 0; ; ++ans) - { - for (int dId = 0; dId < M; ++dId) - { - d[dId] = 0; - } - - int Q[NN]; - int Q_size(NN); - int Q_front(INVALID), Q_back(INVALID); - - for (int i = 0; i < M; ++i) - { - bl[i] = i; - } - for (int i = 0; i < n; ++i) - { - if (mate[i] == INVALID) - { - queue_insert(Q_size, Q_front, Q_back, Q, i); - p[i] = i; - d[i] = 1; - } - } - - int c = N; - bool aug(false); - - while ((Q_front != INVALID && Q_front <= Q_back) && !aug) - { - int x = Q[Q_front]; - //queue_pop(Q_front, Q_back, Q); - queue_pop(Q_front, Q_back); - - if (bl[x] != x) continue; - - for (int y = 0; y < c; ++y) - { - if (bl[y] == y && g[x*M+y] != INVALID) - { - if (d[y] == 0) - { - p[y] = x; - d[y] = 2; - p[mate[y]] = y; - d[mate[y]] = 1; - queue_insert(Q_size, Q_front, Q_back, Q, mate[y]); - } else if (d[y] == 1) - { - int vx[2*M], vy[2*M]; - int vx_length = 0, vy_length = 0; - trace(x, bl, p, vx, vx_length); - trace(y, bl, p, vy, vy_length); - - if (vx[vx_length-1] == vy[vy_length-1]) - { - contract(c, x, y, vx, vx_length, vy, vy_length, b, bIndex, bl, g); - queue_insert(Q_size, Q_front, Q_back, Q, c); - p[c] = p[b[c*M]]; - d[c] = 1; - ++c; - } else - { - aug = true; - int new_vx[2*M], new_vy[2*M]; - new_vx[0] = y; - for (int idx = 0; idx < vx_length; ++idx) - { - new_vx[idx+1] = vx[idx]; - } - ++vx_length; - new_vy[0] = x; - for (int idx = 0; idx < vy_length; ++idx) - { - new_vy[idx+1] = vy[idx]; - } - ++vy_length; - - int A[4*M], B[2*M]; - int A_length = 0, B_length = 0; - - lift(n, g, b, bIndex, new_vx, vx_length, A, A_length); - lift(n, g, b, bIndex, new_vy, vy_length, B, B_length); - - for (int idx = B_length-1; idx >= 0; --idx) - { - A[A_length] = B[idx]; - ++A_length; - } - - for (int i = 0; i < A_length; i += 2) - { - match(A[i], A[i+1], g, mate); - if (i + 2 < A_length) - { - add_edge(A[i+1], A[i + 2], g); - } - } - } - break; - } - } - } - } - if (!aug) - { - totalPairs = ans; - break; - } - } - - int otherNodePairs = lut6Idx + splNodeSpace/2; - - if ((lutIdx - totalPairs + otherNodePairs) > NUM_BLE_PER_SLICE) - { - return false; - } - - int idxL = 0; - - if (splIdx > 0) - { - for (int spId = 0; spId < splIdx; ++spId) - { - res_lut[idxL] = splNodes[spId]; - ++idxL; - } - if (splNodeSpace > splIdx) - { - res_lut[idxL] = INVALID; - ++idxL; - } - } - - for (int iil = 0; iil < lut6Idx; ++iil) - { - res_lut[idxL] = lut6s[iil]; - res_lut[idxL + 1] = INVALID; - idxL += BLE_CAPACITY; - } - - for (int mId = 0; mId < n; ++mId) - { - if (mate[mId] == INVALID) - { - res_lut[idxL] = luts[mId]; - res_lut[idxL + 1] = INVALID; - idxL += BLE_CAPACITY; - } - } - - int ck[N] = {0}; - for (int mId = 0; mId < n; ++mId) - { - if (mate[mId] != INVALID && ck[mId] == 0 && ck[mate[mId]] == 0) - { - ++ck[mId]; - ++ck[mate[mId]]; - - res_lut[idxL] = luts[mId]; - res_lut[idxL + 1] = luts[mate[mId]]; - idxL += BLE_CAPACITY; - } - } - for (int lIdx = idxL; lIdx < SLICE_CAPACITY; ++lIdx) - { - res_lut[lIdx] = INVALID; - } - - //Ensure subSlice-level shared input count is met - if (half_ctrl_mode == 0) - { - //Try to rearrange ffs to avoid compatibility issues - if (fit_ffs(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, - node2fence_region_map, res_lut, lutId, lut_maxShared, - SLICE_CAPACITY, BLE_CAPACITY, res_ff)) - { - return true; - } - - //Revert - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = temp_lut[sg]; - } - return false; - } - - return true; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp deleted file mode 100644 index 8d312fa1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp +++ /dev/null @@ -1,7319 +0,0 @@ -/** - * @file lut_ff_legalization.cpp - * @author Rachel Selina (DREAMPlaceFPGA-PL) - * @date Oct 2023 - * @brief Legalize LUT/FF - */ - -#include -#include -#include -#include -#include -#include -#include -//pybind Dependency -#include -#include -#include -//Lemon Graph Dependency -#include "lemon/list_graph.h" -#include "lemon/matching.h" -#include "lemon/network_simplex.h" -//Common Dependency -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -static const int INVALID = -1; -//Reset below values if required for the architecture -static const int SLICE_MAX_CAP = 20; -static const int SIG_MAX_CAP = 40; -static const int CE_MAX_CAP = 7; -static const int CKSR_MAX_CAP = 2; - -//Mutex for critical section -std::mutex mtx; - -//Struct for Candidate -template -struct Candidate -{ - T score = 0.0; - int siteId = INVALID; - int sigIdx = 0; - int sig[SIG_MAX_CAP]; - int impl_lut[SLICE_MAX_CAP]; - int impl_ff[SLICE_MAX_CAP]; - int impl_cksr[CKSR_MAX_CAP]; - int impl_ce[CE_MAX_CAP]; - - void reset() - { - score = 0.0; - siteId = INVALID; - sigIdx = 0; - - for(int sg = 0; sg < SIG_MAX_CAP; ++sg) - { - sig[sg] = INVALID; - } - for(int sg = 0; sg < SLICE_MAX_CAP; ++sg) - { - impl_lut[sg] = INVALID; - impl_ff[sg] = INVALID; - } - for(int sg = 0; sg < CKSR_MAX_CAP; ++sg) - { - impl_cksr[sg] = INVALID; - } - for(int sg = 0; sg < CE_MAX_CAP; ++sg) - { - impl_ce[sg] = INVALID; - } - } -}; - -//Struct for RipUpCand -template -struct RipUpCand -{ - // If a < b, then a has higher priority than b - bool operator<(const RipUpCand &rhs) const - { - return (legal == rhs.legal ? score > rhs.score : legal); - } - - int siteId = INVALID; - T score = -10000.0; - bool legal = false; - Candidate cand; - - void reset() - { - siteId = INVALID; - score = -10000.0; - legal = false; - cand.reset(); - } - -}; - -//Struct for subSlice -template -struct subSlice -{ - int lut[2] = {INVALID, INVALID}; - int ff[2] = {INVALID, INVALID}; - T score = 0.0; - T improv = 0.0; -}; - -////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////// - -void clear_cand_contents( - const int tsPQ, const int SIG_IDX, const int SLICE_CAPACITY, - const int CKSR_IN_CLB, const int CE_IN_CLB, - int* site_sig_idx, int* site_sig, - int* site_impl_lut, int* site_impl_ff, - int* site_impl_cksr, int* site_impl_ce) -{ - int topIdx(tsPQ*SIG_IDX); - int lutIdx = tsPQ*SLICE_CAPACITY; - int ckIdx = tsPQ*CKSR_IN_CLB; - int ceIdx = tsPQ*CE_IN_CLB; - - for(int sg = 0; sg < SIG_IDX; ++sg) - { - site_sig[topIdx + sg] = INVALID; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_impl_lut[lutIdx + sg] = INVALID; - site_impl_ff[lutIdx + sg] = INVALID; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_impl_cksr[ckIdx + sg] = INVALID; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_impl_ce[ceIdx + sg] = INVALID; - } - site_sig_idx[tsPQ] = 0; -} - -// check if candidate is valid -// Candidate is valid if the instance is not commited to a site -inline bool candidate_validity_check( - const int* is_mlab_node, const int SLICE_CAPACITY, - const int topIdx, const int pqSigIdx, - const int siteId, const int* site_curr_pq_sig, - const int* inst_curr_detSite) -{ - //Check first instance if it is mlab - if (pqSigIdx == 2*SLICE_CAPACITY && - is_mlab_node[site_curr_pq_sig[topIdx]] == 1) - { - int pqInst = site_curr_pq_sig[topIdx]; - if (inst_curr_detSite[pqInst] != INVALID && - inst_curr_detSite[pqInst] != siteId) - { - return false; - } - } else - { - for (int i = 0; i < pqSigIdx; ++i) - { - int pqInst = site_curr_pq_sig[topIdx + i]; - - if (inst_curr_detSite[pqInst] != INVALID && - inst_curr_detSite[pqInst] != siteId) - { - return false; - } - } - } - return true; -} - -// define inst_in_sig -inline bool inst_in_sig(const int instId, const int siteDetSigSize, const int* site_det_sig, const int siteDetIdx) -{ - for(int i = 0; i < siteDetSigSize; ++i) - { - if (site_det_sig[siteDetIdx + i] == instId) - { - return true; - } - } - return false; -} - -// define two_lut_compatibility_check -inline bool two_lut_compatibility_check( - const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int lutTypeInSliceUnit, const int lut_maxShared, const int lutAId, - const int lutBId) -{ - if (lut_type[lutAId] == lutTypeInSliceUnit || lut_type[lutBId] == lutTypeInSliceUnit) - { - return false; - } - - int numInputs = lut_type[lutAId] + lut_type[lutBId]; - - if (numInputs <= lut_maxShared) - { - return true; - } - - //Handle LUT0 - if (lut_type[lutAId] == 0 || lut_type[lutBId] == 0) - { - return false; - } - - //Note: It is important that nets of lutA and lutB are sorted before comparison - std::vector lutAiNets, lutBiNets; - - int lutAIt = flat_node2pin_start_map[lutAId]; - int lutBIt = flat_node2pin_start_map[lutBId]; - int lutAEnd = flat_node2pin_start_map[lutAId+1]; - int lutBEnd = flat_node2pin_start_map[lutBId+1]; - - for (int el = lutAIt; el < lutAEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutAiNets.emplace_back(netId); - } - if (lutAiNets.size() > 1) - { - std::sort(lutAiNets.begin(), lutAiNets.end()); - } - - for (int el = lutBIt; el < lutBEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutBiNets.emplace_back(netId); - } - if (lutBiNets.size() > 1) - { - std::sort(lutBiNets.begin(), lutBiNets.end()); - } - - if (lutAiNets.size() == 0 || lutBiNets.size() == 0) - { - return false; - } - - int idxA = 0, idxB = 0; - int netIdA = lutAiNets[idxA]; - int netIdB = lutBiNets[idxB]; - - while(numInputs > lut_maxShared) - { - if (netIdA < netIdB) - { - ++idxA; - if (idxA < lutAiNets.size()) - { - netIdA = lutAiNets[idxA]; - } else - { - break; - } - } else if (netIdA > netIdB) - { - ++idxB; - if (idxB < lutBiNets.size()) - { - netIdB = lutBiNets[idxB]; - } else - { - break; - } - - } else - { - --numInputs; - ++idxA; - ++idxB; - - if (idxA < lutAiNets.size() && idxB < lutBiNets.size()) - { - netIdA = lutAiNets[idxA]; - netIdB = lutBiNets[idxB]; - } else - { - break; - } - } - } - - return numInputs <= lut_maxShared; -} - -//Ensure flops in subSlice share the same set of ctrl signals -inline bool ffs_ctrl_match( - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* pin_typeIds, const int* pin2net_map, - const int ffInst, const int offInst) -{ - if (ffInst == INVALID || offInst == INVALID) - { - return true; - } - - std::vector ff_ctrl(10, INVALID); - std::vector off_ctrl(10, INVALID); - std::vector ff_pins, off_pins; - - int elIt = flat_node2pin_start_map[ffInst]; - int elEnd = flat_node2pin_start_map[ffInst+1]; - - //std::cout << "FF " << ffInst << " has " << elEnd - elIt << " pins" << std::endl; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - if (pinType < 3 || pinType > 9) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - ff_ctrl[pinType] = netId; - ff_pins.emplace_back(pinType); - } - - if (ff_pins.size() == 0) - { - return true; - } - - elIt = flat_node2pin_start_map[offInst]; - elEnd = flat_node2pin_start_map[offInst+1]; - //std::cout << "Other FF " << offInst << " has " << elEnd - elIt << " pins" << std::endl; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - if (pinType < 3 || pinType > 9) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - off_ctrl[pinType] = netId; - off_pins.emplace_back(pinType); - } - - if (off_pins.size() == 0) - { - return true; - } - - std::sort(ff_pins.begin(), ff_pins.end()); - std::sort(off_pins.begin(), off_pins.end()); - - int idxA = 0, idxB = 0; - int pinTypeA = ff_pins[idxA]; - int pinTypeB = off_pins[idxB]; - - //Only compare if pinType matches - while (idxA < ff_pins.size() && idxB < off_pins.size()) - { - if (pinTypeA < pinTypeB) - { - ++idxA; - - if (idxA < ff_pins.size()) - { - pinTypeA = ff_pins[idxA]; - } else - { - break; - } - } - else if (pinTypeA > pinTypeB) - { - ++idxB; - - if (idxB < off_pins.size()) - { - pinTypeB = off_pins[idxB]; - } else - { - break; - } - } else - { - if (ff_ctrl[pinTypeA] != off_ctrl[pinTypeB]) - { - return false; - } - - ++idxA; - ++idxB; - - if (idxA < ff_pins.size() && idxB < off_pins.size()) - { - pinTypeA = ff_pins[idxA]; - pinTypeB = off_pins[idxB]; - } else - { - break; - } - } - } - - return true; -} - -//Ensure unique inputs and loopbacks of subSlice are within limits -inline bool subSlice_compatibility( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* node2fence_region_map, const int* res_ff, const int* res_lut, - const int lutId, const int SLICE_CAPACITY, const int BLE_CAPACITY, - const int MAX_INPUTS_IN_SUBSLICE, const int MAX_LOOPBACK_IN_SUBSLICE, - const int k, const int ssInstId) -{ - ////DBG - //char printMsg = 0, foundLut(0), foundFF(0); - //int dbgLUTId = INVALID; - //int dbgLUT1Id = INVALID; - //int dbgFFId = INVALID; - //int dbgFF1Id = INVALID; - ////DBG - - int ssId = int(k/2)*2; - std::vector subSliceElements; - - if (res_lut[ssId] != INVALID) - { - subSliceElements.emplace_back(res_lut[ssId]); - ////DBG - //if (res_lut[ssId] == dbgLUTId || res_lut[ssId] == dbgLUT1Id) - //{ - // ++foundLut; - //} - ////DBG - } - - if (res_ff[ssId] != INVALID) - { - subSliceElements.emplace_back(res_ff[ssId]); - ////DBG - //if (res_ff[ssId] == dbgFFId || res_ff[ssId] == dbgFF1Id) - //{ - // ++foundFF; - //} - ////DBG - } - - if (res_lut[ssId+1] != INVALID) - { - subSliceElements.emplace_back(res_lut[ssId+1]); - ////DBG - //if (res_lut[ssId+1] == dbgLUTId || res_lut[ssId+1] == dbgLUT1Id) - //{ - // ++foundLut; - //} - ////DBG - } - - if (res_ff[ssId+1] != INVALID) - { - subSliceElements.emplace_back(res_ff[ssId+1]); - ////DBG - //if (res_ff[ssId+1] == dbgFFId || res_ff[ssId+1] == dbgFF1Id) - //{ - // ++foundFF; - //} - ////DBG - } - - //Check the new inst for compatibility - if (ssInstId != INVALID) - { - subSliceElements.emplace_back(ssInstId); - ////DBG - //if (ssInstId == dbgLUTId || ssInstId == dbgLUT1Id) - //{ - // ++foundLut; - //} - //if (ssInstId == dbgFFId || ssInstId == dbgFF1Id) - //{ - // ++foundFF; - //} - ////DBG - - int lut_type = (node2fence_region_map[ssInstId] == lutId); - int location_available = lut_type ? res_lut[k] == INVALID : res_ff[k] == INVALID; - - if (location_available == 0) - { - return false; - } - if (lut_type == 0 && (res_ff[ssId] != INVALID || res_ff[ssId + 1] != INVALID)) - { - int ffA = (res_ff[ssId] != INVALID) ? res_ff[ssId] : res_ff[ssId + 1]; - if (!ffs_ctrl_match(flat_node2pin_start_map, flat_node2pin_map, - pin_typeIds, pin2net_map, ffA, ssInstId)) - { - return false; - } - } - } - - if(subSliceElements.size() > 2*BLE_CAPACITY) - { - return false; - } - - ////DBG - //if (foundLut > 1 && foundFF > 1) - //{ - // printMsg = 1; - //} - ////DBG - - std::vector all_inNets; - //Check if LUT is driving FF in same subSlice - int lut_ff_conns = 0; - std::vector lut_outNets; - std::vector ff_inNets; - - for (int elId = 0; elId < subSliceElements.size(); ++elId) - { - int instId = subSliceElements[elId]; - - int lut_inst = (node2fence_region_map[instId] == lutId); - - int elIt = flat_node2pin_start_map[instId]; - int elEnd = flat_node2pin_start_map[instId+1]; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - - if (lut_inst == 1 && pinType == 0) - { - lut_outNets.emplace_back(pin2net_map[flat_node2pin_map[el]]); - } - if (pinType != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - - all_inNets.emplace_back(netId); - - if (lut_inst == 0) - { - ff_inNets.emplace_back(netId); - } - } - } - - if (lut_outNets.size() > 0 && ff_inNets.size() > 0) - { - std::sort(lut_outNets.begin(), lut_outNets.end()); - lut_outNets.erase(std::unique(lut_outNets.begin(), lut_outNets.end()), lut_outNets.end()); - - std::sort(ff_inNets.begin(), ff_inNets.end()); - ff_inNets.erase(std::unique(ff_inNets.begin(), ff_inNets.end()), ff_inNets.end()); - - int idxIn = 0, idxOut = 0; - int netIn = ff_inNets[idxIn]; - int netOut = lut_outNets[idxOut]; - - while (true) - { - if (netIn < netOut) - { - ++idxIn; - if (idxIn < ff_inNets.size()) - { - netIn = ff_inNets[idxIn]; - } else - { - break; - } - } else if (netIn > netOut) - { - ++idxOut; - if (idxOut < lut_outNets.size()) - { - netOut = lut_outNets[idxOut]; - } else - { - break; - } - } else - { - ++lut_ff_conns; - break; - } - } - } - - std::sort(all_inNets.begin(), all_inNets.end()); - all_inNets.erase(std::unique(all_inNets.begin(), all_inNets.end()), all_inNets.end()); - - if (all_inNets.size() > MAX_INPUTS_IN_SUBSLICE) - { - return false; - } - - //////DBG - ////if (printMsg == 1) - ////{ - //// printf("%d insts in subSlice: ", subSliceElements.size()); - //// for (int elId = 0; elId < subSliceElements.size(); ++elId) - //// { - //// printf("%d ",subSliceElements[elId]); - //// } - //// printf("\n"); - - //// printf("There are %d input nets in ALM: ", all_inNets.size()); - //// for (int elId = 0; elId < all_inNets.size(); ++elId) - //// { - //// printf("%d ",all_inNets[elId]); - //// } - //// printf("\n"); - ////} - //////DBG - - - ////Check loopback of all subSlices in the Slice - //Get input nets from all subSlices - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - std::vector subSlice_insts; - - if (sg == ssId) continue; - - //FFs - if (res_ff[sg] != INVALID) - { - subSlice_insts.emplace_back(res_ff[sg]); - } - if (res_ff[sg+1] != INVALID) - { - subSlice_insts.emplace_back(res_ff[sg+1]); - } - - //LUTs - if (res_lut[sg] != INVALID) - { - subSlice_insts.emplace_back(res_lut[sg]); - } - if (res_lut[sg+1] != INVALID) - { - subSlice_insts.emplace_back(res_lut[sg+1]); - } - - //Get input nets - for (int el = 0; el < subSlice_insts.size(); ++el) - { - int instId = subSlice_insts[el]; - int pStart = flat_node2pin_start_map[instId]; - int pEnd = flat_node2pin_start_map[instId+1]; - - for (int pId = pStart; pId < pEnd; ++pId) - { - int pinType = pin_typeIds[flat_node2pin_map[pId]]; - - if (pinType != 1) continue; - all_inNets.emplace_back(pin2net_map[flat_node2pin_map[pId]]); - } - } - } - - std::sort(all_inNets.begin(), all_inNets.end()); - all_inNets.erase(std::unique(all_inNets.begin(), all_inNets.end()), all_inNets.end()); - - if (all_inNets.size() == 0) - { - return true; - } - - //Get nets from all subSlices - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - std::vector subSlice_insts; - std::vector subSlice_outNets; - - if (sg == ssId) - { - subSlice_insts = subSliceElements; - } else - { - //FFs - if (res_ff[sg] != INVALID) - { - subSlice_insts.emplace_back(res_ff[sg]); - } - if (res_ff[sg+1] != INVALID) - { - subSlice_insts.emplace_back(res_ff[sg+1]); - } - - //LUTs - if (res_lut[sg] != INVALID) - { - subSlice_insts.emplace_back(res_lut[sg]); - } - if (res_lut[sg+1] != INVALID) - { - subSlice_insts.emplace_back(res_lut[sg+1]); - } - } - - for (int idx = 0; idx < subSlice_insts.size(); ++idx) - { - int instId = subSlice_insts[idx]; - - int ndOutId = 4*instId; - int ndOutPins = ndOutId + 4; - for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - subSlice_outNets.emplace_back(outNetId); - } - } - if (subSlice_outNets.size() <= MAX_LOOPBACK_IN_SUBSLICE) continue; - std::sort(subSlice_outNets.begin(), subSlice_outNets.end()); - subSlice_outNets.erase(std::unique(subSlice_outNets.begin(), subSlice_outNets.end()), subSlice_outNets.end()); - - int num_loopbacks = 0; - //Compare sorted subSlice outNets with Slice input nets - - int idxIn = 0, idxOut = 0; - int netIn = all_inNets[idxIn]; - int netOut = subSlice_outNets[idxOut]; - - while (num_loopbacks <= MAX_LOOPBACK_IN_SUBSLICE) - { - if (netIn < netOut) - { - ++idxIn; - if (idxIn < all_inNets.size()) - { - netIn = all_inNets[idxIn]; - } else - { - break; - } - } else if (netIn > netOut) - { - ++idxOut; - if (idxOut < subSlice_outNets.size()) - { - netOut = subSlice_outNets[idxOut]; - } else - { - break; - } - } else - { - ++num_loopbacks; - ++idxIn; - ++idxOut; - if (idxIn < all_inNets.size() && idxOut < subSlice_outNets.size()) - { - netIn = all_inNets[idxIn]; - netOut = subSlice_outNets[idxOut]; - } else - { - break; - } - } - } - if (sg == ssId && lut_ff_conns > 0) - { - --num_loopbacks; - } - - if (num_loopbacks > MAX_LOOPBACK_IN_SUBSLICE) - { - return false; - } - } - - return true; -} - -//Given lut arrangement, fit FFs -inline bool fit_ffs( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* node2fence_region_map, const int* res_lut, const int lutId, - const int lut_maxShared, const int SLICE_CAPACITY, const int BLE_CAPACITY, - int* res_ff) -{ - ////DBG - //char printMsg = 0; - //int dbgInstId = INVALID; - ////DBG - - //Rearrange all FFs based on compatibility - std::vector rem_ffs; - int temp_ffs[SLICE_MAX_CAP]; - - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - temp_ffs[sg] = res_ff[sg]; - temp_ffs[sg + 1] = res_ff[sg + 1]; - - if (res_ff[sg] != INVALID || res_ff[sg + 1] != INVALID) - { - if (!subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, - res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, sg, INVALID)) - { - if (res_ff[sg] != INVALID) - { - rem_ffs.emplace_back(res_ff[sg]); - res_ff[sg] = INVALID; - } - if (res_ff[sg+1] != INVALID) - { - rem_ffs.emplace_back(res_ff[sg+1]); - res_ff[sg+1] = INVALID; - } - } - } - } - - if (rem_ffs.size() == 0) return true; - - //Greedily assign FF to empty compatible location - char ffLocFound = 0; - for (int el = 0; el < rem_ffs.size(); ++el) - { - int ffId = rem_ffs[el]; - for (int sg = 0; sg < SLICE_CAPACITY; sg += BLE_CAPACITY) - { - if (res_ff[sg] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, sg, ffId)) - { - res_ff[sg] = ffId; - ++ffLocFound; - break; - } - } else if (res_ff[sg + 1] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, sg + 1, ffId)) - { - res_ff[sg + 1] = ffId; - ++ffLocFound; - break; - } - } - } - } - - if (ffLocFound == rem_ffs.size()) - { - ////DBG - //if (printMsg == 1) - //{ - // std::cout << "FFs after fit ffs: "; - // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // std::cout << res_ff[sg] << " "; - // } - // std::cout << std::endl; - //} - ////DBG - - return true; - } - - ////DBG - //if (printMsg == 1) - //{ - // std::cout << "FFs after fit ffs (failed): "; - // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // std::cout << res_ff[sg] << " "; - // } - // std::cout << std::endl; - //} - ////DBG - - //Revert - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_ff[sg] = temp_ffs[sg]; - } - - return false; -} - -//define check_sig_in_site_next_pq_sig -inline bool check_sig_in_site_next_pq_sig( - const int* nwCand_sig, const int nwCand_sigIdx, const int sPQ, - const int PQ_IDX, const int* site_next_pq_validIdx, - const int* site_next_pq_sig, const int* site_next_pq_sig_idx, - const int SIG_IDX) -{ - std::vector candEls, nextEls; - for (int x = 0; x < nwCand_sigIdx; ++x) - { - candEls.emplace_back(nwCand_sig[x]); - } - std::sort(candEls.begin(), candEls.end()); - - for (int i = 0; i < PQ_IDX; ++i) - { - int sigIdx = sPQ + i; - if (site_next_pq_validIdx[sigIdx] != INVALID && - site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) - { - int pqIdx(sigIdx*SIG_IDX); - - nextEls.clear(); - - for (int x = 0; x < site_next_pq_sig_idx[sigIdx]; ++x) - { - nextEls.emplace_back(site_next_pq_sig[pqIdx + x]); - } - - std::sort(nextEls.begin(), nextEls.end()); - - if (candEls == nextEls) - { - return true; - } - } - } - return false; -} - -//define add_inst_to_sig -inline bool add_inst_to_sig( - const int node2prclstrCount, const int* flat_node2precluster_map, - const int instPcl, int* nwCand_sig, - int& nwCand_sigIdx, const int SLICE_CAPACITY) -{ - std::vector temp; - - for (int el = 0; el < node2prclstrCount; ++el) - { - int newInstId = flat_node2precluster_map[instPcl+el]; - //Ensure instance is not in sig - if (!inst_in_sig(newInstId, nwCand_sigIdx, nwCand_sig, 0)) - { - temp.emplace_back(newInstId); - } else - { - return false; - } - } - - if (nwCand_sigIdx + temp.size() > 2*SLICE_CAPACITY) - { - return false; - } - - for (int mBIdx = 0; mBIdx < temp.size(); ++mBIdx) - { - nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; - ++nwCand_sigIdx; - } - return true; -} - -//define add flop to candidate -inline bool add_flop_to_candidate_impl( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, - const int* flop2ctrlSetId_map, const int* node2fence_region_map, - const int* res_lut, const int lutId, const int ffCKSR, - const int ffCE, const int ffId, const int half_ctrl_mode, - const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, - const int CKSR_IN_CLB, const int CE_IN_CLB, const int BLE_CAPACITY, - const int lut_maxShared, int* res_ff, int* res_cksr, int* res_ce) -{ - if (half_ctrl_mode == 1) - { - for (int i = 0; i < CKSR_IN_CLB; ++i) - { - if (res_cksr[i] != INVALID && - res_cksr[i] != ffCKSR) - { - continue; - } - - for (int j = 0; j < CKSR_IN_CLB; ++j) - { - int ceIdx = CKSR_IN_CLB*i + j; - if (res_ce[ceIdx] != INVALID && - res_ce[ceIdx] != ffCE) - { - continue; - } - - int beg = i*HALF_SLICE_CAPACITY+j; - int end = beg + HALF_SLICE_CAPACITY; - for (int k = beg; k < end; k += BLE_CAPACITY) - { - if (res_ff[k] == INVALID) - { - res_ff[k] = ffId; - res_cksr[i] = ffCKSR; - res_ce[ceIdx] = ffCE; - - return true; - } - } - } - } - } else - { - //FF Ctrls are SHARED across the SLICE - int ckID = INVALID; - for (int i = 0; i < CKSR_IN_CLB; ++i) - { - if (res_cksr[i] == ffCKSR || res_cksr[i] == INVALID) - { - ckID = i; - break; - } - } - - if (ckID != INVALID) - { - int fCtrlId = flop2ctrlSetId_map[ffId]; - int fCStartId = ext_ctrlSet_start_map[fCtrlId]; - int fCEndId = ext_ctrlSet_start_map[fCtrlId+1]; - - ////DBG - //int numffCtrls = fCEndId - fCStartId; - //std::cout << "add flop to candidate: " << ffId << " with CKSR: " - // << ffCKSR << " and " << numffCtrls << " Ctrl signals to candidate" << std::endl; - //std::cout << "FF Ctrl signals: "; - //for (int j = fCStartId; j < fCEndId; ++j) - //{ - // std::cout << extended_ctrlSets[j] << " "; - //} - //std::cout << std::endl; - //std::cout << "Existing Candidate ffs: "; - //for (int t = 0; t < SLICE_CAPACITY; ++t) - //{ - // std::cout << res_ff[t] << " "; - //} - //std::cout << std::endl; - //std::cout << " FF CKSR: "; - //for (int t = 0; t < CKSR_IN_CLB; ++t) - //{ - // std::cout << res_cksr[t] << " "; - //} - //std::cout << std::endl; - //std::cout << " FF CE: "; - //for (int t = 0; t < CE_IN_CLB; ++t) - //{ - // std::cout << res_ce[t] << " "; - //} - //std::cout << std::endl; - ////DBG - - std::vector upd_ctrls; - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - if (res_ce[sg] != INVALID) - { - upd_ctrls.emplace_back(res_ce[sg]); - } - } - - int initial_ctrls = upd_ctrls.size(); - - //For multiple ff ctrl signals - Ensure all signals can be accomodated - for (int j = fCStartId; j < fCEndId; ++j) - { - int ffCtrl = extended_ctrlSets[j]; - upd_ctrls.emplace_back(ffCtrl); - } - - if (upd_ctrls.size() > initial_ctrls) - { - std::sort(upd_ctrls.begin(), upd_ctrls.end()); - upd_ctrls.erase(std::unique(upd_ctrls.begin(), upd_ctrls.end()), upd_ctrls.end()); - } - - if (upd_ctrls.size() > CE_IN_CLB) - { - return false; - } - - int fIndex = INVALID; - for (int i = 0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_ff[i] == INVALID) - { - if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i, ffId)) - { - fIndex = i; - } - } else if (res_ff[i+1] == INVALID) - { - - if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i+1, ffId)) - { - fIndex = i+1; - } - } - - if (fIndex == INVALID) - { - continue; - } - - res_ff[fIndex] = ffId; - res_cksr[ckID] = ffCKSR; - - for (int j = 0; j < upd_ctrls.size(); ++j) - { - res_ce[j] = upd_ctrls[j]; - } - for (int j = upd_ctrls.size(); j < CE_IN_CLB; ++j) - { - res_ce[j] = INVALID; - } - - ////DBG - //std::cout << "SUCCEEDED to add flop: " << ffId << " with CKSR: " - // << ffCKSR << " and CE: " << ffCE << " to candidate" << std::endl; - //std::cout << "UPDATED Candidate ffs: "; - //for (int t = 0; t < SLICE_CAPACITY; ++t) - //{ - // std::cout << res_ff[t] << " "; - //} - //std::cout << std::endl; - //std::cout << " FF CKSR: "; - //for (int t = 0; t < CKSR_IN_CLB; ++t) - //{ - // std::cout << res_cksr[t] << " "; - //} - //std::cout << std::endl; - //std::cout << " FF CE: "; - //for (int t = 0; t < CE_IN_CLB; ++t) - //{ - // std::cout << res_ce[t] << " "; - //} - //std::cout << std::endl; - ////DBG - - return true; - } - } - } - ////DBG - //std::cout << "FAILED to add flop: " << ffId << " with CKSR: " - // << ffCKSR << " and CE: " << ffCE << "to candidate" << std::endl; - ////DBG - return false; -} - -// define remove_invalid_neighbor -inline void remove_invalid_neighbor( - const int sIdx, const int sNbrIdx, int* site_nbr_idx, int* site_nbr) -{ - std::vector temp; - for (int i = 0; i < site_nbr_idx[sIdx]; ++i) - { - if (site_nbr[sNbrIdx+i] != INVALID) - { - temp.emplace_back(site_nbr[sNbrIdx+i]); - } - } - - for(unsigned int j = 0; j < temp.size(); ++j) - { - site_nbr[sNbrIdx+j] = temp[j]; - } - for(int j = (int)temp.size(); j < site_nbr_idx[sIdx]; ++j) - { - site_nbr[sNbrIdx+j] = INVALID; - } - site_nbr_idx[sIdx] = temp.size(); -} - -// define compute_wirelength_improv -template -void compute_wirelength_improv( - const T* pos_x, const T* pos_y, const T* net_bbox, const T* pin_offset_x, - const T* pin_offset_y, const T* net_weights, const int* net_pinIdArrayX, - const int* net_pinIdArrayY, const int* flat_net2pin_start_map, - const int* pin2node_map, const int* net2pincount, const T* site_xy, - const T xWirelenWt, const T yWirelenWt, const int currNetId, - const int cand_siteId, const std::vector &pins, T &result) -{ - int cNbId(currNetId*4); - T netXlen = net_bbox[cNbId+2] - net_bbox[cNbId]; - T netYlen = net_bbox[cNbId+3] - net_bbox[cNbId+1]; - if ((int)pins.size() == net2pincount[currNetId]) - { - T bXLo(pin_offset_x[pins[0]]); - T bXHi(pin_offset_x[pins[0]]); - T bYLo(pin_offset_y[pins[0]]); - T bYHi(pin_offset_y[pins[0]]); - - for(auto poI = 1; poI < pins.size(); ++poI) - { - T poX = pin_offset_x[pins[poI]]; - T poY = pin_offset_y[pins[poI]]; - if (poX < bXLo) - { - bXLo = poX; - } else if (poX > bXHi) - { - bXHi = poX; - } - if (poY < bYLo) - { - bYLo = poY; - } else if (poY > bYHi) - { - bYHi = poY; - } - } - result += net_weights[currNetId] * (xWirelenWt * (netXlen - (bXHi-bXLo)) + yWirelenWt * (netYlen - (bYHi - bYLo))); - return; - } - T bXLo(net_bbox[cNbId]); - T bYLo(net_bbox[cNbId+1]); - T bXHi(net_bbox[cNbId+2]); - T bYHi(net_bbox[cNbId+3]); - int cStId = cand_siteId*2; - T locX = site_xy[cStId]; - T locY = site_xy[cStId+1]; - - if (locX <= bXLo) - { - bXLo = locX; - } else - { - int n2pId = flat_net2pin_start_map[currNetId]; - while(n2pId < flat_net2pin_start_map[currNetId+1] && - std::find(pins.begin(), pins.end(), net_pinIdArrayX[n2pId]) != pins.end()) - { - ++n2pId; - } - int reqPId = net_pinIdArrayX[n2pId]; - T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; - bXLo = DREAMPLACE_STD_NAMESPACE::min(pinX, locX); - } - - if (locX >= bXHi) - { - bXHi = locX; - } else - { - int n2pId = flat_net2pin_start_map[currNetId+1]-1; - while(n2pId >= flat_net2pin_start_map[currNetId] && - std::find(pins.begin(), pins.end(), net_pinIdArrayX[n2pId]) != pins.end()) - { - --n2pId; - } - int reqPId = net_pinIdArrayX[n2pId]; - T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; - bXHi = DREAMPLACE_STD_NAMESPACE::max(pinX, locX); - } - - if (locY <= bYLo) - { - bYLo = locY; - } else - { - int n2pId = flat_net2pin_start_map[currNetId]; - while(n2pId < flat_net2pin_start_map[currNetId+1] && - std::find(pins.begin(), pins.end(), net_pinIdArrayY[n2pId]) != pins.end()) - { - ++n2pId; - } - int reqPId = net_pinIdArrayY[n2pId]; - T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; - bYLo = DREAMPLACE_STD_NAMESPACE::min(pinY, locY); - } - - if (locY >= bYHi) - { - bYHi = locY; - } else - { - int n2pId = flat_net2pin_start_map[currNetId+1]-1; - while(n2pId >= flat_net2pin_start_map[currNetId] && - std::find(pins.begin(), pins.end(), net_pinIdArrayY[n2pId]) != pins.end()) - { - --n2pId; - } - int reqPId = net_pinIdArrayY[n2pId]; - T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; - bYHi = DREAMPLACE_STD_NAMESPACE::max(pinY, locY); - } - result += net_weights[currNetId] * (xWirelenWt * (netXlen - (bXHi-bXLo)) + yWirelenWt * (netYlen - (bYHi - bYLo))); - return; -} - -//addLUTToCandidateImpl -inline bool add_lut_to_cand_impl( - const int* node2outpinIdx_map, const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* node2fence_region_map, const int* res_ff, const int lutId, - const int lutTypeInSliceUnit, const int lut_maxShared, - const int lutInstId, const int SLICE_CAPACITY, const int BLE_CAPACITY, - const int half_ctrl_mode, int* res_lut) -{ - if (half_ctrl_mode == 1) - { - for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - res_lut[i] = lutInstId; - return true; - } - } - for (int i=1; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, lutTypeInSliceUnit, - lut_maxShared, res_lut[i-1], lutInstId)) - { - res_lut[i] = lutInstId; - return true; - } - } - } - } else - { - for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, - res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, i, lutInstId)) - { - res_lut[i] = lutInstId; - return true; - } - } else if (res_lut[i+1] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, - res_ff, res_lut, lutId, SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, - BLE_CAPACITY, i+1, lutInstId)) - { - res_lut[i+1] = lutInstId; - return true; - } - } - } - } - - return false; -} - -//computeCandidateScore -template -inline void compute_candidate_score( - const T* pos_x, const T* pos_y, const T* pin_offset_x, const T* pin_offset_y, - const T* net_bbox, const T* net_weights, const T* site_xy, - const int* net_pinIdArrayX, const int* net_pinIdArrayY, - const int* flat_net2pin_start_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* sorted_net_map, const int* pin2net_map, - const int* pin2node_map, const int* net2pincount, const int* lut_type, - const T xWirelenWt, const T yWirelenWt, const T extNetCountWt, - const T wirelenImprovWt, const int netShareScoreMaxNetDegree, - const int wlScoreMaxNetDegree, const int half_ctrl_mode, - const int* res_sig, const int res_siteId, const int res_sigIdx, T &result) -{ - T netShareScore(0.0), wirelenImprov(0.0); - T typeScore(0.0); - std::vector pins; - - for (int i = 0; i < res_sigIdx; ++i) - { - int instId = res_sig[i]; - //For macro nodes, same instId is repeated in sig - if (i != 0 && instId == res_sig[i-1]) continue; - for (int pId = flat_node2pin_start_map[instId]; pId < flat_node2pin_start_map[instId+1]; ++pId) - { - pins.emplace_back(flat_node2pin_map[pId]); - } - if (half_ctrl_mode == 0) - { - typeScore += lut_type[instId]; - } - } - std::sort(pins.begin(), pins.end(), [&pin2net_map,&sorted_net_map](const auto &a, const auto &b){ return pin2net_map[a] == pin2net_map[b] ? a < b : sorted_net_map[pin2net_map[a]] < sorted_net_map[pin2net_map[b]]; }); - - if (pins.empty()) - { - result = T(0.0); - return; - } - - int maxNetDegree = DREAMPLACE_STD_NAMESPACE::max(netShareScoreMaxNetDegree, wlScoreMaxNetDegree); - int currNetId = pin2net_map[pins[0]]; - - if (net2pincount[currNetId] > maxNetDegree) - { - result = T(0.0); - return; - } - - int numIntNets(0), numNets(0); - std::vector currNetIntPins; - - currNetIntPins.emplace_back(pins[0]); - - for(unsigned int pIdx = 1; pIdx < pins.size(); ++pIdx) - { - int netId = pin2net_map[pins[pIdx]]; - if (netId == currNetId) - { - currNetIntPins.emplace_back(pins[pIdx]); - } else - { - if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) - { - ++numNets; - numIntNets += (currNetIntPins.size() == net2pincount[currNetId] ? 1 : 0); - netShareScore += net_weights[currNetId] * (currNetIntPins.size() - 1.0) / DREAMPLACE_STD_NAMESPACE::max(1.0, net2pincount[currNetId] - 1.0); - } - if (net2pincount[currNetId] <= wlScoreMaxNetDegree) - { - compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, currNetId, res_siteId, currNetIntPins, wirelenImprov); - } - currNetId = netId; - if (net2pincount[currNetId] > maxNetDegree) - { - break; - } - currNetIntPins.clear(); - currNetIntPins.emplace_back(pins[pIdx]); - } - } - - if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) - { - ++numNets; - numIntNets += (currNetIntPins.size() == net2pincount[currNetId] ? 1 : 0); - netShareScore += net_weights[currNetId] * (currNetIntPins.size() - 1.0) / DREAMPLACE_STD_NAMESPACE::max(1.0, net2pincount[currNetId] - 1.0); - } - ////DBG - //if (res_siteId == dbg_siteId && res_sigIdx == dbg_sigIdx) - //{ - // std::cout << "Candidate score computation for res_siteId: " << res_siteId - // << " with " << numNets << " total nets and " << numIntNets - // << " internal nets and net share score: " << netShareScore << std::endl; - //} - ////DBG - - if (net2pincount[currNetId] <= wlScoreMaxNetDegree) - { - compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, - net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, - pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, currNetId, - res_siteId, currNetIntPins, wirelenImprov); - } - - netShareScore /= (T(1.0) + extNetCountWt * (numNets - numIntNets)); - result = netShareScore + wirelenImprovWt * wirelenImprov; - - if (half_ctrl_mode == 0) - { - result += T(0.1)*typeScore; - } - - ////DBG - //if (res_siteId == dbg_siteId && res_sigIdx == dbg_sigIdx) - //{ - // std::cout << "Candidate score computation for res_siteId: " << res_siteId - // << " with updated netShareScore: " << netShareScore << " and final score: " - // << result << " based on wirelength improvement of " << wirelenImprov - // << std::endl; - //} - ////DBG -} - -//fitLUTsToCandidateImpl -inline bool fit_luts_to_candidate_impl( - const int* node2outpinIdx_map, const int* lut_type, const int* node2pincount, - const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* flat_node2precluster_map, const int* node2fence_region_map, - const int* special_nodes, const int half_ctrl_mode, const int lutTypeInSliceUnit, - const int lut_maxShared, const int instPcl, const int node2prclstrCount, - const int NUM_BLE_PER_SLICE, const int SLICE_CAPACITY, const int BLE_CAPACITY, - const int lutId, int* res_ff, int* res_lut) -{ - std::vector luts, lut6s, splNodes; - - ////DBG - //std::cout << "fit luts to candidate impl: Try to add " << node2prclstrCount << " luts using graph matching to candidate: "; - //for (int idx = 0; idx < node2prclstrCount; ++idx) - //{ - // int clInstId = flat_node2precluster_map[instPcl + idx]; - // if (node2fence_region_map[clInstId] == lutId) - // { - // std::cout << clInstId << "(" << lut_type[clInstId] << ") "; - // } - //} - //std::cout << std::endl; - - //std::cout << " Candidate luts: "; - //for (int t = 0; t < SLICE_CAPACITY; ++t) - //{ - // std::cout << res_lut[t] << "(" << lut_type[res_lut[t]] << ") "; - //} - //std::cout << std::endl; - ////DBG - //Ensure subSlice-level shared input count is met - int temp_lut[SLICE_MAX_CAP]; - - for (int i = 0; i < SLICE_CAPACITY; ++i) - { - if (res_lut[i] != INVALID) - { - if (special_nodes[res_lut[i]] == 1) - { - splNodes.emplace_back(res_lut[i]); - } else - { - if (lut_type[res_lut[i]] < lutTypeInSliceUnit) - { - luts.emplace_back(res_lut[i]); - } else - { - lut6s.emplace_back(res_lut[i]); - } - } - } - if (half_ctrl_mode == 0) - { - temp_lut[i] = res_lut[i]; - } - } - - for (int idx = 0; idx < node2prclstrCount; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - if (node2fence_region_map[clInstId] == lutId) - { - if (lut_type[clInstId] < lutTypeInSliceUnit) - { - luts.emplace_back(clInstId); - std::sort(luts.begin(), luts.end()); - luts.erase(std::unique(luts.begin(), luts.end()), luts.end()); - } else - { - lut6s.emplace_back(clInstId); - std::sort(lut6s.begin(), lut6s.end()); - lut6s.erase(std::unique(lut6s.begin(), lut6s.end()), lut6s.end()); - } - } - } - - int splNodeSpace = splNodes.size(); - splNodeSpace += (splNodes.size() & 1) ? 1 : 0; - - if (luts.size() + 2*lut6s.size() + splNodeSpace > SLICE_CAPACITY) - { - return false; - } - - lemon::ListGraph graph; - std::vector nodes; - std::vector edges; - std::vector > edgePairs; - graph.clear(); - nodes.clear(); - edges.clear(); - edgePairs.clear(); - - int n = luts.size(); - // - - for (int il = 0; il < n; ++il) - { - nodes.emplace_back(graph.addNode()); - } - for(int ll = 0; ll < n; ++ll) - { - for(int rl = ll+1; rl < n; ++rl) - { - if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, lutTypeInSliceUnit, lut_maxShared, - luts[ll], luts[rl])) - { - edges.emplace_back(graph.addEdge(nodes[ll], nodes[rl])); - edgePairs.emplace_back(ll, rl); - } - } - } - - lemon::MaxMatching mm(graph); - mm.run(); - - int otherNodePairs = lut6s.size() + splNodeSpace/2; - if (n - (int)mm.matchingSize() + otherNodePairs > NUM_BLE_PER_SLICE) - { - return false; - } - - int idxL(0); - //Include special nodes first - if (splNodes.size() > 0) - { - for (int spId = 0; spId < splNodes.size(); ++spId) - { - res_lut[idxL] = splNodes[spId]; - ++idxL; - } - if (splNodeSpace > splNodes.size()) - { - res_lut[idxL] = INVALID; - ++idxL; - } - } - for (int iil = 0; iil < n; ++iil) - { - if (mm.mate(nodes[iil]) == lemon::INVALID && luts[iil] != INVALID) - { - res_lut[idxL] = luts[iil]; - res_lut[idxL + 1] = INVALID; - idxL += BLE_CAPACITY; - } - } - for(unsigned int iil = 0; iil < lut6s.size(); ++iil) - { - res_lut[idxL] = lut6s[iil]; - res_lut[idxL + 1] = INVALID; - idxL += BLE_CAPACITY; - } - for (unsigned int iil = 0; iil < edges.size(); ++iil) - { - if (mm.matching(edges[iil])) - { - const auto &p = edgePairs[iil]; - res_lut[idxL] = luts[p.first]; - res_lut[idxL + 1] = luts[p.second]; - idxL += BLE_CAPACITY; - } - } - - for (int lIdx = idxL; lIdx < SLICE_CAPACITY; ++lIdx) - { - res_lut[lIdx] = INVALID; - } - - ////DBG - //std::cout << "SUCCEEDED fit luts to candidate impl: Try to add " << node2prclstrCount << " luts using graph matching to candidate: "; - //for (int idx = 0; idx < node2prclstrCount; ++idx) - //{ - // int clInstId = flat_node2precluster_map[instPcl + idx]; - // if (node2fence_region_map[clInstId] == lutId) - // { - // std::cout << clInstId << "(" << lut_type[clInstId] << ") "; - // } - //} - //std::cout << std::endl; - - //std::cout << " Candidate luts: "; - //for (int t = 0; t < SLICE_CAPACITY; ++t) - //{ - // std::cout << res_lut[t] << "(" << lut_type[res_lut[t]] << ") "; - //} - //std::cout << std::endl; - ////DBG - - - //Ensure subSlice-level shared input count is met - if (half_ctrl_mode == 0) - { - //Try to rearrange ffs if there are compatibility issues - if (fit_ffs(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_lut, lutId, - lut_maxShared, SLICE_CAPACITY, BLE_CAPACITY, res_ff)) - { - return true; - } - - //Revert - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = temp_lut[sg]; - } - return false; - } - - return true; -} - -//template -inline bool is_inst_in_cand_feasible( - const int* node2outpinIdx_map, const int* node2fence_region_map, const int* lut_type, - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* node2pincount, const int* net2pincount, const int* pin2net_map, - const int* pin_typeIds, const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, const int* site_det_impl_lut, - const int* site_det_impl_ff, const int* site_det_impl_cksr, - const int* site_det_impl_ce, const int* special_nodes, - const int lutTypeInSliceUnit, const int lut_maxShared, - const int siteId, const int instId, const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, const int CKSR_IN_CLB, const int CE_IN_CLB, - const int lutId, const int ffId, const int half_ctrl_mode) -{ - int instPcl = instId*3; - - int sdlutId = siteId*SLICE_CAPACITY; - int sdckId = siteId*CKSR_IN_CLB; - int sdceId = siteId*CE_IN_CLB; - - ///// - int res_lut[SLICE_MAX_CAP]; - int res_ff[SLICE_MAX_CAP]; - int res_cksr[CKSR_MAX_CAP]; - int res_ce[CE_MAX_CAP]; - - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = site_det_impl_lut[sdlutId + sg]; - res_ff[sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - res_cksr[sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - res_ce[sg] = site_det_impl_ce[sdceId + sg]; - } - ///// - - bool lutFail(false); - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; - int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; - - if (node2fence_region_map[clInstId] == lutId) //LUT - { - if (!lutFail && !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, lutId, - lutTypeInSliceUnit, lut_maxShared, clInstId, SLICE_CAPACITY, BLE_CAPACITY, - half_ctrl_mode, res_lut)) - - { - lutFail = true; - } - } else if (node2fence_region_map[clInstId] == ffId) //FF - { - if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, ext_ctrlSet_start_map, - flop2ctrlSetId_map, node2fence_region_map, res_lut, lutId, flop_ctrlSets[clInstCKSR], - flop_ctrlSets[clInstCE], clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, res_cksr, res_ce)) - { - return false; - } - } - } - if (!lutFail) - { - return true; - } - - return fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, node2pincount, net2pincount, - pin2net_map, pin_typeIds, flat_node2pin_start_map, flat_node2pin_map, - flat_node2precluster_map, node2fence_region_map, special_nodes, - half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, instPcl, - flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, SLICE_CAPACITY, - BLE_CAPACITY, lutId, res_ff, res_lut); -} - -inline bool add_inst_to_cand_impl( - const int* node2outpinIdx_map, const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* node2pincount, const int* net2pincount, - const int* pin2net_map, const int* pin_typeIds, const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, const int* flop2ctrlSetId_map, - const int* node2fence_region_map, const int* flop_ctrlSets, const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, const int* special_nodes, const int lutTypeInSliceUnit, - const int lut_maxShared, const int instId, const int lutId, const int ffId, - const int half_ctrl_mode, const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, - int* nwCand_lut, int* nwCand_ff, int* nwCand_cksr, int* nwCand_ce) -{ - int instPcl = instId*3; - - int res_lut[SLICE_MAX_CAP]; - int res_ff[SLICE_MAX_CAP]; - int res_ce[CE_MAX_CAP]; - int res_cksr[CKSR_MAX_CAP]; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = nwCand_lut[sg]; - res_ff[sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - res_cksr[sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - res_ce[sg] = nwCand_ce[sg]; - } - - bool lutFail(false); - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; - int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; - - if (node2fence_region_map[clInstId] == lutId) //LUT - { - if (!lutFail && !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, lutId, - lutTypeInSliceUnit, lut_maxShared, clInstId, SLICE_CAPACITY, BLE_CAPACITY, half_ctrl_mode, - res_lut)) - { - lutFail = true; - } - } else if (node2fence_region_map[clInstId] == ffId) //FF - { - if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, extended_ctrlSets, ext_ctrlSet_start_map, flop2ctrlSetId_map, - node2fence_region_map, res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], - clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - BLE_CAPACITY, lut_maxShared, res_ff, res_cksr, res_ce)) - { - return false; - } - } - } - if (!lutFail) - { - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = res_lut[sg]; - nwCand_ff[sg] = res_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = res_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = res_ce[sg]; - } - return true; - } - if (fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, node2pincount, - net2pincount, pin2net_map, pin_typeIds, flat_node2pin_start_map, - flat_node2pin_map, flat_node2precluster_map, node2fence_region_map, - special_nodes, half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, - instPcl, flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, - SLICE_CAPACITY, BLE_CAPACITY, lutId, res_ff, res_lut)) - { - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = res_lut[sg]; - nwCand_ff[sg] = res_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = res_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = res_ce[sg]; - } - return true; - } - return false; -} - -//template -inline void remove_incompatible_neighbors( - const int* node2outpinIdx_map, const int* node2fence_region_map, const int* lut_type, - const int* flat_node2pin_start_map, const int* flat_node2pin_map, const int* node2pincount, - const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, - const int* flat_node2prclstrCount, const int* flat_node2precluster_map, - const int* flop2ctrlSetId_map, const int* flop_ctrlSets, const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, const int* site_det_impl_lut, const int* site_det_impl_ff, - const int* site_det_impl_cksr, const int* site_det_impl_ce, const int* site_det_sig, - const int* site_det_sig_idx, const int* special_nodes, const int lutTypeInSliceUnit, - const int lut_maxShared, const int siteId, const int sNbrIdx, const int half_ctrl_mode, - const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, - const int lutId, const int ffId, int* site_nbr_idx, int* site_nbr) -{ - int sdtopId = siteId*SIG_IDX; - for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) - { - int instId = site_nbr[sNbrIdx + nbrId]; - - if (inst_in_sig(instId, site_det_sig_idx[siteId], site_det_sig, sdtopId) || - !is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, - pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, - flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, - site_det_impl_ce, special_nodes, lutTypeInSliceUnit, lut_maxShared, - siteId, instId, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, half_ctrl_mode)) - { - site_nbr[sNbrIdx + nbrId] = INVALID; - } - } - //Remove invalid neighbor instances - remove_invalid_neighbor(siteId, sNbrIdx, site_nbr_idx, site_nbr); -} - -template -inline void computeBLEScore( - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* flat_net2pin_start_map, const int* flat_net2pin_map, - const int* pin2net_map, const int* pin2node_map, - const int* node2outpinIdx_map, const int* pin_typeIds, - const int* lut_type, const int half_ctrl_mode, const int lutA, - const int lutB, const int ffA, const int ffB, T& score) -{ - int numShareInputs = 0; - - if (lutA != INVALID && lutB != INVALID && lut_type[lutA] != 0 && lut_type[lutB] != 0) - { - //Note: It is important that nets of lutA and lutB are sorted before comparison - std::vector lutAiNets, lutBiNets; - - int lutAIt = flat_node2pin_start_map[lutA]; - int lutBIt = flat_node2pin_start_map[lutB]; - int lutAEnd = flat_node2pin_start_map[lutA+1]; - int lutBEnd = flat_node2pin_start_map[lutB+1]; - - for (int el = lutAIt; el < lutAEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutAiNets.emplace_back(netId); - } - std::sort(lutAiNets.begin(), lutAiNets.end()); - - for (int el = lutBIt; el < lutBEnd; ++el) - { - //Skip if not an input pin - if (pin_typeIds[flat_node2pin_map[el]] != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - lutBiNets.emplace_back(netId); - } - std::sort(lutBiNets.begin(), lutBiNets.end()); - - int idxA = 0, idxB = 0; - int netIdA = lutAiNets[idxA]; - int netIdB = lutBiNets[idxB]; - - while(true) - { - if (netIdA < netIdB) - { - ++idxA; - if (idxA < lutAiNets.size()) - { - netIdA = lutAiNets[idxA]; - } else - { - break; - } - } else if (netIdA > netIdB) - { - ++idxB; - if (idxB < lutBiNets.size()) - { - netIdB = lutBiNets[idxB]; - } else - { - break; - } - - } else - { - ++numShareInputs; - ++idxA; - ++idxB; - - if (idxA < lutAiNets.size() && idxB < lutBiNets.size()) - { - netIdA = lutAiNets[idxA]; - netIdB = lutBiNets[idxB]; - } else - { - break; - } - } - } - } - - - int numIntNets = 0; - for (int id : {lutA, lutB}) - { - if (id == INVALID) continue; - - int ndOutId = 4*id; - int ndOutPins = ndOutId + 4; - for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - for (int pId = flat_net2pin_start_map[outNetId]; - pId < flat_net2pin_start_map[outNetId+1]; ++pId) - { - int pinId = flat_net2pin_map[pId]; - int nodeId = pin2node_map[pinId]; - if (pin_typeIds[pinId] == 1 && (nodeId == ffA || nodeId == ffB)) - { - ++numIntNets; - } - } - } - } - score = T(0.1) * numShareInputs + numIntNets; - T numFF = (ffA == INVALID ? 0:1) + (ffB == INVALID ? 0 : 1); - - if (half_ctrl_mode == 1) - { - score -= T(0.01)*numFF; - } else - { - //Prefer more ffs being packed - score += T(0.01)*numFF; - } -} - -template -inline void findBestFFs( - const int* flop_ctrlSets, const int* flop2ctrlSetId_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* flat_net2pin_start_map, const int* flat_net2pin_map, - const int* pin2net_map, const int* node2pincount, const int* pin_typeIds, const int* net2pincount, - const int* node2outpinIdx_map, const int* pin2node_map, const int* sorted_net_map, - const int* lut_type, const std::vector &ff, const int half_ctrl_mode, - const int cksr, const int ce0, const int ce1, subSlice& ble) -{ - ble.score = 0.0; - ble.ff[0] = INVALID; - ble.ff[1] = INVALID; - - for(unsigned int aIdx = 0; aIdx < ff.size(); ++aIdx) - { - const int ffA = ff[aIdx]; - int cksrA = flop_ctrlSets[flop2ctrlSetId_map[ffA]*3 + 1]; - int ceA = flop_ctrlSets[flop2ctrlSetId_map[ffA]*3 + 2]; - - if (cksrA == cksr && (ceA == ce0 || ceA == ce1)) - { - T score = 0.0; - computeBLEScore(flat_node2pin_start_map, flat_node2pin_map,flat_net2pin_start_map, - flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, - pin_typeIds, lut_type, half_ctrl_mode, ble.lut[0], ble.lut[1], ffA, INVALID, score); - - if (score > ble.score) - { - ble.ff[0] = ffA; - ble.ff[1] = INVALID; - if (ceA != ce0) - { - std::swap(ble.ff[0], ble.ff[1]); - } - ble.score = score; - } - } - //FF pairs - for(unsigned int bIdx = aIdx + 1; bIdx < ff.size(); ++bIdx) - { - int ffB = ff[bIdx]; - int cksrB = flop_ctrlSets[flop2ctrlSetId_map[ffB]*3 + 1]; - int ceB = flop_ctrlSets[flop2ctrlSetId_map[ffB]*3 + 2]; - if (cksrA == cksr && cksrB == cksr && ((ceA == ce0 && ceB == ce1) || (ceA == ce1 && ceB == ce0))) - { - T score = 0.0; - computeBLEScore(flat_node2pin_start_map, flat_node2pin_map,flat_net2pin_start_map, - flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, - pin_typeIds, lut_type, half_ctrl_mode, ble.lut[0], ble.lut[1], ffA, ffB, score); - - if (score > ble.score) - { - ble.ff[0] = ffA; - ble.ff[1] = ffB; - if (ceA != ce0) - { - std::swap(ble.ff[0], ble.ff[1]); - } - ble.score = score; - } - } - } - } -} - -template -void pairLUTs( - const std::vector &lut, const std::vector > &bleP, const std::vector > &bleS, - const T slotAssignFlowWeightScale, const T slotAssignFlowWeightIncr, const int NUM_BLE_PER_SLICE, - std::vector > &bleLP) -{ - lemon::ListGraph graph; - std::vector nodes; - std::vector edges; - graph.clear(); - nodes.clear(); - edges.clear(); - lemon::ListGraph::EdgeMap wtMap(graph); - - // Get LUT ID to index mapping - std::unordered_map idxMap; - for (unsigned int i = 0; i < lut.size(); ++i) - { - idxMap[lut[i]] = i; - } - // Build the graph use LUT pair score improvement as the edge weights - for (unsigned int i = 0; i < lut.size(); ++i) - { - nodes.emplace_back(graph.addNode()); - } - - for (const auto &ble: bleP) - { - edges.emplace_back(graph.addEdge(nodes[idxMap[ble.lut[0]]], nodes[idxMap[ble.lut[1]]])); - wtMap[edges.back()] = ble.improv * slotAssignFlowWeightScale; - } - // Use iterative max-weighted matching to find the best legal LUT pairing - while (true) - { - lemon::MaxWeightedMatching > mwm(graph, wtMap); - mwm.run(); - if (nodes.size() - mwm.matchingSize() <= NUM_BLE_PER_SLICE) - { - bleLP.clear(); - // Collect the LUT pairing solution - for (unsigned int i = 0; i < edges.size(); ++i) - { - if (mwm.matching(edges[i])) - { - bleLP.emplace_back(bleP[i]); - } - } - for (unsigned int i = 0; i < nodes.size(); ++i) - { - if (mwm.mate(nodes[i]) == lemon::INVALID) - { - bleLP.emplace_back(bleS[i]); - } - } - return; - } - // Increase all edge weight to get a tighter LUT pairing solution - int incr = slotAssignFlowWeightIncr * slotAssignFlowWeightScale; - for (const auto &e : edges) - { - wtMap[e] += incr; - } - } -} - -template -inline bool compare_pq_tops( - const T* site_curr_pq_score, const int* site_curr_pq_top_idx, const int* site_curr_pq_validIdx, - const int* site_curr_pq_siteId, const int* site_curr_pq_sig_idx, const int* site_curr_pq_sig, - const int* site_curr_pq_impl_lut, const int* site_curr_pq_impl_ff, const int* site_curr_pq_impl_cksr, - const int* site_curr_pq_impl_ce, const T* site_next_pq_score, const int* site_next_pq_top_idx, - const int* site_next_pq_validIdx, const int* site_next_pq_siteId, const int* site_next_pq_sig_idx, - const int* site_next_pq_sig, const int* site_next_pq_impl_lut, const int* site_next_pq_impl_ff, - const int* site_next_pq_impl_cksr, const int* site_next_pq_impl_ce, const int siteId, - const int sPQ, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY) -{ - //Check site_curr_pq TOP == site_next_pq TOP - int curr_pq_topId = sPQ+site_curr_pq_top_idx[siteId]; - int next_pq_topId = sPQ+site_next_pq_top_idx[siteId]; - - if (site_curr_pq_validIdx[curr_pq_topId] != site_next_pq_validIdx[next_pq_topId] || - site_curr_pq_validIdx[curr_pq_topId] != 1) - { - return false; - } - if (site_curr_pq_score[curr_pq_topId] == site_next_pq_score[next_pq_topId] && - site_curr_pq_siteId[curr_pq_topId] == site_next_pq_siteId[next_pq_topId] && - site_curr_pq_sig_idx[curr_pq_topId] == site_next_pq_sig_idx[next_pq_topId]) - { - //Check both sig - int currPQSigIdx = curr_pq_topId*SIG_IDX; - int nextPQSigIdx = next_pq_topId*SIG_IDX; - - for (int sg = 0; sg < site_curr_pq_sig_idx[curr_pq_topId]; ++sg) - { - if (site_curr_pq_sig[currPQSigIdx + sg] != site_next_pq_sig[nextPQSigIdx + sg]) - { - return false; - } - } - - //Check impl - int cCKRId = curr_pq_topId*CKSR_IN_CLB; - int cCEId = curr_pq_topId*CE_IN_CLB; - int cFFId = curr_pq_topId*SLICE_CAPACITY; - int nCKRId = next_pq_topId*CKSR_IN_CLB; - int nCEId = next_pq_topId*CE_IN_CLB; - int nFFId = next_pq_topId*SLICE_CAPACITY; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - if (site_curr_pq_impl_lut[cFFId + sg] != site_next_pq_impl_lut[nFFId + sg] || - site_curr_pq_impl_ff[cFFId + sg] != site_next_pq_impl_ff[nFFId + sg]) - { - return false; - } - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - if (site_curr_pq_impl_cksr[cCKRId + sg] != site_next_pq_impl_cksr[nCKRId + sg]) - { - return false; - } - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - if(site_curr_pq_impl_ce[cCEId + sg] != site_next_pq_impl_ce[nCEId + sg]) - { - return false; - } - } - ///// - return true; - } - return false; -} - -////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////// - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// Initialize Nets -template -int initializeNets(const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* sorted_net_idx, - const int* pin2node_map, - const int* net2pincount, - const int num_nets, - T* net_bbox, - int* net_pinIdArrayX, - int* net_pinIdArrayY, - int WLscoreMaxNetDegree, - const int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - const int idx = sorted_net_idx[i]; - - if (net2pincount[idx] > 0 && net2pincount[idx] <= WLscoreMaxNetDegree) - { - int xLo = idx*4; - int yLo = xLo+1; - int xHi = xLo+2; - int yHi = xLo+3; - - int pinIdBeg = flat_net2pin_start_map[idx]; - int pinIdEnd = flat_net2pin_start_map[idx+1]; - - int pnIdx = flat_net2pin_map[pinIdBeg]; - int nodeIdx = pin2node_map[pnIdx]; - - std::vector > tempX, tempY; - - net_bbox[xLo] = pos_x[nodeIdx] + pin_offset_x[pnIdx]; - net_bbox[yLo] = pos_y[nodeIdx] + pin_offset_y[pnIdx]; - net_bbox[xHi] = net_bbox[xLo]; - net_bbox[yHi] = net_bbox[yLo]; - - tempX.emplace_back(pnIdx, net_bbox[xLo]); - tempY.emplace_back(pnIdx, net_bbox[yLo]); - - //Update Net Bbox based on node location and pin offset - for (int pId = pinIdBeg+1; pId < pinIdEnd; ++pId) - { - int pinIdx = flat_net2pin_map[pId]; - int ndIdx = pin2node_map[pinIdx]; - - T valX = pos_x[ndIdx] + pin_offset_x[pinIdx]; - T valY = pos_y[ndIdx] + pin_offset_y[pinIdx]; - - if (valX < net_bbox[xLo]) - { - net_bbox[xLo] = valX; - } else if (valX > net_bbox[xHi]) - { - net_bbox[xHi] = valX; - } - - if (valY < net_bbox[yLo]) - { - net_bbox[yLo] = valY; - } else if (valY > net_bbox[yHi]) - { - net_bbox[yHi] = valY; - } - - tempX.emplace_back(pinIdx, valX); - tempY.emplace_back(pinIdx, valY); - } - - //Sort pinIdArray based on node loc and pin offset - std::sort(tempX.begin(), tempX.end(), [&](const auto &a, const auto &b){ return a.second < b.second; }); - std::sort(tempY.begin(), tempY.end(), [&](const auto &a, const auto &b){ return a.second < b.second; }); - - //Assign sorted values back - int tempId(0); - for (int pId = pinIdBeg; pId < pinIdEnd; ++pId) - { - net_pinIdArrayX[pId] = tempX[tempId].first; - net_pinIdArrayY[pId] = tempY[tempId].first; - ++tempId; - } - } - } - return 0; -} - -// preClustering -template -int preClustering(const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* sorted_node_map, - const int* sorted_node_idx, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* node2fence_region_map, - const int* node2outpinIdx_map, - const int* pin2net_map, - const int* pin2node_map, - const int* pin_typeIds, - const int lutId, - const int ffId, - const int num_nodes, - const T preClusteringMaxDist, - int* flat_node2precluster_map, - int* flat_node2prclstrCount, - const int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - const int instId = sorted_node_idx[i]; - if (node2fence_region_map[instId] == lutId) //Only consider LUTs - { - std::vector > ffs; - int nPIdx = instId*4; - for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - - int pinIdxBeg = flat_net2pin_start_map[outNetId]; - int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; - - T instLocX = pos_x[instId] + pin_offset_x[outPinId]; - T instLocY = pos_y[instId] + pin_offset_y[outPinId]; - for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) - { - int pinIdx = flat_net2pin_map[pinId]; - int nodeIdx = pin2node_map[pinIdx]; - - T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; - T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; - - T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + DREAMPLACE_STD_NAMESPACE::abs(distY); - - if (pin_typeIds[pinIdx] == 1 && node2fence_region_map[nodeIdx] == ffId && - dist <= preClusteringMaxDist) - { - ffs.emplace_back(std::make_pair(nodeIdx, dist)); - } - } - } - if (ffs.empty()) - { - continue; - } - - //Get FF index with min value (without Sort FFs) - std::sort(ffs.begin(), ffs.end(), [&sorted_node_map](const auto &a, const auto &b){ return a.second == b.second ? sorted_node_map[a.first] < sorted_node_map[b.first] : a.second < b.second; }); - - nPIdx = instId*3; - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[0].first; - ++flat_node2prclstrCount[instId]; - int fcIdx = flop2ctrlSetId_map[ffs[0].first]*3 + 1; - int cksr = flop_ctrlSets[fcIdx]; - - for (unsigned int fIdx = 1; fIdx < ffs.size(); ++fIdx) - { - int ctrlIdx = flop2ctrlSetId_map[ffs[fIdx].first]*3 + 1; - int fCksr = flop_ctrlSets[ctrlIdx]; - - if (fCksr == cksr) - { - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[fIdx].first; - ++flat_node2prclstrCount[instId]; - //std::cout << "BEFORE SORT: Inst Id: " << instId << " has precluster: " << flat_node2precluster_map[nPIdx] << ", " << flat_node2precluster_map[nPIdx + 1] << ", " << flat_node2precluster_map[nPIdx + 2] << std::endl; - break; - } - } - - std::sort(flat_node2precluster_map+nPIdx, flat_node2precluster_map+nPIdx + flat_node2prclstrCount[instId], [&sorted_node_map](const auto &a, const auto &b){return sorted_node_map[a] < sorted_node_map[b];}); - - for (int prcl = 0; prcl < flat_node2prclstrCount[instId]; ++prcl) - { - int fIdx = flat_node2precluster_map[nPIdx + prcl]; - int fID = fIdx*3; - if (fIdx != instId) - { - for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) - { - flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; - } - flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[instId]; - } - } - } - } - return 0; -} - -//TODO-Remove is_mlab_node when MLABs are treated separately -//Preclustering to handle carry chains and mlabs -template -int preClusteringUpd( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* sorted_node_map, - const int* sorted_node_idx, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* node2fence_region_map, - const int* node2outpinIdx_map, - const int* pin2net_map, - const int* pin2node_map, - const int* pin_typeIds, - const int* is_mlab_node, - const T preClusteringMaxDist, - const int lutId, - const int ffId, - const int num_nodes, - int* flat_node2precluster_map, - int* flat_node2prclstrCount, - const int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - const int instId = sorted_node_idx[i]; - //Only consider LUTs and skip MLABs - if (node2fence_region_map[instId] == lutId && is_mlab_node[instId] == 0) - { - std::vector > ffs; - int nPIdx = instId*4; - for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - - int pinIdxBeg = flat_net2pin_start_map[outNetId]; - int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; - - T instLocX = pos_x[instId] + pin_offset_x[outPinId]; - T instLocY = pos_y[instId] + pin_offset_y[outPinId]; - - for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) - { - int pinIdx = flat_net2pin_map[pinId]; - int nodeIdx = pin2node_map[pinIdx]; - - T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; - T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; - - T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + DREAMPLACE_STD_NAMESPACE::abs(distY); - - if (pin_typeIds[pinIdx] == 1 && node2fence_region_map[nodeIdx] == ffId && - dist <= preClusteringMaxDist) - { - ffs.emplace_back(std::make_pair(nodeIdx, dist)); - } - } - } - if (ffs.empty()) - { - continue; - } - - //Get FF index with min value (without Sort FFs) - std::sort(ffs.begin(), ffs.end(), [&sorted_node_map](const auto &a, const auto &b){ return a.second == b.second ? sorted_node_map[a.first] < sorted_node_map[b.first] : a.second < b.second; }); - - nPIdx = instId*3; - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[0].first; - ++flat_node2prclstrCount[instId]; - int fcIdx = flop2ctrlSetId_map[ffs[0].first]*3 + 1; - int cksr = flop_ctrlSets[fcIdx]; - - if (ffs.size() > 1) - { - for (unsigned int fIdx = 1; fIdx < ffs.size(); ++fIdx) - { - int ctrlIdx = flop2ctrlSetId_map[ffs[fIdx].first]*3 + 1; - int fCksr = flop_ctrlSets[ctrlIdx]; - - if (fCksr == cksr) - { - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[instId]] = ffs[fIdx].first; - ++flat_node2prclstrCount[instId]; - //std::cout << "BEFORE SORT: Inst Id: " << instId << " has precluster: " << flat_node2precluster_map[nPIdx] << ", " << flat_node2precluster_map[nPIdx + 1] << ", " << flat_node2precluster_map[nPIdx + 2] << std::endl; - break; - } - } - } - - std::sort(flat_node2precluster_map+nPIdx, flat_node2precluster_map+nPIdx + flat_node2prclstrCount[instId], [&sorted_node_map](const auto &a, const auto &b){return sorted_node_map[a] < sorted_node_map[b];}); - - for (int prcl = 0; prcl < flat_node2prclstrCount[instId]; ++prcl) - { - int fIdx = flat_node2precluster_map[nPIdx + prcl]; - int fID = fIdx*3; - if (fIdx != instId) - { - for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) - { - flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; - } - flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[instId]; - } - } - } - } - return 0; -} - -// legalize carry chains -template -int legalizeCarryChainsLauncher( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const T* wlPrecond, - const int* site2addr_map, - const int* flat_cc2node_start_map, - const int* flat_cc2node_map, - const int* spiral_accessor, - const int* site_types, - const int spiralBegin, - const int spiralEnd, - const int num_carry_chains, - const int num_sites_x, - const int num_sites_y, - const int sliceId, - const int SIG_IDX, - const int SLICE_CAPACITY, - const int num_carryInstsInSlice, - const int slice_minX, - const int slice_maxX, - const int slice_minY, - const int slice_maxY, - T* carry_chain_displacements, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes, - const int num_threads) -{ - int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); - - std::vector > carry_chain(num_carry_chains); - std::vector carry_chain_size(num_carry_chains, 0); - std::vector centroidX(num_carry_chains, 0.0), centroidY(num_carry_chains, 0.0); - - int HALF_SLICE_CAPACITY = int(SLICE_CAPACITY/2); - - uint32_t legalcc = 0; - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_carry_chains / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_carry_chains; ++i) - { - T cenX(0), cenY(0), totalWt(0); - - for (int ccIdx = flat_cc2node_start_map[i]; ccIdx < flat_cc2node_start_map[i+1]; ++ccIdx) - { - int instId = flat_cc2node_map[ccIdx]; - carry_chain[i].emplace_back(instId); - - cenX += pos_x[instId] * wlPrecond[instId]; - cenY += pos_y[instId] * wlPrecond[instId]; - totalWt += wlPrecond[instId]; - } - - carry_chain_size[i] = carry_chain[i].size(); - - if (totalWt > 0) - { - cenX /= totalWt; - cenY /= totalWt; - } - - centroidX[i] = cenX; - centroidY[i] = cenY; - } - - //Sort based on carry chain size - std::vector sorted_carry_chains(num_carry_chains); - std::iota(sorted_carry_chains.begin(),sorted_carry_chains.end(),0); //Initializing - std::sort(sorted_carry_chains.begin(),sorted_carry_chains.end(), [&](int i,int j){return carry_chain_size[i]>carry_chain_size[j];} ); - - for (int i = 0; i < num_carry_chains; ++i) - { - const int idx = sorted_carry_chains[i]; - int elCount = carry_chain_size[idx]; - dreamplaceAssertMsg(elCount, "Carry chain of size 0 encountered - CHECK"); - - int sliceSpread = std::ceil(elCount/num_carryInstsInSlice) + 1; - - int cenX = centroidX[idx]; - int cenY = centroidY[idx]; - - int beg(spiralBegin), end(spiralEnd); - T bestX = INVALID; - T bestY = INVALID; - T bestScore = 10000000; - - for (int sId = beg; sId < end; ++sId) - { - int xVal = cenX + spiral_accessor[2*sId]; - int yVal = cenY + spiral_accessor[2*sId+1]; - int siteId = xVal*num_sites_y + yVal; - int sIdx = site2addr_map[siteId]; - - //Check within bounds - if (xVal < slice_minX || xVal > slice_maxX || yVal < slice_minY || yVal > slice_maxY) - { - continue; - } - - int startY = yVal + sliceSpread-1; - - if (site_types[siteId] == sliceId && startY <= slice_maxY) - { - char space_available = 1; - for (int yId = yVal; yId <= startY; ++yId) - { - int siteMap = xVal*num_sites_y + yId; - int sMap = site2addr_map[siteMap]; - - if (site_types[siteMap] != sliceId || site_det_sig_idx[sMap] > 0) - { - space_available = 0; - break; - } - } - - //Legalize carry chain if space is available - if (space_available == 1) - { - if (bestScore == 10000000) - { - int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId]) + - DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId+1]); - r += 2; - int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); - end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; - } - - T dist_score = DREAMPLACE_STD_NAMESPACE::abs(cenX - site_xy[siteId*2]) + - DREAMPLACE_STD_NAMESPACE::abs(cenY - site_xy[siteId*2+1]); - if (dist_score < bestScore) - { - bestX = xVal; - bestY = yVal; - bestScore = dist_score; - } - } - } - } - - if (bestX != INVALID && bestY != INVALID) - { - carry_chain_displacements[idx] = 0; - int startY = bestY + sliceSpread -1; - int elements_left = elCount; - int instOffset = 0; - - for (int yId = startY; yId >= bestY; --yId) - { - int bSiteId = bestX * num_sites_y + yId; - int bSIdx = site2addr_map[bSiteId]; - int sdtopId = bSIdx*SIG_IDX; - int sdlutId = bSIdx*SLICE_CAPACITY; - - int insts_added = DREAMPLACE_STD_NAMESPACE::min(elements_left, SLICE_CAPACITY); - - elements_left -= insts_added; - - if (site_det_sig_idx[bSIdx] == 0) - { - sites_with_special_nodes[bSIdx] = 1; - site_det_score[bSIdx] = 100.0; - site_det_siteId[bSIdx] = bSiteId; - for(int sg = 0; sg < insts_added; ++sg) - { - int instAdd = carry_chain[idx][instOffset]; - site_det_sig[sdtopId + sg] = instAdd; - site_det_impl_lut[sdlutId + sg] = instAdd; - - inst_curr_detSite[instAdd] = bSiteId; - inst_curr_bestSite[instAdd] = bSiteId; - inst_curr_bestScoreImprov[instAdd] = 10000.0; - - inst_next_detSite[instAdd] = bSiteId; - inst_next_bestSite[instAdd] = bSiteId; - inst_next_bestScoreImprov[instAdd] = 10000.0; - - carry_chain_displacements[idx] += DREAMPLACE_STD_NAMESPACE::abs(pos_x[instAdd] - site_xy[bSiteId*2]) + - DREAMPLACE_STD_NAMESPACE::abs(pos_y[instAdd] - site_xy[bSiteId*2+1]); - - ++instOffset; - } - site_det_sig_idx[bSIdx] += insts_added; - } - //DBG - else { - std::cout << "ERROR: More than one carry chain node legalized at site: (" << bestX << ", " << yId << ")" << std::endl; - } - //DBG - } - carry_chain_displacements[idx] /= elCount; - ++legalcc; - } - } - - //DBG - if (legalcc != num_carry_chains) - { - std::cout << "ERROR: Only " << legalcc << " carry chains legalized out of " << num_carry_chains << " - CHECK!" << std::endl; - } - //DBG - - return 0; -} - -// legalize Slice Macros -template -int legalizeMacrosLauncher( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const int* macro2node_map, - const int* macro_site_height, - const int* macro_element_count, - const int* site2addr_map, - const int* spiral_accessor, - const int* site_types, - const int spiralBegin, - const int spiralEnd, - const int num_macros, - const int num_sites_x, - const int num_sites_y, - const int slice_minX, - const int slice_maxX, - const int slice_minY, - const int slice_maxY, - const int sliceId, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* dist_moved, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes) -{ - int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); - //Use macro_site_x/y locations as guidance to legalize macros - std::vector sorted_macroIds(num_macros); - std::iota(sorted_macroIds.begin(),sorted_macroIds.end(),0); //Initializing - - //Sort macros based on Slices occupied - std::sort(sorted_macroIds.begin(),sorted_macroIds.end(), [&](int i,int j){return macro_element_count[i]>macro_element_count[j];} ); - - for (int i = 0; i < num_macros; ++i) - { - const int macroId = sorted_macroIds[i]; - const int instId = macro2node_map[macroId]; - int sliceSpread = macro_site_height[macroId]; - dreamplaceAssertMsg(sliceSpread, "Macro of size 0 encountered - CHECK"); - int macroElCount = macro_element_count[macroId]; - - T initX = pos_x[instId]; - T initY = pos_y[instId]; - - int beg(spiralBegin), end(spiralEnd); - T bestX = INVALID; - T bestY = INVALID; - T bestScore = 10000000; - - for (int sId = beg; sId < end; ++sId) - { - int xVal = initX + spiral_accessor[2*sId]; - int yVal = initY + spiral_accessor[2*sId+1]; - int siteId = xVal * num_sites_y + yVal; - int sIdx = site2addr_map[siteId]; - - //Check within bounds - if (xVal < slice_minX || xVal > slice_maxX || yVal < slice_minY || yVal > slice_maxY) - { - continue; - } - - int startY = yVal + sliceSpread -1; - if (site_types[siteId] == sliceId && startY <= slice_maxY) - { - char space_available = 1; - for (int yId = yVal; yId <= startY; ++yId) - { - int siteMap = xVal*num_sites_y + yId; - int sMap = site2addr_map[siteMap]; - if (site_types[siteMap] != sliceId || site_det_sig_idx[sMap] > 0) - { - space_available = 0; - } - } - - if (space_available == 1) - { - if (bestScore == 10000000) - { - int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId]) + - DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[2*sId+1]); - r += 2; - int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); - end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; - } - - T dist_score = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + - DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); - if (dist_score < bestScore) - { - bestX = xVal; - bestY = yVal; - bestScore = dist_score; - } - } - } - } - - if (bestX != INVALID && bestY != INVALID) - { - dist_moved[macroId] = bestScore; - int elements_left = macroElCount; - int bSiteId = bestX * num_sites_y + bestY; - - inst_curr_detSite[instId] = bSiteId; - inst_curr_bestSite[instId] = bSiteId; - inst_curr_bestScoreImprov[instId] = 10000.0; - - inst_next_detSite[instId] = bSiteId; - inst_next_bestSite[instId] = bSiteId; - inst_next_bestScoreImprov[instId] = 10000.0; - - int startY = bestY + sliceSpread -1; - - for (int yId = startY; yId >= bestY; --yId) - { - bSiteId = bestX * num_sites_y + yId; - int bSIdx = site2addr_map[bSiteId]; - int sdtopId = bSIdx*SIG_IDX; - int sdlutId = bSIdx*SLICE_CAPACITY; - - int insts_added = DREAMPLACE_STD_NAMESPACE::min(elements_left, SLICE_CAPACITY); - - elements_left -= insts_added; - if (site_det_sig_idx[bSIdx] == 0) - { - sites_with_special_nodes[bSIdx] = 1; - site_det_score[bSIdx] = 1000.0; - site_det_siteId[bSIdx] = bSiteId; - for(int sg = 0; sg < insts_added; ++sg) - { - //Include same nodeId across the macro span - site_det_sig[sdtopId + sg] = instId; - site_det_impl_lut[sdlutId + sg] = instId; - } - site_det_sig_idx[bSIdx] += insts_added; - } - //DBG - else { - std::cout << "ERROR: Site not empty - Macro not legalized at (" << bestX << ", " << yId << ")" << std::endl; - } - //DBG - } - } - } - - return 0; -} - -// legalize mlabs -template -int legalizeMlabsLauncher( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const T* mlab_locX, - const T* mlab_locY, - const int* mlab_indices, - const int* site2addr_map, - const int num_mlab_nodes, - const int num_sites_y, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* dist_moved, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes, - const int num_threads) -{ - //Assign mlabs to site locations provided - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_mlab_nodes/ num_threads / SLICE_CAPACITY), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_mlab_nodes; ++i) - { - const int instId = mlab_indices[i]; - - T xVal = mlab_locX[i]; - T yVal = mlab_locY[i]; - - int siteId = xVal * num_sites_y + yVal; - int sIdx = site2addr_map[siteId]; - - dist_moved[i] = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + - DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); - - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - - if (site_det_sig_idx[sIdx] == 0) - { - sites_with_special_nodes[sIdx] = 1; - site_det_score[sIdx] = 1000.0; - site_det_siteId[sIdx] = siteId; - - site_det_sig_idx[sIdx] = 2*SLICE_CAPACITY; - site_det_sig[sdtopId] = instId; - site_det_impl_lut[sdlutId] = instId; - - inst_curr_detSite[instId] = siteId; - inst_curr_bestSite[instId] = siteId; - inst_curr_bestScoreImprov[instId] = 10000.0; - - inst_next_detSite[instId] = siteId; - inst_next_bestSite[instId] = siteId; - inst_next_bestScoreImprov[instId] = 10000.0; - } - //DBG - else { - std::cout << "ERROR: Slice site not empty - MLAB " << instId << " not legalized at (" << xVal << ", " << yVal << ")" << std::endl; - } - //DBG - } - - return 0; -} - -// initSiteNeighbours -template -int initSiteNeighbours( - const T* pos_x, - const T* pos_y, - const T* wlPrecond, - const T* site_xy, - const T* site_det_score, - const int* sorted_node_idx, - const int* node2fence_region_map, - const int* site_types, - const int* spiral_accessor, - const int* site2addr_map, - const int* addr2site_map, - const int* is_mlab_node, - const int* is_mlab_site, - const int* sites_with_special_nodes, - const int* flat_node2precluster_map, - const int* flat_node2prclstrCount, - const T nbrDistEnd, - const T nbrDistBeg, - const T nbrDistIncr, - const int lutId, - const int ffId, - const int sliceId, - const int num_nodes, - const int num_sites_x, - const int num_sites_y, - const int num_clb_sites, - const int spiralBegin, - const int spiralEnd, - const int maxList, - const int numGroups, - const int SCL_IDX, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* site_curr_scl_score, - int* site_nbrList, - int* site_nbrRanges, - int* site_nbrRanges_idx, - int* site_nbr, - int* site_nbr_idx, - int* site_nbrGroup_idx, - int* site_det_siteId, - int* site_det_sig, - int* site_det_sig_idx, - int* site_det_impl_lut, - int* site_curr_scl_siteId, - int* site_curr_scl_validIdx, - int* site_curr_scl_idx, - int* site_curr_scl_sig, - int* site_curr_scl_sig_idx, - int* site_curr_scl_impl_lut, - const int num_threads) -{ - std::vector > > sites_nbrListMap(num_clb_sites); - std::vector site_nbrList_idx(num_clb_sites, 0); - - //Update sites_nbrListMap_instId and sites_nbrListMap_dist - for (int i = 0; i < num_nodes; ++i) - { - const int instId = sorted_node_idx[i]; - int prIdx = instId*3; - int preclusterNodeId = flat_node2precluster_map[prIdx]; - - //Only consider LUTs & FFs AND first precluster is the same as InstID - if ((node2fence_region_map[instId] != lutId && node2fence_region_map[instId] != ffId) || - preclusterNodeId != instId || is_mlab_node[instId] == 1) - { - continue; - } - - //Centroid Calculation - //First element in inst precluster is itself - T cenX = pos_x[preclusterNodeId] * wlPrecond[preclusterNodeId]; - T cenY = pos_y[preclusterNodeId] * wlPrecond[preclusterNodeId]; - T totalWt = wlPrecond[preclusterNodeId]; - - if (flat_node2prclstrCount[instId] > 1) - { - for (int pcl = 1; pcl < flat_node2prclstrCount[instId]; ++pcl) - { - int fdx = flat_node2precluster_map[prIdx + pcl]; - cenX += pos_x[fdx] * wlPrecond[fdx]; - cenY += pos_y[fdx] * wlPrecond[fdx]; - totalWt += wlPrecond[fdx]; - } - } - - if (totalWt > 0) - { - cenX /= totalWt; - cenY /= totalWt; - } - - //Employ spiral accessor to update neighbour list - for (int sIdx = spiralBegin; sIdx < spiralEnd; ++sIdx) - { - int saIdx = sIdx*2; //For x,y - int xVal = cenX + spiral_accessor[saIdx]; - int yVal = cenY + spiral_accessor[saIdx + 1]; - - //Check within bounds - if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) - { - continue; - } - - int siteMapIdx = xVal * num_sites_y + yVal; - int siteId = site2addr_map[siteMapIdx]; - int stMpId = siteMapIdx*2; - - //Check site type and Inst type (CLB) matches - if ((node2fence_region_map[instId] == lutId || node2fence_region_map[instId] == ffId) && - site_types[siteMapIdx] == sliceId && is_mlab_site[siteId] == 0) - { - T dist = DREAMPLACE_STD_NAMESPACE::abs(cenX - site_xy[stMpId]) + - DREAMPLACE_STD_NAMESPACE::abs(cenY - site_xy[stMpId+1]); - if (dist < nbrDistEnd) - { - sites_nbrListMap[siteId].emplace_back(std::make_pair(instId, dist)); - } - } - } - } - - //Update site information based on nbrListMap update - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - // Sort neighbors by their distances - auto &list = sites_nbrListMap[sIdx]; - std::sort(list.begin(), list.end(), [&](const std::pair &l, const std::pair &r){ return l.second < r.second; }); - - int sRIdx = sIdx * (numGroups + 1); - int sNbrIdx = sIdx*maxList; - if (!list.empty()) - { - site_nbrRanges[sRIdx] = 0; - - int grpIdx = 0; - - T maxD = nbrDistBeg; - - for (unsigned il = 0; il < list.size(); ++il) - { - site_nbrList[sNbrIdx + site_nbrList_idx[sIdx]] = list[il].first; - ++site_nbrList_idx[sIdx]; - - while (list[il].second >= maxD) - { - site_nbrRanges[++grpIdx + sRIdx] = il; - maxD += nbrDistIncr; - } - } - while(++grpIdx <= numGroups) - { - site_nbrRanges[sRIdx + grpIdx] = site_nbrList_idx[sIdx]; - } - site_nbrRanges_idx[sIdx] = grpIdx; - } - } - - //runDLInit - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - int siteId = addr2site_map[sIdx]; - int sRIdx = sIdx * (numGroups + 1); - int sNbrIdx = sIdx*maxList; - - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - ////Assign site_nbr - if (numNbrGroups > 0) - { - for (int nIdx = site_nbrRanges[sRIdx]; nIdx < site_nbrRanges[sRIdx+1]; ++nIdx) - { - site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + nIdx]; - ++site_nbr_idx[sIdx]; - } - site_nbrGroup_idx[sIdx] = 1; - } - - int sSCL = sIdx*SCL_IDX; - - site_det_siteId[sIdx] = siteId; - site_curr_scl_siteId[sSCL] = siteId; - site_curr_scl_validIdx[sSCL] = 1; - ++site_curr_scl_idx[sIdx]; - - if (sites_with_special_nodes[sIdx] == 1) - { - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - int sclSigId = sSCL*SIG_IDX; - int scllutIdx = sSCL*SLICE_CAPACITY; - //Assign site_det to site_curr_scl - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - } - } - } - return 0; -} - -//run DL Iteration -template -int runDLIteration( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_bbox, - const T* site_xy, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* node2fence_region_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* flop2ctrlSetId_map, - const int* lut_type, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* node2pincount, - const int* net2pincount, - const int* pin2net_map, - const int* pin_typeIds, - const int* flat_net2pin_start_map, - const int* pin2node_map, - const int* sorted_node_map, - const int* sorted_net_map, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* is_mlab_node, - const int* is_mlab_site, - const int* site_nbrList, - const int* site_nbrRanges, - const int* site_nbrRanges_idx, - const T* net_weights, - const int* addr2site_map, - const int* special_nodes, - const int num_clb_sites, - const int minStableIter, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int minNeighbors, - const int numGroups, - const int netShareScoreMaxNetDegree, - const int wlScoreMaxNetDegree, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const T xWirelenWt, - const T yWirelenWt, - const T wirelenImprovWt, - const T extNetCountWt, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - const int num_threads, - int* site_nbr_idx, - int* site_nbr, - int* site_nbrGroup_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_validIdx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - T* site_det_score, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - T* inst_next_bestScoreImprov, - int* inst_next_bestSite - ) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); - -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - if (is_mlab_site[sIdx]) continue; - - int siteId = addr2site_map[sIdx]; - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - int sdckId = sIdx*CKSR_IN_CLB; - int sdceId = sIdx*CE_IN_CLB; - - int sclSigId = sSCL*SIG_IDX; - int scllutIdx = sSCL*SLICE_CAPACITY; - int sclckIdx = sSCL*CKSR_IN_CLB; - int sclceIdx = sSCL*CE_IN_CLB; - - //(a)Try to commit Top candidates - int commitTopCandidate(INVALID); - - int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); - int topIdx(tsPQ*SIG_IDX); - int lutIdx = tsPQ*SLICE_CAPACITY; - int ckIdx = tsPQ*CKSR_IN_CLB; - int ceIdx = tsPQ*CE_IN_CLB; - - ////DBG - //if (site_curr_pq_idx[sIdx] > 0) - //{ - // std::cout << "For site: " << siteId << "(" << sIdx << "), there are " << site_curr_pq_idx[sIdx] << " elements in site_curr_pq " << std::endl; - // int tsPQ = sPQ + site_curr_pq_top_idx[sIdx]; - - // std::cout << "site curr pq top element has score: " << site_curr_pq_score[tsPQ] << std::endl; - // std::cout << "site curr pq top element has site: " << site_curr_pq_siteId[tsPQ] << std::endl; - // std::cout << "site curr pq top element has sig elements: " << site_curr_pq_sig_idx[tsPQ] << std::endl; - // std::cout << "site curr pq top element sig: "; - // for (int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) - // { - // std::cout << site_curr_pq_sig[topIdx + sg] << " "; - // } - // std::cout << std::endl; - //} - ////DBG - - if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || - !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, - site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], - site_curr_pq_sig, inst_curr_detSite)) - { - commitTopCandidate = 0; - } else if (site_curr_pq_idx[sIdx] > 0) - { - for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) - { - int pqInst = site_curr_pq_sig[topIdx + pIdx]; - if (inst_curr_detSite[pqInst] != siteId && inst_curr_bestSite[pqInst] != siteId) - { - commitTopCandidate = 0; - break; - } - } - } - - if (commitTopCandidate == INVALID) - { - ////// - site_det_score[sIdx] = site_curr_pq_score[tsPQ]; - site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; - site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; - - for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) - { - site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; - site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; - } - ////// - - for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) - { - int sigInst = site_det_sig[sdtopId + iSig]; - inst_next_detSite[sigInst] = siteId; - } - - //Remove Incompatible Neighbors - remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, - pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, - flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, - site_det_sig, site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, - sIdx, sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, site_nbr_idx, site_nbr); - - //Clear pq and make scl only contain the committed candidate - //int sclCount(0); - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - //if (site_next_pq_validIdx[nPQId] != INVALID) - //{ - //Clear contents thoroughly - clear_cand_contents( - nPQId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[nPQId] = INVALID; - site_next_pq_siteId[nPQId] = INVALID; - site_next_pq_score[nPQId] = 0.0; - site_next_pq_sig_idx[nPQId] = 0; - - //++sclCount; - //if (sclCount == site_next_pq_idx[sIdx]) - //{ - // break; - //} - //} - } - site_next_pq_idx[sIdx] = 0; - site_next_pq_top_idx[sIdx] = INVALID; - site_next_stable[sIdx] = 0; - - int sclCount = 0; - for (int vId = 0; vId < SCL_IDX; ++vId) - { - int cSclId = sSCL + vId; - if (site_curr_scl_validIdx[cSclId] != INVALID) - { - //Clear contents thoroughly - clear_cand_contents( - cSclId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - - site_curr_scl_validIdx[cSclId] = INVALID; - site_curr_scl_sig_idx[cSclId] = 0; - site_curr_scl_siteId[cSclId] = INVALID; - site_curr_scl_score[cSclId] = 0.0; - ++sclCount; - if (sclCount == site_curr_scl_idx[sIdx]) - { - break; - } - } - } - site_curr_scl_idx[sIdx] = 0; - - //Assign site_det to site_curr_scl - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - ///// - commitTopCandidate = 1; - } - if (commitTopCandidate == 0) - { - //Remove invalid candidates from site PQ - if (site_next_pq_idx[sIdx] > 0) - { - //int snCnt = 0; - //int maxEntries = site_next_pq_idx[sIdx]; - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - int topIdx = ssPQ*SIG_IDX; - - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, - site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], - site_next_pq_sig, inst_curr_detSite)) - { - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[ssPQ] = INVALID; - site_next_pq_sig_idx[ssPQ] = 0; - site_next_pq_siteId[ssPQ] = INVALID; - site_next_pq_score[ssPQ] = 0.0; - --site_next_pq_idx[sIdx]; - } - //++snCnt; - //if (snCnt == maxEntries) - //{ - // break; - //} - } - } - - //Recompute top idx - site_next_pq_top_idx[sIdx] = INVALID; - if (site_next_pq_idx[sIdx] > 0) - { - //snCnt = 0; - //maxEntries = site_next_pq_idx[sIdx]; - T maxScore(-1000.0); - int maxScoreId(INVALID); - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (site_next_pq_score[ssPQ] > maxScore) - { - maxScore = site_next_pq_score[ssPQ]; - maxScoreId = nIdx; - } - //++snCnt; - //if (snCnt == maxEntries) - //{ - // break; - //} - } - } - site_next_pq_top_idx[sIdx] = maxScoreId; - } - } - - //Remove invalid candidates from seed candidate list (scl) - if (site_curr_scl_idx[sIdx] > 0) - { - int sclCount(0), maxEntries(site_curr_scl_idx[sIdx]); - for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) - { - int ssPQ = sSCL + nIdx; - int topIdx = ssPQ*SIG_IDX; - - if (site_curr_scl_validIdx[ssPQ] != INVALID) - { - if(!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, - site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], - site_curr_scl_sig, inst_curr_detSite)) - { - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - - site_curr_scl_validIdx[ssPQ] = INVALID; - site_curr_scl_sig_idx[ssPQ] = 0; - site_curr_scl_siteId[ssPQ] = INVALID; - site_curr_scl_score[ssPQ] = 0.0; - --site_curr_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - - //If site.scl becomes empty, add site_det into it as the seed - if (site_curr_scl_idx[sIdx] == 0) - { - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - } - } - - // (c) removeCommittedNeighbors(site) - for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) - { - int siteInst = site_nbr[sNbrIdx + sNIdx]; - if (inst_curr_detSite[siteInst] != INVALID) - { - site_nbr[sNbrIdx + sNIdx] = INVALID; - } - } - remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); - - // (d) addNeighbors(site) - Original implementation without staggering - ////DBG - //std::cout << "For site: " << siteId << "(" << sIdx << "), there are " << site_nbr_idx[sIdx] << " site nbrs and nbrGroup_idx: " - // << site_nbrGroup_idx[sIdx] << std::endl; - ////DBG - if (site_nbr_idx[sIdx] < minNeighbors && site_nbrGroup_idx[sIdx] < numNbrGroups) - { - int nbrRIdx = sIdx*(numGroups+1) + site_nbrGroup_idx[sIdx]; - int beg = site_nbrRanges[nbrRIdx]; - int end = site_nbrRanges[nbrRIdx+1]; - - for (int aNIdx = beg; aNIdx < end; ++aNIdx) - { - int instId = site_nbrList[sNbrIdx + aNIdx]; - ////DBG - //std::cout << "For site: " << siteId << "(" << sIdx << "), try to add inst " << instId << " of type " - // << node2fence_region_map[instId] << "to site nbrs" << std::endl; - ////DBG - - if (inst_curr_detSite[instId] == INVALID && - is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, node2pincount, net2pincount, - pin2net_map, pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, - flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - site_det_impl_lut, site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, - special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, instId, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, - lutId, ffId, half_ctrl_mode)) - { - ////DBG - //std::cout << "For site: " << siteId << "(" << sIdx << "), ADD inst " << instId << " of type " - // << node2fence_region_map[instId] << "to site nbrs" << std::endl; - ////DBG - site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = instId; - ++site_nbr_idx[sIdx]; - } - } - ++site_nbrGroup_idx[sIdx]; - } - ////DBG - //std::cout << "After (d) For site: " << siteId << "(" << sIdx << "), there are " << site_nbr_idx[sIdx] << " site nbrs and nbrGroup_idx: " - // << site_nbrGroup_idx[sIdx] << std::endl; - ////DBG - - // (e) createNewCandidates(site) - Original implementation without restricted candidate search space - //Generate new candidates by merging site_nbr to site_curr_scl - const int limit_x = site_curr_scl_idx[sIdx]; - const int limit_y = site_nbr_idx[sIdx]; - const int limit_cands = limit_x*limit_y; - int limit_count(0); - int sclCount(0); - if (limit_cands > 0) - { - for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) - { - int siteCurrIdx = sSCL + scsIdx; - if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) - { - if (limit_count >= limit_cands) break; - // - for (int snIdx = 0; snIdx < site_nbr_idx[sIdx]; ++snIdx) - { - ++limit_count; - - int instId = site_nbr[sNbrIdx + snIdx]; - int instPcl = instId*3; - - ///// - //New candidate = site_curr_scl_validIdx[sSCL + scsIdx] - int sCKRId = siteCurrIdx*CKSR_IN_CLB; - int sCEId = siteCurrIdx*CE_IN_CLB; - int sFFId = siteCurrIdx*SLICE_CAPACITY; - int sGId = siteCurrIdx*SIG_IDX; - - T nwCand_score = site_curr_scl_score[siteCurrIdx]; - int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; - int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; - - //array instantiation - int nwCand_sig[SIG_MAX_CAP]; - int nwCand_lut[SLICE_MAX_CAP]; - int nwCand_ff[SLICE_MAX_CAP]; - int nwCand_ce[CE_MAX_CAP]; - int nwCand_cksr[CKSR_MAX_CAP]; - - for (int sg = 0; sg < site_curr_scl_sig_idx[siteCurrIdx]; ++sg) - { - nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; - nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; - } - ///// - - if (add_inst_to_sig(flat_node2prclstrCount[instId], flat_node2precluster_map, - instPcl, nwCand_sig, nwCand_sigIdx, SLICE_CAPACITY) && - !check_sig_in_site_next_pq_sig(nwCand_sig, nwCand_sigIdx, sPQ, PQ_IDX, - site_next_pq_validIdx, site_next_pq_sig, site_next_pq_sig_idx, SIG_IDX) && - add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, - half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - BLE_CAPACITY, NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) - { - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, - nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); - - int nxtId(INVALID); - //find least score and replace if current score is greater - if (site_next_pq_idx[sIdx] < PQ_IDX) - { - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (site_next_pq_validIdx[sPQ+vId] == INVALID) - { - nxtId = vId; - ++site_next_pq_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (ckscore > site_next_pq_score[sPQ + vId]) - { - ckscore = site_next_pq_score[sPQ + vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - int nTId = sPQ + nxtId; - int nCKRId = nTId*CKSR_IN_CLB; - int nCEId = nTId*CE_IN_CLB; - int nFFId = nTId*SLICE_CAPACITY; - int nSGId = nTId*SIG_IDX; - - ///// - site_next_pq_validIdx[nTId] = 1; - site_next_pq_score[nTId] = nwCand_score; - site_next_pq_siteId[nTId] = nwCand_siteId; - site_next_pq_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - - if (site_next_pq_idx[sIdx] == 1 || nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) - { - site_next_pq_top_idx[sIdx] = nxtId; - } - - nxtId = INVALID; - - if (site_next_scl_idx[sIdx] < SCL_IDX) - { - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (site_next_scl_validIdx[sSCL+vId] == INVALID) - { - nxtId = vId; - ++site_next_scl_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (ckscore > site_next_scl_score[sSCL+vId]) - { - ckscore = site_next_scl_score[sSCL+vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - ///// - nTId = sSCL + nxtId; - nCKRId = nTId*CKSR_IN_CLB; - nCEId = nTId*CE_IN_CLB; - nFFId = nTId*SLICE_CAPACITY; - nSGId = nTId*SIG_IDX; - - site_next_scl_validIdx[nTId] = 1; - site_next_scl_score[nTId] = nwCand_score; - site_next_scl_siteId[nTId] = nwCand_siteId; - site_next_scl_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - } - } - } - } - ++sclCount; - if (sclCount == site_curr_scl_idx[sIdx]) - { - break; - } - } - } - } - - //Remove all candidates in scl that is worse than the worst candidate in PQ - if (site_next_pq_idx[sIdx] > 0) - { - //Find worst candidate in PQ - T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); - - sclCount = 0; - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - if (site_next_pq_validIdx[nPQId] != INVALID) - { - if (ckscore > site_next_pq_score[nPQId]) - { - ckscore = site_next_pq_score[nPQId]; - } - ++sclCount; - if (sclCount == site_next_pq_idx[sIdx]) - { - break; - } - } - } - - //Invalidate worst ones in scl - sclCount = 0; - int maxEntries(site_next_scl_idx[sIdx]); - for (int ckId = 0; ckId < SCL_IDX; ++ckId) - { - int vId = sSCL + ckId; - if (site_next_scl_validIdx[vId] != INVALID) - { - if (ckscore > site_next_scl_score[vId]) - { - //Clear contents thoroughly - clear_cand_contents( - vId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce); - - site_next_scl_validIdx[vId] = INVALID; - site_next_scl_sig_idx[vId] = 0; - site_next_scl_siteId[vId] = INVALID; - site_next_scl_score[vId] = 0.0; - --site_next_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - - if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && - compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, - site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, - site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, - site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, - site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, - site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, - site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, - sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) - { - //Check both sig - site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; - } else - { - site_next_stable[sIdx] = 0; - } - - // (f) broadcastTopCandidate(site) - Original implementation without updated sequential portion - if (site_next_pq_idx[sIdx] > 0) - { - int topIdx = sPQ + site_next_pq_top_idx[sIdx]; - int topSigId = topIdx*SIG_IDX; - - T scoreImprov = site_next_pq_score[topIdx] - site_det_score[sIdx]; - for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[topIdx]; ++ssIdx) - { - int instId = site_next_pq_sig[topSigId + ssIdx]; - - if (inst_curr_detSite[instId] == INVALID && scoreImprov >= inst_next_bestScoreImprov[instId]) - { - mtx.lock(); - if (scoreImprov == inst_next_bestScoreImprov[instId]) - { - if (siteId < inst_next_bestSite[instId]) - { - inst_next_bestSite[instId] = siteId; - } - } - else if (scoreImprov > inst_next_bestScoreImprov[instId]) - { - inst_next_bestSite[instId] = siteId; - inst_next_bestScoreImprov[instId] = scoreImprov; - } - mtx.unlock(); - } - } - } - } - return 0; -} - -//run DL Sync -template -int runDLSynchronize( - const int* node2fence_region_map, - const int* addr2site_map, - const int num_clb_sites, - const int SLICE_CAPACITY, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - const int num_nodes, - const int num_threads, - int* site_nbrGroup_idx, - int* site_nbrRanges_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_pq_validIdx, - int* site_curr_stable, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - int* inst_curr_detSite, - int* inst_curr_bestSite, - T* inst_curr_bestScoreImprov, - int* inst_next_detSite, - int* inst_next_bestSite, - T* inst_next_bestScoreImprov, - int* activeStatus, - int* illegalStatus - ) -{ - //int numSites = num_sites_x * num_sites_y; - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); - - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - int sPQ = sIdx*SCL_IDX; - - site_curr_stable[sIdx] = site_next_stable[sIdx]; - - int curr_scl_size = site_curr_scl_idx[sIdx]; - site_curr_scl_idx[sIdx] = 0; - - int sclCount(0); - //Include valid entries of site_next_scl to site_curr_scl - if (site_next_scl_idx[sIdx] > 0) - { - for (int id = 0; id < SCL_IDX; ++id) - { - int vIdx = sPQ+id; - if (site_next_scl_validIdx[vIdx] != INVALID) - { - int currId(sPQ+site_curr_scl_idx[sIdx]); - - site_curr_scl_validIdx[currId] = 1; - site_curr_scl_siteId[currId] = site_next_scl_siteId[vIdx]; - site_curr_scl_score[currId] = site_next_scl_score[vIdx]; - site_curr_scl_sig_idx[currId] = site_next_scl_sig_idx[vIdx]; - - int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); - int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); - int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); - int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); - - for (int sg = 0; sg < site_next_scl_sig_idx[vIdx]; ++sg) - { - site_curr_scl_sig[currSGId + sg] = site_next_scl_sig[nxtSGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[currFFId + sg] = site_next_scl_impl_lut[nxtFFId + sg]; - site_curr_scl_impl_ff[currFFId + sg] = site_next_scl_impl_ff[nxtFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[currCKId + sg] = site_next_scl_impl_cksr[nxtCKId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[currCEId + sg] = site_next_scl_impl_ce[nxtCEId + sg]; - } - ++site_curr_scl_idx[sIdx]; - ++sclCount; - if (sclCount == site_next_scl_idx[sIdx]) - { - break; - } - } - } - } - - //Invalidate the rest in site_curr_scl - if (curr_scl_size > site_next_scl_idx[sIdx]) - { - for (int ckId = site_curr_scl_idx[sIdx]; ckId < SCL_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - if (site_curr_scl_validIdx[vIdx] != INVALID) - { - site_curr_scl_validIdx[vIdx] = INVALID; - site_curr_scl_sig_idx[vIdx] = 0; - site_curr_scl_siteId[vIdx] = INVALID; - site_curr_scl_score[vIdx] = 0.0; - ++sclCount; - if (sclCount == curr_scl_size) - { - break; - } - } - } - } - - int curr_pq_size = site_curr_pq_idx[sIdx]; - site_curr_pq_idx[sIdx] = 0; - site_curr_pq_top_idx[sIdx] = INVALID; - - sPQ = sIdx*PQ_IDX; - sclCount = 0; - //Include valid entries of site_next_pq to site_curr_pq - if (site_next_pq_idx[sIdx] > 0) - { - for (int id = 0; id < PQ_IDX; ++id) - { - int vIdx = sPQ+id; - if (site_next_pq_validIdx[vIdx] != INVALID) - { - int currId(sPQ+site_curr_pq_idx[sIdx]); - - site_curr_pq_validIdx[currId] = 1; - site_curr_pq_siteId[currId] = site_next_pq_siteId[vIdx]; - site_curr_pq_score[currId] = site_next_pq_score[vIdx]; - site_curr_pq_sig_idx[currId] = site_next_pq_sig_idx[vIdx]; - - int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); - int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); - int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); - int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); - - for (int sg = 0; sg < site_next_pq_sig_idx[vIdx]; ++sg) - { - site_curr_pq_sig[currSGId + sg] = site_next_pq_sig[nxtSGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_pq_impl_lut[currFFId + sg] = site_next_pq_impl_lut[nxtFFId + sg]; - site_curr_pq_impl_ff[currFFId + sg] = site_next_pq_impl_ff[nxtFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_pq_impl_cksr[currCKId + sg] = site_next_pq_impl_cksr[nxtCKId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_pq_impl_ce[currCEId + sg] = site_next_pq_impl_ce[nxtCEId + sg]; - } - if (id == site_next_pq_top_idx[sIdx]) - { - site_curr_pq_top_idx[sIdx] = site_curr_pq_idx[sIdx]; - } - ++site_curr_pq_idx[sIdx]; - ++sclCount; - if (sclCount == site_next_pq_idx[sIdx]) - { - break; - } - } - } - } - - //Invalidate the rest in site_curr_pq - if (curr_pq_size > site_next_pq_idx[sIdx]) - { - for (int ckId = site_curr_pq_idx[sIdx]; ckId < PQ_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - if (site_curr_pq_validIdx[vIdx] != INVALID) - { - site_curr_pq_validIdx[vIdx] = INVALID; - site_curr_pq_sig_idx[vIdx] = 0; - site_curr_pq_siteId[vIdx] = INVALID; - site_curr_pq_score[vIdx] = 0.0; - ++sclCount; - if (sclCount == curr_pq_size) - { - break; - } - } - } - } - - //clear site_next_scl - sPQ = sIdx*SCL_IDX; - //sclCount = 0; - for (int ckId = 0; ckId < SCL_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - //if (site_next_scl_validIdx[vIdx] != INVALID) - //{ - //Clear contents thoroughly - clear_cand_contents( - vIdx, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce); - - site_next_scl_validIdx[vIdx] = INVALID; - site_next_scl_sig_idx[vIdx] = 0; - site_next_scl_siteId[vIdx] = INVALID; - site_next_scl_score[vIdx] = 0.0; - //++sclCount; - //if (sclCount == site_next_scl_idx[sIdx]) - //{ - // break; - //} - //} - } - site_next_scl_idx[sIdx] = 0; - - activeStatus[addr2site_map[sIdx]] = (site_curr_pq_idx[sIdx] > 0 || site_curr_scl_idx[sIdx] > 0 || - site_nbrGroup_idx[sIdx] < numNbrGroups) ? 1 : 0; - } - - chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for(int nIdx = 0; nIdx < num_nodes; ++nIdx) - { - if ((node2fence_region_map[nIdx] == lutId || node2fence_region_map[nIdx] == ffId) && inst_curr_detSite[nIdx] == INVALID) //Only LUT/FF - { - //inst.curr = inst.next - inst_curr_detSite[nIdx] = inst_next_detSite[nIdx]; - inst_curr_bestSite[nIdx] = inst_next_bestSite[nIdx]; - inst_curr_bestScoreImprov[nIdx] = inst_next_bestScoreImprov[nIdx]; - - inst_next_bestSite[nIdx] = INVALID; - inst_next_bestScoreImprov[nIdx] = -10000.0; - - illegalStatus[nIdx] = (inst_curr_detSite[nIdx] == INVALID) ? 1 : 0; - } - } - return 0; -} - - -//run ripup and greedy legalization -template -int ripUp_Greedy_LG( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_weights, - const T* net_bbox, - const T* inst_areas, - const T* wlPrecond, - const T* site_xy, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* spiral_accessor, - const int* node2fence_region_map, - const int* lut_type, - const int* site_types, - const int* node2outpinIdx_map, - const int* node2pincount, - const int* net2pincount, - const int* pin2net_map, - const int* pin2node_map, - const int* pin_typeIds, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* flat_net2pin_start_map, - int* flat_node2prclstrCount, - int* flat_node2precluster_map, - const int* sorted_node_map, - const int* sorted_node_idx, - const int* sorted_net_map, - const int* addr2site_map, - const int* site2addr_map, - const int* sites_with_special_nodes, - const int* special_nodes, - const T nbrDistEnd, - const T xWirelenWt, - const T yWirelenWt, - const T extNetCountWt, - const T wirelenImprovWt, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int num_nodes, - const int num_sites_x, - const int num_sites_y, - const int num_clb_sites, - const int spiralBegin, - const int spiralEnd, - const int half_ctrl_mode, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int netShareScoreMaxNetDegree, - const int wlScoreMaxNetDegree, - const int ripupExpansion, - const int greedyExpansion, - const int SIG_IDX, - const int lutId, - const int ffId, - const int sliceId, - int* inst_curr_detSite, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* site_det_siteId, - T* site_det_score - ) -{ - ////DBG - //std::cout << "RipUP & Greedy LG for " << num_nodes << " nodes" << std::endl; - //char collect_info = 0; - ////DBG - - int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); - for (int i = 0; i < num_nodes; ++i) - { - ////DBG - //std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - ////DBG - - //////DBG - //std::string dbgMsg = ""; - //////DBG - - const int instId = sorted_node_idx[i]; //Remaining insts sorted based on decreasing area - if (inst_curr_detSite[instId] != INVALID) continue; - int instPcl = instId*3; - - //RipUpCandidates - std::vector > ripUpCandidates; - - int xLo = DREAMPLACE_STD_NAMESPACE::max(pos_x[instId] - nbrDistEnd, T(0)); - int yLo = DREAMPLACE_STD_NAMESPACE::max(pos_y[instId] - nbrDistEnd, T(0)); - int xHi = DREAMPLACE_STD_NAMESPACE::min(pos_x[instId] + nbrDistEnd, T(num_sites_x-1)); - int yHi = DREAMPLACE_STD_NAMESPACE::min(pos_y[instId] + nbrDistEnd, T(num_sites_y-1)); - - for (int x = xLo; x <= xHi; ++x) - { - for (int y = yLo; y <= yHi; ++y) - { - int siteId = x*num_sites_y + y; - int sIdx = site2addr_map[siteId]; - if ((node2fence_region_map[instId] == lutId || - node2fence_region_map[instId] == ffId) && - site_types[siteId] == sliceId && sites_with_special_nodes[sIdx] != 1) - { - int slocId = siteId*2; - T dist = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[slocId]) + DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[slocId+1]); - if (dist < nbrDistEnd) - { - RipUpCand rpCand; - rpCand.reset(); - - rpCand.siteId = siteId; - rpCand.cand.score = site_det_score[sIdx]; - rpCand.cand.siteId = site_det_siteId[sIdx]; - - rpCand.cand.sigIdx = site_det_sig_idx[sIdx]; - - /// - int sdSGId = sIdx*SIG_IDX; - int sdLutId = sIdx*SLICE_CAPACITY; - int sdCKId = sIdx*CKSR_IN_CLB; - int sdCEId = sIdx*CE_IN_CLB; - - for (int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - rpCand.cand.sig[sg] = site_det_sig[sdSGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - rpCand.cand.impl_lut[sg] = site_det_impl_lut[sdLutId + sg]; - rpCand.cand.impl_ff[sg] = site_det_impl_ff[sdLutId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - rpCand.cand.impl_cksr[sg] = site_det_impl_cksr[sdCKId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - rpCand.cand.impl_ce[sg] = site_det_impl_ce[sdCEId + sg]; - } - /// - - rpCand.legal = add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, - half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - BLE_CAPACITY, NUM_BLE_PER_SLICE, rpCand.cand.impl_lut, rpCand.cand.impl_ff, - rpCand.cand.impl_cksr, rpCand.cand.impl_ce); - - if (rpCand.legal) - { - /// - bool addInstToSig = add_inst_to_sig(flat_node2prclstrCount[instId], - flat_node2precluster_map, instPcl, - rpCand.cand.sig, rpCand.cand.sigIdx, SLICE_CAPACITY); - //DBG - if (!addInstToSig) - { - std::cout << "ERROR: Unable to add inst: " << instId << " to sig" << std::endl; - } - //DBG - - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, - rpCand.cand.sig, rpCand.cand.siteId, rpCand.cand.sigIdx, - rpCand.cand.score); - - rpCand.score = rpCand.cand.score - site_det_score[sIdx]; - - } else - { - T area = inst_areas[site_det_sig[sdSGId]]; - for (int sInst = 1; sInst < site_det_sig_idx[sIdx]; ++sInst) - { - area += inst_areas[site_det_sig[sdSGId + sInst]]; - } - T wirelenImprov(0.0); - int pStart = flat_node2pin_start_map[instId]; - int pEnd = flat_node2pin_start_map[instId+1]; - for (int pId = pStart; pId < pEnd; ++pId) - { - int pinId = flat_node2pin_map[pId]; - int netId = pin2net_map[pinId]; - if (net2pincount[netId] <= wlScoreMaxNetDegree) - { - compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, net_weights, net_pinIdArrayX, net_pinIdArrayY, flat_net2pin_start_map, pin2node_map, net2pincount, site_xy, xWirelenWt, yWirelenWt, netId, siteId, std::vector{pinId}, wirelenImprov); - } - } - rpCand.score = wirelenImprovWt * wirelenImprov - site_det_score[sIdx] - area; - } - ripUpCandidates.emplace_back(rpCand); - } - } - } - } - - //Sort ripup candidate indices based on legal and score - if (ripUpCandidates.size() > 1) - { - std::sort(ripUpCandidates.begin(), ripUpCandidates.end()); - } - - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Created " + std::to_string(ripUpCandidates.size()) + " RipUPCandidates for Inst: "; - // dbgMsg += std::to_string(instId) + " of type: " + std::to_string(node2fence_region_map[instId]) + "\n"; - // if (node2fence_region_map[instId] == ffId) - // { - // int clInstCKSR = flop2ctrlSetId_map[instId]*3 + 1; - // int clInstCE = flop2ctrlSetId_map[instId]*3 + 2; - // dbgMsg += "FF Node " + std::to_string(instId) + " has clk: " + std::to_string(flop_ctrlSets[clInstCKSR]); - // dbgMsg += " and ce: " + std::to_string(flop_ctrlSets[clInstCE]) + "\n"; - // } else - // { - // dbgMsg += "LUT Node " + std::to_string(instId) + " is of type: " + std::to_string(lut_type[instId]) + "\n"; - // } - //} - //////DBG - - int ripupLegalizeInst(INVALID); - int greedyLegalizeInst(INVALID); - - for (const auto &ripUpCd : ripUpCandidates) - { - int stId = ripUpCd.siteId; - int stAdId = site2addr_map[stId]; - - int sdSGId = stAdId*SIG_IDX; - int sdLutId = stAdId*SLICE_CAPACITY; - int sdCKId = stAdId*CKSR_IN_CLB; - int sdCEId = stAdId*CE_IN_CLB; - - if (ripUpCd.legal) - { - site_det_score[stAdId] = ripUpCd.cand.score; - site_det_siteId[stAdId] = ripUpCd.cand.siteId; - site_det_sig_idx[stAdId] = ripUpCd.cand.sigIdx; - - for (auto sg = 0; sg < ripUpCd.cand.sigIdx; ++sg) - { - site_det_sig[sdSGId + sg] = ripUpCd.cand.sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdLutId + sg] = ripUpCd.cand.impl_lut[sg]; - site_det_impl_ff[sdLutId + sg] = ripUpCd.cand.impl_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdCKId + sg] = ripUpCd.cand.impl_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdCEId + sg] = ripUpCd.cand.impl_ce[sg]; - } - /// - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - inst_curr_detSite[clInstId] = ripUpCd.siteId; - } - - ripupLegalizeInst = 1; - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Found legal RipUPCandidate for Inst: " + std::to_string(instId) + " at site: " + std::to_string(ripUpCd.siteId) +"\n"; - //} - //////DBG - } else - { - //////DBG - //if (collect_info) - //{ - // dbgMsg += "RipUP Site " + std::to_string(site_det_siteId[stAdId]) + " for Inst: " + std::to_string(instId) + "\n"; - //} - //////DBG - int ripupSiteLegalizeInst(INVALID); - - std::vector > dets; - dets.reserve(site_det_sig_idx[stAdId]); - - Candidate tCand; - tCand.reset(); - tCand.score = site_det_score[stAdId]; - tCand.siteId = site_det_siteId[stAdId]; - tCand.sigIdx = site_det_sig_idx[stAdId]; - - for(int sg = 0; sg < tCand.sigIdx; ++sg) - { - tCand.sig[sg] = site_det_sig[sdSGId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - tCand.impl_lut[sg] = site_det_impl_lut[sdLutId+ sg]; - tCand.impl_ff[sg] = site_det_impl_ff[sdLutId+ sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - tCand.impl_cksr[sg] = site_det_impl_cksr[sdCKId+ sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - tCand.impl_ce[sg] = site_det_impl_ce[sdCEId+ sg]; - } - dets.emplace_back(tCand); - tCand.reset(); - - //Rip Up site - for(int sg = 0; sg < site_det_sig_idx[stAdId]; ++sg) - { - inst_curr_detSite[site_det_sig[sdSGId + sg]] = INVALID; - } - - //Clear contents of site_det_sig - clear_cand_contents(stAdId, SIG_IDX, SLICE_CAPACITY, CKSR_IN_CLB, - CE_IN_CLB, site_det_sig_idx, site_det_sig, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce); - - site_det_sig_idx[stAdId] = flat_node2prclstrCount[instId]; - tCand.sigIdx = flat_node2prclstrCount[instId]; - - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - site_det_sig[sdSGId + idx] = clInstId; - tCand.sig[idx] = clInstId; - } - /// - - if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - special_nodes, lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, - half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - BLE_CAPACITY, NUM_BLE_PER_SLICE, tCand.impl_lut, tCand.impl_ff, - tCand.impl_cksr, tCand.impl_ce)) - { - /// - site_det_sig_idx[stAdId] = tCand.sigIdx; - - for (int sg = 0; sg < tCand.sigIdx; ++sg) - { - site_det_sig[sdSGId + sg] = tCand.sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdLutId+ sg] = tCand.impl_lut[sg]; - site_det_impl_ff[sdLutId+ sg] = tCand.impl_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdCKId+ sg] = tCand.impl_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdCEId+ sg] = tCand.impl_ce[sg]; - } - /// - } else - { - //Should not reach here - std::cout << "ERROR: Could not add " << instId << " (inst: " << instId << ") of type " << node2fence_region_map[instId] << " to site: " << stId << std::endl; - } - - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, - tCand.sig, stId, tCand.sigIdx, site_det_score[stAdId]); - - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInst = flat_node2precluster_map[instPcl + idx]; - inst_curr_detSite[clInst] = stId; - } - - int sig[SIG_MAX_CAP]; - int tmp_sigIdx = dets[0].sigIdx; - for (int sg = 0; sg < dets[0].sigIdx; ++sg) - { - sig[sg] = dets[0].sig[sg]; - } - - for (int rIdx = 0; rIdx < tmp_sigIdx; ++rIdx) - { - int ruInst = sig[rIdx]; - if (inst_curr_detSite[ruInst] != INVALID) - { - continue; - } - int beg = spiralBegin; - int r = DREAMPLACE_STD_NAMESPACE::ceil(nbrDistEnd + 1.0); - int end = r ? 2 * (r + 1) * r + 1 : 1; - - int ruInstPcl = flat_node2precluster_map[ruInst*3]; - T cenX(pos_x[ruInstPcl]), cenY(pos_y[ruInstPcl]); - - if (flat_node2prclstrCount[ruInst] > 1) - { - cenX *= wlPrecond[ruInstPcl]; - cenY *= wlPrecond[ruInstPcl]; - T totalWt = wlPrecond[ruInstPcl]; - - for (int idx = 1; idx < flat_node2prclstrCount[ruInst]; ++idx) - { - int clInst = flat_node2precluster_map[ruInst*3 + idx]; - cenX += pos_x[clInst] * wlPrecond[clInst]; - cenY += pos_y[clInst] * wlPrecond[clInst]; - totalWt += wlPrecond[clInst]; - } - if (totalWt > 0) - { - cenX /= totalWt; - cenY /= totalWt; - } - } - - //BestCandidate - Candidate bestCand; - bestCand.reset(); - T bestScoreImprov(-10000.0); - - for (int spId = beg; spId < end; ++spId) - { - int slocIdx = spId*2; - int xVal = cenX + spiral_accessor[slocIdx]; - int yVal = cenY + spiral_accessor[slocIdx + 1]; - - int siteMapId = xVal * num_sites_y + yVal; - - //Check within bounds - if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) - { - continue; - } - if (site_types[siteMapId] == sliceId && - (node2fence_region_map[ruInst] == lutId || - node2fence_region_map[ruInst] == ffId)) - { - int siteMapAIdx = site2addr_map[siteMapId]; - - Candidate cand; - cand.reset(); - cand.score = site_det_score[siteMapAIdx]; - cand.siteId = site_det_siteId[siteMapAIdx]; - cand.sigIdx = site_det_sig_idx[siteMapAIdx]; - - //array instantiation - int sdId(siteMapAIdx*SIG_IDX), sdlutId(siteMapAIdx*SLICE_CAPACITY); - int sdckId(siteMapAIdx*CKSR_IN_CLB), sdceId(siteMapAIdx*CE_IN_CLB); - - for(int sg = 0; sg < cand.sigIdx; ++sg) - { - cand.sig[sg] = site_det_sig[sdId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - cand.impl_lut[sg] = site_det_impl_lut[sdlutId + sg]; - cand.impl_ff[sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - cand.impl_cksr[sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - cand.impl_ce[sg] = site_det_impl_ce[sdceId + sg]; - } - ///// - - if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, node2pincount, net2pincount, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - node2fence_region_map, flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, - special_nodes, lutTypeInSliceUnit, lut_maxShared, ruInst, lutId, ffId, - half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - BLE_CAPACITY, NUM_BLE_PER_SLICE, cand.impl_lut, cand.impl_ff, cand.impl_cksr, - cand.impl_ce) && - add_inst_to_sig(flat_node2prclstrCount[ruInst], flat_node2precluster_map, - ruInst*3, cand.sig, cand.sigIdx, SLICE_CAPACITY)) - { - // Adding the instance to the site is legal - // If this is the first legal position found, set the expansion search radius - if (bestScoreImprov == -10000.0) - { - int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[slocIdx]) + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[slocIdx+ 1]); - r += ripupExpansion; - - int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); - int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); - end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; - } - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, - cand.sig, cand.siteId, cand.sigIdx, cand.score); - - T scoreImprov = cand.score - site_det_score[siteMapAIdx]; - if (scoreImprov > bestScoreImprov) - { - bestCand = cand; - bestScoreImprov = scoreImprov; - } - } - } - - } - if (bestCand.siteId == INVALID) - { - // Cannot find a legal position for this rip-up instance, so moving the instance to the site is illegal - // - // Revert all affected sites' clusters - for (auto rit = dets.rbegin(); rit != dets.rend(); ++rit) - { - int sId = rit->siteId; - int sAId = site2addr_map[sId]; - int sdId(sAId*SIG_IDX), sdlutId(sAId*SLICE_CAPACITY); - int sdckId(sAId*CKSR_IN_CLB), sdceId(sAId*CE_IN_CLB); - - site_det_score[sAId] = rit->score; - site_det_siteId[sAId] = sId; - site_det_sig_idx[sAId] = rit->sigIdx; - - for(int sg = 0; sg < site_det_sig_idx[sAId]; ++sg) - { - site_det_sig[sdId + sg] = rit->sig[sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = rit->impl_lut[sg]; - site_det_impl_ff[sdlutId + sg] = rit->impl_ff[sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = rit->impl_cksr[sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = rit->impl_ce[sg]; - } - } - // Move all ripped instances back to their original sites - int sdId(stAdId*SIG_IDX); - for (int sg = 0; sg < site_det_sig_idx[stAdId]; ++sg) - { - int sdInst = site_det_sig[sdId + sg]; - inst_curr_detSite[sdInst] = stId; - } - // Set the instance as illegal - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int prclInst = flat_node2precluster_map[instPcl + idx]; - inst_curr_detSite[prclInst] = INVALID; - } - - ripupSiteLegalizeInst = 0; - break; - } else - { - int sbId = bestCand.siteId; - int sbAId = site2addr_map[sbId]; - - Candidate tCand; - tCand.reset(); - /// - tCand.score = site_det_score[sbAId]; - tCand.siteId = site_det_siteId[sbAId]; - tCand.sigIdx = site_det_sig_idx[sbAId]; - - int sbSGId = sbAId*SIG_IDX; - int sbLutId = sbAId*SLICE_CAPACITY; - int sbCKId = sbAId*CKSR_IN_CLB; - int sbCEId = sbAId*CE_IN_CLB; - - /// - for(int sg = 0; sg < tCand.sigIdx; ++sg) - { - tCand.sig[sg] = site_det_sig[sbSGId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - tCand.impl_lut[sg] = site_det_impl_lut[sbLutId+ sg]; - tCand.impl_ff[sg] = site_det_impl_ff[sbLutId+ sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - tCand.impl_cksr[sg] = site_det_impl_cksr[sbCKId+ sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - tCand.impl_ce[sg] = site_det_impl_ce[sbCEId+ sg]; - } - dets.emplace_back(tCand); - - //Move ripped instances to this site - site_det_score[sbAId] = bestCand.score; - site_det_siteId[sbAId] = bestCand.siteId; - site_det_sig_idx[sbAId] = bestCand.sigIdx; - - for(auto sg = 0; sg < bestCand.sigIdx; ++sg) - { - site_det_sig[sbSGId + sg] = bestCand.sig[sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sbLutId+ sg] = bestCand.impl_lut[sg]; - site_det_impl_ff[sbLutId+ sg] = bestCand.impl_ff[sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sbCKId+ sg] = bestCand.impl_cksr[sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sbCEId+ sg] = bestCand.impl_ce[sg]; - } - /// - for (int idx = 0; idx < flat_node2prclstrCount[ruInst]; ++idx) - { - int clInst = flat_node2precluster_map[ruInst*3 + idx]; - inst_curr_detSite[clInst] = sbId; - } - } - } - //Ensure instance is legalized to a site for ripup LG to be successful - if (ripupSiteLegalizeInst == INVALID && inst_curr_detSite[instId] != INVALID) - { - ripupLegalizeInst = 1; - } - } - if (ripupLegalizeInst == 1) break; - } - - //Greedy Legalization if RipUP LG failed - if (ripupLegalizeInst != 1) - { - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Starting greedy LG for Inst: " + std::to_string(instId) + " with spiralBegin: "; - // dbgMsg += std::to_string(spiralBegin) + " and end: " + std::to_string(spiralEnd) + "\n"; - //} - //////DBG - inst_curr_detSite[instId] = INVALID; - - int beg(spiralBegin), end(spiralEnd); - - T cenX(pos_x[flat_node2precluster_map[instPcl]] * wlPrecond[flat_node2precluster_map[instPcl]]); - T cenY(pos_y[flat_node2precluster_map[instPcl]] * wlPrecond[flat_node2precluster_map[instPcl]]); - T totalWt(wlPrecond[flat_node2precluster_map[instPcl]]); - - for (int cl = 1; cl < flat_node2prclstrCount[instId]; ++cl) - { - int pclInst = flat_node2precluster_map[instPcl + cl]; - cenX += pos_x[pclInst] * wlPrecond[pclInst]; - cenY += pos_y[pclInst] * wlPrecond[pclInst]; - totalWt += wlPrecond[pclInst]; - } - - if (totalWt > 0) - { - cenX /= totalWt; - cenY /= totalWt; - } - - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Greedy LG: Centroid of Inst: " + std::to_string(instId) + " = (" + std::to_string(cenX); - // dbgMsg += ", " + std::to_string(cenY) + ")" + "\n"; - //} - //////DBG - - //BestCandidate - Candidate bestCand; - bestCand.reset(); - T bestScoreImprov(-10000.0); - - for (int sIdx = beg; sIdx < end; ++sIdx) - { - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Consider " + std::to_string(sIdx) + " within [" + std::to_string(beg) + ", " + std::to_string(end); - // dbgMsg += ") \n"; - //} - //////DBG - - int saIdx = sIdx*2; - int xVal = cenX + spiral_accessor[saIdx]; - int yVal = cenY + spiral_accessor[saIdx + 1]; - int siteMapIdx = xVal * num_sites_y + yVal; - int siteMapAIdx = site2addr_map[siteMapIdx]; - - //Check within bounds - if (xVal < 0 || xVal >= num_sites_x || yVal < 0 || yVal >= num_sites_y) - { - continue; - } - - if (site_types[siteMapIdx] == sliceId && (node2fence_region_map[instId] == lutId || - node2fence_region_map[instId] == ffId) && site_det_sig_idx[siteMapAIdx] < 2*SLICE_CAPACITY) - { - //////DBG - //if (collect_info) - //{ - // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId); - // dbgMsg += " consider site at (" + std::to_string(xVal) + ", " + std::to_string(yVal) + ") "; - // dbgMsg += std::to_string(siteMapIdx) + "\n"; - //} - //////DBG - - ////DBG - //std::cout << "Greedy LG: Inst: " << instId << " site addr map: " << siteMapAIdx << std::endl; - ////DBG - Candidate cand; - cand.reset(); - cand.score = site_det_score[siteMapAIdx]; - cand.siteId = site_det_siteId[siteMapAIdx]; - cand.sigIdx = site_det_sig_idx[siteMapAIdx]; - - int sdId(siteMapAIdx*SIG_IDX), sdlutId(siteMapAIdx*SLICE_CAPACITY); - int sdckId(siteMapAIdx*CKSR_IN_CLB), sdceId(siteMapAIdx*CE_IN_CLB); - - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Greedy LG: Inst: " + std::to_string(instId) + " site det sig element count: " + std::to_string(site_det_sig_idx[siteMapAIdx]) + "\n"; - // dbgMsg += "site det sig contains: "; - // for(int sg = 0; sg < site_det_sig_idx[siteMapAIdx]; ++sg) - // { - // dbgMsg += std::to_string(site_det_sig[sdId + sg]) + " "; - // } - // dbgMsg += "\nsite det impl lut contains: "; - // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // dbgMsg += std::to_string(site_det_impl_lut[sdlutId + sg]) + " "; - // } - // dbgMsg += "\nsite det impl ff contains: "; - // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // dbgMsg += std::to_string(site_det_impl_ff[sdlutId + sg]) + " "; - // } - // dbgMsg += "\n"; - // dbgMsg += "\nsite det impl cksr contains: "; - // for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - // { - // dbgMsg += std::to_string(site_det_impl_cksr[sdckId + sg]) + " "; - // } - // dbgMsg += "\n"; - // dbgMsg += "\nsite det impl ce contains: "; - // for(int sg = 0; sg < CE_IN_CLB; ++sg) - // { - // dbgMsg += std::to_string(site_det_impl_ce[sdceId + sg]) + " "; - // } - // dbgMsg += "\n"; - //} - //////DBG - - for(int sg = 0; sg < site_det_sig_idx[siteMapAIdx]; ++sg) - { - cand.sig[sg] = site_det_sig[sdId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - cand.impl_lut[sg] = site_det_impl_lut[sdlutId + sg]; - cand.impl_ff[sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - cand.impl_cksr[sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - cand.impl_ce[sg] = site_det_impl_ce[sdceId + sg]; - } - ///// - - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Greedy LG: Inst: " + std::to_string(instId) + " consider siteID: " + std::to_string(siteMapIdx) + " - created candidate" + "\n"; - //} - //////DBG - - if (add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, flat_node2pin_map, - node2pincount, net2pincount, pin2net_map, pin_typeIds, flat_node2prclstrCount, - flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, flop_ctrlSets, - extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, lutTypeInSliceUnit, lut_maxShared, - instId, lutId, ffId, half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - BLE_CAPACITY, NUM_BLE_PER_SLICE, cand.impl_lut, cand.impl_ff, cand.impl_cksr, cand.impl_ce) && - add_inst_to_sig(flat_node2prclstrCount[instId], flat_node2precluster_map, - instPcl, cand.sig, cand.sigIdx, SLICE_CAPACITY)) - { - //////DBG - //if (collect_info) - //{ - // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId) + " - possible location at (" + std::to_string(xVal) + ", " + std::to_string(yVal) + ") " + std::to_string(siteMapIdx) + "\n"; - //} - //////DBG - - // Adding the instance to the site is legal - // If this is the first legal position found, set the expansion search radius - if (bestScoreImprov == -10000.0) - { - int r = DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[saIdx]) + DREAMPLACE_STD_NAMESPACE::abs(spiral_accessor[saIdx + 1]); - r += greedyExpansion; - - int maxRad = DREAMPLACE_STD_NAMESPACE::max(num_sites_x, num_sites_y); - int nwR = DREAMPLACE_STD_NAMESPACE::min(maxRad, r); - end = nwR ? 2 * (nwR + 1) * nwR + 1 : 1; - } - //cand_score = computeCandidateScore(cand); - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlScoreMaxNetDegree, half_ctrl_mode, - cand.sig, cand.siteId, cand.sigIdx, cand.score); - - T scoreImprov = cand.score - site_det_score[siteMapAIdx]; - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Cand site Id: " + std::to_string(cand.siteId) + " and cand sig Idx: " + std::to_string(cand.sigIdx) + "\n"; - // dbgMsg += std::to_string(sIdx) + "- Greedy LG Inst: " + std::to_string(instId) + " cand score: " + std::to_string(cand.score) + ", site_det_score: " + std::to_string(site_det_score[siteMapAIdx]) + " and scoreImprov: "; - // dbgMsg += std::to_string(scoreImprov) + " with bestScoreImprov " + std::to_string(bestScoreImprov) + "\n"; - //} - //////DBG - - if (scoreImprov > bestScoreImprov) - { - //std::cout << "Found best candidate for " << idx << std::endl; - bestCand = cand; - bestScoreImprov = scoreImprov; - } - } - - //////DBG - //if (collect_info) - //{ - // dbgMsg += "Greedy LG Inst of " + std::to_string(instId) + " has bestCand site: " + std::to_string(bestCand.siteId) + " with bestScoreImprov " + std::to_string(bestScoreImprov) + "\n"; - //} - //////DBG - } - } - - // Commit the found best legal solution - if (bestCand.siteId != INVALID) - { - int stId = bestCand.siteId; - int stAId = site2addr_map[stId]; - - site_det_score[stAId] = bestCand.score; - site_det_siteId[stAId] = bestCand.siteId; - site_det_sig_idx[stAId] = bestCand.sigIdx; - - int sdId(stAId*SIG_IDX), sdlutId(stAId*SLICE_CAPACITY); - int sdckId(stAId*CKSR_IN_CLB), sdceId(stAId*CE_IN_CLB); - - for (auto sg = 0; sg < bestCand.sigIdx; ++sg) - { - site_det_sig[sdId + sg] = bestCand.sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = bestCand.impl_lut[sg]; - site_det_impl_ff[sdlutId + sg] = bestCand.impl_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = bestCand.impl_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = bestCand.impl_ce[sg]; - } - ///// - - for (int cl = 0; cl < flat_node2prclstrCount[instId]; ++cl) - { - int prclInst = flat_node2precluster_map[instPcl + cl]; - inst_curr_detSite[prclInst] = stId; - } - greedyLegalizeInst = 1; - - } else - { - greedyLegalizeInst = 0; - } - } - - if (ripupLegalizeInst != 1 && greedyLegalizeInst != 1) - { - - if (node2fence_region_map[instId] == ffId) - { - dreamplacePrint(kERROR, "unable to legalize FF inst: %u \n", instId); - } else - { - dreamplacePrint(kERROR, "unable to legalize LUT%s inst: %u \n", lut_type[instId], instId); - } - - //DBG - //if (collect_info) - //{ - //std::cout << dbgMsg << std::endl; - //} - //DBG - break; - } - ////DBG - //std::chrono::steady_clock::time_point pt4= std::chrono::steady_clock::now(); - //std::cout << i << ": inst " << instId << " of type " - // << node2fence_region_map[instId] << " RipUP&Greedy LG takes " - // << std::chrono::duration_cast(pt4-begin).count()/1000000.0 - // << " (s)" << std::endl; - ////DBG - } - - return 0; -} - -// slot assignment -template -int slotAssign( - const T* pos_x, - const T* pos_y, - const T* wlPrecond, - const T* site_xy, - const int* flop_ctrlSets, - const int* flop2ctrlSetId_map, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* pin2net_map, - const int* pin2node_map, - const int* node2pincount, - const int* net2pincount, - const int* node2outpinIdx_map, - const int* node2fence_region_map, - const int* pin_typeIds, - const int* lut_type, - const int* site_types, - const int* sorted_net_map, - const int* addr2site_map, - const int* sites_with_special_nodes, - const T slotAssignFlowWeightScale, - const T slotAssignFlowWeightIncr, - const int num_sites_x, - const int num_sites_y, - const int num_clb_sites, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int NUM_BLE_PER_HALF_SLICE, - const int half_ctrl_mode, - const int lutId, - const int num_threads, - int* site_det_sig_idx, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* site_det_siteId - ) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_clb_sites / num_threads / 16), 1); - -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - if (site_det_sig_idx[sIdx] > 0) - { - int siteId = addr2site_map[sIdx]; - - //initSlotAssign - int sdlutId(sIdx*SLICE_CAPACITY); - int sdckId(sIdx*CKSR_IN_CLB), sdceId(sIdx*CE_IN_CLB); - - //Fixed FF ctrls - if (half_ctrl_mode == 1) - { - std::vector lut, ff; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - if (site_det_impl_lut[sdlutId + sg] != INVALID) - { - lut.push_back(site_det_impl_lut[sdlutId + sg]); - } - if (site_det_impl_ff[sdlutId + sg] != INVALID) - { - ff.push_back(site_det_impl_ff[sdlutId + sg]); - } - } - - // Pre-assign control sets - // Note that the original impl FF assignment is feasible but it is optimized for minimum resource usage - // Therefore, to enlarge the solution space exploration, we do following modifications based on the original control set assignment: - // (1) if ce[1] (ce[3]) is empty, we set it to ce[0] (ce[2]), - // (2) if (cksr[1], ce[2], ce[3]) are empty, we set them to (cksr[0], ce[0], ce[1]) - int cksr[CKSR_MAX_CAP]; - int ce[CE_MAX_CAP]; - - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - cksr[sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - ce[sg] = site_det_impl_ce[sdceId + sg]; - } - - if (ce[1] == INVALID) - { - ce[1] = ce[0]; - } - if (ce[3] == INVALID) - { - ce[3] = ce[2]; - } - if (cksr[1] == INVALID) - { - cksr[1] = cksr[0]; - ce[2] = ce[0]; - ce[3] = ce[1]; - } - - //computeLUTScoreAndScoreImprov - std::vector > bleS(lut.size()), bleP, bleLP; - - for (unsigned int i = 0; i < lut.size(); ++i) - { - auto &ble = bleS[i]; - ble.lut[0] = lut[i]; - ble.lut[1] = INVALID; - - subSlice tempBLE(ble); - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, - flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, - net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, - half_ctrl_mode, cksr[0], ce[0], ce[1], ble); - - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, - flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, - net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, - half_ctrl_mode, cksr[1], ce[2], ce[3], tempBLE); - - if (tempBLE.score > ble.score) - { - ble = tempBLE; - } - } - - bleP.clear(); - // Collect all feasible LUT pairs and compute their best scores and score improvement - for(unsigned int aIdx = 0; aIdx < lut.size(); ++aIdx) - { - const int lutA = lut[aIdx]; - for(unsigned int bIdx = aIdx + 1; bIdx < lut.size(); ++bIdx) - { - const int lutB = lut[bIdx]; - if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, lutTypeInSliceUnit, lut_maxShared, lutA, lutB)) - { - bleP.emplace_back(); - - auto &ble = bleP.back(); - ble.lut[0] = lutA; - ble.lut[1] = lutB; - - subSlice tempBLE(ble); - - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, - flat_node2pin_map, flat_net2pin_start_map, flat_net2pin_map, pin2net_map, - node2pincount, pin_typeIds, net2pincount, node2outpinIdx_map, pin2node_map, - sorted_net_map, lut_type, ff, half_ctrl_mode, cksr[0], ce[0], ce[1], ble); - - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, - flat_node2pin_map, flat_net2pin_start_map, flat_net2pin_map, pin2net_map, - node2pincount, pin_typeIds, net2pincount, node2outpinIdx_map, pin2node_map, - sorted_net_map, lut_type, ff, half_ctrl_mode, cksr[1], ce[2], ce[3], tempBLE); - - if (tempBLE.score > ble.score) - { - ble = tempBLE; - } - // We define the score improvement of a compatible LUT pair (a, b) as - // improv(a, b) = max(BLEScore(a, b, *, *)) - max(BLEScore(a, -, *, *)) - max(BLEScore(b, -, *, *)) - ble.improv = ble.score - bleS[aIdx].score - bleS[bIdx].score; - } - } - } - - //pairLUTs - pairLUTs(lut, bleP, bleS, slotAssignFlowWeightScale, slotAssignFlowWeightIncr, NUM_BLE_PER_SLICE, bleLP); - - //assignLUTsandFFs - // Reset the existing slot assignment in site.det - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = INVALID; - site_det_impl_ff[sdlutId + sg] = INVALID; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = INVALID; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = INVALID; - } - - std::vector scores; - scores.assign(NUM_BLE_PER_SLICE, 0.0); - - // Sort legal LUT/LUT pairs by their score from high to low - std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score > b.score; }); - - // Record the number of available LUT pair slots in low/high half of the slice - int availLo = NUM_BLE_PER_HALF_SLICE; - int availHi = NUM_BLE_PER_HALF_SLICE; - - // Assign LUTs one by one and determine thier best FFs at the same time - for(const auto &estBLE : bleLP) - { - subSlice bleLo(estBLE); - subSlice bleHi(estBLE); - - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, - flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, - net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, - half_ctrl_mode, cksr[0], ce[0], ce[1], bleLo); - - findBestFFs(flop_ctrlSets, flop2ctrlSetId_map, flat_node2pin_start_map, flat_node2pin_map, - flat_net2pin_start_map, flat_net2pin_map, pin2net_map, node2pincount, pin_typeIds, - net2pincount, node2outpinIdx_map, pin2node_map, sorted_net_map, lut_type, ff, - half_ctrl_mode, cksr[1], ce[2], ce[3], bleHi); - - // Try to fit the found BLE in the preferred feasible half slice - int lh = ((availLo && bleLo.score > bleHi.score) || !availHi ? 0 : 1); - const auto &ble = (lh ? bleHi : bleLo); - (lh ? availHi : availLo) -= 1; - - int beg = lh * HALF_SLICE_CAPACITY; - int end = beg + HALF_SLICE_CAPACITY; - - for (int idx = beg; idx < end; idx += BLE_CAPACITY) - { - int tpos = idx/SLICE_CAPACITY; - int pos = idx - int(tpos * SLICE_CAPACITY); - if (site_det_impl_lut[sdlutId + pos] == INVALID && site_det_impl_lut[sdlutId + pos + 1] == INVALID) - { - - // Realize LUT assignment - // Assign LUTs with more inputs at odd slots - // In this way we can also make sure that all LUT6 are assigned at odd positions - int demA = (ble.lut[0] == INVALID ? 0 : lut_type[ble.lut[0]]); - int demB = (ble.lut[1] == INVALID ? 0 : lut_type[ble.lut[1]]); - int flip = (demA > demB ? 1 : 0); - site_det_impl_lut[sdlutId + pos] = ble.lut[flip]; - site_det_impl_lut[sdlutId + pos + 1] = ble.lut[1 - flip]; - - // Realize FF assignment - for (int k : {0, 1}) - { - site_det_impl_ff[sdlutId + pos + k] = ble.ff[k]; - if (ble.ff[k] != INVALID) - { - const int ffId = ble.ff[k]; - site_det_impl_cksr[sdckId + lh]= flop_ctrlSets[flop2ctrlSetId_map[ffId]*3 + 1]; - site_det_impl_ce[sdceId + 2*lh + k]= flop_ctrlSets[flop2ctrlSetId_map[ffId]*3 + 2]; - // Remove the FF assigned from the active list - ff.erase(std::find(ff.begin(), ff.end(), ble.ff[k])); - - } - } - scores[pos / BLE_CAPACITY] = ble.score; - break; - } - } - } - - // Assign the rest of unassigned FFs - // - // We iteratively add one FF at a time - // Each time, we add the FF gives the best score improvement - while (!ff.empty()) - { - // Find the FF gives the best score improvement - int bestFFIdx(INVALID), bestPos(INVALID); - T bestImprov(-10000.0); - for(unsigned int ffIdx = 0; ffIdx < ff.size(); ++ffIdx) - { - int ffI = ff[ffIdx]; - int ffcksr = flop_ctrlSets[flop2ctrlSetId_map[ffI]*3 + 1]; - int ffce = flop_ctrlSets[flop2ctrlSetId_map[ffI]*3 + 2]; - - for (int pos = 0; pos < SLICE_CAPACITY; ++pos) - { - int lh = pos / HALF_SLICE_CAPACITY; - int toe = pos/BLE_CAPACITY; - int oe = pos - int(toe * BLE_CAPACITY); - - if (site_det_impl_ff[sdlutId + pos] == INVALID && ffcksr == cksr[lh] && ffce == ce[2 * lh + oe]) - { - int k = pos / BLE_CAPACITY * BLE_CAPACITY; - site_det_impl_ff[sdlutId + pos] = ffI; - T improv(0.0); - computeBLEScore(flat_node2pin_start_map, flat_node2pin_map, flat_net2pin_start_map, - flat_net2pin_map, pin2net_map, pin2node_map, node2outpinIdx_map, pin_typeIds, - lut_type, half_ctrl_mode, site_det_impl_lut[sdlutId + k], - site_det_impl_lut[sdlutId + k + 1], site_det_impl_ff[sdlutId + k], - site_det_impl_ff[sdlutId + k+1], improv); - - improv -= scores[k / BLE_CAPACITY]; - site_det_impl_ff[sdlutId + pos] = INVALID; - - if (improv > bestImprov) - { - bestFFIdx = ffIdx; - bestPos = pos; - bestImprov = improv; - } - } - } - } - - // Realize the best FF assignment found - int bestFF = ff[bestFFIdx]; - int lh = bestPos / HALF_SLICE_CAPACITY; - int toe = bestPos/BLE_CAPACITY; - int oe = bestPos - int(toe * BLE_CAPACITY); - site_det_impl_ff[sdlutId + bestPos] = bestFF; - site_det_impl_cksr[sdckId + lh] = flop_ctrlSets[flop2ctrlSetId_map[bestFF]*3 + 1]; - site_det_impl_ce[sdceId + 2 * lh + oe] = flop_ctrlSets[flop2ctrlSetId_map[bestFF]*3 + 2]; - - // Remove the best FF found from the active list - ff.erase(ff.begin() + bestFFIdx); - - // Update the BLE slot score - scores[bestPos/BLE_CAPACITY] += bestImprov; - } - - //order BLEs - // Sort BLEs in each half slice by their Y centroid coordinates (cen.y - site.y) - T siteY = site_xy[2*site_det_siteId[sIdx] + 1]; - for (int lh : {0, 1}) - { - bleLP.clear(); - - int beg = lh * HALF_SLICE_CAPACITY; - int end = beg + HALF_SLICE_CAPACITY; - for (int offset = beg; offset < end; offset += BLE_CAPACITY) - { - bleLP.emplace_back(); - auto &ble = bleLP.back(); - std::vector insts; - - for (int k : {0, 1}) - { - if (site_det_impl_lut[sdlutId + offset + k] != INVALID) - { - insts.push_back(site_det_impl_lut[sdlutId + offset + k]); - ble.lut[k] = site_det_impl_lut[sdlutId + offset + k]; - } - if (site_det_impl_ff[sdlutId + offset + k] != INVALID) - { - insts.push_back(site_det_impl_ff[sdlutId + offset + k]); - ble.ff[k] = site_det_impl_ff[sdlutId + offset + k]; - } - } - - // We use ble.score to store centroid.y - site.y of this BLE - if (insts.empty()) - { - ble.score = 0.0; - } else - { - //Centroid of insts - T cenY(pos_y[insts[0]]*wlPrecond[insts[0]]), totalWt(wlPrecond[insts[0]]); - for(unsigned int el = 1; el < insts.size(); ++el) - { - cenY += pos_y[insts[el]] * wlPrecond[insts[el]]; - totalWt += wlPrecond[insts[el]]; - } - if (totalWt > 0) - { - cenY /= totalWt; - } - - ble.score = cenY - siteY; - } - } - - // Sort BLEs in this half slice by their centroid.y - site.y from low to high - // Put them back to the implementation - std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score < b.score; }); - - for(unsigned int i = 0; i < bleLP.size(); ++i) - { - const auto &ble = bleLP[i]; - int offset = lh * HALF_SLICE_CAPACITY + i * BLE_CAPACITY; - for (int k : {0, 1}) - { - site_det_impl_lut[sdlutId + offset + k] = ble.lut[k]; - site_det_impl_ff[sdlutId + offset + k] = ble.ff[k]; - } - } - } - } else - { - ////DBG - //char printMsg = 0; - //std::string dbgMsg = ""; - //dbgMsg += std::to_string(sIdx) + " consider site: " + std::to_string(siteId) + "\n"; - ////DBG - - int res_lut[SLICE_MAX_CAP]; - int res_ff[SLICE_MAX_CAP]; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = site_det_impl_lut[sdlutId + sg]; - res_ff[sg] = site_det_impl_ff[sdlutId + sg]; - } - - for (int sg = 0; sg < HALF_SLICE_CAPACITY; ++sg) - { - int ssIdx = 2*sg; - subSlice ble; - - ble.lut[0] = res_lut[ssIdx]; - ble.lut[1] = res_lut[ssIdx + 1]; - ble.ff[0] = res_ff[ssIdx]; - ble.ff[1] = res_ff[ssIdx + 1]; - - - //DBG - //printMsg = 1; - //DBG - - ////DBG - //dbgMsg += "Initial ALM" + std::to_string(sg) + " contains: "; - //dbgMsg += "lut0: " + std::to_string(ble.lut[0]) + ", "; - //dbgMsg += "lut1: " + std::to_string(ble.lut[1]) + ", "; - //dbgMsg += "ff0: " + std::to_string(ble.ff[0]) + ", "; - //dbgMsg += "ff1: " + std::to_string(ble.ff[1]) + "\n"; - ////DBG - - // Assign LUTs with more inputs at odd slots - if (sites_with_special_nodes[sIdx] != 1) - { - int demA = (ble.lut[0] == INVALID ? 0 : lut_type[ble.lut[0]]); - int demB = (ble.lut[1] == INVALID ? 0 : lut_type[ble.lut[1]]); - int flip = (demA > demB ? 1 : 0); - site_det_impl_lut[sdlutId + ssIdx] = ble.lut[flip]; - site_det_impl_lut[sdlutId + ssIdx + 1] = ble.lut[1 - flip]; - } - - //Given LUT locations, ensure LUT driving FF are in same z - if ((ble.ff[0] != INVALID || ble.ff[1] != INVALID) && - (ble.lut[0] != INVALID || ble.lut[1] != INVALID)) - { - std::vector > lutOuts(2), ffIns(2); - - for (int k : {0, 1}) - { - if (ble.lut[k] == INVALID) continue; - - int ndOutId = 4*ble.lut[k]; - int ndOutPins = ndOutId + 4; - for (int nodeOutId = ndOutId; nodeOutId < ndOutPins; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - lutOuts[k].emplace_back(outNetId); - } - } - - for (int k : {0, 1}) - { - if (ble.ff[k] == INVALID) continue; - - int elIt = flat_node2pin_start_map[ble.ff[k]]; - int elEnd = flat_node2pin_start_map[ble.ff[k]+1]; - - for (int el = elIt; el < elEnd; ++el) - { - //Skip if not an input or output pin - int pinType = pin_typeIds[flat_node2pin_map[el]]; - if (pinType != 1) continue; - - int netId = pin2net_map[flat_node2pin_map[el]]; - ffIns[k].emplace_back(netId); - } - } - - for (int idx : {0, 1}) - { - if (lutOuts[idx].size() == 0) continue; - - for (int idy : {0, 1}) - { - if (ffIns[idy].size() == 0) continue; - - std::sort(lutOuts[idx].begin(), lutOuts[idx].end()); - std::sort(ffIns[idy].begin(), ffIns[idy].end()); - - //Check for matching nets - int lId = 0, fId = 0; - int netlId = lutOuts[idx][lId]; - int netfId = ffIns[idy][fId]; - - while(lId < lutOuts[idx].size() && fId < ffIns[idy].size()) - { - if (netlId < netfId) - { - ++lId; - if (lId < lutOuts[idx].size()) - { - netlId = lutOuts[idx][lId]; - } else - { - break; - } - } else if (netlId > netfId) - { - ++fId; - if (fId < ffIns[idy].size()) - { - netfId = ffIns[idy][fId]; - } else - { - break; - } - } else - { - //Move ff to same z as lut - Flip ffs - if (idx != idy) - { - int ff0 = ble.ff[0]; - int ff1 = ble.ff[1]; - - //Flip flop location - ble.ff[0] = ff1; - ble.ff[1] = ff0; - } - break; - } - } - } - } - } - - ////DBG - //dbgMsg += "Final ALM" + std::to_string(sg) + " contains: "; - //dbgMsg += "lut0: " + std::to_string(ble.lut[0]) + ", "; - //dbgMsg += "lut1: " + std::to_string(ble.lut[1]) + ", "; - //dbgMsg += "ff0: " + std::to_string(ble.ff[0]) + ", "; - //dbgMsg += "ff1: " + std::to_string(ble.ff[1]) + "\n"; - ////DBG - } - - ////DBG - //if (printMsg == 1) - //{ - // std::cout << dbgMsg << std::endl; - //} - ////DBG - - //order subSlices if site does not contain special nodes like carry chains - if (sites_with_special_nodes[sIdx] != 1) - { - // Sort subSlices by their Y centroid coordinates (cen.y - site.y) - std::vector > bleLP; - - T siteY = site_xy[2*site_det_siteId[sIdx] + 1]; - - for (int offset = 0; offset < SLICE_CAPACITY; offset += BLE_CAPACITY) - { - bleLP.emplace_back(); - auto &ble = bleLP.back(); - std::vector insts; - - for (int k : {0, 1}) - { - if (site_det_impl_lut[sdlutId + offset + k] != INVALID) - { - insts.push_back(site_det_impl_lut[sdlutId + offset + k]); - } - ble.lut[k] = site_det_impl_lut[sdlutId + offset + k]; - if (site_det_impl_ff[sdlutId + offset + k] != INVALID) - { - insts.push_back(site_det_impl_ff[sdlutId + offset + k]); - } - ble.ff[k] = site_det_impl_ff[sdlutId + offset + k]; - } - - // We use ble.score to store centroid.y - site.y of this BLE - if (insts.empty()) - { - ble.score = 0.0; - } else - { - //Centroid of insts - T cenY(pos_y[insts[0]]*wlPrecond[insts[0]]), totalWt(wlPrecond[insts[0]]); - for(unsigned int el = 1; el < insts.size(); ++el) - { - cenY += pos_y[insts[el]] * wlPrecond[insts[el]]; - totalWt += wlPrecond[insts[el]]; - } - if (totalWt > 0) - { - cenY /= totalWt; - } - - ble.score = cenY - siteY; - } - } - //std::cout << "siteId: " << siteId << " get scores for all BLEs"<< std::endl; - - // Sort BLEs in slice by their centroid.y - site.y from low to high - // Put them back to the implementation - std::sort(bleLP.begin(), bleLP.end(), [&](const subSlice &a, const subSlice &b){ return a.score < b.score; }); - - for(unsigned int i = 0; i < bleLP.size(); ++i) - { - const auto &ble = bleLP[i]; - int offset = i * BLE_CAPACITY; - for (int k : {0, 1}) - { - site_det_impl_lut[sdlutId + offset + k] = ble.lut[k]; - site_det_impl_ff[sdlutId + offset + k] = ble.ff[k]; - } - } - //std::cout << "siteId: " << siteId << " complete sort all subSlices"<< std::endl; - } - } - } - } - - //std::cout << "Slot assignment done" << std::endl; - return 0; -} - -// Cache the solution -template -int cacheSolution( - const int* site_det_impl_lut, - const int* site_det_impl_ff, - const int* inst_curr_detSite, - const int* addr2site_map, - const int num_sites_y, - const int num_clb_sites, - const int SLICE_CAPACITY, - T* node_x, - T* node_y, - int* node_z - ) -{ - for(int sIdx = 0; sIdx < num_clb_sites; ++sIdx) - { - int siteIdX = addr2site_map[sIdx] / num_sites_y; - int siteIdY = addr2site_map[sIdx] - int(siteIdX*num_sites_y); - int sdlutId(sIdx*SLICE_CAPACITY); - for (int z = 0; z < SLICE_CAPACITY; ++z) - { - for (int id : {site_det_impl_lut[sdlutId + z], site_det_impl_ff[sdlutId + z]}) - { - if (id != INVALID) - { - node_x[id] = siteIdX; - node_y[id] = siteIdY; - node_z[id] = z; - } - } - } - } - return 0; -} - -//////////////////////////////////////////////////////////////////////////// - -//Initialize site neighbors -void initSiteNbrs( - at::Tensor pos, - at::Tensor wlPrecond, - at::Tensor site_xy, - at::Tensor site_det_score, - at::Tensor sorted_node_idx, - at::Tensor node2fence_region_map, - at::Tensor site_types, - at::Tensor spiral_accessor, - at::Tensor site2addr_map, - at::Tensor addr2site_map, - at::Tensor flat_node2precluster_map, - at::Tensor flat_node2prclstrCount, - at::Tensor is_mlab_node, - at::Tensor is_mlab_site, - at::Tensor sites_with_special_nodes, - double nbrDistEnd, - double nbrDistBeg, - double nbrDistIncr, - int lutId, - int ffId, - int sliceId, - int num_nodes, - int num_sites_x, - int num_sites_y, - int num_clb_sites, - int SCL_IDX, - int SIG_IDX, - int SLICE_CAPACITY, - int numGroups, - int maxList, - int spiralBegin, - int spiralEnd, - at::Tensor site_curr_scl_score, - at::Tensor site_curr_scl_siteId, - at::Tensor site_curr_scl_validIdx, - at::Tensor site_curr_scl_idx, - at::Tensor site_curr_scl_sig, - at::Tensor site_curr_scl_sig_idx, - at::Tensor site_curr_scl_impl_lut, - at::Tensor site_nbrRanges, - at::Tensor site_nbrRanges_idx, - at::Tensor site_nbrList, - at::Tensor site_nbr, - at::Tensor site_nbr_idx, - at::Tensor site_nbrGroup_idx, - at::Tensor site_det_siteId, - at::Tensor site_det_sig, - at::Tensor site_det_sig_idx, - at::Tensor site_det_impl_lut, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(wlPrecond); - CHECK_CONTIGUOUS(wlPrecond); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(sorted_node_idx); - CHECK_CONTIGUOUS(sorted_node_idx); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - - CHECK_FLAT(site_types); - CHECK_CONTIGUOUS(site_types); - - CHECK_FLAT(spiral_accessor); - CHECK_CONTIGUOUS(spiral_accessor); - - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - CHECK_FLAT(addr2site_map); - CHECK_CONTIGUOUS(addr2site_map); - - CHECK_FLAT(flat_node2precluster_map); - CHECK_CONTIGUOUS(flat_node2precluster_map); - - CHECK_FLAT(flat_node2prclstrCount); - CHECK_CONTIGUOUS(flat_node2prclstrCount); - - int numNodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initSiteNeighbours", [&] { - initSiteNeighbours( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_site, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), - nbrDistEnd, nbrDistBeg, nbrDistIncr, - lutId, ffId, sliceId, num_nodes, num_sites_x, num_sites_y, - num_clb_sites, spiralBegin, spiralEnd, maxList, - numGroups, SCL_IDX, SIG_IDX, SLICE_CAPACITY, - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), - num_threads); - }); - //std::cout << "Completed initSiteNeighbours" << std::endl; - -} - -//Initialize and handle carry chains and mlabs -void initializeLG( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor sorted_net_idx, - at::Tensor sorted_node_map, - at::Tensor sorted_node_idx, - at::Tensor flat_net2pin_map, - at::Tensor flat_net2pin_start_map, - at::Tensor flop2ctrlSetId_map, - at::Tensor flop_ctrlSets, - at::Tensor node2fence_region_map, - at::Tensor node2outpinIdx_map, - at::Tensor pin2net_map, - at::Tensor pin2node_map, - at::Tensor pin_typeIds, - at::Tensor net2pincount, - at::Tensor is_mlab_node, - double preClusteringMaxDist, - int lutId, - int ffId, - int num_nets, - int num_nodes, - int num_threads, - int WLscoreMaxNetDegree, - at::Tensor net_bbox, - at::Tensor net_pinIdArrayX, - at::Tensor net_pinIdArrayY, - at::Tensor flat_node2precluster_map, - at::Tensor flat_node2prclstrCount) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - CHECK_FLAT(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - CHECK_FLAT(sorted_net_idx); - CHECK_CONTIGUOUS(sorted_net_idx); - - CHECK_FLAT(sorted_node_map); - CHECK_CONTIGUOUS(sorted_node_map); - CHECK_FLAT(sorted_node_idx); - CHECK_CONTIGUOUS(sorted_node_idx); - - CHECK_FLAT(flat_net2pin_map); - CHECK_CONTIGUOUS(flat_net2pin_map); - CHECK_FLAT(flat_net2pin_start_map); - CHECK_CONTIGUOUS(flat_net2pin_start_map); - - CHECK_FLAT(flop2ctrlSetId_map); - CHECK_CONTIGUOUS(flop2ctrlSetId_map); - CHECK_FLAT(flop_ctrlSets); - CHECK_CONTIGUOUS(flop_ctrlSets); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - CHECK_FLAT(node2outpinIdx_map); - CHECK_CONTIGUOUS(node2outpinIdx_map); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(pin2node_map); - CHECK_CONTIGUOUS(pin2node_map); - CHECK_FLAT(pin_typeIds); - CHECK_CONTIGUOUS(pin_typeIds); - - CHECK_FLAT(net2pincount); - CHECK_CONTIGUOUS(net2pincount); - - int numNodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initializeNets", [&] { - initializeNets( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), - WLscoreMaxNetDegree, num_threads); - }); - - //std::cout << "Completed initializeNets " << std::endl; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "preClusteringUpd", [&] { - preClusteringUpd( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), - preClusteringMaxDist, lutId, ffId, num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), - num_threads); - }); - //std::cout << "Completed preclusteringUpd" << std::endl; -} - -//Legalize carry-chains -void legalizeCarryChain( - at::Tensor pos, - at::Tensor site_xy, - at::Tensor wlPrecond, - at::Tensor spiral_accessor, - at::Tensor site_types, - at::Tensor site2addr_map, - at::Tensor flat_cc2node_start_map, - at::Tensor flat_cc2node_map, - int spiralBegin, - int spiralEnd, - int num_sites_x, - int num_sites_y, - int sliceId, - int SIG_IDX, - int SLICE_CAPACITY, - int num_carry_chains, - int num_carryInstsInSlice, - int slice_minX, - int slice_maxX, - int slice_minY, - int slice_maxY, - at::Tensor carry_chain_displacements, - at::Tensor site_det_score, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_next_bestScoreImprov, - at::Tensor sites_with_special_nodes, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestSite, - at::Tensor site_det_siteId, - at::Tensor site_det_sig, - at::Tensor site_det_sig_idx, - at::Tensor site_det_impl_lut, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(wlPrecond); - CHECK_CONTIGUOUS(wlPrecond); - - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - CHECK_FLAT(flat_cc2node_start_map); - CHECK_CONTIGUOUS(flat_cc2node_start_map); - CHECK_FLAT(flat_cc2node_map); - CHECK_CONTIGUOUS(flat_cc2node_map); - - CHECK_FLAT(spiral_accessor); - CHECK_CONTIGUOUS(spiral_accessor); - - CHECK_FLAT(site_types); - CHECK_CONTIGUOUS(site_types); - - int numNodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeCarryChainsLauncher", [&] { - legalizeCarryChainsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_cc2node_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_cc2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - spiralBegin, spiralEnd, num_carry_chains, num_sites_x, num_sites_y, - sliceId, SIG_IDX, SLICE_CAPACITY, num_carryInstsInSlice, - slice_minX, slice_maxX, slice_minY, slice_maxY, - DREAMPLACE_TENSOR_DATA_PTR(carry_chain_displacements, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), - num_threads); - }); - -} - -//MinCost to find sites -void minCostFlow( - pybind11::array_t const& locX, - pybind11::array_t const& locY, - int const num_sites, int const num_nodes, - pybind11::array_t const& sites, - pybind11::array_t const& precond, - double const &lg_max_dist_init, double const &lg_max_dist_incr, - double const &lg_flow_cost_scale, pybind11::list &movVal, pybind11::list &out) -{ - typedef lemon::ListDigraph graphType; - graphType graph; - graphType::ArcMap capLo(graph); - graphType::ArcMap capHi(graph); - graphType::ArcMap cost(graph); - std::vector lNodes, rNodes; - std::vector lArcs, rArcs, mArcs; - std::vector > mArcPairs; - - //Source and target Nodes - graphType::Node s = graph.addNode(), t = graph.addNode(); - - //Add left nodes (blocks) and arcs between source node and left nodes - for (int i = 0; i < num_nodes; ++i) - { - lNodes.emplace_back(graph.addNode()); - lArcs.emplace_back(graph.addArc(s, lNodes.back())); - cost[lArcs.back()] = 0.0; - capLo[lArcs.back()] = 0.0; - capHi[lArcs.back()] = 1.0; - } - - //Add right nodes (sites) and arc between right nodes and target node - for (int j=0; j < num_sites; ++j) - { - rNodes.emplace_back(graph.addNode()); - rArcs.emplace_back(graph.addArc(rNodes.back(), t)); - cost[rArcs.back()] = 0.0; - capLo[rArcs.back()] = 0.0; - capHi[rArcs.back()] = 1.0; - } - - //To improve efficiency, we do not run matching for complete bipartite graph but incrementally add arcs when needed - double distMin = 0.0; - double distMax = lg_max_dist_init; - - while (true) - { - //Generate arcs between left (blocks) and right (sites) nodes, pruning based on distance - for (int blk = 0; blk < num_nodes; ++blk) - { - for (int st = 0; st < num_sites; ++st) - { - double dist = std::abs(locX.at(blk) - sites.at(st*2)) + std::abs(locY.at(blk) - sites.at(st*2+1)); - if (dist >= distMin && dist < distMax) - { - mArcs.emplace_back(graph.addArc(lNodes[blk], rNodes[st])); - mArcPairs.emplace_back(blk, st); - double mArcCost = dist * precond.at(blk) * lg_flow_cost_scale; - cost[mArcs.back()] = mArcCost; - capLo[mArcs.back()] = 0.0; - capHi[mArcs.back()] = 1.0; - } - } - } - - //Run min-cost flow - lemon::NetworkSimplex mcf(graph); - mcf.stSupply(s, t, num_nodes); - mcf.lowerMap(capLo).upperMap(capHi).costMap(cost); - mcf.run(); - - //A feasible solution must have flow size equal to the no of blocks - //If not, we need to increase the max distance constraint - double flowSize = 0.0; - for (const auto &arc : rArcs) - { - flowSize += mcf.flow(arc); - } - if (flowSize != num_nodes) - { - //Increase searching range - distMin = distMax; - distMax += lg_max_dist_incr; - continue; - } - - double maxMov = 0; - double avgMov = 0; - //If the execution hits here, we found a feasible solution - for (int i = 0; i < mArcs.size(); ++i) - { - if (mcf.flow(mArcs[i])) - { - const auto &p = mArcPairs[i]; - double mov = std::abs(locX.at(p.first) - sites.at(p.second*2)) + std::abs(locY.at(p.first) - sites.at(p.second*2+1)); - avgMov += mov; - maxMov = std::max(maxMov, mov); - out[p.first] = sites.at(p.second*2); - out[num_nodes+p.first] = sites.at(p.second*2+1); - } - } - if (num_nodes) - { - avgMov /= num_nodes; - } - movVal[0] = maxMov; - movVal[1] = avgMov; - return; - } -} - -//Legalize Macro -void legalizeMacro( - at::Tensor pos, - at::Tensor site_xy, - at::Tensor macro2node_map, - at::Tensor macro_site_height, - at::Tensor macro_element_count, - at::Tensor spiral_accessor, - at::Tensor site2addr_map, - at::Tensor site_types, - int spiralBegin, - int spiralEnd, - int num_macros, - int num_sites_x, - int num_sites_y, - int slice_minX, - int slice_maxX, - int slice_minY, - int slice_maxY, - int sliceId, - int SIG_IDX, - int SLICE_CAPACITY, - at::Tensor dist_moved, - at::Tensor site_det_score, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_next_bestScoreImprov, - at::Tensor site_det_siteId, - at::Tensor site_det_sig, - at::Tensor site_det_sig_idx, - at::Tensor site_det_impl_lut, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestSite, - at::Tensor sites_with_special_nodes) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - CHECK_FLAT(spiral_accessor); - CHECK_CONTIGUOUS(spiral_accessor); - - CHECK_FLAT(site_types); - CHECK_CONTIGUOUS(site_types); - - CHECK_FLAT(macro2node_map); - CHECK_CONTIGUOUS(macro2node_map); - - int numNodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMacrosLauncher", [&] { - legalizeMacrosLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(macro2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(macro_site_height, int), - DREAMPLACE_TENSOR_DATA_PTR(macro_element_count, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - spiralBegin, spiralEnd, num_macros, num_sites_x, - num_sites_y, slice_minX, slice_maxX, slice_minY, - slice_maxY, sliceId, SIG_IDX, SLICE_CAPACITY, - DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int)); - }); - //std::cout << "Completed legalizeMacro" << std::endl; -} - -//Legalize Mlab -void legalizeMlab( - at::Tensor pos, - at::Tensor site_xy, - at::Tensor mlab_locX, - at::Tensor mlab_locY, - at::Tensor mlab_indices, - at::Tensor site2addr_map, - int num_mlab_nodes, - int num_sites_y, - int SIG_IDX, - int SLICE_CAPACITY, - at::Tensor dist_moved, - at::Tensor site_det_score, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_next_bestScoreImprov, - at::Tensor sites_with_special_nodes, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestSite, - at::Tensor site_det_siteId, - at::Tensor site_det_sig, - at::Tensor site_det_sig_idx, - at::Tensor site_det_impl_lut, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(mlab_locX); - CHECK_CONTIGUOUS(mlab_locX); - CHECK_FLAT(mlab_locY); - CHECK_CONTIGUOUS(mlab_locY); - - CHECK_FLAT(mlab_indices); - CHECK_CONTIGUOUS(mlab_indices); - - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - int numNodes = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMlabsLauncher", [&] { - legalizeMlabsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_locX, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_locY, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_indices, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - num_mlab_nodes, num_sites_y, - SIG_IDX, SLICE_CAPACITY, - DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), - num_threads); - }); - //std::cout << "Completed legalizeMlab" << std::endl; -} - -//runDLIteration -void runDLIter( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor net_bbox, - at::Tensor net_pinIdArrayX, - at::Tensor net_pinIdArrayY, - at::Tensor site_xy, - at::Tensor node2fence_region_map, - at::Tensor flop_ctrlSets, - at::Tensor extended_ctrlSets, - at::Tensor ext_ctrlSet_start_map, - at::Tensor flop2ctrlSetId_map, - at::Tensor lut_type, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor node2outpinIdx_map, - at::Tensor node2pincount, - at::Tensor net2pincount, - at::Tensor pin2net_map, - at::Tensor pin_typeIds, - at::Tensor flat_net2pin_start_map, - at::Tensor pin2node_map, - at::Tensor flat_node2prclstrCount, - at::Tensor flat_node2precluster_map, - at::Tensor is_mlab_node, - at::Tensor is_mlab_site, - at::Tensor site_nbrList, - at::Tensor site_nbrRanges, - at::Tensor site_nbrRanges_idx, - at::Tensor sorted_node_map, - at::Tensor sorted_net_map, - at::Tensor net_weights, - at::Tensor addr2site_map, - at::Tensor special_nodes, - int num_sites_x, - int num_sites_y, - int num_clb_sites, - int minStableIter, - int maxList, - int half_ctrl_mode, - int SLICE_CAPACITY, - int HALF_SLICE_CAPACITY, - int BLE_CAPACITY, - int NUM_BLE_PER_SLICE, - int minNeighbors, - int numGroups, - int netShareScoreMaxNetDegree, - int wlScoreMaxNetDegree, - int lutTypeInSliceUnit, - int lut_maxShared, - double xWirelenWt, - double yWirelenWt, - double wirelenImprovWt, - double extNetCountWt, - int CKSR_IN_CLB, - int CE_IN_CLB, - int SCL_IDX, - int PQ_IDX, - int SIG_IDX, - int lutId, - int ffId, - int num_nodes, - int num_threads, - at::Tensor site_nbr_idx, - at::Tensor site_nbr, - at::Tensor site_nbrGroup_idx, - at::Tensor site_curr_pq_top_idx, - at::Tensor site_curr_pq_sig_idx, - at::Tensor site_curr_pq_sig, - at::Tensor site_curr_pq_idx, - at::Tensor site_curr_pq_validIdx, - at::Tensor site_curr_stable, - at::Tensor site_curr_pq_siteId, - at::Tensor site_curr_pq_score, - at::Tensor site_curr_pq_impl_lut, - at::Tensor site_curr_pq_impl_ff, - at::Tensor site_curr_pq_impl_cksr, - at::Tensor site_curr_pq_impl_ce, - at::Tensor site_curr_scl_score, - at::Tensor site_curr_scl_siteId, - at::Tensor site_curr_scl_idx, - at::Tensor site_curr_scl_validIdx, - at::Tensor site_curr_scl_sig_idx, - at::Tensor site_curr_scl_sig, - at::Tensor site_curr_scl_impl_lut, - at::Tensor site_curr_scl_impl_ff, - at::Tensor site_curr_scl_impl_cksr, - at::Tensor site_curr_scl_impl_ce, - at::Tensor site_next_pq_idx, - at::Tensor site_next_pq_validIdx, - at::Tensor site_next_pq_top_idx, - at::Tensor site_next_pq_score, - at::Tensor site_next_pq_siteId, - at::Tensor site_next_pq_sig_idx, - at::Tensor site_next_pq_sig, - at::Tensor site_next_pq_impl_lut, - at::Tensor site_next_pq_impl_ff, - at::Tensor site_next_pq_impl_cksr, - at::Tensor site_next_pq_impl_ce, - at::Tensor site_next_scl_score, - at::Tensor site_next_scl_siteId, - at::Tensor site_next_scl_idx, - at::Tensor site_next_scl_validIdx, - at::Tensor site_next_scl_sig_idx, - at::Tensor site_next_scl_sig, - at::Tensor site_next_scl_impl_lut, - at::Tensor site_next_scl_impl_ff, - at::Tensor site_next_scl_impl_cksr, - at::Tensor site_next_scl_impl_ce, - at::Tensor site_next_stable, - at::Tensor site_det_score, - at::Tensor site_det_siteId, - at::Tensor site_det_sig_idx, - at::Tensor site_det_sig, - at::Tensor site_det_impl_lut, - at::Tensor site_det_impl_ff, - at::Tensor site_det_impl_cksr, - at::Tensor site_det_impl_ce, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestScoreImprov, - at::Tensor inst_next_bestSite, - at::Tensor activeStatus, - at::Tensor illegalStatus - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - CHECK_FLAT(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - CHECK_FLAT(net_bbox); - CHECK_CONTIGUOUS(net_bbox); - - CHECK_FLAT(net_pinIdArrayX); - CHECK_CONTIGUOUS(net_pinIdArrayX); - CHECK_FLAT(net_pinIdArrayY); - CHECK_CONTIGUOUS(net_pinIdArrayY); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - - CHECK_FLAT(flop_ctrlSets); - CHECK_CONTIGUOUS(flop_ctrlSets); - CHECK_FLAT(flop2ctrlSetId_map); - CHECK_CONTIGUOUS(flop2ctrlSetId_map); - - CHECK_FLAT(lut_type); - CHECK_CONTIGUOUS(lut_type); - - CHECK_FLAT(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - - CHECK_FLAT(node2pincount); - CHECK_CONTIGUOUS(node2pincount); - - CHECK_FLAT(net2pincount); - CHECK_CONTIGUOUS(net2pincount); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(pin_typeIds); - CHECK_CONTIGUOUS(pin_typeIds); - - CHECK_FLAT(flat_net2pin_start_map); - CHECK_CONTIGUOUS(flat_net2pin_start_map); - - CHECK_FLAT(pin2node_map); - CHECK_CONTIGUOUS(pin2node_map); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - int numNodes = pos.numel() / 2; - - //Run DL Iteration - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "runDLIteration", [&] { - runDLIteration( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_site, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), - num_clb_sites, minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, - minNeighbors, numGroups, netShareScoreMaxNetDegree, - wlScoreMaxNetDegree, lutTypeInSliceUnit, lut_maxShared, - xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, - CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, - lutId, ffId, num_threads, - DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int)); - }); - - //std::cout << "Run DL Iter "<< std::endl; - - //Run DL Sync - DREAMPLACE_DISPATCH_FLOATING_TYPES(site_curr_pq_score, "runDLSynchronize", [&] { - runDLSynchronize( - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - num_clb_sites, SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, - SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, num_nodes, num_threads, - DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(activeStatus, int), - DREAMPLACE_TENSOR_DATA_PTR(illegalStatus, int)); - }); - - //std::cout << "Run DL Sync: " << DLStatus << std::endl; -} - -//RipUp & Greedy Legalization -void ripUp_SlotAssign( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor net_weights, - at::Tensor net_bbox, - at::Tensor inst_areas, - at::Tensor wlPrecond, - at::Tensor site_xy, - at::Tensor net_pinIdArrayX, - at::Tensor net_pinIdArrayY, - at::Tensor spiral_accessor, - at::Tensor node2fence_region_map, - at::Tensor lut_type, - at::Tensor site_types, - at::Tensor node2pincount, - at::Tensor net2pincount, - at::Tensor pin2net_map, - at::Tensor pin2node_map, - at::Tensor pin_typeIds, - at::Tensor flop2ctrlSetId_map, - at::Tensor flop_ctrlSets, - at::Tensor extended_ctrlSets, - at::Tensor ext_ctrlSet_start_map, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_net2pin_start_map, - at::Tensor flat_node2prclstrCount, - at::Tensor flat_node2precluster_map, - at::Tensor sorted_node_map, - at::Tensor sorted_node_idx, - at::Tensor sorted_net_map, - at::Tensor node2outpinIdx_map, - at::Tensor flat_net2pin_map, - at::Tensor addr2site_map, - at::Tensor site2addr_map, - at::Tensor sites_with_special_nodes, - at::Tensor special_nodes, - double nbrDistEnd, - double xWirelenWt, - double yWirelenWt, - double extNetCountWt, - double wirelenImprovWt, - double slotAssignFlowWeightScale, - double slotAssignFlowWeightIncr, - int lutTypeInSliceUnit, - int lut_maxShared, - int num_nodes, - int num_sites_x, - int num_sites_y, - int num_clb_sites, - int spiralBegin, - int spiralEnd, - int half_ctrl_mode, - int CKSR_IN_CLB, - int CE_IN_CLB, - int SLICE_CAPACITY, - int HALF_SLICE_CAPACITY, - int BLE_CAPACITY, - int NUM_BLE_PER_SLICE, - int NUM_BLE_PER_HALF_SLICE, - int netShareScoreMaxNetDegree, - int wlScoreMaxNetDegree, - int ripupExpansion, - int greedyExpansion, - int SIG_IDX, - int lutId, - int ffId, - int sliceId, - int num_threads, - at::Tensor inst_curr_detSite, - at::Tensor site_det_sig_idx, - at::Tensor site_det_sig, - at::Tensor site_det_impl_lut, - at::Tensor site_det_impl_ff, - at::Tensor site_det_impl_cksr, - at::Tensor site_det_impl_ce, - at::Tensor site_det_siteId, - at::Tensor site_det_score, - at::Tensor node_x, - at::Tensor node_y, - at::Tensor node_z - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - CHECK_FLAT(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - CHECK_FLAT(net_bbox); - CHECK_CONTIGUOUS(net_bbox); - - CHECK_FLAT(inst_areas); - CHECK_CONTIGUOUS(inst_areas); - - CHECK_FLAT(wlPrecond); - CHECK_CONTIGUOUS(wlPrecond); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(net_pinIdArrayX); - CHECK_CONTIGUOUS(net_pinIdArrayX); - CHECK_FLAT(net_pinIdArrayY); - CHECK_CONTIGUOUS(net_pinIdArrayY); - - CHECK_FLAT(spiral_accessor); - CHECK_CONTIGUOUS(spiral_accessor); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - - CHECK_FLAT(lut_type); - CHECK_CONTIGUOUS(lut_type); - - CHECK_FLAT(site_types); - CHECK_CONTIGUOUS(site_types); - - CHECK_FLAT(node2pincount); - CHECK_CONTIGUOUS(node2pincount); - - CHECK_FLAT(net2pincount); - CHECK_CONTIGUOUS(net2pincount); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - CHECK_FLAT(pin2node_map); - CHECK_CONTIGUOUS(pin2node_map); - - CHECK_FLAT(pin_typeIds); - CHECK_CONTIGUOUS(pin_typeIds); - - CHECK_FLAT(flop2ctrlSetId_map); - CHECK_CONTIGUOUS(flop2ctrlSetId_map); - CHECK_FLAT(flop_ctrlSets); - CHECK_CONTIGUOUS(flop_ctrlSets); - - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - CHECK_FLAT(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - - CHECK_FLAT(flat_net2pin_start_map); - CHECK_CONTIGUOUS(flat_net2pin_start_map); - CHECK_FLAT(flat_net2pin_map); - CHECK_CONTIGUOUS(flat_net2pin_map); - - CHECK_FLAT(flat_node2prclstrCount); - CHECK_CONTIGUOUS(flat_node2prclstrCount); - CHECK_FLAT(flat_node2precluster_map); - CHECK_CONTIGUOUS(flat_node2precluster_map); - - CHECK_FLAT(sorted_node_map); - CHECK_CONTIGUOUS(sorted_node_map); - CHECK_FLAT(sorted_node_idx); - CHECK_CONTIGUOUS(sorted_node_idx); - - CHECK_FLAT(sorted_net_map); - CHECK_CONTIGUOUS(sorted_net_map); - - int numNodes = pos.numel() / 2; - - ////DBG - //CPUTimer::hr_clock_rep timer_start, timer_stop; - //timer_start = CPUTimer::getGlobaltime(); - ////DBG - - //RipUp & Greedy Legalization - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "ripUp_Greedy_LG", [&] { - ripUp_Greedy_LG( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_areas, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), - DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), - DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), - nbrDistEnd, xWirelenWt, yWirelenWt, extNetCountWt, - wirelenImprovWt, lutTypeInSliceUnit, lut_maxShared, - num_nodes, num_sites_x, num_sites_y, num_clb_sites, - spiralBegin, spiralEnd, half_ctrl_mode, CKSR_IN_CLB, CE_IN_CLB, - SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, netShareScoreMaxNetDegree, - wlScoreMaxNetDegree, ripupExpansion, greedyExpansion, - SIG_IDX, lutId, ffId, sliceId, - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t)); - }); - - ////DBG - //timer_stop = CPUTimer::getGlobaltime(); - //dreamplacePrint(kINFO, "RipUp & Greedy LG takes %.4f s\n", - // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()/1000.0); - //timer_start = CPUTimer::getGlobaltime(); - ////DBG - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "slotAssign", [&] { - slotAssign( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(wlPrecond, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int), - slotAssignFlowWeightScale, slotAssignFlowWeightIncr, - num_sites_x, num_sites_y, num_clb_sites, lutTypeInSliceUnit, - lut_maxShared, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, - NUM_BLE_PER_HALF_SLICE, half_ctrl_mode, lutId, num_threads, - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int)); - }); - - ////DBG - //timer_stop = CPUTimer::getGlobaltime(); - //dreamplacePrint(kINFO, "Slot Assign takes %g ms\n", - // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); - //timer_start = CPUTimer::getGlobaltime(); - ////DBG - - //Cache Solution - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "cacheSolution", [&] { - cacheSolution( - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - num_sites_y, num_clb_sites, SLICE_CAPACITY, - DREAMPLACE_TENSOR_DATA_PTR(node_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_z, int)); - }); - - ////DBG - //timer_stop = CPUTimer::getGlobaltime(); - //dreamplacePrint(kINFO, "Cache Solution takes %g ms\n", - // (timer_stop - timer_start) * CPUTimer::getTimerPeriod()); - ////DBG -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("initializeLG", &DREAMPLACE_NAMESPACE::initializeLG, "Initialize LUT/FF legalization to handle carry-chains and mlabs"); - m.def("minCostFlow", &DREAMPLACE_NAMESPACE::minCostFlow, "Min Cost flow to get site locations"); - m.def("legalizeCarryChain", &DREAMPLACE_NAMESPACE::legalizeCarryChain, "Legalize carry-chains"); - m.def("legalizeMacro", &DREAMPLACE_NAMESPACE::legalizeMacro, "Legalize Slice Macros"); - m.def("legalizeMlab", &DREAMPLACE_NAMESPACE::legalizeMlab, "Legalize Mlabs"); - m.def("initSiteNbrs", &DREAMPLACE_NAMESPACE::initSiteNbrs, "Initialize site neighbors"); - m.def("runDLIter", &DREAMPLACE_NAMESPACE::runDLIter, "Run DL Iteration"); - m.def("ripUp_SlotAssign", &DREAMPLACE_NAMESPACE::ripUp_SlotAssign, "Run RipUp and Greedy Legalization and Slot Assign"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp deleted file mode 100644 index 93fc1ed2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp +++ /dev/null @@ -1,760 +0,0 @@ -/** - * @file lut_ff_legalization_cuda.cpp - * @author Rachel Selina - * @date Mar 2021 (DREAMPlaceFPGA-PL) - * @brief Legalize LUT/FF - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE -// Initialize -template -int initLGCudaLauncher( - const T *pos_x, - const T *pos_y, - const T *pin_offset_x, - const T *pin_offset_y, - const int *sorted_node_map, - const int *sorted_node_idx, - const int *sorted_net_idx, - const int *flat_net2pin_map, - const int *flat_net2pin_start_map, - const int *flop2ctrlSetId_map, - const int *flop_ctrlSets, - const int *node2fence_region_map, - const int *node2outpinIdx_map, - const int *pin2net_map, - const int *pin2node_map, - const int *pin_typeIds, - const int *net2pincount, - const int *is_mlab_node, - const T preClusteringMaxDist, - const int ffId, - const int lutId, - const int num_nets, - const int num_nodes, - const int wlscoreMaxNetDegree, - T *net_bbox, - int *net_pinIdArrayX, - int *net_pinIdArrayY, - int *flat_node2precluster_map, - int *flat_node2prclstrCount); - -//runDLIter -template -int runDLCudaLauncher( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_bbox, - const T* site_xy, - const T* net_weights, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* site_types, - const int* spiral_accessor, - const int* node2fence_region_map, - const int* lut_flop_indices, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* lut_type, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* node2pincount, - const int* net2pincount, - const int* pin2net_map, - const int* pin_typeIds, - const int* flat_net2pin_start_map, - const int* pin2node_map, - const int* sorted_net_map, - const int* sorted_node_map, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* is_mlab_node, - const int* site_nbrList, - const int* site_nbrRanges, - const int* site_nbrRanges_idx, - const int* addr2site_map, - const int* site2addr_map, - const int* special_nodes, - const T maxDist, - const T xWirelenWt, - const T yWirelenWt, - const T wirelenImprovWt, - const T extNetCountWt, - const int num_sites_x, - const int num_sites_y, - const int num_clb_sites, - const int num_lutflops, - const int minStableIter, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int minNeighbors, - const int spiralBegin, - const int spiralEnd, - const int intMinVal, - const int numGroups, - const int netShareScoreMaxNetDegree, - const int wlscoreMaxNetDegree, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - const int sliceId, - int* site_nbr_idx, - int* site_nbr, - int* site_nbrGroup_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - int* site_curr_pq_validIdx, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* cumsum_curr_scl, - int* site_curr_scl_validIdx, - int* validIndices_curr_scl, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - T* site_det_score, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* inst_curr_detSite, - T* inst_curr_bestScoreImprov, - int* inst_curr_bestSite, - int* inst_next_detSite, - T* inst_next_bestScoreImprov, - int* inst_next_bestSite, - int* activeStatus, - int* illegalStatus, - int* inst_score_improv, - int* site_score_improv, - int* sorted_clb_siteIds); - -//legalize Mlab -template -int legalizeMlabCudaLauncher( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const T* mlab_locX, - const T* mlab_locY, - const int* mlab_indices, - const int* site2addr_map, - const int num_mlab_nodes, - const int num_sites_y, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* dist_moved, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -void initLegalization( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor sorted_net_idx, - at::Tensor sorted_node_map, - at::Tensor sorted_node_idx, - at::Tensor flat_net2pin_map, - at::Tensor flat_net2pin_start_map, - at::Tensor flop2ctrlSetId_map, - at::Tensor flop_ctrlSets, - at::Tensor node2fence_region_map, - at::Tensor node2outpinIdx_map, - at::Tensor pin2net_map, - at::Tensor pin2node_map, - at::Tensor pin_typeIds, - at::Tensor net2pincount, - at::Tensor is_mlab_node, - double preClusteringMaxDist, - int num_nets, - int num_nodes, - int lutId, - int ffId, - int wlscoreMaxNetDegree, - at::Tensor net_bbox, - at::Tensor net_pinIdArrayX, - at::Tensor net_pinIdArrayY, - at::Tensor flat_node2precluster_map, - at::Tensor flat_node2prclstrCount) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - CHECK_FLAT(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - CHECK_FLAT(sorted_net_idx); - CHECK_CONTIGUOUS(sorted_net_idx); - - CHECK_FLAT(sorted_node_map); - CHECK_CONTIGUOUS(sorted_node_map); - CHECK_FLAT(sorted_node_idx); - CHECK_CONTIGUOUS(sorted_node_idx); - - CHECK_FLAT(flat_net2pin_map); - CHECK_CONTIGUOUS(flat_net2pin_map); - CHECK_FLAT(flat_net2pin_start_map); - CHECK_CONTIGUOUS(flat_net2pin_start_map); - - CHECK_FLAT(flop2ctrlSetId_map); - CHECK_CONTIGUOUS(flop2ctrlSetId_map); - CHECK_FLAT(flop_ctrlSets); - CHECK_CONTIGUOUS(flop_ctrlSets); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - CHECK_FLAT(node2outpinIdx_map); - CHECK_CONTIGUOUS(node2outpinIdx_map); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(pin2node_map); - CHECK_CONTIGUOUS(pin2node_map); - CHECK_FLAT(pin_typeIds); - CHECK_CONTIGUOUS(pin_typeIds); - - CHECK_FLAT(net2pincount); - CHECK_CONTIGUOUS(net2pincount); - - int numNodes = pos.numel() / 2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "initLGCudaLauncher", [&] { - initLGCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), - preClusteringMaxDist, ffId, lutId, - num_nets, num_nodes, wlscoreMaxNetDegree, - DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int)); - }); - - -} - -//RunDLIteration -void runDLIter( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor net_bbox, - at::Tensor site_xy, - at::Tensor net_weights, - at::Tensor net_pinIdArrayX, - at::Tensor net_pinIdArrayY, - at::Tensor site_types, - at::Tensor spiral_accessor, - at::Tensor node2fence_region_map, - at::Tensor lut_flop_indices, - at::Tensor flop2ctrlSetId_map, - at::Tensor flop_ctrlSets, - at::Tensor extended_ctrlSets, - at::Tensor ext_ctrlSet_start_map, - at::Tensor lut_type, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor node2outpinIdx_map, - at::Tensor node2pincount, - at::Tensor net2pincount, - at::Tensor pin2net_map, - at::Tensor pin_typeIds, - at::Tensor flat_net2pin_start_map, - at::Tensor pin2node_map, - at::Tensor sorted_net_map, - at::Tensor sorted_node_map, - at::Tensor flat_node2prclstrCount, - at::Tensor flat_node2precluster_map, - at::Tensor is_mlab_node, - at::Tensor site_nbrList, - at::Tensor site_nbrRanges, - at::Tensor site_nbrRanges_idx, - at::Tensor addr2site_map, - at::Tensor site2addr_map, - at::Tensor special_nodes, - double maxDist, - double xWirelenWt, - double yWirelenWt, - double wirelenImprovWt, - double extNetCountWt, - int num_sites_x, - int num_sites_y, - int num_clb_sites, - int num_lutflops, - int minStableIter, - int maxList, - int half_ctrl_mode, - int SLICE_CAPACITY, - int HALF_SLICE_CAPACITY, - int BLE_CAPACITY, - int NUM_BLE_PER_SLICE, - int minNeighbors, - int spiralBegin, - int spiralEnd, - int intMinVal, - int numGroups, - int netShareScoreMaxNetDegree, - int wlscoreMaxNetDegree, - int lutTypeInSliceUnit, - int lut_maxShared, - int CKSR_IN_CLB, - int CE_IN_CLB, - int SCL_IDX, - int PQ_IDX, - int SIG_IDX, - int lutId, - int ffId, - int sliceId, - at::Tensor site_nbr_idx, - at::Tensor site_nbr, - at::Tensor site_nbrGroup_idx, - at::Tensor site_curr_pq_top_idx, - at::Tensor site_curr_pq_sig_idx, - at::Tensor site_curr_pq_sig, - at::Tensor site_curr_pq_idx, - at::Tensor site_curr_stable, - at::Tensor site_curr_pq_siteId, - at::Tensor site_curr_pq_validIdx, - at::Tensor site_curr_pq_score, - at::Tensor site_curr_pq_impl_lut, - at::Tensor site_curr_pq_impl_ff, - at::Tensor site_curr_pq_impl_cksr, - at::Tensor site_curr_pq_impl_ce, - at::Tensor site_curr_scl_score, - at::Tensor site_curr_scl_siteId, - at::Tensor site_curr_scl_idx, - at::Tensor cumsum_curr_scl, - at::Tensor site_curr_scl_validIdx, - at::Tensor validIndices_curr_scl, - at::Tensor site_curr_scl_sig_idx, - at::Tensor site_curr_scl_sig, - at::Tensor site_curr_scl_impl_lut, - at::Tensor site_curr_scl_impl_ff, - at::Tensor site_curr_scl_impl_cksr, - at::Tensor site_curr_scl_impl_ce, - at::Tensor site_next_pq_idx, - at::Tensor site_next_pq_validIdx, - at::Tensor site_next_pq_top_idx, - at::Tensor site_next_pq_score, - at::Tensor site_next_pq_siteId, - at::Tensor site_next_pq_sig_idx, - at::Tensor site_next_pq_sig, - at::Tensor site_next_pq_impl_lut, - at::Tensor site_next_pq_impl_ff, - at::Tensor site_next_pq_impl_cksr, - at::Tensor site_next_pq_impl_ce, - at::Tensor site_next_scl_score, - at::Tensor site_next_scl_siteId, - at::Tensor site_next_scl_idx, - at::Tensor site_next_scl_validIdx, - at::Tensor site_next_scl_sig_idx, - at::Tensor site_next_scl_sig, - at::Tensor site_next_scl_impl_lut, - at::Tensor site_next_scl_impl_ff, - at::Tensor site_next_scl_impl_cksr, - at::Tensor site_next_scl_impl_ce, - at::Tensor site_next_stable, - at::Tensor site_det_score, - at::Tensor site_det_siteId, - at::Tensor site_det_sig_idx, - at::Tensor site_det_sig, - at::Tensor site_det_impl_lut, - at::Tensor site_det_impl_ff, - at::Tensor site_det_impl_cksr, - at::Tensor site_det_impl_ce, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestScoreImprov, - at::Tensor inst_next_bestSite, - at::Tensor activeStatus, - at::Tensor illegalStatus, - at::Tensor inst_score_improv, - at::Tensor site_score_improv, - at::Tensor sorted_clb_siteIds) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(pin_offset_x); - CHECK_CONTIGUOUS(pin_offset_x); - CHECK_FLAT(pin_offset_y); - CHECK_CONTIGUOUS(pin_offset_y); - - CHECK_FLAT(net_bbox); - CHECK_CONTIGUOUS(net_bbox); - - CHECK_FLAT(net_pinIdArrayX); - CHECK_CONTIGUOUS(net_pinIdArrayX); - CHECK_FLAT(net_pinIdArrayY); - CHECK_CONTIGUOUS(net_pinIdArrayY); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - CHECK_FLAT(spiral_accessor); - CHECK_CONTIGUOUS(spiral_accessor); - CHECK_FLAT(site_types); - CHECK_CONTIGUOUS(site_types); - - CHECK_FLAT(node2fence_region_map); - CHECK_CONTIGUOUS(node2fence_region_map); - - CHECK_FLAT(lut_flop_indices); - CHECK_CONTIGUOUS(lut_flop_indices); - - CHECK_FLAT(flop_ctrlSets); - CHECK_CONTIGUOUS(flop_ctrlSets); - CHECK_FLAT(flop2ctrlSetId_map); - CHECK_CONTIGUOUS(flop2ctrlSetId_map); - - CHECK_FLAT(lut_type); - CHECK_CONTIGUOUS(lut_type); - - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - CHECK_FLAT(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - - CHECK_FLAT(node2pincount); - CHECK_CONTIGUOUS(node2pincount); - - CHECK_FLAT(net2pincount); - CHECK_CONTIGUOUS(net2pincount); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(pin_typeIds); - CHECK_CONTIGUOUS(pin_typeIds); - - CHECK_FLAT(flat_net2pin_start_map); - CHECK_CONTIGUOUS(flat_net2pin_start_map); - - CHECK_FLAT(pin2node_map); - CHECK_CONTIGUOUS(pin2node_map); - - CHECK_FLAT(sorted_net_map); - CHECK_CONTIGUOUS(sorted_net_map); - CHECK_FLAT(sorted_node_map); - CHECK_CONTIGUOUS(sorted_node_map); - - CHECK_FLAT(flat_node2prclstrCount); - CHECK_CONTIGUOUS(flat_node2prclstrCount); - CHECK_FLAT(flat_node2precluster_map); - CHECK_CONTIGUOUS(flat_node2precluster_map); - - CHECK_FLAT(site_nbrList); - CHECK_CONTIGUOUS(site_nbrList); - CHECK_FLAT(site_nbrRanges); - CHECK_CONTIGUOUS(site_nbrRanges); - CHECK_FLAT(site_nbrRanges_idx); - CHECK_CONTIGUOUS(site_nbrRanges_idx); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - CHECK_FLAT(addr2site_map); - CHECK_CONTIGUOUS(addr2site_map); - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - int numNodes = pos.numel() / 2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "runDLCudaLauncher", [&] { - runDLCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_bbox, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayX, int), - DREAMPLACE_TENSOR_DATA_PTR(net_pinIdArrayY, int), - DREAMPLACE_TENSOR_DATA_PTR(site_types, int), - DREAMPLACE_TENSOR_DATA_PTR(spiral_accessor, int), - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int), - DREAMPLACE_TENSOR_DATA_PTR(lut_flop_indices, int), - DREAMPLACE_TENSOR_DATA_PTR(flop2ctrlSetId_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flop_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(extended_ctrlSets, int), - DREAMPLACE_TENSOR_DATA_PTR(ext_ctrlSet_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(lut_type, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2outpinIdx_map, int), - DREAMPLACE_TENSOR_DATA_PTR(node2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(net2pincount, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin_typeIds, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_node_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2prclstrCount, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2precluster_map, int), - DREAMPLACE_TENSOR_DATA_PTR(is_mlab_node, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrList, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrRanges_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(addr2site_map, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - DREAMPLACE_TENSOR_DATA_PTR(special_nodes, int), - maxDist, xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, - num_sites_x, num_sites_y, num_clb_sites, num_lutflops, - minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, - spiralBegin, spiralEnd, intMinVal, - numGroups, netShareScoreMaxNetDegree, wlscoreMaxNetDegree, - lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, - CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, sliceId, - DREAMPLACE_TENSOR_DATA_PTR(site_nbr_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_nbrGroup_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(cumsum_curr_scl, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(validIndices_curr_scl, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_curr_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_top_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_pq_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_validIdx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_scl_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(site_next_stable, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ff, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_cksr, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_ce, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(activeStatus, int), - DREAMPLACE_TENSOR_DATA_PTR(illegalStatus, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_score_improv, int), - DREAMPLACE_TENSOR_DATA_PTR(site_score_improv, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_clb_siteIds, int) - ); - }); - //std::cout << "Run DL Iter "<< std::endl; -} - -//Legalize Mlabs -void legalizeMlab( - at::Tensor pos, - at::Tensor site_xy, - at::Tensor mlab_locX, - at::Tensor mlab_locY, - at::Tensor mlab_indices, - at::Tensor site2addr_map, - int num_mlab_nodes, - int num_sites_y, - int SIG_IDX, - int SLICE_CAPACITY, - at::Tensor dist_moved, - at::Tensor site_det_score, - at::Tensor inst_curr_bestScoreImprov, - at::Tensor inst_next_bestScoreImprov, - at::Tensor site_det_siteId, - at::Tensor site_det_sig_idx, - at::Tensor site_det_sig, - at::Tensor site_det_impl_lut, - at::Tensor inst_curr_detSite, - at::Tensor inst_curr_bestSite, - at::Tensor inst_next_detSite, - at::Tensor inst_next_bestSite, - at::Tensor sites_with_special_nodes) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(site_xy); - CHECK_CONTIGUOUS(site_xy); - - CHECK_FLAT(mlab_locX); - CHECK_CONTIGUOUS(mlab_locX); - CHECK_FLAT(mlab_locY); - CHECK_CONTIGUOUS(mlab_locY); - - CHECK_FLAT(mlab_indices); - CHECK_CONTIGUOUS(mlab_indices); - - CHECK_FLAT(site2addr_map); - CHECK_CONTIGUOUS(site2addr_map); - - int numNodes = pos.numel() / 2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "legalizeMlabCudaLauncher", [&] { - legalizeMlabCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + numNodes, - DREAMPLACE_TENSOR_DATA_PTR(site_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_locX, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_locY, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(mlab_indices, int), - DREAMPLACE_TENSOR_DATA_PTR(site2addr_map, int), - num_mlab_nodes, num_sites_y, SIG_IDX, SLICE_CAPACITY, - DREAMPLACE_TENSOR_DATA_PTR(dist_moved, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_score, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestScoreImprov, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(site_det_siteId, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig_idx, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_sig, int), - DREAMPLACE_TENSOR_DATA_PTR(site_det_impl_lut, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_curr_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_detSite, int), - DREAMPLACE_TENSOR_DATA_PTR(inst_next_bestSite, int), - DREAMPLACE_TENSOR_DATA_PTR(sites_with_special_nodes, int) - ); - }); - //std::cout << "Legalize MLabs"<< std::endl; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("initLegalization", &DREAMPLACE_NAMESPACE::initLegalization, - "initialize LUT/FF legalization (CUDA)"); - m.def("runDLIter", &DREAMPLACE_NAMESPACE::runDLIter, "Run DL Iteration (CUDA)"); - m.def("legalizeMlab", &DREAMPLACE_NAMESPACE::legalizeMlab, "Legalize Mlab (CUDA)"); -} - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu deleted file mode 100644 index 011decf9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu +++ /dev/null @@ -1,4322 +0,0 @@ -/** - * @file lut_ff_legalization_cuda_kernel.cu - * @author Rachel Selina (DREAMPlaceFPGA-PL) - * @date Aug 2023 - * @brief Legalize LUT/FF - */ -#include -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utility/src/print.cuh" -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" - -//local dependency -#include "lut_ff_legalization/src/graph_matching.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -#define THREAD_COUNT 64 -#define INVALID -1 -#define INPUT_PIN 1 -//Reset below values if required: Below values are for a maximum SLICE_CAPACITY of 20 -#define SLICE_MAX_CAP 20 -#define SIG_MAX_CAP 40 -#define CE_MAX_CAP 7 -#define CKSR_MAX_CAP 2 - -//Clear entries in candidate -inline __device__ void clear_cand_contents(const int tsPQ, const int SIG_IDX, - const int CKSR_IN_CLB, const int CE_IN_CLB, const int SLICE_CAPACITY, - int* site_sig_idx, int* site_sig, int* site_impl_lut, int* site_impl_ff, - int* site_impl_cksr, int* site_impl_ce) -{ - int topIdx(tsPQ*SIG_IDX); - int lutIdx = tsPQ*SLICE_CAPACITY; - int ckIdx = tsPQ*CKSR_IN_CLB; - int ceIdx = tsPQ*CE_IN_CLB; - - for(int sg = 0; sg < SIG_IDX; ++sg) - { - site_sig[topIdx + sg] = INVALID; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_impl_lut[lutIdx + sg] = INVALID; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_impl_ff[lutIdx + sg] = INVALID; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_impl_cksr[ckIdx + sg] = INVALID; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_impl_ce[ceIdx + sg] = INVALID; - } -} - -//Check if entry exists in array -inline __device__ bool val_in_array( - const int* array, const int arraySize, const int arrayIdx, const int val) -{ - for (int idx = 0; idx < arraySize; ++idx) - { - if (array[arrayIdx+idx] == val) - { - return true; - } - } - return false; -} - -/// define candidate_validity_check -// Candidate is valid if the instance is not commited to a site -inline __device__ bool candidate_validity_check( - const int* is_mlab_node, const int SLICE_CAPACITY, - const int topIdx, const int pqSigIdx, - const int siteId, const int* site_curr_pq_sig, - const int* inst_curr_detSite) -{ - //Check first instance if it is mlab - if (pqSigIdx == 2*SLICE_CAPACITY && - is_mlab_node[site_curr_pq_sig[topIdx]] == 1) - { - int pqInst = site_curr_pq_sig[topIdx]; - if (inst_curr_detSite[pqInst] != INVALID && - inst_curr_detSite[pqInst] != siteId) - { - return false; - } - } else - { - for (int i = 0; i < pqSigIdx; ++i) - { - int pqInst = site_curr_pq_sig[topIdx + i]; - - if (inst_curr_detSite[pqInst] != INVALID && - inst_curr_detSite[pqInst] != siteId) - { - return false; - } - } - } - return true; -} - -////SUBFUCTIONS//// - -//define add flop to candidate -inline __device__ bool add_flop_to_candidate_impl( - const int* node2outpinIdx_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, - const int* flop2ctrlSetId_map, const int* node2fence_region_map, - const int* res_lut, const int lutId, const int ffCKSR, const int ffCE, - const int ffId, const int half_ctrl_mode, const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, const int CKSR_IN_CLB, const int CE_IN_CLB, - const int BLE_CAPACITY, const int lut_maxShared, int* res_ff, int* res_cksr, - int* res_ce) -{ - if (half_ctrl_mode == 1) - { - for (int i = 0; i < CKSR_IN_CLB; ++i) - { - if (res_cksr[i] != INVALID && - res_cksr[i] != ffCKSR) - { - continue; - } - - for (int j = 0; j < CKSR_IN_CLB; ++j) - { - int ceIdx = CKSR_IN_CLB*i + j; - if (res_ce[ceIdx] != INVALID && - res_ce[ceIdx] != ffCE) - { - continue; - } - - int beg = i*HALF_SLICE_CAPACITY+j; - int end = beg + HALF_SLICE_CAPACITY; - for (int k = beg; k < end; k += BLE_CAPACITY) - { - if (res_ff[k] == INVALID) - { - res_ff[k] = ffId; - res_cksr[i] = ffCKSR; - res_ce[ceIdx] = ffCE; - return true; - } - } - } - } - } else - { - //FF Ctrls are SHARED across the SLICE - int ckID = INVALID; - for (int i = 0; i < CKSR_IN_CLB; ++i) - { - if (res_cksr[i] == ffCKSR || res_cksr[i] == INVALID) - { - ckID = i; - break; - } - } - - if (ckID != INVALID) - { - int fCtrlId = flop2ctrlSetId_map[ffId]; - int fCStartId = ext_ctrlSet_start_map[fCtrlId]; - int fCEndId = ext_ctrlSet_start_map[fCtrlId+1]; - - int upd_ctrls[SLICE_MAX_CAP]; - int num_upd_ctrls(0); - - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - if (res_ce[sg] != INVALID) - { - upd_ctrls[num_upd_ctrls] = res_ce[sg]; - ++num_upd_ctrls; - } - } - - int num_init_ctrls = num_upd_ctrls; - - //Ensure all FF ctrls can be accommodated in Slice - for (int j = fCStartId; j < fCEndId; ++j) - { - upd_ctrls[num_upd_ctrls] = extended_ctrlSets[j]; - ++num_upd_ctrls; - } - - if (num_upd_ctrls > num_init_ctrls) - { - sort_array(upd_ctrls, num_upd_ctrls); - remove_duplicates(upd_ctrls, num_upd_ctrls); - } - - if (num_upd_ctrls > CE_IN_CLB) - { - return false; - } - - int fIndex = INVALID; - for (int i = 0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_ff[i] == INVALID) - { - if(subSlice_compatibility(node2outpinIdx_map, - flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, - res_ff, res_lut, lutId, SLICE_CAPACITY, - BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, - i, ffId)) - { - fIndex = i; - break; - } - } else if (res_ff[i+1] == INVALID) - { - if(subSlice_compatibility(node2outpinIdx_map, - flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, - res_ff, res_lut, lutId, SLICE_CAPACITY, - BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, - i+1, ffId)) - { - fIndex = i+1; - break; - } - } - } - - if (fIndex != INVALID) - { - res_ff[fIndex] = ffId; - res_cksr[ckID] = ffCKSR; - - for (int j = 0; j < num_upd_ctrls; ++j) - { - res_ce[j] = upd_ctrls[j]; - } - for (int j = num_upd_ctrls; j < CE_IN_CLB; ++j) - { - res_ce[j] = INVALID; - } - return true; - } - } - } - return false; -} - -// define remove_invalid_neighbor -inline __device__ void remove_invalid_neighbor( - const int sIdx, const int sNbrIdx, int* site_nbr_idx, int* site_nbr) -{ - int temp[1024]; - int tempSize(0); - for (int i = 0; i < site_nbr_idx[sIdx]; ++i) - { - int instId = site_nbr[sNbrIdx + i]; - - if (instId != INVALID) - { - temp[tempSize] = instId; - ++tempSize; - } - } - - for (int j = 0; j < tempSize; ++j) - { - site_nbr[sNbrIdx+j] = temp[j]; - } - for (int j = tempSize; j < site_nbr_idx[sIdx]; ++j) - { - site_nbr[sNbrIdx+j] = INVALID; - } - site_nbr_idx[sIdx] = tempSize; - - //DBG - if (tempSize > 1000) - { - printf("WARN: remove_invalid_neighbor() has tempSize > 1000 for site: %d\n", sIdx); - } - //DBG -} -//addLUTToCandidateImpl -inline __device__ bool add_lut_to_cand_impl( - const int* node2outpinIdx_map, const int* lut_type, - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* pin2net_map, const int* pin_typeIds, const int* node2fence_region_map, - const int* res_ff, const int lutTypeInSliceUnit, const int lutId, - const int lut_maxShared, const int lutInstId, const int SLICE_CAPACITY, - const int BLE_CAPACITY, const int half_ctrl_mode, int* res_lut) -{ - if (half_ctrl_mode == 1) - { - for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - res_lut[i] = lutInstId; - return true; - } - } - for (int i=1; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - if (two_lut_compatibility_check(lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, lutTypeInSliceUnit, - lut_maxShared, res_lut[i-1], lutInstId)) - { - res_lut[i] = lutInstId; - return true; - } - } - } - } else - { - for (int i=0; i < SLICE_CAPACITY; i += BLE_CAPACITY) - { - if (res_lut[i] == INVALID) - { - if(subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i, lutInstId)) - { - res_lut[i] = lutInstId; - return true; - } - } else if(res_lut[i+1] == INVALID) - { - if (subSlice_compatibility(node2outpinIdx_map, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, node2fence_region_map, res_ff, res_lut, lutId, - SLICE_CAPACITY, BLE_CAPACITY, lut_maxShared, BLE_CAPACITY, i+1, lutInstId)) - { - res_lut[i+1] = lutInstId; - return true; - } - } - } - } - return false; -} - -//template -__device__ bool is_inst_in_cand_feasible( - const int* node2outpinIdx_map, const int* node2fence_region_map, - const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* flat_node2prclstrCount, const int* flat_node2precluster_map, - const int* flop2ctrlSetId_map, const int* flop_ctrlSets, - const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, - const int* site_det_impl_lut, const int* site_det_impl_ff, - const int* site_det_impl_cksr, const int* site_det_impl_ce, - const int* special_nodes, const int lutTypeInSliceUnit, const int lut_maxShared, - const int siteId, const int instId, const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, const int CKSR_IN_CLB, const int CE_IN_CLB, - const int lutId, const int ffId, const int half_ctrl_mode) -{ - ////DBG - //int dbgInstId = 326924; - //int dbgSId = 3595; - ////DBG - - int instPcl = instId*3; - - int sdlutId = siteId*SLICE_CAPACITY; - int sdckId = siteId*CKSR_IN_CLB; - int sdceId = siteId*CE_IN_CLB; - - int res_lut[SLICE_MAX_CAP]; - int res_ff[SLICE_MAX_CAP]; - int res_cksr[CKSR_MAX_CAP]; - int res_ce[CE_MAX_CAP]; - - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = site_det_impl_lut[sdlutId + sg]; - res_ff[sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - res_cksr[sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - res_ce[sg] = site_det_impl_ce[sdceId + sg]; - } - ///// - - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: Here for instId %d \n", siteId, instId); - //} - ////DBG - - bool lutFail(false); - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; - int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; - - if (node2fence_region_map[clInstId] == lutId) //LUT - { - if (!lutFail && - !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, - lutTypeInSliceUnit, lutId, lut_maxShared, clInstId, SLICE_CAPACITY, - BLE_CAPACITY, half_ctrl_mode, res_lut)) - { - lutFail = true; - } - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: Check add lut to cand impl for instId %d, lutFail %d \n", siteId, clInstId, lutFail); - //} - ////DBG - - } else if (node2fence_region_map[clInstId] == ffId) //FF - { - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: Check add flop to candidate impl for instId %d \n", siteId, clInstId); - //} - ////DBG - - if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, - ext_ctrlSet_start_map, flop2ctrlSetId_map, node2fence_region_map, - res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], - clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, - res_cksr, res_ce)) - { - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: add flop to candidate impl for instId %d returned False\n", siteId, clInstId); - //} - ////DBG - - return false; - } - } - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: Done for instId %d \n", siteId, clInstId); - //} - ////DBG - } - if (!lutFail) - { - return true; - } - - ////DBG - //if (siteId == dbgSId && instId == dbgInstId) - //{ - // printf("%d is inst in cand feasible: lutFail %d use graph matching\n", siteId, lutFail); - // printf("Contents of res_lut: "); - // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // printf("%d ", res_lut[sg]); - // } - // printf("\n"); - - // printf("Contents of res_lut with lut_type and special_nodes: "); - // for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // printf("%d", res_lut[sg]); - // if (res_lut[sg] != INVALID) - // { - // printf(" (%d, %d)", lut_type[res_lut[sg]], special_nodes[res_lut[sg]]); - // } - // printf(", "); - // } - // printf("\n"); - //} - ////DBG - - return fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, pin2net_map, pin_typeIds, - flat_node2pin_start_map, flat_node2pin_map, flat_node2precluster_map, - node2fence_region_map, special_nodes, half_ctrl_mode, lutTypeInSliceUnit, - lut_maxShared, instPcl, flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, - SLICE_CAPACITY, BLE_CAPACITY, lutId, res_lut, res_ff); -} - -inline __device__ bool add_inst_to_cand_impl( - const int* node2outpinIdx_map, const int* lut_type, - const int* flat_node2pin_start_map, const int* flat_node2pin_map, - const int* pin2net_map, const int* pin_typeIds, - const int* flat_node2prclstrCount, const int* flat_node2precluster_map, - const int* flop2ctrlSetId_map, const int* node2fence_region_map, - const int* flop_ctrlSets, const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, const int* special_nodes, - const int lutTypeInSliceUnit, const int lut_maxShared, const int instId, - const int lutId, const int ffId, const int half_ctrl_mode, const int CKSR_IN_CLB, - const int CE_IN_CLB, const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, int* nwCand_lut, - int* nwCand_ff, int* nwCand_cksr, int* nwCand_ce) -{ - int instPcl = instId*3; - - //array instantiation - int res_lut[SLICE_MAX_CAP]; - int res_ff[SLICE_MAX_CAP]; - int res_cksr[CKSR_MAX_CAP]; - int res_ce[CE_MAX_CAP]; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - res_lut[sg] = nwCand_lut[sg]; - res_ff[sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - res_cksr[sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - res_ce[sg] = nwCand_ce[sg]; - } - ///// - //DBG - - bool lutFail(false); - for (int idx = 0; idx < flat_node2prclstrCount[instId]; ++idx) - { - int clInstId = flat_node2precluster_map[instPcl + idx]; - int clInstCKSR = flop2ctrlSetId_map[clInstId]*3 + 1; - int clInstCE = flop2ctrlSetId_map[clInstId]*3 + 2; - - if (node2fence_region_map[clInstId] == lutId) //LUT - { - if (!lutFail && - !add_lut_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, node2fence_region_map, res_ff, - lutTypeInSliceUnit, lutId, lut_maxShared, clInstId, SLICE_CAPACITY, - BLE_CAPACITY, half_ctrl_mode, res_lut)) - { - lutFail = true; - } - } else if (node2fence_region_map[clInstId] == ffId) //FF - { - if(!add_flop_to_candidate_impl(node2outpinIdx_map, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, extended_ctrlSets, - ext_ctrlSet_start_map, flop2ctrlSetId_map, node2fence_region_map, - res_lut, lutId, flop_ctrlSets[clInstCKSR], flop_ctrlSets[clInstCE], - clInstId, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, - CKSR_IN_CLB, CE_IN_CLB, BLE_CAPACITY, lut_maxShared, res_ff, - res_cksr, res_ce)) - { - return false; - } - } - } - - if (!lutFail) - { - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = res_lut[sg]; - nwCand_ff[sg] = res_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = res_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = res_ce[sg]; - } - - return true; - } - - if(fit_luts_to_candidate_impl(node2outpinIdx_map, lut_type, pin2net_map, - pin_typeIds, flat_node2pin_start_map, flat_node2pin_map, - flat_node2precluster_map, node2fence_region_map, special_nodes, - half_ctrl_mode, lutTypeInSliceUnit, lut_maxShared, instPcl, - flat_node2prclstrCount[instId], NUM_BLE_PER_SLICE, SLICE_CAPACITY, - BLE_CAPACITY, lutId, res_lut, res_ff)) - { - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = res_lut[sg]; - nwCand_ff[sg] = res_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = res_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = res_ce[sg]; - } - - return true; - } - return false; -} - -//template -inline __device__ void remove_incompatible_neighbors( - const int* node2outpinIdx_map, const int* node2fence_region_map, - const int* lut_type, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* pin2net_map, const int* pin_typeIds, - const int* flat_node2prclstrCount, const int* flat_node2precluster_map, - const int* flop2ctrlSetId_map, const int* flop_ctrlSets, - const int* extended_ctrlSets, const int* ext_ctrlSet_start_map, - const int* site_det_impl_lut, const int* site_det_impl_ff, - const int* site_det_impl_cksr, const int* site_det_impl_ce, - const int* site_det_sig, const int* site_det_sig_idx, const int* special_nodes, - const int lutTypeInSliceUnit, const int lut_maxShared, const int siteId, - const int sNbrIdx, const int half_ctrl_mode, const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, const int SIG_IDX, const int CKSR_IN_CLB, - const int CE_IN_CLB, const int lutId, const int ffId, int* site_nbr_idx, - int* site_nbr) -{ - ////DBG - //int dbgSId = 3595; - ////DBG - - int sdtopId = siteId*SIG_IDX; - - ////DBG - //if (siteId == dbgSId) - //{ - // int sdtopId = siteId*SIG_IDX; - // int sdlutId = siteId*SLICE_CAPACITY; - // int sdckId = siteId*CKSR_IN_CLB; - // int sdceId = siteId*CE_IN_CLB; - // printf("%d remove incompatible neighbors for total neighbor instances of %d :", siteId, site_nbr_idx[siteId]); - - // for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) - // { - // printf("%d ", site_nbr[sNbrIdx + nbrId]); - // } - // printf("\n"); - - // ////// - // printf("There are %d elements in site_det_sig: ", site_det_sig_idx[siteId]); - // for(int sg = 0; sg < site_det_sig_idx[siteId]; ++sg) - // { - // printf("%d ",site_det_sig[sdtopId + sg]); - // } - // printf("\n"); - // printf("LUTs in site_det_impl_lut: "); - // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // printf("%d ", site_det_impl_lut[sdlutId + sg]); - // } - // printf("\n"); - // printf("FFs in site_det_impl_ff: "); - // for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - // { - // printf("%d ", site_det_impl_ff[sdlutId + sg]); - // } - // printf("\n"); - // printf("FFs CKSR: "); - // for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - // { - // printf("%d ", site_det_impl_cksr[sdckId + sg]); - // } - // printf("\n"); - // printf("FFs CE: "); - // for(int sg = 0; sg < CE_IN_CLB; ++sg) - // { - // printf("%d ", site_det_impl_ce[sdceId + sg]); - // } - // printf("\n"); - // ////// - //} - ////DBG - - for (int nbrId = 0; nbrId < site_nbr_idx[siteId]; ++nbrId) - { - int instId = site_nbr[sNbrIdx + nbrId]; - - ////DBG - //if (siteId == dbgSId) - //{ - // printf("%d remove incompatible neighbors %d of %d: Consider instId %d of type %d", - // siteId, nbrId, site_nbr_idx[siteId], instId, node2fence_region_map[instId]); - // if (node2fence_region_map[instId] == 1) - // { - // int clInstCKSR = flop2ctrlSetId_map[instId]*3 + 1; - // printf(" has clk %d and ctrls: ", flop_ctrlSets[clInstCKSR]); - // int cStartId = ext_ctrlSet_start_map[flop2ctrlSetId_map[instId]]; - // int cEndId = ext_ctrlSet_start_map[flop2ctrlSetId_map[instId]+1]; - // for (int ctrlId = cStartId; ctrlId < cEndId; ++ctrlId) - // { - // printf("%d ", extended_ctrlSets[ctrlId]); - // } - // } - // printf("\n"); - //} - ////DBG - - if (val_in_array(site_det_sig, site_det_sig_idx[siteId], sdtopId, instId) || - !is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, special_nodes, - lutTypeInSliceUnit, lut_maxShared, siteId, instId, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, - CE_IN_CLB, lutId, ffId, half_ctrl_mode)) - { - site_nbr[sNbrIdx + nbrId] = INVALID; - } - - ////DBG - //if (siteId == dbgSId) - //{ - // printf("%d remove incompatible neighbors %d of %d: Done with instId %d of type %d\n", - // siteId, nbrId, site_nbr_idx[siteId], instId, node2fence_region_map[instId]); - //} - ////DBG - - } - ////DBG - //if (siteId == dbgSId) - //{ - // printf("%d remove incompatible neighbors: Marked invalid instance neighbors in site_nbr \n", siteId); - //} - ////DBG - - //Remove invalid neighbor instances - remove_invalid_neighbor(siteId, sNbrIdx, site_nbr_idx, site_nbr); - ////DBG - //if (siteId == dbgSId) - //{ - // printf("%d remove incompatible neighbors: Updated site_nbr has %d instances\n", siteId, site_nbr_idx[siteId]); - //} - ////DBG - -} - -//WL Improv -template -__device__ void compute_wirelength_improv( - const T* pos_x, const T* pos_y, const T* net_bbox, const T* pin_offset_x, - const T* pin_offset_y, const T* net_weights, const T* site_xy, - const int* net2pincount, const int* flat_net2pin_start_map, - const int* net_pinIdArrayX, const int* net_pinIdArrayY, - const int* pin2node_map, const T xWirelenWt, const T yWirelenWt, - const int currNetId, const int res_siteId, const int cNIPIdx, - const int* currNetIntPins, T& wirelenImprov) -{ - //Compute wirelenImprov - int cNbId = currNetId*4; - T netXlen = net_bbox[cNbId+2] - net_bbox[cNbId]; - T netYlen = net_bbox[cNbId+3] - net_bbox[cNbId+1]; - if (cNIPIdx == net2pincount[currNetId]) - { - T bXLo(pin_offset_x[currNetIntPins[0]]); - T bXHi(pin_offset_x[currNetIntPins[0]]); - T bYLo(pin_offset_y[currNetIntPins[0]]); - T bYHi(pin_offset_y[currNetIntPins[0]]); - for (int poI = 1; poI < cNIPIdx; ++poI) - { - T poX = pin_offset_x[currNetIntPins[poI]]; - T poY = pin_offset_y[currNetIntPins[poI]]; - if (poX < bXLo) - { - bXLo = poX; - } else if (poX > bXHi) - { - bXHi = poX; - } - if (poY < bYLo) - { - bYLo = poY; - } else if (poY > bYHi) - { - bYHi = poY; - } - } - wirelenImprov += net_weights[currNetId] * - (xWirelenWt * (netXlen - (bXHi-bXLo)) + - yWirelenWt * (netYlen - (bYHi - bYLo))); - return; - } - - T bXLo(net_bbox[cNbId]); - T bYLo(net_bbox[cNbId+1]); - T bXHi(net_bbox[cNbId+2]); - T bYHi(net_bbox[cNbId+3]); - - int sId = res_siteId*2; - T locX = site_xy[sId]; - T locY = site_xy[sId+1]; - - if (locX <= bXLo) - { - bXLo = locX; - } else - { - int n2pId = flat_net2pin_start_map[currNetId]; - while (n2pId < flat_net2pin_start_map[currNetId+1] && - val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayX[n2pId])) - { - ++n2pId; - } - int reqPId = net_pinIdArrayX[n2pId]; - T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; - bXLo = DREAMPLACE_STD_NAMESPACE::min(pinX, locX); - } - - if (locX >= bXHi) - { - bXHi = locX; - } else - { - int n2pId = flat_net2pin_start_map[currNetId+1]-1; - while (n2pId >= flat_net2pin_start_map[currNetId] && - val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayX[n2pId])) - { - --n2pId; - } - int reqPId = net_pinIdArrayX[n2pId]; - T pinX = pos_x[pin2node_map[reqPId]] + pin_offset_x[reqPId]; - bXHi = DREAMPLACE_STD_NAMESPACE::max(pinX, locX); - } - - if (locY <= bYLo) - { - bYLo = locY; - } else - { - int n2pId = flat_net2pin_start_map[currNetId]; - while (n2pId < flat_net2pin_start_map[currNetId+1] && - val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayY[n2pId])) - { - ++n2pId; - } - int reqPId = net_pinIdArrayY[n2pId]; - T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; - bYLo = DREAMPLACE_STD_NAMESPACE::min(pinY, locY); - } - - if (locY >= bYHi) - { - bYHi = locY; - } else - { - int n2pId = flat_net2pin_start_map[currNetId+1]-1; - while (n2pId >= flat_net2pin_start_map[currNetId] && - val_in_array(currNetIntPins, cNIPIdx, 0, net_pinIdArrayY[n2pId])) - { - --n2pId; - } - int reqPId = net_pinIdArrayY[n2pId]; - T pinY = pos_y[pin2node_map[reqPId]] + pin_offset_y[reqPId]; - bYHi = DREAMPLACE_STD_NAMESPACE::max(pinY, locY); - } - wirelenImprov += net_weights[currNetId] * - (xWirelenWt * (netXlen - (bXHi-bXLo)) + - yWirelenWt * (netYlen - (bYHi - bYLo))); - return; -} - -//computeCandidateScore -template -__device__ void compute_candidate_score( - const T* pos_x, const T* pos_y, const T* pin_offset_x, const T* pin_offset_y, - const T* net_bbox, const T* net_weights, const T* site_xy, - const int* net_pinIdArrayX, const int* net_pinIdArrayY, - const int* flat_net2pin_start_map, const int* flat_node2pin_start_map, - const int* flat_node2pin_map, const int* sorted_net_map, const int* pin2net_map, - const int* pin2node_map, const int* net2pincount, const int* lut_type, - const T xWirelenWt, const T yWirelenWt, const T extNetCountWt, - const T wirelenImprovWt, const int netShareScoreMaxNetDegree, - const int wlscoreMaxNetDegree, const int half_ctrl_mode, const int* res_sig, - const int res_siteId, const int res_sigIdx, T &result) -{ - T netShareScore = T(0.0); - T wirelenImprov = T(0.0); - T typeScore = T(0.0); - int pins[512]; - int pinIdx = 0; - - for (int i = 0; i < res_sigIdx; ++i) - { - int instId = res_sig[i]; - //For macro nodes, same instId is repeated in sig - if (i != 0 && instId == res_sig[i-1]) continue; - for (int pId = flat_node2pin_start_map[instId]; - pId < flat_node2pin_start_map[instId+1]; ++pId) - { - pins[pinIdx] = flat_node2pin_map[pId]; - ++pinIdx; - } - if (half_ctrl_mode == 0) - { - typeScore += lut_type[instId]; - } - } - sort_array(pins, pinIdx); - //remove_duplicates(pins, pinIdx); - - if (pinIdx == 0) - { - result = T(0.0); - return; - } - - int maxNetDegree = DREAMPLACE_STD_NAMESPACE::max(netShareScoreMaxNetDegree, - wlscoreMaxNetDegree); - int currNetId = pin2net_map[pins[0]]; - - if (net2pincount[currNetId] > maxNetDegree) - { - result = T(0.0); - return; - } - - int numIntNets(0), numNets(0); - int currNetIntPins[512]; - int cNIPIdx = 0; - - currNetIntPins[cNIPIdx] = pins[0]; - ++cNIPIdx; - - for (int pId = 1; pId < pinIdx; ++pId) - { - int netId = pin2net_map[pins[pId]]; - if (netId == currNetId) - { - currNetIntPins[cNIPIdx] = pins[pId]; - ++cNIPIdx; - } else - { - if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) - { - ++numNets; - numIntNets += (cNIPIdx == net2pincount[currNetId] ? 1 : 0); - netShareScore += net_weights[currNetId] * (cNIPIdx - 1.0) / DREAMPLACE_STD_NAMESPACE::max(T(1.0), net2pincount[currNetId] - T(1.0)); - } - if (net2pincount[currNetId] <= wlscoreMaxNetDegree) - { - compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, - net_weights, site_xy, net2pincount, flat_net2pin_start_map, net_pinIdArrayX, - net_pinIdArrayY, pin2node_map, xWirelenWt, yWirelenWt, currNetId, res_siteId, - cNIPIdx, currNetIntPins, wirelenImprov); - } - currNetId = netId; - if (net2pincount[currNetId] > maxNetDegree) - { - break; - } - cNIPIdx = 0; - currNetIntPins[cNIPIdx] = pins[pId]; - ++cNIPIdx; - } - } - - //Handle last net - if (net2pincount[currNetId] <= netShareScoreMaxNetDegree) - { - ++numNets; - numIntNets += (cNIPIdx == net2pincount[currNetId] ? 1 : 0); - netShareScore += net_weights[currNetId] * (cNIPIdx - 1.0) / DREAMPLACE_STD_NAMESPACE::max(T(1.0), net2pincount[currNetId] - T(1.0)); - } - - if (net2pincount[currNetId] <= wlscoreMaxNetDegree) - { - compute_wirelength_improv(pos_x, pos_y, net_bbox, pin_offset_x, pin_offset_y, - net_weights, site_xy, net2pincount, flat_net2pin_start_map, net_pinIdArrayX, - net_pinIdArrayY, pin2node_map, xWirelenWt, yWirelenWt, currNetId, res_siteId, - cNIPIdx, currNetIntPins, wirelenImprov); - } - netShareScore /= (T(1.0) + extNetCountWt * (numNets - numIntNets)); - result = netShareScore + wirelenImprovWt * wirelenImprov; - - if (half_ctrl_mode == 0) - { - result += T(0.1)*typeScore; - } -} - -template -inline __device__ bool compare_pq_tops( - const T* site_curr_pq_score, const int* site_curr_pq_top_idx, const int* site_curr_pq_validIdx, - const int* site_curr_pq_siteId, const int* site_curr_pq_sig_idx, const int* site_curr_pq_sig, - const int* site_curr_pq_impl_lut, const int* site_curr_pq_impl_ff, const int* site_curr_pq_impl_cksr, - const int* site_curr_pq_impl_ce, const T* site_next_pq_score, const int* site_next_pq_top_idx, - const int* site_next_pq_validIdx, const int* site_next_pq_siteId, const int* site_next_pq_sig_idx, - const int* site_next_pq_sig, const int* site_next_pq_impl_lut, const int* site_next_pq_impl_ff, - const int* site_next_pq_impl_cksr, const int* site_next_pq_impl_ce, const int siteId, - const int sPQ, const int SIG_IDX, const int CKSR_IN_CLB, const int CE_IN_CLB, - const int SLICE_CAPACITY) -{ - //Check site_curr_pq TOP == site_next_pq TOP - int curr_pq_topId = sPQ+site_curr_pq_top_idx[siteId]; - int next_pq_topId = sPQ+site_next_pq_top_idx[siteId]; - - if (site_curr_pq_validIdx[curr_pq_topId] != site_next_pq_validIdx[next_pq_topId] || - site_curr_pq_validIdx[curr_pq_topId] != 1) - { - return false; - } - if (site_curr_pq_score[curr_pq_topId] == site_next_pq_score[next_pq_topId] && - site_curr_pq_siteId[curr_pq_topId] == site_next_pq_siteId[next_pq_topId] && - site_curr_pq_sig_idx[curr_pq_topId] == site_next_pq_sig_idx[next_pq_topId]) - { - //Check both sig - int currPQSigIdx = curr_pq_topId*SIG_IDX; - int nextPQSigIdx = next_pq_topId*SIG_IDX; - - for (int sg = 0; sg < site_curr_pq_sig_idx[curr_pq_topId]; ++sg) - { - if (site_curr_pq_sig[currPQSigIdx + sg] != site_next_pq_sig[nextPQSigIdx + sg]) - { - return false; - } - } - - //Check impl - int cCKRId = curr_pq_topId*CKSR_IN_CLB; - int cCEId = curr_pq_topId*CE_IN_CLB; - int cFFId = curr_pq_topId*SLICE_CAPACITY; - int nCKRId = next_pq_topId*CKSR_IN_CLB; - int nCEId = next_pq_topId*CE_IN_CLB; - int nFFId = next_pq_topId*SLICE_CAPACITY; - - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - if (site_curr_pq_impl_lut[cFFId + sg] != site_next_pq_impl_lut[nFFId + sg] || - site_curr_pq_impl_ff[cFFId + sg] != site_next_pq_impl_ff[nFFId + sg]) - { - return false; - } - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - if (site_curr_pq_impl_cksr[cCKRId + sg] != site_next_pq_impl_cksr[nCKRId + sg]) - { - return false; - } - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - if(site_curr_pq_impl_ce[cCEId + sg] != site_next_pq_impl_ce[nCEId + sg]) - { - return false; - } - } - return true; - } - return false; -} - -//////////////////////////////// -//////////////////////////////// -//////////////////////////////// - -template -__global__ void initNets( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* sorted_net_idx, - const int* pin2node_map, - const int* net2pincount, - const int num_nets, - const int wlscoreMaxNetDegree, - T* net_bbox, - int* net_pinIdArrayX, - int* net_pinIdArrayY) -{ - int i = threadIdx.x + blockDim.x * blockIdx.x; - while (i < num_nets) - { - const int idx = sorted_net_idx[i]; - - if (net2pincount[idx] > 0 && net2pincount[idx] <= wlscoreMaxNetDegree) - { - int pinIdxBeg = flat_net2pin_start_map[idx]; - int pinIdxEnd = flat_net2pin_start_map[idx+1]; - - int xLo = idx*4; - int yLo = xLo+1; - int xHi = xLo+2; - int yHi = xLo+3; - - int pnIdx = flat_net2pin_map[pinIdxBeg]; - int nodeIdx = pin2node_map[pnIdx]; - - net_bbox[xLo] = pos_x[nodeIdx] + pin_offset_x[pnIdx]; - net_bbox[yLo] = pos_y[nodeIdx] + pin_offset_y[pnIdx]; - net_bbox[xHi] = net_bbox[xLo]; - net_bbox[yHi] = net_bbox[yLo]; - - int tempX[512]; - int tempY[512]; - T temp_flat_net2pinX[512]; - T temp_flat_net2pinY[512]; - int tempId = 0; - - temp_flat_net2pinX[tempId] = net_bbox[xLo]; - temp_flat_net2pinY[tempId] = net_bbox[yLo]; - tempX[tempId] = pnIdx; - tempY[tempId] = pnIdx; - - ++tempId; - - //Update Net Bbox based on node location and pin offset - for (int pId = pinIdxBeg+1; pId < pinIdxEnd; ++pId) - { - int pinIdx = flat_net2pin_map[pId]; - int ndIdx = pin2node_map[pinIdx]; - - T valX = pos_x[ndIdx] + pin_offset_x[pinIdx]; - T valY = pos_y[ndIdx] + pin_offset_y[pinIdx]; - - if (valX < net_bbox[xLo]) - { - net_bbox[xLo] = valX; - } else if (valX > net_bbox[xHi]) - { - net_bbox[xHi] = valX; - } - - if (valY < net_bbox[yLo]) - { - net_bbox[yLo] = valY; - } else if (valY > net_bbox[yHi]) - { - net_bbox[yHi] = valY; - } - - temp_flat_net2pinX[tempId] = valX; - temp_flat_net2pinY[tempId] = valY; - - tempX[tempId] = pinIdx; - tempY[tempId] = pinIdx; - - ++tempId; - } - - //Sort pinIdArray based on node loc and pin offset - Bubble sort - for (int ix = 1; ix < tempId; ++ix) - { - for (int jx = 0; jx < tempId-1; ++jx) - { - //Sort X - if (temp_flat_net2pinX[jx] > temp_flat_net2pinX[jx+1]) - { - int tempVal = tempX[jx]; - tempX[jx] = tempX[jx+1]; - tempX[jx+1] = tempVal; - - T net2pinVal = temp_flat_net2pinX[jx]; - temp_flat_net2pinX[jx] = temp_flat_net2pinX[jx+1]; - temp_flat_net2pinX[jx+1] = net2pinVal; - } - - //Sort Y - if (temp_flat_net2pinY[jx] > temp_flat_net2pinY[jx+1]) - { - int tempVal = tempY[jx]; - tempY[jx] = tempY[jx+1]; - tempY[jx+1] = tempVal; - - T net2pinVal = temp_flat_net2pinY[jx]; - temp_flat_net2pinY[jx] = temp_flat_net2pinY[jx+1]; - temp_flat_net2pinY[jx+1] = net2pinVal; - } - } - } - - //Assign sorted values back - tempId = 0; - for (int pId = pinIdxBeg; pId < pinIdxEnd; ++pId) - { - net_pinIdArrayX[pId] = tempX[tempId]; - net_pinIdArrayY[pId] = tempY[tempId]; - ++tempId; - } - } - i += blockDim.x * gridDim.x; - } -} - -//TODO-Remove is_mlab_node when MLABs are treated separately -//Preclustering to handle carry chains and mlabs -template -__global__ void preClustering( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* sorted_node_map, - const int* sorted_node_idx, - const int* flat_net2pin_start_map, - const int* flat_net2pin_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* node2fence_region_map, - const int* node2outpinIdx_map, - const int* pin2net_map, - const int* pin2node_map, - const int* pin_typeIds, - const int* is_mlab_node, - const T preClusteringMaxDist, - const int lutId, - const int ffId, - const int num_nodes, - int* flat_node2precluster_map, - int* flat_node2prclstrCount) -{ - int i = threadIdx.x + blockDim.x * blockIdx.x; - //const int blk = blockDim.x * gridDim.x; - while (i < num_nodes) - { - const int idx = sorted_node_idx[i]; - //Only consider LUTs and skip MLABs - if (node2fence_region_map[idx] == lutId && is_mlab_node[idx] == 0) - { - int ff_insts[SLICE_MAX_CAP]; - T ff_dists[SLICE_MAX_CAP]; - int ffIdx = 0; - int nPIdx = idx*4; - - for (int nodeOutId = nPIdx; nodeOutId < nPIdx+4; ++nodeOutId) - { - int outPinId = node2outpinIdx_map[nodeOutId]; - if (outPinId == INVALID) continue; - - int outNetId = pin2net_map[outPinId]; - - int pinIdxBeg = flat_net2pin_start_map[outNetId]; - int pinIdxEnd = flat_net2pin_start_map[outNetId+1]; - - T instLocX = pos_x[idx] + pin_offset_x[outPinId]; - T instLocY = pos_y[idx] + pin_offset_y[outPinId]; - - for (int pinId = pinIdxBeg; pinId < pinIdxEnd; ++pinId) - { - int pinIdx = flat_net2pin_map[pinId]; - int nodeIdx = pin2node_map[pinIdx]; - - T distX = instLocX - pos_x[nodeIdx] - pin_offset_x[pinIdx]; - T distY = instLocY - pos_y[nodeIdx] - pin_offset_y[pinIdx]; - - T dist = DREAMPLACE_STD_NAMESPACE::abs(distX) + - DREAMPLACE_STD_NAMESPACE::abs(distY); - - if (pin_typeIds[pinIdx] == INPUT_PIN && - node2fence_region_map[nodeIdx] == ffId && - dist < preClusteringMaxDist) - { - ff_insts[ffIdx] = nodeIdx; - ff_dists[ffIdx] = dist; - ++ffIdx; - } - } - } - - //Check if ff is empty - if (ffIdx > 0) - { - //Sort ff_insts/ff_dists based on dist and sorted_node_map - for (int ix = 1; ix < ffIdx; ++ix) - { - for (int jx = 0; jx < ffIdx-1; ++jx) - { - if (ff_dists[jx] == ff_dists[jx+1]) - { - if (sorted_node_map[ff_insts[jx]] > sorted_node_map[ff_insts[jx+1]]) - { - int tempVal = ff_insts[jx]; - ff_insts[jx] = ff_insts[jx+1]; - ff_insts[jx+1] = tempVal; - - T distVal = ff_dists[jx]; - ff_dists[jx] = ff_dists[jx+1]; - ff_dists[jx+1] = distVal; - } - } else - { - if (ff_dists[jx] > ff_dists[jx+1]) - { - int tempVal = ff_insts[jx]; - ff_insts[jx] = ff_insts[jx+1]; - ff_insts[jx+1] = tempVal; - - T distVal = ff_dists[jx]; - ff_dists[jx] = ff_dists[jx+1]; - ff_dists[jx+1] = distVal; - } - } - } - } - - int nPIdx = idx*3; - - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[idx]] = ff_insts[0]; - ++flat_node2prclstrCount[idx]; - - int fcIdx = flop2ctrlSetId_map[ff_insts[0]]*3 + 1; - int cksr = flop_ctrlSets[fcIdx]; - - for (int fIdx = 1; fIdx < ffIdx; ++fIdx) - { - int ctrlIdx = flop2ctrlSetId_map[ff_insts[fIdx]]*3 + 1; - int fCksr = flop_ctrlSets[ctrlIdx]; - - if (fCksr == cksr) - { - flat_node2precluster_map[nPIdx + flat_node2prclstrCount[idx]] = ff_insts[fIdx]; - ++flat_node2prclstrCount[idx]; - break; - } - } - - //Sort precluster based on idx - for (int ix = nPIdx+1; ix < nPIdx + flat_node2prclstrCount[idx]; ++ix) - { - for (int jx = nPIdx; jx < nPIdx + flat_node2prclstrCount[idx]-1; ++jx) - { - if (sorted_node_map[flat_node2precluster_map[jx]] > sorted_node_map[flat_node2precluster_map[jx+1]]) - { - int val = flat_node2precluster_map[jx]; - flat_node2precluster_map[jx] = flat_node2precluster_map[jx+1]; - flat_node2precluster_map[jx+1] = val; - } - } - } - - for (int prcl = 0; prcl < flat_node2prclstrCount[idx]; ++prcl) - { - int fIdx = flat_node2precluster_map[nPIdx + prcl]; - int fID = fIdx*3; - if (fIdx != idx) - { - for (int cl = 0; cl < flat_node2prclstrCount[idx]; ++cl) - { - flat_node2precluster_map[fID + cl] = flat_node2precluster_map[nPIdx + cl]; - } - flat_node2prclstrCount[fIdx] = flat_node2prclstrCount[idx]; - } - } - } - } - i += blockDim.x * gridDim.x; - } -} - -//runDLIteration -template -__global__ void runDLIteration( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_bbox, - const T* site_xy, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* node2fence_region_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* lut_type, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* net2pincount, - const int* pin2net_map, - const int* pin_typeIds, - const int* flat_net2pin_start_map, - const int* pin2node_map, - const int* sorted_net_map, - const int* sorted_node_map, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* is_mlab_node, - const int* site_nbrList, - const int* site_nbrRanges, - const int* site_nbrRanges_idx, - const T* net_weights, - const int* addr2site_map, - const int* special_nodes, - const int num_clb_sites, - const int minStableIter, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int minNeighbors, - const int intMinVal, - const int numGroups, - const int netShareScoreMaxNetDegree, - const int wlscoreMaxNetDegree, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const T xWirelenWt, - const T yWirelenWt, - const T wirelenImprovWt, - const T extNetCountWt, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - int* validIndices_curr_scl, - int* site_nbr_idx, - int* site_nbr, - int* site_nbrGroup_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_validIdx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - T* site_det_score, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - T* inst_next_bestScoreImprov, - int* inst_next_bestSite, - int* inst_score_improv, - int* site_score_improv - ) -{ - for (int sIdx = threadIdx.x + blockDim.x * blockIdx.x; - sIdx < num_clb_sites; sIdx += blockDim.x*gridDim.x) - { - int siteId = addr2site_map[sIdx]; - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - - int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - int sdckId = sIdx*CKSR_IN_CLB; - int sdceId = sIdx*CE_IN_CLB; - - int sclSigId = sSCL*SIG_IDX; - int scllutIdx = sSCL*SLICE_CAPACITY; - int sclckIdx = sSCL*CKSR_IN_CLB; - int sclceIdx = sSCL*CE_IN_CLB; - - //(a)Try to commit Top candidates - char commitTopCandidate(INVALID); - - int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); - int topIdx(tsPQ*SIG_IDX); - int lutIdx = tsPQ*SLICE_CAPACITY; - int ckIdx = tsPQ*CKSR_IN_CLB; - int ceIdx = tsPQ*CE_IN_CLB; - - if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || - !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, - site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], - site_curr_pq_sig, inst_curr_detSite)) - { - commitTopCandidate = 0; - } else { - - for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) - { - int pqInst = site_curr_pq_sig[topIdx + pIdx]; - - if (inst_curr_detSite[pqInst] != siteId && - inst_curr_bestSite[pqInst] != siteId) - { - commitTopCandidate = 0; - break; - } - } - } - - if (commitTopCandidate == INVALID) - { - ////// - site_det_score[sIdx] = site_curr_pq_score[tsPQ]; - site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; - site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; - - for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) - { - site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; - site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; - } - ////// - - for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) - { - int sigInst = site_det_sig[sdtopId + iSig]; - inst_next_detSite[sigInst] = siteId; - } - - //Remove Incompatible Neighbors - remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, site_det_sig, - site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, - sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, - site_nbr_idx, site_nbr); - - //Clear pq and make scl only contain the committed candidate - //int sclCount = 0; - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - //if (site_next_pq_validIdx[nPQId] != INVALID) - //{ - //Clear contents thoroughly - clear_cand_contents( - nPQId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[nPQId] = INVALID; - site_next_pq_sig_idx[nPQId] = 0; - site_next_pq_siteId[nPQId] = INVALID; - site_next_pq_score[nPQId] = T(0.0); - //++sclCount; - //if (sclCount == site_next_pq_idx[sIdx]) - //{ - // break; - //} - //} - } - site_next_pq_idx[sIdx] = 0; - site_next_pq_top_idx[sIdx] = INVALID; - - int sclCount = 0; - for (int vId = 0; vId < SCL_IDX; ++vId) - { - int cSclId = sSCL + vId; - if (site_curr_scl_validIdx[cSclId] != INVALID) - { - //Clear contents thoroughly - clear_cand_contents( - cSclId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - - site_curr_scl_validIdx[cSclId] = INVALID; - site_curr_scl_sig_idx[cSclId] = 0; - site_curr_scl_siteId[cSclId] = INVALID; - site_curr_scl_score[cSclId] = 0.0; - ++sclCount; - if (sclCount == site_curr_scl_idx[sIdx]) - { - break; - } - } - } - site_curr_scl_idx[sIdx] = 0; - - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - ///// - commitTopCandidate = 1; - } - - if (commitTopCandidate == 0) - { - //Remove invalid candidates from site PQ - if (site_next_pq_idx[sIdx] > 0) - { - //int snCnt = 0; - //int maxEntries = site_next_pq_idx[sIdx]; - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - int tpIdx = ssPQ*SIG_IDX; - - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, - site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], - site_next_pq_sig, inst_curr_detSite)) - { - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[ssPQ] = INVALID; - site_next_pq_sig_idx[ssPQ] = 0; - site_next_pq_siteId[ssPQ] = INVALID; - site_next_pq_score[ssPQ] = 0.0; - --site_next_pq_idx[sIdx]; - } - //++snCnt; - //if (snCnt == maxEntries) - //{ - // break; - //} - } - } - - site_next_pq_top_idx[sIdx] = INVALID; - - if (site_next_pq_idx[sIdx] > 0) - { - int snCnt = 0; - int maxEntries = site_next_pq_idx[sIdx]; - T maxScore(-1000.0); - int maxScoreId(INVALID); - //Recompute top idx - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (site_next_pq_score[ssPQ] > maxScore) - { - maxScore = site_next_pq_score[ssPQ]; - maxScoreId = nIdx; - } - ++snCnt; - if (snCnt == maxEntries) - { - break; - } - } - } - site_next_pq_top_idx[sIdx] = maxScoreId; - } - } - - //Remove invalid candidates from seed candidate list (scl) - if (site_curr_scl_idx[sIdx] > 0) - { - int sclCount = 0; - int maxEntries = site_curr_scl_idx[sIdx]; - for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) - { - int ssPQ = sSCL + nIdx; - int tpIdx = ssPQ*SIG_IDX; - - if (site_curr_scl_validIdx[ssPQ] != INVALID) - { - if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, - site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], - site_curr_scl_sig, inst_curr_detSite)) - { - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - - site_curr_scl_validIdx[ssPQ] = INVALID; - site_curr_scl_sig_idx[ssPQ] = 0; - site_curr_scl_siteId[ssPQ] = INVALID; - site_curr_scl_score[ssPQ] = 0.0; - --site_curr_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - //If site.scl becomes empty, add site.det into it as the seed - if (site_curr_scl_idx[sIdx] == 0) - { - - //site.curr.scl.emplace_back(site.det); - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - } - } - - // (c) removeCommittedNeighbors(site) - for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) - { - int siteInst = site_nbr[sNbrIdx + sNIdx]; - if (inst_curr_detSite[siteInst] != INVALID) - { - site_nbr[sNbrIdx + sNIdx] = INVALID; - } - } - remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); - - // (d) addNeighbors(site) - ////reuse site_nbrGroup_idx to store the Ids for STAGGERED NEW CANDIDATE ADDITION - int maxNeighbors = site_nbrRanges[sIdx*(numGroups+1) + numGroups]; - if (site_nbr_idx[sIdx] < minNeighbors && site_nbrGroup_idx[sIdx] <= maxNeighbors) - { - int beg = site_nbrGroup_idx[sIdx]; - ////STAGGERED ADDITION SET TO SLICE/16 or SLICE_CAPACITY/8 - ///For ISPD'2016 benchmarks, SLICE=32 and SLICE_CAPACITY=16 - int end = DREAMPLACE_STD_NAMESPACE::min(site_nbrGroup_idx[sIdx]+SLICE_CAPACITY/8, maxNeighbors); - site_nbrGroup_idx[sIdx] = end; - - for (int aNIdx = beg; aNIdx < end; ++aNIdx) - { - int instId = site_nbrList[sNbrIdx + aNIdx]; - - if (inst_curr_detSite[instId] == INVALID && - is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, - lut_type, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, flat_node2prclstrCount, - flat_node2precluster_map, flop2ctrlSetId_map, flop_ctrlSets, - extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, - special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, instId, - SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, half_ctrl_mode)) - { - site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + aNIdx]; - ++site_nbr_idx[sIdx]; - } - } - } - - //Generate indices for kernel_2 - int validId = 0; - for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) - { - int siteCurrIdx = sSCL + scsIdx; - if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) - { - validIndices_curr_scl[sSCL+validId] = siteCurrIdx; - ++validId; - } - if (validId == site_curr_scl_idx[sIdx]) break; - } - - // (e) createNewCandidates(site) - //Generate new candidates by merging site_nbr to site_curr_scl - - const int limit_x = site_curr_scl_idx[sIdx]; - const int limit_y = site_nbr_idx[sIdx]; - ////RESTRICTED NEW CANDIDATE EXPLORATION SET TO SLICE/8 or SLICE_CAPACITY/4 - const int limit_cands = DREAMPLACE_STD_NAMESPACE::min(SLICE_CAPACITY/4,limit_x*limit_y); - - for (int scsIdx = 0; scsIdx < limit_cands; ++scsIdx) - { - int sclId = scsIdx/limit_y; - int snIdx = scsIdx/limit_x; - int siteCurrIdx = validIndices_curr_scl[sSCL + sclId]; - - ///// - int sCKRId = siteCurrIdx*CKSR_IN_CLB; - int sCEId = siteCurrIdx*CE_IN_CLB; - int sFFId = siteCurrIdx*SLICE_CAPACITY; - int sGId = siteCurrIdx*SIG_IDX; - - T nwCand_score = site_curr_scl_score[siteCurrIdx]; - int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; - int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; - - //array instantiation - int nwCand_sig[SIG_MAX_CAP]; - int nwCand_lut[SLICE_MAX_CAP]; - int nwCand_ff[SLICE_MAX_CAP]; - int nwCand_ce[CE_MAX_CAP]; - int nwCand_cksr[CKSR_MAX_CAP]; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; - nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; - } - - int instId = site_nbr[sNbrIdx + snIdx]; - int instPcl = instId*3; - - int addInstToSig = INVALID; - if (nwCand_sigIdx >= 2*SLICE_CAPACITY) - { - addInstToSig = 0; - } - - if (addInstToSig == INVALID) - { - int temp[4]; //Max precluster size = 3 - int tIdx(0); - - for (int el = 0; el < flat_node2prclstrCount[instId]; ++el) - { - int newInstId = flat_node2precluster_map[instPcl+el]; - if (!val_in_array(nwCand_sig, nwCand_sigIdx, 0, newInstId)) - { - temp[tIdx] = newInstId; - ++tIdx; - } else - { - addInstToSig = 0; - break; - } - } - - if (addInstToSig == INVALID && (nwCand_sigIdx + tIdx > 2*SLICE_CAPACITY)) - { - addInstToSig = 0; - } - - if (addInstToSig == INVALID) - { - for (int mBIdx = 0; mBIdx < tIdx; ++mBIdx) - { - nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; - ++nwCand_sigIdx; - } - addInstToSig = 1; - } - } - - if (addInstToSig == 1) - { - //check cand sig is in site_next_pq - int candSigInSiteNextPQ = INVALID; - //int cnt = 0; - for (int i = 0; i < PQ_IDX; ++i) - { - int sigIdx = sPQ + i; - if (site_next_pq_validIdx[sigIdx] != INVALID) - { - if (site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) - { - int pqIdx(sigIdx*SIG_IDX), mtch(0); - - for (int k = 0; k < nwCand_sigIdx; ++k) - { - for (int l = 0; l < nwCand_sigIdx; ++l) - { - if (site_next_pq_sig[pqIdx + l] == nwCand_sig[k]) - { - ++mtch; - break; - } - } - } - if (mtch == nwCand_sigIdx) - { - candSigInSiteNextPQ = 1; - break; - } - } - } - } - - if (candSigInSiteNextPQ == INVALID && - add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, flat_node2prclstrCount, - flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, - flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, - lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, half_ctrl_mode, - CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) - { - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlscoreMaxNetDegree, half_ctrl_mode, - nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); - - int nxtId(INVALID); - - if (site_next_pq_idx[sIdx] < PQ_IDX) - { - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (site_next_pq_validIdx[sPQ+vId] == INVALID) - { - nxtId = vId; - ++site_next_pq_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (ckscore > site_next_pq_score[sPQ + vId]) - { - ckscore = site_next_pq_score[sPQ + vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - int nTId = sPQ + nxtId; - int nCKRId = nTId*CKSR_IN_CLB; - int nCEId = nTId*CE_IN_CLB; - int nFFId = nTId*SLICE_CAPACITY; - int nSGId = nTId*SIG_IDX; - - ///// - site_next_pq_validIdx[nTId] = 1; - site_next_pq_score[nTId] = nwCand_score; - site_next_pq_siteId[nTId] = nwCand_siteId; - site_next_pq_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - - if (site_next_pq_idx[sIdx] == 1 || - nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) - { - site_next_pq_top_idx[sIdx] = nxtId; - } - - nxtId = INVALID; - - if (site_next_scl_idx[sIdx] < SCL_IDX) - { - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (site_next_scl_validIdx[sSCL+vId] == INVALID) - { - nxtId = vId; - ++site_next_scl_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (ckscore > site_next_scl_score[sSCL+vId]) - { - ckscore = site_next_scl_score[sSCL+vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - ///// - nTId = sSCL + nxtId; - nCKRId = nTId*CKSR_IN_CLB; - nCEId = nTId*CE_IN_CLB; - nFFId = nTId*SLICE_CAPACITY; - nSGId = nTId*SIG_IDX; - - site_next_scl_validIdx[nTId] = 1; - site_next_scl_score[nTId] = nwCand_score; - site_next_scl_siteId[nTId] = nwCand_siteId; - site_next_scl_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - } - } - } - } - } - - //Remove all candidates in scl that is worse than the worst candidate in PQ - if (site_next_pq_idx[sIdx] > 0) - { - //Find worst candidate in PQ - T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); - - int sclCount = 0; - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - if (site_next_pq_validIdx[nPQId] != INVALID) - { - if (ckscore > site_next_pq_score[nPQId]) - { - ckscore = site_next_pq_score[nPQId]; - } - ++sclCount; - if (sclCount == site_next_pq_idx[sIdx]) - { - break; - } - } - } - - //Invalidate worst ones in scl - sclCount = 0; - int maxEntries = site_next_scl_idx[sIdx]; - for (int ckId = 0; ckId < SCL_IDX; ++ckId) - { - int vId = sSCL + ckId; - if (site_next_scl_validIdx[vId] != INVALID) - { - if (ckscore > site_next_scl_score[vId]) - { - //Clear contents thoroughly - clear_cand_contents( - vId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce); - - site_next_scl_validIdx[vId] = INVALID; - site_next_scl_sig_idx[vId] = 0; - site_next_scl_siteId[vId] = INVALID; - site_next_scl_score[vId] = 0.0; - --site_next_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - - //Update stable Iteration count - if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && - compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, - site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, - site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, - site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, - site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, - site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, - site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, - sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) - { - site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; - } else - { - site_next_stable[sIdx] = 0; - } - - //// (f) broadcastTopCandidate(site) - if (site_next_pq_idx[sIdx] > 0) - { - int tpIdx = sPQ + site_next_pq_top_idx[sIdx]; - int topSigId = tpIdx*SIG_IDX; - - T scoreImprov = site_next_pq_score[tpIdx] - site_det_score[sIdx]; - - ////UPDATED SEQUENTIAL PORTION - int scoreImprovInt = DREAMPLACE_STD_NAMESPACE::max(int(scoreImprov*10000), intMinVal); - site_score_improv[sIdx] = scoreImprovInt + siteId; - - for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[tpIdx]; ++ssIdx) - { - int instId = site_next_pq_sig[topSigId + ssIdx]; - - if (inst_curr_detSite[instId] == INVALID) - { - atomicMax(&inst_score_improv[instId], scoreImprovInt); - } - } - } - } -} - -//runDLIteration split kernel 1 -template -__global__ void runDLIteration_kernel_1( - const int* node2fence_region_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* lut_type, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* pin2net_map, - const int* pin_typeIds, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* is_mlab_node, - const int* site_nbrList, - const int* site_nbrRanges, - const int* site_nbrRanges_idx, - const int* addr2site_map, - const int* special_nodes, - const int num_clb_sites, - const int minStableIter, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int minNeighbors, - const int numGroups, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - int* site_nbr_idx, - int* site_nbr, - int* site_nbrGroup_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - T* site_det_score, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* validIndices_curr_scl, - int* cumsum_curr_scl - ) -{ - for (int sIdx = threadIdx.x + blockDim.x * blockIdx.x; - sIdx < num_clb_sites; sIdx += blockDim.x*gridDim.x) - { - int siteId = addr2site_map[sIdx]; - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - - int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - int sdckId = sIdx*CKSR_IN_CLB; - int sdceId = sIdx*CE_IN_CLB; - - int sclSigId = sSCL*SIG_IDX; - int scllutIdx = sSCL*SLICE_CAPACITY; - int sclckIdx = sSCL*CKSR_IN_CLB; - int sclceIdx = sSCL*CE_IN_CLB; - - //(a)Try to commit Top candidates - int commitTopCandidate(INVALID); - - int tsPQ(sPQ + site_curr_pq_top_idx[sIdx]); - int topIdx(tsPQ*SIG_IDX); - int lutIdx = tsPQ*SLICE_CAPACITY; - int ckIdx = tsPQ*CKSR_IN_CLB; - int ceIdx = tsPQ*CE_IN_CLB; - - if (site_curr_pq_idx[sIdx] == 0 || site_curr_stable[sIdx] < minStableIter || - !candidate_validity_check(is_mlab_node, SLICE_CAPACITY, topIdx, - site_curr_pq_sig_idx[tsPQ], site_curr_pq_siteId[tsPQ], - site_curr_pq_sig, inst_curr_detSite)) - { - commitTopCandidate = 0; - } else { - for (int pIdx = 0; pIdx < site_curr_pq_sig_idx[tsPQ]; ++pIdx) - { - int pqInst = site_curr_pq_sig[topIdx + pIdx]; - - if (inst_curr_detSite[pqInst] != siteId && - inst_curr_bestSite[pqInst] != siteId) - { - commitTopCandidate = 0; - break; - } - } - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d has commitTopCandidate: %d\n", sIdx, siteId, commitTopCandidate); - //} - ////DBG - - if (commitTopCandidate == INVALID) - { - ////// - site_det_score[sIdx] = site_curr_pq_score[tsPQ]; - site_det_siteId[sIdx] = site_curr_pq_siteId[tsPQ]; - site_det_sig_idx[sIdx] = site_curr_pq_sig_idx[tsPQ]; - - for(int sg = 0; sg < site_curr_pq_sig_idx[tsPQ]; ++sg) - { - site_det_sig[sdtopId + sg] = site_curr_pq_sig[topIdx + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_det_impl_lut[sdlutId + sg] = site_curr_pq_impl_lut[lutIdx + sg]; - site_det_impl_ff[sdlutId + sg] = site_curr_pq_impl_ff[lutIdx + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_det_impl_cksr[sdckId + sg] = site_curr_pq_impl_cksr[ckIdx + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_det_impl_ce[sdceId + sg] = site_curr_pq_impl_ce[ceIdx + sg]; - } - ////// - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d commitTopCandidate: %d assign site_curr pq top to site_det with sig size: %d\n", sIdx, siteId, commitTopCandidate, site_det_sig_idx[sIdx]); - //} - ////DBG - - for(int iSig = 0; iSig < site_det_sig_idx[sIdx]; ++iSig) - { - int sigInst = site_det_sig[sdtopId + iSig]; - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d commitTopCandidate: %d assign sigInst %d to site \n", sIdx, siteId, commitTopCandidate, sigInst); - //} - ////DBG - - inst_next_detSite[sigInst] = siteId; - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d commitTopCandidate: %d update inst_next_detSite \n", sIdx, siteId, commitTopCandidate); - //} - ////DBG - - //Remove Incompatible Neighbors - remove_incompatible_neighbors(node2outpinIdx_map, node2fence_region_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, pin2net_map, pin_typeIds, - flat_node2prclstrCount, flat_node2precluster_map, flop2ctrlSetId_map, - flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, site_det_sig, - site_det_sig_idx, special_nodes, lutTypeInSliceUnit, lut_maxShared, sIdx, - sNbrIdx, half_ctrl_mode, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, lutId, ffId, - site_nbr_idx, site_nbr); - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d commitTopCandidate: %d complete remove incompatible neighbors\n", sIdx, siteId, commitTopCandidate); - //} - ////DBG - - //Clear pq and make scl only contain the committed candidate - //int sclCount = 0; - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - //if (site_next_pq_validIdx[nPQId] != INVALID) - //{ - //Clear contents thoroughly - clear_cand_contents( - nPQId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[nPQId] = INVALID; - site_next_pq_sig_idx[nPQId] = 0; - site_next_pq_siteId[nPQId] = INVALID; - site_next_pq_score[nPQId] = T(0.0); - //++sclCount; - //if (sclCount == site_next_pq_idx[sIdx]) - //{ - // break; - //} - //} - } - site_next_pq_idx[sIdx] = 0; - site_next_pq_top_idx[sIdx] = INVALID; - - int sclCount = 0; - for (int vId = 0; vId < SCL_IDX; ++vId) - { - int cSclId = sSCL + vId; - if (site_curr_scl_validIdx[cSclId] != INVALID) - { - //Clear contents thoroughly - clear_cand_contents( - cSclId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - - site_curr_scl_validIdx[cSclId] = INVALID; - site_curr_scl_sig_idx[cSclId] = 0; - site_curr_scl_siteId[cSclId] = INVALID; - site_curr_scl_score[cSclId] = 0.0; - ++sclCount; - if (sclCount == site_curr_scl_idx[sIdx]) - { - break; - } - } - } - site_curr_scl_idx[sIdx] = 0; - - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - ///// - commitTopCandidate = 1; - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // //printf("%d Site %d: After possible commit has commitTopCandidate: %d\n", sIdx, siteId, commitTopCandidate); - // printf("%d Site %d: Completed (a)", sIdx, siteId); - //} - ////DBG - - if (commitTopCandidate == 0) - { - //(b) removeInvalidCandidates - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d with %d elements in next PQ\n", sIdx, siteId, commitTopCandidate, site_next_pq_idx[sIdx]); - //} - ////DBG - - //Remove invalid candidates from site PQ - if (site_next_pq_idx[sIdx] > 0) - { - //int snCnt = 0; - //int maxEntries = site_next_pq_idx[sIdx]; - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - int tpIdx = ssPQ*SIG_IDX; - - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, - site_next_pq_sig_idx[ssPQ], site_next_pq_siteId[ssPQ], - site_next_pq_sig, inst_curr_detSite)) - { - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce); - - site_next_pq_validIdx[ssPQ] = INVALID; - site_next_pq_sig_idx[ssPQ] = 0; - site_next_pq_siteId[ssPQ] = INVALID; - site_next_pq_score[ssPQ] = 0.0; - --site_next_pq_idx[sIdx]; - } - //++snCnt; - //if (snCnt == maxEntries) - //{ - // break; - //} - } - } - - site_next_pq_top_idx[sIdx] = INVALID; - - if (site_next_pq_idx[sIdx] > 0) - { - int snCnt = 0; - int maxEntries = site_next_pq_idx[sIdx]; - T maxScore(-1000.0); - int maxScoreId(INVALID); - //Recompute top idx - for (int nIdx = 0; nIdx < PQ_IDX; ++nIdx) - { - int ssPQ = sPQ + nIdx; - if (site_next_pq_validIdx[ssPQ] != INVALID) - { - if (site_next_pq_score[ssPQ] > maxScore) - { - maxScore = site_next_pq_score[ssPQ]; - maxScoreId = nIdx; - } - ++snCnt; - if (snCnt == maxEntries) - { - break; - } - } - } - site_next_pq_top_idx[sIdx] = maxScoreId; - } - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d removed invalid candiates in next PQ with %d elements, site curr scl has %d elements \n", - // sIdx, siteId, commitTopCandidate, site_next_pq_idx[sIdx], site_curr_scl_idx[sIdx]); - //} - ////DBG - - //Remove invalid candidates from seed candidate list (scl) - if (site_curr_scl_idx[sIdx] > 0) - { - int sclCount = 0; - int maxEntries = site_curr_scl_idx[sIdx]; - for (int nIdx = 0; nIdx < SCL_IDX; ++nIdx) - { - int ssPQ = sSCL + nIdx; - int tpIdx = ssPQ*SIG_IDX; - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d nIdx %d ssPQ %d tpIdx %d site_curr_scl_validIdx[ssPQ] %d\n", - // sIdx, siteId, commitTopCandidate, nIdx, ssPQ, tpIdx, site_curr_scl_validIdx[ssPQ]); - //} - ////DBG - - if (site_curr_scl_validIdx[ssPQ] != INVALID) - { - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d, site_curr_scl_sig_idx[ssPQ] %d\n", - // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ], site_curr_scl_sig_idx[ssPQ]); - //} - ////DBG - - if (!candidate_validity_check(is_mlab_node, SLICE_CAPACITY, tpIdx, - site_curr_scl_sig_idx[ssPQ], site_curr_scl_siteId[ssPQ], - site_curr_scl_sig, inst_curr_detSite)) - { - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d before clear_cand_contents %d\n", - // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ]); - //} - ////DBG - //Clear contents thoroughly - clear_cand_contents( - ssPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce); - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d ssPQ %d site_curr_scl_validIdx[ssPQ] %d after clear_cand_contents %d\n", - // sIdx, siteId, commitTopCandidate, ssPQ, site_curr_scl_validIdx[ssPQ]); - //} - ////DBG - - site_curr_scl_validIdx[ssPQ] = INVALID; - site_curr_scl_sig_idx[ssPQ] = 0; - site_curr_scl_siteId[ssPQ] = INVALID; - site_curr_scl_score[ssPQ] = 0.0; - --site_curr_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d removed invalid candiates in site curr scl with %d elements \n", - // sIdx, siteId, commitTopCandidate, site_curr_scl_idx[sIdx]); - //} - ////DBG - - - //If site.scl becomes empty, add site.det into it as the seed - if (site_curr_scl_idx[sIdx] == 0) - { - - site_curr_scl_score[sSCL] = site_det_score[sIdx]; - site_curr_scl_siteId[sSCL] = site_det_siteId[sIdx]; - site_curr_scl_sig_idx[sSCL] = site_det_sig_idx[sIdx]; - site_curr_scl_validIdx[sSCL] = 1; - - for(int sg = 0; sg < site_det_sig_idx[sIdx]; ++sg) - { - site_curr_scl_sig[sclSigId + sg] = site_det_sig[sdtopId + sg]; - } - for(int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[scllutIdx + sg] = site_det_impl_lut[sdlutId + sg]; - site_curr_scl_impl_ff[scllutIdx + sg] = site_det_impl_ff[sdlutId + sg]; - } - for(int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[sclckIdx + sg] = site_det_impl_cksr[sdckId + sg]; - } - for(int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[sclceIdx + sg] = site_det_impl_ce[sdceId + sg]; - } - ++site_curr_scl_idx[sIdx]; - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: commitTopCandidate %d add site det to site curr scl with %d elements \n", - // sIdx, siteId, commitTopCandidate, site_curr_scl_idx[sIdx]); - //} - ////DBG - - - } - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: Completed (a) and (b) \n", sIdx, siteId); - //} - ////DBG - - // (c) removeCommittedNeighbors(site) - - for (int sNIdx = 0; sNIdx < site_nbr_idx[sIdx]; ++sNIdx) - { - int siteInst = site_nbr[sNbrIdx + sNIdx]; - if (inst_curr_detSite[siteInst] != INVALID) - { - site_nbr[sNbrIdx + sNIdx] = INVALID; - } - } - remove_invalid_neighbor(sIdx, sNbrIdx, site_nbr_idx, site_nbr); - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: Completed (c) \n", sIdx, siteId); - //} - ////DBG - - // (d) addNeighbors(site) - ////STAGGERED ADDITION OF NEW NEIGHBORS - int maxNeighbors = site_nbrRanges[sIdx*(numGroups+1) + numGroups]; - if (site_nbr_idx[sIdx] < minNeighbors && - site_nbrGroup_idx[sIdx] <= maxNeighbors) - { - int beg = site_nbrGroup_idx[sIdx]; - ///STAGGERED ADDITION SET TO SLICE/16 or SLICE_CAPACITY/8 - int end = DREAMPLACE_STD_NAMESPACE::min(site_nbrGroup_idx[sIdx]+SLICE_CAPACITY/8, maxNeighbors); - site_nbrGroup_idx[sIdx] = end; - - for (int aNIdx = beg; aNIdx < end; ++aNIdx) - { - int instId = site_nbrList[sNbrIdx + aNIdx]; - - if (inst_curr_detSite[instId] == INVALID && - is_inst_in_cand_feasible(node2outpinIdx_map, node2fence_region_map, - lut_type, flat_node2pin_start_map, flat_node2pin_map, - pin2net_map, pin_typeIds, flat_node2prclstrCount, - flat_node2precluster_map, flop2ctrlSetId_map, flop_ctrlSets, - extended_ctrlSets, ext_ctrlSet_start_map, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, special_nodes, - lutTypeInSliceUnit, lut_maxShared, sIdx, instId, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, CKSR_IN_CLB, - CE_IN_CLB, lutId, ffId, half_ctrl_mode)) - { - site_nbr[sNbrIdx + site_nbr_idx[sIdx]] = site_nbrList[sNbrIdx + aNIdx]; - ++site_nbr_idx[sIdx]; - } - } - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: Completed (d) New Neighbor addition \n", sIdx, siteId); - //} - ////DBG - - //Generate indices for kernel_2 - int validId = 0; - for (int scsIdx = 0; scsIdx < SCL_IDX; ++scsIdx) - { - int siteCurrIdx = sSCL + scsIdx; - if (site_curr_scl_validIdx[siteCurrIdx] != INVALID) - { - validIndices_curr_scl[sSCL+validId] = siteCurrIdx; - ++validId; - } - if (validId == site_curr_scl_idx[sIdx]) break; - } - - cumsum_curr_scl[sIdx] = site_curr_scl_idx[sIdx]*site_nbr_idx[sIdx]; - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: Completed valid indices generation for kernel_2 \n", sIdx, siteId); - //} - ////DBG - } -} - -//runDLIteration split kernel 2 -template -__global__ void runDLIteration_kernel_2( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_bbox, - const T* site_xy, - const T* net_weights, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* node2fence_region_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* flat_net2pin_start_map, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* lut_type, - const int* net2pincount, - const int* pin2net_map, - const int* pin_typeIds, - const int* pin2node_map, - const int* sorted_node_map, - const int* sorted_net_map, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* validIndices_curr_scl, - const int* sorted_clb_siteIds, - const int* addr2site_map, - const int* special_nodes, - const T xWirelenWt, - const T yWirelenWt, - const T wirelenImprovWt, - const T extNetCountWt, - const int num_clb_sites, - const int intMinVal, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int netShareScoreMaxNetDegree, - const int wlscoreMaxNetDegree, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - int* site_nbr_idx, - int* site_nbr, - int* site_curr_pq_top_idx, - int* site_curr_pq_validIdx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - T* site_det_score, - int* inst_curr_detSite, - T* inst_next_bestScoreImprov, - int* inst_next_bestSite, - int* inst_score_improv, - int* site_score_improv - ) -{ - for (int sId = threadIdx.x + blockDim.x * blockIdx.x; - sId < num_clb_sites; sId += blockDim.x*gridDim.x) - { - const int sIdx = sorted_clb_siteIds[sId]; - - int siteId = addr2site_map[sIdx]; - - int sPQ(sIdx*PQ_IDX), sSCL(sIdx*SCL_IDX), sNbrIdx(sIdx*maxList); - - // (e) createNewCandidates(site) - //Generate new candidates by merging site_nbr to site_curr_scl - const int limit_x = site_curr_scl_idx[sIdx]; - const int limit_y = site_nbr_idx[sIdx]; - ///RESTRICTED NEW CANDIDATE SPACE EXPLORATION SET TO SLICE/8 or SLICE_CAPACITY/4 - const int limit_cands = DREAMPLACE_STD_NAMESPACE::min(SLICE_CAPACITY/4,limit_x*limit_y); - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: has %d candidates\n", sIdx, siteId, limit_cands); - //} - ////DBG - - for (int scsIdx = 0; scsIdx < limit_cands; ++scsIdx) - { - int sclId = scsIdx/limit_y; - int snIdx = scsIdx/limit_x; - int siteCurrIdx = validIndices_curr_scl[sSCL + sclId]; - - ///// - int sCKRId = siteCurrIdx*CKSR_IN_CLB; - int sCEId = siteCurrIdx*CE_IN_CLB; - int sFFId = siteCurrIdx*SLICE_CAPACITY; - int sGId = siteCurrIdx*SIG_IDX; - - T nwCand_score = site_curr_scl_score[siteCurrIdx]; - int nwCand_siteId = site_curr_scl_siteId[siteCurrIdx]; - int nwCand_sigIdx = site_curr_scl_sig_idx[siteCurrIdx]; - - //array instantiation - int nwCand_sig[SIG_MAX_CAP]; - int nwCand_lut[SLICE_MAX_CAP]; - int nwCand_ff[SLICE_MAX_CAP]; - int nwCand_ce[CE_MAX_CAP]; - int nwCand_cksr[CKSR_MAX_CAP]; - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: cand has %d sig elements\n", sIdx, siteId, nwCand_sigIdx); - //} - ////DBG - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - nwCand_sig[sg] = site_curr_scl_sig[sGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - nwCand_lut[sg] = site_curr_scl_impl_lut[sFFId + sg]; - nwCand_ff[sg] = site_curr_scl_impl_ff[sFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - nwCand_cksr[sg] = site_curr_scl_impl_cksr[sCKRId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - nwCand_ce[sg] = site_curr_scl_impl_ce[sCEId + sg]; - } - - int instId = site_nbr[sNbrIdx + snIdx]; - int instPcl = instId*3; - - int addInstToSig = INVALID; - if (nwCand_sigIdx >= 2*SLICE_CAPACITY) - { - addInstToSig = 0; - } - - if (addInstToSig == INVALID) - { - int temp[4]; //Max precluster size = 3 - int tIdx(0); - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: Inst %d has %d precluster count\n", sIdx, siteId, instId, flat_node2prclstrCount[instId]); - //} - ////DBG - - for (int el = 0; el < flat_node2prclstrCount[instId]; ++el) - { - int newInstId = flat_node2precluster_map[instPcl+el]; - if (!val_in_array(nwCand_sig, nwCand_sigIdx, 0, newInstId)) - { - temp[tIdx] = newInstId; - ++tIdx; - } else - { - addInstToSig = 0; - break; - } - } - - if (addInstToSig == INVALID && (nwCand_sigIdx + tIdx > 2*SLICE_CAPACITY)) - { - addInstToSig = 0; - } - - if (addInstToSig == INVALID) - { - for (int mBIdx = 0; mBIdx < tIdx; ++mBIdx) - { - nwCand_sig[nwCand_sigIdx] = temp[mBIdx]; - ++nwCand_sigIdx; - } - addInstToSig = 1; - } - } - - if (addInstToSig == 1) - { - //check cand sig is in site_next_pq - int candSigInSiteNextPQ = INVALID; - for (int i = 0; i < PQ_IDX; ++i) - { - int sigIdx = sPQ + i; - if (site_next_pq_validIdx[sigIdx] != INVALID) - { - if (site_next_pq_sig_idx[sigIdx] == nwCand_sigIdx) - { - int pqIdx(sigIdx*SIG_IDX), mtch(0); - - for (int k = 0; k < nwCand_sigIdx; ++k) - { - for (int l = 0; l < nwCand_sigIdx; ++l) - { - if (site_next_pq_sig[pqIdx + l] == nwCand_sig[k]) - { - ++mtch; - break; - } - } - } - if (mtch == nwCand_sigIdx) - { - candSigInSiteNextPQ = 1; - break; - } - } - } - } - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: cand in site_next_pq: %d \n", sIdx, siteId, candSigInSiteNextPQ); - //} - ////DBG - - if (candSigInSiteNextPQ == INVALID && - add_inst_to_cand_impl(node2outpinIdx_map, lut_type, flat_node2pin_start_map, - flat_node2pin_map, pin2net_map, pin_typeIds, flat_node2prclstrCount, - flat_node2precluster_map, flop2ctrlSetId_map, node2fence_region_map, - flop_ctrlSets, extended_ctrlSets, ext_ctrlSet_start_map, special_nodes, - lutTypeInSliceUnit, lut_maxShared, instId, lutId, ffId, half_ctrl_mode, - CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, HALF_SLICE_CAPACITY, BLE_CAPACITY, - NUM_BLE_PER_SLICE, nwCand_lut, nwCand_ff, nwCand_cksr, nwCand_ce)) - { - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: compute cand score\n", sIdx, siteId); - //} - ////DBG - - compute_candidate_score(pos_x, pos_y, pin_offset_x, pin_offset_y, - net_bbox, net_weights, site_xy, net_pinIdArrayX, net_pinIdArrayY, - flat_net2pin_start_map, flat_node2pin_start_map, flat_node2pin_map, - sorted_net_map, pin2net_map, pin2node_map, net2pincount, lut_type, - xWirelenWt, yWirelenWt, extNetCountWt, wirelenImprovWt, - netShareScoreMaxNetDegree, wlscoreMaxNetDegree, half_ctrl_mode, - nwCand_sig, nwCand_siteId, nwCand_sigIdx, nwCand_score); - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0) - //{ - // printf("%d Site %d: cand score: %.2f \n", sIdx, siteId, nwCand_score); - //} - ////DBG - - int nxtId(INVALID); - - if (site_next_pq_idx[sIdx] < PQ_IDX) - { - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (site_next_pq_validIdx[sPQ+vId] == INVALID) - { - nxtId = vId; - ++site_next_pq_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < PQ_IDX; ++vId) - { - if (ckscore > site_next_pq_score[sPQ + vId]) - { - ckscore = site_next_pq_score[sPQ + vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - int nTId = sPQ + nxtId; - int nCKRId = nTId*CKSR_IN_CLB; - int nCEId = nTId*CE_IN_CLB; - int nFFId = nTId*SLICE_CAPACITY; - int nSGId = nTId*SIG_IDX; - - ///// - site_next_pq_validIdx[nTId] = 1; - site_next_pq_score[nTId] = nwCand_score; - site_next_pq_siteId[nTId] = nwCand_siteId; - site_next_pq_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_pq_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_pq_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_pq_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_pq_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_pq_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - - if (site_next_pq_idx[sIdx] == 1 || - nwCand_score > site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]) - { - site_next_pq_top_idx[sIdx] = nxtId; - } - - nxtId = INVALID; - - if (site_next_scl_idx[sIdx] < SCL_IDX) - { - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (site_next_scl_validIdx[sSCL+vId] == INVALID) - { - nxtId = vId; - ++site_next_scl_idx[sIdx]; - break; - } - } - } else - { - //find least score and replace if current score is greater - T ckscore(nwCand_score); - for (int vId = 0; vId < SCL_IDX; ++vId) - { - if (ckscore > site_next_scl_score[sSCL+vId]) - { - ckscore = site_next_scl_score[sSCL+vId]; - nxtId = vId; - } - } - } - - if (nxtId != INVALID) - { - ///// - nTId = sSCL + nxtId; - nCKRId = nTId*CKSR_IN_CLB; - nCEId = nTId*CE_IN_CLB; - nFFId = nTId*SLICE_CAPACITY; - nSGId = nTId*SIG_IDX; - - site_next_scl_validIdx[nTId] = 1; - site_next_scl_score[nTId] = nwCand_score; - site_next_scl_siteId[nTId] = nwCand_siteId; - site_next_scl_sig_idx[nTId] = nwCand_sigIdx; - - for (int sg = 0; sg < nwCand_sigIdx; ++sg) - { - site_next_scl_sig[nSGId + sg] = nwCand_sig[sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_next_scl_impl_lut[nFFId + sg] = nwCand_lut[sg]; - site_next_scl_impl_ff[nFFId + sg] = nwCand_ff[sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_next_scl_impl_cksr[nCKRId + sg] = nwCand_cksr[sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_next_scl_impl_ce[nCEId + sg] = nwCand_ce[sg]; - } - ///// - } - } - } - } - - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) - //{ - // printf("%d Site %d: completed big for loop\n", sIdx, siteId); - //} - ////DBG - - //Remove all candidates in scl that is worse than the worst candidate in PQ - if (site_next_pq_idx[sIdx] > 0) - { - //Find worst candidate in PQ - T ckscore(site_next_pq_score[sPQ + site_next_pq_top_idx[sIdx]]); - - for (int vId = 0; vId < PQ_IDX; ++vId) - { - int nPQId = sPQ + vId; - if (site_next_pq_validIdx[nPQId] != INVALID) - { - if (ckscore > site_next_pq_score[nPQId]) - { - ckscore = site_next_pq_score[nPQId]; - } - } - } - - //Invalidate worst ones in scl - int sclCount = 0; - int maxEntries = site_next_scl_idx[sIdx]; - for (int ckId = 0; ckId < SCL_IDX; ++ckId) - { - int vId = sSCL + ckId; - if (site_next_scl_validIdx[vId] != INVALID) - { - if (ckscore > site_next_scl_score[vId]) - { - //Clear contents thoroughly - clear_cand_contents( - vId, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce); - - site_next_scl_validIdx[vId] = INVALID; - site_next_scl_sig_idx[vId] = 0; - site_next_scl_siteId[vId] = INVALID; - site_next_scl_score[vId] = 0.0; - --site_next_scl_idx[sIdx]; - } - ++sclCount; - if (sclCount == maxEntries) - { - break; - } - } - } - } - - //Update stable Iteration count - if (site_curr_pq_idx[sIdx] > 0 && site_next_pq_idx[sIdx] > 0 && - compare_pq_tops(site_curr_pq_score, site_curr_pq_top_idx, - site_curr_pq_validIdx, site_curr_pq_siteId, site_curr_pq_sig_idx, - site_curr_pq_sig, site_curr_pq_impl_lut, site_curr_pq_impl_ff, - site_curr_pq_impl_cksr, site_curr_pq_impl_ce, site_next_pq_score, - site_next_pq_top_idx, site_next_pq_validIdx, site_next_pq_siteId, - site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, - site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, - sIdx, sPQ, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY)) - { - site_next_stable[sIdx] = site_curr_stable[sIdx] + 1; - } else - { - site_next_stable[sIdx] = 0; - } - - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) - //{ - // printf("%d Site %d: completed (e) \n", sIdx, siteId); - //} - ////DBG - - //// (f) broadcastTopCandidate(site) - if (site_next_pq_idx[sIdx] > 0) - { - int tpIdx = sPQ + site_next_pq_top_idx[sIdx]; - int topSigId = tpIdx*SIG_IDX; - - T scoreImprov = site_next_pq_score[tpIdx] - site_det_score[sIdx]; - - ////UPDATED SEQUENTIAL PORTION - int scoreImprovInt = DREAMPLACE_STD_NAMESPACE::max(int(scoreImprov*10000), intMinVal); - site_score_improv[sIdx] = scoreImprovInt + siteId; - - for (int ssIdx = 0; ssIdx < site_next_pq_sig_idx[tpIdx]; ++ssIdx) - { - int instId = site_next_pq_sig[topSigId + ssIdx]; - - if (inst_curr_detSite[instId] == INVALID) - { - atomicMax(&inst_score_improv[instId], scoreImprovInt); - } - } - } - ////DBG - //if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0 && blockIdx.x == 0 && limit_cands > 0) - //{ - // printf("%d Site %d: completed (f) \n", sIdx, siteId); - //} - ////DBG - } -} - -//runDLSyncSites -template -__global__ void runDLSyncSites( - const int* site_nbrRanges_idx, - const int* site_nbrGroup_idx, - const int* addr2site_map, - const int num_clb_sites, - const int SLICE_CAPACITY, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - int* site_curr_pq_top_idx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_validIdx, - int* site_curr_pq_siteId, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* site_curr_scl_validIdx, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_validIdx, - T* site_next_pq_score, - int* site_next_pq_top_idx, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_idx, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - int* activeStatus) -{ - int sIdx = threadIdx.x + blockDim.x * blockIdx.x; - while(sIdx < num_clb_sites) - { - int numNbrGroups = (site_nbrRanges_idx[sIdx] == 0) ? 0 : site_nbrRanges_idx[sIdx]-1; - int sPQ = sIdx*SCL_IDX; - - site_curr_stable[sIdx] = site_next_stable[sIdx]; - - int curr_scl_size = site_curr_scl_idx[sIdx]; - site_curr_scl_idx[sIdx] = 0; - int sclCount = 0; - - //Include valid entries of site.next.scl to site.curr.scl - if (site_next_scl_idx[sIdx] > 0) - { - for (int id = 0; id < SCL_IDX; ++id) - { - int vIdx = sPQ+id; - if (site_next_scl_validIdx[vIdx] != INVALID) - { - int currId = sPQ+site_curr_scl_idx[sIdx]; - - site_curr_scl_validIdx[currId] = 1; - site_curr_scl_siteId[currId] = site_next_scl_siteId[vIdx]; - site_curr_scl_score[currId] = site_next_scl_score[vIdx]; - site_curr_scl_sig_idx[currId] = site_next_scl_sig_idx[vIdx]; - - int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); - int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); - int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); - int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); - - for (int sg = 0; sg < site_next_scl_sig_idx[vIdx]; ++sg) - { - site_curr_scl_sig[currSGId + sg] = site_next_scl_sig[nxtSGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_scl_impl_lut[currFFId + sg] = site_next_scl_impl_lut[nxtFFId + sg]; - site_curr_scl_impl_ff[currFFId + sg] = site_next_scl_impl_ff[nxtFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_scl_impl_cksr[currCKId + sg] = site_next_scl_impl_cksr[nxtCKId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_scl_impl_ce[currCEId + sg] = site_next_scl_impl_ce[nxtCEId + sg]; - } - ++site_curr_scl_idx[sIdx]; - ++sclCount; - if (sclCount == site_next_scl_idx[sIdx]) - { - break; - } - } - } - } - - ////Invalidate the rest in site.curr.scl - if (curr_scl_size > site_next_scl_idx[sIdx]) - { - for (int ckId = site_curr_scl_idx[sIdx]; ckId < SCL_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - if (site_curr_scl_validIdx[vIdx] != INVALID) - { - site_curr_scl_validIdx[vIdx] = INVALID; - site_curr_scl_sig_idx[vIdx] = 0; - site_curr_scl_siteId[vIdx] = INVALID; - site_curr_scl_score[vIdx] = T(0.0); - ++sclCount; - if (sclCount == curr_scl_size) - { - break; - } - } - } - } - - int curr_pq_size = site_curr_pq_idx[sIdx]; - site_curr_pq_idx[sIdx] = 0; - site_curr_pq_top_idx[sIdx] = INVALID; - - sPQ = sIdx*PQ_IDX; - sclCount = 0; - //Include valid entries of site.next.pq to site.curr.pq - if (site_next_pq_idx[sIdx] > 0) - { - for (int id = 0; id < PQ_IDX; ++id) - { - int vIdx = sPQ+id; - if (site_next_pq_validIdx[vIdx] != INVALID) - { - int currId = sPQ+site_curr_pq_idx[sIdx]; - - site_curr_pq_validIdx[currId] = 1; - site_curr_pq_siteId[currId] = site_next_pq_siteId[vIdx]; - site_curr_pq_score[currId] = site_next_pq_score[vIdx]; - site_curr_pq_sig_idx[currId] = site_next_pq_sig_idx[vIdx]; - - int currFFId(currId*SLICE_CAPACITY), nxtFFId(vIdx*SLICE_CAPACITY); - int currCKId(currId*CKSR_IN_CLB), nxtCKId(vIdx*CKSR_IN_CLB); - int currCEId(currId*CE_IN_CLB), nxtCEId(vIdx*CE_IN_CLB); - int currSGId(currId*SIG_IDX), nxtSGId(vIdx*SIG_IDX); - - for (int sg = 0; sg < site_next_pq_sig_idx[vIdx]; ++sg) - { - site_curr_pq_sig[currSGId + sg] = site_next_pq_sig[nxtSGId + sg]; - } - for (int sg = 0; sg < SLICE_CAPACITY; ++sg) - { - site_curr_pq_impl_lut[currFFId + sg] = site_next_pq_impl_lut[nxtFFId + sg]; - site_curr_pq_impl_ff[currFFId + sg] = site_next_pq_impl_ff[nxtFFId + sg]; - } - for (int sg = 0; sg < CKSR_IN_CLB; ++sg) - { - site_curr_pq_impl_cksr[currCKId + sg] = site_next_pq_impl_cksr[nxtCKId + sg]; - } - for (int sg = 0; sg < CE_IN_CLB; ++sg) - { - site_curr_pq_impl_ce[currCEId + sg] = site_next_pq_impl_ce[nxtCEId + sg]; - } - if (id == site_next_pq_top_idx[sIdx]) - { - site_curr_pq_top_idx[sIdx] = site_curr_pq_idx[sIdx]; - } - ++site_curr_pq_idx[sIdx]; - ++sclCount; - if (sclCount == site_next_pq_idx[sIdx]) - { - break; - } - } - } - } - - //Invalidate the rest in site.curr.pq - if (curr_pq_size > site_next_pq_idx[sIdx]) - { - for (int ckId = site_curr_pq_idx[sIdx]; ckId < PQ_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - if (site_curr_pq_validIdx[vIdx] != INVALID) - { - site_curr_pq_validIdx[vIdx] = INVALID; - site_curr_pq_sig_idx[vIdx] = 0; - site_curr_pq_siteId[vIdx] = INVALID; - site_curr_pq_score[vIdx] = T(0.0); - ++sclCount; - if (sclCount == curr_pq_size) - { - break; - } - } - } - } - - sPQ = sIdx*SCL_IDX; - //sclCount = 0; - for (int ckId = 0; ckId < SCL_IDX; ++ckId) - { - int vIdx = sPQ+ckId; - //if (site_next_scl_validIdx[vIdx] != INVALID) - //{ - //Clear contents thoroughly - clear_cand_contents( - vIdx, SIG_IDX, CKSR_IN_CLB, CE_IN_CLB, SLICE_CAPACITY, - site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce); - - site_next_scl_validIdx[vIdx] = INVALID; - site_next_scl_sig_idx[vIdx] = 0; - site_next_scl_siteId[vIdx] = INVALID; - site_next_scl_score[vIdx] = 0.0; - //++sclCount; - //if (sclCount == site_next_scl_idx[sIdx]) - //{ - // break; - //} - //} - } - site_next_scl_idx[sIdx] = 0; - - activeStatus[addr2site_map[sIdx]] = (site_curr_pq_idx[sIdx] > 0 || - site_curr_scl_idx[sIdx] > 0 || - site_nbrGroup_idx[sIdx] < numNbrGroups) ? 1: 0; - - sIdx += blockDim.x * gridDim.x; - } -} - -//runDLSyncInsts -template -__global__ void runDLSyncInsts( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const int* site_types, - const int* site2addr_map, - const int* spiral_accessor, - const int* lut_flop_indices, - const int* site_score_improv, - const int* site_curr_pq_top_idx, - const int* site_curr_pq_sig_idx, - const int* site_curr_pq_sig, - const int* site_curr_pq_idx, - const T maxDist, - const int spiralBegin, - const int spiralEnd, - const int intMinVal, - const int num_nodes, - const int num_sites_x, - const int num_sites_y, - const int sliceId, - const int maxSites, - const int SIG_IDX, - const int PQ_IDX, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* inst_score_improv, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* illegalStatus) -{ - int nIdx = threadIdx.x + blockDim.x * blockIdx.x; - while(nIdx < num_nodes) - { - const int nodeId = lut_flop_indices[nIdx]; - if (inst_curr_detSite[nodeId] == INVALID) //Only LUT/FF - { - //POST PROCESSING TO IDENTIFY INSTANCE BEST SITE - //REPLACEMENT FOR SEQUENTIAL PORTION - if (inst_score_improv[nodeId] > intMinVal) - { - int bestSite = maxSites; - int &instScoreImprov = inst_score_improv[nodeId]; - T instScoreImprovT = T(instScoreImprov/10000.0); - T posX = pos_x[nodeId]; - T posY = pos_y[nodeId]; - - for (int spIdx = spiralBegin; spIdx < spiralEnd; ++spIdx) - { - int saIdx = spIdx*2; - int xVal = posX + spiral_accessor[saIdx]; - int yVal = posY + spiral_accessor[saIdx+1]; - - int siteId = xVal * num_sites_y + yVal; - - if (xVal >= 0 && xVal < num_sites_x && - yVal >= 0 && yVal < num_sites_y && - site_types[siteId] == sliceId) - { - int stMpId = siteId *2; - int sIdx = site2addr_map[siteId]; - int tsPQ = sIdx*PQ_IDX + site_curr_pq_top_idx[sIdx]; - int topIdx = tsPQ*SIG_IDX; - int site_score = site_score_improv[sIdx] - siteId; - - T dist = DREAMPLACE_STD_NAMESPACE::abs(posX - site_xy[stMpId]) + - DREAMPLACE_STD_NAMESPACE::abs(posY - site_xy[stMpId+1]); - - if (instScoreImprov == site_score && - site_curr_pq_idx[sIdx] > 0) - { - for (int idx = 0; idx < site_curr_pq_sig_idx[tsPQ]; ++idx) - { - if (site_curr_pq_sig[topIdx+idx] == nodeId && - siteId < bestSite && dist < maxDist) - { - bestSite = siteId; - inst_next_bestSite[nodeId] = siteId; - inst_next_bestScoreImprov[nodeId] = instScoreImprovT; - } - } - } - } - } - instScoreImprov = intMinVal; - } - //END post processing - - - inst_curr_detSite[nodeId] = inst_next_detSite[nodeId]; - inst_curr_bestSite[nodeId] = inst_next_bestSite[nodeId]; - inst_curr_bestScoreImprov[nodeId] = inst_next_bestScoreImprov[nodeId]; - - inst_next_bestSite[nodeId] = INVALID; - inst_next_bestScoreImprov[nodeId] = T(-10000.0); - - illegalStatus[nodeId] = (inst_curr_detSite[nodeId] == INVALID) ? 1 : 0; - } - nIdx += blockDim.x * gridDim.x; - } -} - -//legalize Mlab -template -__global__ void legalizeMlab_kernel( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const T* mlab_locX, - const T* mlab_locY, - const int* mlab_indices, - const int* site2addr_map, - const T max_score, - const int num_mlab_nodes, - const int num_sites_y, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* dist_moved, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes) -{ - int nIdx = threadIdx.x + blockDim.x * blockIdx.x; - while(nIdx < num_mlab_nodes) - { - const int instId = mlab_indices[nIdx]; - - T xVal = mlab_locX[nIdx]; - T yVal = mlab_locY[nIdx]; - - int siteId = xVal * num_sites_y + yVal; - int sIdx = site2addr_map[siteId]; - - dist_moved[nIdx] = DREAMPLACE_STD_NAMESPACE::abs(pos_x[instId] - site_xy[siteId*2]) + - DREAMPLACE_STD_NAMESPACE::abs(pos_y[instId] - site_xy[siteId*2+1]); - - int sdtopId = sIdx*SIG_IDX; - int sdlutId = sIdx*SLICE_CAPACITY; - - if (site_det_sig_idx[sIdx] == 0) - { - sites_with_special_nodes[sIdx] = 1; - site_det_score[sIdx] = max_score; - site_det_siteId[sIdx] = siteId; - - site_det_sig_idx[sIdx] = 2*SLICE_CAPACITY; - site_det_sig[sdtopId] = instId; - site_det_impl_lut[sdlutId] = instId; - - inst_curr_detSite[instId] = siteId; - inst_curr_bestSite[instId] = siteId; - inst_curr_bestScoreImprov[instId] = max_score; - - inst_next_detSite[instId] = siteId; - inst_next_bestSite[instId] = siteId; - inst_next_bestScoreImprov[instId] = max_score; - } - //DBG - else { - printf("ERROR: Slice site not empty - MLAB %d not legalized at (%.2f, %.2f)\n", instId, xVal, yVal); - } - //DBG - - nIdx += blockDim.x * gridDim.x; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//init nets and precluster -template -int initLGCudaLauncher( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const int* sorted_node_map, - const int* sorted_node_idx, - const int* sorted_net_idx, - const int* flat_net2pin_map, - const int* flat_net2pin_start_map, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* node2fence_region_map, - const int* node2outpinIdx_map, - const int* pin2net_map, - const int* pin2node_map, - const int* pin_typeIds, - const int* net2pincount, - const int* is_mlab_node, - const T preClusteringMaxDist, - const int ffId, - const int lutId, - const int num_nets, - const int num_nodes, - const int wlscoreMaxNetDegree, - T* net_bbox, - int* net_pinIdArrayX, - int* net_pinIdArrayY, - int* flat_node2precluster_map, - int* flat_node2prclstrCount) -{ - int block_count = ceilDiv(num_nets + THREAD_COUNT-1, THREAD_COUNT); - initNets<<>>( - pos_x, pos_y, pin_offset_x, pin_offset_y, - flat_net2pin_start_map, flat_net2pin_map, - sorted_net_idx, pin2node_map, net2pincount, - num_nets, wlscoreMaxNetDegree, net_bbox, - net_pinIdArrayX, net_pinIdArrayY); - - int nodes_block_count = ceilDiv(num_nodes + THREAD_COUNT-1, THREAD_COUNT); - preClustering<<>>( - pos_x, pos_y, pin_offset_x, pin_offset_y, - sorted_node_map, sorted_node_idx, - flat_net2pin_start_map, flat_net2pin_map, - flop2ctrlSetId_map, flop_ctrlSets, - node2fence_region_map, node2outpinIdx_map, - pin2net_map, pin2node_map, pin_typeIds, - is_mlab_node, preClusteringMaxDist, - lutId, ffId, num_nodes, - flat_node2precluster_map, flat_node2prclstrCount); - - cudaDeviceSynchronize(); - return 0; -} - -//runDLIter -template -int runDLCudaLauncher( - const T* pos_x, - const T* pos_y, - const T* pin_offset_x, - const T* pin_offset_y, - const T* net_bbox, - const T* site_xy, - const T* net_weights, - const int* net_pinIdArrayX, - const int* net_pinIdArrayY, - const int* site_types, - const int* spiral_accessor, - const int* node2fence_region_map, - const int* lut_flop_indices, - const int* flop2ctrlSetId_map, - const int* flop_ctrlSets, - const int* extended_ctrlSets, - const int* ext_ctrlSet_start_map, - const int* lut_type, - const int* flat_node2pin_start_map, - const int* flat_node2pin_map, - const int* node2outpinIdx_map, - const int* node2pincount, - const int* net2pincount, - const int* pin2net_map, - const int* pin_typeIds, - const int* flat_net2pin_start_map, - const int* pin2node_map, - const int* sorted_net_map, - const int* sorted_node_map, - const int* flat_node2prclstrCount, - const int* flat_node2precluster_map, - const int* is_mlab_node, - const int* site_nbrList, - const int* site_nbrRanges, - const int* site_nbrRanges_idx, - const int* addr2site_map, - const int* site2addr_map, - const int* special_nodes, - const T maxDist, - const T xWirelenWt, - const T yWirelenWt, - const T wirelenImprovWt, - const T extNetCountWt, - const int num_sites_x, - const int num_sites_y, - const int num_clb_sites, - const int num_lutflops, - const int minStableIter, - const int maxList, - const int half_ctrl_mode, - const int SLICE_CAPACITY, - const int HALF_SLICE_CAPACITY, - const int BLE_CAPACITY, - const int NUM_BLE_PER_SLICE, - const int minNeighbors, - const int spiralBegin, - const int spiralEnd, - const int intMinVal, - const int numGroups, - const int netShareScoreMaxNetDegree, - const int wlscoreMaxNetDegree, - const int lutTypeInSliceUnit, - const int lut_maxShared, - const int CKSR_IN_CLB, - const int CE_IN_CLB, - const int SCL_IDX, - const int PQ_IDX, - const int SIG_IDX, - const int lutId, - const int ffId, - const int sliceId, - int* site_nbr_idx, - int* site_nbr, - int* site_nbrGroup_idx, - int* site_curr_pq_top_idx, - int* site_curr_pq_sig_idx, - int* site_curr_pq_sig, - int* site_curr_pq_idx, - int* site_curr_stable, - int* site_curr_pq_siteId, - int* site_curr_pq_validIdx, - T* site_curr_pq_score, - int* site_curr_pq_impl_lut, - int* site_curr_pq_impl_ff, - int* site_curr_pq_impl_cksr, - int* site_curr_pq_impl_ce, - T* site_curr_scl_score, - int* site_curr_scl_siteId, - int* site_curr_scl_idx, - int* cumsum_curr_scl, - int* site_curr_scl_validIdx, - int* validIndices_curr_scl, - int* site_curr_scl_sig_idx, - int* site_curr_scl_sig, - int* site_curr_scl_impl_lut, - int* site_curr_scl_impl_ff, - int* site_curr_scl_impl_cksr, - int* site_curr_scl_impl_ce, - int* site_next_pq_idx, - int* site_next_pq_validIdx, - int* site_next_pq_top_idx, - T* site_next_pq_score, - int* site_next_pq_siteId, - int* site_next_pq_sig_idx, - int* site_next_pq_sig, - int* site_next_pq_impl_lut, - int* site_next_pq_impl_ff, - int* site_next_pq_impl_cksr, - int* site_next_pq_impl_ce, - T* site_next_scl_score, - int* site_next_scl_siteId, - int* site_next_scl_idx, - int* site_next_scl_validIdx, - int* site_next_scl_sig_idx, - int* site_next_scl_sig, - int* site_next_scl_impl_lut, - int* site_next_scl_impl_ff, - int* site_next_scl_impl_cksr, - int* site_next_scl_impl_ce, - int* site_next_stable, - T* site_det_score, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* site_det_impl_ff, - int* site_det_impl_cksr, - int* site_det_impl_ce, - int* inst_curr_detSite, - T* inst_curr_bestScoreImprov, - int* inst_curr_bestSite, - int* inst_next_detSite, - T* inst_next_bestScoreImprov, - int* inst_next_bestSite, - int* activeStatus, - int* illegalStatus, - int* inst_score_improv, - int* site_score_improv, - int* sorted_clb_siteIds - ) -{ - int block_count = ceilDiv(num_clb_sites + THREAD_COUNT-1, THREAD_COUNT); - - //- Uncomment below line for debg - //DL kernel split Implementation to enable scheduling - //Use below line for debg - //runDLIteration_kernel_1<<<1, 1>>>( - runDLIteration_kernel_1<<>>( - node2fence_region_map, flop2ctrlSetId_map, flop_ctrlSets, - extended_ctrlSets, ext_ctrlSet_start_map, lut_type, - flat_node2pin_start_map, flat_node2pin_map, node2outpinIdx_map, pin2net_map, - pin_typeIds, flat_node2prclstrCount, flat_node2precluster_map, - is_mlab_node, site_nbrList, site_nbrRanges, site_nbrRanges_idx, - addr2site_map, special_nodes, num_clb_sites, minStableIter, - maxList, half_ctrl_mode, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, - numGroups, lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, CE_IN_CLB, - SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, site_nbr_idx, site_nbr, - site_nbrGroup_idx, site_curr_pq_top_idx, site_curr_pq_sig_idx, - site_curr_pq_sig, site_curr_pq_idx, site_curr_stable, - site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, - site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, - site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, - site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, - site_curr_scl_impl_ce, site_next_pq_idx, site_next_pq_validIdx, - site_next_pq_top_idx, site_next_pq_impl_lut, site_next_pq_impl_ff, - site_next_pq_impl_cksr, site_next_pq_impl_ce, - site_next_pq_score, site_next_pq_siteId, - site_next_pq_sig_idx, site_next_pq_sig, site_det_score, - site_det_siteId, site_det_sig_idx, site_det_sig, site_det_impl_lut, - site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, - inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, - validIndices_curr_scl, cumsum_curr_scl); - - cudaDeviceSynchronize(); - //printf("Completed ruNDLIter_k1"); - - //////Use thrust to sort cumsum_curr_scl to compute sorted siteIds based on load - thrust::device_ptr cumsum_ptr = thrust::device_pointer_cast(cumsum_curr_scl); - thrust::device_ptr sortedId_ptr = thrust::device_pointer_cast(sorted_clb_siteIds); - - thrust::sequence(sortedId_ptr, sortedId_ptr+num_clb_sites, 0); - thrust::sort_by_key(cumsum_ptr, cumsum_ptr+num_clb_sites, sortedId_ptr, thrust::greater()); - //Note: order of cumsum_curr_scl is also changed but it is not used in the next steps - //printf("Completed sorting of indices\n"); - - //Use below line for debg - //runDLIteration_kernel_2<<<1, 1>>>( - runDLIteration_kernel_2<<>>( - pos_x, pos_y, pin_offset_x, pin_offset_y, net_bbox, site_xy, - net_weights, net_pinIdArrayX, net_pinIdArrayY, node2fence_region_map, - flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, - ext_ctrlSet_start_map, flat_net2pin_start_map, - flat_node2pin_start_map, flat_node2pin_map, node2outpinIdx_map, lut_type, net2pincount, - pin2net_map, pin_typeIds, pin2node_map, sorted_node_map, - sorted_net_map, flat_node2prclstrCount, flat_node2precluster_map, - validIndices_curr_scl, sorted_clb_siteIds, addr2site_map, - special_nodes, xWirelenWt, yWirelenWt, wirelenImprovWt, extNetCountWt, - num_clb_sites, intMinVal, maxList, half_ctrl_mode, SLICE_CAPACITY, - HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, netShareScoreMaxNetDegree, - wlscoreMaxNetDegree, lutTypeInSliceUnit, lut_maxShared, CKSR_IN_CLB, - CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, lutId, ffId, site_nbr_idx, - site_nbr, site_curr_pq_top_idx, site_curr_pq_validIdx, - site_curr_pq_sig_idx, site_curr_pq_sig, site_curr_pq_impl_lut, - site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, - site_curr_pq_idx, site_curr_stable, site_curr_pq_siteId, - site_curr_pq_score, site_curr_scl_score, site_curr_scl_siteId, - site_curr_scl_idx, site_curr_scl_validIdx, site_curr_scl_sig_idx, - site_curr_scl_sig, site_curr_scl_impl_lut, site_curr_scl_impl_ff, - site_curr_scl_impl_cksr, site_curr_scl_impl_ce, site_next_pq_idx, - site_next_pq_validIdx, site_next_pq_top_idx, site_next_pq_score, - site_next_pq_siteId, site_next_pq_sig_idx, site_next_pq_sig, - site_next_pq_impl_lut, site_next_pq_impl_ff, site_next_pq_impl_cksr, - site_next_pq_impl_ce, site_next_scl_score, site_next_scl_siteId, - site_next_scl_idx, site_next_scl_validIdx, site_next_scl_sig_idx, - site_next_scl_sig, site_next_scl_impl_lut, site_next_scl_impl_ff, - site_next_scl_impl_cksr, site_next_scl_impl_ce, site_next_stable, - site_det_score, inst_curr_detSite, inst_next_bestScoreImprov, - inst_next_bestSite, inst_score_improv, site_score_improv); - - cudaDeviceSynchronize(); - //printf("Completed ruNDLIter_k2\n"); - - ////Use either combined runDLIteration or runDLIter1 + runDLIter2 - //runDLIteration<<>>( - // pos_x, pos_y, pin_offset_x, pin_offset_y, net_bbox, site_xy, - // net_pinIdArrayX, net_pinIdArrayY, node2fence_region_map, - // flop2ctrlSetId_map, flop_ctrlSets, extended_ctrlSets, - // ext_ctrlSet_start_map, lut_type, flat_node2pin_start_map, - // flat_node2pin_map, node2outpinIdx_map, net2pincount, pin2net_map, pin_typeIds, - // flat_net2pin_start_map, pin2node_map, sorted_net_map, sorted_node_map, - // flat_node2prclstrCount, flat_node2precluster_map, is_mlab_node, site_nbrList, - // site_nbrRanges, site_nbrRanges_idx, net_weights, addr2site_map, - // special_nodes, num_clb_sites, minStableIter, maxList, half_ctrl_mode, SLICE_CAPACITY, - // HALF_SLICE_CAPACITY, BLE_CAPACITY, NUM_BLE_PER_SLICE, minNeighbors, - // intMinVal, numGroups, netShareScoreMaxNetDegree, wlscoreMaxNetDegree, - // lutTypeInSliceUnit, lut_maxShared, xWirelenWt, yWirelenWt, - // wirelenImprovWt, extNetCountWt, CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, - // PQ_IDX, SIG_IDX, lutId, ffId, validIndices_curr_scl, site_nbr_idx, - // site_nbr, site_nbrGroup_idx, site_curr_pq_top_idx, site_curr_pq_validIdx, site_curr_pq_sig_idx, - // site_curr_pq_sig, site_curr_pq_idx, site_curr_stable, - // site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, - // site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, - // site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, - // site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, - // site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, - // site_curr_scl_impl_ce, site_next_pq_idx, site_next_pq_validIdx, - // site_next_pq_top_idx, site_next_pq_score, site_next_pq_siteId, - // site_next_pq_sig_idx, site_next_pq_sig, site_next_pq_impl_lut, - // site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, - // site_next_scl_score, site_next_scl_siteId, site_next_scl_idx, - // site_next_scl_validIdx, site_next_scl_sig_idx, site_next_scl_sig, - // site_next_scl_impl_lut, site_next_scl_impl_ff, site_next_scl_impl_cksr, - // site_next_scl_impl_ce, site_next_stable, site_det_score, - // site_det_siteId, site_det_sig_idx, site_det_sig, site_det_impl_lut, - // site_det_impl_ff, site_det_impl_cksr, site_det_impl_ce, - // inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, - // inst_next_bestScoreImprov, inst_next_bestSite, inst_score_improv, - // site_score_improv); - //cudaDeviceSynchronize(); - - int nodes_block_count = ceilDiv(num_lutflops + THREAD_COUNT - 1, THREAD_COUNT); - int maxSites = num_sites_x*num_sites_y; - - runDLSyncInsts<<>>( - pos_x, pos_y, site_xy, site_types, site2addr_map, spiral_accessor, - lut_flop_indices, site_score_improv, site_curr_pq_top_idx, - site_curr_pq_sig_idx, site_curr_pq_sig, site_curr_pq_idx, maxDist, - spiralBegin, spiralEnd, intMinVal, num_lutflops, num_sites_x, - num_sites_y, sliceId, maxSites, SIG_IDX, PQ_IDX, - inst_curr_bestScoreImprov, inst_next_bestScoreImprov, inst_score_improv, - inst_curr_detSite, inst_curr_bestSite, inst_next_detSite, - inst_next_bestSite, illegalStatus); - - cudaDeviceSynchronize(); - //printf("End of runDLSyncInsts\n"); - - runDLSyncSites<<>>( - site_nbrRanges_idx, site_nbrGroup_idx, addr2site_map, num_clb_sites, - SLICE_CAPACITY, CKSR_IN_CLB, CE_IN_CLB, SCL_IDX, PQ_IDX, SIG_IDX, - site_curr_pq_top_idx, site_curr_pq_sig_idx, site_curr_pq_sig, - site_curr_pq_idx, site_curr_stable, site_curr_pq_validIdx, - site_curr_pq_siteId, site_curr_pq_score, site_curr_pq_impl_lut, - site_curr_pq_impl_ff, site_curr_pq_impl_cksr, site_curr_pq_impl_ce, - site_curr_scl_score, site_curr_scl_siteId, site_curr_scl_idx, - site_curr_scl_validIdx, site_curr_scl_sig_idx, site_curr_scl_sig, - site_curr_scl_impl_lut, site_curr_scl_impl_ff, site_curr_scl_impl_cksr, - site_curr_scl_impl_ce, site_next_pq_validIdx, site_next_pq_score, - site_next_pq_top_idx, site_next_pq_siteId, site_next_pq_sig_idx, - site_next_pq_sig, site_next_pq_idx, site_next_pq_impl_lut, - site_next_pq_impl_ff, site_next_pq_impl_cksr, site_next_pq_impl_ce, - site_next_scl_score, site_next_scl_siteId, site_next_scl_idx, - site_next_scl_validIdx, site_next_scl_sig_idx, site_next_scl_sig, - site_next_scl_impl_lut, site_next_scl_impl_ff, site_next_scl_impl_cksr, - site_next_scl_impl_ce, site_next_stable, activeStatus); - - cudaDeviceSynchronize(); - //printf("End of runDLSyncSites\n"); - - return 0; -} - -//legalize Mlab -template -int legalizeMlabCudaLauncher( - const T* pos_x, - const T* pos_y, - const T* site_xy, - const T* mlab_locX, - const T* mlab_locY, - const int* mlab_indices, - const int* site2addr_map, - const int num_mlab_nodes, - const int num_sites_y, - const int SIG_IDX, - const int SLICE_CAPACITY, - T* dist_moved, - T* site_det_score, - T* inst_curr_bestScoreImprov, - T* inst_next_bestScoreImprov, - int* site_det_siteId, - int* site_det_sig_idx, - int* site_det_sig, - int* site_det_impl_lut, - int* inst_curr_detSite, - int* inst_curr_bestSite, - int* inst_next_detSite, - int* inst_next_bestSite, - int* sites_with_special_nodes) -{ - int block_count = ceilDiv(num_mlab_nodes + THREAD_COUNT-1, THREAD_COUNT); - - T max_score = T(10000.0); - - legalizeMlab_kernel<<>>( - pos_x, pos_y, site_xy, mlab_locX, mlab_locY, mlab_indices, - site2addr_map, max_score, num_mlab_nodes, num_sites_y, - SIG_IDX, SLICE_CAPACITY, dist_moved, site_det_score, - inst_curr_bestScoreImprov, inst_next_bestScoreImprov, - site_det_siteId, site_det_sig_idx, site_det_sig, - site_det_impl_lut, inst_curr_detSite, inst_curr_bestSite, - inst_next_detSite, inst_next_bestSite, sites_with_special_nodes); - - cudaDeviceSynchronize(); - //printf("Completed legalize Mlab"); - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int initLGCudaLauncher \ - (const T* pos_x, const T* pos_y, const T* pin_offset_x, \ - const T* pin_offset_y, const int* sorted_node_map, \ - const int* sorted_node_idx, const int* sorted_net_idx, \ - const int* flat_net2pin_map, const int* flat_net2pin_start_map, \ - const int* flop2ctrlSetId_map, const int* flop_ctrlSets, \ - const int* node2fence_region_map, const int* node2outpinIdx_map, \ - const int* pin2net_map, const int* pin2node_map, \ - const int* pin_typeIds, const int* net2pincount, \ - const int* is_mlab_node, const T preClusteringMaxDist, \ - const int ffId, const int lutId, const int num_nets, \ - const int num_nodes, const int WLscoreMaxNetDegre, T* net_bbox, \ - int* net_pinIdArrayX, int* net_pinIdArrayY, int* flat_node2precluster_map, \ - int* flat_node2prclstrCount); \ - \ - template int runDLCudaLauncher \ - (const T* pos_x, const T* pos_y, const T* pin_offset_x, \ - const T* pin_offset_y, const T* net_bbox, const T* site_xy, \ - const T* net_weights, const int* net_pinIdArrayX, \ - const int* net_pinIdArrayY, const int* site_types, \ - const int* spiral_accessor, const int* node2fence_region_map, \ - const int* lut_flop_indices, const int* flop2ctrlSetId_map, \ - const int* flop_ctrlSets, const int* extended_ctrlSets, \ - const int* ext_ctrlSet_start_map, const int* lut_type, \ - const int* flat_node2pin_start_map, const int* flat_node2pin_map, \ - const int* node2outpinIdx_map, const int* node2pincount, \ - const int* net2pincount, const int* pin2net_map, const int* pin_typeIds, \ - const int* flat_net2pin_start_map, const int* pin2node_map, \ - const int* sorted_net_map, const int* sorted_node_map, \ - const int* flat_node2prclstrCount, const int* flat_node2precluster_map, \ - const int* is_mlab_node, const int* site_nbrList, \ - const int* site_nbrRanges, const int* site_nbrRanges_idx, \ - const int* addr2site_map, const int* site2addr_map, \ - const int* special_nodes, const T maxDist, const T xWirelenWt, \ - const T yWirelenWt, const T wirelenImprovWt, const T extNetCountWt, \ - const int num_sites_x, const int num_sites_y, \ - const int num_clb_sites, const int num_lutflops, \ - const int minStableIter, const int maxList, const int half_ctrl_mode, \ - const int SLICE_CAPACITY, const int HALF_SLICE_CAPACITY, \ - const int BLE_CAPACITY, const int NUM_BLE_PER_SLICE, \ - const int minNeighbors, const int spiralBegin, const int spiralEnd, \ - const int intMinVal, const int numGroups, \ - const int netShareScoreMaxNetDegree, const int wlscoreMaxNetDegree, \ - const int lutTypeInSliceUnit, const int lut_maxShared, \ - const int CKSR_IN_CLB, const int CE_IN_CLB, const int SCL_IDX, \ - const int PQ_IDX, const int SIG_IDX, const int ludId, \ - const int ffId, const int sliceId, int* site_nbr_idx, \ - int* site_nbr, int* site_nbrGroup_idx, int* site_curr_pq_top_idx, \ - int* site_curr_pq_sig_idx, int* site_curr_pq_sig, \ - int* site_curr_pq_idx, int* site_curr_stable, \ - int* site_curr_pq_siteId, int* site_curr_pq_validIdx, \ - T* site_curr_pq_score, int* site_curr_pq_impl_lut, \ - int* site_curr_pq_impl_ff, int* site_curr_pq_impl_cksr, \ - int* site_curr_pq_impl_ce, T* site_curr_scl_score, \ - int* site_curr_scl_siteId, int* site_curr_scl_idx, \ - int* cumsum_curr_scl, int* site_curr_scl_validIdx, \ - int* validIndices_curr_scl, int* site_curr_scl_sig_idx, \ - int* site_curr_scl_sig, int* site_curr_scl_impl_lut, \ - int* site_curr_scl_impl_ff, int* site_curr_scl_impl_cksr, \ - int* site_curr_scl_impl_ce, int* site_next_pq_idx, \ - int* site_next_pq_validIdx, int* site_next_pq_top_idx, \ - T* site_next_pq_score, int* site_next_pq_siteId, \ - int* site_next_pq_sig_idx, int* site_next_pq_sig, \ - int* site_next_pq_impl_lut, int* site_next_pq_impl_ff, \ - int* site_next_pq_impl_cksr, int* site_next_pq_impl_ce, \ - T* site_next_scl_score, int* site_next_scl_siteId, \ - int* site_next_scl_idx, int* site_next_scl_validIdx, \ - int* site_next_scl_sig_idx, int* site_next_scl_sig, \ - int* site_next_scl_impl_lut, int* site_next_scl_impl_ff, \ - int* site_next_scl_impl_cksr, int* site_next_scl_impl_ce, \ - int* site_next_stable, T* site_det_score, int* site_det_siteId, \ - int* site_det_sig_idx, int* site_det_sig, int* site_det_impl_lut, \ - int* site_det_impl_ff, int* site_det_impl_cksr, \ - int* site_det_impl_ce, int* inst_curr_detSite, \ - T* inst_curr_bestScoreImprov, int* inst_curr_bestSite, \ - int* inst_next_detSite, T* inst_next_bestScoreImprov, \ - int* inst_next_bestSite, int* activeStatus, int* illegalStatus, \ - int* inst_score_improv, int* site_score_improv, \ - int* sorted_clb_siteIds); \ - \ - template int legalizeMlabCudaLauncher \ - (const T* pos_x, const T* pos_y, const T* site_xy, const T* mlab_locX, \ - const T* mlab_locY, const int* mlab_indices, const int* site2addr_map, \ - const int num_mlab_nodes, const int num_sites_y, const int SIG_IDX, \ - const int SLICE_CAPACITY, T* dist_moved, T* site_det_score, \ - T* inst_curr_bestScoreImprov, T* inst_next_bestScoreImprov, \ - int* site_det_siteId, int* site_det_sig_idx, int* site_det_sig, \ - int* site_det_impl_lut, int* inst_curr_detSite, int* inst_curr_bestSite, \ - int* inst_next_detSite, int* inst_next_bestSite, \ - int* sites_with_special_nodes); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt deleted file mode 100644 index 5c7c06c6..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(OP_NAME move_boundary) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/move_boundary.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/move_boundary_cuda.cpp - src/move_boundary_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py deleted file mode 100644 index 88a78790..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py +++ /dev/null @@ -1,90 +0,0 @@ -## -# @file move_boundary.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# - -import math -import torch -from torch import nn -from torch.autograd import Function - -import dreamplacefpga.ops.move_boundary.move_boundary_cpp as move_boundary_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.move_boundary.move_boundary_cuda as move_boundary_cuda - -import pdb - -class MoveBoundaryFunction(Function): - """ - @brief Bound cells into layout boundary, perform in-place update - """ - @staticmethod - def forward( - pos, - node_size_x, - node_size_y, - xl, - yl, - xh, - yh, - num_movable_nodes, - num_filler_nodes, - num_threads - ): - if pos.is_cuda: - output = move_boundary_cuda.forward( - pos.view(pos.numel()), - node_size_x, - node_size_y, - xl, - yl, - xh, - yh, - num_movable_nodes, - num_filler_nodes - ) - else: - output = move_boundary_cpp.forward( - pos.view(pos.numel()), - node_size_x, - node_size_y, - xl, - yl, - xh, - yh, - num_movable_nodes, - num_filler_nodes, - num_threads - ) - return output - -class MoveBoundary(object): - """ - @brief Bound cells into layout boundary, perform in-place update - """ - def __init__(self, node_size_x, node_size_y, xl, yl, xh, yh, num_movable_nodes, num_filler_nodes, num_threads): - super(MoveBoundary, self).__init__() - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.num_movable_nodes = num_movable_nodes - self.num_filler_nodes = num_filler_nodes - self.num_threads = num_threads - def __call__(self, pos): - return MoveBoundaryFunction.forward( - pos, - node_size_x=self.node_size_x, - node_size_y=self.node_size_y, - xl=self.xl, - yl=self.yl, - xh=self.xh, - yh=self.yh, - num_movable_nodes=self.num_movable_nodes, - num_filler_nodes=self.num_filler_nodes, - num_threads=self.num_threads - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp deleted file mode 100644 index 725c89f5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file move_boundary.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Move out-of-bound cells back to inside placement region - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeMoveBoundaryMapLauncher( - T* x_tensor, T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes, - const int num_threads - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -at::Tensor move_boundary_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - double xl, - double yl, - double xh, - double yh, - int num_movable_nodes, - int num_filler_nodes, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeMoveBoundaryMapLauncher", [&] { - computeMoveBoundaryMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - xl, yl, xh, yh, - pos.numel()/2, - num_movable_nodes, - num_filler_nodes, - num_threads - ); - }); - - return pos; -} - -template -int computeMoveBoundaryMapLauncher( - T* x_tensor, T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes, - const int num_threads - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - if (i < num_movable_nodes || i >= num_nodes-num_filler_nodes) - { - x_tensor[i] = std::max(x_tensor[i], xl); - x_tensor[i] = std::min(x_tensor[i], xh-node_size_x_tensor[i]); - - y_tensor[i] = std::max(y_tensor[i], yl); - y_tensor[i] = std::min(y_tensor[i], yh-node_size_y_tensor[i]); - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::move_boundary_forward, "MoveBoundary forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp deleted file mode 100644 index 258a6db9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file hpwl_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Move out-of-bound cells back to inside placement region - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeMoveBoundaryMapCudaLauncher( - T* x_tensor, T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -at::Tensor move_boundary_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - double xl, - double yl, - double xh, - double yh, - int num_movable_nodes, - int num_filler_nodes - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeMoveBoundaryMapCudaLauncher", [&] { - computeMoveBoundaryMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - xl, yl, xh, yh, - pos.numel()/2, - num_movable_nodes, - num_filler_nodes - ); - }); - - return pos; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::move_boundary_forward, "MoveBoundary forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu deleted file mode 100644 index 7d244abb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeMoveBoundary( - T* x_tensor, - const T* node_size_x_tensor, - const T xl, const T xh, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_movable_nodes || (i >= num_nodes-num_filler_nodes && i < num_nodes)) - { - x_tensor[i] = min(xh-node_size_x_tensor[i], max(xl, x_tensor[i])); - } -} - -template -int computeMoveBoundaryMapCudaLauncher( - T* x_tensor, T* y_tensor, - const T* node_size_x_tensor, const T* node_size_y_tensor, - const T xl, const T yl, const T xh, const T yh, - const int num_nodes, - const int num_movable_nodes, - const int num_filler_nodes - ) -{ - cudaError_t status; - cudaStream_t stream_y; - status = cudaStreamCreate(&stream_y); - if (status != cudaSuccess) - { - printf("cudaStreamCreate failed for stream_y\n"); - fflush(stdout); - return 1; - } - - int thread_count = 512; - int block_count = (num_nodes - 1 + thread_count) / thread_count; - computeMoveBoundary<<>>( - x_tensor, - node_size_x_tensor, - xl, xh, - num_nodes, - num_movable_nodes, - num_filler_nodes - ); - - computeMoveBoundary<<>>( - y_tensor, - node_size_y_tensor, - yl, yh, - num_nodes, - num_movable_nodes, - num_filler_nodes - ); - - /* destroy stream */ - status = cudaStreamDestroy(stream_y); - if (status != cudaSuccess) - { - printf("stream_y destroy failed\n"); - fflush(stdout); - return 1; - } - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeMoveBoundaryMapCudaLauncher(\ - T* x_tensor, T* y_tensor, \ - const T* node_size_x_tensor, const T* node_size_y_tensor, \ - const T xl, const T yl, const T xh, const T yh, \ - const int num_nodes, \ - const int num_movable_nodes, \ - const int num_filler_nodes \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt deleted file mode 100644 index c952075d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -set(OP_NAME pin_pos) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${CUB_DIR} - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/pin_pos.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/pin_pos_cuda.cpp - src/pin_pos_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda_segment - src/pin_pos_cuda_segment.cpp - src/pin_pos_cuda_segment_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - ${TARGET_NAME}_cuda_segment - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py deleted file mode 100644 index f6f191cd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Aug 2019 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py deleted file mode 100644 index a580156e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py +++ /dev/null @@ -1,217 +0,0 @@ -## -# @file pin_pos.py -# @author Xiaohan Gao (DREAMPlace) -# @date Sep 2019 -# @brief Compute pin pos -# - -import math -import torch -from torch import nn -from torch.autograd import Function - -import dreamplacefpga.ops.pin_pos.pin_pos_cpp as pin_pos_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.pin_pos.pin_pos_cuda as pin_pos_cuda - import dreamplacefpga.ops.pin_pos.pin_pos_cuda_segment as pin_pos_cuda_segment - -import pdb - -class PinPosFunction(Function): - """ - @brief Given cell locations, compute pin locations. - """ - @staticmethod - def forward( - ctx, - pos, - pin_offset_x, - pin_offset_y, - pin2node_map, - flat_node2pin_map, - flat_node2pin_start_map, - num_physical_nodes, - num_threads - ): - ctx.pos = pos.view(pos.numel()) - if pos.is_cuda: - output = pin_pos_cuda.forward( - ctx.pos, - pin_offset_x, - pin_offset_y, - pin2node_map, - flat_node2pin_map, - flat_node2pin_start_map - ) - else: - output = pin_pos_cpp.forward( - ctx.pos, - pin_offset_x, - pin_offset_y, - pin2node_map, - flat_node2pin_map, - flat_node2pin_start_map, - num_threads - ) - ctx.pin_offset_x = pin_offset_x - ctx.pin_offset_y = pin_offset_y - ctx.pin2node_map = pin2node_map - ctx.flat_node2pin_map = flat_node2pin_map - ctx.flat_node2pin_start_map = flat_node2pin_start_map - ctx.num_physical_nodes = num_physical_nodes - ctx.num_threads = num_threads - - return output - - @staticmethod - def backward(ctx, grad_pin_pos): - # grad_pin_pos is not contiguous - if grad_pin_pos.is_cuda: - output = pin_pos_cuda.backward( - grad_pin_pos.contiguous(), - ctx.pos, - ctx.pin_offset_x, - ctx.pin_offset_y, - ctx.pin2node_map, - ctx.flat_node2pin_map, - ctx.flat_node2pin_start_map, - ctx.num_physical_nodes - ) - else: - output = pin_pos_cpp.backward( - grad_pin_pos.contiguous(), - ctx.pos, - ctx.pin_offset_x, - ctx.pin_offset_y, - ctx.pin2node_map, - ctx.flat_node2pin_map, - ctx.flat_node2pin_start_map, - ctx.num_physical_nodes, - ctx.num_threads - ) - return output, None, None, None, None, None, None, None - -class PinPosSegmentFunction(Function): - """ - @brief Given cell locations, compute pin locations. - """ - @staticmethod - def forward( - ctx, - pos, - pin_offset_x, - pin_offset_y, - pin2node_map, - flat_node2pin_map, - flat_node2pin_start_map, - num_physical_nodes - ): - ctx.pos = pos.view(pos.numel()) - if not pos.is_cuda: - assert 0, "CPU version NOT implemented" - else: - output = pin_pos_cuda_segment.forward( - ctx.pos, - pin_offset_x, - pin_offset_y, - pin2node_map, - flat_node2pin_map, - flat_node2pin_start_map - ) - ctx.pin_offset_x = pin_offset_x - ctx.pin_offset_y = pin_offset_y - ctx.pin2node_map = pin2node_map - ctx.flat_node2pin_map = flat_node2pin_map - ctx.flat_node2pin_start_map = flat_node2pin_start_map - ctx.num_physical_nodes = num_physical_nodes - - if pos.is_cuda: - torch.cuda.synchronize() - - return output - - @staticmethod - def backward(ctx, grad_pin_pos): - # grad_pin_pos is not contiguous - if grad_pin_pos.is_cuda: - output = pin_pos_cuda_segment.backward( - grad_pin_pos.contiguous(), - ctx.pos, - ctx.pin_offset_x, - ctx.pin_offset_y, - ctx.pin2node_map, - ctx.flat_node2pin_map, - ctx.flat_node2pin_start_map, - ctx.num_physical_nodes - ) - else: - assert 0, "CPU version NOT implemented" - if grad_pin_pos.is_cuda: - torch.cuda.synchronize() - - return output, None, None, None, None, None, None, None - -class PinPos(nn.Module): - """ - @brief Given cell locations, compute pin locations. - Different from torch.index_add which computes x[index[i]] += t[i], - the forward function compute x[i] += t[index[i]] - """ - def __init__(self, pin_offset_x, pin_offset_y, pin2node_map, flat_node2pin_map, flat_node2pin_start_map, num_physical_nodes, num_threads, algorithm='segment'): - """ - @brief initialization - @param pin_offset pin offset in x or y direction, only computes one direction - @param algorithm segment|node-by-node - @param num_threads number of threads - """ - super(PinPos, self).__init__() - self.pin_offset_x = pin_offset_x - self.pin_offset_y = pin_offset_y - self.pin2node_map = pin2node_map.long() - self.flat_node2pin_map = flat_node2pin_map - self.flat_node2pin_start_map = flat_node2pin_start_map - self.num_physical_nodes = num_physical_nodes - self.algorithm = algorithm - self.num_threads = num_threads - - def forward(self, pos): - """ - @brief API - @param pos cell locations. The array consists of x locations of movable cells, fixed cells, and filler cells, then y locations of them - """ - assert pos.numel() % 2 == 0 - num_nodes = pos.numel() // 2 - if pos.is_cuda: - if self.algorithm == 'segment': - return PinPosSegmentFunction.apply( - pos, - self.pin_offset_x, - self.pin_offset_y, - self.pin2node_map, - self.flat_node2pin_map, - self.flat_node2pin_start_map, - self.num_physical_nodes - ) - else: - return PinPosFunction.apply( - pos, - self.pin_offset_x, - self.pin_offset_y, - self.pin2node_map, - self.flat_node2pin_map, - self.flat_node2pin_start_map, - self.num_physical_nodes, - self.num_threads - ) - else: - return PinPosFunction.apply( - pos, - self.pin_offset_x, - self.pin_offset_y, - self.pin2node_map, - self.flat_node2pin_map, - self.flat_node2pin_start_map, - self.num_physical_nodes, - self.num_threads - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp deleted file mode 100644 index 805766ca..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @file pin_pos.cpp - * @author Yibo Lin (DREAMPlace) - * @date Aug 2019 - * @brief Given cell locations, compute pin locations on CPU - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief Given cell locations, compute pin locations -/// @param x cell locations in x direction -/// @param y cell locations in y direction -/// @param pin_offset_x pin offset in x direction -/// @param pin_offset_y pin offset in y direction -/// @param pin2node_map map pin index to node index -/// @param flat_node2pin_map map node index to pins -/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node -/// @param num_pins number of pins -/// @param num_threads number of threads -/// @param pin_x pin positions in x direction -/// @param pin_y pin positions in y direction -template -int computePinPosLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - const int num_threads, - T* pin_x, T* pin_y - ); - -template -int computePinPosGradLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - const int num_threads, - T* grad, T* grad_y - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Given cell locations, compute pin locations -/// @param pos cell locations in x and then y direction -/// @param pin_offset_x pin offset in x direction -/// @param pin_offset_y pin offset in y direction -/// @param pin2node_map map pin index to node index -/// @param flat_node2pin_map map node index to pins -/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node -/// @param num_nodes number of nodes -/// @param num_pins number of pins -/// @param num_threads number of threads -/// @return pin positions in x and then y direction -at::Tensor pin_pos_forward( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosLauncher", [&] { - computePinPosLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_pins, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins - ); - }); - - return out; -} - -at::Tensor pin_pos_backward( - at::Tensor grad_out, - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map, - int num_physical_nodes, - int num_threads - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(grad_out); - CHECK_EVEN(grad_out); - CHECK_CONTIGUOUS(grad_out); - - auto out = at::zeros_like(pos); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradLauncher", [&] { - computePinPosGradLauncher( - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_physical_nodes, - num_pins, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes - ); - }); - - return out; -} - -/// @brief Given cell locations, compute pin locations -/// @param x cell locations in x direction -/// @param y cell locations in y direction -/// @param pin_offset_x pin offset in x direction -/// @param pin_offset_y pin offset in y direction -/// @param pin2node_map map pin index to node index -/// @param flat_node2pin_map map node index to pins -/// @param flat_node2pin_start_map start index of flat_node2pin_map for each node -/// @param num_nodes number of nodes -/// @param num_pins number of pins -/// @param num_threads number of threads -/// @param pin_x pin positions in x direction -/// @param pin_y pin positions in y direction -template -int computePinPosLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - const int num_threads, - T* pin_x, T* pin_y - ) -{ - // density_map_tensor should be initialized outside - -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_pins; ++i) - { - int node_id = pin2node_map[i]; - pin_x[i] = pin_offset_x[i] + x[node_id]; - pin_y[i] = pin_offset_y[i] + y[node_id]; - } - - return 0; -} - -template -int computePinPosGradLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - const int num_threads, - T* grad_x, T* grad_y - ) -{ -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - int bgn = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i+1]; - T& gx = grad_x[i]; - T& gy = grad_y[i]; - for (int j = bgn; j < end; ++j) - { - int pin_id = flat_node2pin_map[j]; - gx += grad_out_x[pin_id]; - gy += grad_out_y[pin_id]; - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp deleted file mode 100644 index eda599f0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @file pin_pos_cuda.cpp - * @author Xiaohan Gao (DREAMPlace) - * @date Sep 2019 - * @brief Given cell locations, compute pin locations on CPU - */ - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computePinPosCudaLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - T* pin_x, T* pin_y - ); - -template -int computePinPosGradCudaLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - T* grad, T* grad_y - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -at::Tensor pin_pos_forward( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosCudaLauncher", [&] { - computePinPosCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins - ); - }); - - return out; -} - -at::Tensor pin_pos_backward( - at::Tensor grad_out, - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map, - int num_physical_nodes - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(grad_out); - CHECK_EVEN(grad_out); - CHECK_CONTIGUOUS(grad_out); - - auto out = at::zeros_like(pos); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradCudaLauncher", [&] { - computePinPosGradCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_physical_nodes, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes - ); - }); - - return out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu deleted file mode 100644 index b9b97e98..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief Compute pin position from node position -template -__global__ void computePinPos( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const K* pin2node_map, - const int num_pins, - T* pin_x, T* pin_y - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int node_id = pin2node_map[i]; - pin_x[i] = pin_offset_x[i] + x[node_id]; - pin_y[i] = pin_offset_y[i] + y[node_id]; - } -} - -template -int computePinPosCudaLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - T* pin_x, T* pin_y - ) -{ - int thread_count = 512; - - computePinPos<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(x, y, pin_offset_x, pin_offset_y, pin2node_map, num_pins, pin_x, pin_y); - - return 0; -} - -/// @brief Compute pin position from node position -template -__global__ void computeNodeGrad( - const T* grad_out_x, - const T* grad_out_y, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - const int num_nodes, - T* grad_x, - T* grad_y - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nodes) - { - T& gx = grad_x[i]; - T& gy = grad_y[i]; - gx = 0; - gy = 0; - for (int j = flat_node2pin_start_map[i]; j < flat_node2pin_start_map[i+1]; ++j) - { - int pin_id = flat_node2pin_map[j]; - gx += grad_out_x[pin_id]; - gy += grad_out_y[pin_id]; - } - } -} - -template -int computePinPosGradCudaLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - T* grad_x, T* grad_y - ) -{ - int thread_count = 512; - - computeNodeGrad<<<(num_nodes + thread_count - 1) / thread_count, thread_count>>>( - grad_out_x, - grad_out_y, - flat_node2pin_map, - flat_node2pin_start_map, - num_nodes, - grad_x, - grad_y - ); - - return 0; -} - - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computePinPosCudaLauncher(\ - const T* x, const T* y, \ - const T* pin_offset_x, \ - const T* pin_offset_y, \ - const long* pin2node_map, \ - const int* flat_node2pin_map, \ - const int* flat_node2pin_start_map, \ - int num_pins, \ - T* pin_x, T* pin_y \ - );\ - \ - template int computePinPosGradCudaLauncher(\ - const T* grad_out_x, const T* grad_out_y, \ - const T* x, const T* y, \ - const T* pin_offset_x, \ - const T* pin_offset_y, \ - const long* pin2node_map, \ - const int* flat_node2pin_map, \ - const int* flat_node2pin_start_map, \ - int num_nodes, \ - int num_pins, \ - T* grad_x, T* grad_y \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp deleted file mode 100644 index 7a6f4f0c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file pin_pos_cuda_segment.cpp - * @author Xiaohan Gao (DREAMPlace) - * @date Sep 2019 - * @brief Given cell locations, compute pin locations on CPU - */ - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computePinPosCudaSegmentLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - T* pin_x, T* pin_y - ); - -template -int computePinPosGradCudaSegmentLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - T* grad, T* grad_y, - T* grad_perm_buf ///< 2*num_pins - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -at::Tensor pin_pos_forward( - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - auto out = at::zeros(pin_offset_x.numel()*2, pos.options()); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosCudaSegmentLauncher", [&] { - computePinPosCudaSegmentLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_pins - ); - }); - - return out; -} - -at::Tensor pin_pos_backward( - at::Tensor grad_out, - at::Tensor pos, - at::Tensor pin_offset_x, - at::Tensor pin_offset_y, - at::Tensor pin2node_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map, - int num_physical_nodes - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(grad_out); - CHECK_EVEN(grad_out); - CHECK_CONTIGUOUS(grad_out); - - auto out = at::zeros_like(pos); - int num_nodes = pos.numel()/2; - int num_pins = pin_offset_x.numel(); - auto grad_perm_buf = at::empty({2*num_pins}, pos.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computePinPosGradCudaSegmentLauncher", [&] { - computePinPosGradCudaSegmentLauncher( - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, long), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - num_physical_nodes, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t)+num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(grad_perm_buf, scalar_t) - ); - }); - - return out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::pin_pos_forward, "PinPos forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::pin_pos_backward, "PinPos backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu deleted file mode 100644 index cfd9f86a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -#include "utility/src/utils_cub.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void permuteGrad( - const T* grad_out_x, - const T* grad_out_y, - const int* flat_node2pin_map, - const int num_pins, - T* grad_out_x_perm, - T* grad_out_y_perm - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int pin_id = flat_node2pin_map[i]; - grad_out_x_perm[i] = grad_out_x[pin_id]; - grad_out_y_perm[i] = grad_out_y[pin_id]; - } -} - -/// @brief Compute pin position from node position -template -__global__ void computePinPos( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const K* pin2node_map, - const int num_pins, - T* pin_x, T* pin_y - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int node_id = pin2node_map[i]; - pin_x[i] = pin_offset_x[i] + x[node_id]; - pin_y[i] = pin_offset_y[i] + y[node_id]; - } -} - -template -int computePinPosCudaSegmentLauncher( - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_pins, - T* pin_x, T* pin_y - ) -{ - int thread_count = 512; - - computePinPos<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(x, y, pin_offset_x, pin_offset_y, pin2node_map, num_pins, pin_x, pin_y); - - return 0; -} - -template -int computePinPosGradCudaSegmentLauncher( - const T* grad_out_x, const T* grad_out_y, - const T* x, const T* y, - const T* pin_offset_x, - const T* pin_offset_y, - const long* pin2node_map, - const int* flat_node2pin_map, - const int* flat_node2pin_start_map, - int num_nodes, - int num_pins, - T* grad_x, T* grad_y, - T* grad_perm_buf ///< 2*num_pins, buffer for store the permutated gradients - ) -{ - int thread_count = 512; - - T* grad_out_x_perm = grad_perm_buf; - T* grad_out_y_perm = grad_perm_buf + num_pins; - - permuteGrad<<<(num_pins+thread_count-1) / thread_count, thread_count>>>(grad_out_x, grad_out_y, flat_node2pin_map, num_pins, grad_out_x_perm, grad_out_y_perm); - - void* d_temp_storage = NULL; - size_t temp_storage_bytes = 0; - - // allocate temp storage - cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_x_perm, grad_x, - num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); - cudaMalloc(&d_temp_storage, temp_storage_bytes); - - // for x - cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_x_perm, grad_x, - num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); - // for y - cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, grad_out_y_perm, grad_y, - num_nodes, flat_node2pin_start_map, flat_node2pin_start_map + 1); - - cudaFree(d_temp_storage); - - return 0; -} - - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computePinPosCudaSegmentLauncher(\ - const T* x, const T* y, \ - const T* pin_offset_x, \ - const T* pin_offset_y, \ - const long* pin2node_map, \ - const int* flat_node2pin_map, \ - const int* flat_node2pin_start_map, \ - int num_pins, \ - T* pin_x, T* pin_y \ - );\ - \ - template int computePinPosGradCudaSegmentLauncher(\ - const T* grad_out_x, const T* grad_out_y, \ - const T* x, const T* y, \ - const T* pin_offset_x, \ - const T* pin_offset_y, \ - const long* pin2node_map, \ - const int* flat_node2pin_map, \ - const int* flat_node2pin_start_map, \ - int num_nodes, \ - int num_pins, \ - T* grad_x, T* grad_y, \ - T* grad_perm_buf \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt deleted file mode 100644 index 141afcca..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(OP_NAME pin_utilization) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/pin_utilization_map.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/pin_utilization_map_cuda.cpp - src/pin_utilization_map_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py deleted file mode 100644 index 7caf7dcd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Dec 2019 -# - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py deleted file mode 100644 index c68e2408..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py +++ /dev/null @@ -1,106 +0,0 @@ -import math -import torch -from torch import nn -from torch.autograd import Function -import pdb - -import dreamplacefpga.ops.pin_utilization.pin_utilization_cpp as pin_utilization_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.pin_utilization.pin_utilization_cuda as pin_utilization_cuda - -class PinUtilization(nn.Module): - def __init__(self, - node_size_x, node_size_y, - pin_weights, - flat_node2pin_start_map, - xl, xh, yl, yh, - num_movable_nodes, num_filler_nodes, - num_bins_x, num_bins_y, - unit_pin_capacity, - pin_stretch_ratio, - deterministic_flag, - num_threads - ): - super(PinUtilization, self).__init__() - self.node_size_x = node_size_x - self.node_size_y = node_size_y - self.xl = xl - self.xh = xh - self.yl = yl - self.yh = yh - self.num_nodes = len(node_size_x) - self.num_movable_nodes = num_movable_nodes - self.num_filler_nodes = num_filler_nodes - self.num_physical_nodes = self.num_nodes - num_filler_nodes - self.num_bins_x = num_bins_x - self.num_bins_y = num_bins_y - self.bin_size_x = (xh - xl) / num_bins_x - self.bin_size_y = (yh - yl) / num_bins_y - self.deterministic_flag = deterministic_flag - self.num_threads = num_threads - - self.unit_pin_capacity = unit_pin_capacity - self.pin_stretch_ratio = pin_stretch_ratio - - # for each physical node, we use the pin counts as the weights - if pin_weights is not None: - self.pin_weights = pin_weights - elif flat_node2pin_start_map is not None: - self.pin_weights = (flat_node2pin_start_map[1:self.num_physical_nodes + 1] - - flat_node2pin_start_map[:self.num_physical_nodes]).to(self.node_size_x.dtype) - else: - assert "either pin_weights or flat_node2pin_start_map is required" - - self.reset() - - def reset(self): - # to make the pin density map smooth, we stretch each pin to a ratio of the pin utilization bin - self.half_node_size_stretch_x = 0.5 * self.node_size_x[:self.num_physical_nodes].clamp(min=self.bin_size_x * self.pin_stretch_ratio) - self.half_node_size_stretch_y = 0.5 * self.node_size_y[:self.num_physical_nodes].clamp(min=self.bin_size_y * self.pin_stretch_ratio) - - def forward(self, pos): - if pos.is_cuda: - output = pin_utilization_cuda.forward( - pos, - self.node_size_x, - self.node_size_y, - self.half_node_size_stretch_x, - self.half_node_size_stretch_y, - self.pin_weights, - self.xl, - self.yl, - self.xh, - self.yh, - self.bin_size_x, - self.bin_size_y, - self.num_physical_nodes, - self.num_bins_x, - self.num_bins_y, - self.deterministic_flag - ) - else: - output = pin_utilization_cpp.forward( - pos, - self.node_size_x, - self.node_size_y, - self.half_node_size_stretch_x, - self.half_node_size_stretch_y, - self.pin_weights, - self.xl, - self.yl, - self.xh, - self.yh, - self.bin_size_x, - self.bin_size_y, - self.num_physical_nodes, - self.num_bins_x, - self.num_bins_y, - self.num_threads, - self.deterministic_flag - ) - - # convert demand to utilization in each bin - output.mul_(1 / (self.bin_size_x * self.bin_size_y * self.unit_pin_capacity)); - - return output diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp deleted file mode 100644 index 6bd98614..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @file pin_utilization_map.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map pin by pin -template -int pinDemandMapLauncher( - const T *node_x, const T *node_y, - const T *node_size_x, const T *node_size_y, - const T *half_node_size_stretch_x, - const T *half_node_size_stretch_y, - const T *pin_weights, - const T xl, const T yl, - const T xh, const T yh, - const T bin_size_x, - const T bin_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_nodes, - const int num_threads, - AtomicOp atomic_add_op, - typename AtomicOp::type *pin_utilization_map - ) -{ - const T inv_bin_size_x = 1.0 / bin_size_x; - const T inv_bin_size_y = 1.0 / bin_size_y; - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - const T node_center_x = node_x[i] + node_size_x[i]/2; - const T node_center_y = node_y[i] + node_size_y[i]/2; - - const T x_min = node_center_x - half_node_size_stretch_x[i]; - const T x_max = node_center_x + half_node_size_stretch_x[i]; - int bin_index_xl = int((x_min - xl) * inv_bin_size_x); - int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - const T y_min = node_center_y - half_node_size_stretch_y[i]; - const T y_max = node_center_y + half_node_size_stretch_y[i]; - int bin_index_yl = int((y_min - yl) * inv_bin_size_y); - int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - T density = pin_weights[i] / (half_node_size_stretch_x[i] * half_node_size_stretch_y[i] * 4); - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - T bin_xl = xl + x * bin_size_x; - T bin_yl = yl + y * bin_size_y; - T bin_xh = bin_xl + bin_size_x; - T bin_yh = bin_yl + bin_size_y; - T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * - DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); - int index = x * num_bins_y + y; - atomic_add_op(&pin_utilization_map[index], overlap * density); - } - } - } - return 0; -} - -at::Tensor pin_utilization_map_forward( - at::Tensor pos, at::Tensor node_size_x, at::Tensor node_size_y, - at::Tensor half_node_size_stretch_x, at::Tensor half_node_size_stretch_y, - at::Tensor pin_weights, double xl, double yl, double xh, double yh, - double bin_size_x, double bin_size_y, int num_physical_nodes, - int num_bins_x, int num_bins_y, - int num_threads, int deterministic_flag - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - CHECK_FLAT(half_node_size_stretch_x); - CHECK_CONTIGUOUS(half_node_size_stretch_x); - - CHECK_FLAT(half_node_size_stretch_y); - CHECK_CONTIGUOUS(half_node_size_stretch_y); - - CHECK_FLAT(pin_weights); - CHECK_CONTIGUOUS(pin_weights); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - at::Tensor pin_utilization_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); - auto num_nodes = pos.numel()/2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "pinDemandMapLauncher", [&] { - if (deterministic_flag == 1) { - double diearea = (xh - xl) * (yh - yl); - int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); - long scale_factor = (1L << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - - std::vector buf_map(num_bins, 0); - AtomicAdd atomic_add_op(scale_factor); - - pinDemandMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), - xl, yl, xh, yh, bin_size_x, bin_size_y, - num_bins_x, num_bins_y, num_physical_nodes, - num_threads, atomic_add_op, buf_map.data()); - - scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t), - buf_map.data(), 1.0 / scale_factor, num_bins, - num_threads); - } else { - AtomicAdd atomic_add_op; - pinDemandMapLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), - xl, yl, xh, yh, bin_size_x, bin_size_y, - num_bins_x, num_bins_y, num_physical_nodes, - num_threads, atomic_add_op, - DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t)); - } - }); - - return pin_utilization_map; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::pin_utilization_map_forward, "compute pin utilization map"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp deleted file mode 100644 index edf470a2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @file pin_utilization_map_cuda.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map pin by pin -template -int pinDemandMapCudaLauncher( - const T *node_x, const T *node_y, - const T *node_size_x, const T *node_size_y, - const T *half_node_size_stretch_x, - const T *half_node_size_stretch_y, - const T *pin_weights, - const T xl, const T yl, - const T xh, const T yh, - const T bin_size_x, - const T bin_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_nodes, - bool deterministic_flag, - T *pin_utilization_map - ); - -at::Tensor pin_utilization_map_forward( - at::Tensor pos, - at::Tensor node_size_x, - at::Tensor node_size_y, - at::Tensor half_node_size_stretch_x, - at::Tensor half_node_size_stretch_y, - at::Tensor pin_weights, - double xl, - double yl, - double xh, - double yh, - double bin_size_x, - double bin_size_y, - int num_physical_nodes, - int num_bins_x, - int num_bins_y, - int deterministic_flag - ) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - CHECK_FLAT(half_node_size_stretch_x); - CHECK_CONTIGUOUS(half_node_size_stretch_x); - - CHECK_FLAT(half_node_size_stretch_y); - CHECK_CONTIGUOUS(half_node_size_stretch_y); - - CHECK_FLAT(pin_weights); - CHECK_CONTIGUOUS(pin_weights); - - CHECK_FLAT(node_size_x); - CHECK_CONTIGUOUS(node_size_x); - - CHECK_FLAT(node_size_y); - CHECK_CONTIGUOUS(node_size_y); - - at::Tensor pin_utilization_map = at::zeros({num_bins_x, num_bins_y}, pos.options()); - auto num_nodes = pos.numel()/2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "pinDemandMapCudaLauncher", [&] { - pinDemandMapCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(node_size_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(node_size_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(half_node_size_stretch_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_weights, scalar_t), - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_bins_x, num_bins_y, - num_physical_nodes, - (bool)deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(pin_utilization_map, scalar_t) - ); - }); - - return pin_utilization_map; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::pin_utilization_map_forward, "compute pin utilization map (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu deleted file mode 100644 index 3a0a623f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu +++ /dev/null @@ -1,164 +0,0 @@ -/** - * @file pin_utilization_map_cuda_kernel.cu - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) - * @date Apr 2023 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -// fill the demand map net by net -template -__global__ void pinDemandMap( - const T *node_x, const T *node_y, - const T *node_size_x, const T *node_size_y, - const T *half_node_size_stretch_x, - const T *half_node_size_stretch_y, - const T *pin_weights, - const T xl, const T yl, - const T xh, const T yh, - const T bin_size_x, - const T bin_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_nodes, - AtomicOp atomic_add_op, - typename AtomicOp::type *pin_utilization_map - ) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - - if (i < num_nodes) - { - const T node_center_x = node_x[i] + node_size_x[i]/2; - const T node_center_y = node_y[i] + node_size_y[i]/2; - - const T x_min = node_center_x - half_node_size_stretch_x[i]; - const T x_max = node_center_x + half_node_size_stretch_x[i]; - int bin_index_xl = int((x_min - xl) / bin_size_x); - int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - const T y_min = node_center_y - half_node_size_stretch_y[i]; - const T y_max = node_center_y + half_node_size_stretch_y[i]; - int bin_index_yl = int((y_min - yl) / bin_size_y); - int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - T density = pin_weights[i] / (half_node_size_stretch_x[i] * half_node_size_stretch_y[i] * 4); - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - T bin_xl = xl + x * bin_size_x; - T bin_yl = yl + y * bin_size_y; - T bin_xh = bin_xl + bin_size_x; - T bin_yh = bin_yl + bin_size_y; - T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * - DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); - int index = x * num_bins_y + y; - atomic_add_op(&pin_utilization_map[index], overlap * density); - } - } - } -} - -// fill the demand map net by net -template -int pinDemandMapCudaLauncher( - const T *node_x, const T *node_y, - const T *node_size_x, const T *node_size_y, - const T *half_node_size_stretch_x, - const T *half_node_size_stretch_y, - const T *pin_weights, - const T xl, const T yl, - const T xh, const T yh, - const T bin_size_x, - const T bin_size_y, - const int num_bins_x, - const int num_bins_y, - const int num_nodes, - bool deterministic_flag, - T *pin_utilization_map - ) -{ - if (deterministic_flag) - { - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - unsigned long long int *buf_map = NULL; - allocateCUDA(buf_map, num_bins, unsigned long long int); - - AtomicAddCUDA atomic_add_op(scale_factor); - - int thread_count = 512; - int block_count = ceilDiv(num_bins, thread_count); - - copyScaleArray<<>>( - buf_map, pin_utilization_map, scale_factor, num_bins); - - block_count = ceilDiv(num_nodes, thread_count); - pinDemandMap<<>>( - node_x, node_y, - node_size_x, node_size_y, - half_node_size_stretch_x, half_node_size_stretch_y, - pin_weights, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_bins_x, num_bins_y, - num_nodes, - atomic_add_op, - buf_map - ); - - block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - pin_utilization_map, buf_map, T(1.0 / scale_factor), num_bins); - - destroyCUDA(buf_map); - } else - { - AtomicAddCUDA atomic_add_op; - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - pinDemandMap<<>>( - node_x, node_y, - node_size_x, node_size_y, - half_node_size_stretch_x, half_node_size_stretch_y, - pin_weights, - xl, yl, xh, yh, - bin_size_x, bin_size_y, - num_bins_x, num_bins_y, - num_nodes, - atomic_add_op, - pin_utilization_map - ); - } - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int pinDemandMapCudaLauncher( \ - const T *node_x, const T *node_y, const T *node_size_x, const T *node_size_y, \ - const T *half_node_size_stretch_x, const T *half_node_size_stretch_y, \ - const T *pin_weights, const T xl, const T yl, const T xh, const T yh, \ - const T bin_size_x, const T bin_size_y, const int num_bins_x, \ - const int num_bins_y, const int num_nodes, bool deterministic_flag, \ - T *pin_utilization_map \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt deleted file mode 100644 index 694b8177..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -set(OP_NAME place_io) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${LIMBO_SOURCE_DIR} - ${Boost_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ) - -set(LINK_LIBS - #draw_place_cxx - utility_cxx - # begin targets in Limbo - #lefparseradapt - #defparseradapt - #verilogparser - bookshelfparser - #gdsparser - programoptions - gzstream - # end targets in Limbo - OpenMP::OpenMP_CXX - ${ZLIB_LIBRARIES} - ) - -if(CAIRO_FOUND) - set(INCLUDE_DIRS ${INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}) - set(LINK_LIBS ${LINK_LIBS} ${CAIRO_LIBRARIES}) - set(DRAWPLACE 1) -else() - set(DRAWPLACE 0) -endif() - -add_pytorch_extension(${TARGET_NAME}_cpp - src/BenchMetrics.cpp - src/BinMap.cpp - src/Enums.cpp - src/Net.cpp - src/Node.cpp - src/Region.cpp - src/Group.cpp - src/Params.cpp - src/PlaceDB.cpp - src/BookshelfWriter.cpp - src/PyPlaceDB.cpp - src/PybindPlaceDB.cpp - src/PybindPyPlaceDB.cpp - src/place_io.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS} - EXTRA_DEFINITIONS DRAWPLACE=${DRAWPLACE}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py deleted file mode 100644 index 824b3b37..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Aug 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py deleted file mode 100644 index fe76ce5e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py +++ /dev/null @@ -1,52 +0,0 @@ -## -# @file place_io.py -# @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Mar 2021 -# - -from torch.autograd import Function - -import dreamplacefpga.ops.place_io.place_io_cpp as place_io_cpp -import pdb - -class PlaceIOFunction(Function): - @staticmethod - def read(params): - """ - @brief read design and store in placement database - """ - args = params.aux_input - if "aux_input" in params.__dict__ and params.aux_input: - return place_io_cpp.forward(args) - - @staticmethod - def pydb(raw_db): - """ - @brief convert to python database - @param raw_db original placement database - """ - return place_io_cpp.pydb(raw_db) - - @staticmethod - def write(raw_db, filename, node_x, node_y, node_z): - """ - @brief write solution in specific format - @param raw_db original placement database - @param filename output file - @param sol_file_format solution file format, DEF|DEFSIMPLE|BOOKSHELF|BOOKSHELFALL - Always *.pl for FPGA - @param node_x x coordinates of cells, only need movable cells; if none, use original position - @param node_y y coordinates of cells, only need movable cells; if none, use original position - """ - return place_io_cpp.write(raw_db, filename, node_x, node_y, node_z) - - @staticmethod - def apply(raw_db, node_x, node_y, node_z): - """ - @brief apply solution - @param raw_db original placement database - @param node_x x coordinates of cells, only need movable cells - @param node_y y coordinates of cells, only need movable cells - @param node_z z coordinates of cells, only need movable cells - """ - return place_io_cpp.apply(raw_db, node_x, node_y, node_z) - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp deleted file mode 100644 index 671efafa..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************* - > File Name: BenchMetrics.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "BenchMetrics.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -BenchMetrics::BenchMetrics() -{ - initPlaceDBFlag = false; - //initAlgoDBFlag = false; -} - -void BenchMetrics::print() const -{ - if (initPlaceDBFlag) - { - dreamplacePrint(kINFO, "design name = %s\n", designName.c_str()); - dreamplacePrint(kINFO, "number of macros = %lu\n", numMacro); - dreamplacePrint(kINFO, "number of nodes = %lu (movable %lu, fixed %lu)\n", numNodes, numMovable, numFixed); - dreamplacePrint(kINFO, "number of nets = %lu\n", numNets); - dreamplacePrint(kINFO, "number of pin connections = %lu\n", numPins); - dreamplacePrint(kINFO, "site dimensions = (%d, %d)\n", siteWidth, rowHeight); - dreamplacePrint(kINFO, "die dimensions = (%d, %d, %d, %d)\n", dieArea.xl(), dieArea.yl(), dieArea.xh(), dieArea.yh()); - if (numIgnoredNet) - dreamplacePrint(kWARN, "# ingored nets = %lu (nets belong to the same cells)\n", numIgnoredNet); - if (numDuplicateNet) - dreamplacePrint(kWARN, "# duplicate nets = %lu\n", numDuplicateNet); - } -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h deleted file mode 100644 index 63544476..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************* - > File Name: BenchMetrics.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_BENCHMETRICS_H -#define DREAMPLACE_BENCHMETRICS_H - -#include -#include -#include "Box.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// ================================================ -/// a simple class storing metrics for benchmarks -/// which is to help report benchmark statistics -/// ================================================ - -struct BenchMetrics -{ - /// metrics from PlaceDB - std::string designName; - std::size_t numMacro; - std::size_t numNodes; - std::size_t numMovable; - std::size_t numFixed; - //std::size_t numIOPin; - std::size_t numNets; - std::size_t numPins; - unsigned siteWidth; - unsigned rowHeight; - Box dieArea; - std::size_t numIgnoredNet; - std::size_t numDuplicateNet; - - bool initPlaceDBFlag; ///< a flag indicates whether it is initialized, must set to true after initialization, from PlaceDB - //bool initAlgoDBFlag; - - BenchMetrics(); - - void print() const; -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h deleted file mode 100644 index aef12d13..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h +++ /dev/null @@ -1,197 +0,0 @@ -/************************************************************************* - > File Name: Bin.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun Jun 21 20:06:50 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_BIN_H -#define DREAMPLACE_BIN_H - -#include "Box.h" -#include "Object.h" -#include "HrchyList.h" - -DREAMPLACE_BEGIN_NAMESPACE - -enum BinType -{ - kBin = 0, - kSBin = 1, - kBinTypeNum = 2 -}; - -inline BinType& operator++(BinType& t) -{ - if (t == kBin) return (t = kSBin); - else if (t == kSBin) return (t = kBinTypeNum); - dreamplaceAssertMsg(0, "overflow for increment BinType"); - return t; -} - -class Bin : public Box -{ - public: - typedef Object::coordinate_type coordinate_type; - typedef Box base_type; - typedef coordinate_traits::index_type index_type; - typedef coordinate_traits::area_type area_type; - typedef coordinate_traits::site_area_type site_area_type; - - /// constructor - Bin (coordinate_type xl = std::numeric_limits::max(), - coordinate_type yl = std::numeric_limits::max(), - coordinate_type xh = std::numeric_limits::min(), - coordinate_type yh = std::numeric_limits::min(), - BinType t = kBin, - index_type id1D = std::numeric_limits::max(), - index_type id2DX = std::numeric_limits::max(), - index_type id2DY = std::numeric_limits::max()); - /// copy constructor - Bin (Bin const& rhs); - /// assignment - Bin& operator=(Bin const& rhs); - - /// member functions - BinType type() const {return m_type;} - Bin& setType(BinType t) {m_type = t; return *this;} - - index_type index1D() const {return m_index1D;} - Bin& setIndex1D(index_type id) {m_index1D = id; return *this;} - - index_type index2D(Direction1DType d) const {return m_index2D[d];} - Bin& setIndex2D(Direction1DType d, index_type v) {m_index2D[d] = v; return *this;} - - index_type indexX() const {return index2D(kX);} - index_type indexY() const {return index2D(kY);} - - /// \return demand of cell area - area_type demand() const {return m_demand;} - Bin& setDemand(area_type v) {m_demand = v; return *this;} - Bin& incrDemand(area_type v) {m_demand += v; return *this;} - Bin& decrDemand(area_type v) {m_demand -= v; return *this;} - - /// \return capacity of area for cells in the bin, excluding fixed cells and forbidden regions - area_type capacity() const {return m_capacity;} - Bin& setCapacity(area_type v) {m_capacity = v; return *this;} - Bin& incrCapacity(area_type v) {m_capacity += v; return *this;} - Bin& decrCapacity(area_type v) {m_capacity -= v; return *this;} - - /// \return pin demand - index_type pinDemand() const {return m_pinDemand;} - Bin& setPinDemand(index_type v) {m_pinDemand = v; return *this;} - Bin& incrPinDemand(index_type v) {m_pinDemand += v; return *this;} - Bin& decrPinDemand(index_type v) {m_pinDemand -= v; return *this;} - - /// \return site capacity - site_area_type siteCapacity() const {return m_siteCapacity;} - Bin& setSiteCapacity(site_area_type v) {m_siteCapacity = v; return *this;} - Bin& incrSiteCapacity(site_area_type v) {m_siteCapacity += v; return *this;} - Bin& decrSiteCapacity(site_area_type v) {m_siteCapacity -= v; return *this;} - - /// \return density, area density - double density() const; - /// given a demand, \return density - double density(area_type d) const; - /// \return pin density, pin number per site - double pinDensity() const; - /// given a pin demand, \return pin density - double pinDensity(index_type d) const; - - HrchyList const& binRows() const {return m_vBinRowId;} - HrchyList& binRows() {return m_vBinRowId;} - - std::vector const& nodes() const {return m_vNodeId;} - std::vector& nodes() {return m_vNodeId;} - - protected: - void copy(Bin const& rhs); - - BinType m_type; ///< type of bin - index_type m_index1D; ///< index in bin array - index_type m_index2D[2]; ///< index in x and y direction of bin map - area_type m_capacity; ///< area available for cells - site_area_type m_siteCapacity; ///< number of sites available for cells - area_type m_demand; ///< area taken by cells - index_type m_pinDemand; ///< number of pins - - /// may not be used - std::vector m_vNodeId; ///< nodes in the bin, initialize iff it is used - /// for sub bins - HrchyList m_vBinRowId; ///< bin rows in the bin, from low to high, left to right -}; - -inline Bin::Bin(Bin::coordinate_type xl, Bin::coordinate_type yl, - Bin::coordinate_type xh, Bin::coordinate_type yh, - BinType t, - index_type id1D, index_type id2DX, index_type id2DY) - : Bin::base_type(xl, yl, xh, yh) - , m_type (t) - , m_index1D(id1D) -{ - m_index2D[kX] = id2DX; - m_index2D[kY] = id2DY; - m_capacity = 0; - m_siteCapacity = 0; - m_demand = 0; - m_pinDemand = 0; -} -inline Bin::Bin(Bin const& rhs) - : Bin::base_type(rhs) -{ - copy(rhs); -} -inline Bin& Bin::operator=(Bin const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void Bin::copy(Bin const& rhs) -{ - m_type = rhs.m_type; - m_index1D = rhs.m_index1D; - m_index2D[kX] = rhs.m_index2D[kX]; - m_index2D[kY] = rhs.m_index2D[kY]; - m_capacity = rhs.m_capacity; - m_siteCapacity = rhs.m_siteCapacity; - m_demand = rhs.m_demand; - m_pinDemand = rhs.m_pinDemand; - m_vBinRowId = rhs.m_vBinRowId; - m_vNodeId = rhs.m_vNodeId; -} -inline double Bin::density() const -{ - if (m_capacity == 0) - return (m_demand == 0)? 0.0 : std::numeric_limits::max(); - else - return (double)m_demand/m_capacity; -} -inline double Bin::density(Bin::area_type d) const -{ - if (m_capacity == 0) - return (d == 0)? 0.0 : std::numeric_limits::max(); - else - return (double)d/m_capacity; -} -inline double Bin::pinDensity() const -{ - if (m_siteCapacity == 0) - return (m_pinDemand == 0)? 0.0 : std::numeric_limits::max(); - else - return (double)m_pinDemand/m_siteCapacity; -} -inline double Bin::pinDensity(Bin::index_type d) const -{ - if (m_siteCapacity == 0) - return (d == 0)? 0.0 : std::numeric_limits::max(); - else - return (double)d/m_siteCapacity; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp deleted file mode 100644 index 0b7524b3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************* - > File Name: BinMap.cpp - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Tue 23 Jun 2015 08:57:40 PM CDT - ************************************************************************/ - -#include "BinMap.h" - -DREAMPLACE_BEGIN_NAMESPACE - -BinMap& BinMap::set(BinType type, BinMap::index_type xNum, BinMap::index_type yNum) -{ - m_dimension[kX] = xNum; - m_dimension[kY] = yNum; - m_vBin.resize(xNum*yNum); - - // only set bin indices - // bin coordinates are set outside since layout information is not known - index_type id2DX = 0; - index_type id2DY = 0; - for (index_type id1D = 0, id1De = m_vBin.size(); id1D < id1De; ++id1D) - { - Bin& bin = m_vBin.at(id1D); - bin.setType(type); - bin.setIndex1D(id1D); - bin.setIndex2D(kX, id2DX) - .setIndex2D(kY, id2DY); - - id2DX += 1; - if (id2DX == m_dimension[kX]) - { - id2DX = 0; - id2DY += 1; - } - } - - return *this; -} - -BinMap& BinMap::resetBinDemand() -{ - for (BinMap1DIterator it = begin1D(), ite = end1D(); it != ite; ++it) - { - it->setDemand(0); - it->setPinDemand(0); - } - return *this; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h deleted file mode 100644 index 8915628d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h +++ /dev/null @@ -1,114 +0,0 @@ -/************************************************************************* - > File Name: BinMap.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun Jun 21 21:23:08 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_BINMAP_H -#define DREAMPLACE_BINMAP_H - -#include -#include "Bin.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// class BinMap holds all bins -class BinMap -{ - public: - typedef Object::index_type index_type; - typedef std::vector::iterator BinMap1DIterator; - typedef std::vector::const_iterator BinMap1DConstIterator; - - /// constructor - BinMap(BinType t = kBin, index_type xNum = 0, index_type yNum = 0); - /// copy constructor - BinMap(BinMap const& rhs); - /// assignment - BinMap& operator=(BinMap const& rhs); - - /// member functions - /// initialize bin map - BinMap& set(BinType type, index_type xNum, index_type yNum); - - /// \return dimensions - index_type dimension(Direction1DType d) const {return m_dimension[d];} - index_type dimensionX() const {return dimension(kX);} - index_type dimensionY() const {return dimension(kY);} - - /// \return total number of bins - index_type size() const {return m_vBin.size();} - - /// \return bin with index 1D - Bin const& getBin(index_type id1D) const {return m_vBin.at(id1D);} - Bin& getBin(index_type id1D) {return m_vBin.at(id1D);} - - /// \return bin with index 2D (x and y) - Bin const& getBin(index_type id2DX, index_type id2DY) const {return m_vBin.at(m_dimension[kX]*id2DY + id2DX);} - Bin& getBin(index_type id2DX, index_type id2DY) {return m_vBin.at(m_dimension[kX]*id2DY + id2DX);} - - /// \return left bin of current bin - /// if current bin is leftmost, return itself - Bin const& getLeftBin(Bin const& bin) const {return (bin.index1D()%dimensionX() == 0)? bin : getBin(bin.index1D()-1);} - Bin& getLeftBin(Bin& bin) {return (bin.index1D()%dimensionX() == 0)? bin : getBin(bin.index1D()-1);} - - /// \return right bin of current bin - /// if current bin is right most, return itself - Bin const& getRightBin(Bin const& bin) const {return ((bin.index1D()+1)%dimensionX() == 0)? bin : getBin(bin.index1D()+1);} - Bin& getRightBin(Bin& bin) {return ((bin.index1D()+1)%dimensionX() == 0)? bin : getBin(bin.index1D()+1);} - - /// \return lower bin of current bin - /// if current bin is lowest, return itself - Bin const& getLowerBin(Bin const& bin) const {return (bin.index1D() < dimensionX())? bin : getBin(bin.index1D()-dimensionX());} - Bin& getLowerBin(Bin& bin) {return (bin.index1D() < dimensionX())? bin : getBin(bin.index1D()-dimensionX());} - - /// \return upper bin of current bin - /// if current bin is top, return itself - Bin const& getUpperBin(Bin const& bin) const {return (bin.index1D()+dimensionX() >= size())? bin : getBin(bin.index1D()+dimensionX());} - Bin& getUpperBin(Bin& bin) {return (bin.index1D()+dimensionX() >= size())? bin : getBin(bin.index1D()+dimensionX());} - - /// \return 1D iterators of bins - BinMap1DIterator begin1D() {return m_vBin.begin();} - BinMap1DIterator end1D() {return m_vBin.end();} - BinMap1DConstIterator begin1D() const {return m_vBin.begin();} - BinMap1DConstIterator end1D() const {return m_vBin.end();} - - /// reset bin demand to zero - BinMap& resetBinDemand(); - - protected: - void copy(BinMap const& rhs); - - std::vector m_vBin; ///< bin array, corresponds to Bin::m_index1D - ///< the indexing order is locally horizontal and globally vertical - index_type m_dimension[2]; ///< 2D dimension in x and y direction of bin map, number of bins in x and y directions -}; - -inline BinMap::BinMap(BinType t, BinMap::index_type xNum, BinMap::index_type yNum) -{ - set(t, xNum, yNum); -} -inline BinMap::BinMap(BinMap const& rhs) -{ - copy(rhs); -} -inline BinMap& BinMap::operator=(BinMap const& rhs) -{ - if (this != &rhs) - copy(rhs); - return *this; -} -inline void BinMap::copy(BinMap const& rhs) -{ - m_vBin = rhs.m_vBin; - m_dimension[kX] = rhs.m_dimension[kX]; - m_dimension[kY] = rhs.m_dimension[kY]; -} - -typedef BinMap::BinMap1DIterator BinMap1DIterator; -typedef BinMap::BinMap1DConstIterator BinMap1DConstIterator; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp deleted file mode 100644 index 392e3dc9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/************************************************************************* - > File Name: BookshelfWriter.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "BookshelfWriter.h" -#include "Iterators.h" -#include "PlaceDB.h" -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -bool BookShelfWriter::write(std::string const& outFile, - float const* x, float const* y, - PlaceDB::index_type const* z) const -{ - std::string outFileNoSuffix = limbo::trim_file_suffix(outFile); - return writePlx(outFileNoSuffix, x, y, z); -} -bool BookShelfWriter::writeAll(std::string const& outFile, - float const* x, float const* y, - PlaceDB::index_type const* z) const -{ - std::string outFileNoSuffix = limbo::trim_file_suffix(outFile); - std::string designName = "design"; - - bool flag = writePlx(outFileNoSuffix, x, y, z); - - return true; -} - -bool BookShelfWriter::writePlx(std::string const& outFileNoSuffix, - float const* x, float const* y, - PlaceDB::index_type const* z) const -{ - FILE* out = openFile(outFileNoSuffix, "pl"); - if (out == NULL) - return false; - - writeHeader(out, "pl"); // use pl instead of plx to accommodate parser - - for (int mIdx = 0; mIdx < m_db.numMovable() + m_db.numFixed(); ++mIdx) - { - float xx = m_db.nodeX(mIdx); - float yy = m_db.nodeY(mIdx); - PlaceDB::index_type zz = m_db.nodeZ(mIdx); - fprintf(out, "%s %g %g %d", m_db.nodeName(mIdx).c_str(), xx, yy, zz); - if (mIdx < m_db.numMovable()) - { - fprintf(out, "\n"); - } else - { - fprintf(out, " /FIXED \n"); - } - } - - closeFile(out); - return true; -} - -void BookShelfWriter::writeHeader(FILE* os, std::string const& fileType) const -{ - fprintf(os, "\n"); -} -FILE* BookShelfWriter::openFile(std::string const& outFileNoSuffix, std::string const& fileType) const -{ - dreamplacePrint(kINFO, "writing placement to %s\n", (outFileNoSuffix+"."+fileType).c_str()); - - FILE* out = fopen((outFileNoSuffix+"."+fileType).c_str(), "w"); - if (out == NULL) - dreamplacePrint(kERROR, "unable to open %s for write\n", (outFileNoSuffix+"."+fileType).c_str()); - return out; -} -void BookShelfWriter::closeFile(FILE* os) const -{ - fclose(os); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h deleted file mode 100644 index 60dd7cd0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************* - > File Name: BookshelfWriter.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_BOOKSHELFWRITER_H -#define DREAMPLACE_BOOKSHELFWRITER_H - -#include -#include -#include "PlaceWriter.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class BookShelfWriter : public PlaceSolWriter -{ - public: - typedef PlaceSolWriter base_type; - typedef PlaceDB::index_type index_type; - - BookShelfWriter(PlaceDB const& db) : base_type(db) {} - BookShelfWriter(BookShelfWriter const& rhs) : base_type(rhs) {} - - /// write .plx file - /// \param outFile is plx file name - /// \param first, last should contain components to write - bool write(std::string const& outFile, - float const* x = NULL, float const* y = NULL, - PlaceDB::index_type const* z = NULL) const; - /// write all files in book shelf format - /// \param outFile is aux file name - /// \param first, last should contain components to write - bool writeAll(std::string const& outFile, - float const* x = NULL, float const* y = NULL, - PlaceDB::index_type const* z = NULL) const; - - protected: - bool writePlx(std::string const& outFileNoSuffix, - float const* x = NULL, float const* y = NULL, - PlaceDB::index_type const* z = NULL) const; - void writeHeader(FILE* os, std::string const& fileType) const; - FILE* openFile(std::string const& outFileNoSuffix, std::string const& fileType) const; - void closeFile(FILE* os) const; -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h deleted file mode 100644 index a2a7cecb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h +++ /dev/null @@ -1,451 +0,0 @@ -/*********************************************************************************** - > File Name: Box.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - **********************************************************************************/ - -#ifndef DREAMPLACE_BOX_H -#define DREAMPLACE_BOX_H - -#include "Point.h" -#include "Interval.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -class Box -{ - public: - typedef T coordinate_type; - typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; - typedef typename coordinate_traits::area_type area_type; - typedef Interval interval_type; - typedef Point point_type; - - ///==== constructors ==== - /// default constructor will result in an invalid box - /// but it is more compatible with functions such as encompass() - Box() - { - set( - std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::min() - ); - } - Box(coordinate_type xl, - coordinate_type yl, - coordinate_type xh, - coordinate_type yh) - { - set(xl, yl, xh, yh); - } - Box(interval_type const& ix, interval_type const& iy) - { - set(kX, ix); - set(kY, iy); - } - Box(Box const& rhs) - { - copy(rhs); - } - Box& operator=(Box const& rhs) - { - copy(rhs); - return *this; - } - template - explicit Box(BoxType const& rhs) - { - copy(rhs); - } - template - Box& operator=(BoxType const& rhs) - { - copy(rhs); - return *this; - } - - ///==== member functions ==== - /// set to uninitialized status - Box& unset() - { - set(std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::min()); - return *this; - } - Box& set(coordinate_type xl, coordinate_type yl, coordinate_type xh, coordinate_type yh) - { - m_range[kX].set(xl, xh); - m_range[kY].set(yl, yh); - return *this; - } - Box& set(Direction1DType d, interval_type const& range) - { - m_range[d] = range; - return *this; - } - Box& set(Direction1DType xy, Direction1DType lh, coordinate_type v) - { - m_range[xy].set(lh, v); - return *this; - } - Box& set(Direction2DType d, coordinate_type v) - { - return set(getXY(d), getLH(d), v); - } - /// encompass box with an input point - Box& encompass(point_type const& p) - { - m_range[kX].encompass(p.get(kX)); - m_range[kY].encompass(p.get(kY)); - return *this; - } - /// encompass box with an input box - Box& encompass(Box const& b) - { - m_range[kX].encompass(b.get(kX)); - m_range[kY].encompass(b.get(kY)); - return *this; - } - /// make sure a valid box - Box& adjust() - { - m_range[kX].adjust(); - m_range[kY].adjust(); - return *this; - } - interval_type const& get(Direction1DType xy) const - { - return m_range[xy]; - } - coordinate_type get(Direction1DType xy, Direction1DType lh) const - { - return m_range[xy].get(lh); - } - coordinate_type get(Direction2DType d) const - { - return get(getXY(d), getLH(d)); - } - coordinate_type xl() const - { - return get(kX, kLOW); - } - coordinate_type yl() const - { - return get(kY, kLOW); - } - coordinate_type xh() const - { - return get(kX, kHIGH); - } - coordinate_type yh() const - { - return get(kY, kHIGH); - } - manhattan_distance_type delta(Direction1DType d) const - { - return m_range[d].delta(); - } - manhattan_distance_type width() const - { - return m_range[kX].delta(); - } - manhattan_distance_type height() const - { - return m_range[kY].delta(); - } - /// \return area - area_type area() const - { - // be careful about overflow - return (area_type)width()*(area_type)height(); - } - - ///==== overload operators ==== - template - bool operator==(const BoxType& rhs) const - { - return m_range[kX] == rhs.m_range[kX] && m_range[kY] == rhs.m_range[kY]; - } - template - bool operator!=(const BoxType& rhs) const { return !((*this) == rhs); } - - std::string toString() const {return "(" + limbo::to_string(xl()) + ", " + limbo::to_string(yl()) + ", " + limbo::to_string(xh()) + ", " + limbo::to_string(yh()) + ")";} - protected: - template - void copy(BoxType const& rhs) - { - m_range[kX] = rhs.m_range[kX]; - m_range[kY] = rhs.m_range[kY]; - } - - interval_type m_range[2]; ///< two ranges define a box -}; - -/// \return lower left point -template -inline typename Box::point_type -ll(Box const& b) -{ - return typename Box::point_type(b.xl(), b.yl()); -} -/// \return lower right point -template -inline typename Box::point_type -lr(Box const& b) -{ - return typename Box::point_type(b.xh(), b.yl()); -} -/// \return upper left point -template -inline typename Box::point_type -ul(Box const& b) -{ - return typename Box::point_type(b.xl(), b.yh()); -} -/// \return upper right point -template -inline typename Box::point_type -ur(Box const& b) -{ - return typename Box::point_type(b.xh(), b.yh()); -} -/// \return center coordinate -template -inline typename coordinate_traits::coordinate_type -center(Box const& b, Direction1DType d) -{ - return center(b.get(d)); -} -/// \return center point -template -inline typename Box::point_type -center(Box const& b) -{ - return typename Box::point_type(center(b, kX), center(b, kY)); -} -/// \return half perimeter -template -inline typename coordinate_traits::manhattan_distance_type -halfPerimeter(Box const& b) -{ - return b.delta(kX)+b.delta(kY); -} -/// \return perimeter -template -inline typename coordinate_traits::manhattan_distance_type -perimeter(Box const& b) -{ - return halfPerimeter(b)*2; -} -/// specialization for integers -template <> -inline coordinate_traits::manhattan_distance_type -perimeter(Box const& b) -{ - return halfPerimeter(b)<<1; -} -/// \return true if a point is inside a box -template -inline bool contain(Box const& b, Point const& p) -{ - return contain(b.get(kX), p.x()) && contain(b.get(kY), p.y()); -} -/// \return true if a box contains the other -template -inline bool contain(Box const& b1, Box const& b2) -{ - return contain(b1.get(kX), b2.get(kX)) && contain(b1.get(kY), b2.get(kY)); -} -/// move a box in x or y direction -/// \param v denotes displacement -template -inline Box& move(Box& b, Direction1DType d, typename coordinate_traits::coordinate_type v) -{ - typename Box::interval_type ivl = b.get(d); - move(ivl, v); - return b.set(d, ivl); -} -/// move a box in two directions -/// \param vx denotes displacement in x -/// \param vy denotes displacement in y -template -inline Box& move(Box& b, typename coordinate_traits::coordinate_type vx, typename coordinate_traits::coordinate_type vy) -{ - return move(move(b, kX, vx), kY, vy); -} -/// move a box in two directions -/// \param p.x() denotes destination in x -/// \param p.y() denotes destination in y -template -inline Box& move(Box& b, Point const& p) -{ - return move(b, p.x(), p.y()); -} -/// move a box to a position in x or y direction -/// \param v denotes destination of left lower corner -template -inline Box& moveTo(Box& b, Direction1DType d, typename coordinate_traits::coordinate_type v) -{ - typename Box::interval_type ivl = b.get(d); - moveTo(ivl, v); - return b.set(d, ivl); -} -/// move a box in two directions -/// \param vx denotes destination in x -/// \param vy denotes destination in y -template -inline Box& moveTo(Box& b, typename coordinate_traits::coordinate_type vx, typename coordinate_traits::coordinate_type vy) -{ - return moveTo(moveTo(b, kX, vx), kY, vy); -} -/// move a box in two directions -/// \param p.x() denotes destination in x -/// \param p.y() denotes destination in y -template -inline Box& moveTo(Box& b, Point const& p) -{ - return moveTo(b, p.x(), p.y()); -} -/// \return manhattan distance in a direction between a box and a point -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Box const& b, typename Box::point_type const& p, Direction1DType d) -{ - return distance(b.get(d), p.get(d)); -} -/// \return manhattan distance between a box and a point -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Box const& b, typename Box::point_type const& p) -{ - return manhattanDistance(b, p, kX)+manhattanDistance(b, p, kY); -} -/// \return square distance between a box and a point -template -inline typename coordinate_traits::euclidean_distance_type -squareDistance(Box const& b, typename Box::point_type const& p) -{ - typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; - return (euclidean_distance_type)( - pow(manhattanDistance(b, p, kX), 2) - + pow(manhattanDistance(b, p, kY), 2) - ); -} -/// \return euclidean distance between a box and a point -template -inline typename coordinate_traits::euclidean_distance_type -euclideanDistance(Box const& b, typename Box::point_type const& p) -{ - return sqrt(squareDistance(b, p)); -} -/// \return manhattan distance in a direction between two boxes -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Box const& b1, Box const& b2, Direction1DType d) -{ - return distance(b1.get(d), b2.get(d)); -} -/// \return manhattan distance between two boxes -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Box const& b1, Box const& b2) -{ - return manhattanDistance(b1, b2, kX) + manhattanDistance(b1, b2, kY); -} -/// \return square distance between two boxes -template -inline typename coordinate_traits::euclidean_distance_type -squareDistance(Box const& b1, Box const& b2) -{ - typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; - return (euclidean_distance_type)( - pow(manhattanDistance(b1, b2, kX), 2) - + pow(manhattanDistance(b1, b2, kY), 2) - ); -} -/// \return euclidean distance between two boxes -template -inline typename coordinate_traits::euclidean_distance_type -euclideanDistance(Box const& b1, Box const& b2) -{ - return sqrt(squareDistance(b1, b2)); -} -/// \return the intersection of two boxes -template -inline std::pair, bool> -intersection(Box const& b1, Box const& b2, bool consider_touch = true) -{ - std::pair, bool> ivl[2] = { - intersection(b1.get(kX), b2.get(kX), consider_touch), - intersection(b1.get(kY), b2.get(kY), consider_touch) - }; - return std::make_pair(Box(ivl[kX].first, ivl[kY].first), ivl[kX].second && ivl[kY].second); -} -/// \return true if two boxes have intersection -template -inline bool intersects(Box const& b1, Box const& b2, bool consider_touch = true) -{ - return intersects(b1.get(kX), b2.get(kX), consider_touch) - && intersects(b1.get(kY), b2.get(kY), consider_touch); -} -/// \return the intersection area of two boxes -template -inline typename coordinate_traits::area_type -intersectArea(Box const& b1, Box const& b2) -{ - typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; - typedef typename coordinate_traits::area_type area_type; - manhattan_distance_type dist[2] = { - intersectDistance(b1.get(kX), b2.get(kX)), - intersectDistance(b1.get(kY), b2.get(kY)) - }; - return (area_type)dist[kX]*dist[kY]; -} - -/// \return true if a point is on boundary of the box -template -inline bool onBoundary(Box const& b, Point const& p) -{ - return (onBoundary(b.get(kX), p.x()) && contain(b.get(kY), p.y())) - || (onBoundary(b.get(kY), p.y()) && contain(b.get(kX), p.x())); -} - -/// \return true if boxes can be merged - merge b1 and b2 to b2 -template -inline bool mergeBoxes(Box &b1, Box &b2, int sitePerColumn) //Rectilinear not considered -{ - if (b1.xl() == b2.xl() && b1.xh() == b2.xh()) - { - if (sitePerColumn == 1) - { - b2.set(std::min(b1.xl(), b2.xl()), std::min(b1.yl(), b2.yl()), std::max(b1.xh(), b2.xh()), std::max(b1.yh(), b2.yh())); - } else - { - //Boxes on top/bottom of each other - if (b1.yh() == b2.yl()) //b2 on top of b1 - { - b2.set(b1.xl(), b1.yl(), b2.xh(), b2.yh()); - } else if (b1.yl() == b2.yh()) //b2 below b1 - { - b2.set(b2.xl(), b2.yl(), b1.xh(), b1.yh()); - } //Boxes partially inside another box - merge to one big box - else if (b1.yl() < b2.yh() && b2.yh() < b1.yh()) - { - b2.set(b2.xl(), b2.yl(), b2.xh(), b1.yh()); - } else if (b2.yl() < b1.yh() && b1.yh() < b2.yh()) //b2 below b1 - { - b2.set(b2.xl(), b1.yl(), b2.xh(), b2.yh()); - } - } - return true; - } - return false; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp deleted file mode 100644 index a05977cf..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************* - > File Name: Enums.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "Enums.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#ifndef ENUM2STR -#define ENUM2STR(map, var) \ - map[enum_wrap_type::var] = #var -#endif - -#ifndef STR2ENUM -#define STR2ENUM(map, var) \ - map[#var] = enum_wrap_type::var -#endif - -std::string InstBlk::enum2Str(InstBlk::enum_type const& e) const -{ - static std::map mEnum2Str; - static bool init = true; - - if (init) - { - ENUM2STR(mEnum2Str, LUT0); - ENUM2STR(mEnum2Str, LUT1); - ENUM2STR(mEnum2Str, LUT2); - ENUM2STR(mEnum2Str, LUT3); - ENUM2STR(mEnum2Str, LUT4); - ENUM2STR(mEnum2Str, LUT5); - ENUM2STR(mEnum2Str, LUT6); - ENUM2STR(mEnum2Str, LUT6_2); - ENUM2STR(mEnum2Str, FDRE); - ENUM2STR(mEnum2Str, DSP48E2); - ENUM2STR(mEnum2Str, RAMB36E2); - ENUM2STR(mEnum2Str, BUFGCE); - ENUM2STR(mEnum2Str, IBUF); - ENUM2STR(mEnum2Str, OBUF); - ENUM2STR(mEnum2Str, UNKNOWN); - init = false; - } - - return mEnum2Str.at(e); -} - -InstBlk::enum_type InstBlk::str2Enum(std::string const& s) const -{ - static std::map mStr2Enum; - static bool init = true; - - if (init) - { - STR2ENUM(mStr2Enum, LUT0); - STR2ENUM(mStr2Enum, LUT1); - STR2ENUM(mStr2Enum, LUT2); - STR2ENUM(mStr2Enum, LUT3); - STR2ENUM(mStr2Enum, LUT4); - STR2ENUM(mStr2Enum, LUT5); - STR2ENUM(mStr2Enum, LUT6); - STR2ENUM(mStr2Enum, LUT6_2); - STR2ENUM(mStr2Enum, FDRE); - STR2ENUM(mStr2Enum, DSP48E2); - STR2ENUM(mStr2Enum, RAMB36E2); - STR2ENUM(mStr2Enum, BUFGCE); - STR2ENUM(mStr2Enum, IBUF); - STR2ENUM(mStr2Enum, OBUF); - STR2ENUM(mStr2Enum, UNKNOWN); - init = false; - } - - std::map::const_iterator found = mStr2Enum.find(s); - if (found == mStr2Enum.end()) - { - dreamplacePrint(kWARN, "%s unknown enum type %s, set to UNKNOWN\n", __func__, s.c_str()); - return enum_wrap_type::UNKNOWN; - } - else - { - return found->second; - } -} - -std::string Site::enum2Str(Site::enum_type const& e) const -{ - static std::map mEnum2Str; - static bool init = true; - - if (init) - { - ENUM2STR(mEnum2Str, IO); - ENUM2STR(mEnum2Str, SLICE); - ENUM2STR(mEnum2Str, DSP); - ENUM2STR(mEnum2Str, BRAM); - ENUM2STR(mEnum2Str, UNKNOWN); - init = false; - } - - return mEnum2Str.at(e); -} - -Site::enum_type Site::str2Enum(std::string const& s) const -{ - static std::map mStr2Enum; - static bool init = true; - - if (init) - { - STR2ENUM(mStr2Enum, IO); - STR2ENUM(mStr2Enum, SLICE); - STR2ENUM(mStr2Enum, DSP); - STR2ENUM(mStr2Enum, BRAM); - STR2ENUM(mStr2Enum, UNKNOWN); - init = false; - } - - std::map::const_iterator found = mStr2Enum.find(s); - if (found == mStr2Enum.end()) - { - dreamplacePrint(kWARN, "%s unknown enum type %s, set to UNKNOWN\n", __func__, s.c_str()); - return enum_wrap_type::UNKNOWN; - } - else - { - return found->second; - } -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h deleted file mode 100644 index 63af34f3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h +++ /dev/null @@ -1,222 +0,0 @@ -/************************************************************************* - > File Name: Enums.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_ENUMS_H -#define DREAMPLACE_ENUMS_H - -#include -#include -#include -#include "Util.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// base class for enumeration types -/// these types are not recommended for storage, since they takes larger memory -template -class EnumExt -{ - public: - typedef EnumType enum_type; - EnumExt() {} - EnumExt& operator=(EnumExt const& rhs) - { - if (this != &rhs) - m_value = rhs.m_value; - return *this; - } - EnumExt& operator=(enum_type const& rhs) - { - m_value = rhs; - return *this; - } - EnumExt& operator=(std::string const& rhs) - { - m_value = str2Enum(rhs); - return *this; - } - virtual operator std::string() const - { - return enum2Str(m_value); - } - operator int() const - { - return value(); - } - enum_type value() const - { - return m_value; - } - - bool operator==(EnumExt const& rhs) const {return m_value == rhs.m_value;} - bool operator==(enum_type const& rhs) const {return m_value == rhs;} - bool operator==(std::string const& rhs) const {return *this == EnumExt(rhs);} - bool operator!=(EnumExt const& rhs) const {return m_value != rhs.m_value;} - bool operator!=(enum_type const& rhs) const {return m_value != rhs;} - bool operator!=(std::string const& rhs) const {return *this != EnumExt(rhs);} - - friend std::ostream& operator<<(std::ostream& os, const EnumExt& rhs) - { - rhs.print(os); - return os; - } - protected: - virtual void print(std::ostream& os) const {os << this->enum2Str(m_value);} - - virtual std::string enum2Str(enum_type const&) const = 0; - virtual enum_type str2Enum(std::string const&) const = 0; - - enum_type m_value; -}; - -/// class InstBlk denotes Instance type -struct InstBlkEnum -{ - enum InstBlkType - { - LUT0 = 0, - LUT1 = 0, - LUT2 = 1, - LUT3 = 2, - LUT4 = 3, - LUT5 = 4, - LUT6 = 5, - LUT6_2 = 5, - FDRE = 6, - DSP48E2 = 7, - RAMB36E2 = 8, - BUFGCE = 9, - IBUF = 10, - OBUF = 11, - UNKNOWN = 12 - }; -}; -class InstBlk : public EnumExt -{ - public: - typedef InstBlkEnum enum_wrap_type; - typedef enum_wrap_type::InstBlkType enum_type; - typedef EnumExt base_type; - - InstBlk() : base_type() {m_value = enum_wrap_type::UNKNOWN;} - InstBlk(InstBlk const& rhs) : base_type() {m_value = rhs.m_value;} - InstBlk(enum_type const& rhs) : base_type() {m_value = rhs;} - InstBlk(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} - InstBlk& operator=(InstBlk const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - InstBlk& operator=(enum_type const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - InstBlk& operator=(std::string const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - - protected: - virtual std::string enum2Str(enum_type const& e) const; - virtual enum_type str2Enum(std::string const& s) const; -}; - - -/// class Site denotes Site type -struct SiteEnum -{ - enum SiteType - { - IO = 0, - SLICE = 1, - DSP = 2, - BRAM = 3, - UNKNOWN = 4 - }; -}; -class Site : public EnumExt -{ - public: - typedef SiteEnum enum_wrap_type; - typedef enum_wrap_type::SiteType enum_type; - typedef EnumExt base_type; - - Site() : base_type() {m_value = enum_wrap_type::UNKNOWN;} - Site(Site const& rhs) : base_type() {m_value = rhs.m_value;} - Site(enum_type const& rhs) : base_type() {m_value = rhs;} - Site(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} - Site& operator=(Site const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - Site& operator=(enum_type const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - Site& operator=(std::string const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - - protected: - virtual std::string enum2Str(enum_type const& e) const; - virtual enum_type str2Enum(std::string const& s) const; -}; - -/// class RegionEnumType denotes the region type defined in DEF -struct RegionTypeEnum -{ - enum RegionEnumType - { - FENCE = 0, - GUIDE = 1, - UNKNOWN = 2 - }; -}; - -class RegionType : public EnumExt -{ - public: - typedef RegionTypeEnum enum_wrap_type; - typedef enum_wrap_type::RegionEnumType enum_type; - typedef EnumExt base_type; - - RegionType() : base_type() {m_value = enum_wrap_type::UNKNOWN;} - RegionType(RegionType const& rhs) : base_type() {m_value = rhs.m_value;} - RegionType(enum_type const& rhs) : base_type() {m_value = rhs;} - RegionType(std::string const& rhs) : base_type() {m_value = str2Enum(rhs);} - RegionType& operator=(RegionType const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - RegionType& operator=(enum_type const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - RegionType& operator=(std::string const& rhs) - { - this->base_type::operator=(rhs); - return *this; - } - - protected: - virtual std::string enum2Str(enum_type const& e) const; - virtual enum_type str2Enum(std::string const& s) const; -}; - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h deleted file mode 100644 index 165db190..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h +++ /dev/null @@ -1,87 +0,0 @@ -/************************************************************************* - > File Name: GeometryApi.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Tue Jun 30 16:57:11 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_GEOMETRYAPI_H -#define DREAMPLACE_GEOMETRYAPI_H - -#include -#include -#include -#include "Util.h" -#include "Point.h" -#include "Box.h" - -/// limbo.geometry api -/// with this api, algorithms in limbo.geometry are available -namespace limbo { namespace geometry { - -/// \brief specialization for boost::polygon::point_data -template -struct point_traits > -{ - typedef T coordinate_type; - typedef DREAMPLACE_NAMESPACE::Point point_type; - - static coordinate_type get(const point_type& point, orientation_2d const& orient) - { - if (orient == HORIZONTAL) return point.x(); - else if (orient == VERTICAL) return point.y(); - else {assert(0); return 0;} - } - static void set(point_type& point, orientation_2d const& orient, coordinate_type const& value) - { - if (orient == HORIZONTAL) point.set(DREAMPLACE_NAMESPACE::kX, value); - else if (orient == VERTICAL) point.set(DREAMPLACE_NAMESPACE::kY, value); - else dreamplaceAssertMsg(0, "unknown orient"); - } - static point_type construct(coordinate_type const& x, coordinate_type const& y) - { - return point_type(x, y); - } -}; - -/// \brief specialization for boost::polygon::rectangle_data -template -struct rectangle_traits > -{ - typedef T coordinate_type; - typedef DREAMPLACE_NAMESPACE::Box rectangle_type; - - static coordinate_type get(const rectangle_type& rect, direction_2d const& dir) - { - switch (dir) - { - case LEFT: return rect.xl(); - case BOTTOM: return rect.yl(); - case RIGHT: return rect.xh(); - case TOP: return rect.yh(); - default: dreamplaceAssertMsg(0, "unknown orient"); - } - return std::numeric_limits::max(); - } - static void set(rectangle_type& rect, direction_2d const& dir, coordinate_type const& value) - { - switch (dir) - { - case LEFT: rect.set(DREAMPLACE_NAMESPACE::kXLOW, value); break; - case BOTTOM: rect.set(DREAMPLACE_NAMESPACE::kYLOW, value); break; - case RIGHT: rect.set(DREAMPLACE_NAMESPACE::kXHIGH, value); break; - case TOP: rect.set(DREAMPLACE_NAMESPACE::kYHIGH, value); break; - default: dreamplaceAssertMsg(0, "unknown orient"); - } - } - static rectangle_type construct(coordinate_type const& xl, coordinate_type const& yl, - coordinate_type const& xh, coordinate_type const& yh) - { - return rectangle_type(xl, yl, xh, yh); - } -}; - -}}// namespace limbo // namespace geometry - - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp deleted file mode 100644 index 9bc020e0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file Group.cpp - * @author Yibo Lin (DREAMPlace) - * @date Dec 2019 - */ -#include "Group.h" - -DREAMPLACE_BEGIN_NAMESPACE - -Group::Group() - : Group::base_type() - , m_name("") - , m_vNodeName() - , m_vNodeId() - , m_region_id(std::numeric_limits::max()) -{ -} -Group::Group(Group const& rhs) - : Group::base_type(rhs) -{ - copy(rhs); -} -Group& Group::operator=(Group const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -void Group::copy(Group const& rhs) -{ - m_name = rhs.m_name; - m_vNodeName = rhs.m_vNodeName; - m_vNodeId = rhs.m_vNodeId; - m_region_id = rhs.m_region_id; -} - - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h deleted file mode 100644 index 535acd4c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file Group.h (DREAMPlace) - * @author Yibo Lin - * @date Dec 2019 - */ - -#ifndef DREAMPLACE_GROUP_H -#define DREAMPLACE_GROUP_H - -#include -#include "Object.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class Group : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef coordinate_traits::index_type index_type; - - /// default constructor - Group(); - /// copy constructor - Group(Group const& rhs); - /// assignment - Group& operator=(Group const& rhs); - - /// node functions - std::string const& name() const {return m_name;} - Group& setName(std::string const& name) {m_name = name; return *this;} - - std::vector const& nodeNames() const {return m_vNodeName;} - std::vector& nodeNames() {return m_vNodeName;} - - std::vector const& nodes() const {return m_vNodeId;} - std::vector& nodes() {return m_vNodeId;} - - index_type region() const {return m_region_id;} - Group& setRegion(index_type region_id) {m_region_id = region_id; return *this;} - - protected: - void copy(Group const& rhs); - - std::string m_name; ///< group name - std::vector m_vNodeName; ///< group node names, they names may be regex - std::vector m_vNodeId; ///< group node indices - index_type m_region_id; ///< region index -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h deleted file mode 100644 index a2128ed7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h +++ /dev/null @@ -1,262 +0,0 @@ -/************************************************************************* - > File Name: HrchyList.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Mon 01 Feb 2016 02:17:29 PM CST - ************************************************************************/ - -#ifndef DREAMPLACE_HRCHYLIST_H -#define DREAMPLACE_HRCHYLIST_H - -#include -#include -#include -#include "Util.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// HrchyList defines a hierarchical list for data storage. -/// Data is stored like a matrix with 2-dimension (row/column). -/// The second dimension may have different size with different first dimension. -/// So it is not actually a matrix. -/// The api allows to iterate the HrchyList like a 1D array. - -template -class HrchyList -{ - public: - typedef T value_type; - typedef unsigned int index_type; - typedef std::vector container_type; - typedef typename container_type::iterator iterator_type; - typedef typename container_type::const_iterator const_iterator_type; - - /// constructor - HrchyList() - { - } - /// copy constructor - HrchyList(HrchyList const& rhs) - { - copy(rhs); - } - /// assignment - HrchyList& operator=(HrchyList const& rhs) - { - if (this != &rhs) - copy(rhs); - return *this; - } - /// destructor - ~HrchyList() {} - - /// \return size of all data - index_type size() const {return m_vData.size();} - /// \return number of rows - index_type numRows() const {return m_vRowBeginIdx.size();} - /// \return next row begin index - /// overflow to m_vData.size() - index_type getNextRowBeginIndex(index_type rowIdx) const - { - index_type nr = numRows(); - index_type nextRowIdx = rowIdx+1; - index_type nextRowBeginIdx; - if (nextRowIdx >= nr) // rowIdx is last row - nextRowBeginIdx = m_vData.size(); - else - { - nextRowBeginIdx = m_vRowBeginIdx[nextRowIdx]; - if (nextRowBeginIdx >= s_largeIdx)// nextRowIdx is empty - nextRowBeginIdx -= s_largeIdx; - } - return nextRowBeginIdx; - } - /// \return number of columns in a specific row - index_type numCols(index_type rowIdx) const - { - index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; - index_type nextRowBeginIdx; - if (rowBeginIdx < s_largeIdx) // non-empty row - nextRowBeginIdx = getNextRowBeginIndex(rowIdx); - else // empty row - nextRowBeginIdx = rowBeginIdx; - return nextRowBeginIdx-rowBeginIdx; - } - - /// \return a row of data - std::pair row(index_type rowIdx) - { - index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; - index_type nextRowBeginIdx; - if (rowBeginIdx < s_largeIdx) // non-empty row - nextRowBeginIdx = getNextRowBeginIndex(rowIdx); - else // empty row - nextRowBeginIdx = rowBeginIdx = rowBeginIdx-s_largeIdx; - return std::make_pair(m_vData.begin()+rowBeginIdx, m_vData.begin()+nextRowBeginIdx); - } - std::pair row(index_type rowIdx) const - { - index_type rowBeginIdx = m_vRowBeginIdx[rowIdx]; - index_type nextRowBeginIdx; - if (rowBeginIdx < s_largeIdx) // non-empty row - nextRowBeginIdx = getNextRowBeginIndex(rowIdx); - else // empty row - nextRowBeginIdx = rowBeginIdx = rowBeginIdx-s_largeIdx; - return std::make_pair(m_vData.begin()+rowBeginIdx, m_vData.begin()+nextRowBeginIdx); - } - /// \return range of rows - /// assume \param rowIdxL < \param rowIdxH and they are all within the range - std::pair rowRange(index_type rowIdxL, index_type rowIdxH) - { - index_type rowBeginIdxL = m_vRowBeginIdx[rowIdxL]; - if (rowBeginIdxL >= s_largeIdx) // empty row of rowIdxL - rowBeginIdxL -= s_largeIdx; - index_type nextRowBeginIdxH = getNextRowBeginIndex(rowIdxH); - - return std::make_pair(m_vData.begin()+rowBeginIdxL, m_vData.begin()+nextRowBeginIdxH); - } - std::pair rowRange(index_type rowIdxL, index_type rowIdxH) const - { - index_type rowBeginIdxL = m_vRowBeginIdx[rowIdxL]; - if (rowBeginIdxL >= s_largeIdx) // empty row of rowIdxL - rowBeginIdxL -= s_largeIdx; - index_type nextRowBeginIdxH = getNextRowBeginIndex(rowIdxH); - - return std::make_pair(m_vData.begin()+rowBeginIdxL, m_vData.begin()+nextRowBeginIdxH); - } - - /// \return the index of first non-empty row - index_type frontNonemptyRowIndex() const - { - index_type rowIdx = 0; - index_type nr = numRows(); - while (rowIdx < nr && m_vRowBeginIdx[rowIdx] >= s_largeIdx) - ++rowIdx; - return rowIdx; - } - /// \return the index of last non-empty row - index_type backNonemptyRowIndex() const - { - index_type rowIdx = numRows(); - while (rowIdx > 0 && m_vRowBeginIdx[rowIdx-1] >= s_largeIdx) - --rowIdx; - return rowIdx-1; // overflow if no non-empty row - } - - /// \return a single value - /// assume row is not empty - T& at(index_type rowIdx, index_type colIdx) {return m_vData[m_vRowBeginIdx[rowIdx]+colIdx];} - T const& at(index_type rowIdx, index_type colIdx) const {return m_vData[m_vRowBeginIdx[rowIdx]+colIdx];} - - /// \return begin iterator of data - iterator_type begin() {return m_vData.begin();} - const_iterator_type begin() const {return m_vData.begin();} - /// \return end iterator of data - iterator_type end() {return m_vData.end();} - const_iterator_type end() const {return m_vData.end();} - /// \return data - std::vector& data() {return m_vData;} - std::vector const& data() const {return m_vData;} - - /// \return the begin iterator of m_vRowBeginIdx - std::vector::iterator beginRowIndex() {return m_vRowBeginIdx.begin();} - std::vector::const_iterator beginRowIndex() const {return m_vRowBeginIdx.begin();} - /// \return the end iterator of m_vRowBeginIdx - std::vector::iterator endRowIndex() {return m_vRowBeginIdx.end();} - std::vector::const_iterator endRowIndex() const {return m_vRowBeginIdx.end();} - - /// construct HrchyList with a 2D array - void set(std::vector > const& mData) - { - index_type numData = 0; - for (typename std::vector >::const_iterator it1 = mData.begin(), it1e = mData.end(); it1 != it1e; ++it1) - numData += it1->size(); - m_vData.resize(numData); - m_vRowBeginIdx.resize(mData.size(), 0); - - index_type count = 0; - for (index_type rowIdx = 0; rowIdx < mData.size(); ++rowIdx) - { - m_vRowBeginIdx[rowIdx] = count; - if (mData[rowIdx].empty()) - m_vRowBeginIdx[rowIdx] += s_largeIdx; - - for (index_type colIdx = 0; colIdx < mData[rowIdx].size(); ++colIdx) - m_vData[count++] = mData[rowIdx][colIdx]; - } - } - /// construct HrchyList by three steps - /// this is the first step that append all the data to m_vData - void setPartial1(value_type v) - { - m_vData.push_back(v); - } - /// this is the second step that given all the row index of existing data - /// construct the rest, \param vRowId must have the same order with m_vData - /// \param rn denotes number of rows in the list - /// \param vRowId assumes the row id starts from 0 - void setPartial2(std::vector const& vRowId, std::vector >& mData, index_type rn) - { - mData.resize(rn); - for (index_type i = 0, ie = m_vData.size(); i < ie; ++i) - mData[vRowId[i]].push_back(m_vData[i]); - // users are allowed to manipulate mData before calling the third step - } - /// this is the third step - /// actually an alias of set() - void setPartial3(std::vector > const& mData) - { - set(mData); - } - /// clear all data - void clear() - { - m_vData.clear(); - m_vRowBeginIdx.clear(); - } - - /// for debug - void printHrchy(std::ostream& os = std::cout) const - { - for (index_type rowIdx = 0; rowIdx < numRows(); ++rowIdx) - { - std::pair found = row(rowIdx); - os << "#" << rowIdx << ": (" << numCols(rowIdx) << ")"; - for (const_iterator_type it = found.first; it != found.second; ++it) - os << *it << " "; - os << "\n"; - } - } - void print(std::ostream& os = std::cout) const - { - os << "data: "; - for (const_iterator_type it = begin(), ite = end(); it != ite; ++it) - os << *it << " "; - os << "\n"; - os << "row begin idx: "; - for (std::vector::const_iterator it = beginRowIndex(), ite = endRowIndex(); it != ite; ++it) - os << *it << " "; - os << "\n"; - os << "first non-empty row index: " << frontNonemptyRowIndex() << "\n"; - os << "last non-empty row index: " << backNonemptyRowIndex() << "\n"; - } - protected: - void copy(HrchyList const& rhs) - { - m_vData = rhs.m_vData; - m_vRowBeginIdx = rhs.m_vRowBeginIdx; - } - - std::vector m_vData; - std::vector m_vRowBeginIdx; ///< the index of row beginners - ///< if a row is empty, m_vRowBeginIdx[] is set to s_largeIdx + m_vRowBeginIdx[non-empty index] - ///< e.g. row 1 is empty, row 2 is not empty, then m_vRowBeginIdx[1] = s_largeIdx + m_vRowBeginIdx[2] - static const index_type s_largeIdx; ///< a very large number for index -}; - -template -const typename HrchyList::index_type HrchyList::s_largeIdx = std::numeric_limits::index_type>::max()>>1; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h deleted file mode 100644 index b2fcdab5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************************* - > File Name: Index.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sat 27 Jun 2015 04:31:39 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_INDEX_H -#define DREAMPLACE_INDEX_H - -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// 2D index class in x and y direction -template -class Index2D -{ - public: - typedef T index_type; - - ///==== constructors ==== - Index2D(index_type ix = std::numeric_limits::max(), - index_type iy = std::numeric_limits::max()) - { - set(ix, iy); - } - Index2D(Index2D const& rhs) - { - copy(rhs); - } - Index2D& operator=(Index2D const& rhs) - { - copy(rhs); - return *this; - } - - /// member functions - Index2D& set(Direction1DType d, index_type v) - { - m_index[d] = v; - } - Index2D& set(index_type ix, index_type iy) - { - m_index[kX] = ix; - m_index[kY] = iy; - } - - index_type get(Direction1DType d) const {return m_index[d];} - index_type x() const {return m_index[kX];} - index_type y() const {return m_index[kY];} - - /// compatible to operator [] - index_type const& operator[](Direction1DType d) const {return m_index[d];} - index_type& operator[](Direction1DType d) {return m_index[d];} - - protected: - void copy(Index2D const& rhs) - { - m_index[kX] = rhs.m_index[kX]; - m_index[kY] = rhs.m_index[kY]; - } - - T m_index[2]; -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h deleted file mode 100644 index 4a05166c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h +++ /dev/null @@ -1,266 +0,0 @@ -/************************************************************************* - > File Name: Interval.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Jun 2015 08:52:22 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_INTERVAL_H -#define DREAMPLACE_INTERVAL_H - -#include -#include -#include "Util.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// an interval class -/// it consists of a low value and a high value -template -class Interval -{ - public: - typedef T coordinate_type; - typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; - - ///==== constructors ==== - /// default constructor will result in an invalid interval - /// but it is more compatible to functions such as encompass() - Interval(coordinate_type l = std::numeric_limits::max(), - coordinate_type h = std::numeric_limits::min()) - { - set(l, h); - } - - Interval(Interval const& rhs) - { - copy(rhs); - } - - Interval& operator=(Interval const& rhs) - { - copy(rhs); - return *this; - } - - template - Interval& operator=(IntervalType const& rhs) - { - copy(rhs); - return *this; - } - - ///==== public member functions ==== - Interval& set(coordinate_type l, coordinate_type h) - { - m_coords[kLOW] = l; - m_coords[kHIGH] = h; - return *this; - } - Interval& set(Direction1DType d, coordinate_type v) - { - m_coords[d] = v; - return *this; - } - /// encompass interval with an input value - Interval& encompass(coordinate_type v) - { - if (m_coords[kLOW] > v) m_coords[kLOW] = v; - if (m_coords[kHIGH] < v) m_coords[kHIGH] = v; - return *this; - } - /// encompass interval with an input interval - Interval& encompass(Interval const& i) - { - encompass(i.get(kLOW)); - encompass(i.get(kHIGH)); - return *this; - } - /// make sure low is no larger than high - Interval& adjust() - { - if (m_coords[kLOW] > m_coords[kHIGH]) - swap(m_coords[kLOW], m_coords[kHIGH]); - return *this; - } - coordinate_type get(Direction1DType d) const - { - return m_coords[d]; - } - coordinate_type low() const - { - return m_coords[kLOW]; - } - coordinate_type high() const - { - return m_coords[kHIGH]; - } - manhattan_distance_type delta() const - { - return high() - low(); - } - - ///==== overload operators ==== - bool operator==(Interval const& rhs) const - { - return low() == rhs.low() && high() == rhs.high(); - } - - bool operator!=(Interval const& rhs) const - { - return low() != rhs.low() || high() != rhs.high(); - } - - bool operator<(Interval const& rhs) const - { - if (m_coords[0] != rhs.m_coords[0]) { - return m_coords[0] < rhs.m_coords[0]; - } - return m_coords[1] < rhs.m_coords[1]; - } - - bool operator<=(Interval const& rhs) const - { - return !(rhs < *this); - } - - bool operator>(Interval const& rhs) const - { - return rhs < *this; - } - - bool operator>=(Interval const& rhs) const - { - return !((*this) < rhs); - } - - /// comparison function objects - struct CompareByLow - { - bool operator()(Interval const& i1, Interval const& i2) const {return i1.low() < i2.low();} - }; - struct CompareByHigh - { - bool operator()(Interval const& i1, Interval const& i2) const {return i1.high() < i2.high();} - }; - struct CompareByCenter - { - bool operator()(Interval const& i1, Interval const& i2) const {return i1.center() < i2.center();} - }; - - protected: - template - void copy(IntervalType const& rhs) - { - m_coords[0] = rhs.m_coords[0]; - m_coords[1] = rhs.m_coords[1]; - } - - coordinate_type m_coords[2]; ///< low and high values -}; - -/// \return center of an interval -template -inline typename coordinate_traits::coordinate_type -center(Interval const& i1) -{ - return (i1.low()+i1.high())/2; -} -/// specialization for integers -template <> -inline coordinate_traits::coordinate_type -center(Interval const& i1) -{ - return (i1.low()+i1.high())>>1; -} -/// \return the intersection of two intervals -/// false denotes no intersection -template -inline std::pair, bool> -intersection(Interval const& i1, Interval const& i2, bool consider_touch = true) -{ - typedef typename coordinate_traits::coordinate_type coordinate_type; - coordinate_type l = std::max(i1.low(), i2.low()); - coordinate_type h = std::min(i1.high(), i2.high()); - bool valid = (consider_touch)? l <= h : l < h; - - return std::make_pair(Interval(l, h), valid); -} -/// \return true if two intervals have intersection -template -inline bool intersects(Interval const& i1, Interval const& i2, bool consider_touch = true) -{ - typedef typename coordinate_traits::coordinate_type coordinate_type; - coordinate_type l = std::max(i1.low(), i2.low()); - coordinate_type h = std::min(i1.high(), i2.high()); - return (consider_touch)? l <= h : l < h; -} -/// \return the intersection distance of two intervals -template -inline typename coordinate_traits::manhattan_distance_type -intersectDistance(Interval const& i1, Interval const& i2) -{ - typedef typename coordinate_traits::coordinate_type coordinate_type; - typedef typename coordinate_traits::manhattan_distance_type manhattan_distance_type; - coordinate_type l = std::max(i1.low(), i2.low()); - coordinate_type h = std::min(i1.high(), i2.high()); - return (l < h)? (manhattan_distance_type)h-l : 0; -} - -/// \return true if an interval contains a value -template -inline bool contain(Interval const& i1, typename coordinate_traits::coordinate_type v) -{ - return i1.low() <= v && v <= i1.high(); -} -/// \return true if an interval contains the other -template -inline bool contain(Interval const& i1, Interval const& i2) -{ - return i1.low() <= i2.low() && i2.high() <= i1.high(); -} -/// move an interval -template -inline Interval& move(Interval& i1, typename coordinate_traits::coordinate_type v) -{ - return i1.set(i1.low()+v, i1.high()+v); -} -/// move an interval to a destination -/// \param v is the final lower value, the delta of interval does not change -template -inline Interval& moveTo(Interval& i1, typename coordinate_traits::coordinate_type v) -{ - typename coordinate_traits::manhattan_distance_type delta = i1.delta(); - return i1.set(v, v+delta); -} -/// \return distance -template -inline typename coordinate_traits::manhattan_distance_type -distance(Interval const& i1, typename coordinate_traits::coordinate_type v) -{ - if (v < i1.low()) return i1.low()-v; - else if (v > i1.high()) return v-i1.high(); - else return 0; -} -/// \return the distance between two intervals -/// \return 0 if two intervals have intersection -template -inline typename coordinate_traits::manhattan_distance_type -distance(Interval const& i1, Interval const& i2) -{ - typedef typename coordinate_traits::coordinate_type coordinate_type; - coordinate_type l = std::max(i1.low(), i2.low()); - coordinate_type h = std::min(i1.high(), i2.high()); - return (l > h)? l-h : 0; // different from computing intersection -} - -/// \return true if the value is on the boundary of the interval -template -inline bool onBoundary(Interval const& t, typename coordinate_traits::coordinate_type v) -{ - return t.low() == v || t.high() == v; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h deleted file mode 100644 index a7ba360a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h +++ /dev/null @@ -1,403 +0,0 @@ -/************************************************************************* - > File Name: Iterators.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 21 Jun 2015 01:27:50 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_ITERATORS_H -#define DREAMPLACE_ITERATORS_H - -#include "PlaceDB.h" -#include "Index.h" -//#include "RowMap.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// whether T is a const type -template struct is_constant {const static bool value = false;}; - -template -struct is_constant {const static bool value = true;}; - -/// if T1 is const type, set T2 as const type -/// otherwise, still use T2 -template -struct constant_helper -{ - typedef T2& reference_type; - typedef T2* pointer_type; -}; -template -struct constant_helper -{ - typedef T2 const& reference_type; - typedef const T2* pointer_type; -}; - -template -struct IteratorDeref; - -template -struct IteratorDimension; - -/// database iterator, generic class for all 1D iterators -template -class DBIterator -{ - public: - typedef PlaceDBType placedb_type; - typedef IteratorTagType iterator_tag_type; - typedef typename placedb_type::index_type index_type; - typedef IteratorDeref iterator_deref_type; - typedef typename iterator_deref_type::reference_type reference_type; - typedef typename iterator_deref_type::pointer_type pointer_type; - - /// constructor - DBIterator(index_type id = std::numeric_limits::max(), - index_type idxl = std::numeric_limits::max(), - index_type idxh = std::numeric_limits::max(), - PlaceDBType* db = NULL) : m_index (id), m_range(idxl, idxh), m_db(db) {} - /// copy constructor - DBIterator(DBIterator const& rhs) {copy(rhs);} - template - DBIterator(DBIterator const& rhs) {copy(rhs);} - /// assignment - DBIterator& operator=(DBIterator const& rhs) {copy(rhs); return *this;} - /// destructor - virtual ~DBIterator() {} - - DBIterator& operator+=(int offset) {m_index += offset; return *this;} - DBIterator& operator-=(int offset) {return this->operator+=(-offset);} - DBIterator& operator++() {return this->operator+=(1);} - DBIterator& operator--() {return this->operator-=(1);} - DBIterator operator++(int) const - { - DBIterator tmp (*this); - return (++tmp); - } - DBIterator operator--(int) const - { - DBIterator tmp (*this); - return (--tmp); - } - DBIterator operator+(int offset) const - { - DBIterator tmp (*this); - return (tmp+=offset); - } - DBIterator operator-(int offset) const {return this->operator+(-offset);} - - bool operator==(DBIterator const& rhs) const {return this->compare(rhs);} - bool operator!=(DBIterator const& rhs) const {return !this->compare(rhs);} - - /// unsafe dereference - reference_type deref() const - { - dreamplaceAssertMsg(inRange(), "index = %u out of range [%u, %u]\n", m_index, m_range.low(), m_range.high()); - return s_deref(*m_db, m_index); - } - - reference_type operator*() const - { - return deref(); - } - pointer_type operator->() const - { - return &deref(); - } - bool inRange() const {return m_range.low() <= m_index && m_index < m_range.high();} - - Interval range() const {return m_range;} - PlaceDBType& placeDB() const {return *m_db;} - index_type index() const {return m_index;} - - protected: - template - void copy(DBIterator const& rhs) - { - // only allow conversion iterator -> const_iterator - // failed to use enable_if like specialization - // use static assertion instead - dreamplaceStaticAssert::value || !is_constant::value>("do not allow conversion from const iterator to iterator"); - m_index = rhs.index(); - m_range = rhs.range(); - m_db = &rhs.placeDB(); - } - /// \return true if indice are same or both out of range - bool compare(DBIterator const& rhs) const - { - return m_db == rhs.m_db && (m_index == rhs.m_index || (!inRange() && !rhs.inRange())); - } - - index_type m_index; ///< current index - Interval m_range; ///< range of index - PlaceDBType* m_db; - - static iterator_deref_type s_deref; ///< a static member for dereference to avoid frequent construction -}; - -template -typename DBIterator::iterator_deref_type DBIterator::s_deref; - -/// iterator for 2D data structures -/// in such kind of structures, a 2D map is used -/// the iterator traverses through 2D map and find the corresponding element -template -class DB2DIterator -{ - public: - typedef DBType db_type; - typedef IteratorTagType iterator_tag_type; - typedef typename db_type::index_type index_type; - typedef Index2D index2d_type; - typedef IteratorDeref iterator_deref_type; - typedef typename iterator_deref_type::reference_type reference_type; - typedef typename iterator_deref_type::pointer_type pointer_type; - /// get dimensions of 2D map - /// need to provide two functions to get level-1 and level-2 dimensions - typedef IteratorDimension iterator_dimension_type; - - /// constructor - DB2DIterator(index_type ix = std::numeric_limits::max(), - index_type iy = std::numeric_limits::max(), - DBType* db = NULL) : m_index(ix, iy), m_db(db) {} - /// copy constructor - DB2DIterator(DB2DIterator const& rhs) {copy(rhs);} - template - DB2DIterator(DB2DIterator const& rhs) {copy(rhs);} - /// assignment - DB2DIterator& operator=(DB2DIterator const& rhs) {copy(rhs); return *this;} - /// destructor - virtual ~DB2DIterator() {} - - /// member functions - DB2DIterator& operator++() {return this->increment();} - DB2DIterator operator++(int) const - { - DB2DIterator tmp (*this); - return (++tmp); - } - - bool operator==(DB2DIterator const& rhs) const {return this->compare(rhs);} - bool operator!=(DB2DIterator const& rhs) const {return !this->compare(rhs);} - - /// unsafe dereference - reference_type deref() const - { - dreamplaceAssertMsg(inRange(), "index = [%u, %u] out of range [%u, %u]\n", m_index.x(), m_index.y()); - return s_deref(*m_db, m_index); - } - - reference_type operator*() const - { - return deref(); - } - pointer_type operator->() const - { - return &deref(); - } - bool inRange() const - { - return m_index[kX] < s_dimension(*m_db, kX, m_index) && m_index[kY] < s_dimension(*m_db, kY, m_index); - } - - index2d_type const& index() const {return m_index;} - DBType& db() {return *m_db;} - - protected: - template - void copy(DB2DIterator const& rhs) - { - // only allow conversion iterator -> const_iterator - // failed to use enable_if like specialization - // use static assertion instead - dreamplaceStaticAssert::value || !is_constant::value>("do not allow conversion from const iterator to iterator"); - m_index = rhs.index(); - m_db = &rhs.db(); - } - /// \return true if indice are same or both out of range - bool compare(DB2DIterator const& rhs) const - { - return m_db == rhs.m_db && (m_index == rhs.m_index || (!inRange() && !rhs.inRange())); - } - /// self-increment 1 - /// do nothing if out-of-range - DB2DIterator& increment() - { - if (m_index[iterator_dimension_type::direct1] < s_dimension(*m_db, iterator_dimension_type::direct1, m_index)) - { - if (m_index[iterator_dimension_type::direct2]+1 < s_dimension(*m_db, iterator_dimension_type::direct2, m_index)) - m_index[iterator_dimension_type::direct2] += 1; - else - { - m_index[iterator_dimension_type::direct1] += 1; - m_index[iterator_dimension_type::direct2] = 0; - } - } - return *this; - } - - index2d_type m_index; ///< 2D index - DBType* m_db; ///< data - - static iterator_deref_type s_deref; ///< a static member for dereference to avoid frequent construction - static iterator_dimension_type s_dimension; ///< a static member to avoid frequent construction -}; - -template -typename DB2DIterator::iterator_deref_type DB2DIterator::s_deref; -template -typename DB2DIterator::iterator_dimension_type DB2DIterator::s_dimension; - -struct MovableNodeIteratorTag {}; -struct FixedNodeIteratorTag {}; -//struct PlaceBlockageIteratorTag {}; -//struct NonCoreNodeIteratorTag {}; -//struct IOPinNodeIteratorTag {}; -//struct CellMacroIteratorTag {}; -//struct IOPinMacroIteratorTag {}; -//struct SubRowMap2DIteratorTag {}; - -typedef DBIterator MovableNodeIterator; -typedef DBIterator FixedNodeIterator; -//typedef DBIterator PlaceBlockageIterator; -//typedef DBIterator NonCoreNodeIterator; -//typedef DBIterator IOPinNodeIterator; -//typedef DBIterator CellMacroIterator; -//typedef DBIterator IOPinMacroIterator; -//typedef DB2DIterator SubRowMap2DIterator; - -typedef DBIterator MovableNodeConstIterator; -typedef DBIterator FixedNodeConstIterator; -//typedef DBIterator PlaceBlockageConstIterator; -//typedef DBIterator NonCoreNodeConstIterator; -//typedef DBIterator IOPinNodeConstIterator; -//typedef DBIterator CellMacroConstIterator; -//typedef DBIterator IOPinMacroConstIterator; -//typedef DB2DIterator SubRowMap2DConstIterator; - -/// specialization for different iterators -template -struct IteratorDeref -{ - typedef typename constant_helper::reference_type reference_type; - typedef typename constant_helper::pointer_type pointer_type; - typedef DBIterator iterator_type; - typedef typename iterator_type::placedb_type placedb_type; - typedef typename iterator_type::index_type index_type; - inline reference_type operator()(placedb_type& db, index_type index) const - { - return db.nodes().at(db.movableNodeIndices().at(index)); - } -}; -template -struct IteratorDeref -{ - typedef typename constant_helper::reference_type reference_type; - typedef typename constant_helper::pointer_type pointer_type; - typedef DBIterator iterator_type; - typedef typename iterator_type::placedb_type placedb_type; - typedef typename iterator_type::index_type index_type; - inline reference_type operator()(placedb_type& db, index_type index) const - { - return db.nodes().at(db.fixedNodeIndices().at(index)); - } -}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DBIterator iterator_type; -// typedef typename iterator_type::placedb_type placedb_type; -// typedef typename iterator_type::index_type index_type; -// inline reference_type operator()(placedb_type& db, index_type index) const -// { -// return db.nodes().at(db.placeBlockageIndices().at(index)); -// } -//}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DBIterator iterator_type; -// typedef typename iterator_type::placedb_type placedb_type; -// typedef typename iterator_type::index_type index_type; -// inline reference_type operator()(placedb_type& db, index_type index) const -// { -// return db.nodes().at(db.nonCoreNodeIndices().at(index)); -// } -//}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DBIterator iterator_type; -// typedef typename iterator_type::placedb_type placedb_type; -// typedef typename iterator_type::index_type index_type; -// inline reference_type operator()(placedb_type& db, index_type index) const -// { -// return db.nodes().at(index); -// } -//}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DBIterator iterator_type; -// typedef typename iterator_type::placedb_type placedb_type; -// typedef typename iterator_type::index_type index_type; -// inline reference_type operator()(placedb_type& db, index_type index) const -// { -// return db.macros().at(index); -// } -//}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DBIterator iterator_type; -// typedef typename iterator_type::placedb_type placedb_type; -// typedef typename iterator_type::index_type index_type; -// inline reference_type operator()(placedb_type& db, index_type index) const -// { -// return db.macros().at(index); -// } -//}; -//template -//struct IteratorDeref -//{ -// typedef typename constant_helper::reference_type reference_type; -// typedef typename constant_helper::pointer_type pointer_type; -// typedef DB2DIterator iterator_type; -// typedef typename iterator_type::db_type db_type; -// typedef typename iterator_type::index2d_type index2d_type; -// inline reference_type operator()(db_type& db, index2d_type const& index) const -// { -// return db.getSubRow(index[kY], index[kX]); -// } -//}; -// -//template -//struct IteratorDimension -//{ -// typedef DB2DIterator iterator_type; -// typedef typename iterator_type::db_type db_type; -// typedef typename iterator_type::index_type index_type; -// typedef typename iterator_type::index2d_type index2d_type; -// const static Direction1DType direct1 = kY; -// const static Direction1DType direct2 = kX; -// inline index_type operator()(db_type& db, Direction1DType d, index2d_type const& index) const -// { -// return (d == kY)? db.getNumRows() : db.getSubRowsByRowIndex(index[kY]).size(); -// } -//}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h deleted file mode 100644 index 03fa2297..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h +++ /dev/null @@ -1,171 +0,0 @@ -/************************************************************************* - > File Name: LibCell.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_MACRO_H -#define DREAMPLACE_MACRO_H - -#include -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -//class Macro : public Box, public Object -class LibCell : public Object -{ - public: - typedef Object base_type; - typedef coordinate_traits::index_type index_type; - typedef std::map string2index_map_type; - - /// default constructor - LibCell(); - explicit LibCell(const std::string &name) : m_name(name) {} - /// copy constructor - LibCell(LibCell const& rhs); - /// assignment - LibCell& operator=(LibCell const& rhs); - - string2index_map_type const& libCellPinName2Type() const {return m_mPinName2Type;} - string2index_map_type& libCellPinName2Type() {return m_mPinName2Type;} - - int pinType(std::string const& s) const - { - string2index_map_type::const_iterator found = m_mPinName2Type.find(s); - //return (found != m_mPinName2Type.end())? found->second : std::numeric_limits::max(); - return (found != m_mPinName2Type.end())? found->second : -1; - } - - void addInputPin(std::string& s) - { - m_inputPins.push_back(s); - m_mPinName2Type.insert(std::make_pair(s, 1)); - } - void addOutputPin(std::string& s) - { - m_outputPins.push_back(s); - m_mPinName2Type.insert(std::make_pair(s, 0)); - } - void addClkPin(std::string& s) - { - m_clkPins.push_back(s); - m_mPinName2Type.insert(std::make_pair(s, 2)); - } - void addCtrlPin(std::string& s) - { - m_ctrlPins.push_back(s); - m_mPinName2Type.insert(std::make_pair(s, 3)); - } - //When LUT and adder are combined together as single cell - need to differentiate the signals - void addInputAddPin(std::string& s) - { - m_inputPins.push_back(s); - if (s == "cin0" || s.find("cin") != std::string::npos) - { - m_mPinName2Type.insert(std::make_pair(s, 31)); - } else if (s == "sharein0" || s.find("sharein") != std::string::npos) - { - m_mPinName2Type.insert(std::make_pair(s, 41)); - } else - { - m_mPinName2Type.insert(std::make_pair(s, 21)); - } - } - void addOutputAddPin(std::string& s) - { - m_outputPins.push_back(s); - if (s == "cout0" || s.find("cout") != std::string::npos) - { - m_mPinName2Type.insert(std::make_pair(s, 30)); - } else if (s == "shareout0" || s.find("shareout") != std::string::npos) - { - m_mPinName2Type.insert(std::make_pair(s, 40)); - } else - { - m_mPinName2Type.insert(std::make_pair(s, 20)); - } - } - - // Getters - const std::string & name() const { return m_name; } - index_type id() const { return m_id; } - - const std::vector & inputPinArray() const { return m_inputPins; } - std::vector & inputPinArray() { return m_inputPins; } - const std::string& inputPin(index_type i) const { return m_inputPins.at(i); } - index_type numInputPins() const { return m_inputPins.size(); } - - const std::vector & outputPinArray() const { return m_outputPins; } - std::vector & outputPinArray() { return m_outputPins; } - const std::string& outputPin(index_type i) const { return m_outputPins.at(i); } - index_type numOutputPins() const { return m_outputPins.size(); } - - const std::vector & clkPinArray() const { return m_clkPins; } - std::vector & clkPinArray() { return m_clkPins; } - const std::string& clkPin(index_type i) const { return m_clkPins.at(i); } - index_type numClkPins() const { return m_clkPins.size(); } - - const std::vector & ctrlPinArray() const { return m_ctrlPins; } - std::vector & ctrlPinArray() { return m_ctrlPins; } - const std::string& ctrlPin(index_type i) const { return m_ctrlPins.at(i); } - index_type numCtrlPins() const { return m_ctrlPins.size(); } - - // Setters - void setId(index_type id) { m_id = id; } - - - protected: - void copy(LibCell const& rhs); - - std::string m_name; ///< LibCell name - index_type m_id; - - std::vector m_inputPins; ///< standard cell pins - std::vector m_outputPins; ///< standard cell pins - std::vector m_clkPins; ///< standard cell pins - std::vector m_ctrlPins; ///< standard cell pins - string2index_map_type m_mPinName2Type; ///< map names of standard cell pins to type -}; - -inline LibCell::LibCell() - : LibCell::base_type() - , m_name("") - , m_id(std::numeric_limits::max()) - , m_inputPins() - , m_outputPins() - , m_clkPins() - , m_ctrlPins() - , m_mPinName2Type() -{ -} -inline LibCell::LibCell(LibCell const& rhs) - : LibCell::base_type(rhs) -{ - copy(rhs); -} -inline LibCell& LibCell::operator=(LibCell const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void LibCell::copy(LibCell const& rhs) -{ - m_name = rhs.m_name; - m_id = rhs.m_id; - m_inputPins = rhs.m_inputPins; - m_outputPins = rhs.m_outputPins; - m_clkPins = rhs.m_clkPins; - m_ctrlPins = rhs.m_ctrlPins; - m_mPinName2Type = rhs.m_mPinName2Type; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h deleted file mode 100644 index 92c5783b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h +++ /dev/null @@ -1,160 +0,0 @@ -/************************************************************************* - > File Name: Macro.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Mon Jun 15 22:57:05 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_MACRO_H -#define DREAMPLACE_MACRO_H - -#include -#include -#include -#include "MacroPin.h" -#include "MacroObs.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class Macro : public Box, public Object -{ - public: - typedef Object base_type2; - typedef base_type2::coordinate_type coordinate_type; - typedef Box base_type1; - typedef coordinate_traits::index_type index_type; - typedef Point point_type; - typedef std::map string2index_map_type; - - /// default constructor - Macro(); - /// copy constructor - Macro(Macro const& rhs); - /// assignment - Macro& operator=(Macro const& rhs); - - /// member functions - std::string const& name() const {return m_name;} - Macro& setName(std::string const& s) {m_name = s; return *this;} - - std::string const& className() const {return m_className;} - Macro& setClassName(std::string const& s) {m_className = s; return *this;} - - std::string const& siteName() const {return m_siteName;} - Macro& setSiteName(std::string const& s) {m_siteName = s; return *this;} - - std::string const& edgeName(Direction1DType d) const {return m_edgeName[d];} - Macro& setEdgeName(Direction1DType d, std::string const& s) {m_edgeName[d] = s; return *this;} - Macro& setEdgeName(std::string const& sl, std::string const& sr) {m_edgeName[kLEFT] = sl; m_edgeName[kRIGHT] = sr; return *this;} - - unsigned char symmetry() const {return m_symmetry;} - Macro& setSymmetry(unsigned char s) {m_symmetry = s; return *this;} - - point_type const& initOrigin() const {return m_initOrigin;} - Macro& setInitOrigin(point_type const& p) {m_initOrigin = p; return *this;} - Macro& setInitOrigin(coordinate_type x, coordinate_type y) {m_initOrigin.set(x, y); return *this;} - - MacroObs const& obs() const {return m_obs;} - MacroObs& obs() {return m_obs;} - - std::vector const& macroPins() const {return m_vMacroPin;} - std::vector& macroPins() {return m_vMacroPin;} - - string2index_map_type const& macroPinName2Index() const {return m_mMacroPinName2Index;} - string2index_map_type& macroPinName2Index() {return m_mMacroPinName2Index;} - - /// \return macro pin with given index - MacroPin const& macroPin(index_type id) const {return m_vMacroPin.at(id);} - MacroPin& macroPin(index_type id) {return m_vMacroPin.at(id);} - - /// \return macro pin index with given name - index_type macroPinIndex(std::string const& s) const - { - string2index_map_type::const_iterator found = m_mMacroPinName2Index.find(s); - return (found != m_mMacroPinName2Index.end())? found->second : std::numeric_limits::max(); - } - - /// add a macro pin to m_vMacroPin and insert its index to m_mMacroPinName2Index - /// \return true if succeed, false if already exists - /// the first value of return pair is the index in m_vMacroPin - /// \param n denotes name of the pin - std::pair addMacroPin(std::string const& n); - protected: - void copy(Macro const& rhs); - - std::string m_name; ///< macro name - std::string m_className; ///< class name, usually not useful - std::string m_siteName; ///< site name, usually not useful - std::string m_edgeName[2]; ///< edge name (left/right) - unsigned char m_symmetry; ///< 3-bit: x, y, R90 - point_type m_initOrigin; ///< initial origin in LEF file, the actual origins are adjusted to (0, 0) after read-in - - MacroObs m_obs; ///< obstructions - - std::vector m_vMacroPin; ///< standard cell pins - string2index_map_type m_mMacroPinName2Index; ///< map names of standard cell pins to index -}; - -inline Macro::Macro() - : Macro::base_type1() - , Macro::base_type2() - , m_name("") - , m_className("") - , m_siteName("") - , m_edgeName() - , m_symmetry(std::numeric_limits::max()) - , m_initOrigin() - , m_obs() - , m_vMacroPin() - , m_mMacroPinName2Index() -{ -} -inline Macro::Macro(Macro const& rhs) - : Macro::base_type1(rhs) - , Macro::base_type2(rhs) -{ - copy(rhs); -} -inline Macro& Macro::operator=(Macro const& rhs) -{ - if (this != &rhs) - { - this->base_type1::operator=(rhs); - this->base_type2::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void Macro::copy(Macro const& rhs) -{ - m_name = rhs.m_name; - m_className = rhs.m_className; - m_siteName = rhs.m_siteName; - m_edgeName[0] = rhs.m_edgeName[0]; - m_edgeName[1] = rhs.m_edgeName[1]; - m_symmetry = rhs.m_symmetry; - m_initOrigin = rhs.m_initOrigin; - m_obs = rhs.m_obs; - m_vMacroPin = rhs.m_vMacroPin; - m_mMacroPinName2Index = rhs.m_mMacroPinName2Index; -} -inline std::pair Macro::addMacroPin(std::string const& n) -{ - string2index_map_type::iterator found = m_mMacroPinName2Index.find(n); - if (found != m_mMacroPinName2Index.end()) // already exist - return std::make_pair(found->second, false); - else // not exist, create macro pin - { - m_vMacroPin.push_back(MacroPin()); - MacroPin& mp = m_vMacroPin.back(); - mp.setName(n); - mp.setId(m_vMacroPin.size()-1); - std::pair insertRet = m_mMacroPinName2Index.insert(std::make_pair(mp.name(), mp.id())); - dreamplaceAssertMsg(insertRet.second, "failed to insert macro pin (%s, %d).(%s, %d)", name().c_str(), id(), mp.name().c_str(), mp.id()); - return std::make_pair(mp.id(), true); - } -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h deleted file mode 100644 index 0a0a0c3f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h +++ /dev/null @@ -1,102 +0,0 @@ -/************************************************************************* - > File Name: MacroObs.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Wed 22 Jul 2015 11:26:26 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_MACROOBS_H -#define DREAMPLACE_MACROOBS_H - -#include -#include -#include -#include "Box.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class MacroObs : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef Box box_type; - typedef std::map > obs_map_type; - typedef obs_map_type::iterator ObsIterator; - typedef obs_map_type::const_iterator ObsConstIterator; - - /// default constructor - MacroObs(); - /// copy constructor - MacroObs(MacroObs const& rhs); - /// assignment - MacroObs& operator=(MacroObs const& rhs); - - /// member functions - obs_map_type const& obsMap() const {return m_mObs;} - obs_map_type& obsMap() {return m_mObs;} - MacroObs& add(std::string const& layerName, box_type const& box); - template - MacroObs& add(std::string const& layerName, Iterator first, Iterator last); - - bool empty() const {return m_mObs.empty();} - ObsIterator begin() {return m_mObs.begin();} - ObsIterator end() {return m_mObs.end();} - ObsConstIterator begin() const {return m_mObs.begin();} - ObsConstIterator end() const {return m_mObs.end();} - - protected: - void copy(MacroObs const& rhs); - - obs_map_type m_mObs; ///< map of obstructions, layer -> rectangles -}; - -inline MacroObs::MacroObs() - : MacroObs::base_type() - , m_mObs() -{ -} -inline MacroObs::MacroObs(MacroObs const& rhs) - : MacroObs::base_type(rhs) -{ - copy(rhs); -} -inline MacroObs& MacroObs::operator=(MacroObs const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void MacroObs::copy(MacroObs const& rhs) -{ - m_mObs = rhs.m_mObs; -} -inline MacroObs& MacroObs::add(std::string const& layerName, MacroObs::box_type const& box) -{ - obs_map_type::iterator found = m_mObs.find(layerName); - if (found == m_mObs.end()) - m_mObs.insert(std::make_pair(layerName, std::vector(1, box))); - else - found->second.push_back(box); - return *this; -} -template -inline MacroObs& MacroObs::add(std::string const& layerName, Iterator first, Iterator last) -{ - obs_map_type::iterator found = m_mObs.find(layerName); - if (found == m_mObs.end()) - m_mObs.insert(std::make_pair(layerName, std::vector(first, last))); - else - found->second.insert(found->second.end(), first, last); - return *this; -} - -typedef MacroObs::ObsIterator ObsIterator; -typedef MacroObs::ObsConstIterator ObsConstIterator; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h deleted file mode 100644 index df0c57b3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h +++ /dev/null @@ -1,205 +0,0 @@ -/************************************************************************* - > File Name: MacroPin.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Tue Jun 16 21:08:59 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_MACROPIN_H -#define DREAMPLACE_MACROPIN_H - -#include -#include -#include "Object.h" -#include "Box.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// class MacroPort describes the ports in a macro pin -/// it consists of rectangles and polygons -class MacroPort : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef Box box_type; - - /// default constructor - MacroPort(); - /// copy constructor - MacroPort(MacroPort const& rhs); - /// assignment - MacroPort& operator=(MacroPort const& rhs); - - box_type const& bbox() const {return m_bbox;} - MacroPort& setBbox(box_type const& b) {m_bbox = b; return *this;} - - std::vector const& boxes() const {return m_vBox;} - std::vector& boxes() {return m_vBox;} - - std::vector const& layers() const {return m_vLayer;} - std::vector& layers() {return m_vLayer;} - protected: - void copy(MacroPort const& rhs); - - box_type m_bbox; ///< bounding box of port - std::vector m_vBox; ///< decomposed rectangles from the polygon of port - std::vector m_vLayer; ///< layers, the same number as boxes -}; - -inline MacroPort::MacroPort() - : MacroPort::base_type() - , m_bbox() - , m_vBox() - , m_vLayer() -{ -} -inline MacroPort::MacroPort(MacroPort const& rhs) - : MacroPort::base_type(rhs) -{ - copy(rhs); -} -inline MacroPort& MacroPort::operator=(MacroPort const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void MacroPort::copy(MacroPort const& rhs) -{ - m_bbox = rhs.m_bbox; - m_vBox = rhs.m_vBox; - m_vLayer = rhs.m_vLayer; -} - -/// class MacroPin describes the pins of a standard cell -/// it contains detailed physical information such as name, shape, and direction -/// since a pin is usually a rectilinear polygon, the decomposed rectangles are introduced to store its shape -class MacroPin : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef coordinate_traits::index_type index_type; - typedef Box box_type; - - /// default constructor - MacroPin(); - /// copy constructor - MacroPin(MacroPin const& rhs); - /// assignment - MacroPin& operator=(MacroPin const& rhs); - - /// member functions - std::string const& name() const {return m_name;} - MacroPin& setName(std::string const& s) {m_name = s; return *this;} - - SignalDirect const& direct() const {return m_direct;} - MacroPin& setDirect(SignalDirect const& d) {m_direct = d; return *this;} - - box_type const& bbox() const {return m_bbox;} - MacroPin& setBbox(box_type const& b) {m_bbox = b; return *this;} - - std::vector const& macroPorts() const {return m_vMacroPort;} - std::vector& macroPorts() {return m_vMacroPort;} - - MacroPort const& macroPort(index_type id) const {return m_vMacroPort.at(id);} - MacroPort& macroPort(index_type id) {return m_vMacroPort.at(id);} - - /// add macro port and set index - index_type addMacroPort(); - protected: - void copy(MacroPin const& rhs); - - std::string m_name; ///< pin name - SignalDirect m_direct; ///< signal direction of pin - box_type m_bbox; ///< bounding box of pin - std::vector m_vMacroPort; ///< ports in a pin -}; - -inline MacroPin::MacroPin() - : MacroPin::base_type() - , m_name("") - , m_direct() - , m_bbox() - , m_vMacroPort() -{ -} -inline MacroPin::MacroPin(MacroPin const& rhs) - : MacroPin::base_type(rhs) -{ - copy(rhs); -} -inline MacroPin& MacroPin::operator=(MacroPin const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void MacroPin::copy(MacroPin const& rhs) -{ - m_name = rhs.m_name; - m_direct = rhs.m_direct; - m_bbox = rhs.m_bbox; - m_vMacroPort = rhs.m_vMacroPort; -} - -inline MacroPin::index_type MacroPin::addMacroPort() -{ - m_vMacroPort.push_back(MacroPort()); - MacroPort& mp = m_vMacroPort.back(); - mp.setId(m_vMacroPort.size()-1); - - return mp.id(); -} - -///==== helper functions ==== -/// compute bounding box from a port shape -inline void deriveMacroPortBbox(MacroPort& mp) -{ - typedef MacroPort::coordinate_type coordinate_type; - typedef MacroPort::box_type box_type; - // construct an invalid box - box_type box ( - std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::min() - ); - // compute bounding box - std::vector const& vBox = mp.boxes(); - for (std::vector::const_iterator it = vBox.begin(), ite = vBox.end(); it != ite; ++it) - box.encompass(*it); - // update bounding box - mp.setBbox(box); -} -/// compute bounding box from pin shape -inline void deriveMacroPinBbox(MacroPin& mp) -{ - // assume bounding box in macro port is update-to-date - typedef MacroPin::coordinate_type coordinate_type; - typedef MacroPin::box_type box_type; - // construct an invalid box - box_type box ( - std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::min(), - std::numeric_limits::min() - ); - // compute bounding box - std::vector const& vMacroPort = mp.macroPorts(); - for (std::vector::const_iterator it = vMacroPort.begin(), ite = vMacroPort.end(); it != ite; ++it) - box.encompass(it->bbox()); - // update bounding box - mp.setBbox(box); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp deleted file mode 100644 index eddf9023..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************************* - > File Name: Net.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "Net.h" - -DREAMPLACE_BEGIN_NAMESPACE - -Net::Net() - : Net::base_type() - , m_name("") - , m_id(std::numeric_limits::max()) - , m_weight(1) - , m_vPinId() -{ -} -Net::Net(Net const& rhs) - : Net::base_type(rhs) -{ - copy(rhs); -} -Net& Net::operator=(Net const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -void Net::copy(Net const& rhs) -{ - m_name = rhs.m_name; - m_id = rhs.m_id; - m_weight = rhs.m_weight; - m_vPinId = rhs.m_vPinId; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h deleted file mode 100644 index 43e73c66..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************* - > File Name: Net.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_NET_H -#define DREAMPLACE_NET_H - -#include -#include "Pin.h" -#include "Box.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class Net : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef coordinate_traits::index_type index_type; - typedef coordinate_traits::weight_type weight_type; - - /// default constructor - Net(); - explicit Net(const std::string &name) : m_name(name) {} - /// copy constructor - Net(Net const& rhs); - /// assignment - Net& operator=(Net const& rhs); - - - void addPin(index_type pinId) { m_vPinId.push_back(pinId); } - - // Getters - const std::string & name() const { return m_name; } - index_type id() const { return m_id; } - weight_type weight() const { return m_weight; } - - const std::vector & pinIdArray() const { return m_vPinId; } - std::vector & pinIdArray() { return m_vPinId; } - index_type pinId(index_type i) const { return m_vPinId.at(i); } - index_type numPins() const { return m_vPinId.size(); } - - // Setters - void setId(index_type id) { m_id = id; } - void setWeight(weight_type w) { m_weight = w; } - - protected: - void copy(Net const& rhs); - - std::string m_name; - index_type m_id; - weight_type m_weight; ///< weight of net - std::vector m_vPinId; ///< index of pins, the first one is source -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp deleted file mode 100644 index 43ef47c1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - > File Name: Node.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "Node.h" - -DREAMPLACE_BEGIN_NAMESPACE - -Node::Node() - : Node::base_type() - , m_name("") - , m_typeName("") - , m_id(std::numeric_limits::max()) - , m_libCellId(std::numeric_limits::max()) - , m_type(std::numeric_limits::max()) - , m_cType(InstBlkEnum::UNKNOWN) - , m_x(0.0) - , m_y(0.0) - , m_z(0) - , m_fixed(false) - , m_vPinId() -{ -} -Node::Node(Node const& rhs) - : Node::base_type(rhs) -{ - copy(rhs); -} -Node& Node::operator=(Node const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -void Node::copy(Node const& rhs) -{ - m_name = rhs.m_name; - m_typeName = rhs.m_typeName; - m_id = rhs.m_id; - m_libCellId = rhs.m_libCellId; - m_type = rhs.m_type; - m_cType = rhs.m_cType; - m_x = rhs.m_x; - m_y = rhs.m_y; - m_z = rhs.m_z; - m_fixed = rhs.m_fixed; - m_vPinId = rhs.m_vPinId; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h deleted file mode 100644 index 4aa3a6de..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************* - > File Name: Node.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_NODE_H -#define DREAMPLACE_NODE_H - -#include -#include "Pin.h" -#include "Enums.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// class Node denotes an instantiation of a standard cell -class Node : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef coordinate_traits::index_type index_type; - - /// default constructor - Node(); - explicit Node(const std::string &name) : m_name(name) {} - explicit Node(const std::string &name, const std::string &type) : m_name(name), m_typeName(type) {} - /// copy constructor - Node(Node const& rhs); - /// assignment - Node& operator=(Node const& rhs); - - InstBlkEnum::InstBlkType cellType() const {return (InstBlkEnum::InstBlkType)m_cType;} - void setCellType(InstBlkEnum::InstBlkType s) {m_cType = s; m_type = (index_type)s;} - void setCellType(InstBlk const& s) {m_cType = s; m_type = (index_type)s;} - - void addPin(index_type pinId) { m_vPinId.push_back(pinId); } - - // Getters - const std::string & name() const { return m_name; } - index_type id() const { return m_id; } - index_type typeId() const { return m_type; } - index_type macroId() const { return m_libCellId; } - - const std::string & typeName() const { return m_typeName; } - float x() const { return m_x; } - float y() const { return m_y; } - index_type z() const { return m_z; } - bool fixed() const { return (m_type > 8) ? true : false; } - const std::vector & pinIdArray() const { return m_vPinId; } - std::vector & pinIdArray() { return m_vPinId; } - index_type pinId(index_type i) const { return m_vPinId.at(i); } - index_type numPins() const { return m_vPinId.size(); } - - // Setters - void setId(index_type id) { m_id = id; } - void setLibCellId(index_type lId) { m_libCellId = lId; } - void setX(float x) { m_x = x; } - void setY(float y) { m_y = y; } - void setZ(index_type z) { m_z = z; } - void setFixed(bool b) { m_fixed = b; } - - bool isLUT6() const { return (m_type == 5) ? true : false; } - bool isLUT() const { return (m_type < 6) ? true : false; } - bool isFF() const { return (m_type == 6) ? true : false; } - bool isDSP() const { return (m_type == 7) ? true : false; } - bool isRAM() const { return (m_type == 8) ? true : false; } - - - protected: - void copy(Node const& rhs); - - std::string m_name; - std::string m_typeName; - index_type m_id, m_libCellId, m_type; - char m_cType; - float m_x, m_y; - index_type m_z; - bool m_fixed; - std::vector m_vPinId; ///< index of pins - -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h deleted file mode 100644 index 36dcc287..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************************************* - > File Name: Object.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Jun 2015 04:07:46 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_OBJECT_H -#define DREAMPLACE_OBJECT_H - -#include -#include "Util.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// base class for all objects -class Object -{ - public: - typedef int coordinate_type; - typedef coordinate_traits::index_type index_type; - - /// default constructor - Object() : m_id(std::numeric_limits::max()) {} - /// copy constructor - Object(Object const& rhs) : m_id(rhs.m_id) {} - /// assignment - Object& operator=(Object const& rhs) - { - m_id = rhs.m_id; - return *this; - } - /// destructor - ~Object() {} - - index_type id() const {return m_id;} - void setId(index_type i) {m_id = i;} - - std::string toString() const {return limbo::to_string(m_id);} - - protected: - index_type m_id; ///< index of object -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp deleted file mode 100644 index c6710fe0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/************************************************************************* - > File Name: Params.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "Params.h" -#include "Util.h" -#include -#include -#include -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -std::string toString(SolutionFileFormat ff) -{ - switch (ff) - { - case BOOKSHELF: return "BOOKSHELF"; - case BOOKSHELFALL: return "BOOKSHELFALL"; - default: return "UNKNOWN"; - } -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h deleted file mode 100644 index a5093aeb..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************************************* - > File Name: Params.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_PARAMS_H -#define DREAMPLACE_PARAMS_H - -#include -#include -#include -#include - -#include "Enums.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// placement solution format -enum SolutionFileFormat -{ - BOOKSHELF, // write placement solution .plx in bookshlef format - BOOKSHELFALL // write .nodes, .nets, ... in bookshlef format -}; - -/// convert enums to string -extern std::string toString(SolutionFileFormat ff); - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h deleted file mode 100644 index 30570788..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************* - > File Name: Pin.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_PIN_H -#define DREAMPLACE_PIN_H - -#include -#include "Object.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class Pin : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef coordinate_traits::index_type index_type; - - /// default constructor - Pin(); - explicit Pin(const std::string &name) : m_name(name) {} - /// copy constructor - Pin(Pin const& rhs); - /// assignment - Pin& operator=(Pin const& rhs); - - // Getters - const std::string & name() const { return m_name; } - index_type id() const { return m_id; } - index_type nodeId() const { return m_nodeId; } - index_type netId() const { return m_netId; } - - // Setters - void setId(index_type id) { m_id = id; } - void setNodeId(index_type nodeId) { m_nodeId = nodeId; } - void setNetId(index_type netId) { m_netId = netId; } - - protected: - void copy(Pin const& rhs); - - std::string m_name; ///< index to the macro pin list of corresponding macro - index_type m_id; ///< index to the macro pin list of corresponding macro - index_type m_nodeId; ///< corresponding node - index_type m_netId; ///< corresponding net -}; - -inline Pin::Pin() - : Pin::base_type() - , m_name("") - , m_id(std::numeric_limits::max()) - , m_nodeId(std::numeric_limits::max()) - , m_netId(std::numeric_limits::max()) -{ -} -inline Pin::Pin(Pin const& rhs) - : Pin::base_type(rhs) -{ - copy(rhs); -} -inline Pin& Pin::operator=(Pin const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void Pin::copy(Pin const& rhs) -{ - m_name = rhs.m_name; - m_id = rhs.m_id; - m_nodeId = rhs.m_nodeId; - m_netId = rhs.m_netId; -} - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp deleted file mode 100644 index 79766dc8..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp +++ /dev/null @@ -1,1235 +0,0 @@ -/************************************************************************* - > File Name: PlaceDB.cpp - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#include "PlaceDB.h" -#include -#include -#include -#include -#include "BookshelfWriter.h" -#include "Iterators.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -int get_last_digit_from_string(const std::string &val) -{ - //Conversion of char to int - return (int)val.back()-48; -} - -/// default constructor -PlaceDB::PlaceDB() { - num_movable_nodes = 0; - num_fixed_nodes = 0; - m_numLibCell = 0; - m_numLUT = 0; - m_numFF = 0; - m_numCCs = 0; - wlXWeight = 0.0; - wlYWeight = 0.0; - pinRouteCap = 0; - routeCapH = 0; - routeCapV = 0; -} - -void PlaceDB::add_bookshelf_node(std::string& name, std::string& type) -{ - ////DBG - //std::cout << " add bookshelf node " << name << " of type " << type << std::endl; - ////DBG - index_type rsrcType(rsrcTypes.size()+1); - std::string rsrcName; - - string2string_map_type::iterator fnd = inst2RsrcMap.find(type); - if (fnd == inst2RsrcMap.end()) - { - dreamplacePrint(kWARN, "Unknown instance type not found in .scl file: %s, %s\n", - type.c_str()); - } else - { - rsrcName = inst2RsrcMap[type]; - rsrcType = rsrcType2IndexMap[rsrcName]; - } - - if (rsrcType < rsrcTypes.size()) - { - if (node_names.size() == 0 && fixed_node_names.size() == 0) - { - nodeCount.resize(rsrcTypes.size()); - } - ++nodeCount[rsrcType]; - - if (rsrcType == ioId || rsrcType == pllId) - { - ////DBG - //std::cout << "Added fixed bookshelf node " << name << " of type " << type - // << " with fixed nodeId: " << fixed_node_names.size() << std::endl; - ////DBG - fixed_node_name2id_map.insert(std::make_pair(name, fixed_node_names.size())); - fixed_node_names.emplace_back(name); - fixed_node_types.emplace_back(type); - fixed_node2fence_region_map.emplace_back(rsrcType); - fixed_node_x.emplace_back(0.0); - fixed_node_y.emplace_back(0.0); - fixed_node_z.emplace_back(0); - ++num_fixed_nodes; - - double width(0.0), height(0.0); - - string2index_map_type::iterator found = rsrcInstType2IndexMap.find(type); - if (found != rsrcInstType2IndexMap.end()) - { - width = rsrcInstWidth[rsrcInstType2IndexMap[type]]; - height = rsrcInstHeight[rsrcInstType2IndexMap[type]]; - } else - { - width = rsrcInstWidth[rsrcType2IndexMap[rsrcName]]; - height = rsrcInstHeight[rsrcType2IndexMap[rsrcName]]; - } - - fixed_node_size_x.emplace_back(width); - fixed_node_size_y.emplace_back(height); - fixed_lut_type.emplace_back(0); - fixed_cluster_lut_type.emplace_back(0); - - } else - { - ////DBG - //std::cout << "Added movable bookshelf node " << name << " of type " << type << " with nodeId: " << node_names.size() << std::endl; - ////DBG - node_name2id_map.insert(std::make_pair(name, node_names.size())); - node_names.emplace_back(name); - node_types.emplace_back(type); - node2fence_region_map.emplace_back(rsrcType); - node_x.emplace_back(0.0); - node_y.emplace_back(0.0); - node_z.emplace_back(0); - ++num_movable_nodes; - - double width(0.0), height(0.0); - - string2index_map_type::iterator found = rsrcInstType2IndexMap.find(type); - if (found != rsrcInstType2IndexMap.end()) - { - width = rsrcInstWidth[rsrcInstType2IndexMap[type]]; - height = rsrcInstHeight[rsrcInstType2IndexMap[type]]; - } else //FF, CARRY8 - { - width = rsrcInstWidth[rsrcType2IndexMap[rsrcName]]; - height = rsrcInstHeight[rsrcType2IndexMap[rsrcName]]; - } - - if (rsrcType == lutId) - { - node_size_x.emplace_back(std::sqrt(width)); - node_size_y.emplace_back(std::sqrt(height)); - int val = get_last_digit_from_string(type); - lut_type.emplace_back(val); - //std::cout << "Instance: " << name << " of type: " << type << " has lut type: " << val; - --val; - val = std::max(0, val); - cluster_lut_type.emplace_back(val); - lut_indices.emplace_back(node_names.size()-1); - //std::cout << " and cluster lut type: " << val << std::endl; - ++m_numLUT; - } - else if (rsrcType == ffId) - { - node_size_x.emplace_back(std::sqrt(width)); - node_size_y.emplace_back(std::sqrt(height)); - lut_type.emplace_back(0); - cluster_lut_type.emplace_back(0); - flop_indices.emplace_back(node_names.size()-1); - ++m_numFF; - } else - { - node_size_x.emplace_back(width); - node_size_y.emplace_back(height); - lut_type.emplace_back(0); - cluster_lut_type.emplace_back(0); - } - } - - } else - { - dreamplacePrint(kWARN, "Unknown type component found in .nodes file: %s, %s of type: %s with id: %d\n", - name.c_str(), type.c_str(), rsrcName.c_str(), rsrcType); - } - - std::vector temp; - node2pin_map.emplace_back(temp); - node2pincount_map.emplace_back(0); - is_cc_node.emplace_back(0); - node2ccId_map.emplace_back(-1); - org_node2ccIndex_map.emplace_back(-1); - ////DBG - //std::cout << "Added bookshelf node " << name << " of type " << type << std::endl; - ////DBG -} - -void PlaceDB::update_nodes() { - index_type num_nodes = 0; - //Re-arrange all nodes to accomodate carry chains - if (m_numCCs > 0) - { - ////DBG - //std::cout << " Update nodes before reading design net information" << std::endl; - ////DBG - - num_nodes = node_names.size()+fixed_node_names.size(); - - //Carry Chains are movable nodes - org_num_movable_nodes = num_movable_nodes; - org_node_name2id_map = node_name2id_map; - org_node_size_x = node_size_x; - org_node_size_y = node_size_y; - org_node2fence_region_map = node2fence_region_map; - org_node_names = node_names; - org_node_types = node_types; - org_nodeCount = nodeCount; - org_is_cc_node = is_cc_node; - org_node2ccId_map = node2ccId_map; - org_flop_indices = flop_indices; - org_lut_type = lut_type; - org_node2pincount_map = node2pincount_map; - org_node2pin_map = node2pin_map; - - org_node_x = node_x; - org_node_y = node_y; - org_node_z = node_z; - index_type numFF = m_numFF; - index_type numLUT = m_numLUT; - std::vector temp_cluster_lut_type = cluster_lut_type; - - node_name2id_map.clear(); - lut_type.clear(); - cluster_lut_type.clear(); - node_size_x.clear(); - node_size_y.clear(); - node_names.clear(); - node_types.clear(); - node_x.clear(); - node_y.clear(); - node_z.clear(); - node2fence_region_map.clear(); - is_cc_node.clear(); - node2ccId_map.clear(); - flop_indices.clear(); - nodeCount.clear(); - nodeCount.resize(org_nodeCount.size(),0); - new2org_node_map.resize(num_nodes, -1); - cc2nodeId_map.clear(); - cc2nodeId_map.resize(m_numCCs, 0); - - if (ioId != 100000) - { - nodeCount[ioId] = org_nodeCount[ioId]; - } - if (pllId != 100000) - { - nodeCount[pllId] = org_nodeCount[pllId]; - } - - m_numFF = 0; - m_numLUT = 0; - num_movable_nodes = 0; - - for (unsigned int nId = 0; nId < org_num_movable_nodes; ++nId) - { - if (org_is_cc_node[nId] == 0) //Not a carry node - { - std::string node_name = org_node_names[nId]; - index_type rId = org_node2fence_region_map[nId]; - - node_name2id_map[node_name] = node_names.size(); - node_size_x.emplace_back(org_node_size_x[nId]); - node_size_y.emplace_back(org_node_size_y[nId]); - node_types.emplace_back(org_node_types[nId]); - node2fence_region_map.emplace_back(rId); - - node_x.emplace_back(org_node_x[nId]); - node_y.emplace_back(org_node_y[nId]); - node_z.emplace_back(org_node_z[nId]); - lut_type.emplace_back(org_lut_type[nId]); - cluster_lut_type.emplace_back(temp_cluster_lut_type[nId]); - - new2org_node_map[node_names.size()] = nId; - if (rId == ffId) - { - ++m_numFF; - flop_indices.emplace_back(node_names.size()); - } - else if (rId == lutId) - { - ++m_numLUT; - } - - node_names.emplace_back(node_name); - ++nodeCount[rId]; - node2ccId_map.emplace_back(-1); - is_cc_node.emplace_back(0); - - std::vector temp; - } else - { - index_type ccId = org_node2ccId_map[nId]; - index_type cc_headId = org_flat_cc2node_map[org_flat_cc2node_start_map[ccId]]; - std::string cc_name = org_node_names[cc_headId]; - index_type rId = org_node2fence_region_map[cc_headId]; - - string2index_map_type::iterator found = node_name2id_map.find(cc_name); - - if (found != node_name2id_map.end()) - { - //Node part of existing carry chain - std::string node_name = org_node_names[nId]; - index_type cc_nodeId = node_name2id_map[cc_name]; - node_name2id_map[node_name] = cc_nodeId; - - //Assign larger type to root - if (org_lut_type[nId] > lut_type[cc_nodeId]) - { - lut_type[cc_nodeId] = org_lut_type[nId]; - cluster_lut_type[cc_nodeId] = temp_cluster_lut_type[nId]; - node_types[cc_nodeId] = org_node_types[nId]; - } - ////DBG - //std::cout << nId << " Node " << node_name << " assigned to " << node_name2id_map[cc_name] << std::endl; - ////DBG - } else - { - node_name2id_map[cc_name] = node_names.size(); //Assign nodeId for root node - node_name2id_map[org_node_names[nId]] = node_name2id_map[cc_name]; //Assign current node if not root to same nodeId - lut_type.emplace_back(org_lut_type[cc_headId]); - cluster_lut_type.emplace_back(temp_cluster_lut_type[cc_headId]); - node_types.emplace_back(org_node_types[cc_headId]); - node_x.emplace_back(org_node_x[cc_headId]); - node_y.emplace_back(org_node_y[cc_headId]); - node_z.emplace_back(org_node_z[cc_headId]); - node2fence_region_map.emplace_back(rId); - - node_size_x.emplace_back(std::sqrt(org_node_size_x[cc_headId]*org_node_size_x[cc_headId]*cc_element_count[ccId])); - node_size_y.emplace_back(std::sqrt(org_node_size_y[cc_headId]*org_node_size_y[cc_headId]*cc_element_count[ccId])); - - ////DBG - //std::cout << nId << " Carry chain " << cc_name << " " << node_name2id_map[cc_name] - // << " with " << cc_element_count[ccId] << " elements is of size " - // << node_size_x.back() << " x " - // << node_size_y.back() << std::endl; - ////DBG - - new2org_node_map[node_names.size()] = cc_headId; - if (rId == ffId) - { - ++m_numFF; - flop_indices.emplace_back(node_names.size()); - } - else if (rId == lutId) - { - ++m_numLUT; - } - - cc2nodeId_map[ccId] = node_names.size(); - node2ccId_map.emplace_back(ccId); //Same as org - is_cc_node.emplace_back(1); - node_names.emplace_back(cc_name); - ++nodeCount[rId]; - } - } - } - - index_type org_num_nodes = org_num_movable_nodes + fixed_node_names.size(); - org_is_cc_node.resize(org_num_nodes,0); - org_node2pincount_map.resize(org_num_nodes, 0); - org_node2outpinCount.resize(org_num_nodes, 0); - org_node2outpinIdx_map.resize(4*org_num_nodes, -1); - org_node2pin_map.resize(org_num_nodes); - } - - num_movable_nodes = node_names.size(); - num_nodes = num_movable_nodes + fixed_node_names.size(); - is_cc_node.resize(num_nodes,0); - node2pincount_map.resize(num_nodes, 0); - node2outpinCount.resize(num_nodes, 0); - node2outpinIdx_map.resize(4*num_nodes, -1); - node2pin_map.resize(num_nodes); - - ////DBG - //std::cout << "Total nodes: " << num_nodes - // << " = " << num_movable_nodes << " + " - // << fixed_node_names.size() << std::endl; - ////DBG - -} - -void PlaceDB::add_bookshelf_net(BookshelfParser::Net const& n) { - - ////DBG - //std::cout << " Add bookshelf net: " << n.net_name << " with " << n.vNetPin.size() << std::endl; - ////DBG - - // check the validity of nets - // if a node has multiple pins in the net, only one is kept - std::vector vNetPin = n.vNetPin; - - index_type netId(net_names.size()); - net2pincount_map.emplace_back(vNetPin.size()); - net_name2id_map.insert(std::make_pair(n.net_name, netId)); - net_names.emplace_back(n.net_name); - - std::vector netPins; - if (flat_net2pin_start_map.size() == 0) - { - flat_net2pin_start_map.emplace_back(0); - } - - for (unsigned i = 0; i < vNetPin.size(); ++i) - { - BookshelfParser::NetPin const& netPin = vNetPin[i]; - index_type nodeId, org_nodeId, pinId(pin_names.size()); - - ////DBG - //std::cout << "Consider net pin " << netPin.pin_name << std::endl; - ////DBG - - pin_names.emplace_back(netPin.pin_name); - pin2net_map.emplace_back(netId); - - string2index_map_type::iterator found = node_name2id_map.find(netPin.node_name); - std::string nodeType, org_nodeType; - - ////DBG - //std::cout << "Consider net pin connected to node " << netPin.node_name << std::endl; - ////DBG - if (found != node_name2id_map.end()) - { - nodeId = node_name2id_map.at(netPin.node_name); - ////DBG - //std::cout << "Here for net pin " << netPin.pin_name << " part of nodeId: " << nodeId << " " << netPin.node_name << std::endl; - ////DBG - - pin2nodeType_map.emplace_back(node2fence_region_map[nodeId]); - ////DBG - //std::cout << "Here for net pin " << netPin.pin_name << " with pin node type: " << node2fence_region_map[nodeId] << std::endl; - ////DBG - - nodeType = node_types[nodeId]; - ////DBG - //std::cout << "Here for net pin " << netPin.pin_name << " with node type: " << nodeType << std::endl; - ////DBG - - - if (is_cc_node[nodeId] == 0) - { - pin_offset_x.emplace_back(0.5*node_size_x[nodeId]); - ////DBG - //std::cout << "Here for net pin " << netPin.pin_name << " with pin_offset_x : " << 0.5*node_size_x[nodeId] << std::endl; - ////DBG - pin_offset_y.emplace_back(0.5*node_size_y[nodeId]); - ////DBG - //std::cout << "Here for net pin " << netPin.pin_name << " with pin_offset_y : " << 0.5*node_size_y[nodeId] << std::endl; - ////DBG - } else - { //carry chain node - pin_offset_x.emplace_back(0.5*node_size_x[nodeId]); - org_nodeId = org_node_name2id_map.at(netPin.node_name); - index_type ccId = org_node2ccId_map[org_nodeId]; - double element_height = node_size_y[nodeId]/cc_element_count[ccId]; - pin_offset_y.emplace_back((org_node2ccIndex_map[org_nodeId]+0.5)*element_height); - - ////DBG - //std::cout << "Net pin " << netPin.pin_name << " of node: " - // << netPin.node_name << " part of " << cc_element_count[ccId] - // << " node carry chain has node2ccIndex: " - // << org_node2ccIndex_map[org_nodeId] << " has pin_offset: " - // << pin_offset_x.back() << " x " - // << pin_offset_y.back() << std::endl; - ////DBG - - //index_type inv_ccIndex = cc_element_count[node2ccId_map[nodeId]] -1 - org_node2ccIndex_map[org_nodeId]; - //LibCell const& lCell = m_vLibCell.at(m_LibCellName2Index.at(nodeType)); - //int pinTypeId(lCell.pinType(netPin.pin_name)); - - //if (pinTypeId == 1) //Input - //{ - // temp_input_pin_info[ccId].emplace_back(std::make_pair(pinId, inv_ccIndex)); - //} else if (pinTypeId == 0 || pinTypeId == 20) //Output pin - //{ - // temp_output_pin_info[ccId].emplace_back(std::make_pair(pinId, inv_ccIndex)); - //} - - ////DBG - //if (ccId == 0) - //{ - // std::cout << "CC0 Consider pin " << netPin.pin_name << " of type: " << pinTypeId - // << " with offset: " << inv_ccIndex << " (" - // << org_node2ccIndex_map[org_nodeId] << ")" << std::endl; - //} - ////DBG - } - //Required to udpate original datastructures - if (m_numCCs > 0) - { - org_nodeId = org_node_name2id_map.at(netPin.node_name); - org_pin2nodeType_map.emplace_back(org_node2fence_region_map[org_nodeId]); - org_nodeType = org_node_types[org_nodeId]; - org_pin_offset_x.emplace_back(0.5*org_node_size_x[org_nodeId]); - org_pin_offset_y.emplace_back(0.5*org_node_size_y[org_nodeId]); - } - } else - { - ////DBG - //std::cout << " Node is fixed type " << nodeType << " in lib" << std::endl; - ////DBG - string2index_map_type::iterator fnd = fixed_node_name2id_map.find(netPin.node_name); - if (fnd != fixed_node_name2id_map.end()) - { - nodeId = fixed_node_name2id_map.at(netPin.node_name); - pin2nodeType_map.emplace_back(fixed_node2fence_region_map[nodeId]); - pin_offset_x.emplace_back(0.5*fixed_node_size_x[nodeId]); - pin_offset_y.emplace_back(0.5*fixed_node_size_y[nodeId]); - nodeType = fixed_node_types[nodeId]; - nodeId += num_movable_nodes; - - if (m_numCCs > 0) - { - org_nodeId = fixed_node_name2id_map.at(netPin.node_name); - org_pin2nodeType_map.emplace_back(fixed_node2fence_region_map[org_nodeId]); - org_pin_offset_x.emplace_back(0.5*fixed_node_size_x[org_nodeId]); - org_pin_offset_y.emplace_back(0.5*fixed_node_size_y[org_nodeId]); - org_nodeType = fixed_node_types[org_nodeId]; - org_nodeId += org_num_movable_nodes; - } - - } else - { - dreamplacePrint(kERROR, "Net %s connects to instance %s pin %s. However instance %s is not specified in .nodes file. FIX\n", - n.net_name.c_str(), netPin.node_name.c_str(), netPin.pin_name.c_str(), netPin.node_name.c_str()); - } - } - - std::string pType(""); - - ////DBG - //std::cout << " Check for nodeType " << nodeType << " in lib" << std::endl; - ////DBG - - LibCell const& lCell = m_vLibCell.at(m_LibCellName2Index.at(nodeType)); - int pinTypeId(lCell.pinType(netPin.pin_name)); - - if (pinTypeId == -1) - { - dreamplacePrint(kWARN, "Net %s connects to instance %s pin %s. However pin %s is not listed in .lib as a valid pin for instance type %s. FIX\n", - n.net_name.c_str(), netPin.node_name.c_str(), netPin.pin_name.c_str(), netPin.pin_name.c_str(), nodeType.c_str()); - } - - switch(pinTypeId) - { - case 2: //CLK - { - pType = "CK"; - break; - } - case 3: //CTRL - { - if (netPin.pin_name.find("CE") != std::string::npos || - netPin.pin_name.find("devclrn") != std::string::npos) - //if (netPin.pin_name == "CE" || netPin.pin_name == "devclrn" || - //netPin.pin_name == "devclrn0") - { - pType = "CE"; - } else if (netPin.pin_name == "R" || netPin.pin_name == "S" || - netPin.pin_name.find("sclr") != std::string::npos) - //netPin.pin_name == "sclr" || netPin.pin_name == "sclr0") - { - pType = "SR"; - pinTypeId = 4; - // else if (netPin.pin_name.find("prn") != std::string::npos ) - } else if (netPin.pin_name.find("prn") != std::string::npos) - // else if (netPin.pin_name == "prn" || - // netPin.pin_name == "prn0") - { - pType = "PR"; - pinTypeId = 5; - } else if (netPin.pin_name.find("aload") != std::string::npos) - // else if (netPin.pin_name == "aload" || - // netPin.pin_name == "aload0") - { - pType = "AL"; - pinTypeId = 6; - } else if (netPin.pin_name.find("sload") != std::string::npos) - // else if (netPin.pin_name == "sload" || - // netPin.pin_name == "sload0") - { - pType = "SL"; - pinTypeId = 7; - } else if (netPin.pin_name.find("devpor") != std::string::npos) - // else if (netPin.pin_name == "devpor" || - // netPin.pin_name == "devpor0") - { - pType = "DP"; - pinTypeId = 8; - } else if (netPin.pin_name.find("clrn") != std::string::npos) - // else if (netPin.pin_name == "clrn" || - // netPin.pin_name == "clrn0") - { - pType = "DC"; - pinTypeId = 9; - } - break; - } - default: - { - break; - } - } - - ////DBG - //std::cout << " pType " << pType << " and pinTypeId " << pinTypeId << std::endl; - ////DBG - - pin_types.emplace_back(pType); - pin_typeIds.emplace_back(pinTypeId); - ++node2pincount_map[nodeId]; - pin2node_map.emplace_back(nodeId); - node2pin_map[nodeId].emplace_back(pinId); - - if (m_numCCs > 0) - { - ++org_node2pincount_map[org_nodeId]; - org_pin2node_map.emplace_back(org_nodeId); - org_node2pin_map[org_nodeId].emplace_back(pinId); - } - - - //node2outpin info is mainly required for LUT/FF LG - // cout (30) and shareout (40) are not considered - if (pinTypeId == 0 || pinTypeId == 20) //Output pin - { - if (node2outpinCount[nodeId] < 4) - { - int n2oIdx = nodeId*4 + node2outpinCount[nodeId]; - node2outpinIdx_map[n2oIdx] = pinId; - ++node2outpinCount[nodeId]; - } - - if (m_numCCs > 0 && org_node2outpinCount[org_nodeId] < 4) - { - int n2oIdx = org_nodeId*4 + org_node2outpinCount[org_nodeId]; - org_node2outpinIdx_map[n2oIdx] = pinId; - ++org_node2outpinCount[org_nodeId]; - } - } - - netPins.emplace_back(pinId); - flat_net2pin_map.emplace_back(pinId); - } - flat_net2pin_start_map.emplace_back(flat_net2pin_map.size()); - net2pin_map.emplace_back(netPins); - - ////DBG - //std::cout << net_names.size() << " added net: " << n.net_name << " with " << n.vNetPin.size() << std::endl; - ////DBG -} - -void PlaceDB::add_bookshelf_carry(BookshelfParser::CarryChain const& carry_chain) -{ - ////DBG - //std::cout << "add carry chain: " << carry_chain.name << " containing " << carry_chain.elCount << " nodes " << std::endl; - ////DBG - - if (cc_element_count.size() == 0) - { - org_flat_cc2node_start_map.emplace_back(0); - } - - for (unsigned i = 0; i < carry_chain.elements.size(); ++i) - { - index_type nodeId = node_names.size()+1; - - string2index_map_type::iterator found = node_name2id_map.find(carry_chain.elements[i]); - if (found == node_name2id_map.end()) - { - std::cout << "Carry Chain node " << carry_chain.elements[i] << " is not part of design.nodes - CHECK" << std::endl; - continue; - } - - nodeId = node_name2id_map[carry_chain.elements[i]]; - org_node2ccIndex_map[nodeId] = carry_chain.elements.size()-i-1; - org_flat_cc2node_map.emplace_back(nodeId); - node2ccId_map[nodeId] = cc_element_count.size(); - is_cc_node[nodeId] = 1; - - ////DBG - // std::cout << "Carry chain element " << i << " " << carry_chain.elements[i] << " has nodeId: " << nodeId - // << ", ccId: " << node2ccId_map[nodeId] << " and cc index: " << org_node2ccIndex_map[nodeId] << std::endl; - ////DBG - } - org_flat_cc2node_start_map.emplace_back(org_flat_cc2node_map.size()); - cc_element_count.emplace_back(carry_chain.elCount); - ++m_numCCs; - - ////Initialize 2D vector - //std::vector > temp; - //temp_input_pin_info.emplace_back(temp); - //temp_output_pin_info.emplace_back(temp); - ////DBG - //std::cout << "Added carry chain " << m_numCCs << " with " << cc_element_count.back() << " nodes" << std::endl; - ////DBG -} - -void PlaceDB::resize_sites(int xSize, int ySize) -{ - m_dieArea.set(0, 0, xSize, ySize); - m_siteDB.resize(xSize, std::vector(ySize, 0)); - initSiteMapValUpd = true; -} - -void PlaceDB::site_info_update(int x, int y, std::string const& name) -{ - if (initSiteMapValUpd) - { - int xh = m_dieArea.xh(); - int yh = m_dieArea.yh(); - m_dieArea.set(x, y, xh, yh); - initSiteMapValUpd = false; - } - int siteId = siteType2IndexMap[name]; - m_siteDB[x][y] = siteId; -} - -void PlaceDB::resize_clk_regions(int xReg, int yReg) -{ - m_clkRegX = xReg; - m_clkRegY = yReg; -} - -void PlaceDB::add_clk_region(std::string const& name, int xl, int yl, int xh, int yh, int xm, int ym) -{ - clk_region temp; - temp.xl = xl; - temp.yl = yl; - temp.xh = xh; - temp.yh = yh; - temp.xm = xm; - temp.ym = ym; - m_clkRegionDB.emplace_back(temp); - m_clkRegions.emplace_back(name); -} - -void PlaceDB::add_lib_cell(std::string const& name) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(name); - if (found == m_LibCellName2Index.end()) // Ignore if already exists - { - m_vLibCell.push_back(LibCell(name)); - LibCell& lCell = m_vLibCell.back(); - //lCell.setName(name); - lCell.setId(m_vLibCell.size() - 1); - std::pair insertRet = - m_LibCellName2Index.insert(std::make_pair(lCell.name(), lCell.id())); - dreamplaceAssertMsg(insertRet.second, "failed to insert libCell (%s, %d)", - lCell.name().c_str(), lCell.id()); - - m_numLibCell = m_vLibCell.size(); // update number of libCells - } - m_libCellTemp = name; -} - -void PlaceDB::add_input_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addInputPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::add_input_add_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addInputAddPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::add_output_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addOutputPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::add_output_add_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addOutputAddPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::add_clk_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addClkPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::add_ctrl_pin(std::string& pName) -{ - string2index_map_type::iterator found = m_LibCellName2Index.find(m_libCellTemp); - - if (found != m_LibCellName2Index.end()) - { - LibCell& lCell = m_vLibCell.at(m_LibCellName2Index.at(m_libCellTemp)); - lCell.addCtrlPin(pName); - } else - { - dreamplacePrint(kWARN, "libCell not found in .lib file: %s\n", - m_libCellTemp.c_str()); - } -} - -void PlaceDB::set_bookshelf_node_pos(std::string const& name, double x, double y, int z) -{ - string2index_map_type::iterator found = fixed_node_name2id_map.find(name); - //bool fixed(true); - - if (found != fixed_node_name2id_map.end()) - { - fixed_node_x.at(fixed_node_name2id_map.at(name)) = x; - fixed_node_y.at(fixed_node_name2id_map.at(name)) = y; - fixed_node_z.at(fixed_node_name2id_map.at(name)) = z; - } else - { - //string2index_map_type::iterator fnd = mov_node_name2id_map.find(name); - node_x.at(node_name2id_map.at(name)) = x; - node_y.at(node_name2id_map.at(name)) = y; - node_z.at(node_name2id_map.at(name)) = z; - } - -} - -void PlaceDB::add_site(BookshelfParser::Site const& st) -{ - siteType2IndexMap.insert(std::make_pair(st.name, siteTypes.size()+1)); - siteTypes.emplace_back(st.name); - - std::vector temp; - for (unsigned i = 0; i < st.rsrcs.size(); ++i) - { - std::string rsrc = st.rsrcs[i].first; - temp.emplace_back(rsrc); - siteRsrcCountMap.insert(std::make_pair(rsrc, st.rsrcs[i].second)); - rsrc2SiteMap.insert(std::make_pair(rsrc, st.name)); - } - siteResources.emplace_back(temp); - temp.clear(); -} - -void PlaceDB::add_rsrc(BookshelfParser::Rsrc const& rsrc) -{ - if (rsrc.rsrcCells.size() > 0) - { - //std::cout << "add resource: " << rsrc.name << " with " << rsrc.rsrcCells.size() << " cells " << std::endl; - rsrcType2IndexMap.insert(std::make_pair(rsrc.name, rsrcTypes.size())); - rsrcTypes.emplace_back(rsrc.name); - for (unsigned i = 0; i < rsrc.rsrcCells.size(); ++i) - { - inst2RsrcMap.insert(std::make_pair(rsrc.rsrcCells[i], rsrc.name)); - rsrcInstType2IndexMap.insert(std::make_pair(rsrc.rsrcCells[i], rsrcInstTypes.size())); - //std::cout << "rsrc Inst: " << rsrc.rsrcCells[i] << " assigned to id: " << rsrcInstType2IndexMap[rsrc.rsrcCells[i]] << std::endl; - rsrcInstTypes.emplace_back(rsrc.rsrcCells[i]); - } - rsrcInsts.emplace_back(rsrc.rsrcCells); - - if (rsrc.name.find("FF") != std::string::npos || - rsrc.name.find("dffeas") != std::string::npos) - { - ffId = rsrcTypes.size()-1; - } - else if (rsrc.name.find("LUT") != std::string::npos || - rsrc.name.find("lcell_comb") != std::string::npos) - { - lutId = rsrcTypes.size()-1; - } - else if (rsrc.name.find("IO") != std::string::npos || - rsrc.name.find("io") != std::string::npos) - { - ioId = rsrcTypes.size()-1; - } - else if (rsrc.name.find("PLL") != std::string::npos || - rsrc.name.find("pll") != std::string::npos) - { - pllId = rsrcTypes.size()-1; - } - } -} - -void PlaceDB::set_site_per_column(int val) -{ - sitePerColumn = val; -} - -void PlaceDB::set_site_dimensions(std::string const& sName, double w, double h) -{ - if (siteWidth.size() == 0 || siteHeight.size() == 0 || rsrcInstWidth.size() == 0 || rsrcInstHeight.size() == 0) - { - siteWidth.resize(siteTypes.size()+1, 1.0); - siteHeight.resize(siteTypes.size()+1, 1.0); - rsrcInstWidth.resize(rsrcInstTypes.size(), 1.0); - rsrcInstHeight.resize(rsrcInstTypes.size(), 1.0); - } - - unsigned siteId = siteType2IndexMap[sName]; - siteWidth[siteId] = w; - siteHeight[siteId] = h; - - unsigned rsrcId = rsrcType2IndexMap[siteResources[siteId-1][0]]; - for (unsigned srId = 0; srId < rsrcInsts[rsrcId].size(); ++srId) - { - std::string rsrcInst = rsrcInsts[rsrcId][srId]; - rsrcInstWidth[rsrcInstType2IndexMap[rsrcInst]] = w; - rsrcInstHeight[rsrcInstType2IndexMap[rsrcInst]] = h; - } -} - -void PlaceDB::set_slice_element(std::string const& sName, int cnt) -{ - sliceElements.emplace_back(std::make_pair(sName, cnt)); -} - -void PlaceDB::set_cell_dimensions(std::string const& cName, double w, double h) -{ - rsrcInstWidth[rsrcInstType2IndexMap[cName]] = w; - rsrcInstHeight[rsrcInstType2IndexMap[cName]] = h; -} - -void PlaceDB::set_lut_max_shared(int cnt) -{ - lutMaxShared = cnt; -} - -void PlaceDB::set_lut_type_in_sliceUnit(int cnt) -{ - lutTypeInSliceUnit = cnt; -} - -void PlaceDB::set_lut_fractureability(BookshelfParser::LUTFract const& lutFract) -{ - if (lutFractures.size() == 0) - { - lutFractures.resize(rsrcInsts[lutId].size()); - } - - int fId = get_last_digit_from_string(lutFract.name)-1; - - if (fId >= 0) - { - for (unsigned i = 0; i < lutFract.fractCells.size(); ++i) - { - lutFractures[fId].emplace_back(get_last_digit_from_string(lutFract.fractCells[i])-1); - } - } -} - -void PlaceDB::set_sliceFF_ctrl_mode(std::string const& mode) -{ - sliceFF_ctrl_mode = mode; -} - -void PlaceDB::set_sliceFF_ctrl(std::string const& sName, int cnt) -{ - sliceFFCtrl.emplace_back(std::make_pair(sName, cnt)); -} - -void PlaceDB::set_sliceUnitFF_ctrl(std::string const& sName, int cnt) -{ - sliceFFUnitCtrl.emplace_back(std::make_pair(sName, cnt)); -} - -void PlaceDB::set_FFCtrl_type(std::string const& type) -{ - ////DBG - //std::cout << "set ff ctrl type as " << type << std::endl; - ////DBG - ffCtrlType = type; -} - -void PlaceDB::set_wl_weight_x(double wt) -{ - wlXWeight = wt; -} - -void PlaceDB::set_wl_weight_y(double wt) -{ - wlYWeight = wt; -} - -void PlaceDB::set_pin_route_cap(int pinCap) -{ - pinRouteCap = pinCap; -} - -void PlaceDB::set_route_cap_H(int hRouteCap) -{ - routeCapH = hRouteCap; -} - -void PlaceDB::set_route_cap_V(int vRouteCap) -{ - routeCapV = vRouteCap; - //std::cout << "Vertical Routing Capacity = " << vRouteCap << std::endl; -} - -void PlaceDB::set_siteOut(BookshelfParser::SiteOut const& st) -{ - if (siteOutCoordinate.size() == 0) - { - siteOutCoordinate.resize(siteTypes.size()+1, ""); - siteOutValue.resize(siteTypes.size()+1, 0); - } - - for (unsigned i = 0; i < st.siteTypes.size(); ++i) - { - index_type siteId = siteType2IndexMap[st.siteTypes[i]]; - //std::cout << "Assign siteType : " << st.siteTypes[i] << " with siteId: " << siteId << " as " << st.coordinate << " " << st.value << std::endl; - siteOutCoordinate[siteId] = st.coordinate; - siteOutValue[siteId] = st.value; - } - -} - -void PlaceDB::set_bookshelf_design(std::string& name) { - m_designName.swap(name); -} - -void PlaceDB::bookshelf_end() { - // // parsing bookshelf format finishes - // // now it is necessary to init data that is not set in bookshelf - //Flatten node2pin - flat_node2pin_map.reserve(pin_names.size()); - flat_node2pin_start_map.emplace_back(0); - for (const auto& sub : node2pin_map) - { - flat_node2pin_map.insert(flat_node2pin_map.end(), sub.begin(), sub.end()); - flat_node2pin_start_map.emplace_back(flat_node2pin_map.size()); - } - - org_fixed_node_name2id_map = fixed_node_name2id_map; - for (auto& el : fixed_node_name2id_map) - { - el.second += num_movable_nodes; - } - - node_name2id_map.insert(fixed_node_name2id_map.begin(), fixed_node_name2id_map.end()); - lut_type.insert(lut_type.end(), fixed_lut_type.begin(), fixed_lut_type.end()); - cluster_lut_type.insert(cluster_lut_type.end(), fixed_cluster_lut_type.begin(), fixed_cluster_lut_type.end()); - node_size_x.insert(node_size_x.end(), fixed_node_size_x.begin(), fixed_node_size_x.end()); - node_size_y.insert(node_size_y.end(), fixed_node_size_y.begin(), fixed_node_size_y.end()); - node_names.insert(node_names.end(), fixed_node_names.begin(), fixed_node_names.end()); - node_types.insert(node_types.end(), fixed_node_types.begin(), fixed_node_types.end()); - node_x.insert(node_x.end(), fixed_node_x.begin(), fixed_node_x.end()); - node_y.insert(node_y.end(), fixed_node_y.begin(), fixed_node_y.end()); - node_z.insert(node_z.end(), fixed_node_z.begin(), fixed_node_z.end()); - node2fence_region_map.insert(node2fence_region_map.end(), fixed_node2fence_region_map.begin(), fixed_node2fence_region_map.end()); - - if (m_numCCs > 0) - { - org_flat_node2pin_map.reserve(pin_names.size()); - org_flat_node2pin_start_map.emplace_back(0); - for (const auto& sub : org_node2pin_map) - { - org_flat_node2pin_map.insert(org_flat_node2pin_map.end(), sub.begin(), sub.end()); - org_flat_node2pin_start_map.emplace_back(org_flat_node2pin_map.size()); - } - - for (auto& el : org_fixed_node_name2id_map) - { - el.second += org_num_movable_nodes; - } - - org_node_name2id_map.insert(org_fixed_node_name2id_map.begin(), org_fixed_node_name2id_map.end()); - org_lut_type.insert(org_lut_type.end(), fixed_lut_type.begin(), fixed_lut_type.end()); - org_node_size_x.insert(org_node_size_x.end(), fixed_node_size_x.begin(), fixed_node_size_x.end()); - org_node_size_y.insert(org_node_size_y.end(), fixed_node_size_y.begin(), fixed_node_size_y.end()); - org_node_names.insert(org_node_names.end(), fixed_node_names.begin(), fixed_node_names.end()); - org_node_types.insert(org_node_types.end(), fixed_node_types.begin(), fixed_node_types.end()); - org_node2fence_region_map.insert(org_node2fence_region_map.end(), fixed_node2fence_region_map.begin(), fixed_node2fence_region_map.end()); - org_node_x.insert(org_node_x.end(), fixed_node_x.begin(), fixed_node_x.end()); - org_node_y.insert(org_node_y.end(), fixed_node_y.begin(), fixed_node_y.end()); - org_node_z.insert(org_node_z.end(), fixed_node_z.begin(), fixed_node_z.end()); - - dreamplacePrint(kINFO, "Design contains %d carry chains\n", m_numCCs); - - //Generate required info on input/output pins of each carry chain node - - ////DBG - ////Verify info - //std::cout << "General info: "; - //index_type ccnodeId = cc2nodeId_map[0]; - //std::cout << " consider node " << ccnodeId << " " << node_names[ccnodeId] << std::endl; - //for (index_type idx = flat_node2pin_start_map[ccnodeId]; - // idx < flat_node2pin_start_map[ccnodeId+1]; ++idx) - //{ - // index_type pinId = flat_node2pin_map[idx]; - // if (pin_typeIds[pinId] == 1) - // { - // std::cout << "Input Pin " << pin_names[pinId] << " is of type " << pin_typeIds[pinId] << std::endl; - // } else if (pin_typeIds[pinId] == 0 || pin_typeIds[pinId] == 20) //Output pin - // { - // std::cout << "Output Pin " << pin_names[pinId] << " is of type " << pin_typeIds[pinId] << std::endl; - // } - //} - - //std::cout << "Input pins of first cc node with offset: " << std::endl; - //for (auto el : temp_input_pin_info[0]) - //{ - // std::cout << el.second << " Input Pin " << pin_names[el.first] << " is of type " << pin_typeIds[el.first] << std::endl; - //} - //std::cout << "Output pins of first cc node with offset: " << std::endl; - //for (auto el : temp_output_pin_info[0]) - //{ - // std::cout << el.second << " Output Pin " << pin_names[el.first] << " is of type " << pin_typeIds[el.first] << std::endl; - //} - ////DBG - - //overall_cc_input_pin_start_map.emplace_back(0); - //overall_cc_output_pin_start_map.emplace_back(0); - //for (index_type ccIdx = 0; ccIdx < m_numCCs; ++ccIdx) - //{ - // ////DBG - // //std::cout << "Input pins of first cc node with offset: " << std::endl; - // //for (auto el : temp_input_pin_info[ccIdx]) - // //{ - // // std::cout << el.second << " Input Pin " << pin_names[el.first] << " " << el.first << " is of type " << pin_typeIds[el.first] << std::endl; - // //} - // ////DBG - - // std::sort(temp_input_pin_info[ccIdx].begin(), temp_input_pin_info[ccIdx].end(), - // [&](const auto &a, const auto &b){ return a.second < b.second; }); - - // ////DBG - // //std::cout << "After sorting Input pins of first cc node with offset: " << std::endl; - // //for (auto el : temp_input_pin_info[ccIdx]) - // //{ - // // std::cout << el.second << " Input Pin " << pin_names[el.first] << " " << el.first << " is of type " << pin_typeIds[el.first] << std::endl; - // //} - // ////DBG - - // int currEl = -1; - // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); - // for (auto el : temp_input_pin_info[ccIdx]) - // { - // if (currEl == -1) - // { - // currEl = el.second; - // } else if (currEl != el.second) - // { - // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); - // currEl = el.second; - // } - // flat_cc_input_pins_map.emplace_back(el.first); - // } - // flat_cc_input_pin_start_map.emplace_back(flat_cc_input_pins_map.size()); - // overall_cc_input_pin_start_map.emplace_back(flat_cc_input_pin_start_map.size()); - - // ////DBG - // //std::cout << "Contents of flat_cc_input_pins_map: " << std::endl; - // //for (auto el : flat_cc_input_pins_map) - // //{ - // // std::cout << el << " "; - // //} - // //std::cout << std::endl; - // //std::cout << "Contents of flat_cc_input_pin_start_map: " << std::endl; - // //for (auto el : flat_cc_input_pin_start_map) - // //{ - // // std::cout << el << " "; - // //} - // //std::cout << std::endl; - // //std::cout << "Contents of overall_cc_input_pin_start_map: " << std::endl; - // //for (auto el : overall_cc_input_pin_start_map) - // //{ - // // std::cout << el << " "; - // //} - // //std::cout << std::endl; - // ////DBG - - // std::sort(temp_output_pin_info[ccIdx].begin(), temp_output_pin_info[ccIdx].end(), - // [&](const auto &a, const auto &b){ return a.second < b.second; }); - // currEl = -1; - // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); - // for (auto el : temp_output_pin_info[ccIdx]) - // { - // if (currEl == -1) - // { - // currEl = el.second; - // } else if (currEl != el.second) - // { - // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); - // currEl = el.second; - // } - // flat_cc_output_pins_map.emplace_back(el.first); - // } - // flat_cc_output_pin_start_map.emplace_back(flat_cc_output_pins_map.size()); - // overall_cc_output_pin_start_map.emplace_back(flat_cc_output_pin_start_map.size()); - //} - } -} - -bool PlaceDB::write(std::string const& filename) const { - - return write(filename, NULL, NULL); -} - -bool PlaceDB::write(std::string const& filename, - float const* x, - float const* y, - PlaceDB::index_type const* z) const { - return BookShelfWriter(*this).write(filename, x, y, z); -} - -DREAMPLACE_END_NAMESPACE - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h deleted file mode 100644 index f6717a99..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h +++ /dev/null @@ -1,682 +0,0 @@ -/************************************************************************* - > File Name: PlaceDB.h - > Author: Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - > Mail: yibolin@utexas.edu - > Created Time: Mon 14 Mar 2016 09:22:46 PM CDT - > Updated: Mar 2021 - ************************************************************************/ - -#ifndef DREAMPLACE_PLACEDB_H -#define DREAMPLACE_PLACEDB_H - -#include // bookshelf parser -#include - -#include "Node.h" -#include "Net.h" -#include "Pin.h" -#include "LibCell.h" -#include "Params.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class PlaceDB; - -//Introduce new struct for clk region information -struct clk_region -{ - int xl; - int yl; - int xm; - int ym; - int xh; - int yh; -}; - -class PlaceDB : public BookshelfParser::BookshelfDataBase -{ - public: - typedef Object::coordinate_type coordinate_type; - typedef coordinate_traits::manhattan_distance_type manhattan_distance_type; - typedef coordinate_traits::index_type index_type; - typedef coordinate_traits::area_type area_type; - typedef hashspace::unordered_map string2index_map_type; - typedef hashspace::unordered_map string2string_map_type; - typedef Box diearea_type; - - /// default constructor - PlaceDB(); - - /// destructor - virtual ~PlaceDB() {} - - /// member functions - /// data access - - std::vector const& nodeNames() const {return node_names;} - std::vector& nodeNames() {return node_names;} - std::string const& nodeName(index_type id) const {return node_names.at(id);} - std::string& nodeName(index_type id) {return node_names.at(id);} - - std::vector const& nodeTypes() const {return node_types;} - std::vector& nodeTypes() {return node_types;} - std::string const& nodeType(index_type id) const {return node_types.at(id);} - std::string& nodeType(index_type id) {return node_types.at(id);} - - std::vector const& nodeXLocs() const {return node_x;} - std::vector& nodeXLocs() {return node_x;} - double const& nodeX(index_type id) const {return node_x.at(id);} - double& nodeX(index_type id) {return node_x.at(id);} - - std::vector const& nodeYLocs() const {return node_y;} - std::vector& nodeYLocs() {return node_y;} - double const& nodeY(index_type id) const {return node_y.at(id);} - double& nodeY(index_type id) {return node_y.at(id);} - - std::vector const& nodeZLocs() const {return node_z;} - std::vector& nodeZLocs() {return node_z;} - index_type const& nodeZ(index_type id) const {return node_z.at(id);} - index_type& nodeZ(index_type id) {return node_z.at(id);} - - std::vector const& nodeXSizes() const {return node_size_x;} - std::vector& nodeXSizes() {return node_size_x;} - double const& nodeXSize(index_type id) const {return node_size_x.at(id);} - double& nodeXSize(index_type id) {return node_size_x.at(id);} - - std::vector const& nodeYSizes() const {return node_size_y;} - std::vector& nodeYSizes() {return node_size_y;} - double const& nodeYSize(index_type id) const {return node_size_y.at(id);} - double& nodeYSize(index_type id) {return node_size_y.at(id);} - - std::vector const& node2FenceRegionMap() const {return node2fence_region_map;} - std::vector& node2FenceRegionMap() {return node2fence_region_map;} - index_type const& nodeFenceRegion(index_type id) const {return node2fence_region_map.at(id);} - index_type& nodeFenceRegion(index_type id) {return node2fence_region_map.at(id);} - - std::vector const& node2OutPinId() const {return node2outpinIdx_map;} - std::vector& node2OutPinId() {return node2outpinIdx_map;} - - std::vector const& node2PinCount() const {return node2pincount_map;} - std::vector& node2PinCount() {return node2pincount_map;} - index_type const& node2PinCnt(index_type id) const {return node2pincount_map.at(id);} - index_type& node2PinCnt(index_type id) {return node2pincount_map.at(id);} - - std::vector const& flopIndices() const {return flop_indices;} - std::vector& flopIndices() {return flop_indices;} - index_type const& flopIndex(index_type id) const {return flop_indices.at(id);} - index_type& flopIndex(index_type id) {return flop_indices.at(id);} - - std::vector const& lutIndices() const {return lut_indices;} - std::vector& lutIndices() {return lut_indices;} - index_type const& lutIndex(index_type id) const {return lut_indices.at(id);} - index_type& lutIndex(index_type id) {return lut_indices.at(id);} - - std::vector const& lutTypes() const {return lut_type;} - std::vector& lutTypes() {return lut_type;} - - std::vector const& clusterlutTypes() const {return cluster_lut_type;} - std::vector& clusterlutTypes() {return cluster_lut_type;} - - std::vector const& node2OutPinCount() const {return node2outpinCount;} - std::vector& node2OutPinCount() {return node2outpinCount;} - - std::vector > const& node2PinMap() const {return node2pin_map;} - std::vector >& node2PinMap() {return node2pin_map;} - index_type const& node2PinIdx(index_type xloc, index_type yloc) const {return node2pin_map.at(xloc).at(yloc);} - index_type& node2PinIdx(index_type xloc, index_type yloc) {return node2pin_map.at(xloc).at(yloc);} - - std::vector const& node_count() const {return nodeCount;} - std::vector& node_count() {return nodeCount;} - - //Nodes part of Carry chain - std::vector const& ccElementCount() const {return cc_element_count;} - std::vector& ccElementCount() {return cc_element_count;} - - std::vector const& node2CCIdMap() const {return node2ccId_map;} - std::vector& node2CCIdMap() {return node2ccId_map;} - - std::vector const& cc2nodeIdMap() const {return cc2nodeId_map;} - std::vector& cc2nodeIdMap() {return cc2nodeId_map;} - - std::vector const& isCCNode() const {return is_cc_node;} - std::vector& isCCNode() {return is_cc_node;} - - //std::vector const& flatCCInputPinsMap() const {return flat_cc_input_pins_map;} - //std::vector& flatCCInputPinsMap() {return flat_cc_input_pins_map;} - - //std::vector const& flatCCOutputPinsMap() const {return flat_cc_output_pins_map;} - //std::vector& flatCCOutputPinsMap() {return flat_cc_output_pins_map;} - - //std::vector const& flatCCInputPinStartMap() const {return flat_cc_input_pin_start_map;} - //std::vector& flatCCInputPinStartMap() {return flat_cc_input_pin_start_map;} - - //std::vector const& flatCCOutputPinStartMap() const {return flat_cc_output_pin_start_map;} - //std::vector& flatCCOutputPinStartMap() {return flat_cc_output_pin_start_map;} - - //std::vector const& overallCCInputPinStartMap() const {return overall_cc_input_pin_start_map;} - //std::vector& overallCCInputPinStartMap() {return overall_cc_input_pin_start_map;} - - //std::vector const& overallCCOutputPinStartMap() const {return overall_cc_output_pin_start_map;} - //std::vector& overallCCOutputPinStartMap() {return overall_cc_output_pin_start_map;} - - std::vector const& netNames() const {return net_names;} - std::vector& netNames() {return net_names;} - std::string const& netName(index_type id) const {return net_names.at(id);} - std::string& netName(index_type id) {return net_names.at(id);} - - std::size_t numNets() const {return net_names.size();} - - std::vector const& net2PinCount() const {return net2pincount_map;} - std::vector& net2PinCount() {return net2pincount_map;} - index_type const& net2PinCnt(index_type id) const {return net2pincount_map.at(id);} - index_type& net2PinCnt(index_type id) {return net2pincount_map.at(id);} - - std::vector > const& net2PinMap() const {return net2pin_map;} - std::vector >& net2PinMap() {return net2pin_map;} - index_type const& net2PinIdx(index_type xloc, index_type yloc) const {return net2pin_map.at(xloc).at(yloc);} - index_type& net2PinIdx(index_type xloc, index_type yloc) {return net2pin_map.at(xloc).at(yloc);} - - std::vector const& flatNet2PinMap() const {return flat_net2pin_map;} - std::vector& flatNet2PinMap() {return flat_net2pin_map;} - - std::vector const& flatNet2PinStartMap() const {return flat_net2pin_start_map;} - std::vector& flatNet2PinStartMap() {return flat_net2pin_start_map;} - - std::vector const& flatNode2PinStartMap() const {return flat_node2pin_start_map;} - std::vector& flatNode2PinStartMap() {return flat_node2pin_start_map;} - - std::vector const& flatNode2PinMap() const {return flat_node2pin_map;} - std::vector& flatNode2PinMap() {return flat_node2pin_map;} - - std::vector const& pinNames() const {return pin_names;} - std::vector& pinNames() {return pin_names;} - std::string const& pinName(index_type id) const {return pin_names.at(id);} - std::string& pinName(index_type id) {return pin_names.at(id);} - - std::size_t numPins() const {return pin_names.size();} - - std::vector const& pin2NetMap() const {return pin2net_map;} - std::vector& pin2NetMap() {return pin2net_map;} - - std::vector const& pin2NodeMap() const {return pin2node_map;} - std::vector& pin2NodeMap() {return pin2node_map;} - index_type const& pin2Node(index_type id) const {return pin2node_map.at(id);} - index_type& pin2Node(index_type id) {return pin2node_map.at(id);} - - std::vector const& pin2NodeTypeMap() const {return pin2nodeType_map;} - std::vector& pin2NodeTypeMap() {return pin2nodeType_map;} - - std::vector const& pinTypes() const {return pin_types;} - std::vector& pinTypes() {return pin_types;} - - std::vector const& pinTypeIds() const {return pin_typeIds;} - std::vector& pinTypeIds() {return pin_typeIds;} - - std::vector const& pinOffsetX() const {return pin_offset_x;} - std::vector& pinOffsetX() {return pin_offset_x;} - - std::vector const& pinOffsetY() const {return pin_offset_y;} - std::vector& pinOffsetY() {return pin_offset_y;} - - std::vector const& libCells() const {return m_vLibCell;} - std::vector& libCells() {return m_vLibCell;} - LibCell const& libCell(index_type id) const {return m_vLibCell.at(id);} - LibCell& libCell(index_type id) {return m_vLibCell.at(id);} - - std::size_t numLibCells() const {return m_vLibCell.size();} - - std::size_t siteRows() const {return m_siteDB.size();} - std::size_t siteCols() const {return m_siteDB[0].size();} - index_type const& siteVal(index_type xloc, index_type yloc) const {return m_siteDB.at(xloc).at(yloc);} - index_type& siteVal(index_type xloc, index_type yloc) {return m_siteDB.at(xloc).at(yloc);} - - /// be careful to use die area because it is larger than the actual rowBbox() which is the placement area - /// it is safer to use rowBbox() - diearea_type const& dieArea() const {return m_dieArea;} - - string2index_map_type const& libCellName2Index() const {return m_LibCellName2Index;} - string2index_map_type& libCellName2Index() {return m_LibCellName2Index;} - - string2index_map_type const& nodeName2Index() const {return node_name2id_map;} - string2index_map_type& nodeName2Index() {return node_name2id_map;} - - string2index_map_type const& netName2Index() const {return net_name2id_map;} - string2index_map_type& netName2Index() {return net_name2id_map;} - - std::size_t numMovable() const {return node_names.size()-fixed_node_names.size();} - std::size_t numFixed() const {return fixed_node_names.size();} - - std::vector const& orgNode2CCIdMap() const {return org_node2ccId_map;} - std::vector& orgNode2CCIdMap() {return org_node2ccId_map;} - - std::vector const& isOrgCCNode() const {return org_is_cc_node;} - std::vector& isOrgCCNode() {return org_is_cc_node;} - - std::vector const& new2OrgNodeMap() const {return new2org_node_map;} - std::vector& new2OrgNodeMap() {return new2org_node_map;} - int const& orgNodeMap(index_type id) const {return new2org_node_map.at(id);} - int& orgNodeMap(index_type id) {return new2org_node_map.at(id);} - - std::size_t numOrgMovable() const {return org_node_names.size()-fixed_node_names.size();} - - string2index_map_type const& orgNodeName2Index() const {return org_node_name2id_map;} - string2index_map_type& orgNodeName2Index() {return org_node_name2id_map;} - - std::vector const& orgNodeNames() const {return org_node_names;} - std::vector& orgNodeNames() {return org_node_names;} - std::string const& orgNodeName(index_type id) const {return org_node_names.at(id);} - std::string& orgNodeName(index_type id) {return org_node_names.at(id);} - - std::vector const& orgNodeTypes() const {return org_node_types;} - std::vector& orgNodeTypes() {return org_node_types;} - std::string const& orgNodeType(index_type id) const {return org_node_types.at(id);} - std::string& orgNodeType(index_type id) {return org_node_types.at(id);} - - std::vector const& orgNodeXSizes() const {return org_node_size_x;} - std::vector& orgNodeXSizes() {return org_node_size_x;} - - std::vector const& orgNodeYSizes() const {return org_node_size_y;} - std::vector& orgNodeYSizes() {return org_node_size_y;} - - std::vector const& orgNode2FenceRegionMap() const {return org_node2fence_region_map;} - std::vector& orgNode2FenceRegionMap() {return org_node2fence_region_map;} - - std::vector const& orgNodeCount() const {return org_nodeCount;} - std::vector& orgNodeCount() {return org_nodeCount;} - - std::vector const& orgFlopIndices() const {return org_flop_indices;} - std::vector& orgFlopIndices() {return org_flop_indices;} - index_type const& orgFlopIndex(index_type id) const {return org_flop_indices.at(id);} - index_type& orgFlopIndex(index_type id) {return org_flop_indices.at(id);} - - std::vector const& orgLutTypes() const {return org_lut_type;} - std::vector& orgLutTypes() {return org_lut_type;} - - std::vector const& orgPinOffsetX() const {return org_pin_offset_x;} - std::vector& orgPinOffsetX() {return org_pin_offset_x;} - - std::vector const& orgPinOffsetY() const {return org_pin_offset_y;} - std::vector& orgPinOffsetY() {return org_pin_offset_y;} - - std::vector const& orgPin2NodeTypeMap() const {return org_pin2nodeType_map;} - std::vector& orgPin2NodeTypeMap() {return org_pin2nodeType_map;} - - std::vector const& orgNode2PinCount() const {return org_node2pincount_map;} - std::vector& orgNode2PinCount() {return org_node2pincount_map;} - - std::vector const& orgPin2NodeMap() const {return org_pin2node_map;} - std::vector& orgPin2NodeMap() {return org_pin2node_map;} - - std::vector const& orgNode2OutPinCount() const {return org_node2outpinCount;} - std::vector& orgNode2OutPinCount() {return org_node2outpinCount;} - - std::vector const& orgNode2OutPinId() const {return org_node2outpinIdx_map;} - std::vector& orgNode2OutPinId() {return org_node2outpinIdx_map;} - - std::vector const& orgFlatNode2PinMap() const {return org_flat_node2pin_map;} - std::vector& orgFlatNode2PinMap() {return org_flat_node2pin_map;} - - std::vector const& orgFlatNode2PinStartMap() const {return org_flat_node2pin_start_map;} - std::vector& orgFlatNode2PinStartMap() {return org_flat_node2pin_start_map;} - - std::vector const& orgNodeXLocs() const {return org_node_x;} - std::vector& orgNodeXLocs() {return org_node_x;} - - std::vector const& orgNodeYLocs() const {return org_node_y;} - std::vector& orgNodeYLocs() {return org_node_y;} - - std::vector const& orgNodeZLocs() const {return org_node_z;} - std::vector& orgNodeZLocs() {return org_node_z;} - - std::vector const& orgflatCCNodeMap() const {return org_flat_cc2node_map;} - std::vector& orgflatCCNodeMap() {return org_flat_cc2node_map;} - - std::vector const& orgflatCCNodeStartMap() const {return org_flat_cc2node_start_map;} - std::vector& orgflatCCNodeStartMap() {return org_flat_cc2node_start_map;} - - //Site & Resources - std::vector const& site_types() const {return siteTypes;} - std::vector& site_types() {return siteTypes;} - std::string const& site_type(index_type id) const {return siteTypes.at(id);} - std::string& site_type(index_type id) {return siteTypes.at(id);} - - std::vector > const& site_resources_map() const {return siteResources;} - std::vector >& site_resources_map() {return siteResources;} - std::string const& site_resource(index_type xloc, index_type yloc) const {return siteResources.at(xloc).at(yloc);} - std::string& site_resource(index_type xloc, index_type yloc) {return siteResources.at(xloc).at(yloc);} - - string2string_map_type const& rsrc2site_map() const {return rsrc2SiteMap;} - string2string_map_type& rsrc2site_map() {return rsrc2SiteMap;} - std::string const& rsrc_type2site(std::string rsrcType) const {return rsrc2SiteMap.at(rsrcType);} - std::string& rsrc_type2site(std::string rsrcType) {return rsrc2SiteMap.at(rsrcType);} - - string2index_map_type const& site_rsrc2count_map() const {return siteRsrcCountMap;} - string2index_map_type& site_rsrc2count_map() {return siteRsrcCountMap;} - - std::vector const& rsrc_types() const {return rsrcTypes;} - std::vector& rsrc_types() {return rsrcTypes;} - std::string const& rsrc_type(index_type id) const {return rsrcTypes.at(id);} - std::string& rsrc_type(index_type id) {return rsrcTypes.at(id);} - - std::vector > const& rsrc_insts_map() const {return rsrcInsts;} - std::vector >& rsrc_insts_map() {return rsrcInsts;} - - string2string_map_type const& inst2rsrc_map() const {return inst2RsrcMap;} - string2string_map_type& inst2rsrc_map() {return inst2RsrcMap;} - - index_type const& site_per_column() const {return sitePerColumn;} - index_type& site_per_column() {return sitePerColumn;} - - std::vector const& site_widths() const {return siteWidth;} - std::vector& site_widths() {return siteWidth;} - double const& site_width(index_type id) const {return siteWidth.at(id);} - double& site_width(index_type id) {return siteWidth.at(id);} - - std::vector const& site_heights() const {return siteHeight;} - std::vector& site_heights() {return siteHeight;} - double const& site_height(index_type id) const {return siteHeight.at(id);} - double& site_height(index_type id) {return siteHeight.at(id);} - - std::vector const& rsrc_inst_types() const {return rsrcInstTypes;} - std::vector& rsrc_inst_types() {return rsrcInstTypes;} - std::string const& rsrc_inst_type(index_type id) const {return rsrcInstTypes.at(id);} - std::string& rsrc_inst_type(index_type id) {return rsrcInstTypes.at(id);} - - std::vector const& site_out_coordinates() const {return siteOutCoordinate;} - std::vector& site_out_coordinates() {return siteOutCoordinate;} - std::string const& site_out_coordinate(index_type id) const {return siteOutCoordinate.at(id);} - std::string& site_out_coordinate(index_type id) {return siteOutCoordinate.at(id);} - - std::vector const& site_out_values() const {return siteOutValue;} - std::vector& site_out_values() {return siteOutValue;} - index_type const& site_out_value(index_type id) const {return siteOutValue.at(id);} - index_type& site_out_value(index_type id) {return siteOutValue.at(id);} - - std::vector const& rsrc_inst_widths() const {return rsrcInstWidth;} - std::vector& rsrc_inst_widths() {return rsrcInstWidth;} - double const& rsrc_inst_width(index_type id) const {return rsrcInstWidth.at(id);} - double& rsrc_inst_width(index_type id) {return rsrcInstWidth.at(id);} - - std::vector const& rsrc_inst_heights() const {return rsrcInstHeight;} - std::vector& rsrc_inst_heights() {return rsrcInstHeight;} - double const& rsrc_inst_height(index_type id) const {return rsrcInstHeight.at(id);} - double& rsrc_inst_height(index_type id) {return rsrcInstHeight.at(id);} - - std::vector > const& lut_fractures_map() const {return lutFractures;} - std::vector >& lut_fractures_map() {return lutFractures;} - index_type const& lut_fracture(index_type xloc, index_type yloc) const {return lutFractures.at(xloc).at(yloc);} - index_type& lut_fracture(index_type xloc, index_type yloc) {return lutFractures.at(xloc).at(yloc);} - - string2index_map_type const& site_type2index_map() const {return siteType2IndexMap;} - string2index_map_type& site_type2index_map() {return siteType2IndexMap;} - index_type const& site_type2index(std::string siteType) const {return siteType2IndexMap.at(siteType);} - index_type& site_type2index(std::string siteType) {return siteType2IndexMap.at(siteType);} - - string2index_map_type const& rsrc_type2index_map() const {return rsrcType2IndexMap;} - string2index_map_type& rsrc_type2index_map() {return rsrcType2IndexMap;} - index_type const& rsrc_type2index(std::string rsrcType) const {return rsrcType2IndexMap.at(rsrcType);} - index_type& rsrc_type2index(std::string rsrcType) {return rsrcType2IndexMap.at(rsrcType);} - - string2index_map_type const& rsrc_inst_type2index_map() const {return rsrcInstType2IndexMap;} - string2index_map_type& rsrc_inst_type2index_map() {return rsrcInstType2IndexMap;} - - std::vector > const& slice_elements() const {return sliceElements;} - std::vector >& slice_elements() {return sliceElements;} - - std::vector > const& slice_FF_ctrls() const {return sliceFFCtrl;} - std::vector >& slice_FF_ctrls() {return sliceFFCtrl;} - std::string const& slice_FF_ctrl_signal(index_type idx) const {return sliceFFCtrl.at(idx).first;} - std::string& slice_FF_ctrl_signal(index_type idx) {return sliceFFCtrl.at(idx).first;} - index_type const& slice_FF_ctrl_count(index_type idx) const {return sliceFFCtrl.at(idx).second;} - index_type& slice_FF_ctrl_count(index_type idx) {return sliceFFCtrl.at(idx).second;} - - std::vector > const& sliceUnit_FF_ctrls() const {return sliceFFUnitCtrl;} - std::vector >& sliceUnit_FF_ctrls() {return sliceFFUnitCtrl;} - std::string const& sliceUnit_FF_ctrl_signal(index_type idx) const {return sliceFFUnitCtrl.at(idx).first;} - std::string& sliceUnit_FF_ctrl_signal(index_type idx) {return sliceFFUnitCtrl.at(idx).first;} - index_type const& sliceUnit_FF_ctrl_count(index_type idx) const {return sliceFFUnitCtrl.at(idx).second;} - index_type& sliceUnit_FF_ctrl_count(index_type idx) {return sliceFFUnitCtrl.at(idx).second;} - - std::string const& ff_ctrl_type() const {return ffCtrlType;} - std::string& ff_ctrl_type() {return ffCtrlType;} - - double wl_weight_x() const {return wlXWeight;} - double wl_weight_y() const {return wlYWeight;} - std::string slice_ff_ctrl_mode() const {return sliceFF_ctrl_mode;} - index_type lut_shared_max_pins() const {return lutMaxShared;} - index_type lut_type_in_sliceUnit() const {return lutTypeInSliceUnit;} - index_type pin_route_cap() const {return pinRouteCap;} - index_type route_cap_h() const {return routeCapH;} - index_type route_cap_v() const {return routeCapV;} - - //std::size_t numMovable() const {return num_movable_nodes;} - //std::size_t numFixed() const {return num_fixed_nodes;} - std::size_t numLibCell() const {return m_numLibCell;} - std::size_t numLUT() const {return m_numLUT;} - std::size_t numFF() const {return m_numFF;} - std::size_t numCCNodes() const {return m_numCCs;} - std::string designName() const {return m_designName;} - - /// \return die area information of layout - double xl() const {return m_dieArea.xl();} - double yl() const {return m_dieArea.yl();} - double xh() const {return m_dieArea.xh();} - double yh() const {return m_dieArea.yh();} - manhattan_distance_type width() const {return m_dieArea.width();} - manhattan_distance_type height() const {return m_dieArea.height();} - - ///==== Bookshelf Callbacks ==== - virtual void add_bookshelf_node(std::string& name, std::string& type); //Updated for FPGA - virtual void add_bookshelf_net(BookshelfParser::Net const& n); - virtual void add_bookshelf_carry(BookshelfParser::CarryChain const& carry_chain); - virtual void set_bookshelf_node_pos(std::string const& name, double x, double y, int z); - virtual void resize_sites(int xSize, int ySize); - virtual void site_info_update(int x, int y, std::string const& name); - virtual void resize_clk_regions(int xReg, int yReg); - virtual void add_clk_region(std::string const& name, int xl, int yl, int xh, int yh, int xm, int ym); - virtual void add_lib_cell(std::string const& name); - virtual void add_input_pin(std::string& pName); - virtual void add_input_add_pin(std::string& pName); - virtual void add_output_pin(std::string& pName); - virtual void add_output_add_pin(std::string& pName); - virtual void add_clk_pin(std::string& pName); - virtual void add_ctrl_pin(std::string& pName); - virtual void add_site(BookshelfParser::Site const& st); - virtual void add_rsrc(BookshelfParser::Rsrc const& rsrc); - virtual void set_site_per_column(int val); - virtual void set_site_dimensions(std::string const& sName, double w, double h); - virtual void set_slice_element(std::string const& sName, int cnt); - virtual void set_cell_dimensions(std::string const& cName, double w, double h); - virtual void set_lut_max_shared(int cnt); - virtual void set_lut_type_in_sliceUnit(int cnt); - virtual void set_lut_fractureability(BookshelfParser::LUTFract const& lutFract); - virtual void set_sliceFF_ctrl_mode(std::string const& mode); - virtual void set_sliceFF_ctrl(std::string const& sName, int cnt); - virtual void set_sliceUnitFF_ctrl(std::string const& sName, int cnt); - virtual void set_FFCtrl_type(std::string const& type); - virtual void set_wl_weight_x(double wt); - virtual void set_wl_weight_y(double wt); - virtual void set_pin_route_cap(int pinCap); - virtual void set_route_cap_H(int hRouteCap); - virtual void set_route_cap_V(int vRouteCap); - virtual void set_siteOut(BookshelfParser::SiteOut const& st); - virtual void set_bookshelf_design(std::string& name); - virtual void update_nodes(); - virtual void bookshelf_end(); - - /// write placement solutions - virtual bool write(std::string const& filename) const; - virtual bool write(std::string const& filename, float const* x = NULL, float const* y = NULL, index_type const* z = NULL) const; - - std::vector > m_siteDB; //FPGA Site Information - std::vector m_clkRegionDB; //FPGA clkRegion Information - std::vector m_clkRegions; //FPGA clkRegion Names - int m_clkRegX; - int m_clkRegY; - std::vector m_vLibCell; ///< library definition for cell types - diearea_type m_dieArea; ///< die area, it can be larger than actual placement area - string2index_map_type m_LibCellName2Index; ///< map name of lib cell to index of m_vLibCell - - //Temp storage for libcell name considered - std::string m_libCellTemp; - - //Ensure correct node and pin_offset sizes for LUT/FF - index_type lutId = 100000; - index_type ffId = 100000; - index_type ioId = 100000; - index_type pllId = 100000; - - //Site & Resources Info - index_type sitePerColumn = 0; - std::vector > siteResources; - std::vector siteTypes; - string2index_map_type siteType2IndexMap; - std::vector siteWidth; - std::vector siteHeight; - string2index_map_type siteRsrcCountMap; - string2string_map_type rsrc2SiteMap; - - std::vector rsrcInstTypes; - string2index_map_type rsrcInstType2IndexMap; - std::vector rsrcTypes; - string2index_map_type rsrcType2IndexMap; - std::vector > rsrcInsts; - std::vector rsrcInstWidth; - std::vector rsrcInstHeight; - string2string_map_type inst2RsrcMap; - - std::vector > lutFractures; - std::vector > sliceElements; - std::vector > sliceFFCtrl; - std::vector > sliceFFUnitCtrl; - - std::vector siteOutCoordinate; - std::vector siteOutValue; - - std::string ffCtrlType; - double wlXWeight; - double wlYWeight; - index_type lutMaxShared; - index_type lutTypeInSliceUnit; - index_type pinRouteCap; - index_type routeCapH; - index_type routeCapV; - std::string sliceFF_ctrl_mode; - - std::size_t num_movable_nodes; ///< number of movable cells - std::size_t num_fixed_nodes; ///< number of fixed cells - std::size_t m_numLibCell; ///< number of standard cells in the library - std::size_t m_numLUT; ///< number of LUTs in design - std::size_t m_numFF; ///< number of FFs in design - std::size_t m_numCCs; ///< number of carry chains in design - - std::string m_designName; ///< for writing def file - - //temp flag - bool initSiteMapValUpd; - //Flattened - std::vector node_names; - std::vector node_types; - std::vector node_size_x; - std::vector node_size_y; - std::vector fixed_node_size_x; - std::vector fixed_node_size_y; - std::vector node_x; - std::vector node_y; - std::vector node_z; - std::vector fixed_node_x; - std::vector fixed_node_y; - std::vector fixed_node_z; - std::vector nodeCount; - - //New approach to parsing - std::vector fixed_node_names; - std::vector fixed_node_types; - std::vector net_names; - std::vector pin_names; - std::vector pin_types; - std::vector node2fence_region_map; - std::vector fixed_node2fence_region_map; - std::vector > node2pin_map; - std::vector node2pincount_map; - std::vector net2pincount_map; - std::vector node2outpinIdx_map; - std::vector node2outpinCount; - std::vector pin_typeIds; - std::vector pin2node_map; - std::vector pin2net_map; - std::vector pin2nodeType_map; - std::vector > net2pin_map; - std::vector flat_net2pin_map; - std::vector flat_net2pin_start_map; - std::vector flat_node2pin_map; - std::vector flat_node2pin_start_map; - std::vector cc_element_count; - std::vector is_cc_node; - std::vector flop_indices; - std::vector lut_type; - std::vector cluster_lut_type; - std::vector fixed_lut_type; - std::vector fixed_cluster_lut_type; - std::vector node2ccId_map; - std::vector cc2nodeId_map; - - std::vector pin_offset_x; - std::vector pin_offset_y; - - //string2index_map_type mov_node_name2id_map; - string2index_map_type fixed_node_name2id_map; - string2index_map_type node_name2id_map; - string2index_map_type net_name2id_map; - - //Data structures for original info when carry chains exist - string2index_map_type org_node_name2id_map; - std::vector org_node_names; - std::vector org_node_types; - std::vector org_node_size_x; - std::vector org_node_size_y; - std::vector org_node2fence_region_map; - std::vector org_nodeCount; - std::vector org_is_cc_node; - std::vector org_flop_indices; - std::vector org_lut_type; - std::vector org_node2ccId_map; - std::vector org_node2ccIndex_map; - std::vector org_flat_cc2node_map; - std::vector org_flat_cc2node_start_map; - std::vector new2org_node_map; - - std::vector org_node_x; - std::vector org_node_y; - std::vector org_node_z; - std::vector org_pin_offset_x; - std::vector org_pin_offset_y; - std::vector org_pin2nodeType_map; - std::vector org_node2pincount_map; - std::vector org_pin2node_map; - std::vector > org_node2pin_map; - std::vector org_node2outpinCount; - std::vector org_node2outpinIdx_map; - std::vector org_flat_node2pin_map; - std::vector org_flat_node2pin_start_map; - //Temporary org data structures - std::size_t org_num_movable_nodes; ///< number of movable cells - string2index_map_type org_fixed_node_name2id_map; - std::vector lut_indices; - ////Get information of carry chain node input/output pins - //std::vector flat_cc_input_pins_map; - //std::vector flat_cc_output_pins_map; - //std::vector flat_cc_input_pin_start_map; - //std::vector flat_cc_output_pin_start_map; - //std::vector overall_cc_input_pin_start_map; - //std::vector overall_cc_output_pin_start_map; - - ////Temporary datastructure - //std::vector > > temp_input_pin_info; - //std::vector > > temp_output_pin_info; -}; - -DREAMPLACE_END_NAMESPACE - -#endif - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h deleted file mode 100644 index 9618b1a3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - > File Name: PlaceWriter.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Mon 20 Jul 2015 11:34:51 AM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_PLACEWRITER_H -#define DREAMPLACE_PLACEWRITER_H - -#include -#include -#include "PlaceDB.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// class PlaceSolWriter is the base class to write placement solutions -class PlaceSolWriter -{ - public: - PlaceSolWriter(PlaceDB const& db) : m_db(db) {} - PlaceSolWriter(PlaceSolWriter const& rhs) : m_db(rhs.m_db) {} - PlaceSolWriter& operator=(PlaceSolWriter const& rhs); - protected: - PlaceDB const& m_db; -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h deleted file mode 100644 index 0857d5cd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h +++ /dev/null @@ -1,189 +0,0 @@ -/************************************************************************* - > File Name: Point.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Jun 2015 08:29:15 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_POINT_H -#define DREAMPLACE_POINT_H - -#include "Util.h" -#include - -DREAMPLACE_BEGIN_NAMESPACE - -template -class Point -{ - public: - typedef T coordinate_type; - - ///==== constructors ==== - Point(coordinate_type x = 0, coordinate_type y = 0) - { - set(x, y); - } - - Point(Point const& rhs) - { - copy(rhs); - } - - Point& operator=(Point const& rhs) - { - copy(rhs); - return *this; - } - - template - explicit Point(PointType const& rhs) - { - copy(rhs); - } - - template - Point& operator=(PointType const& rhs) - { - copy(rhs); - return *this; - } - - ///==== public member functions ==== - Point& set(coordinate_type x, coordinate_type y) - { - m_coords[kX] = x; - m_coords[kY] = y; - return *this; - } - Point& set(Direction1DType d, coordinate_type v) - { - m_coords[d] = v; - return *this; - } - coordinate_type get(Direction1DType d) const - { - return m_coords[d]; - } - coordinate_type x() const - { - return m_coords[kX]; - } - coordinate_type y() const - { - return m_coords[kY]; - } - - ///==== overload operators ==== - bool operator==(Point const& rhs) const - { - return (m_coords[0] == rhs.m_coords[0]) && - (m_coords[1] == rhs.m_coords[1]); - } - - bool operator!=(Point const& rhs) const - { - return !(*this == rhs); - } - - bool operator<(Point const& rhs) const - { - return (m_coords[0] < rhs.m_coords[0]) || - ((m_coords[0] == rhs.m_coords[0]) && - (m_coords[1] < rhs.m_coords[1])); - } - - bool operator<=(Point const& rhs) const - { - return !(rhs < *this); - } - - bool operator>(Point const& rhs) const - { - return rhs < *this; - } - - bool operator>=(Point const& rhs) const - { - return !(*this < rhs); - } - - Point& operator+=(Point const& rhs) - { - m_coords[kX] += rhs.get(kX); - m_coords[kY] += rhs.get(kY); - return *this; - } - Point& operator-=(Point const& rhs) - { - m_coords[kX] -= rhs.get(kX); - m_coords[kY] -= rhs.get(kY); - return *this; - } - - protected: - template - void copy(PointType const& rhs) - { - m_coords[0] = rhs.m_coords[0]; - m_coords[1] = rhs.m_coords[1]; - } - - coordinate_type m_coords[2]; ///< x and y values -}; - -/// \return manhattan distance of two points in 1D direction -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Point const& p1, Point const& p2, Direction1DType d) -{ - return limbo::abs(p1.get(d) - p2.get(d)); -} - -/// \return manhattan distance of two points -template -inline typename coordinate_traits::manhattan_distance_type -manhattanDistance(Point const& p1, Point const& p2) -{ - return manhattanDistance(p1, p2, kX) + manhattanDistance(p1, p2, kY); -} - -/// \return squared euclidean distance of two points -template -inline typename coordinate_traits::euclidean_distance_type -squareDistance(Point const& p1, Point const& p2) -{ - typedef typename coordinate_traits::euclidean_distance_type euclidean_distance_type; - return (euclidean_distance_type)( - pow(manhattanDistance(p1, p2, kX), 2) - + pow(manhattanDistance(p1, p2, kY), 2) - ); -} - -/// \return euclidean distance of two points -template -inline typename coordinate_traits::euclidean_distance_type -euclideanDistance(Point const& p1, Point const& p2) -{ - return sqrt(squareDistance(p1, p2)); -} - -/// overload operator+ -template -inline Point operator+(Point const& rhs1, Point const& rhs2) -{ - Point p (rhs1); - return (p += rhs2); -} - -/// overload operator- -template -inline Point operator-(Point const& rhs1, Point const& rhs2) -{ - Point p (rhs1); - return (p -= rhs2); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp deleted file mode 100644 index c868bb4f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/** - * @file PyPlaceDB.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Mar 2021 - * @brief Placement database for python - */ -#include -#include -#include -#include "PyPlaceDB.h" -#include - -DREAMPLACE_BEGIN_NAMESPACE - -const int INVALID = -1; - -bool readBookshelf(PlaceDB& db, std::string const& auxPath) -{ - // read bookshelf - if (!auxPath.empty()) - { - std::string const& filename = auxPath; - dreamplacePrint(kINFO, "reading %s\n", filename.c_str()); - bool flag = BookshelfParser::read(db, filename); - if (!flag) - { - dreamplacePrint(kERROR, "Bookshelf file parsing failed: %s\n", filename.c_str()); - return false; - } - ////DBG - //else - //{ - // std::cout << "Bookshelf file parsing successful " << std::endl; - //} - ////DBG - } - else dreamplacePrint(kWARN, "no Bookshelf file specified\n"); - - return true; -} - -void PyPlaceDB::set(PlaceDB const& db) -{ - num_ccNodes = db.numCCNodes(); //macros - num_terminals = db.numFixed(); //IOs - num_movable_nodes = db.numMovable(); // Movable cells - num_physical_nodes = num_terminals + num_movable_nodes; - - node_count = pybind11::cast(std::move(db.node_count())); - - //Node Info - node_names = pybind11::cast(std::move(db.nodeNames())); - node_types = pybind11::cast(std::move(db.nodeTypes())); - node_size_x = pybind11::cast(std::move(db.nodeXSizes())); - node_size_y = pybind11::cast(std::move(db.nodeYSizes())); - node2fence_region_map = pybind11::cast(std::move(db.node2FenceRegionMap())); - node_x = pybind11::cast(std::move(db.nodeXLocs())); - node_y = pybind11::cast(std::move(db.nodeYLocs())); - node_z = pybind11::cast(std::move(db.nodeZLocs())); - flop_indices = pybind11::cast(std::move(db.flopIndices())); - lut_type = pybind11::cast(std::move(db.lutTypes())); - cluster_lut_type = pybind11::cast(std::move(db.clusterlutTypes())); - - ////DBG CHECK contents of lut_type and cluster_lut_type - //for (int i = 0; i < 10; ++i) - //{ - // std::cout << "Instance: " << *node_names[i] << " of type: " << *node_types[i] << " has lut type: " << *lut_type[i]; - // std::cout << " and cluster_lut_type: " << *cluster_lut_type[i] << std::endl; - //} - ////DBG - - //Carry chain Info - if (num_ccNodes > 0) - { - org_num_movable_nodes = db.numOrgMovable(); // Movable cells - org_node_names = pybind11::cast(std::move(db.orgNodeNames())); - org_node_types = pybind11::cast(std::move(db.orgNodeTypes())); - org_node_size_x = pybind11::cast(std::move(db.orgNodeXSizes())); - org_node_size_y = pybind11::cast(std::move(db.orgNodeYSizes())); - org_node2fence_region_map = pybind11::cast(std::move(db.orgNode2FenceRegionMap())); - org_node_count = pybind11::cast(std::move(db.orgNodeCount())); - org_flop_indices = pybind11::cast(std::move(db.orgFlopIndices())); - org_lut_type = pybind11::cast(std::move(db.orgLutTypes())); - org_pin_offset_x = pybind11::cast(std::move(db.orgPinOffsetX())); - org_pin_offset_y = pybind11::cast(std::move(db.orgPinOffsetY())); - org_pin2nodeType_map = pybind11::cast(std::move(db.orgPin2NodeTypeMap())); - org_node2pincount_map = pybind11::cast(std::move(db.orgNode2PinCount())); - org_pin2node_map = pybind11::cast(std::move(db.orgPin2NodeMap())); - org_node2outpinCount = pybind11::cast(std::move(db.orgNode2OutPinCount())); - org_node2outpinIdx_map = pybind11::cast(std::move(db.orgNode2OutPinId())); - org_flat_node2pin_map = pybind11::cast(std::move(db.orgFlatNode2PinMap())); - org_flat_node2pin_start_map = pybind11::cast(std::move(db.orgFlatNode2PinStartMap())); - org_flat_cc2node_map = pybind11::cast(std::move(db.orgflatCCNodeMap())); - org_flat_cc2node_start_map = pybind11::cast(std::move(db.orgflatCCNodeStartMap())); - org_is_cc_node = pybind11::cast(std::move(db.isOrgCCNode())); - org_node_name2id_map = pybind11::cast(std::move(db.orgNodeName2Index())); - org_node_x = pybind11::cast(std::move(db.orgNodeXLocs())); - org_node_y = pybind11::cast(std::move(db.orgNodeYLocs())); - org_node_z = pybind11::cast(std::move(db.orgNodeZLocs())); - - cc_element_count = pybind11::cast(std::move(db.ccElementCount())); - node2ccId_map = pybind11::cast(std::move(db.node2CCIdMap())); - cc2nodeId_map = pybind11::cast(std::move(db.cc2nodeIdMap())); - org_node2ccId_map = pybind11::cast(std::move(db.orgNode2CCIdMap())); - new2org_node_map= pybind11::cast(std::move(db.new2OrgNodeMap())); - is_cc_node = pybind11::cast(std::move(db.isCCNode())); - //flat_cc_input_pins_map = pybind11::cast(std::move(db.flatCCInputPinsMap())); - //flat_cc_output_pins_map = pybind11::cast(std::move(db.flatCCOutputPinsMap())); - //flat_cc_input_pin_start_map = pybind11::cast(std::move(db.flatCCInputPinStartMap())); - //flat_cc_output_pin_start_map = pybind11::cast(std::move(db.flatCCOutputPinStartMap())); - //overall_cc_input_pin_start_map = pybind11::cast(std::move(db.overallCCInputPinStartMap())); - //overall_cc_output_pin_start_map = pybind11::cast(std::move(db.overallCCOutputPinStartMap())); - } - - node2outpinIdx_map = pybind11::cast(std::move(db.node2OutPinId())); - node2outpinCount = pybind11::cast(std::move(db.node2OutPinCount())); - node2pincount_map = pybind11::cast(std::move(db.node2PinCount())); - node2pin_map = pybind11::cast(std::move(db.node2PinMap())); - node_name2id_map = pybind11::cast(std::move(db.nodeName2Index())); - //movable_node_name2id_map = pybind11::cast(std::move(db.movNodeName2Index())); - //fixed_node_name2id_map = pybind11::cast(std::move(db.fixedNodeName2Index())); - flat_node2pin_map = pybind11::cast(std::move(db.flatNode2PinMap())); - flat_node2pin_start_map = pybind11::cast(std::move(db.flatNode2PinStartMap())); - - net_names = pybind11::cast(std::move(db.netNames())); - net2pincount_map = pybind11::cast(std::move(db.net2PinCount())); - net2pin_map = pybind11::cast(std::move(db.net2PinMap())); - flat_net2pin_map = pybind11::cast(std::move(db.flatNet2PinMap())); - flat_net2pin_start_map = pybind11::cast(std::move(db.flatNet2PinStartMap())); - net_name2id_map = pybind11::cast(std::move(db.netName2Index())); - - pin_names = pybind11::cast(std::move(db.pinNames())); - pin_types = pybind11::cast(std::move(db.pinTypes())); - pin_typeIds = pybind11::cast(std::move(db.pinTypeIds())); - pin_offset_x = pybind11::cast(std::move(db.pinOffsetX())); - pin_offset_y = pybind11::cast(std::move(db.pinOffsetY())); - pin2net_map = pybind11::cast(std::move(db.pin2NetMap())); - pin2node_map = pybind11::cast(std::move(db.pin2NodeMap())); - pin2nodeType_map = pybind11::cast(std::move(db.pin2NodeTypeMap())); - - //////DBG - //std::cout << "There are " << std::to_string(num_physical_nodes) << " nodes, " << std::to_string(db.numPins()) - // << " pins and " << std::to_string(db.numNets()) << " nets and " << std::to_string(db.numCCNodes()) - // << " macros in the design" << std::endl; - //////DBG - - //CtrlSets - sliceFFCtrls = pybind11::cast(std::move(db.slice_FF_ctrls())); - sliceUnitFFCtrls = pybind11::cast(std::move(db.sliceUnit_FF_ctrls())); - - //TODO - Make FF Ctrl signal generation part generic for any architecture - //Currently only US and Stratix-IV architectures supported - if (db.slice_ff_ctrl_mode() == "HALF") - { - //Xilinx US and related - std::unordered_map ceMapping; - std::unordered_map > cksrMapping; - PlaceDB::index_type numCKSR(0), numCE(0); - - for (unsigned int sFIdx = 0; sFIdx < db.numFF(); ++sFIdx) - { - unsigned int fIdx = db.flopIndex(sFIdx); - - int ck(INVALID), sr(INVALID), ce(INVALID), cksrId(INVALID), ceId(INVALID); - - //for (auto pin_id : node.pinIdArray()) - for (unsigned int pIdx = 0; pIdx < db.node2PinCnt(fIdx); ++pIdx) - { - PlaceDB::index_type pin_id = db.node2PinIdx(fIdx, pIdx); - - switch(pin_typeIds[pin_id].cast()) - { - case 2: - { - ck = pin2net_map[pin_id].cast(); - break; - } - case 3: - { - ce = pin2net_map[pin_id].cast(); - break; - } - case 4: - { - sr = pin2net_map[pin_id].cast(); - break; - } - default: - { - break; - } - } - } - - auto ckIt = cksrMapping.find(ck); - - if (ckIt == cksrMapping.end()) - { - cksrId = numCKSR; - cksrMapping[ck][sr] = numCKSR++; - } else - { - auto &srMap = ckIt->second; - auto srIt = srMap.find(sr); - if (srIt == srMap.end()) - { - cksrId = numCKSR; - srMap[sr] = numCKSR++; - } else - { - cksrId = srIt->second; - } - } - - auto ceIt = ceMapping.find(ce); - if (ceIt == ceMapping.end()) - { - ceId = numCE; - ceMapping[ce] = numCE++; - } else - { - ceId = ceIt->second; - } - ctrlSets.append(std::make_tuple(fIdx, cksrId, ceId)); - if (num_ccNodes > 0) - { - org_ctrlSets.append(std::make_tuple(db.orgNodeMap(fIdx), cksrId, ceId)); - } - } - } else - { - //Intel Stratix-IV and related - std::unordered_map clkMapping; - PlaceDB::index_type numCTRL(0), numCLK(0); - - int ctrl_signal_count = db.slice_FF_ctrl_count(1); - dreamplaceAssertMsg((ctrl_signal_count == 7), "Check if correct Ctrl signal count is provided"); - - std::vector ff_ctrls; - - ext_ctrlSet_start_map.append(0); - if (num_ccNodes > 0) - { - org_ext_ctrlSet_start_map.append(0); - } - - for (unsigned int sFIdx = 0; sFIdx < db.numFF(); ++sFIdx) - { - unsigned int fIdx = db.flopIndex(sFIdx); - - int ck(INVALID), ckId(INVALID); - - std::vector curVal(10, INVALID); - - for (unsigned int pIdx = 0; pIdx < db.node2PinCnt(fIdx); ++pIdx) - { - PlaceDB::index_type pin_id = db.node2PinIdx(fIdx, pIdx); - PlaceDB::index_type pinTypeId = pin_typeIds[pin_id].cast(); - - if(pinTypeId > 1 && pinTypeId < 10) - { - curVal[pinTypeId] = pin2net_map[pin_id].cast(); - - if (pinTypeId == 2) //CLK - { - ck = curVal[pinTypeId]; - } else if (curVal[pinTypeId] != INVALID) - { - ff_ctrls.emplace_back(curVal[pinTypeId]); - } - } - } - ext_ctrlSet_start_map.append(ff_ctrls.size()); - if (num_ccNodes > 0) - { - org_ext_ctrlSet_start_map.append(ff_ctrls.size()); - } - - auto ckIt = clkMapping.find(ck); - if (ckIt == clkMapping.end()) - { - ckId = numCLK; - clkMapping[ck] = numCLK++; - } else - { - ckId = ckIt->second; - } - - ctrlSets.append(std::make_tuple(fIdx, ckId, 0)); - if (num_ccNodes > 0) - { - org_ctrlSets.append(std::make_tuple(db.orgNodeMap(fIdx), ckId, 0)); - } - } - extended_ctrlSets = pybind11::cast(std::move(ff_ctrls)); - if (num_ccNodes > 0) - { - org_extended_ctrlSets = pybind11::cast(std::move(ff_ctrls)); - } - - dreamplacePrint(kINFO, "Design has %d unique clk signal(s) and multiple ctrl_mode combinations\n", numCLK); - } - - //SiteInfo - siteTypes = pybind11::cast(std::move(db.site_types())); - siteWidths = pybind11::cast(std::move(db.site_widths())); - siteHeights = pybind11::cast(std::move(db.site_heights())); - rsrcTypes = pybind11::cast(std::move(db.rsrc_types())); - rsrcInstWidths = pybind11::cast(std::move(db.rsrc_inst_widths())); - rsrcInstHeights = pybind11::cast(std::move(db.rsrc_inst_heights())); - siteResources = pybind11::cast(std::move(db.site_resources_map())); - rsrcInsts = pybind11::cast(std::move(db.rsrc_insts_map())); - rsrc2siteMap = pybind11::cast(std::move(db.rsrc2site_map())); - inst2rsrcMap = pybind11::cast(std::move(db.inst2rsrc_map())); - siteRsrc2CountMap = pybind11::cast(std::move(db.site_rsrc2count_map())); - siteType2indexMap = pybind11::cast(std::move(db.site_type2index_map())); - rsrcType2indexMap = pybind11::cast(std::move(db.rsrc_type2index_map())); - rsrcInstType2indexMap = pybind11::cast(std::move(db.rsrc_inst_type2index_map())); - sliceElements = pybind11::cast(std::move(db.slice_elements())); - rsrcInstTypes = pybind11::cast(std::move(db.rsrc_inst_types())); - lutFracturesMap = pybind11::cast(std::move(db.lut_fractures_map())); - siteOutCoordinates = pybind11::cast(std::move(db.site_out_coordinates())); - siteOutValues = pybind11::cast(std::move(db.site_out_values())); - - xl = db.xl(); - yl = db.yl(); - xh = db.xh(); - yh = db.yh(); - - //Initialize site_type2index values that are used - sliceIdx = INVALID; - ioIdx = INVALID; - bramIdx = INVALID; - m9kIdx = INVALID; - m144kIdx = INVALID; - dspIdx = INVALID; - pllIdx = INVALID; - emptyIdx = INVALID; - - if (db.site_type2index_map().find("SLICE") != db.site_type2index_map().end()) - { - sliceIdx = db.site_type2index("SLICE"); - } - if (db.site_type2index_map().find("io") != db.site_type2index_map().end()) - { - ioIdx = db.site_type2index("io"); - } else if(db.site_type2index_map().find("IO") != db.site_type2index_map().end()) - { - ioIdx = db.site_type2index("IO"); - } - if (db.site_type2index_map().find("BRAM") != db.site_type2index_map().end()) - { - bramIdx = db.site_type2index("BRAM"); - } - if (db.site_type2index_map().find("M9K") != db.site_type2index_map().end()) - { - m9kIdx = db.site_type2index("M9K"); - } - if (db.site_type2index_map().find("M144K") != db.site_type2index_map().end()) - { - m144kIdx = db.site_type2index("M144K"); - } - if (db.site_type2index_map().find("DSP") != db.site_type2index_map().end()) - { - dspIdx = db.site_type2index("DSP"); - } - if (db.site_type2index_map().find("PLL") != db.site_type2index_map().end()) - { - pllIdx = db.site_type2index("PLL"); - } - if (db.site_type2index_map().find("EMPTY") != db.site_type2index_map().end()) - { - emptyIdx = db.site_type2index("EMPTY"); - } - - typedef Box box_type; - std::vector > region_boxes(db.site_types().size()+1); - - int maxVal = std::max(db.siteRows(), db.siteCols()); - - for (int i = 0, ie = db.siteRows(); i < ie; ++i) - { - pybind11::list rowVals, lg_rowXY; - for (int j = 0, je = db.siteCols(); j < je; ++j) - { - pybind11::list siteXY, lg_Site; - - if (db.siteVal(i,j) == sliceIdx) - { - lg_Site.append(i+0.5); - lg_Site.append(j+0.5); - } else - { - lg_Site.append(i); - lg_Site.append(j); - } - lg_rowXY.append(lg_Site); - if(db.siteVal(i,j) != 0 && db.siteVal(i,j) != emptyIdx) - { - if (db.siteVal(i,j) == sliceIdx) - { - siteXY.append(i); - siteXY.append(j); - sliceSiteXYs.append(siteXY); - - int siteW = int(db.site_width(sliceIdx)); - int siteH = int(db.site_height(sliceIdx)); - Box slicebox(i, j, i+siteW, j+siteH); - - if (region_boxes[sliceIdx].size() == 0) - { - region_boxes[sliceIdx].emplace_back(slicebox); - } else - { - if (!mergeBoxes(slicebox, region_boxes[sliceIdx].back(), db.site_per_column())) - { - region_boxes[sliceIdx].emplace_back(slicebox); - } - } - } - else if (db.siteVal(i,j) == dspIdx || db.siteVal(i,j) == bramIdx || db.siteVal(i,j) == m9kIdx || db.siteVal(i,j) == m144kIdx) - { - double siteHeight = db.site_height(db.siteVal(i,j)); - siteXY.append(i); - - int siteW = int(db.site_width(db.siteVal(i,j))); - int siteH = int(siteHeight); - Box drbox(i, j, i+siteW, j+siteH); - - if (std::floor(siteHeight) == siteHeight) - { - siteXY.append(j); - } else - { - siteXY.append(std::round(j/siteHeight)*siteHeight); - drbox.set(i, int(std::round(j/siteHeight)*siteHeight), i+siteW, int(std::round((j+siteH)/siteHeight)*siteHeight)); - } - if (region_boxes[db.siteVal(i,j)].size() == 0) - { - region_boxes[db.siteVal(i,j)].emplace_back(drbox); - } else - { - if (!mergeBoxes(drbox, region_boxes[db.siteVal(i,j)].back(), db.site_per_column())) - { - region_boxes[db.siteVal(i,j)].emplace_back(drbox); - } - } - - if (db.siteVal(i,j) == dspIdx) - { - dspSiteXYs.append(siteXY); - } else if (db.siteVal(i,j) == bramIdx || db.siteVal(i,j) == m9kIdx) - { - ramSite0XYs.append(siteXY); - } else - { - ramSite1XYs.append(siteXY); - } - } else if (db.siteVal(i,j) == ioIdx) - { - int siteW = int(db.site_width(ioIdx)); - int siteH = int(db.site_height(ioIdx)); - Box iobox(i, j, i+siteW, j+siteH); - - if (region_boxes[ioIdx].size() == 0) - { - region_boxes[ioIdx].emplace_back(iobox); - } else - { - if (!mergeBoxes(iobox, region_boxes[ioIdx].back(), db.site_per_column())) - { - region_boxes[ioIdx].emplace_back(iobox); - } - } - } else if (db.siteVal(i,j) == pllIdx) - { - int siteW = int(db.site_width(pllIdx)); - int siteH = int(db.site_height(pllIdx)); - Box pllbox(i, j, i+siteW, j+siteH); - - if (region_boxes[pllIdx].size() == 0) - { - region_boxes[pllIdx].emplace_back(pllbox); - } else - { - if (!mergeBoxes(pllbox, region_boxes[pllIdx].back(), db.site_per_column())) - { - region_boxes[pllIdx].emplace_back(pllbox); - } - } - } - } - - rowVals.append(db.siteVal(i,j)); - } - site_type_map.append(rowVals); - lg_siteXYs.append(lg_rowXY); - } - - //Update flat_region_boxes and flat_region_boxes_start using region_boxes - unsigned int flat_len = 0; - flat_region_boxes_start.append(flat_len); - for (unsigned int rgn = 0; rgn < db.rsrc_types().size(); ++rgn) - { - int site_type = db.site_type2index(db.rsrc_type2site(db.rsrc_type(rgn))); - for (auto el : region_boxes[site_type]) - { - pybind11::list flat_region; - flat_region.append(el.xl()); - flat_region.append(el.yl()); - flat_region.append(el.xh()); - - if (db.site_per_column() == 1) - { - flat_region.append(yh); - } else - { - flat_region.append(el.yh()); - } - - flat_region_boxes.append(flat_region); - flat_len += 1; - } - flat_region_boxes_start.append(flat_len); - } - - - num_sites_x = db.siteRows(); - num_sites_y = db.siteCols(); - - // routing information initialized - num_routing_grids_x = db.width(); - num_routing_grids_y = db.height(); - routing_grid_xl = xl; - routing_grid_yl = yl; - routing_grid_xh = xh; - routing_grid_yh = yh; - - ff_ctrl_type = db.ff_ctrl_type(); - wl_weightX = db.wl_weight_x(); - wl_weightY = db.wl_weight_y(); - sliceFF_ctrl_mode = db.slice_ff_ctrl_mode(); - lut_maxShared = db.lut_shared_max_pins(); - lut_type_in_sliceUnit = db.lut_type_in_sliceUnit(); - pinRouteCap = db.pin_route_cap(); - routeCapH = db.route_cap_h(); - routeCapV = db.route_cap_v(); - - ////Spiral Accessor - unsigned int rad = std::max(num_sites_x, num_sites_y); - spiral_maxVal = (2 * rad * (1+rad)) +1; - spiral_accessor.append(std::make_tuple(0, 0)); - - for(int r = 1; r <= rad; ++r) - { - // The 1st quadrant - for (int x = r, y = 0; y < r; --x, ++y) - { - spiral_accessor.append(std::make_tuple(x, y)); - } - // The 2nd quadrant - for (int x = 0, y = r; y > 0; --x, --y) - { - spiral_accessor.append(std::make_tuple(x, y)); - } - // The 3rd quadrant - for (int x = -r, y = 0; y > -r; ++x, --y) - { - spiral_accessor.append(std::make_tuple(x, y)); - } - // The 4th quadrant - for (int x = 0, y = -r; y < 0; ++x, ++y) - { - spiral_accessor.append(std::make_tuple(x, y)); - } - } -} - -DREAMPLACE_END_NAMESPACE - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h deleted file mode 100644 index f434c09b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @file PyPlaceDB.h - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Mar 2021 - * @brief Placement database for python - */ - -#ifndef _DREAMPLACE_PLACE_IO_PYPLACEDB_H -#define _DREAMPLACE_PLACE_IO_PYPLACEDB_H - -//#include -#include -#include -#include -//#include -#include "PlaceDB.h" -#include "Iterators.h" -#include "Box.h" -#include "utility/src/torch.h" - -DREAMPLACE_BEGIN_NAMESPACE - -bool readBookshelf(PlaceDB& db, std::string const& auxPath); - -/// database for python -struct PyPlaceDB -{ - pybind11::list node_names; ///< 1D array, cell name - pybind11::list node_size_x; ///< 1D array, cell width - pybind11::list node_size_y; ///< 1D array, cell height - pybind11::list node_types; ///< 1D array, nodeTypes(FPGA) - pybind11::list flop_indices; ///< 1D array, nodeTypes(FPGA) - //pybind11::list lut_indices; ///< 1D array, nodeTypes(FPGA) - //pybind11::list flop_lut_indices; ///< 1D array, nodeTypes(FPGA) - //pybind11::list dsp_indices; ///< 1D array, nodeTypes(FPGA) - //pybind11::list ram_indices; ///< 1D array, nodeTypes(FPGA) - //pybind11::list dsp_ram_indices; ///< 1D array, nodeTypes(FPGA) - pybind11::list node2fence_region_map; ///< only record fence regions for each cell - - pybind11::list node_x; ///< 1D array, cell position x - pybind11::list node_y; ///< 1D array, cell position y - pybind11::list node_z; ///< 1D array, cell position z (FPGA) - pybind11::list node2pin_map; ///< array of 1D array, contains pin id of each node - pybind11::list flat_node2pin_map; ///< flatten version of node2pin_map - pybind11::list flat_node2pin_start_map; ///< starting index of each node in flat_node2pin_map - pybind11::list node2pincount_map; ///< array of 1D array, number of pins in node - pybind11::list net2pincount_map; ///< array of 1D array, number of pins in net - pybind11::list node2outpinIdx_map; ///< array of 1D array, output pin idx of each node - pybind11::list node2outpinCount; ///< array of 1D array, output pin count of each node - pybind11::list lut_type; ///< 1D array, nodeTypes(FPGA) - pybind11::list cluster_lut_type; ///< 1D array, LUT types for clustering - pybind11::dict node_name2id_map; ///< node name to id map, cell name - - //pybind11::dict movable_node_name2id_map; ///< node name to id map, cell name - //pybind11::dict fixed_node_name2id_map; ///< node name to id map, cell name - //pybind11::list fixedNodes; ///< 1D array, nodeTypes(FPGA) - unsigned int num_terminals; ///< number of terminals, essentially IOs - unsigned int num_movable_nodes; ///< number of movable nodes - unsigned int num_physical_nodes; ///< number of movable nodes + terminals (FPGA) - pybind11::list node_count; ///< 1D array, count of resource types - - unsigned int num_ccNodes; ///< number of carry chain nodes in design (FPGA) - pybind11::list cc_element_count; ///< No of elements in all the carry chains - pybind11::list is_cc_node; ///< Specifies if node is part of carry chain - pybind11::list node2ccId_map; ///< Node id to carry chain indexing - pybind11::list cc2nodeId_map; ///< carry chain id to node indexing - //pybind11::list flat_cc2node_map; ///< flat cc2node map - //pybind11::list flat_cc2node_start_map; ///< flat cc2node start map - //pybind11::list carry_chain_nets; ///< cc nets - //unsigned int num_carry_chains; ///< number of carry chain nodes in design (FPGA) - - unsigned int org_num_movable_nodes; ///< number of movable nodes - pybind11::dict org_node_name2id_map; ///< node name to id map, cell name - pybind11::list org_node_names; ///< 1D array, cell name - pybind11::list org_node_types; ///< 1D array, nodeTypes(FPGA) - pybind11::list org_node_size_x; ///< 1D array, cell width - pybind11::list org_node_size_y; ///< 1D array, cell width - pybind11::list org_node2fence_region_map; ///< only record fence regions for each cell - pybind11::list org_node_count; ///< count based on node type - pybind11::list org_flop_indices; ///< 1D array, nodeTypes(FPGA) - pybind11::list org_lut_type; ///< 1D array, nodeTypes(FPGA) - pybind11::list org_pin_offset_x; ///< 1D array, pin offset x to its node - pybind11::list org_pin_offset_y; ///< 1D array, pin offset y to its node - pybind11::list org_pin2nodeType_map; ///< 1D array, pin to node type - pybind11::list org_node2pincount_map; ///< array of 1D array, number of pins in node - pybind11::list org_pin2node_map; ///< 1D array, contain parent node id of each pin - pybind11::list org_node2outpinCount; ///< array of 1D array, output pin count of each node - pybind11::list org_node2outpinIdx_map; ///< array of 1D array, output pin idx of each node - pybind11::list org_flat_node2pin_map; ///< flatten version of node2pin_map - pybind11::list org_flat_node2pin_start_map; ///< starting index of each node in flat_node2pin_map - pybind11::list org_flat_cc2node_map; ///< flat cc2node map - pybind11::list org_flat_cc2node_start_map; ///< flat cc2node start map - pybind11::list org_is_cc_node; ///< Specifies if node is part of carry chain - pybind11::list org_node2ccId_map; ///< Node id to carry chain indexing - pybind11::list org_node_x; ///< 1D array, cell position x - pybind11::list org_node_y; ///< 1D array, cell position y - pybind11::list org_node_z; ///< 1D array, cell position z (FPGA) - pybind11::list new2org_node_map; ///< Node id to org node id mapping - pybind11::list org_ctrlSets; ///< 1D array, FF ctrl set (FPGA) - pybind11::list org_extended_ctrlSets; ///< 1D array, FF ctrl signals (FPGA) - pybind11::list org_ext_ctrlSet_start_map; ///< 1D array, FF ctrl set start map (FPGA) - //pybind11::list flat_cc_input_pins_map; ///< flat carry chain node input pin mapping - //pybind11::list flat_cc_output_pins_map; ///< flat carry chain node output pin mapping - //pybind11::list flat_cc_input_pin_start_map; ///< flat carry chain node input pin mapping - //pybind11::list flat_cc_output_pin_start_map; ///< flat carry chain node output pin mapping - //pybind11::list overall_cc_input_pin_start_map; ///< overall carry chain node input pin mapping - //pybind11::list overall_cc_output_pin_start_map; ///< overall carry chain node output pin mapping - - pybind11::list pin_offset_x; ///< 1D array, pin offset x to its node - pybind11::list pin_offset_y; ///< 1D array, pin offset y to its node - pybind11::list pin_names; ///< 1D array, pin names (FPGA) - pybind11::list pin_types; ///< 1D array, pin types (FPGA) - pybind11::list pin_typeIds; ///< 1D array, pin types (FPGA) - pybind11::list pin2node_map; ///< 1D array, contain parent node id of each pin - pybind11::list pin2net_map; ///< 1D array, contain parent net id of each pin - pybind11::list pin2nodeType_map; ///< 1D array, pin to node type - - pybind11::list net_names; ///< net name - pybind11::list net2pin_map; ///< array of 1D array, each row stores pin id - pybind11::list flat_net2pin_map; ///< flatten version of net2pin_map - pybind11::list flat_net2pin_start_map; ///< starting index of each net in flat_net2pin_map - pybind11::dict net_name2id_map; ///< net name to id map - //pybind11::list net_weights; ///< net weight - - int num_sites_x; ///< number of sites in horizontal direction (FPGA) - int num_sites_y; ///< number of sites in vertical direction (FPGA) - pybind11::list siteTypes; ///< 1D array of site types - pybind11::list siteWidths; ///< 1D array of site widths - pybind11::list siteHeights; ///< 1D array of site heights - pybind11::list rsrcTypes; ///< 1D array of rsrc types - pybind11::list rsrcInstWidths; ///< 1D array of rsrc Inst widths - pybind11::list rsrcInstHeights; ///< 1D array of rsrc Inst heights - pybind11::list siteResources; ///< 2D array of site resources - pybind11::list rsrcInsts; ///< 2D array of resource instances - pybind11::list rsrcInstTypes; ///< 1D array of resource instances - pybind11::dict rsrc2siteMap; ///< rsrc to site map - pybind11::dict inst2rsrcMap; ///< inst to rsrc map - pybind11::dict siteRsrc2CountMap; ///< site rsrc to count map - pybind11::dict siteType2indexMap; ///< site type to id map - pybind11::dict rsrcType2indexMap; ///< rsrc type to id map - pybind11::dict rsrcInstType2indexMap; ///< rsrc inst type to id map - pybind11::list sliceElements; ///< 1D array of pairs - rsrc type and count - pybind11::list lutFracturesMap; ///< 2D array of LUT fractures - pybind11::list sliceFFCtrls; ///< 1D array of pairs - slice ff ctrls - pybind11::list sliceUnitFFCtrls; ///< 1D array of pairs - slice unit ff ctrls - pybind11::list siteOutCoordinates; ///< 1D array of site output coordinates - pybind11::list siteOutValues; ///< 1D array of site output values - pybind11::list site_type_map; ///< 2D array, site type of each site (FPGA) - pybind11::list lg_siteXYs; ///< 2D array, site XYs for CLB at center (FPGA) - //pybind11::list regions; ///< array of 1D array, each region contains rectangles - pybind11::list dspSiteXYs; ///< 1D array of DSP sites (FPGA) - pybind11::list ramSite0XYs; ///< 1D array of RAM sites (FPGA) - pybind11::list ramSite1XYs; ///< 1D array of RAM sites (FPGA) - pybind11::list sliceSiteXYs; ///< 1D array of Slice sites (FPGA) - //pybind11::list regionsLimits; ///< array of 1D array, each region contains rectangles - pybind11::list flat_region_boxes; ///< flatten version of regions - pybind11::list flat_region_boxes_start; ///< starting index of each region in flat_region_boxes - - pybind11::list spiral_accessor; ///< spiral accessor - - pybind11::list ctrlSets; ///< 1D array, FF ctrl set (FPGA) - pybind11::list extended_ctrlSets; ///< 1D array, FF ctrl signals (FPGA) - pybind11::list ext_ctrlSet_start_map; ///< 1D array, FF ctrl set start map (FPGA) - //pybind11::list flat_ctrlSets; ///< 1D array, FF ctrl set (FPGA) - //unsigned int num_nodes; ///< number of nodes, including terminals and terminal_NIs - unsigned int spiral_maxVal; ///< maxVal in spiral_accessor - unsigned int num_routing_grids_x; ///< number of routing grids in x - unsigned int num_routing_grids_y; ///< number of routing grids in y - int routing_grid_xl; ///< routing grid region may be different from placement region - int routing_grid_yl; - int routing_grid_xh; - int routing_grid_yh; - int xl; - int yl; - int xh; - int yh; - - std::string ff_ctrl_type; - double wl_weightX; - double wl_weightY; - std::string sliceFF_ctrl_mode; - int lut_maxShared; - int lut_type_in_sliceUnit; - int pinRouteCap; - int routeCapH; - int routeCapV; - - //Site type Identifier - int sliceIdx; - int ioIdx; - int bramIdx; - int m9kIdx; - int m144kIdx; - int dspIdx; - int pllIdx; - int emptyIdx; - - //pybind11::list node2orig_node_map; ///< due to some fixed nodes may have non-rectangular shapes, we flat the node list; - // ///< this map maps the new indices back to the original ones - //pybind11::list pin_direct; ///< 1D array, pin direction IO - //pybind11::list rows; ///< NumRows x 4 array, stores xl, yl, xh, yh of each row - //pybind11::list node_count; ///< Node count based on resource type (FPGA) - //pybind11::list unit_horizontal_capacities; ///< number of horizontal tracks of layers per unit distance - //pybind11::list unit_vertical_capacities; /// number of vertical tracks of layers per unit distance - //pybind11::list initial_horizontal_demand_map; ///< initial routing demand from fixed cells, indexed by (layer, grid x, grid y) - //pybind11::list initial_vertical_demand_map; ///< initial routing demand from fixed cells, indexed by (layer, grid x, grid y) - //pybind11::list binCapMaps; ///< array of 2D array, Bin Capacity map for all resource types (FPGA) - //pybind11::list fixedDemandMaps; ///< array of 2D array, Bin Capacity map for all resource types (FPGA) - //double total_space_area; ///< total placeable space area excluding fixed cells. - // ///< This is not the exact area, because we cannot exclude the overlapping fixed cells within a bin. - //int num_movable_pins; - - PyPlaceDB() - { - } - - PyPlaceDB(PlaceDB const& db) - { - set(db); - } - - void set(PlaceDB const& db); -}; - -DREAMPLACE_END_NAMESPACE - -#endif - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp deleted file mode 100644 index 20fcbd79..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/** - * @file PybindPlaceDB.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Mar 2021 - * @brief Python binding for PlaceDB - */ - -#include "PyPlaceDB.h" - -PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector >); -//PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector>); - -PYBIND11_MAKE_OPAQUE(DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type); - -PYBIND11_MAKE_OPAQUE(std::vector>); -PYBIND11_MAKE_OPAQUE(std::vector>); -//PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); -PYBIND11_MAKE_OPAQUE(std::vector); -//PYBIND11_MAKE_OPAQUE(std::vector); - -void bind_PlaceDB(pybind11::module& m) -{ - pybind11::bind_vector >(m, "VectorBool"); - pybind11::bind_vector >(m, "VectorCoordinate", pybind11::buffer_protocol()); - pybind11::bind_vector >(m, "VectorIndex", pybind11::buffer_protocol()); - //pybind11::bind_vector > >(m, "2DVectorIndex", pybind11::buffer_protocol()); - //pybind11::bind_vector >(m, "VectorLong", pybind11::buffer_protocol()); - //pybind11::bind_vector >(m, "VectorULong", pybind11::buffer_protocol()); - //pybind11::bind_vector >(m, "VectorFloat", pybind11::buffer_protocol()); - //pybind11::bind_vector >(m, "VectorDouble", pybind11::buffer_protocol()); - pybind11::bind_vector >(m, "VectorString"); - - pybind11::bind_map(m, "MapString2Index"); - - // DREAMPLACE_NAMESPACE::Object.h - pybind11::class_ (m, "Object") - .def(pybind11::init<>()) - .def("id", &DREAMPLACE_NAMESPACE::Object::id) - .def("__str__", &DREAMPLACE_NAMESPACE::Object::toString) - ; - - // Box.h - pybind11::class_> (m, "BoxCoordinate") - .def(pybind11::init<>()) - .def(pybind11::init::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type>()) - .def("xl", &DREAMPLACE_NAMESPACE::Box::xl) - .def("yl", &DREAMPLACE_NAMESPACE::Box::yl) - .def("xh", &DREAMPLACE_NAMESPACE::Box::xh) - .def("yh", &DREAMPLACE_NAMESPACE::Box::yh) - .def("width", &DREAMPLACE_NAMESPACE::Box::width) - .def("height", &DREAMPLACE_NAMESPACE::Box::height) - .def("area", &DREAMPLACE_NAMESPACE::Box::area) - .def("__str__", &DREAMPLACE_NAMESPACE::Box::toString) - ; - pybind11::class_> (m, "BoxIndex") - .def(pybind11::init<>()) - .def(pybind11::init::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type, DREAMPLACE_NAMESPACE::Box::coordinate_type>()) - .def("xl", &DREAMPLACE_NAMESPACE::Box::xl) - .def("yl", &DREAMPLACE_NAMESPACE::Box::yl) - .def("xh", &DREAMPLACE_NAMESPACE::Box::xh) - .def("yh", &DREAMPLACE_NAMESPACE::Box::yh) - .def("width", &DREAMPLACE_NAMESPACE::Box::width) - .def("height", &DREAMPLACE_NAMESPACE::Box::height) - .def("area", &DREAMPLACE_NAMESPACE::Box::area) - .def("__str__", &DREAMPLACE_NAMESPACE::Box::toString) - ; - pybind11::bind_vector> >(m, "VectorBoxCoordinate"); - pybind11::bind_vector> >(m, "VectorBoxIndex"); - - // DREAMPLACE_NAMESPACE::LibCell.h - pybind11::class_ (m, "LibCell") - .def(pybind11::init<>()) - .def("name", &DREAMPLACE_NAMESPACE::LibCell::name) - .def("id", &DREAMPLACE_NAMESPACE::LibCell::id) - .def("inputPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::inputPinArray) - .def("outputPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::outputPinArray) - .def("clkPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::clkPinArray) - .def("ctrlPinArray", (std::vector const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::ctrlPinArray) - .def("libCellPinName2Type", (DREAMPLACE_NAMESPACE::LibCell::string2index_map_type const& (DREAMPLACE_NAMESPACE::LibCell::*)() const) &DREAMPLACE_NAMESPACE::LibCell::libCellPinName2Type) - ; - pybind11::bind_vector >(m, "VectorLibCell"); - - // DREAMPLACE_NAMESPACE::PlaceDB.h - pybind11::class_ (m, "PlaceDB") - .def(pybind11::init<>()) - .def("nodeNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeNames) - .def("nodeName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeName) - .def("nodeTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeTypes) - .def("nodeType", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeType) - .def("nodeXLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXLocs) - .def("nodeX", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeX) - .def("nodeYLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYLocs) - .def("nodeY", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeY) - .def("nodeZLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeZLocs) - .def("nodeZ", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeZ) - .def("nodeXSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXSizes) - .def("nodeXSize", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeXSize) - .def("nodeYSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYSizes) - .def("nodeYSize", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeYSize) - .def("node2FenceRegionMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2FenceRegionMap) - .def("nodeFenceRegion", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeFenceRegion) - .def("node2OutPinId", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2OutPinId) - .def("node2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinCount) - .def("node2PinCnt", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinCnt) - .def("flopIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flopIndices) - .def("flopIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::flopIndex) - .def("lutIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lutIndices) - .def("lutIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::lutIndex) - .def("lutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lutTypes) - .def("clusterlutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::clusterlutTypes) - .def("node2OutPinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2OutPinCount) - .def("orgflatCCNodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgflatCCNodeMap) - .def("orgflatCCNodeStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgflatCCNodeStartMap) - .def("ccElementCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::ccElementCount) - .def("node2CCIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2CCIdMap) - .def("cc2nodeIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::cc2nodeIdMap) - .def("isCCNode", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::isCCNode) - //.def("flatCCInputPinsMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCInputPinsMap) - //.def("flatCCOutputPinsMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCOutputPinsMap) - //.def("flatCCInputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCInputPinStartMap) - //.def("flatCCOutputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatCCOutputPinStartMap) - //.def("overallCCInputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::overallCCInputPinStartMap) - //.def("overallCCOutputPinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::overallCCOutputPinStartMap) - .def("node2PinMap", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinMap) - .def("node2PinIdx", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::node2PinIdx) - .def("netNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::netNames) - .def("netName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::netName) - .def("net2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinCount) - .def("net2PinCnt", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinCnt) - .def("net2PinMap", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinMap) - .def("net2PinIdx", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::net2PinIdx) - .def("flatNet2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNet2PinMap) - .def("flatNet2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNet2PinStartMap) - .def("flatNode2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNode2PinMap) - .def("flatNode2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::flatNode2PinStartMap) - .def("pinNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinNames) - .def("pinName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::pinName) - .def("pin2NetMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NetMap) - .def("pin2NodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NodeMap) - .def("pin2Node", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2Node) - .def("pin2NodeTypeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pin2NodeTypeMap) - .def("pinTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinTypes) - .def("pinTypeIds", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinTypeIds) - .def("pinOffsetX", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinOffsetX) - .def("pinOffsetY", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::pinOffsetY) - .def("orgNodeNames", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeNames) - .def("orgNodeName", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeName) - .def("orgNodeTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeTypes) - .def("orgNodeType", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeType) - .def("orgNodeXSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeXSizes) - .def("orgNodeYSizes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeYSizes) - .def("orgFlatNode2PinMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlatNode2PinMap) - .def("orgFlatNode2PinStartMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlatNode2PinStartMap) - .def("orgPin2NodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPin2NodeMap) - .def("orgPin2NodeTypeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPin2NodeTypeMap) - .def("orgNode2OutPinId", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2OutPinId) - .def("orgNode2PinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2PinCount) - .def("orgFlopIndices", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlopIndices) - .def("orgFlopIndex", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgFlopIndex) - .def("orgLutTypes", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgLutTypes) - .def("orgNode2OutPinCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2OutPinCount) - .def("orgNode2FenceRegionMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2FenceRegionMap) - .def("orgNodeCount", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeCount) - .def("orgNode2CCIdMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNode2CCIdMap) - .def("orgNodeMap", (int const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeMap) - .def("isOrgCCNode", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::isOrgCCNode) - .def("orgPinOffsetX", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPinOffsetX) - .def("orgPinOffsetY", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgPinOffsetY) - .def("orgNodeName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeName2Index) - .def("orgNodeXLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeXLocs) - .def("orgNodeYLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeYLocs) - .def("orgNodeZLocs", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::orgNodeZLocs) - .def("new2OrgNodeMap", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::new2OrgNodeMap) - .def("libCells", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::libCells) - .def("libCell", (DREAMPLACE_NAMESPACE::LibCell const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::libCell) - .def("siteRows", &DREAMPLACE_NAMESPACE::PlaceDB::siteRows) - .def("siteCols", &DREAMPLACE_NAMESPACE::PlaceDB::siteCols) - .def("siteVal", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::siteVal) - .def("dieArea", &DREAMPLACE_NAMESPACE::PlaceDB::dieArea) - .def("nodeName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::nodeName2Index) - .def("libCellName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::libCellName2Index) - .def("netName2Index", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::netName2Index) - .def("site_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_types) - .def("site_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type) - .def("site_resources_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_resources_map) - .def("site_resource", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_resource) - .def("rsrc2site_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2string_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc2site_map) - .def("site_rsrc2count_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_rsrc2count_map) - .def("rsrc_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_types) - .def("rsrc_insts_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_insts_map) - .def("inst2rsrc_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2string_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::inst2rsrc_map) - .def("site_per_column", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_per_column) - .def("site_widths", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_widths) - .def("site_width", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_width) - .def("site_heights", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_heights) - .def("site_height", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_height) - .def("rsrc_inst_types", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_types) - .def("rsrc_inst_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_type) - .def("site_out_coordinates", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_coordinates) - .def("site_out_coordinate", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_coordinate) - .def("site_out_values", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_values) - .def("site_out_value", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_out_value) - .def("rsrc_inst_widths", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_widths) - .def("rsrc_inst_width", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_width) - .def("rsrc_inst_heights", (std::vector const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_heights) - .def("rsrc_inst_height", (double const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_height) - .def("lut_fractures_map", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::lut_fractures_map) - .def("lut_fracture", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type, DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::lut_fracture) - .def("site_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type2index_map) - .def("site_type2index", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(std::string) const) &DREAMPLACE_NAMESPACE::PlaceDB::site_type2index) - .def("rsrc_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_type2index_map) - .def("rsrc_type2index", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(std::string) const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_type2index) - .def("rsrc_inst_type2index_map", (DREAMPLACE_NAMESPACE::PlaceDB::string2index_map_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::rsrc_inst_type2index_map) - .def("slice_elements", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_elements) - .def("slice_FF_ctrls", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrls) - .def("slice_FF_ctrl_signal", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrl_signal) - .def("slice_FF_ctrl_count", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::slice_FF_ctrl_count) - .def("sliceUnit_FF_ctrls", (std::vector > const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrls) - .def("sliceUnit_FF_ctrl_signal", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrl_signal) - .def("sliceUnit_FF_ctrl_count", (DREAMPLACE_NAMESPACE::PlaceDB::index_type const& (DREAMPLACE_NAMESPACE::PlaceDB::*)(DREAMPLACE_NAMESPACE::PlaceDB::index_type) const) &DREAMPLACE_NAMESPACE::PlaceDB::sliceUnit_FF_ctrl_count) - .def("ff_ctrl_type", (std::string const& (DREAMPLACE_NAMESPACE::PlaceDB::*)() const) &DREAMPLACE_NAMESPACE::PlaceDB::ff_ctrl_type) - .def("wl_weight_x", &DREAMPLACE_NAMESPACE::PlaceDB::wl_weight_x) - .def("wl_weight_y", &DREAMPLACE_NAMESPACE::PlaceDB::wl_weight_y) - .def("slice_ff_ctrl_mode", &DREAMPLACE_NAMESPACE::PlaceDB::slice_ff_ctrl_mode) - .def("lut_shared_max_pins", &DREAMPLACE_NAMESPACE::PlaceDB::lut_shared_max_pins) - .def("lut_type_in_sliceUnit", &DREAMPLACE_NAMESPACE::PlaceDB::lut_type_in_sliceUnit) - .def("pin_route_cap", &DREAMPLACE_NAMESPACE::PlaceDB::pin_route_cap) - .def("route_cap_h", &DREAMPLACE_NAMESPACE::PlaceDB::route_cap_h) - .def("route_cap_v", &DREAMPLACE_NAMESPACE::PlaceDB::route_cap_v) - .def("numMovable", &DREAMPLACE_NAMESPACE::PlaceDB::numMovable) - .def("numOrgMovable", &DREAMPLACE_NAMESPACE::PlaceDB::numOrgMovable) - .def("numFixed", &DREAMPLACE_NAMESPACE::PlaceDB::numFixed) - .def("numLibCell", &DREAMPLACE_NAMESPACE::PlaceDB::numLibCell) - .def("numLUT", &DREAMPLACE_NAMESPACE::PlaceDB::numLUT) - .def("numFF", &DREAMPLACE_NAMESPACE::PlaceDB::numFF) - .def("numCCNodes", &DREAMPLACE_NAMESPACE::PlaceDB::numCCNodes) - .def("numNets", &DREAMPLACE_NAMESPACE::PlaceDB::numNets) - .def("numPins", &DREAMPLACE_NAMESPACE::PlaceDB::numPins) - .def("designName", &DREAMPLACE_NAMESPACE::PlaceDB::designName) - .def("xl", &DREAMPLACE_NAMESPACE::PlaceDB::xl) - .def("yl", &DREAMPLACE_NAMESPACE::PlaceDB::yl) - .def("xh", &DREAMPLACE_NAMESPACE::PlaceDB::xh) - .def("yh", &DREAMPLACE_NAMESPACE::PlaceDB::yh) - .def("width", &DREAMPLACE_NAMESPACE::PlaceDB::width) - .def("height", &DREAMPLACE_NAMESPACE::PlaceDB::height) - ; -} - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp deleted file mode 100644 index 7627bbfc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @file PybindPyPlaceDB.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Mar 2021 - * @brief Python binding for PyPlaceDB - */ - -#include "PyPlaceDB.h" - -void bind_PyPlaceDB(pybind11::module& m) -{ - pybind11::class_(m, "PyPlaceDB") - .def(pybind11::init<>()) - .def_readwrite("node_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_names) - .def_readwrite("node_size_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_size_x) - .def_readwrite("node_size_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_size_y) - .def_readwrite("node_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_types) - .def_readwrite("flop_indices", &DREAMPLACE_NAMESPACE::PyPlaceDB::flop_indices) - .def_readwrite("node2fence_region_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2fence_region_map) - .def_readwrite("node_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_x) - .def_readwrite("node_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_y) - .def_readwrite("node_z", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_z) - .def_readwrite("node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2pin_map) - .def_readwrite("flat_node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_node2pin_map) - .def_readwrite("flat_node2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_node2pin_start_map) - .def_readwrite("node2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2pincount_map) - .def_readwrite("net2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net2pincount_map) - .def_readwrite("node2outpinIdx_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2outpinIdx_map) - .def_readwrite("node2outpinCount", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2outpinCount) - .def_readwrite("lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_type) - .def_readwrite("cluster_lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::cluster_lut_type) - .def_readwrite("node_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_name2id_map) - .def_readwrite("num_terminals", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_terminals) - .def_readwrite("num_movable_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_movable_nodes) - .def_readwrite("num_physical_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_physical_nodes) - .def_readwrite("node_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::node_count) - .def_readwrite("num_ccNodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_ccNodes) - .def_readwrite("cc_element_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::cc_element_count) - .def_readwrite("is_cc_node", &DREAMPLACE_NAMESPACE::PyPlaceDB::is_cc_node) - .def_readwrite("node2ccId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::node2ccId_map) - .def_readwrite("cc2nodeId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::cc2nodeId_map) - //.def_readwrite("flat_cc2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc2node_map) - //.def_readwrite("flat_cc2node_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc2node_start_map) - //.def_readwrite("carry_chain_nets", &DREAMPLACE_NAMESPACE::PyPlaceDB::carry_chain_nets) - //.def_readwrite("num_carry_chains", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_carry_chains) - .def_readwrite("org_node_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_name2id_map) - .def_readwrite("org_num_movable_nodes", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_num_movable_nodes) - .def_readwrite("org_node_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_names) - .def_readwrite("org_node_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_types) - .def_readwrite("org_node_size_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_size_x) - .def_readwrite("org_node_size_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_size_y) - .def_readwrite("org_node_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_x) - .def_readwrite("org_node_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_y) - .def_readwrite("org_node_z", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_z) - .def_readwrite("org_node2fence_region_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2fence_region_map) - .def_readwrite("org_node_count", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node_count) - .def_readwrite("org_flop_indices", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flop_indices) - .def_readwrite("org_lut_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_lut_type) - .def_readwrite("org_pin_offset_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin_offset_x) - .def_readwrite("org_pin_offset_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin_offset_y) - .def_readwrite("org_pin2nodeType_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin2nodeType_map) - .def_readwrite("org_node2pincount_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2pincount_map) - .def_readwrite("org_pin2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_pin2node_map) - .def_readwrite("org_node2outpinCount", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2outpinCount) - .def_readwrite("org_node2outpinIdx_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2outpinIdx_map) - .def_readwrite("org_flat_node2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_node2pin_map) - .def_readwrite("org_flat_node2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_node2pin_start_map) - .def_readwrite("org_flat_cc2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_cc2node_map) - .def_readwrite("org_flat_cc2node_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_flat_cc2node_start_map) - .def_readwrite("org_node2ccId_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_node2ccId_map) - .def_readwrite("org_is_cc_node", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_is_cc_node) - .def_readwrite("org_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_ctrlSets) - .def_readwrite("org_extended_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_extended_ctrlSets) - .def_readwrite("org_ext_ctrlSet_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::org_ext_ctrlSet_start_map) - .def_readwrite("new2org_node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::new2org_node_map) - //.def_readwrite("flat_cc_input_pins_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_input_pins_map) - //.def_readwrite("flat_cc_output_pins_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_output_pins_map) - //.def_readwrite("flat_cc_input_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_input_pin_start_map) - //.def_readwrite("flat_cc_output_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_cc_output_pin_start_map) - //.def_readwrite("overall_cc_input_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::overall_cc_input_pin_start_map) - //.def_readwrite("overall_cc_output_pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::overall_cc_output_pin_start_map) - .def_readwrite("pin_offset_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_offset_x) - .def_readwrite("pin_offset_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_offset_y) - .def_readwrite("pin2nodeType_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2nodeType_map) - .def_readwrite("pin_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_names) - .def_readwrite("pin_types", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_types) - .def_readwrite("pin_typeIds", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin_typeIds) - .def_readwrite("pin2net_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2net_map) - .def_readwrite("pin2node_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::pin2node_map) - .def_readwrite("net_names", &DREAMPLACE_NAMESPACE::PyPlaceDB::net_names) - .def_readwrite("net2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net2pin_map) - .def_readwrite("flat_net2pin_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_net2pin_map) - .def_readwrite("flat_net2pin_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_net2pin_start_map) - .def_readwrite("net_name2id_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::net_name2id_map) - .def_readwrite("num_sites_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_sites_x) - .def_readwrite("num_sites_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_sites_y) - .def_readwrite("siteTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteTypes) - .def_readwrite("siteWidths", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteWidths) - .def_readwrite("siteHeights", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteHeights) - .def_readwrite("rsrcTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcTypes) - .def_readwrite("rsrcInstWidths", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstWidths) - .def_readwrite("rsrcInstHeights", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstHeights) - .def_readwrite("siteResources", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteResources) - .def_readwrite("rsrcInsts", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInsts) - .def_readwrite("rsrcInstTypes", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstTypes) - .def_readwrite("siteRsrc2CountMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteRsrc2CountMap) - .def_readwrite("rsrc2siteMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrc2siteMap) - .def_readwrite("inst2rsrcMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::inst2rsrcMap) - .def_readwrite("siteType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteType2indexMap) - .def_readwrite("rsrcType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcType2indexMap) - .def_readwrite("rsrcInstType2indexMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::rsrcInstType2indexMap) - .def_readwrite("sliceElements", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceElements) - .def_readwrite("lutFracturesMap", &DREAMPLACE_NAMESPACE::PyPlaceDB::lutFracturesMap) - .def_readwrite("sliceFFCtrls", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceFFCtrls) - .def_readwrite("sliceUnitFFCtrls", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceUnitFFCtrls) - .def_readwrite("siteOutCoordinates", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteOutCoordinates) - .def_readwrite("siteOutValues", &DREAMPLACE_NAMESPACE::PyPlaceDB::siteOutValues) - .def_readwrite("site_type_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::site_type_map) - .def_readwrite("lg_siteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::lg_siteXYs) - .def_readwrite("sliceSiteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceSiteXYs) - .def_readwrite("dspSiteXYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::dspSiteXYs) - .def_readwrite("ramSite0XYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::ramSite0XYs) - .def_readwrite("ramSite1XYs", &DREAMPLACE_NAMESPACE::PyPlaceDB::ramSite1XYs) - .def_readwrite("flat_region_boxes", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_region_boxes) - .def_readwrite("flat_region_boxes_start", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_region_boxes_start) - .def_readwrite("spiral_accessor", &DREAMPLACE_NAMESPACE::PyPlaceDB::spiral_accessor) - .def_readwrite("spiral_maxVal", &DREAMPLACE_NAMESPACE::PyPlaceDB::spiral_maxVal) - .def_readwrite("ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::ctrlSets) - .def_readwrite("extended_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::extended_ctrlSets) - .def_readwrite("ext_ctrlSet_start_map", &DREAMPLACE_NAMESPACE::PyPlaceDB::ext_ctrlSet_start_map) - //.def_readwrite("flat_ctrlSets", &DREAMPLACE_NAMESPACE::PyPlaceDB::flat_ctrlSets) - .def_readwrite("num_routing_grids_x", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_routing_grids_x) - .def_readwrite("num_routing_grids_y", &DREAMPLACE_NAMESPACE::PyPlaceDB::num_routing_grids_y) - .def_readwrite("routing_grid_xl", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_xl) - .def_readwrite("routing_grid_yl", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_yl) - .def_readwrite("routing_grid_xh", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_xh) - .def_readwrite("routing_grid_yh", &DREAMPLACE_NAMESPACE::PyPlaceDB::routing_grid_yh) - .def_readwrite("xl", &DREAMPLACE_NAMESPACE::PyPlaceDB::xl) - .def_readwrite("yl", &DREAMPLACE_NAMESPACE::PyPlaceDB::yl) - .def_readwrite("xh", &DREAMPLACE_NAMESPACE::PyPlaceDB::xh) - .def_readwrite("yh", &DREAMPLACE_NAMESPACE::PyPlaceDB::yh) - .def_readwrite("ff_ctrl_type", &DREAMPLACE_NAMESPACE::PyPlaceDB::ff_ctrl_type) - .def_readwrite("wl_weightX", &DREAMPLACE_NAMESPACE::PyPlaceDB::wl_weightX) - .def_readwrite("wl_weightY", &DREAMPLACE_NAMESPACE::PyPlaceDB::wl_weightY) - .def_readwrite("sliceFF_ctrl_mode", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceFF_ctrl_mode) - .def_readwrite("lut_maxShared", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_maxShared) - .def_readwrite("lut_type_in_sliceUnit", &DREAMPLACE_NAMESPACE::PyPlaceDB::lut_type_in_sliceUnit) - .def_readwrite("pinRouteCap", &DREAMPLACE_NAMESPACE::PyPlaceDB::pinRouteCap) - .def_readwrite("routeCapH", &DREAMPLACE_NAMESPACE::PyPlaceDB::routeCapH) - .def_readwrite("routeCapV", &DREAMPLACE_NAMESPACE::PyPlaceDB::routeCapV) - .def_readwrite("sliceIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::sliceIdx) - .def_readwrite("ioIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::ioIdx) - .def_readwrite("bramIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::bramIdx) - .def_readwrite("m9kIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::m9kIdx) - .def_readwrite("m144kIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::m144kIdx) - .def_readwrite("dspIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::dspIdx) - .def_readwrite("pllIdx", &DREAMPLACE_NAMESPACE::PyPlaceDB::pllIdx) - ; - -} - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp deleted file mode 100644 index 7001295b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file Region.cpp - * @author Yibo Lin (DREAMPlace) - * @date Dec 2019 - */ -#include "Region.h" - -DREAMPLACE_BEGIN_NAMESPACE - -Region::Region() - : Region::base_type() - , m_vBox() - , m_name("") - , m_type(RegionTypeEnum::UNKNOWN) -{ -} -Region::Region(Region const& rhs) - : Region::base_type(rhs) -{ - copy(rhs); -} -Region& Region::operator=(Region const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -void Region::copy(Region const& rhs) -{ - m_vBox = rhs.m_vBox; - m_name = rhs.m_name; - m_type = rhs.m_type; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h deleted file mode 100644 index 24824df0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file Region.h - * @author Yibo Lin (DREAMPlace) - * @mail yibolin@pku.edu.cn - * @date Dec 2019 - */ - -#ifndef DREAMPLACE_REGION_H -#define DREAMPLACE_REGION_H - -#include -#include "Object.h" -#include "Box.h" -#include "Enums.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// class Region denotes a region like fence or guide -class Region : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - typedef Box box_type; - typedef coordinate_traits::index_type index_type; - typedef Point point_type; - - /// default constructor - Region(); - /// copy constructor - Region(Region const& rhs); - /// assignment - Region& operator=(Region const& rhs); - - /// member functions - std::vector const& boxes() const {return m_vBox;} - std::vector& boxes() {return m_vBox;} - Region& addBox(box_type const& box) {m_vBox.push_back(box); return *this;} - Region& setBox(index_type i, box_type const& box) {m_vBox.at(i) = box; return *this;} - - std::string const& name() const {return m_name;} - Region& setName(std::string const& name) {m_name = name; return *this;} - - RegionTypeEnum::RegionEnumType type() const {return (RegionTypeEnum::RegionEnumType)m_type;} - Region& setType(RegionTypeEnum::RegionEnumType t) {m_type = t; return *this;} - Region& setType(RegionType const& t) {return setType(t.value());} - - protected: - void copy(Region const& rhs); - - std::vector m_vBox; ///< rectangles for the region - std::string m_name; ///< region name - char m_type; ///< region type -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h deleted file mode 100644 index f572af5a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h +++ /dev/null @@ -1,239 +0,0 @@ -/************************************************************************* - > File Name: Row.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Wed Jun 17 21:54:17 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_ROW_H -#define DREAMPLACE_ROW_H - -#include "Object.h" -#include "Box.h" -#include "Enums.h" -#include - -DREAMPLACE_BEGIN_NAMESPACE - -class Row : public Box, public Object -{ - public: - typedef Object base_type2; - typedef base_type2::coordinate_type coordinate_type; - typedef Box base_type1; - typedef coordinate_traits::index_type index_type; - - /// default constructor - Row(); - /// copy constructor - Row(Row const& rhs); - /// assignment - Row& operator=(Row const& rhs); - - /// member functions - std::string const& name() const {return m_name;} - Row& setName(std::string const& s) {m_name = s; return *this;} - - std::string const& macroName() const {return m_macroName;} - Row& setMacroName(std::string const& s) {m_macroName = s; return *this;} - - Orient const& orient() const {return m_orient;} - Row& setOrient(Orient const& o) {m_orient = o; return *this;} - - coordinate_type const& step(Direction1DType d) const {return m_step[d];} - Row& setStep(Direction1DType d, coordinate_type v) {m_step[d] = v; return *this;} - Row& setStep(coordinate_type vx, coordinate_type vy) {m_step[kX] = vx; m_step[kY] = vy; return *this;} - - index_type numSites(Direction1DType d) const {return delta(d)/step(d);} - protected: - void copy(Row const& rhs); - - std::string m_name; ///< name of row - std::string m_macroName; ///< macro name of row, usually not used - Orient m_orient; - coordinate_type m_step[2]; ///< step of rows in x and y direction, usually same as site width and height -}; - -inline Row::Row() - : Row::base_type1() - , Row::base_type2() - , m_name("") - , m_macroName("") - , m_orient() -{ - m_step[kX] = std::numeric_limits::max(); - m_step[kY] = std::numeric_limits::max(); -} -inline Row::Row(Row const& rhs) - : Row::base_type1(rhs) - , Row::base_type2(rhs) -{ - copy(rhs); -} -inline Row& Row::operator=(Row const& rhs) -{ - if (this != &rhs) - { - this->base_type1::operator=(rhs); - this->base_type2::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void Row::copy(Row const& rhs) -{ - m_name = rhs.m_name; - m_macroName = rhs.m_macroName; - m_orient = rhs.m_orient; - m_step[kX] = rhs.m_step[kX]; - m_step[kY] = rhs.m_step[kY]; -} - -/// compare by row bottom edge -/// tie break by row id -struct CompareByRowBottomCoord -{ - bool operator()(Row const& row1, Row const& row2) const - { - return row1.yl() < row2.yl() || (row1.yl() == row2.yl() && row1.id() < row2.id()); - } -}; - -/// a row is divided into sub row due to fixed cells or blockages -class SubRow : public Box -{ - public: - typedef Object::coordinate_type coordinate_type; - typedef Box base_type; - typedef coordinate_traits::index_type index_type; - typedef coordinate_traits::area_type area_type; - - /// default constructor - SubRow(); - /// copy constructor - SubRow(SubRow const& rhs); - /// assignment - SubRow& operator=(SubRow const& rhs); - - /// member functions - index_type index1D() const {return m_index1D;} - SubRow& setIndex1D(index_type id) {m_index1D = id; return *this;} - - index_type rowId() const {return m_rowId;} - SubRow& setRowId(index_type id) {m_rowId = id; return *this;} - - /// \return sub row id in a row for sub row map indexing - index_type subRowId() const {return m_subRowId;} - SubRow& setSubRowId(index_type id) {m_subRowId = id; return *this;} - - std::vector const& binRows() const {return m_vBinRowId;} - std::vector& binRows() {return m_vBinRowId;} - - protected: - void copy(SubRow const& rhs); - - index_type m_index1D; ///< index in sub row array - index_type m_rowId; ///< parent row index - index_type m_subRowId; ///< sub row id in a row - std::vector m_vBinRowId; ///< indices of bin sub rows -}; -inline SubRow::SubRow() - : SubRow::base_type() - , m_index1D (std::numeric_limits::max()) - , m_rowId(std::numeric_limits::max()) - , m_subRowId(std::numeric_limits::max()) -{ -} -inline SubRow::SubRow(SubRow const& rhs) - : SubRow::base_type(rhs) -{ - copy(rhs); -} -inline SubRow& SubRow::operator=(SubRow const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void SubRow::copy(SubRow const& rhs) -{ - m_index1D = rhs.m_index1D; - m_rowId = rhs.m_rowId; - m_subRowId = rhs.m_subRowId; - m_vBinRowId = rhs.m_vBinRowId; -} - -/// row in a bin -class BinRow : public Box -{ - public: - typedef Object::coordinate_type coordinate_type; - typedef Box base_type; - typedef coordinate_traits::index_type index_type; - typedef coordinate_traits::area_type area_type; - - /// default constructor - BinRow(); - /// copy constructor - BinRow(BinRow const& rhs); - /// assignment - BinRow& operator=(BinRow const& rhs); - - /// member functions - index_type index1D() const {return m_index1D;} - BinRow& setIndex1D(index_type id) {m_index1D = id; return *this;} - - index_type binId() const {return m_binId;} - BinRow& setBinId(index_type id) {m_binId = id; return *this;} - - index_type subRowId() const {return m_subRowId;} - BinRow& setSubRowId(index_type id) {m_subRowId = id; return *this;} - - //std::vector const& binSubRows() const {return m_vBSRowId;} - //std::vector& binSubRows() {return m_vBSRowId;} - - protected: - void copy(BinRow const& rhs); - - index_type m_index1D; ///< bin row index in 1D array - index_type m_binId; ///< parent bin 1D index - index_type m_subRowId; ///< parent sub row 1D index - //std::vector m_vBSRowId; ///< indices of bin sub rows -}; - -inline BinRow::BinRow() - : BinRow::base_type() - , m_index1D(std::numeric_limits::max()) - , m_binId(std::numeric_limits::max()) - , m_subRowId(std::numeric_limits::max()) -{ -} -inline BinRow::BinRow(BinRow const& rhs) - : BinRow::base_type(rhs) -{ - copy(rhs); -} -inline BinRow& BinRow::operator=(BinRow const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void BinRow::copy(BinRow const& rhs) -{ - m_index1D = rhs.m_index1D; - m_binId = rhs.m_binId; - m_subRowId = rhs.m_subRowId; - //m_vBSRowId = rhs.m_vBSRowId; -} - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp deleted file mode 100644 index 8e5462dd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/************************************************************************* - > File Name: RowMap.cpp - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Wed Jun 24 21:19:32 2015 - ************************************************************************/ - -#include "RowMap.h" -#include "AlgoDB.h" -#include "AlgoHelpers.h" -#include "Iterators.h" -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -/// helper object of ObsWraper for collectBlockageIntervals() -struct CollectFixedCellIntervalHelper -{ - typedef AlgoDB::index_type index_type; - typedef AlgoDB::interval_type interval_type; - typedef AlgoDB::box_type box_type; - - PlaceDB const& placeDB; - SubRowMap const& subRowMap; - std::vector >& mBlkInterval; - - CollectFixedCellIntervalHelper(PlaceDB const& db, SubRowMap const& map, std::vector >& mInterval) - : placeDB(db) - , subRowMap(map) - , mBlkInterval(mInterval) - { - } - CollectFixedCellIntervalHelper(CollectFixedCellIntervalHelper const& rhs) - : placeDB(rhs.placeDB) - , subRowMap(rhs.subRowMap) - , mBlkInterval(rhs.mBlkInterval) - { - } - inline void operator()(Node const& /*node*/, box_type const& box) - { - subRowMap.collectBlockageIntervals(placeDB, box, mBlkInterval); - } -}; - -SubRowMap& SubRowMap::set(PlaceDB const& db) -{ - std::vector const& vRow = db.rows(); - index_type numRow = vRow.size(); - m_vSubRow.clear(); - m_mSubRowId.resize(numRow); - // create sub rows - // construct a 2D blockage map - std::vector > mBlkInterval (numRow); - ObsWraper cfciHelper (db, CollectFixedCellIntervalHelper(db, *this, mBlkInterval)); - for (FixedNodeConstIterator it = db.fixedNodeBegin(); it.inRange(); ++it) - { - Node const& node = *it; - cfciHelper(node); - } - for (std::vector::const_iterator it = db.placeBlockages().begin(); it != db.placeBlockages().end(); ++it) - collectBlockageIntervals(db, *it, mBlkInterval); - // clean blockage map so that there is no overlap between row slots - for (std::vector > >::iterator it1 = mBlkInterval.begin(), it1e = mBlkInterval.end(); it1 != it1e; ++it1) - { - std::vector > vMergedBlkInterval; - // sort by low coordinates - std::sort(it1->begin(), it1->end(), Interval::CompareByLow()); - // merge intervals - for (index_type i = 0, ie = it1->size(); i < ie; ++i) - { - Interval const& icur = it1->at(i); - if (!vMergedBlkInterval.empty() && intersects(vMergedBlkInterval.back(), icur, true)) - vMergedBlkInterval.back().encompass(icur); - else - vMergedBlkInterval.push_back(icur); - } - // apply vMergedBlkInterval to mBlkInterval - it1->swap(vMergedBlkInterval); - } - // create sub rows in each row - for (index_type i = 0; i < numRow; ++i) - { - Row const& row = db.rows().at(i); - std::vector > const& vBlkInterval = mBlkInterval.at(i); - std::vector& vSubRowId = m_mSubRowId.at(i); - vSubRowId.reserve(vBlkInterval.size()+1); - for (index_type j = 0, je = vBlkInterval.size(); j <= je; ++j) - { - coordinate_type xl = (j == 0)? row.xl() : vBlkInterval[j-1].high(); - coordinate_type xh = (j < vBlkInterval.size())? vBlkInterval[j].low() : row.xh(); - - if (xl+db.siteWidth() <= xh) // at least 1 site - { - // create and add sub row - // set data that is not going to change - m_vSubRow.push_back(SubRow()); - SubRow& srow = m_vSubRow.back(); - srow.setIndex1D(m_vSubRow.size()-1); // object id - srow.set(xl, row.yl(), xh, row.yh()); - srow.setRowId(row.id()); // id for level-1 indexing - srow.setSubRowId(vSubRowId.size()); // id for level-2 indexing - vSubRowId.push_back(srow.index1D()); - } - } - } - - return *this; -} - -void SubRowMap::collectBlockageIntervals(PlaceDB const& db, SubRowMap::box_type const& box, std::vector >& mBlkInterval) const -{ - std::vector const& vRow = db.rows(); - // collect intervals to blockage map - // it is possible that the blockage may not align to sites - // scale it up so that all the sub rows start and end to sites - box_type adjustBox ( - db.rowXL()+floor((box.xl()-db.rowXL())/db.siteWidth())*db.siteWidth(), - box.yl(), - db.rowXL()+ceil((double)(box.xh()-db.rowXL())/db.siteWidth())*db.siteWidth(), - box.yh() - ); - Interval idxRange (db.getRowIndexRange(adjustBox.yl(), adjustBox.yh())); - for (index_type i = idxRange.low(); i <= idxRange.high(); ++i) - { - if (i < mBlkInterval.size() && intersects(vRow[i], adjustBox, false)) // only collect valid intervals - mBlkInterval[i].push_back(adjustBox.get(kX)); - } -} - -BinRowMap& BinRowMap::set(AlgoDB& algo) -{ - BinType bt = kSBin; // use sbin to initialize bin row - SubRowMap const& subRowMap = algo.subRowMap(); - m_vBinRow.clear(); - m_mBinRowId.resize(subRowMap.numRows()); - // create bin rows according to sub row map and bin map - // traverse through all sub rows - for (SubRowMap1DConstIterator it = subRowMap.begin1D(), ite = subRowMap.end1D(); it != ite; ++it) - { - SubRow const& srow = *it; - std::vector& vBinRowId = m_mBinRowId.at(srow.rowId()); - Box idxBox (algo.getBinIndexRange(bt, srow.xl(), srow.yl(), srow.xh(), srow.yh())); - for (index_type iy = idxBox.yl(); iy <= idxBox.yh(); ++iy) - { - vBinRowId.reserve(idxBox.width()+1); - for (index_type ix = idxBox.xl(); ix <= idxBox.xh(); ++ix) - { - Bin& bin = algo.getBinByIndex(bt, ix, iy); - std::pair, bool> intersectBox = intersection(srow, bin, false); - if (intersectBox.second) // at least have intersection - { - // create bin row - m_vBinRow.push_back(BinRow()); - BinRow& brow = m_vBinRow.back(); - brow.set(kX, intersectBox.first.get(kX)).set(kY, srow.get(kY)); - brow.setIndex1D(m_vBinRow.size()-1); // object id - brow.setBinId(bin.index1D()); - brow.setSubRowId(srow.index1D()); // id to find parent sub row - vBinRowId.push_back(brow.index1D()); - // add to bin - // first step - bin.binRows().setPartial1(brow.index1D()); - } - } - } - } - // iterate through all bins to construct m_vBinRowId in bins - for (BinMap1DIterator it = algo.binMap(bt).begin1D(), ite = algo.binMap(bt).end1D(); it != ite; ++it) - { - Bin& bin = *it; - std::vector vRowIdOfBinRows (bin.binRows().size()); // row id of bin rows, because row id is continuous vertically - index_type count = 0; - for (HrchyList::const_iterator_type itBR = bin.binRows().begin(), itBRe = bin.binRows().end(); itBR != itBRe; ++itBR) - vRowIdOfBinRows[count++] = algo.getSubRow(getBinRow(*itBR).subRowId()).rowId(); // assume sub row is initialized - - Interval rowIdxInv = algo.placeDB().getRowIndexRange(bin.yl(), bin.yh()-1); // min, max row id - for (std::vector::iterator itSRID = vRowIdOfBinRows.begin(), itSRIDe = vRowIdOfBinRows.end(); itSRID != itSRIDe; ++itSRID) - { - *itSRID -= rowIdxInv.low(); // normalize to 0 - } - std::vector > mData; - // second step - bin.binRows().setPartial2(vRowIdOfBinRows, mData, rowIdxInv.delta()+1); - // the target is to sort bin rows from left to right within each row - // considering that bin row id is ordered from bottom to top, left to right, by construction - // it should be enough to sort by the indices - for (std::vector >::iterator itd = mData.begin(), itde = mData.end(); itd != itde; ++itd) - std::sort(itd->begin(), itd->end()); - // third step - bin.binRows().setPartial3(mData); - } -#ifdef DEBUG - // maybe no longer reasonable check - for (BinMap1DConstIterator it = algo.binMap(bt).begin1D(), ite = algo.binMap(bt).end1D(); it != ite; ++it) - { - Bin const& bin = *it; - // check whether bin rows in a sbin are sorted - dreamplaceAssert(boost::algorithm::is_sorted(bin.binRows().begin(), bin.binRows().end())); - } -#endif - - return *this; -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h deleted file mode 100644 index 2f060951..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h +++ /dev/null @@ -1,208 +0,0 @@ -/************************************************************************* - > File Name: RowMap.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Wed Jun 24 21:03:54 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_ROWMAP_H -#define DREAMPLACE_ROWMAP_H - -#include "Row.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// forward declaration of data -class BinMap; -class PlaceDB; -class AlgoDB; - -/// sub row map -/// sub rows should not contain any region of fixed cells -class SubRowMap -{ - public: - typedef Object::index_type index_type; - typedef Object::coordinate_type coordinate_type; - typedef Interval interval_type; - typedef Box box_type; - typedef std::vector::iterator SubRowMap1DIterator; ///< 1D iterator - typedef std::vector::const_iterator SubRowMap1DConstIterator; ///< 1D const iterator - - /// constructor - SubRowMap(); - /// copy constructor - SubRowMap(SubRowMap const& rhs); - /// assignment - SubRowMap& operator=(SubRowMap const& rhs); - - /// member functions - /// initialize - SubRowMap& set(PlaceDB const& db); - - /// \return level-1 indexing number - index_type numRows() const {return m_mSubRowId.size();} - - /// \return total number of sub rows - index_type numSubRows() const {return m_vSubRow.size();} - /// \return sub rows - std::vector const& getSubRows() const {return m_vSubRow;} - std::vector& getSubRows() {return m_vSubRow;} - - /// \return sub row index array in a row - std::vector const& getSubRowsByRowIndex(index_type i) const {return m_mSubRowId.at(i);} - /// \return sub row array iterators in a row - /// this is more convenient than getSubRowsByRowIndex() - std::pair::const_iterator, std::vector::const_iterator> getSubRowItersByRowIndex(index_type i) const - { - if (i+1 < numRows()) - return std::make_pair(m_vSubRow.begin()+m_mSubRowId[i].front(), m_vSubRow.begin()+m_mSubRowId[i+1].front()); - else // last row - return std::make_pair(m_vSubRow.begin()+m_mSubRowId[i].front(), m_vSubRow.end()); - } - /// index version of getSubRowItersByRowIndex() - std::pair getSubRowIndicesByRowIndex(index_type i) const - { - if (i+1 < numRows()) - return std::make_pair(m_mSubRowId[i].front(), m_mSubRowId[i+1].front()); - else // last row - return std::make_pair(m_mSubRowId[i].front(), numSubRows()); - } - - /// \return sub row from object id - SubRow const& getSubRow(index_type id) const {return m_vSubRow.at(id);} - SubRow& getSubRow(index_type id) {return m_vSubRow.at(id);} - - /// \return sub row from row id and sub row id - SubRow const& getSubRow(index_type rowId, index_type subRowId) const {return m_vSubRow.at(m_mSubRowId[rowId][subRowId]);} - SubRow& getSubRow(index_type rowId, index_type subRowId) {return m_vSubRow.at(m_mSubRowId[rowId][subRowId]);} - - SubRowMap1DIterator begin1D() {return m_vSubRow.begin();} - SubRowMap1DIterator end1D() {return m_vSubRow.end();} - - SubRowMap1DConstIterator begin1D() const {return m_vSubRow.begin();} - SubRowMap1DConstIterator end1D() const {return m_vSubRow.end();} -#if 0 - /// TO DO: create iterator class for such kind of data structure - /// traversal through level-1 - SubRowMap1DIterator1 begin1() {return m_mSubRow.begin();} - SubRowMap1DIterator1 end1() {return m_mSubRow.end();} - SubRowMap1DConstIterator1 begin1() const {return m_mSubRow.begin();} - SubRowMap1DConstIterator1 end1() const {return m_mSubRow.end();} - /// traversal through level-2 - SubRowMap1DIterator2 begin2(SubRowMap1DIterator1 it) {return it->begin();} - SubRowMap1DIterator2 end2(SubRowMap1DIterator1 it) {return it->end();} - SubRowMap1DConstIterator2 begin2(SubRowMap1DConstIterator1 it) const {return it->begin();} - SubRowMap1DConstIterator2 end2(SubRowMap1DConstIterator1 it) const {return it->end();} -#endif - protected: - void copy(SubRowMap const& rhs); - /// a helper function to set() - void collectBlockageIntervals(PlaceDB const& db, box_type const& box, std::vector >& mBlkInterval) const; - - std::vector m_vSubRow; ///< actual sub rows are saved in a 1D array, must keep low to high, left to right order - std::vector > m_mSubRowId; ///< 2D bin row map, level-1 indexing is number of rows - ///< number of sub rows may be different between rows - - friend struct CollectFixedCellIntervalHelper; -}; - -inline SubRowMap::SubRowMap() - : m_vSubRow () - , m_mSubRowId() -{ -} -inline SubRowMap::SubRowMap(SubRowMap const& rhs) -{ - copy(rhs); -} -inline SubRowMap& SubRowMap::operator=(SubRowMap const& rhs) -{ - if (this != &rhs) - copy(rhs); - return *this; -} -inline void SubRowMap::copy(SubRowMap const& rhs) -{ - m_vSubRow = rhs.m_vSubRow; - m_mSubRowId = rhs.m_mSubRowId; -} - -typedef SubRowMap::SubRowMap1DIterator SubRowMap1DIterator; -typedef SubRowMap::SubRowMap1DConstIterator SubRowMap1DConstIterator; - -/// bin row map -/// bin rows should not contain any region of fixed cells -class BinRowMap -{ - public: - typedef Object::index_type index_type; - typedef BinRow::coordinate_type coordinate_type; - typedef std::vector::iterator BinRowMap1DIterator; ///< 1D iterator - typedef std::vector::const_iterator BinRowMap1DConstIterator; ///< 1D const iterator - - /// constructor - BinRowMap(); - /// copy constructor - BinRowMap(BinRowMap const& rhs); - /// assignment - BinRowMap& operator=(BinRowMap const& rhs); - - /// member functions - /// initialize - BinRowMap& set(AlgoDB& algo); - - index_type numRows() const {return m_mBinRowId.size();} - index_type numBinRows() const {return m_vBinRow.size();} - - /// \return bin rows - std::vector const& getBinRows() const {return m_vBinRow;} - std::vector& getBinRows() {return m_vBinRow;} - - /// \return bin row array in a row - std::vector const& getBinRowsByRowIndex(index_type i) const {return m_mBinRowId.at(i);} - - /// \return bin row from object id - BinRow const& getBinRow(index_type id) const {return m_vBinRow.at(id);} - BinRow& getBinRow(index_type id) {return m_vBinRow.at(id);} - - BinRowMap1DIterator begin1D() {return m_vBinRow.begin();} - BinRowMap1DIterator end1D() {return m_vBinRow.end();} - - BinRowMap1DConstIterator begin1D() const {return m_vBinRow.begin();} - BinRowMap1DConstIterator end1D() const {return m_vBinRow.end();} - protected: - void copy(BinRowMap const& rhs); - - std::vector m_vBinRow; ///< actual bin rows are saved in a 1D array, must be in low to high, left to right order - std::vector > m_mBinRowId; ///< 2D bin row map for indexing, level-1 indexing is number of rows - ///< number of bin rows may be different between rows -}; - -inline BinRowMap::BinRowMap() - : m_vBinRow () - , m_mBinRowId() -{ -} -inline BinRowMap::BinRowMap(BinRowMap const& rhs) -{ - copy(rhs); -} -inline BinRowMap& BinRowMap::operator=(BinRowMap const& rhs) -{ - if (this != &rhs) - copy(rhs); - return *this; -} -inline void BinRowMap::copy(BinRowMap const& rhs) -{ - m_vBinRow = rhs.m_vBinRow; - m_mBinRowId = rhs.m_mBinRowId; -} - -typedef BinRowMap::BinRowMap1DIterator BinRowMap1DIterator; -typedef BinRowMap::BinRowMap1DConstIterator BinRowMap1DConstIterator; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h deleted file mode 100644 index a2ca443c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h +++ /dev/null @@ -1,87 +0,0 @@ -/************************************************************************* - > File Name: Site.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Wed Jun 17 22:09:40 2015 - ************************************************************************/ - -#ifndef DREAMPLACE_SITE_H -#define DREAMPLACE_SITE_H - -#include "Object.h" - -DREAMPLACE_BEGIN_NAMESPACE - -class Site : public Object -{ - public: - typedef Object base_type; - typedef base_type::coordinate_type coordinate_type; - - /// default constructor - Site(); - /// copy constructor - Site(Site const& rhs); - /// assignment - Site& operator=(Site const& rhs); - - /// member functions - std::string const& name() const {return m_name;} - Site& setName(std::string const& s) {m_name = s; return *this;} - - std::string const& className() const {return m_className;} - Site& setClassName(std::string const& s) {m_className = s; return *this;} - - unsigned char symmetry() const {return m_symmetry;} - Site& setSymmetry(unsigned char s) {m_symmetry = s; return *this;} - - coordinate_type size(Direction1DType d) const {return m_size[d];} - Site& setSize(Direction1DType d, coordinate_type v) {m_size[d] = v; return *this;} - coordinate_type width() const {return m_size[kX];} - coordinate_type height() const {return m_size[kY];} - - protected: - void copy(Site const& rhs); - - std::string m_name; ///< site name - std::string m_className; ///< class name - unsigned char m_symmetry; ///< 3-bit: x, y, R90 - coordinate_type m_size[2]; ///< width and height -}; - -inline Site::Site() - : Site::base_type() - , m_name("") - , m_className("") - , m_symmetry(std::numeric_limits::max()) -{ - m_size[kX] = std::numeric_limits::max(); - m_size[kY] = std::numeric_limits::max(); -} -inline Site::Site(Site const& rhs) - : Site::base_type(rhs) -{ - copy(rhs); -} -inline Site& Site::operator=(Site const& rhs) -{ - if (this != &rhs) - { - this->base_type::operator=(rhs); - copy(rhs); - } - return *this; -} -inline void Site::copy(Site const& rhs) -{ - m_name = rhs.m_name; - m_className = rhs.m_className; - m_symmetry = rhs.m_symmetry; - m_size[kX] = rhs.m_size[kX]; - m_size[kY] = rhs.m_size[kY]; -} - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp deleted file mode 100644 index 935de9cd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/************************************************************************* - > File Name: SubRowNodeMap.cpp - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Feb 2016 11:16:56 AM CST - ************************************************************************/ - -#include "SubRowNodeMap.h" -#include "AlgoDB.h" -#include "Iterators.h" -#include - -DREAMPLACE_BEGIN_NAMESPACE - -SubRowNodeMap::SubRowNodeMap(AlgoDB const* db) -{ - if (db) // only initialize if it is set - set(db); -} - -SubRowNodeMap::SubRowNodeMap(SubRowNodeMap const& rhs) -{ - copy(rhs); -} - -SubRowNodeMap& SubRowNodeMap::operator=(SubRowNodeMap const& rhs) -{ - if (this != &rhs) - copy(rhs); - return *this; -} - -SubRowNodeMap::~SubRowNodeMap() -{ -} - -SubRowNodeMap& SubRowNodeMap::set(AlgoDB const* db) -{ - m_algoDB = db; - m_vMap.clear(); - m_vMap.resize(m_algoDB->subRowMap().numSubRows()); - m_avgMovableWidth = m_algoDB->placeDB().avgMovableNodeWidth(); - - return *this; -} - -void SubRowNodeMap::copy(SubRowNodeMap const& rhs) -{ - m_algoDB = rhs.m_algoDB; - m_vMap = rhs.m_vMap; - m_avgMovableWidth = rhs.m_avgMovableWidth; -} - -void SubRowNodeMap::setSubRowNodes(bool moveDummyFixedCellOnly) -{ - // reset maps - for (std::vector::iterator it = m_vMap.begin(), ite = m_vMap.end(); it != ite; ++it) - it->clear(); - // distribute nodes - coordinate_type rowHeight = m_algoDB->placeDB().rowHeight(); - std::vector > mNode (m_algoDB->subRowMap().numSubRows()); - for (MovableNodeConstIterator it = m_algoDB->placeDB().movableNodeBegin(); it.inRange(); ++it) - { - Node const& node = *it; - if (moveDummyFixedCellOnly && node.status() != PlaceStatusEnum::DUMMY_FIXED) - continue; - // this for loop only works for movable cells, because fixed instances may occupy partial row - coordinate_type y = node.yl(); - while (y < node.yh()) - { - index_type idx = m_algoDB->getSubRowIndexSafe(node.xl(), y+1); // compute sub row index - mNode[idx].push_back(map_element_type(node.id(), node.initPos().x(), node.get(kX))); - - y += rowHeight; // next row - } - } - for (index_type i = 0, ie = m_vMap.size(); i < ie; ++i) - { -#ifdef USE_RTREE - map_type(mNode[i].begin(), mNode[i].end()).swap(m_vMap.at(i)); -#elif defined(USE_INTERVALHASHMAP) - map_type(m_algoDB->getSubRow(i).xl(), m_algoDB->getSubRow(i).xh(), m_avgMovableWidth, mNode[i].begin(), mNode[i].end()).swap(m_vMap.at(i)); -#endif - } -} - -std::vector SubRowNodeMap::queryRange(Box const& box) const -{ - // go through bins and extract sub rows - Box idxBinBox = m_algoDB->getBinIndexRange(kSBin, box.xl(), box.yl(), box.xh(), box.yh()); - std::vector vSubRow; - for (index_type idxX = idxBinBox.xl(); idxX <= idxBinBox.xh(); ++idxX) - for (index_type idxY = idxBinBox.yl(); idxY <= idxBinBox.yh(); ++idxY) - { - Bin const& bin = m_algoDB->getBinByIndex(kSBin, idxX, idxY); - for (HrchyList::const_iterator_type itBR = bin.binRows().begin(), itBRe = bin.binRows().end(); itBR != itBRe; ++itBR) - { - BinRow const& brow = m_algoDB->getBinRow(*itBR); - vSubRow.push_back(brow.subRowId()); - } - } - // remove duplicates of sub rows - removeDuplicates(vSubRow); - - // go through sub rows and extract nodes - std::vector vNodeInBox; // result - for (std::vector::const_iterator itSR = vSubRow.begin(), itSRe = vSubRow.end(); itSR != itSRe; ++itSR) - { - SubRow const& srow = m_algoDB->getSubRow(*itSR); - if (intersects(srow, box, false)) // consider sub row that has overlap with the box - { - std::pair found = queryRange(srow.index1D(), box.xl(), box.xh(), true); - for (map_const_iterator_type itn = found.first; itn != found.second; ++itn) - { -#ifdef DEBUG -#ifdef USE_RTREE - dreamplaceAssert(getMapElementHigh(itn) > box.xl()); // skip cells that do not have overlap with the range -#endif -#endif -#ifdef USE_RTREE - vNodeInBox.push_back(getMapElementId(itn)); -#elif defined(USE_INTERVALHASHMAP) - if (getMapElementHigh(itn) > box.xl()) // skip cells that do not have overlap with the range - vNodeInBox.push_back(getMapElementId(itn)); -#endif - } - } - } - return vNodeInBox; -} - -std::vector SubRowNodeMap::queryRange( - SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type yl, - SubRowNodeMap::coordinate_type xh, SubRowNodeMap::coordinate_type yh) const -{ - return queryRange(Box(xl, yl, xh, yh)); -} - -std::pair -SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, - SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary) const -{ - map_type const& map = subRowMap(idx); -#ifdef USE_RTREE - if (noBoundary) - return std::make_pair( - map.qbegin(bgi::intersects(interval_type(xl, xh)) - && bgi::satisfies(NoBoundaryPredicate(xl, xh))), - map.qend() - ); - else - return std::make_pair( - map.qbegin(bgi::intersects(interval_type(xl, xh))), - map.qend() - ); -#elif defined(USE_INTERVALHASHMAP) - return query(map, xl, xh, !noBoundary); -#endif -} - -void SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary, std::vector& vNode) const -{ - map_type const& map = subRowMap(idx); -#ifdef USE_RTREE - if (noBoundary) - map.query(bgi::intersects(interval_type(xl, xh)), - std::back_inserter(vNode)); - else - map.query(bgi::intersects(interval_type(xl, xh)) && bgi::satisfies(NoBoundaryPredicate(xl, xh)), - std::back_inserter(vNode)); -#elif defined(USE_INTERVALHASHMAP) - std::pair found = query(map, xl, xh, !noBoundary); - vNode.assign(found.first, found.second); -#endif -} - -void SubRowNodeMap::queryRange(SubRowNodeMap::index_type idx, SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh, bool noBoundary, SubRowNodeMap::map_type& targetMap) const -{ - map_type const& map = subRowMap(idx); -#ifdef USE_RTREE - if (noBoundary) - map.query(bgi::intersects(interval_type(xl, xh)), - bgi::inserter(targetMap)); - else - map.query(bgi::intersects(interval_type(xl, xh)) && bgi::satisfies(NoBoundaryPredicate(xl, xh)), - bgi::inserter(targetMap)); -#elif defined(USE_INTERVALHASHMAP) - std::pair found = query(map, xl, xh, !noBoundary); - for (map_const_iterator_type it = found.first; it != found.second; ++it) - targetMap.insert(*it); -#endif -} - -bool SubRowNodeMap::count(SubRowNodeMap::index_type idx, Node const& node) const -{ - return subRowMap(idx).count( - map_element_type(node.id(), node.initPos().x(), node.get(kX)) - ); -} - -bool SubRowNodeMap::erase(SubRowNodeMap::index_type idx, Node const& node) -{ - return subRowMap(idx).remove( - map_element_type(node.id(), node.initPos().x(), node.get(kX)) - ); -} - -void SubRowNodeMap::insert(SubRowNodeMap::index_type idx, Node const& node) -{ - subRowMap(idx).insert( - map_element_type(node.id(), node.initPos().x(), node.get(kX)) - ); -} - -void SubRowNodeMap::print(SubRowNodeMap::index_type idx) const -{ - printRange(idx, m_algoDB->placeDB().rowXL(), m_algoDB->placeDB().rowXH()); -} - -void SubRowNodeMap::printRange(SubRowNodeMap::index_type idx, - SubRowNodeMap::coordinate_type xl, SubRowNodeMap::coordinate_type xh) const -{ - char prefix[16]; - dreamplaceSPrint(kNONE, prefix, "r%u: ", idx); -#ifdef USE_RTREE - for (map_const_iterator_type it = subRowMap(idx).qbegin( - bgi::intersects( - interval_type(xl, xh) - ) - && bgi::satisfies( - NoBoundaryPredicate(xl, xh) - )); it != subRowMap(idx).qend(); ++it) - { - dreamplacePrint(kNONE, "%s%u@%d", prefix, getMapElementId(it), getMapElementLow(it)); - dreamplaceSPrint(kNONE, prefix, ", "); - } -#elif defined(USE_INTERVALHASHMAP) - std::pair found = query(subRowMap(idx), xl, xh, true); - for (map_const_iterator_type it = found.first; it != found.second; ++it) - { - dreamplacePrint(kNONE, "%s%u@%d", prefix, getMapElementId(it), getMapElementLow(it)); - dreamplaceSPrint(kNONE, prefix, ", "); - } -#endif - dreamplacePrint(kNONE, "\n"); -} - -#if 0 -Node const& SubRowNodeMap::getMapElement(SubRowNodeMap::map_const_iterator_type const& it) const -{ - return getMapElement(*it); -} - -Node const& SubRowNodeMap::getMapElement(SubRowNodeMap::map_element_type const& v) const -{ - return m_algoDB->node(getMapElementId(v)); -} -#endif - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h deleted file mode 100644 index d7121052..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h +++ /dev/null @@ -1,169 +0,0 @@ -/************************************************************************* - > File Name: SubRowNodeMap.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Feb 2016 10:27:34 AM CST - ************************************************************************/ - -#ifndef DREAMPLACE_SUBROWNODEMAP_H -#define DREAMPLACE_SUBROWNODEMAP_H - -#include -#include "Interval.h" -#include "Box.h" -#include "Object.h" -#include "Node.h" -#include "NodeMapElement.h" -#include "SubRowNodeMapHelper.h" - -// IntervalHashMap is slightly faster than rtree for large benchmarks -//#define USE_RTREE -#define USE_INTERVALHASHMAP - -DREAMPLACE_BEGIN_NAMESPACE - -/// forward declaration of data -class AlgoDB; - -#ifdef USE_RTREE -namespace bg = boost::geometry; -namespace bgi = boost::geometry::index; -namespace bgm = boost::geometry::model; -#endif - -/// a query map for nodes stored in sub rows, indexed with rtree -/// Node pointer is directly used for rtree indexing, -/// which means you have to be careful when update the position of nodes -/// -/// it must be initialized after SubRowMap and BinRowMap are ready - -/// query map -class SubRowNodeMap -{ - public: - typedef Object::coordinate_type coordinate_type; - typedef Object::index_type index_type; - typedef Interval interval_type; - typedef NodeMapElement map_element_type; - -#ifdef USE_RTREE - /// use 1D point in boost to adapt rtree - typedef bgi::rtree > map_type; - typedef map_type::const_query_iterator map_const_iterator_type; -#elif defined(USE_INTERVALHASHMAP) - typedef IntervalHashMap > map_type; - typedef map_type::const_iterator_type map_const_iterator_type; -#endif - - /// constructor - SubRowNodeMap(AlgoDB const* db = NULL); - /// copy constructor - SubRowNodeMap(SubRowNodeMap const& rhs); - /// assignment - SubRowNodeMap& operator=(SubRowNodeMap const& rhs); - /// destructor - ~SubRowNodeMap(); - - /// member functions - /// initialize - SubRowNodeMap& set(AlgoDB const* db); - - /// distribute nodes into sub rows, must be called before using the map - /// can be called multiple times - void setSubRowNodes(bool moveDummyFixedCellOnly); - - /// \return true if an element exists in a sub row - bool count(index_type idx, Node const& node) const; - /// erase an element - bool erase(index_type idx, Node const& node); - /// emplace - void insert(index_type idx, Node const& node); - - /// query a range in a sub row - /// the left element is not the absolute boundary, it is relaxed to make sure all the nodes with possible overlap are included - /// \param noBoundary, if true, remove boundary intersects - std::pair queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary) const; - /// directly set \param vNode instead of returning iterators - void queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary, std::vector& vNode) const; - void queryRange(index_type idx, coordinate_type xl, coordinate_type xh, bool noBoundary, map_type& targetMap) const; - /// query with a region - /// might be slow, as we need to go through all the bins and extract sub rows - /// if the sub row is known, directly call subRowMap() and then queryRange() is recommended - std::vector queryRange(Box const& box) const; - std::vector queryRange(coordinate_type xl, coordinate_type yl, coordinate_type xh, coordinate_type yh) const; - - /// helper functions for boost box - /// it is better to write accessors with map_const_iterator_type because we can change the internal map_type more easily - static inline coordinate_type getMapElementLow(map_const_iterator_type const& it) {return it->inv.low();} - static inline coordinate_type getMapElementLow(map_element_type const& v) {return v.inv.low();} - static inline coordinate_type getMapElementHigh(map_const_iterator_type const& it) {return it->inv.high();} - static inline coordinate_type getMapElementHigh(map_element_type const& v) {return v.inv.high();} - static inline index_type getMapElementId(map_const_iterator_type const& it) {return it->nodeId;} - static inline index_type getMapElementId(map_element_type const& v) {return v.nodeId;} - //Node const& getMapElement(map_const_iterator_type const& it) const; - //Node const& getMapElement(map_element_type const& v) const; - - /// for debug - /// print cells in a sub row - void print(index_type idx) const; - /// print range of cells in a sub row - void printRange(index_type idx, coordinate_type xl, coordinate_type xh) const; - protected: - void copy(SubRowNodeMap const& rhs); - - /// forbiden to public due to security - /// \return map of a single sub row - map_type& subRowMap(index_type idx) {return m_vMap.at(idx);} - map_type const& subRowMap(index_type idx) const {return m_vMap.at(idx);} - - AlgoDB const* m_algoDB; ///< AlgoDB has some helper functions useful for indexing - std::vector m_vMap; ///< same number of sub rows - coordinate_type m_avgMovableWidth; ///< average width of movable cells, only used in USE_INTERVALHASHMAP -}; - -/// the overlap predicate in boost only works for box, it does not give the correct solution for segments -/// the intersects will count the boundaries, which is not preferred -struct OverlapPredicate -{ - typedef Object::coordinate_type coordinate_type; - typedef Interval interval_type; - - interval_type inv; - - OverlapPredicate(coordinate_type xl, coordinate_type xh) : inv(xl, xh) {} - OverlapPredicate(interval_type const& i) : inv(i) {} - OverlapPredicate(OverlapPredicate const& rhs) : inv(rhs.inv) {} - - template - inline bool operator()(ValueType const& v) const - { - return !(SubRowNodeMap::getMapElementHigh(v) <= inv.low() - || SubRowNodeMap::getMapElementLow(v) >= inv.high()); - } -}; - -/// remove boundary intersection because bgi::intersects allows intersection of boundaries -/// I found that it is around 1.5x faster than OverlapPredicate if I combine it with bgi::intersects -struct NoBoundaryPredicate -{ - typedef Object::coordinate_type coordinate_type; - - coordinate_type xl; - coordinate_type xh; - - NoBoundaryPredicate(coordinate_type l, coordinate_type h) : xl(l), xh(h) {} - NoBoundaryPredicate(NoBoundaryPredicate const& rhs) : xl(rhs.xl), xh(rhs.xh) {} - - /// \return true if it is not boundary intersection - template - inline bool operator()(ValueType const& v) const - { - return SubRowNodeMap::getMapElementHigh(v) != xl - && SubRowNodeMap::getMapElementLow(v) != xh; - } - -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h deleted file mode 100644 index 8e05c30a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************* - > File Name: SubRowNodeMapHelper.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Feb 2016 09:24:33 PM CST - ************************************************************************/ - -#ifndef DREAMPLACE_SUBROWNODEMAPHELPER_H -#define DREAMPLACE_SUBROWNODEMAPHELPER_H - -#include -#include -#include "Interval.h" -#include "IntervalHashMap.h" - -/// API for Boost.Geometry -namespace boost { namespace geometry { namespace index { - -template -struct indexable< std::pair >, T> > -{ - typedef std::pair >, T> V; - - typedef boost::geometry::model::point point_type; - typedef boost::geometry::model::box const& result_type; - - result_type operator()(V const& v) const { return v.first; } -}; - -template <> -struct indexable -{ - typedef DREAMPLACE_NAMESPACE::Node const* value_type; - typedef DREAMPLACE_NAMESPACE::Interval const& result_type; - - result_type operator()(value_type const& v) const {return v->get(DREAMPLACE_NAMESPACE::kX);} -}; - -template <> -struct indexable -{ - typedef DREAMPLACE_NAMESPACE::NodeMapElement value_type; - typedef DREAMPLACE_NAMESPACE::NodeMapElement::interval_type const& result_type; - - result_type operator()(value_type const& v) const {return v.inv;} -}; - -}}} // namespace boost // namespace geometry // namespace index - -namespace boost { namespace geometry { namespace traits { - -//////// for intervals //////// -template -struct tag > -{ - typedef box_tag type; -}; - -template -struct point_type > -{ - // a 1D point - typedef boost::geometry::model::point type; -}; - -template -struct indexed_access -< - DREAMPLACE_NAMESPACE::Interval, - min_corner, Dimension -> -{ - typedef CoordinateType coordinate_type; - - static inline coordinate_type get(DREAMPLACE_NAMESPACE::Interval const& inv) - { - return inv.low(); - } - static inline void set(DREAMPLACE_NAMESPACE::Interval& inv, coordinate_type const& value) - { - inv.set(DREAMPLACE_NAMESPACE::kLOW, value); - } -}; - - -template -struct indexed_access -< - DREAMPLACE_NAMESPACE::Interval, - max_corner, Dimension -> -{ - typedef CoordinateType coordinate_type; - - static inline coordinate_type get(DREAMPLACE_NAMESPACE::Interval const& inv) - { - return inv.high(); - } - static inline void set(DREAMPLACE_NAMESPACE::Interval& inv, coordinate_type const& value) - { - inv.set(DREAMPLACE_NAMESPACE::kHIGH, value); - } -}; - -}}} // namespace boost // namespace geometry // namespace traits - -/// API for IntervalHashMap -DREAMPLACE_BEGIN_NAMESPACE - -template <> -struct IntervalHashMapTraits -{ - typedef NodeMapElement value_type; - typedef value_type::coordinate_type coordinate_type; - typedef unsigned index_type; - - static coordinate_type low(value_type const& v) {return v.inv.low(); } - static coordinate_type high(value_type const& v) {return v.inv.high();} - static bool equal(value_type const& v1, value_type const& v2) {return v1.nodeId == v2.nodeId;} - - /// compare object for sorting - struct CompareByLow - { - bool operator()(value_type const& v1, value_type const& v2) const {return low(v1) < low(v2) || (low(v1) == low(v2) && v1.nodeId < v2.nodeId);} - bool operator()(coordinate_type v1, value_type const& v2) const {return v1 < low(v2);} - bool operator()(value_type const& v1, coordinate_type v2) const {return low(v1) < v2;} - }; -}; - -DREAMPLACE_END_NAMESPACE - - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h deleted file mode 100644 index e089b4d5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h +++ /dev/null @@ -1,214 +0,0 @@ -/************************************************************************* - > File Name: util.h - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Sun 14 Jun 2015 04:08:18 PM CDT - ************************************************************************/ - -#ifndef DREAMPLACE_UTIL_H -#define DREAMPLACE_UTIL_H - -#include -#include -#include -#include -#include "utility/src/Msg.h" - -/// headers for hash tables -#include -#include -DREAMPLACE_BEGIN_NAMESPACE -namespace hashspace = std; -DREAMPLACE_END_NAMESPACE - -DREAMPLACE_BEGIN_NAMESPACE - -enum Direction1DType { - kLOW = 0, - kHIGH = 1, - kX = 0, - kY = 1, - kLEFT = 0, - kRIGHT = 1, - kBOTTOM = 0, - kTOP = 1 -}; - -enum Direction2DType { - kXLOW = 0, - kXHIGH = 1, - kYLOW = 2, - kYHIGH = 3 -}; - -/// extract x/y information from Direction2DType -inline Direction1DType getXY(Direction2DType d) -{ - return Direction1DType(d>1); -} -/// extract low/high information from Direction2DType -inline Direction1DType getLH(Direction2DType d) -{ - return Direction1DType(d&1); -} -/// construct Direction2DType from x/y and low/high information -inline Direction2DType to2D(Direction1DType xy, Direction1DType lh) -{ - return Direction2DType(((int)xy<<1)+(int)lh); -} - -/// data traits -/// define a template class of data traits -/// which will make it easier for generic change of data type -template -struct coordinate_traits; - -/// specialization for int -template <> -struct coordinate_traits -{ - typedef float coordinate_type; - typedef double euclidean_distance_type; - typedef long manhattan_distance_type; - typedef long area_type; - typedef unsigned int site_index_type; ///< site index in a row structure - typedef unsigned long site_area_type; ///< number of sites for a region - typedef unsigned int index_type; ///< index (id) - typedef float weight_type; ///< type for net or node weights -}; -/// specialization for unsigned int -template <> -struct coordinate_traits -{ - typedef float coordinate_type; - typedef double euclidean_distance_type; - typedef long manhattan_distance_type; - typedef long area_type; - typedef unsigned int site_index_type; ///< site index in a row structure - typedef unsigned long site_area_type; ///< number of sites for a region - typedef unsigned int index_type; ///< index (id) - typedef float weight_type; ///< type for net or node weights -}; -/// specialization for float -template <> -struct coordinate_traits -{ - typedef float coordinate_type; - typedef double euclidean_distance_type; - typedef double manhattan_distance_type; - typedef double area_type; - typedef unsigned int site_index_type; ///< site index in a row structure - typedef double site_area_type; ///< number of sites for a region - typedef unsigned int index_type; ///< index (id) - typedef float weight_type; ///< type for net or node weights -}; -/// specialization for double -template <> -struct coordinate_traits -{ - typedef float coordinate_type; - typedef long double euclidean_distance_type; - typedef long double manhattan_distance_type; - typedef long double area_type; - typedef unsigned long site_index_type; ///< site index in a row structure - typedef long double site_area_type; ///< number of sites for a region - typedef unsigned long index_type; ///< index (id) - typedef float weight_type; ///< type for net or node weights -}; - -/// type helper for non-const/const -template struct ConstTypeHelper; -template struct ConstTypeHelper -{ - typedef T value_type; - typedef T& reference_type; - typedef T* pointer_type; -}; -template struct ConstTypeHelper -{ - typedef T value_type; - typedef T const& reference_type; - typedef T const* pointer_type; -}; - -/// @brief Match a string with a wildcard pattern. -/// Copied from geeksforgeeks -/// https://www.geeksforgeeks.org/wildcard-pattern-matching/ -class WildcardMatch -{ - public: - /// @param str target string - /// @param pattern target pattern - /// @param n length of string - /// @param m length of pattern - inline bool operator()(const char* str, const char* pattern, std::size_t n, std::size_t m) - { - // empty pattern can only match with - // empty string - if (m == 0) - return (n == 0); - - // lookup table for storing results of - // subproblems - m_n = n; - m_m = m; - m_lookup.resize((n + 1)*(m + 1)); - - // initailze lookup table to false - memset(m_lookup.data(), false, sizeof(unsigned char)*m_lookup.size()); - - // empty pattern can match with empty string - lookup(0, 0) = true; - - // Only '*' can match with empty string - for (std::size_t j = 1; j <= m; j++) - if (pattern[j - 1] == '*') - lookup(0, j) = lookup(0, j - 1); - - // fill the table in bottom-up fashion - for (std::size_t i = 1; i <= n; i++) - { - for (std::size_t j = 1; j <= m; j++) - { - // Two cases if we see a '*' - // a) We ignore ‘*’ character and move - // to next character in the pattern, - // i.e., ‘*’ indicates an empty sequence. - // b) '*' character matches with ith - // character in input - if (pattern[j - 1] == '*') - lookup(i, j) = lookup(i, j - 1) || lookup(i - 1, j); - - // Current characters are considered as - // matching in two cases - // (a) current character of pattern is '?' - // (b) characters actually match - else if (pattern[j - 1] == '?' || - str[i - 1] == pattern[j - 1]) - lookup(i, j) = lookup(i - 1, j - 1); - - // If characters don't match - else lookup(i, j) = false; - } - } - - return lookup(n, m); - } - protected: - inline unsigned char lookup(std::size_t i, std::size_t j) const - { - return m_lookup.at(i*(m_m + 1) + j); - } - inline unsigned char& lookup(std::size_t i, std::size_t j) - { - return m_lookup.at(i*(m_m + 1) + j); - } - - std::vector m_lookup; ///< lookup table - std::size_t m_n; ///< length of string - std::size_t m_m; /// < length of pattern -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp deleted file mode 100644 index 8309b8f7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/** - * @file place_io.cpp - * @author Yibo Lin (DREAMPlace), Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Mar 2021 - * @brief Python binding - */ - -#include "PyPlaceDB.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// take numpy array -template -bool write(PlaceDB const& db, - std::string const& filename, - pybind11::array_t const& x, - pybind11::array_t const& y - ) -{ - float* vx = NULL; - float* vy = NULL; - - // assume all the movable nodes are in front of fixed nodes - // this is ensured by PlaceDB::sortNodeByPlaceStatus() - PlaceDB::index_type lenx = x.size(); - if (lenx >= db.numMovable()) - { - vx = new float [lenx]; - for (PlaceDB::index_type i = 0; i < lenx; ++i) - { - vx[i] = x.at(i); - } - } - PlaceDB::index_type leny = y.size(); - if (leny >= db.numMovable()) - { - vy = new float [leny]; - for (PlaceDB::index_type i = 0; i < leny; ++i) - { - vy[i] = y.at(i); - } - } - - //bool flag = db.write(filename, ff, vx, vy); - bool flag = db.write(filename, vx, vy); - - if (vx) - { - delete [] vx; - } - if (vy) - { - delete [] vy; - } - - return flag; -} - -/// take numpy array -template -void apply(PlaceDB& db, - pybind11::array_t const& x, - pybind11::array_t const& y, - pybind11::array_t const& z - ) -{ - // assume all the movable nodes are in front of fixed nodes - if (db.numCCNodes() == 0) - { - for (int nIdx = 0; nIdx < db.numMovable()+db.numFixed(); ++nIdx) - { - float xx = x.at(nIdx); - float yy = y.at(nIdx); - PlaceDB::index_type zz = z.at(nIdx); - db.nodeXLocs().at(nIdx) = xx; - db.nodeYLocs().at(nIdx) = yy; - db.nodeZLocs().at(nIdx) = zz; - } - } else - { - for (int nIdx = 0; nIdx < db.numOrgMovable()+db.numFixed(); ++nIdx) - { - float xx = x.at(nIdx); - float yy = y.at(nIdx); - PlaceDB::index_type zz = z.at(nIdx); - db.orgNodeXLocs().at(nIdx) = xx; - db.orgNodeYLocs().at(nIdx) = yy; - db.orgNodeZLocs().at(nIdx) = zz; - } - } -} - -PlaceDB place_io_forward(pybind11::str const& auxPath) -{ - - DREAMPLACE_NAMESPACE::PlaceDB db; - - bool flag; - - // read bookshelf - flag = DREAMPLACE_NAMESPACE::readBookshelf(db, auxPath); - dreamplaceAssertMsg(flag, "Failed to read input Bookshelf files"); - - return db; -} - -DREAMPLACE_END_NAMESPACE - -// create Python binding - -void bind_PlaceDB(pybind11::module&); -void bind_PyPlaceDB(pybind11::module&); - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - - bind_PlaceDB(m); - bind_PyPlaceDB(m); - - m.def("write", [](DREAMPLACE_NAMESPACE::PlaceDB const& db, - std::string const& filename, - pybind11::array_t const& x, - pybind11::array_t const& y) {return write(db, filename, x, y);}, - "Write Placement Solution (float)"); - m.def("write", [](DREAMPLACE_NAMESPACE::PlaceDB const& db, - std::string const& filename, - pybind11::array_t const& x, - pybind11::array_t const& y) {return write(db, filename, x, y);}, - "Write Placement Solution (double)"); - m.def("apply", [](DREAMPLACE_NAMESPACE::PlaceDB& db, - pybind11::array_t const& x, - pybind11::array_t const& y, - pybind11::array_t const& z) {apply(db, x, y, z);}, - "Apply Placement Solution (float)"); - m.def("apply", [](DREAMPLACE_NAMESPACE::PlaceDB& db, - pybind11::array_t const& x, - pybind11::array_t const& y, - pybind11::array_t const& z) {apply(db, x, y, z);}, - "Apply Placement Solution (double)"); - m.def("pydb", [](DREAMPLACE_NAMESPACE::PlaceDB const& db){return DREAMPLACE_NAMESPACE::PyPlaceDB(db);}, "Convert PlaceDB to PyPlaceDB"); - m.def("forward", &DREAMPLACE_NAMESPACE::place_io_forward, "PlaceDB IO Read"); -} - - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt deleted file mode 100644 index 853b1662..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(OP_NAME precondWL) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(precondWL_cpp - src/precondWL.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - precondWL_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(precondWL_cuda - src/precondWL_cuda.cpp - src/precondWL_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - precondWL_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py deleted file mode 100644 index 995c4cb7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Nov 2020 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py deleted file mode 100644 index 04ba16e5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py +++ /dev/null @@ -1,73 +0,0 @@ -## -# @file precondWL.py -# @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) -# @date Nov 2020 -# - -import torch -from torch.autograd import Function -from torch import nn -import numpy as np -import pdb -import time - -import dreamplacefpga.ops.precondWL.precondWL_cpp as precondWL_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.precondWL.precondWL_cuda as precondWL_cuda - -class PrecondWL(nn.Module): - """ - @brief Compute wirelength preconditioner. - """ - def __init__(self, flat_node2pin_start, flat_node2pin, pin2net_map, flat_net2pin, net_weights, num_nodes, num_movable_nodes, device, num_threads): - """ - @brief initialization - @param flat_node2pin_start_map - @param flat_node2pin_map node to pin map - @param flat_netpin flat netpin map, length of #pins - @param pin2net_map pin to net map - @param net_weights weight of nets - @param num_nodes - @param num_threads - """ - super(PrecondWL, self).__init__() - self.flat_node2pin_start=flat_node2pin_start - self.flat_node2pin=flat_node2pin - self.flat_net2pin=flat_net2pin - self.pin2net_map=pin2net_map - self.net_weights=net_weights - self.num_nodes=num_nodes - self.num_movable_nodes=num_movable_nodes - self.num_threads=num_threads - self.device=device - - def forward(self): - out = torch.zeros(self.num_nodes, dtype=torch.float32, device=self.device) - ##TODO - Temporary fix expecting net_weights to be empty - if self.net_weights.shape == []: - net_weights = torch.ones(self.num_nodes, dtype=torch.float32, device=self.device) - else: - net_weights = torch.clamp(self.net_weights, min=1.0) - - if out.is_cuda: - precondWL_cuda.forward( - self.flat_node2pin_start, - self.flat_node2pin, - self.pin2net_map, - self.flat_net2pin, - net_weights, - self.num_movable_nodes, - out) - else: - precondWL_cpp.forward( - net_weights, - self.flat_node2pin_start, - self.flat_node2pin, - self.flat_net2pin, - self.pin2net_map, - self.num_movable_nodes, - self.num_threads, - out) - return out - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp deleted file mode 100644 index 2743db50..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @file precondWL.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Nov 2020 - * @brief Compute precond WL - */ -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computePrecondWLLauncher( - const T *net_weights, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const int *flat_net2pin, - const int *pin2net_map, - int num_nodes, - int num_threads, - T *out - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute wirelength preconditioner -void forward( - at::Tensor net_weights, - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor flat_net2pin, - at::Tensor pin2net_map, - int num_nodes, - int num_threads, - at::Tensor out) -{ - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - CHECK_FLAT(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - - CHECK_FLAT(flat_net2pin); - CHECK_CONTIGUOUS(flat_net2pin); - - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(out, "computePrecondWLLauncher", [&] { - computePrecondWLLauncher( - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - num_nodes, num_threads, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t) - ); - }); -} - -template -int computePrecondWLLauncher( - const T *net_weights, - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const int *flat_net2pin, - const int *pin2net_map, - int num_nodes, - int num_threads, - T *out - ) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nodes/ num_threads / 16), 1); - //#pragma omp parallel for schedule(static) - //#pragma omp parallel for num_threads(num_threads) - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nodes; ++i) - { - int beg = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i+1]; - - for (int p = beg; p < end; ++p) - { - int netId = pin2net_map[flat_node2pin_map[p]]; - int numPins = flat_net2pin[netId+1] - flat_net2pin[netId]; - //Ignore single pin nets - if (numPins > 1) - { - out[i] += net_weights[netId]/(numPins-1.0); - } - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "PrecondWL forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp deleted file mode 100644 index 33122e38..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file precondWL_cuda.cpp - * @author Rachel Selina Rajarathnam (DREAMPlaceFPGA) - * @date Nov 2020 - * @brief Compute precond WL - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" -using namespace torch::indexing; - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computePrecondWLCudaLauncher( - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const int *pin2net_map, - const int *flat_net2pin, - const T *net_weights, - int num_nodes, - T *out - ); - - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - - -/// @brief Compute half-perimeter wirelength along with net bbox -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_mask an array to record whether compute the where for a net or not -void forward( - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor pin2net_map, - at::Tensor flat_net2pin, - at::Tensor net_weights, - int num_nodes, - at::Tensor out - ) -{ - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - CHECK_FLAT(flat_node2pin_map); - CHECK_CONTIGUOUS(flat_node2pin_map); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(flat_net2pin); - CHECK_CONTIGUOUS(flat_net2pin); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(out, "computePrecondWLCudaLauncher", [&] { - computePrecondWLCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin, int), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(out, scalar_t) - ); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "PrecondWL forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu deleted file mode 100644 index 74d57f77..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu +++ /dev/null @@ -1,78 +0,0 @@ -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computePrecondWL( - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const int *pin2net_map, - const int *flat_net2pin, - const T *net_weights, - int num_nodes, - T *out - ) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nodes) - { - int beg = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i+1]; - - for (int p = beg; p < end; ++p) - { - int netId = pin2net_map[flat_node2pin_map[p]]; - int numPins = flat_net2pin[netId+1] - flat_net2pin[netId]; - //Ignore single pin nets - if (numPins > 1) - { - out[i] += net_weights[netId]/(numPins-1.0); - } - } - } -} - -template -int computePrecondWLCudaLauncher( - const int *flat_node2pin_start_map, - const int *flat_node2pin_map, - const int *pin2net_map, - const int *flat_net2pin, - const T *net_weights, - int num_nodes, - T *out - ) -{ - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - - computePrecondWL<<>>( - flat_node2pin_start_map, - flat_node2pin_map, - pin2net_map, - flat_net2pin, - net_weights, - num_nodes, - out - ); - - return 0; -} - -// manually instantiate the template function -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computePrecondWLCudaLauncher( \ - const int *flat_node2pin_start_map, \ - const int *flat_node2pin_map, \ - const int *pin2net_map, \ - const int *flat_net2pin, \ - const T *net_weights, \ - int num_nodes, \ - T *out \ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt deleted file mode 100644 index 8e84a8bc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -set(OP_NAME rmst_wl) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${FLUTE_INCLUDE_DIRS} - ) - -set(LINK_LIBS utility_cxx - ${FLUTE_LINK_DIRS}/libflute.a - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/rmst_wl.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py deleted file mode 100644 index 7356da9b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py deleted file mode 100644 index 33dce44c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py +++ /dev/null @@ -1,50 +0,0 @@ -## -# @file rmst_wl.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# - -import torch -from torch.autograd import Function -from torch import nn - -import dreamplace.ops.rmst_wl.rmst_wl_cpp as rmst_wl_cpp - -class RMSTWLFunction(Function): - """compute half-perimeter wirelength. - @param pos pin location (x array, y array), not cell location - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param ignore_net_degree ignore nets with degree larger than some value - """ - @staticmethod - def forward(ctx, pos, flat_netpin, netpin_start, ignore_net_degree, read_lut_flag, POWVFILE, POSTFILE): - output = pos.new_empty(netpin_start.numel()-1) - if pos.is_cuda: - assert 0, "CUDA version NOT IMPLEMENTED" - rmst_wl_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, ignore_net_degree, output) - else: - rmst_wl_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, ignore_net_degree, read_lut_flag, POWVFILE, POSTFILE, output) - return output - - -class RMSTWL(nn.Module): - def __init__(self, flat_netpin, netpin_start, ignore_net_degree=None, POWVFILE="POWV9.dat", POSTFILE="POST9.dat"): - super(RMSTWL, self).__init__() - self.flat_netpin = flat_netpin - self.netpin_start = netpin_start - if ignore_net_degree is None: - self.ignore_net_degree = self.flat_netpin.numel() - else: - self.ignore_net_degree = ignore_net_degree - self.POWVFILE = POWVFILE - self.POSTFILE = POSTFILE - def forward(self, pos, read_lut_flag): - return RMSTWLFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.ignore_net_degree, - read_lut_flag, - self.POWVFILE, - self.POSTFILE - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp deleted file mode 100644 index b14897e4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file rmst_wl.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -extern "C" -{ -#include -} - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeRMSTWLLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const int ignore_net_degree, - int num_nets, - int read_lut_flag, - const char* POWVFILE, - const char* POSTFILE, - T* rmst_wl - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel()&1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -int rmst_wl_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - int ignore_net_degree, - int read_lut_flag, - const char* POWVFILE, - const char* POSTFILE, - at::Tensor rmst_wl) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(rmst_wl); - CHECK_CONTIGUOUS(rmst_wl); - - int ret = 0; - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeRMSTWLLauncher", [&] { - computeRMSTWLLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t)+pos.numel()/2, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - ignore_net_degree, - netpin_start.numel()-1, - read_lut_flag, - POWVFILE, - POSTFILE, - DREAMPLACE_TENSOR_DATA_PTR(rmst_wl, scalar_t) - ); - }); - return ret; -} - -template -int computeRMSTWLLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const int ignore_net_degree, - int num_nets, - int read_lut_flag, - const char* POWVFILE, - const char* POSTFILE, - T* rmst_wl - ) -{ - // read look-up table for flute - if (read_lut_flag) - { - readLUT(POWVFILE, POSTFILE); - } - // temporary store x and y positions - std::vector vx (ignore_net_degree, 0); - std::vector vy (ignore_net_degree, 0); - int scale = 1000; // scale factor, flute only supports integer - for (int i = 0; i < num_nets; ++i) - { - int degree = netpin_start[i+1]-netpin_start[i]; - // ignore large degree nets - if (degree >= ignore_net_degree) - { - rmst_wl[i] = 0; - continue; - } - - std::fill(vx.begin(), vx.end(), 0); - std::fill(vy.begin(), vy.end(), 0); - for (int j = netpin_start[i], k = 0; j < netpin_start[i+1]; ++j, ++k) - { - vx[k] = x[flat_netpin[j]]*scale; - vy[k] = y[flat_netpin[j]]*scale; - } - int wl = flute_wl(degree, vx.data(), vy.data(), ACCURACY); - rmst_wl[i] = wl/(T)scale; - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::rmst_wl_forward, "RMSTWL forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt deleted file mode 100644 index 9cbb90a9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -set(OP_NAME rudy) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/rudy.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) - -add_pytorch_extension(${TARGET_NAME}_cuda - src/rudy_cuda.cpp - src/rudy_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py deleted file mode 100644 index 7caf7dcd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Dec 2019 -# - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py deleted file mode 100644 index 7f1e822d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py +++ /dev/null @@ -1,106 +0,0 @@ -''' -@File rudy.py -@Author: Jake Gu (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) -@Date Apr 2023 -''' - -import math -import torch -from torch import nn -from torch.autograd import Function -import matplotlib.pyplot as plt -import pdb - -import dreamplacefpga.ops.rudy.rudy_cpp as rudy_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.rudy.rudy_cuda as rudy_cuda - -class Rudy(nn.Module): - def __init__(self, - netpin_start, flat_netpin, net_weights, - xl, xh, yl, yh, - num_bins_x, num_bins_y, - unit_horizontal_capacity, - unit_vertical_capacity, - deterministic_flag, - initial_horizontal_utilization_map=None, - initial_vertical_utilization_map=None, - num_threads=None - ): - super(Rudy, self).__init__() - self.netpin_start = netpin_start - self.flat_netpin = flat_netpin - self.net_weights = net_weights - self.xl = xl - self.yl = yl - self.xh = xh - self.yh = yh - self.num_threads = num_threads - self.num_bins_x = num_bins_x - self.num_bins_y = num_bins_y - self.bin_size_x = (xh - xl) / num_bins_x - self.bin_size_y = (yh - yl) / num_bins_y - - # initialize parameters - self.unit_horizontal_capacity = unit_horizontal_capacity - self.unit_vertical_capacity = unit_vertical_capacity - - self.deterministic_flag = deterministic_flag - - self.initial_horizontal_utilization_map = initial_horizontal_utilization_map - self.initial_vertical_utilization_map = initial_vertical_utilization_map - - #plt.imsave("rudy_initial.png", (self.initial_horizontal_utilization_map + self.initial_vertical_utilization_map).data.cpu().numpy().T, origin='lower') - - def forward(self, pin_pos): - horizontal_utilization_map = torch.zeros((self.num_bins_x, self.num_bins_y), dtype=pin_pos.dtype, device=pin_pos.device) - vertical_utilization_map = torch.zeros_like(horizontal_utilization_map) - if pin_pos.is_cuda: - rudy_cuda.forward( - pin_pos, - self.netpin_start, - self.flat_netpin, - self.net_weights, - self.bin_size_x, - self.bin_size_y, - self.xl, - self.yl, - self.xh, - self.yh, - self.num_bins_x, - self.num_bins_y, - self.deterministic_flag, - horizontal_utilization_map, - vertical_utilization_map - ) - else: - rudy_cpp.forward( - pin_pos, - self.netpin_start, - self.flat_netpin, - self.net_weights, - self.bin_size_x, - self.bin_size_y, - self.xl, - self.yl, - self.xh, - self.yh, - self.num_bins_x, - self.num_bins_y, - self.num_threads, - self.deterministic_flag, - horizontal_utilization_map, - vertical_utilization_map - ) - - # convert demand to utilization in each bin - bin_area = self.bin_size_x * self.bin_size_y - horizontal_utilization_map.mul_(1 / (bin_area * self.unit_horizontal_capacity)) - vertical_utilization_map.mul_(1 / (bin_area * self.unit_vertical_capacity)) - - # infinity norm - route_utilization_map = torch.max(horizontal_utilization_map.abs_(), vertical_utilization_map.abs_()) - - return route_utilization_map - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h deleted file mode 100644 index 371dc002..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef DREAMPLACE_ROUTABILITY_PARAMETERS_H -#define DREAMPLACE_ROUTABILITY_PARAMETERS_H - -/// The function returns the mean wirelength weight for a net with 'num_pins' pins using wiring distribution map (WDM). -/// For more details, refer the paper 'RISA: Accurate and efficient placement routability modeling' -// WARNING: the 'low ... high' syntax below is a GCC extension -// It is not guaranteed to be supported by other compilers -#define DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT \ - T netWiringDistributionMapWeight(int num_pins) \ - { \ - \ - switch (num_pins) \ - { \ - case 1: case 2: case 3: \ - return 1.0000; \ - case 4: \ - return 1.0828; \ - case 5: \ - return 1.1536; \ - case 6: \ - return 1.2206; \ - case 7: \ - return 1.2823; \ - case 8: \ - return 1.3385; \ - case 9: \ - return 1.3991; \ - case 10: \ - return 1.4493; \ - case 11: case 12: case 13: case 14: case 15: \ - return 1.6899; \ - case 16: case 17: case 18: case 19: case 20: \ - return 1.8924; \ - case 21: case 22: case 23: case 24: case 25: \ - return 2.0743; \ - case 26: case 27: case 28: case 29: case 30: \ - return 2.2334; \ - case 31: case 32: case 33: case 34: case 35: \ - return 2.3892; \ - case 36: case 37: case 38: case 39: case 40: \ - return 2.5356; \ - case 41: case 42: case 43: case 44: case 45: \ - return 2.6625; \ - default: \ - return 2.7933; \ - } \ - } - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp deleted file mode 100644 index 53a46c59..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/** - * @file rudy.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/torch.h" -#include "utility/src/utils.h" -#include "rudy/src/parameters.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -template -inline DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT; - -// fill the demand map net by net -template -int rudyLauncher( - const T *pin_pos_x, - const T *pin_pos_y, - const int *netpin_start, - const int *flat_netpin, - const T *net_weights, - const T bin_size_x, - const T bin_size_y, - T xl, T yl, - T xh, T yh, - int num_bins_x, - int num_bins_y, - int num_nets, - int num_threads, - AtomicOp atomic_add_op, - typename AtomicOp::type *horizontal_utilization_map, - typename AtomicOp::type *vertical_utilization_map) -{ - const T inv_bin_size_x = 1.0 / bin_size_x; - const T inv_bin_size_y = 1.0 / bin_size_y; - - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); - #pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - T x_max = -std::numeric_limits::max(); - T x_min = std::numeric_limits::max(); - T y_max = -std::numeric_limits::max(); - T y_min = std::numeric_limits::max(); - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - const T xx = pin_pos_x[pin_id]; - x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); - x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); - const T yy = pin_pos_y[pin_id]; - y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); - y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); - } - - // compute the bin box that this net will affect - int bin_index_xl = int((x_min - xl) * inv_bin_size_x); - int bin_index_xh = int((x_max - xl) * inv_bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - int bin_index_yl = int((y_min - yl) * inv_bin_size_y); - int bin_index_yh = int((y_max - yl) * inv_bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - T wt = netWiringDistributionMapWeight(netpin_start[i + 1] - netpin_start[i]); - if (net_weights) - { - wt *= net_weights[i]; - } - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - T bin_xl = xl + x * bin_size_x; - T bin_yl = yl + y * bin_size_y; - T bin_xh = bin_xl + bin_size_x; - T bin_yh = bin_yl + bin_size_y; - T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * - DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); - overlap *= wt; - int index = x * num_bins_y + y; - // Following Wuxi's implementation, a tolerance is added to avoid 0-size bounding box - atomic_add_op(&horizontal_utilization_map[index], overlap / (y_max - y_min + std::numeric_limits::epsilon())); - atomic_add_op(&vertical_utilization_map[index], overlap / (x_max - x_min + std::numeric_limits::epsilon())); - } - } - } - return 0; -} - -void rudy_forward( - at::Tensor pin_pos, - at::Tensor netpin_start, - at::Tensor flat_netpin, - at::Tensor net_weights, - double bin_size_x, - double bin_size_y, - double xl, - double yl, - double xh, - double yh, - int num_bins_x, - int num_bins_y, - int num_threads, - int deterministic_flag, - at::Tensor horizontal_utilization_map, - at::Tensor vertical_utilization_map - ) -{ - CHECK_FLAT(pin_pos); - CHECK_EVEN(pin_pos); - CHECK_CONTIGUOUS(pin_pos); - - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pin_pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pin_pos, "rudyLauncher", [&] { - if (deterministic_flag == 1) { - double diearea = (xh - xl) * (yh - yl); - int integer_bits = DREAMPLACE_STD_NAMESPACE::max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = DREAMPLACE_STD_NAMESPACE::max(64 - integer_bits, 0); - long scale_factor = (1L << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - - std::vector horizontal_buf_map(num_bins, 0); - std::vector vertical_buf_map(num_bins, 0); - AtomicAdd atomic_add_op(scale_factor); - - rudyLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - (net_weights.numel()) ? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, - bin_size_x, bin_size_y, xl, yl, xh, yh, - num_bins_x, num_bins_y, num_nets, num_threads, - atomic_add_op, horizontal_buf_map.data(), vertical_buf_map.data()); - - scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), - horizontal_buf_map.data(), 1.0 / scale_factor, num_bins, num_threads); - scaleAdd(DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t), - vertical_buf_map.data(), 1.0 / scale_factor, num_bins, num_threads); - } else { - AtomicAdd atomic_add_op; - rudyLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - (net_weights.numel()) ? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, - bin_size_x, bin_size_y, xl, yl, xh, yh, - num_bins_x, num_bins_y, num_nets, num_threads, - atomic_add_op, - DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t)); - } - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::rudy_forward, "compute RUDY map"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp deleted file mode 100644 index 009430b7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @file rudy_cuda.cpp - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) - * @date Apr 2023 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -// fill the demand map net by net -template -int rudyCudaLauncher(const T *pin_pos_x, - const T *pin_pos_y, - const int *netpin_start, - const int *flat_netpin, - const T *net_weights, - const T bin_size_x, - const T bin_size_y, - const T xl, const T yl, - const T xh, const T yh, - const int num_bins_x, - const int num_bins_y, - const int num_nets, - bool deterministic_flag, - T *horizontal_utilization_map, - T *vertical_utilization_map); - -void rudy_forward( - at::Tensor pin_pos, - at::Tensor netpin_start, - at::Tensor flat_netpin, - at::Tensor net_weights, - double bin_size_x, - double bin_size_y, - double xl, - double yl, - double xh, - double yh, - int num_bins_x, - int num_bins_y, - int deterministic_flag, - at::Tensor horizontal_utilization_map, - at::Tensor vertical_utilization_map - ) -{ - CHECK_FLAT(pin_pos); - CHECK_EVEN(pin_pos); - CHECK_CONTIGUOUS(pin_pos); - - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pin_pos.numel() / 2; - - // Call the cuda kernel launcher - DREAMPLACE_DISPATCH_FLOATING_TYPES(pin_pos, "rudyCudaLauncher", [&] { - rudyCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pin_pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - (net_weights.numel())? DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t) : nullptr, - bin_size_x, bin_size_y, - xl, yl, xh, yh, - num_bins_x, num_bins_y, num_nets, - (bool)deterministic_flag, - DREAMPLACE_TENSOR_DATA_PTR(horizontal_utilization_map, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(vertical_utilization_map, scalar_t)); - }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::rudy_forward, "compute RUDY map (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu deleted file mode 100644 index e99b5114..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu +++ /dev/null @@ -1,190 +0,0 @@ -/** - * @file rudy_cuda_kernel.cu - * @author Zixuan Jiang, Jiaqi Gu, Yibo Lin (DREAMPlace) - * @date Dec 2019 - * @brief Compute the RUDY/RISA map for routing demand. - * A routing/pin utilization estimator based on the following two papers - * "Fast and Accurate Routing Demand Estimation for efficient Routability-driven Placement", by Peter Spindler, DATE'07 - * "RISA: Accurate and Efficient Placement Routability Modeling", by Chih-liang Eric Cheng, ICCAD'94 - */ - -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" -#include "rudy/src/parameters.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -inline __device__ DEFINE_NET_WIRING_DISTRIBUTION_MAP_WEIGHT; - -template -__global__ void rudy( - const T *pin_pos_x, - const T *pin_pos_y, - const int *netpin_start, - const int *flat_netpin, - const T *net_weights, - const T bin_size_x, - const T bin_size_y, - const T xl, const T yl, - const T xh, const T yh, - const int num_bins_x, - const int num_bins_y, - const int num_nets, - AtomicOp atomic_add_op, - typename AtomicOp::type *horizontal_utilization_map, - typename AtomicOp::type *vertical_utilization_map) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nets) - { - const int start = netpin_start[i]; - const int end = netpin_start[i + 1]; - - T x_max = -cuda::numeric_limits::max(); - T x_min = cuda::numeric_limits::max(); - T y_max = -cuda::numeric_limits::max(); - T y_min = cuda::numeric_limits::max(); - - for (int j = start; j < end; ++j) - { - int pin_id = flat_netpin[j]; - const T xx = pin_pos_x[pin_id]; - x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); - x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); - const T yy = pin_pos_y[pin_id]; - y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); - y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); - } - - // compute the bin box that this net will affect - int bin_index_xl = int((x_min - xl) / bin_size_x); - int bin_index_xh = int((x_max - xl) / bin_size_x) + 1; - bin_index_xl = DREAMPLACE_STD_NAMESPACE::max(bin_index_xl, 0); - bin_index_xh = DREAMPLACE_STD_NAMESPACE::min(bin_index_xh, num_bins_x); - - int bin_index_yl = int((y_min - yl) / bin_size_y); - int bin_index_yh = int((y_max - yl) / bin_size_y) + 1; - bin_index_yl = DREAMPLACE_STD_NAMESPACE::max(bin_index_yl, 0); - bin_index_yh = DREAMPLACE_STD_NAMESPACE::min(bin_index_yh, num_bins_y); - - T wt = netWiringDistributionMapWeight(end - start); - if (net_weights) - { - wt *= net_weights[i]; - } - - for (int x = bin_index_xl; x < bin_index_xh; ++x) - { - for (int y = bin_index_yl; y < bin_index_yh; ++y) - { - T bin_xl = xl + x * bin_size_x; - T bin_yl = yl + y * bin_size_y; - T bin_xh = bin_xl + bin_size_x; - T bin_yh = bin_yl + bin_size_y; - T overlap = DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(x_max, bin_xh) - DREAMPLACE_STD_NAMESPACE::max(x_min, bin_xl), (T)0) * - DREAMPLACE_STD_NAMESPACE::max(DREAMPLACE_STD_NAMESPACE::min(y_max, bin_yh) - DREAMPLACE_STD_NAMESPACE::max(y_min, bin_yl), (T)0); - overlap *= wt; - int index = x * num_bins_y + y; - // Following Wuxi's implementation, a tolerance is added to avoid 0-size bounding box - atomic_add_op(&horizontal_utilization_map[index], overlap / (y_max - y_min + cuda::numeric_limits::epsilon())); - atomic_add_op(&vertical_utilization_map[index], overlap / (x_max - x_min + cuda::numeric_limits::epsilon())); - } - } - } -} - -// fill the demand map net by net -template -int rudyCudaLauncher(const T *pin_pos_x, - const T *pin_pos_y, - const int *netpin_start, - const int *flat_netpin, - const T *net_weights, - const T bin_size_x, - const T bin_size_y, - const T xl, const T yl, - const T xh, const T yh, - const int num_bins_x, - const int num_bins_y, - const int num_nets, - bool deterministic_flag, - T *horizontal_utilization_map, - T *vertical_utilization_map) -{ - if (deterministic_flag) - { - // total die area - double diearea = (xh - xl) * (yh - yl); - int integer_bits = max((int)ceil(log2(diearea)) + 1, 32); - int fraction_bits = max(64 - integer_bits, 0); - unsigned long long int scale_factor = (1UL << fraction_bits); - int num_bins = num_bins_x * num_bins_y; - unsigned long long int *buf_map = NULL; - allocateCUDA(buf_map, num_bins*2, unsigned long long int); - - unsigned long long int *horizontal_buf_map = buf_map; - unsigned long long int *vertical_buf_map = buf_map + num_bins; - - AtomicAddCUDA atomic_add_op(scale_factor); - - int thread_count = 512; - int block_count = ceilDiv(num_bins, thread_count); - - copyScaleArray<<>>( - horizontal_buf_map, horizontal_utilization_map, scale_factor, num_bins); - copyScaleArray<<>>( - vertical_buf_map, vertical_utilization_map, scale_factor, num_bins); - - block_count = ceilDiv(num_nets, thread_count); - rudy<<>>( - pin_pos_x, pin_pos_y, netpin_start, - flat_netpin, net_weights, bin_size_x, - bin_size_y, xl, yl, xh, yh, - num_bins_x, num_bins_y, num_nets, - atomic_add_op, - horizontal_buf_map, - vertical_buf_map - ); - - block_count = ceilDiv(num_bins, thread_count); - copyScaleArray<<>>( - horizontal_utilization_map, horizontal_buf_map, T(1.0 / scale_factor), num_bins); - copyScaleArray<<>>( - vertical_utilization_map, vertical_buf_map, T(1.0 / scale_factor), num_bins); - - destroyCUDA(buf_map); - } else - { - AtomicAddCUDA atomic_add_op; - int thread_count = 512; - int block_count = ceilDiv(num_nets, thread_count); - rudy<<>>( - pin_pos_x, pin_pos_y, netpin_start, - flat_netpin, net_weights, bin_size_x, - bin_size_y, xl, yl, xh, yh, - num_bins_x, num_bins_y, num_nets, - atomic_add_op, - horizontal_utilization_map, - vertical_utilization_map - ); - } - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int rudyCudaLauncher( \ - const T *pin_pos_x, const T *pin_pos_y, \ - const int *netpin_start, const int *flat_netpin, \ - const T *net_weights, const T bin_size_x, \ - const T bin_size_y, const T xl, const T yl, \ - const T xh, const T yh, const int num_bins_x, \ - const int num_bins_y, const int num_nets, \ - bool deterministic_flag, \ - T *horizontal_utilization_map, \ - T *vertical_utilization_map); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt deleted file mode 100644 index 7aef9a01..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(OP_NAME sortNode2Pin) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/sortNode2Pin.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) -add_pytorch_extension(${TARGET_NAME}_cuda - src/sortNode2Pin_cuda.cpp - src/sortNode2Pin_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py deleted file mode 100644 index 11284438..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -## -# @file __init__.py -# @author Rachel Selina (DREAMPlaceFPGA-PL) -# @date Nov 2021 -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py deleted file mode 100644 index c460595c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py +++ /dev/null @@ -1,58 +0,0 @@ -## -# @file sortNode2Pin.py -# @author Rachel Selina (DREAMPlaceFPGA-PL) -# @date Nov 2021 -# - -import torch -from torch.autograd import Function -from torch import nn -import numpy as np -import pdb -import time - -import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin_cpp as sortNode2Pin_cpp -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.sortNode2Pin.sortNode2Pin_cuda as sortNode2Pin_cuda - -class SortNode2Pin(nn.Module): - """ - @brief Compute wirelength preconditioner. - """ - def __init__(self, flat_node2pin_start, flat_node2pin, num_nodes, device, num_threads): - """ - @brief initialization - @param flat_node2pin_start_map - @param flat_node2pin_map node to pin map - @param num_nodes - @param num_threads - """ - super(SortNode2Pin, self).__init__() - self.flat_node2pin_start=flat_node2pin_start - self.flat_node2pin=flat_node2pin - self.num_nodes=num_nodes - self.device=device - self.num_threads = num_threads - - def forward(self, sorted_pin_map): - node2pinId = torch.zeros(self.num_nodes, dtype=torch.int32, device=self.device) - - if node2pinId.is_cuda: - sortNode2Pin_cuda.forward( - self.flat_node2pin_start, - self.flat_node2pin, - sorted_pin_map, - self.num_nodes, - node2pinId) - else: - sortNode2Pin_cpp.forward( - self.flat_node2pin_start, - self.flat_node2pin, - sorted_pin_map, - self.num_nodes, - self.num_threads, - node2pinId) - - return node2pinId - diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp deleted file mode 100644 index 0dc31ba6..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file sortNode2Pin.cpp - * @author Rachel Selina (DREAMPlaceFPGA-PL) - * @date Nov 2021 - * @brief sort node2pin - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -int computeSortNode2PinLauncher( - const int *flat_node2pin_start_map, - int *flat_node2pin_map, - const int *sorted_pin_map, - const int num_nodes, - const int num_threads, - int *out - ); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Sort node2pin -void forward( - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor sorted_pin_map, - int num_nodes, - int num_threads, - at::Tensor out) -{ - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - //CHECK_FLAT(flat_node2pin_map); - //CHECK_CONTIGUOUS(flat_node2pin_map); - CHECK_FLAT(sorted_pin_map); - CHECK_CONTIGUOUS(sorted_pin_map); - - //DREAMPLACE_DISPATCH_FLOATING_TYPES(out.type(), "computeSortNode2PinLauncher", [&] { - computeSortNode2PinLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_pin_map, int), - num_nodes, - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(out, int) - ); - // }); -} - -int computeSortNode2PinLauncher( - const int *flat_node2pin_start_map, - int *flat_node2pin_map, - const int *sorted_pin_map, - const int num_nodes, - const int num_threads, - int *out - ) -{ - #pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < num_nodes; ++i) - { - int beg = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i+1]; - - std::sort(flat_node2pin_map+beg, flat_node2pin_map+end, [&sorted_pin_map](const auto &a, const auto &b){return sorted_pin_map[a] < sorted_pin_map[b];}); - out[i] = sorted_pin_map[flat_node2pin_map[beg]]; - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "SortNode2Pin forward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp deleted file mode 100644 index d78907a2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file sortNode2Pin_cuda.cpp - * @author Rachel Selina (DREAMPlaceFPGA-PL) - * @date Nov 2021 - * @brief sort node2pin - */ - -#include "utility/src/torch.h" -#include "utility/src/Msg.h" -using namespace torch::indexing; - -DREAMPLACE_BEGIN_NAMESPACE - -int computeSortNode2PinCudaLauncher( - const int *flat_node2pin_start_map, - int *flat_node2pin_map, - const int *sorted_pin_map, - const int num_nodes, - int *out - ); - - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - - -/// @brief sort node2pin map -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -void forward( - at::Tensor flat_node2pin_start_map, - at::Tensor flat_node2pin_map, - at::Tensor sorted_pin_map, - int num_nodes, - at::Tensor out - ) -{ - CHECK_FLAT(flat_node2pin_start_map); - CHECK_CONTIGUOUS(flat_node2pin_start_map); - //CHECK_FLAT(flat_node2pin_map); - //CHECK_CONTIGUOUS(flat_node2pin_map); - CHECK_FLAT(sorted_pin_map); - CHECK_CONTIGUOUS(sorted_pin_map); - - //DREAMPLACE_DISPATCH_FLOATING_TYPES(out.type(), "computeSortNode2PinCudaLauncher", [&] { - computeSortNode2PinCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int), - DREAMPLACE_TENSOR_DATA_PTR(sorted_pin_map, int), - num_nodes, - DREAMPLACE_TENSOR_DATA_PTR(out, int) - ); - // }); -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("forward", &DREAMPLACE_NAMESPACE::forward, "SortNode2Pin forward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu deleted file mode 100644 index 5b6b900e..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file sortNode2Pin.cpp - * @author Rachel Selina (DREAMPlaceFPGA-PL) - * @date Nov 2021 - * @brief sort node2pin - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utility/src/utils.cuh" -#include "utility/src/limits.h" - -DREAMPLACE_BEGIN_NAMESPACE - -__global__ void computeSortNode2Pin( - const int *flat_node2pin_start_map, - int *flat_node2pin_map, - const int *sorted_pin_map, - const int num_nodes, - int *out - ) -{ - const int i = threadIdx.x + blockDim.x * blockIdx.x; - if (i < num_nodes) - { - int beg = flat_node2pin_start_map[i]; - int end = flat_node2pin_start_map[i+1]; - - //Sort - for (int ix = beg+1; ix < end; ++ix) - { - for (int jx = beg; jx < end-1; ++jx) - { - if (sorted_pin_map[flat_node2pin_map[jx]] > sorted_pin_map[flat_node2pin_map[jx+1]]) - { - int val = flat_node2pin_map[jx]; - flat_node2pin_map[jx] = flat_node2pin_map[jx+1]; - flat_node2pin_map[jx+1] = val; - } - } - } - //Sort - - out[i] = sorted_pin_map[flat_node2pin_map[beg]]; - } -} - -int computeSortNode2PinCudaLauncher( - const int *flat_node2pin_start_map, - int *flat_node2pin_map, - const int *sorted_pin_map, - const int num_nodes, - int *out - ) -{ - int thread_count = 512; - int block_count = ceilDiv(num_nodes, thread_count); - - computeSortNode2Pin<<>>( - flat_node2pin_start_map, - flat_node2pin_map, - sorted_pin_map, - num_nodes, - out - ); - - return 0; -} - -// manually instantiate the template function -#define REGISTER_KERNEL_LAUNCHER() \ - template int computeSortNode2PinCudaLauncher() \ - (const int *flat_node2pin_start_map, int *flat_node2pin_map, \ - const int *sorted_pin_map, const int num_nodes, int *out ); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt deleted file mode 100644 index 421f1261..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -set(OP_NAME utility) - -file(GLOB CPP_SOURCES - "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c" - ) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS "") - -add_torch_extension(${TARGET_NAME}_cxx ${CPP_SOURCES} - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS} - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h deleted file mode 100644 index ff8f4612..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file Box.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef _DREAMPLACE_UTILITY_BOX_H -#define _DREAMPLACE_UTILITY_BOX_H - -#include -#include "utility/src/limits.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct Box -{ - T xl; - T yl; - T xh; - T yh; - - /// @brief default constructor - DREAMPLACE_HOST_DEVICE Box() { invalidate(); } - - /// @brief constructor - /// @param xxl xl - /// @param yyl yl - /// @param xxh xh - /// @param yyh yh - DREAMPLACE_HOST_DEVICE Box(T xxl, T yyl, T xxh, T yyh) - : xl(xxl), yl(yyl), xh(xxh), yh(yyh) {} - /// @brief invalidate the box - DREAMPLACE_HOST_DEVICE void invalidate() { - xl = cuda::numeric_limits::max(); - yl = cuda::numeric_limits::max(); - xh = cuda::numeric_limits::lowest(); - yh = cuda::numeric_limits::lowest(); - } - /// @brief check if the box is valid - DREAMPLACE_HOST_DEVICE bool valid() const { return (xl <= xh) && (yl <= yh); } - /// @brief encompass a point - /// @param x - /// @param y - DREAMPLACE_HOST_DEVICE void encompass(T x, T y) { - xl = DREAMPLACE_STD_NAMESPACE::min(xl, x); - xh = DREAMPLACE_STD_NAMESPACE::max(xh, x); - yl = DREAMPLACE_STD_NAMESPACE::min(yl, y); - yh = DREAMPLACE_STD_NAMESPACE::min(yh, y); - } - /// @brief encompass a box - /// @param xxl xl - /// @param yyl yl - /// @param xxh xh - /// @param yyh yh - DREAMPLACE_HOST_DEVICE void encompass(T xxl, T yyl, T xxh, T yyh) { - encompass(xxl, yyl); - encompass(xxh, yyh); - } - /// @brief bloat x direction by 2*dx, and y direction by 2*dy - /// @param dx - /// @param dy - DREAMPLACE_HOST_DEVICE void bloat(T dx, T dy) { - xl -= dx; - xh += dx; - yl -= dy; - yh += dy; - } - /// @brief check if a point is contained by the box - /// @param x - /// @param y - /// @return true if contains - DREAMPLACE_HOST_DEVICE bool contains(T x, T y) const { - return xl <= x && x <= xh && yl <= y && y <= yh; - } - /// @brief check if a box is contained by the box - /// @param xxl xl - /// @param yyl yl - /// @param xxh xh - /// @param yyh yh - /// @return true if contains - DREAMPLACE_HOST_DEVICE bool contains(T xxl, T yyl, T xxh, T yyh) const { - return contains(xxl, yyl) && contains(xxh, yyh); - } - /// @return width of the box - DREAMPLACE_HOST_DEVICE T width() const { return xh - xl; } - /// @return height of the box - DREAMPLACE_HOST_DEVICE T height() const { return yh - yl; } - /// @return x coordinate of the center of the box - DREAMPLACE_HOST_DEVICE T center_x() const { return (xl + xh) / 2; } - /// @return y coordinate of the center of the box - DREAMPLACE_HOST_DEVICE T center_y() const { return (yl + yh) / 2; } - /// @return center manhattan distance to another box - DREAMPLACE_HOST_DEVICE T center_distance(const Box& rhs) const { - return fabs(rhs.center_x() - center_x()) + - fabs(rhs.center_y() - center_y()); - } - /// @return area of the box - DREAMPLACE_HOST_DEVICE T area() const { return width() * height(); } - /// @brief print the box - DREAMPLACE_HOST_DEVICE void print() const { - printf("(%g, %g, %g, %g)\n", (double)xl, (double)yl, (double)xh, - (double)yh); - } -}; - -/// @brief simplest box for shared memory -/// Non-empty constructor may result in data race -template -struct SharedBox { - T xl; - T yl; - T xh; - T yh; -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h deleted file mode 100644 index e0be9fa9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @file ComplexNumber.h - * @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) - * @date Aug 2019 - * @brief Complex number for CPU - */ - -#ifndef DREAMPLACE_UTILITY_COMPLEXNUMBER_H -#define DREAMPLACE_UTILITY_COMPLEXNUMBER_H - -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct ComplexType -{ - T x; - T y; - DREAMPLACE_HOST_DEVICE ComplexType() { - x = 0; - y = 0; - } - - DREAMPLACE_HOST_DEVICE ComplexType(T real, T imag) { - x = real; - y = imag; - } - - DREAMPLACE_HOST_DEVICE ~ComplexType() {} -}; - -template -inline DREAMPLACE_HOST_DEVICE ComplexType complexMul( - const ComplexType &x, const ComplexType &y) { - ComplexType res; - res.x = x.x * y.x - x.y * y.y; - res.y = x.x * y.y + x.y * y.x; - return res; -} - -template -inline DREAMPLACE_HOST_DEVICE T RealPartOfMul(const ComplexType &x, - const ComplexType &y) { - return x.x * y.x - x.y * y.y; -} - -template -inline DREAMPLACE_HOST_DEVICE T ImaginaryPartOfMul(const ComplexType &x, - const ComplexType &y) { - return x.x * y.y + x.y * y.x; -} - -template -inline DREAMPLACE_HOST_DEVICE ComplexType complexAdd( - const ComplexType &x, const ComplexType &y) { - ComplexType res; - res.x = x.x + y.x; - res.y = x.y + y.y; - return res; -} - -template -inline DREAMPLACE_HOST_DEVICE ComplexType complexSubtract( - const ComplexType &x, const ComplexType &y) { - ComplexType res; - res.x = x.x - y.x; - res.y = x.y - y.y; - return res; -} - -template -inline DREAMPLACE_HOST_DEVICE ComplexType complexConj( - const ComplexType &x) { - ComplexType res; - res.x = x.x; - res.y = -x.y; - return res; -} - -template -inline DREAMPLACE_HOST_DEVICE ComplexType complexMulConj( - const ComplexType &x, const ComplexType &y) { - ComplexType res; - res.x = x.x * y.x - x.y * y.y; - res.y = -(x.x * y.y + x.y * y.x); - return res; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh deleted file mode 100644 index 8da5de47..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh +++ /dev/null @@ -1,566 +0,0 @@ -/** - * @file DetailedPlaceDB.cuh - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDB_CUH -#define _DREAMPLACE_UTILITY_DETAILEDPLACEDB_CUH - -#include -#include "utility/src/utils.cuh" -#include "utility/src/utils_cub.cuh" -#include "legality_check/src/legality_check.h" -#include "draw_place/src/draw_place.h" -//#include -//#include -//#include -//#include -//#include - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct Space -{ - T xl; - T xh; -}; - -struct BinMapIndex -{ - int bin_id; - int sub_id; -}; - -struct RowMapIndex -{ - int row_id; - int sub_id; -}; - -/// @brief a wrapper class of required data for detailed placement -template -struct DetailedPlaceDB -{ - typedef T type; - - const T* init_x; - const T* init_y; - const T* node_size_x; - const T* node_size_y; - const T* flat_region_boxes; ///< number of boxes x 4 - const int* flat_region_boxes_start; ///< number of regions + 1 - const int* node2fence_region_map; ///< length of number of movable cells - T* x; - T* y; - const int* flat_net2pin_map; - const int* flat_net2pin_start_map; - const int* pin2net_map; - const int* flat_node2pin_map; - const int* flat_node2pin_start_map; - const int* pin2node_map; - const T* pin_offset_x; - const T* pin_offset_y; - const unsigned char* net_mask; - T xl; - T yl; - T xh; - T yh; - T site_width; - T row_height; - T bin_size_x; - T bin_size_y; - int num_bins_x; - int num_bins_y; - int num_sites_x; - int num_sites_y; - int num_nodes; - int num_movable_nodes; - int num_nets; - int num_pins; - int num_regions; ///< number of regions for flat_region_boxes and flat_region_boxes_start - - inline __device__ int pos2site_x(T xx) const - { - return min(max((int)floorDiv((xx-xl), site_width), 0), num_sites_x-1); - } - inline __device__ int pos2site_y(T yy) const - { - return min(max((int)floorDiv((yy-yl), row_height), 0), num_sites_y-1); - } - /// @brief site index as an upper bound - inline __device__ int pos2site_ub_x(T xx) const - { - return min(max(ceilDiv((xx-xl), site_width), 1), num_sites_x); - } - /// @brief site index as an upper bound - inline __device__ int pos2site_ub_y(T yy) const - { - return min(max(ceilDiv((yy-yl), row_height), 1), num_sites_y); - } - inline __device__ int pos2bin_x(T xx) const - { - int bx = floorDiv((xx-xl), bin_size_x); - bx = max(bx, 0); - bx = min(bx, num_bins_x-1); - return bx; - } - inline __device__ int pos2bin_y(T yy) const - { - int by = floorDiv((yy-yl), bin_size_y); - by = max(by, 0); - by = min(by, num_bins_y-1); - return by; - } - inline __device__ void shift_box_to_layout(Box& box) const - { - box.xl = max(box.xl, xl); - box.xl = min(box.xl, xh); - box.xh = max(box.xh, xl); - box.xh = min(box.xh, xh); - box.yl = max(box.yl, yl); - box.yl = min(box.yl, yh); - box.yh = max(box.yh, yl); - box.yh = min(box.yh, yh); - } - inline __device__ Box box2sitebox(const Box& box) const - { - // xh, yh are exclusive - Box sitebox ( - pos2site_x(box.xl), - pos2site_y(box.yl), - pos2site_ub_x(box.xh), - pos2site_ub_y(box.yh) - ); - - return sitebox; - } - inline __device__ Box box2binbox(const Box& box) const - { - Box binbox ( - pos2bin_x(box.xl), - pos2bin_y(box.yl), - pos2bin_x(box.xh), - pos2bin_y(box.yh) - ); - - return binbox; - } - /// @brief align x coordinate to site - inline __device__ T align2site(T xx) const - { - return (int)floorDiv((xx - xl), site_width) * site_width + xl; - } - /// @brief align x coordinate to site for a space; - /// make sure the space is shrinked. - inline __device__ Space align2site(Space space) const - { - space.xl = ceilDiv((space.xl - xl), site_width) * site_width + xl; - space.xh = floorDiv((space.xh - xl), site_width) * site_width + xl; - return space; - } - /// @brief compute optimal region for a cell - /// The method to compute optimal region ignores the pin offsets of the target cell. - /// If we want to consider the pin offsets, there may not be feasible box for the optimal region. - /// Thus, this is just an approximate optimal region. - /// When using the optimal region, one needs to refer to the center of the cell to the region, or the region completely covers the entire cell. - __device__ Box compute_optimal_region(int node_id, const T* xx, const T* yy) const - { - Box box ( - xh, // some large number - yh, // some large number - xl, // some small number - yl // some small number - ); - for (int node2pin_id = flat_node2pin_start_map[node_id]; node2pin_id < flat_node2pin_start_map[node_id+1]; ++node2pin_id) - { - int node_pin_id = flat_node2pin_map[node2pin_id]; - int net_id = pin2net_map[node_pin_id]; - if (net_mask[net_id]) - { - for (int net2pin_id = flat_net2pin_start_map[net_id]; net2pin_id < flat_net2pin_start_map[net_id+1]; ++net2pin_id) - { - int net_pin_id = flat_net2pin_map[net2pin_id]; - int other_node_id = pin2node_map[net_pin_id]; - if (node_id != other_node_id) - { - box.xl = min(box.xl, xx[other_node_id]+pin_offset_x[net_pin_id]); - box.xh = max(box.xh, xx[other_node_id]+pin_offset_x[net_pin_id]); - box.yl = min(box.yl, yy[other_node_id]+pin_offset_y[net_pin_id]); - box.yh = max(box.yh, yy[other_node_id]+pin_offset_y[net_pin_id]); - } - } - } - } - shift_box_to_layout(box); - - return box; - } - /// @brief compute HPWL for a net - __device__ T compute_net_hpwl(int net_id, const T* xx, const T* yy) const - { - Box box ( - xh, // some large number - yh, // some large number - xl, // some small number - yl // some small number - ); - for (int net2pin_id = flat_net2pin_start_map[net_id]; net2pin_id < flat_net2pin_start_map[net_id+1]; ++net2pin_id) - { - int net_pin_id = flat_net2pin_map[net2pin_id]; - int other_node_id = pin2node_map[net_pin_id]; - box.xl = min(box.xl, xx[other_node_id]+pin_offset_x[net_pin_id]); - box.xh = max(box.xh, xx[other_node_id]+pin_offset_x[net_pin_id]); - box.yl = min(box.yl, yy[other_node_id]+pin_offset_y[net_pin_id]); - box.yh = max(box.yh, yy[other_node_id]+pin_offset_y[net_pin_id]); - } - if (box.xl == xh || box.yl == yh) // use xh/yh as some large number - { - return (T)0; - } - return (box.xh-box.xl) + (box.yh-box.yl); - } - /// @brief compute HPWL for all nets - __device__ T compute_total_hpwl() const - { - //printf("[D] start compute_total_hpwl\n"); - T total_hpwl = 0; - for (int net_id = 0; net_id < num_nets; ++net_id) - { - //if (net_mask[net_id]) - { - total_hpwl += compute_net_hpwl(net_id, x, y); - } - } - //printf("[D] end compute_total_hpwl\n"); - return total_hpwl; - } - /// @brief check whether a cell is within its fence region - __device__ bool inside_fence(int node_id, T xx, T yy) const - { - T node_xl = xx; - T node_yl = yy; - T node_xh = node_xl + node_size_x[node_id]; - T node_yh = node_yl + node_size_y[node_id]; - - bool legal_flag = true; - int region_id = node2fence_region_map[node_id]; - if (region_id < num_regions) - { - int box_bgn = flat_region_boxes_start[region_id]; - int box_end = flat_region_boxes_start[region_id + 1]; - T node_area = (node_xh - node_xl) * (node_yh - node_yl); - // I assume there is no overlap between boxes of a region - // otherwise, preprocessing is required - for (int box_id = box_bgn; box_id < box_end; ++box_id) - { - int box_offset = box_id*4; - T box_xl = flat_region_boxes[box_offset]; - T box_yl = flat_region_boxes[box_offset + 1]; - T box_xh = flat_region_boxes[box_offset + 2]; - T box_yh = flat_region_boxes[box_offset + 3]; - - T dx = max(min(node_xh, box_xh) - max(node_xl, box_xl), (T)0); - T dy = max(min(node_yh, box_yh) - max(node_yl, box_yl), (T)0); - T overlap = dx*dy; - if (overlap > 0) - { - node_area -= overlap; - } - } - if (node_area > 0) // not consumed by boxes within a region - { - legal_flag = false; - } - } - return legal_flag; - } - /// @brief distribute cells to rows - __host__ void make_row2node_map(const T* host_x, const T* host_y, - const T* host_node_size_x, const T* host_node_size_y, - int host_num_nodes, - std::vector >& row2node_map, - int num_threads) const - { - // distribute cells to rows - for (int i = 0; i < host_num_nodes; ++i) - { - T node_yl = host_y[i]; - T node_yh = node_yl+host_node_size_y[i]; - - int row_idxl = floorDiv(node_yl-yl, row_height); - int row_idxh = ceilDiv(node_yh-yl, row_height); - row_idxl = max(row_idxl, 0); - row_idxh = min(row_idxh, num_sites_y); - - for (int row_id = row_idxl; row_id < row_idxh; ++row_id) - { - T row_yl = yl+row_id*row_height; - T row_yh = row_yl+row_height; - - if (node_yl < row_yh && node_yh > row_yl) // overlap with row - { - row2node_map[row_id].push_back(i); - } - } - } - - // sort cells within rows -#ifdef _OPENMP -#pragma omp parallel for num_threads (num_threads) schedule(dynamic, 1) -#endif - for (int i = 0; i < num_sites_y; ++i) - { - auto& row2nodes = row2node_map[i]; - // sort cells within rows according to left edges - std::sort(row2nodes.begin(), row2nodes.end(), - [&] (int node_id1, int node_id2) { - T x1 = host_x[node_id1]; - T x2 = host_x[node_id2]; - return x1 < x2 || (x1 == x2 && node_id1 < node_id2); - }); - // After sorting by left edge, - // there is a special case for fixed cells where - // one fixed cell is completely within another in a row. - // This will cause failure to detect some overlaps. - // We need to remove the "small" fixed cell that is inside another. - if (!row2nodes.empty()) - { - std::vector tmp_nodes; - tmp_nodes.reserve(row2nodes.size()); - tmp_nodes.push_back(row2nodes.front()); - for (int j = 1, je = row2nodes.size(); j < je; ++j) - { - int node_id1 = row2nodes.at(j-1); - int node_id2 = row2nodes.at(j); - // two fixed cells - if (node_id1 >= num_movable_nodes && node_id2 >= num_movable_nodes) - { - T xl1 = host_x[node_id1]; - T xl2 = host_x[node_id2]; - T width1 = host_node_size_x[node_id1]; - T width2 = host_node_size_x[node_id2]; - T xh1 = xl1 + width1; - T xh2 = xl2 + width2; - // only collect node_id2 if its right edge is righter than node_id1 - if (xh1 < xh2) - { - tmp_nodes.push_back(node_id2); - } - } - else - { - tmp_nodes.push_back(node_id2); - } - } - row2nodes.swap(tmp_nodes); - - // sort according to center - std::sort(row2nodes.begin(), row2nodes.end(), - [&] (int node_id1, int node_id2) { - T x1 = host_x[node_id1] + host_node_size_x[node_id1]/2; - T x2 = host_x[node_id2] + host_node_size_x[node_id2]/2; - return x1 < x2 || (x1 == x2 && node_id1 < node_id2); - }); - } - } - } - /// @brief distribute cells to rows - __host__ void make_row2node_map_with_spaces(const T* host_x, const T* host_y, - const T* host_node_size_x, const T* host_node_size_y, - std::vector >& row2node_map, std::vector& node2row_map, std::vector >& spaces, - int num_threads) const - { - make_row2node_map(host_x, host_y, host_node_size_x, host_node_size_y, num_nodes + 2, - row2node_map, num_threads); - - // construct node2row_map - for (int i = 0; i < num_sites_y; ++i) - { - for (unsigned int j = 0; j < row2node_map[i].size(); ++j) - { - int node_id = row2node_map[i][j]; - if (node_id < num_movable_nodes) - { - RowMapIndex& row_id = node2row_map[node_id]; - row_id.row_id = i; - row_id.sub_id = j; - } - } - } - - // construct spaces - for (int i = 0; i < num_sites_y; ++i) - { - for (unsigned int j = 0; j < row2node_map[i].size(); ++j) - { - int node_id = row2node_map[i][j]; - if (node_id < num_movable_nodes) - { - assert(j); - int left_node_id = row2node_map[i][j-1]; - spaces[node_id].xl = host_x[left_node_id] + host_node_size_x[left_node_id]; - assert(j+1 < row2node_map[i].size()); - int right_node_id = row2node_map[i][j+1]; - spaces[node_id].xh = host_x[right_node_id]; - } - } - } - } - /// @brief distribute movable cells to bins according to cell (xl, yl) - /// @param bin2node_map flatten bin map, column-major - /// @param node2bin_index_map the index of cell in bin2node_map - __host__ void make_bin2node_map(const T* host_x, const T* host_y, - const T* host_node_size_x, const T* host_node_size_y, - std::vector >& bin2node_map, std::vector& node2bin_map) const - { - // construct bin2node_map - for (int i = 0; i < num_movable_nodes; ++i) - { - int node_id = i; - T node_x = host_x[node_id] + host_node_size_x[node_id]/2; - T node_y = host_y[node_id] + host_node_size_y[node_id]/2; - - int bx = min(max((int)floorDiv(node_x-xl, bin_size_x), 0), num_bins_x-1); - int by = min(max((int)floorDiv(node_y-yl, bin_size_y), 0), num_bins_y-1); - int bin_id = bx*num_bins_y+by; - int sub_id = bin2node_map.at(bin_id).size(); - bin2node_map.at(bin_id).push_back(node_id); - } - // sort cells within bins - //auto comp = [&] (int node_id1, int node_id2) { - // return host_x[node_id1] < host_x[node_id2] || (host_x[node_id1] == host_x[node_id2] && host_y[node_id1] < host_y[node_id2]); - //}; - //for (auto& bin2nodes : bin2node_map) - //{ - // std::sort(bin2nodes.begin(), bin2nodes.end(), comp); - //} - // construct node2bin_map - for (int bin_id = 0; bin_id < bin2node_map.size(); ++bin_id) - { - for (int sub_id = 0; sub_id < bin2node_map[bin_id].size(); ++sub_id) - { - int node_id = bin2node_map[bin_id][sub_id]; - BinMapIndex& bm_idx = node2bin_map.at(node_id); - bm_idx.bin_id = bin_id; - bm_idx.sub_id = sub_id; - } - } -#ifdef DEBUG - int max_num_nodes_per_bin = 0; - for (int i = 0; i < bin2node_map.size(); ++i) - { - max_num_nodes_per_bin = max(max_num_nodes_per_bin, (int)bin2node_map[i].size()); - } - printf("[D] max_num_nodes_per_bin = %d\n", max_num_nodes_per_bin); -#endif - } - /// @brief check whether placement is legal - bool check_legality(const T* host_x, const T* host_y, const T* host_node_size_x, const T* host_node_size_y) const - { - std::vector host_flat_region_boxes_start (num_regions + 1); - std::vector host_node2fence_region_map (num_movable_nodes); - checkCUDA(cudaMemcpy(host_flat_region_boxes_start.data(), flat_region_boxes_start, sizeof(int)*host_flat_region_boxes_start.size(), cudaMemcpyDeviceToHost)); - checkCUDA(cudaMemcpy(host_node2fence_region_map.data(), node2fence_region_map, sizeof(int)*host_node2fence_region_map.size(), cudaMemcpyDeviceToHost)); - std::vector host_flat_region_boxes (host_flat_region_boxes_start.back()*4); - checkCUDA(cudaMemcpy(host_flat_region_boxes.data(), flat_region_boxes, sizeof(T)*host_flat_region_boxes.size(), cudaMemcpyDeviceToHost)); - - return legalityCheckKernelCPU( - host_x, host_y, - host_node_size_x, host_node_size_y, - host_flat_region_boxes.data(), host_flat_region_boxes_start.data(), host_node2fence_region_map.data(), - xl, yl, xh, yh, - site_width, row_height, - num_nodes, - num_movable_nodes, - num_regions - ); - } - /// @brief draw placement - void draw_place(const char* filename) const - { - drawPlaceLauncher( - x, y, - node_size_x, node_size_y, - pin_offset_x, pin_offset_y, - pin2node_map, - num_nodes, - num_movable_nodes, - 0, - flat_net2pin_start_map[num_nets], - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - filename - ); - } -}; - -/// @brief Automatic determine scaling factor by types -template ::value> -struct HPWLScaleTraits; - -/// @brief For floating point numbers, no scaling. -template -struct HPWLScaleTraits -{ - static constexpr T scale = 1; -}; - -/// @brief For integers, scale. -template -struct HPWLScaleTraits -{ - static constexpr int scale = 1000; -}; - -/// @brief compute total HPWL -/// This function is mainly for evaluation, so the performance is not highly tuned. -/// Consistency is more important. -/// Thus integer is adopted. -template -__global__ void compute_total_hpwl_kernel(DetailedPlaceDB db, const T* xx, const T* yy, V* net_hpwls) -{ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < db.num_nets; i += blockDim.x * gridDim.x) - { - net_hpwls[i] = V(db.compute_net_hpwl(i, xx, yy)*HPWLScaleTraits::scale); - //if (db.net_mask[i]) - //{ - // net_hpwls[i] = V(db.compute_net_hpwl(i, xx, yy)*(T)scale); - //} - //else - //{ - // net_hpwls[i] = 0; - //} - } -} - -template -T compute_total_hpwl(const DetailedPlaceDB& db, const T* xx, const T* yy, V* net_hpwls) -{ - compute_total_hpwl_kernel<<>>(db, xx, yy, net_hpwls); - //auto hpwl = thrust::reduce(thrust::device, net_hpwls, net_hpwls+db.num_nets); - - V* d_out = NULL; - // Determine temporary device storage requirements - void *d_temp_storage = NULL; - size_t temp_storage_bytes = 0; - cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, net_hpwls, d_out, db.num_nets); - // Allocate temporary storage - checkCUDA(cudaMalloc(&d_temp_storage, temp_storage_bytes)); - checkCUDA(cudaMalloc(&d_out, sizeof(V))); - // Run sum-reduction - cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, net_hpwls, d_out, db.num_nets); - // copy d_out to hpwl - V hpwl = 0; - checkCUDA(cudaMemcpy(&hpwl, d_out, sizeof(V), cudaMemcpyDeviceToHost)); - destroyCUDA(d_temp_storage); - destroyCUDA(d_out); - - return T(hpwl)/HPWLScaleTraits::scale; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h deleted file mode 100644 index 7fea33d0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h +++ /dev/null @@ -1,393 +0,0 @@ -/** - * @file DetailedPlaceDB.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDB_H -#define _DREAMPLACE_UTILITY_DETAILEDPLACEDB_H - -#include "utility/src/utils.h" -// helper functions -#include "draw_place/src/draw_place.h" -#include "legality_check/src/legality_check.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct Space -{ - T xl; - T xh; -}; - -struct BinMapIndex -{ - int bin_id; - int sub_id; -}; - -struct RowMapIndex -{ - int row_id; - int sub_id; -}; - -/// @brief a wrapper class of required data for detailed placement -template -struct DetailedPlaceDB -{ - typedef T type; - - const T* init_x; - const T* init_y; - const T* node_size_x; - const T* node_size_y; - const T* flat_region_boxes; ///< number of boxes x 4 - const int* flat_region_boxes_start; ///< number of regions + 1 - const int* node2fence_region_map; ///< length of number of movable cells - T* x; - T* y; - const int* flat_net2pin_map; - const int* flat_net2pin_start_map; - const int* pin2net_map; - const int* flat_node2pin_map; - const int* flat_node2pin_start_map; - const int* pin2node_map; - const T* pin_offset_x; - const T* pin_offset_y; - const unsigned char* net_mask; - T xl; - T yl; - T xh; - T yh; - T site_width; - T row_height; - T bin_size_x; - T bin_size_y; - int num_bins_x; - int num_bins_y; - int num_sites_x; - int num_sites_y; - int num_nodes; - int num_movable_nodes; - int num_nets; - int num_pins; - int num_regions; ///< number of regions for flat_region_boxes and - ///< flat_region_boxes_start - - inline int pos2site_x(T xx) const { - int sx = floorDiv(xx - xl, site_width); - sx = std::max(sx, 0); - sx = std::min(sx, num_sites_x - 1); - return sx; - } - inline int pos2site_y(T yy) const { - int sy = floorDiv(yy - yl, row_height); - sy = std::max(sy, 0); - sy = std::min(sy, num_sites_y - 1); - return sy; - } - /// @brief site index as an upper bound - inline int pos2site_ub_x(T xx) const { - int sx = ceilDiv(xx - xl, site_width); - sx = std::max(sx, 1); - sx = std::min(sx, num_sites_x); - return sx; - } - /// @brief site index as an upper bound - inline int pos2site_ub_y(T yy) const { - int sy = ceilDiv(yy - yl, row_height); - sy = std::max(sy, 1); - sy = std::min(sy, num_sites_y); - return sy; - } - inline int pos2bin_x(T xx) const { - int bx = floorDiv(xx - xl, bin_size_x); - bx = std::max(bx, 0); - bx = std::min(bx, num_bins_x - 1); - return bx; - } - inline int pos2bin_y(T yy) const { - int by = floorDiv(yy - yl, bin_size_y); - by = std::max(by, 0); - by = std::min(by, num_bins_y - 1); - return by; - } - inline void shift_box_to_layout(Box& box) const { - box.xl = std::max(box.xl, xl); - box.xl = std::min(box.xl, xh); - box.xh = std::max(box.xh, xl); - box.xh = std::min(box.xh, xh); - box.yl = std::max(box.yl, yl); - box.yl = std::min(box.yl, yh); - box.yh = std::max(box.yh, yl); - box.yh = std::min(box.yh, yh); - } - inline Box box2sitebox(const Box& box) const { - // xh, yh are exclusive - Box sitebox(pos2site_x(box.xl), pos2site_y(box.yl), - pos2site_ub_x(box.xh), pos2site_ub_y(box.yh)); - - return sitebox; - } - inline Box box2binbox(const Box& box) const { - Box binbox(pos2bin_x(box.xl), pos2bin_y(box.yl), pos2bin_x(box.xh), - pos2bin_y(box.yh)); - - return binbox; - } - /// @brief align x coordinate to site - inline T align2site(T xx) const { - return floorDiv(xx - xl, site_width) * site_width + xl; - } - /// @brief align x coordinate to site for a space; - /// make sure the space is shrinked. - inline Space align2site(Space space) const { - space.xl = ceilDiv(space.xl - xl, site_width) * site_width + xl; - space.xh = floorDiv(space.xh - xl, site_width) * site_width + xl; - return space; - } - /// @brief compute optimal region for a cell - /// The method to compute optimal region ignores the pin offsets of the target - /// cell. If we want to consider the pin offsets, there may not be feasible - /// box for the optimal region. Thus, this is just an approximate optimal - /// region. When using the optimal region, one needs to refer to the center of - /// the cell to the region, or the region completely covers the entire cell. - Box compute_optimal_region(int node_id) const { - Box box(std::numeric_limits::max(), std::numeric_limits::max(), - -std::numeric_limits::max(), -std::numeric_limits::max()); - for (int node2pin_id = flat_node2pin_start_map[node_id]; - node2pin_id < flat_node2pin_start_map[node_id + 1]; ++node2pin_id) { - int node_pin_id = flat_node2pin_map[node2pin_id]; - int net_id = pin2net_map[node_pin_id]; - if (net_mask[net_id]) { - for (int net2pin_id = flat_net2pin_start_map[net_id]; - net2pin_id < flat_net2pin_start_map[net_id + 1]; ++net2pin_id) { - int net_pin_id = flat_net2pin_map[net2pin_id]; - int other_node_id = pin2node_map[net_pin_id]; - if (node_id != other_node_id) { - box.xl = - std::min(box.xl, x[other_node_id] + pin_offset_x[net_pin_id]); - box.xh = - std::max(box.xh, x[other_node_id] + pin_offset_x[net_pin_id]); - box.yl = - std::min(box.yl, y[other_node_id] + pin_offset_y[net_pin_id]); - box.yh = - std::max(box.yh, y[other_node_id] + pin_offset_y[net_pin_id]); - } - } - } - } - shift_box_to_layout(box); - - return box; - } - /// @brief compute HPWL for a net - T compute_net_hpwl(int net_id) const { - Box box(std::numeric_limits::max(), std::numeric_limits::max(), - -std::numeric_limits::max(), -std::numeric_limits::max()); - for (int net2pin_id = flat_net2pin_start_map[net_id]; - net2pin_id < flat_net2pin_start_map[net_id + 1]; ++net2pin_id) { - int net_pin_id = flat_net2pin_map[net2pin_id]; - int other_node_id = pin2node_map[net_pin_id]; - box.xl = std::min(box.xl, x[other_node_id] + pin_offset_x[net_pin_id]); - box.xh = std::max(box.xh, x[other_node_id] + pin_offset_x[net_pin_id]); - box.yl = std::min(box.yl, y[other_node_id] + pin_offset_y[net_pin_id]); - box.yh = std::max(box.yh, y[other_node_id] + pin_offset_y[net_pin_id]); - } - if (box.xl == std::numeric_limits::max() || - box.yl == std::numeric_limits::max()) { - return (T)0; - } - return (box.xh - box.xl) + (box.yh - box.yl); - } - /// @brief compute HPWL for all nets - T compute_total_hpwl() const { - // dreamplacePrint(kDEBUG, "start compute_total_hpwl\n"); - T total_hpwl = 0; - for (int net_id = 0; net_id < num_nets; ++net_id) { - // if (net_mask[net_id]) - { total_hpwl += compute_net_hpwl(net_id); } - } - // dreamplacePrint(kDEBUG, "end compute_total_hpwl\n"); - return total_hpwl; - } - /// @brief distribute cells to rows - void make_row2node_map(const T* vx, const T* vy, - std::vector >& row2node_map, - int num_threads) const { - // distribute cells to rows - for (int i = 0; i < num_nodes; ++i) { - // T node_xl = vx[i]; - T node_yl = vy[i]; - // T node_xh = node_xl+node_size_x[i]; - T node_yh = node_yl + node_size_y[i]; - - int row_idxl = floorDiv(node_yl - yl, row_height); - int row_idxh = ceilDiv(node_yh - yl, row_height); - row_idxl = std::max(row_idxl, 0); - row_idxh = std::min(row_idxh, num_sites_y); - - for (int row_id = row_idxl; row_id < row_idxh; ++row_id) { - T row_yl = yl + row_id * row_height; - T row_yh = row_yl + row_height; - - if (node_yl < row_yh && node_yh > row_yl) // overlap with row - { - row2node_map[row_id].push_back(i); - } - } - } - - // sort cells within rows - // it is safer to sort by center - // sometimes there might be cells with 0 sizes -#ifdef _OPENMP -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, 1) -#endif - for (int i = 0; i < num_sites_y; ++i) { - auto& row2nodes = row2node_map[i]; - // sort cells within rows according to left edges - std::sort(row2nodes.begin(), row2nodes.end(), - [&](int node_id1, int node_id2) { - T x1 = vx[node_id1]; - T x2 = vx[node_id2]; - return x1 < x2 || (x1 == x2 && node_id1 < node_id2); - }); - // After sorting by left edge, - // there is a special case for fixed cells where - // one fixed cell is completely within another in a row. - // This will cause failure to detect some overlaps. - // We need to remove the "small" fixed cell that is inside another. - if (!row2nodes.empty()) { - std::vector tmp_nodes; - tmp_nodes.reserve(row2nodes.size()); - tmp_nodes.push_back(row2nodes.front()); - for (int j = 1, je = row2nodes.size(); j < je; ++j) { - int node_id1 = row2nodes.at(j - 1); - int node_id2 = row2nodes.at(j); - // two fixed cells - if (node_id1 >= num_movable_nodes && node_id2 >= num_movable_nodes) { - T xl1 = vx[node_id1]; - T xl2 = vx[node_id2]; - T width1 = node_size_x[node_id1]; - T width2 = node_size_x[node_id2]; - T xh1 = xl1 + width1; - T xh2 = xl2 + width2; - // only collect node_id2 if its right edge is righter than node_id1 - if (xh1 < xh2) { - tmp_nodes.push_back(node_id2); - } - } else { - tmp_nodes.push_back(node_id2); - } - } - row2nodes.swap(tmp_nodes); - - // sort according to center - std::sort(row2nodes.begin(), row2nodes.end(), - [&](int node_id1, int node_id2) { - T x1 = vx[node_id1] + node_size_x[node_id1] / 2; - T x2 = vx[node_id2] + node_size_x[node_id2] / 2; - return x1 < x2 || (x1 == x2 && node_id1 < node_id2); - }); - } - } - } - /// @brief distribute movable cells to bins - void make_bin2node_map(const T* host_x, const T* host_y, - const T* host_node_size_x, const T* host_node_size_y, - std::vector >& bin2node_map, - std::vector& node2bin_map) const { - // construct bin2node_map - for (int i = 0; i < num_movable_nodes; ++i) { - int node_id = i; - T node_x = host_x[node_id] + host_node_size_x[node_id] / 2; - T node_y = host_y[node_id] + host_node_size_y[node_id] / 2; - - int bx = std::min(std::max((int)floorDiv(node_x - xl, bin_size_x), 0), - num_bins_x - 1); - int by = std::min(std::max((int)floorDiv(node_y - yl, bin_size_y), 0), - num_bins_y - 1); - int bin_id = bx * num_bins_y + by; - // int sub_id = bin2node_map.at(bin_id).size(); - bin2node_map.at(bin_id).push_back(node_id); - } - // construct node2bin_map - for (unsigned int bin_id = 0; bin_id < bin2node_map.size(); ++bin_id) { - for (unsigned int sub_id = 0; sub_id < bin2node_map[bin_id].size(); - ++sub_id) { - int node_id = bin2node_map[bin_id][sub_id]; - BinMapIndex& bm_idx = node2bin_map.at(node_id); - bm_idx.bin_id = bin_id; - bm_idx.sub_id = sub_id; - } - } -#ifdef DEBUG - int max_num_nodes_per_bin = 0; - for (unsigned int i = 0; i < bin2node_map.size(); ++i) { - max_num_nodes_per_bin = - std::max(max_num_nodes_per_bin, (int)bin2node_map[i].size()); - } - printf("[D] max_num_nodes_per_bin = %d\n", max_num_nodes_per_bin); -#endif - } - /// @brief check whether placement is legal - bool check_legality() const { - return legalityCheckKernelCPU( - x, y, node_size_x, node_size_y, flat_region_boxes, - flat_region_boxes_start, node2fence_region_map, xl, yl, xh, yh, - site_width, row_height, num_nodes, num_movable_nodes, num_regions); - } - /// @brief check whether a cell is within its fence region - bool inside_fence(int node_id, T xx, T yy) const { - T node_xl = xx; - T node_yl = yy; - T node_xh = node_xl + node_size_x[node_id]; - T node_yh = node_yl + node_size_y[node_id]; - - bool legal_flag = true; - int region_id = node2fence_region_map[node_id]; - if (region_id < num_regions) { - int box_bgn = flat_region_boxes_start[region_id]; - int box_end = flat_region_boxes_start[region_id + 1]; - T node_area = (node_xh - node_xl) * (node_yh - node_yl); - // I assume there is no overlap between boxes of a region - // otherwise, preprocessing is required - for (int box_id = box_bgn; box_id < box_end; ++box_id) { - int box_offset = box_id * 4; - T box_xl = flat_region_boxes[box_offset]; - T box_yl = flat_region_boxes[box_offset + 1]; - T box_xh = flat_region_boxes[box_offset + 2]; - T box_yh = flat_region_boxes[box_offset + 3]; - - T dx = std::max(std::min(node_xh, box_xh) - std::max(node_xl, box_xl), - (T)0); - T dy = std::max(std::min(node_yh, box_yh) - std::max(node_yl, box_yl), - (T)0); - T overlap = dx * dy; - if (overlap > 0) { - node_area -= overlap; - } - } - if (node_area > 0) // not consumed by boxes within a region - { - legal_flag = false; - } - } - return legal_flag; - } - /// @brief draw placement - void draw_place(const char* filename) const { - drawPlaceLauncher(x, y, node_size_x, node_size_y, pin_offset_x, - pin_offset_y, pin2node_map, num_nodes, - num_movable_nodes, 0, flat_net2pin_start_map[num_nets], - xl, yl, xh, yh, site_width, row_height, bin_size_x, - bin_size_y, filename); - } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh deleted file mode 100644 index 7dfb2211..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh +++ /dev/null @@ -1,142 +0,0 @@ -/** - * @file FlatNestedVector.cuh - * @author Yibo Lin (DREAMPlace) - * @date Mar 2019 - */ -#ifndef _DREAMPLACE_UTILITY_FLATNESTEDVECTOR_CUH -#define _DREAMPLACE_UTILITY_FLATNESTEDVECTOR_CUH - -#include -#include -//#include -//#include - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct FlatNestedVector -{ - T* flat_element_map; ///< allocate on device, length of flat_dim1_start_map[size1] - unsigned int* flat_dim1_start_map; ///< allocate on device, length of size1+1 - unsigned int size1; ///< length in dimension 1 - - /// @brief constructor - __host__ FlatNestedVector() - : flat_element_map(nullptr) - , flat_dim1_start_map(nullptr) - , size1(0) - { - } - - /// @brief initialization - __host__ void initialize(const std::vector >& nested_map) - { - // construct flat map on host - unsigned int num_elements = 0; - for (typename std::vector >::const_iterator it = nested_map.begin(); it != nested_map.end(); ++it) - { - num_elements += it->size(); - } - std::vector host_flat_element_map (num_elements); - std::vector host_flat_dim1_start_map (nested_map.size()+1); - - num_elements = 0; - for (unsigned int i = 0; i < nested_map.size(); ++i) - { - const std::vector& vec = nested_map[i]; - std::copy(vec.begin(), vec.end(), host_flat_element_map.begin()+num_elements); - host_flat_dim1_start_map[i] = num_elements; - num_elements += vec.size(); - } - host_flat_dim1_start_map[nested_map.size()] = num_elements; - - // copy to device - size1 = nested_map.size(); - allocateCopyCUDA(flat_element_map, host_flat_element_map.data(), host_flat_element_map.size()); - allocateCopyCUDA(flat_dim1_start_map, host_flat_dim1_start_map.data(), host_flat_dim1_start_map.size()); - } - - __host__ void destroy() - { - if (flat_element_map) - { - destroyCUDA(flat_element_map); - destroyCUDA(flat_dim1_start_map); - } - } - - /// @brief access element - inline __device__ const T& operator()(unsigned int i, unsigned int j) const - { -#ifdef DEBUG - if (!(i < size1 && j < size(i))) - { - printf("%u < %u && %u < %u\n", i, size1, j, size(i)); - } -#endif - assert(i < size1 && j < size(i)); - return flat_element_map[flat_dim1_start_map[i]+j]; - } - - /// @brief access element - inline __device__ T& operator()(unsigned int i, unsigned int j) - { -#ifdef DEBUG - if (!(i < size1 && j < size(i))) - { - printf("%u < %u && %u < %u\n", i, size1, j, size(i)); - } -#endif - assert(i < size1 && j < size(i)); - return flat_element_map[flat_dim1_start_map[i]+j]; - } - - /// @brief access each row - inline __device__ const T* operator()(unsigned int i) const - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return flat_element_map+flat_dim1_start_map[i]; - } - - /// @brief access each row - inline __device__ T* operator()(unsigned int i) - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return flat_element_map+flat_dim1_start_map[i]; - } - - /// @brief length of each row - inline __device__ unsigned int size(unsigned int i) const - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return flat_dim1_start_map[i+1]-flat_dim1_start_map[i]; - } - - /// @brief total number of elements - inline __device__ unsigned int size() const - { - return flat_dim1_start_map[size1]; - } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h deleted file mode 100644 index b5142d4c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file LegalizationDB.h - * @author Yibo Lin (DREAMPlace) - * @date Nov 2019 - */ - -#ifndef _DREAMPLACE_UTILITY_LEGALIZATIONDB_H -#define _DREAMPLACE_UTILITY_LEGALIZATIONDB_H - -#include -#include "legality_check/src/legality_check.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief a wrapper class of required data for legalization -template -struct LegalizationDB -{ - typedef T type; - - const T* init_x; - const T* init_y; - const T* node_size_x; - const T* node_size_y; - const T* node_weights; - const T* flat_region_boxes; ///< number of boxes x 4 - const int* flat_region_boxes_start; ///< number of regions + 1 - const int* node2fence_region_map; ///< length of number of movable cells - T* x; - T* y; - - T xl; - T yl; - T xh; - T yh; - - T site_width; - T row_height; - T bin_size_x; - T bin_size_y; - - int num_bins_x; - int num_bins_y; - int num_sites_x; - int num_sites_y; - - int num_nodes; - int num_movable_nodes; - int num_regions; ///< number of regions for flat_region_boxes and - ///< flat_region_boxes_start - - /// @brief check whether a cell is regarded as movable macros in legalization. - /// This is mainly because it is painful to handle these cells for - /// legalization. - inline bool is_dummy_fixed(int node_id) const { -#ifdef DEBUG - dreamplaceAssert(node_id < db.num_nodes); -#endif - T height = node_size_y[node_id]; - return (node_id < num_movable_nodes && - height > (row_height * DUMMY_FIXED_NUM_ROWS)); - } - /// @brief align cell to a row - inline T align2row(T y, T height) const { - T yy = std::max(std::min(y, yh - height), yl); - yy = floorDiv(yy - yl, row_height) * row_height + yl; - return yy; - } - /// @brief align cell to a site - inline T align2site(T x, T width) const { - T xx = std::max(std::min(x, xh - width), xl); - xx = floorDiv(xx - xl, site_width) * site_width + xl; - return xx; - } - /// @brief check whether placement is legal - bool check_legality() const { - return legalityCheckKernelCPU( - x, y, node_size_x, node_size_y, flat_region_boxes, - flat_region_boxes_start, node2fence_region_map, xl, yl, xh, yh, - site_width, row_height, num_nodes, num_movable_nodes, num_regions); - } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp deleted file mode 100644 index 47c8117b..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************* - > File Name: Msg.cpp - > Author: Yibo Lin (DREAMPlace) - > Mail: yibolin@utexas.edu - > Created Time: Fri 31 Jul 2015 03:20:14 PM CDT - ************************************************************************/ - -#include "utility/src/Msg.h" -#include -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -int dreamplacePrint(MessageType m, const char* format, ...) -{ - va_list args; - va_start(args, format); - int ret = dreamplaceVPrintStream(m, stdout, format, args); - va_end(args); - - return ret; -} - -int dreamplacePrintStream(MessageType m, FILE* stream, const char* format, ...) -{ - va_list args; - va_start(args, format); - int ret = dreamplaceVPrintStream(m, stream, format, args); - va_end(args); - - return ret; -} - -int dreamplaceVPrintStream(MessageType m, FILE* stream, const char* format, va_list args) -{ - // print prefix - char prefix[16]; - dreamplaceSPrintPrefix(m, prefix); - fprintf(stream, "%s", prefix); - - // print message - int ret = vfprintf(stream, format, args); - - return ret; -} - -int dreamplaceSPrint(MessageType m, char* buf, const char* format, ...) -{ - va_list args; - va_start(args, format); - int ret = dreamplaceVSPrint(m, buf, format, args); - va_end(args); - - return ret; -} - -int dreamplaceVSPrint(MessageType m, char* buf, const char* format, va_list args) -{ - // print prefix - char prefix[16]; - dreamplaceSPrintPrefix(m, prefix); - sprintf(buf, "%s", prefix); - - // print message - int ret = vsprintf(buf+strlen(prefix), format, args); - - return ret; -} - -int dreamplaceSPrintPrefix(MessageType m, char* prefix) -{ - switch (m) - { - case kNONE: - return sprintf(prefix, "%c", '\0'); - case kINFO: - return sprintf(prefix, "[INFO ] "); - case kWARN: - return sprintf(prefix, "[WARNING] "); - case kERROR: - return sprintf(prefix, "[ERROR ] "); - case kDEBUG: - return sprintf(prefix, "[DEBUG ] "); - case kASSERT: - return sprintf(prefix, "[ASSERT ] "); - default: - dreamplaceAssertMsg(0, "unknown message type"); - } - return 0; -} - -void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName, const char* format, ...) -{ - // construct message - char buf[1024]; - va_list args; - va_start(args, format); - vsprintf(buf, format, args); - va_end(args); - - // print message - dreamplacePrintStream(kASSERT, stderr, "%s:%u: %s: Assertion `%s' failed: %s\n", fileName, lineNum, funcName, expr, buf); -} - -void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName) -{ - // print message - dreamplacePrintStream(kASSERT, stderr, "%s:%u: %s: Assertion `%s' failed\n", fileName, lineNum, funcName, expr); -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h deleted file mode 100644 index dc17de60..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file Msg.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ - -#ifndef DREAMPLACE_MSG_H -#define DREAMPLACE_MSG_H - -#include -#include -#include -#include -#include "utility/src/namespace.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// message type for print functions -enum MessageType { - kNONE = 0, - kINFO = 1, - kWARN = 2, - kERROR = 3, - kDEBUG = 4, - kASSERT = 5 -}; - -/// print to screen (stdout) -int dreamplacePrint(MessageType m, const char* format, ...); -/// print to stream -int dreamplacePrintStream(MessageType m, FILE* stream, const char* format, ...); -/// core function to print formatted data from variable argument list -int dreamplaceVPrintStream(MessageType m, FILE* stream, const char* format, va_list args); -/// format to a buffer -int dreamplaceSPrint(MessageType m, char* buf, const char* format, ...); -/// core function to format a buffer -int dreamplaceVSPrint(MessageType m, char* buf, const char* format, va_list args); -/// format prefix -int dreamplaceSPrintPrefix(MessageType m, char* buf); - -/// assertion -void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName, const char* format, ...); -void dreamplacePrintAssertMsg(const char* expr, const char* fileName, unsigned lineNum, const char* funcName); - -#define dreamplaceAssertMsg(condition, args...) do {\ - if (!(condition)) \ - {\ - ::DREAMPLACE_NAMESPACE::dreamplacePrintAssertMsg(#condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, args); \ - abort(); \ - }\ -} while (false) -#define dreamplaceAssert(condition) do {\ - if (!(condition)) \ - {\ - ::DREAMPLACE_NAMESPACE::dreamplacePrintAssertMsg(#condition, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - abort(); \ - }\ -} while (false) - -/// static assertion -template -struct dreamplaceStaticAssert; -template <> -struct dreamplaceStaticAssert -{ - dreamplaceStaticAssert(const char* = NULL) {} -}; - - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh deleted file mode 100644 index 184daac2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @file PitchNestedVector.cuh - * @author Yibo Lin (DREAMPlace) - * @date Mar 2019 - * @brief Flat nested array using pitched memory - */ -#ifndef _DREAMPLACE_UTILITY_PITCHNESTEDVECTOR_CUH -#define _DREAMPLACE_UTILITY_PITCHNESTEDVECTOR_CUH - -#include -#include -//#include -//#include - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct PitchNestedVector -{ - T* flat_element_map; ///< allocate on device, length of size1*size2 - unsigned int* dim2_sizes; ///< sizes of dimension 2 - unsigned int size1; ///< length in dimension 1 - unsigned int size2; ///< maximum length in dimension 2 - unsigned int num_elements; ///< total number of elements - - /// @brief constructor - __host__ PitchNestedVector() - : flat_element_map(nullptr) - , dim2_sizes(nullptr) - , size1(0) - , size2(0) - { - } - - /// @brief initialization - __host__ void initialize(const std::vector >& nested_map) - { - // construct flat map on host - unsigned int max_num_elements = 0; - num_elements = 0; - for (typename std::vector >::const_iterator it = nested_map.begin(); it != nested_map.end(); ++it) - { - max_num_elements = max(max_num_elements, (unsigned int)it->size()); - num_elements += it->size(); - } - std::vector host_flat_element_map (nested_map.size()*max_num_elements, std::numeric_limits::max()); - std::vector host_dim2_sizes (nested_map.size()); - - for (unsigned int i = 0; i < nested_map.size(); ++i) - { - const std::vector& vec = nested_map[i]; - std::copy(vec.begin(), vec.end(), host_flat_element_map.begin()+max_num_elements*i); - host_dim2_sizes[i] = vec.size(); - } - - // copy to device - size1 = nested_map.size(); - size2 = max_num_elements; - allocateCopyCUDA(flat_element_map, host_flat_element_map.data(), host_flat_element_map.size()); - allocateCopyCUDA(dim2_sizes, host_dim2_sizes.data(), host_dim2_sizes.size()); - } - - __host__ void destroy() - { - if (flat_element_map) - { - destroyCUDA(flat_element_map); - destroyCUDA(dim2_sizes); - } - } - - /// @brief access element - inline __device__ const T& operator()(unsigned int i, unsigned int j) const - { -#ifdef DEBUG - if (!(i < size1 && j < size(i))) - { - printf("%u < %u && %u < %u\n", i, size1, j, size(i)); - } -#endif - assert(i < size1 && j < size(i)); - return flat_element_map[i*size2+j]; - } - - /// @brief access element - inline __device__ T& operator()(unsigned int i, unsigned int j) - { -#ifdef DEBUG - if (!(i < size1 && j < size(i))) - { - printf("%u < %u && %u < %u\n", i, size1, j, size(i)); - } -#endif - assert(i < size1 && j < size(i)); - return flat_element_map[i*size2+j]; - } - - /// @brief access each row - inline __device__ const T* operator()(unsigned int i) const - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return flat_element_map+i*size2; - } - - /// @brief access each row - inline __device__ T* operator()(unsigned int i) - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return flat_element_map+i*size2; - } - - /// @brief length of each row - inline __device__ unsigned int size(unsigned int i) const - { -#ifdef DEBUG - if (!(i < size1)) - { - printf("%u < %u\n", i, size1); - } -#endif - assert(i < size1); - return dim2_sizes[i]; - } - - /// @brief total number of elements - inline __device__ unsigned int size() const - { - return num_elements; - } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh deleted file mode 100644 index d3be0563..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file atomic_ops.cuh - * @author Yibo Lin (DREAMPlace) - * @date Oct 2019 - */ -#include -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief A class generalized scaled atomic addition for floating point number and integers. -/// For integer, we use it as a fixed point number with the LSB part for fractions. -template ::value> -struct AtomicAddCUDA -{ - typedef T type; - - /// @brief constructor - AtomicAddCUDA(type = 1) - { - } - - template - __device__ __forceinline__ type operator()(type* dst, V v) const - { - return atomicAdd(dst, (type)v); - } -}; - -/// @brief For atomic addition of fixed point number using integers. -template -struct AtomicAddCUDA -{ - typedef T type; - - type scale_factor; ///< a scale factor to scale fraction into integer - - /// @brief constructor - /// @param sf scale factor - AtomicAddCUDA(type sf = 1) - : scale_factor(sf) - { - } - - template - __device__ __forceinline__ type operator()(type* dst, V v) const - { - type sv = v*scale_factor; - return atomicAdd(dst, sv); - } -}; - -template -__global__ void copyScaleArray(T *dst, V *src, T scale_factor, int n) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < n) { - dst[i] = src[i] * scale_factor; - } -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h deleted file mode 100644 index de8d8b15..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file atomic_ops.h - * @author Yibo Lin (DREAMPlace) - * @date Apr 2020 - */ -#include -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief A class generalized scaled atomic addition for floating point number -/// and integers. For integer, we use it as a fixed point number with the LSB -/// part for fractions. -template ::value> -struct AtomicAdd { - typedef T type; - - /// @brief constructor - AtomicAdd(type = 1) {} - - template - inline void operator()(type* dst, V v) const { -#pragma omp atomic - *dst += v; - } -}; - -/// @brief For atomic addition of fixed point number using integers. -template -struct AtomicAdd { - typedef T type; - - type scale_factor; ///< a scale factor to scale fraction into integer - - /// @brief constructor - /// @param sf scale factor - AtomicAdd(type sf = 1) : scale_factor(sf) {} - - template - inline void operator()(type* dst, V v) const { - type sv = v * scale_factor; -#pragma omp atomic - *dst += sv; - } -}; - -/// @brief Perform a += b * scale_factor -template -void scaleAdd(T* dst, const V* src, W scale_factor, int n, int num_threads) { -#pragma omp parallel for num_threads(num_threads) - for (int i = 0; i < n; ++i) { - dst[i] += src[i] * scale_factor; - } -} - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h deleted file mode 100644 index f4fd5ff3..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file defs.h - * @author Yibo Lin (DREAMPlace) - * @date Apr 2020 - */ -#ifndef _DREAMPLACE_UTILITY_DEFS_H -#define _DREAMPLACE_UTILITY_DEFS_H - -#include "utility/src/namespace.h" - -DREAMPLACE_BEGIN_NAMESPACE - -#ifndef __NVCC__ - -/// namespace definition to make functions like -/// min/max general between C++ and CUDA -#define DREAMPLACE_STD_NAMESPACE std -/// namespace definition to make functions -/// general between C++ and CUDA -#define DREAMPLACE_HOST_DEVICE - -#else - -#define DREAMPLACE_STD_NAMESPACE -#define DREAMPLACE_HOST_DEVICE __host__ __device__ - -#define allocateCUDA(var, size, type) \ - { \ - cudaError_t status = cudaMalloc(&(var), (size) * sizeof(type)); \ - if (status != cudaSuccess) { \ - dreamplacePrint(kERROR, "cudaMalloc failed for " #var "\n"); \ - } \ - } - -#define destroyCUDA(var) \ - { \ - cudaError_t status = cudaFree(var); \ - if (status != cudaSuccess) { \ - dreamplacePrint(kERROR, "cudaFree failed for " #var "\n"); \ - } \ - } - -#define checkCUDA(status) \ - { \ - dreamplaceAssertMsg(status == cudaSuccess, "CUDA Runtime Error: %s\n", \ - cudaGetErrorString(status)); \ - } - -#define allocateCopyCUDA(var, rhs, size) \ - { \ - allocateCUDA(var, size, decltype(*rhs)); \ - checkCUDA(cudaMemcpy(var, rhs, sizeof(decltype(*rhs)) * (size), \ - cudaMemcpyHostToDevice)); \ - } - -#define checkCURAND(x) \ - { dreamplaceAssert(x == CURAND_STATUS_SUCCESS); } - -#define allocateCopyCPU(var, rhs, size, T) \ - { \ - var = (T*)malloc(sizeof(T) * (size)); \ - checkCUDA(cudaMemcpy((void*)var, (void*)rhs, sizeof(T) * (size), \ - cudaMemcpyDeviceToHost)); \ - } - -#endif - -#define destroyCPU(var) \ - { free((void*)var); } - -/// A heuristic to detect movable macros. -/// If a cell has a height larger than how many rows, we regard them as movable -/// macros. -#define DUMMY_FIXED_NUM_ROWS 2 - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h deleted file mode 100644 index e4bbec06..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @file diamond_search.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ -#ifndef _DREAMPLACE_GLOBAL_MOVE_DIAMOND_SEARCH_H -#define _DREAMPLACE_GLOBAL_MOVE_DIAMOND_SEARCH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief grid index -template -struct GridIndex { - T ir; ///< row index - T ic; ///< column index - - GridIndex() - : ir(std::numeric_limits::max()), ic(std::numeric_limits::max()) {} - - GridIndex(T r, T c) : ir(r), ic(c) {} - - T manhattan_distance(const GridIndex& rhs) const { - return fabs(ir - rhs.ir) + fabs(ic - rhs.ic); - } - double angle(const GridIndex& rhs) const { - T dx = ir - rhs.ir; - T dy = ic - rhs.ic; - return atan2(dy, dx); - } -}; - -/// @brief compare grid (row index, column index) by its manhattan distance to a -/// target grid -template -struct CompareGridByDistance2Target { - GridIndex target; - CompareGridByDistance2Target(const GridIndex& g) : target(g) {} - bool operator()(const GridIndex& g1, const GridIndex& g2) const { - T d1 = g1.manhattan_distance(target); - double angle1 = g1.angle(target); - T d2 = g2.manhattan_distance(target); - double angle2 = g2.angle(target); - return d1 < d2 || (d1 == d2 && (angle1 < angle2)); - } -}; - -/// @brief kernel to generate the sequence for diamond search -/// @tparam the template must be a signed integer -/// @param num_rows number of rows -/// @param num_cols number of columns -/// @return the sequence in order from small distance to the center grid to -/// large -template -std::vector > diamond_search_sequence_kernel(T num_rows, - T num_cols) { - //// 2D grid map in row major - //// each element is the (row index, column index) - // std::vector > grid_map (num_rows*num_cols, GridIndex(0, - // 0)); for (T ir = 0; ir < num_rows; ++ir) - //{ - // for (T ic = 0; ic < num_cols; ++ic) - // { - // grid_map[ir*num_cols+ic] = GridIndex(-(T)num_rows/2+ir, - // -(T)num_cols/2+ic); - // } - //} - - //// sort from small distance to large - // std::sort(grid_map.begin(), grid_map.end(), - // CompareGridByDistance2Target(GridIndex(0, 0))); - - // directly generate diamond shape grids - // in clock-wise direction - // the sequence covers the following shape - // 1 - // 111 - // 11111 - // 111 - // 1 - std::vector > grid_map; - grid_map.reserve(num_rows * num_cols / 2); - T max_sum = std::min(num_rows, num_cols) / 2; - grid_map.push_back(GridIndex(0, 0)); - for (T sum = 1; sum <= max_sum; ++sum) { - // y > 0, x [-sum, sum] - for (T ir = -sum; ir < sum; ++ir) { - grid_map.push_back(GridIndex(ir, sum - std::abs(ir))); - } - // y < 0, x [sum, -sum] - for (T ir = sum; ir > -sum; --ir) { - grid_map.push_back(GridIndex(ir, -(sum - std::abs(ir)))); - } - } - - return grid_map; -} - -/// @brief top API to generate the sequence for diamond search -/// @param num_rows number of rows -/// @param num_cols number of columns -/// @return the sequence in order from small distance to the center grid to -/// large -template -std::vector::type> > -diamond_search_sequence(T num_rows, T num_cols) { - return diamond_search_sequence_kernel::type>( - num_rows, num_cols); -} - -template -void diamond_search_print(const std::vector >& grid_sequence) { - unsigned int sum = 0; - unsigned int count = 0; - GridIndex target(0, 0); - printf("[0] "); - for (typename std::vector >::const_iterator - it = grid_sequence.begin(); - it != grid_sequence.end(); ++it, ++count) { - T distance = it->manhattan_distance(target); - if (sum != distance) { - sum = distance; - printf("\n[%u] ", count); - } - printf("(%d,%d) ", it->ir, it->ic); - } - printf("\n"); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h deleted file mode 100644 index 730aa556..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h +++ /dev/null @@ -1,316 +0,0 @@ -/** - * @file limits.h - * @author Yibo Lin (DREAMPlace) - * @date Feb 2019 - */ - -#ifndef _DREAMPLACE_UTILITY_LIMITS_H -#define _DREAMPLACE_UTILITY_LIMITS_H - -#include -#include - -DREAMPLACE_BEGIN_NAMESPACE - -namespace cuda { // namespace cuda - -template -struct numeric_limits_base { - typedef T type; -}; -template -struct numeric_limits : public numeric_limits_base {}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return CHAR_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return CHAR_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return CHAR_MIN; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits - : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return UCHAR_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return SHRT_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return SHRT_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return SHRT_MIN; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits - : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return USHRT_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return INT_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return INT_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return INT_MIN; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return UINT_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return LONG_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return LONG_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return LONG_MIN; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits - : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return ULONG_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return LLONG_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return LLONG_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return LLONG_MIN; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits - : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { return 0; } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return ULLONG_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { return 0; } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { return 0; } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return FLT_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return FLT_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return -FLT_MAX; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { - return FLT_EPSILON; - } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return DBL_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return DBL_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return -DBL_MAX; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { - return DBL_EPSILON; - } -}; - -template <> -struct numeric_limits : public numeric_limits_base { - /** The minimum finite value, or for floating types with - denormalization, the minimum positive normalized value. */ - DREAMPLACE_HOST_DEVICE static constexpr type min() noexcept { - return LDBL_MIN; - } - - /** The maximum finite value. */ - DREAMPLACE_HOST_DEVICE static constexpr type max() noexcept { - return LDBL_MAX; - } - - /** A finite value x such that there is no other finite value y - * where y < x. */ - DREAMPLACE_HOST_DEVICE static constexpr type lowest() noexcept { - return -LDBL_MAX; - } - - /** A the machine epsilon. */ - DREAMPLACE_HOST_DEVICE static constexpr type epsilon() noexcept { - return LDBL_EPSILON; - } -}; -} // namespace cuda - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h deleted file mode 100644 index d00bb2da..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @file DetailedPlaceDBUtils.h - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ -#ifndef _DREAMPLACE_UTILITY_DETAILEDPLACEDBUTILS_H -#define _DREAMPLACE_UTILITY_DETAILEDPLACEDBUTILS_H - -DREAMPLACE_BEGIN_NAMESPACE - -template -struct DetailedPlaceDB; - -template -struct LegalizationDB; - -/// @brief make a database for detailed placement. -/// Only movable nodes will be moved. Fixed nodes and filler nodes are fixed. -/// -/// @param init_pos initial x/y location of nodes, including movable nodes, -/// fixed nodes, and filler nodes, [0, num_movable_nodes) are movable nodes, -/// [num_movable_nodes, num_nodes-num_filler_nodes) are fixed nodes, -/// [num_nodes-num_filler_nodes, num_nodes) are filler nodes. x values are at -/// the first half of the array, y values are at the second half of the array -/// @param node_size_x width of nodes, including movable nodes, fixed nodes, and -/// filler nodes, [0, num_movable_nodes) are movable nodes, [num_movable_nodes, -/// num_nodes-num_filler_nodes) are fixed nodes, [num_nodes-num_filler_nodes, -/// num_nodes) are filler nodes -/// @param node_size_y height of nodes, including movable nodes, fixed nodes, -/// and filler nodes, same as node_size_x -/// @param flat_net2pin_map consists pins of each net, pins belonging to the -/// same net are abutting to each other. -/// @param flat_net2pin_start_map bookmark for the starting index of each net in -/// flat_net2pin_map. The length is number of nets. The last entry equals to the -/// number of pins. -/// @param pin2net_map maps pin index to net index. -/// @param flat_node2pin_map consists pins of each node, pins belonging to the -/// same node are abutting to each other. -/// @param flat_node2pin_start_map bookmark for the starting index of each node -/// in flat_node2pin_map. The length is number of nodes. The last entry equals -/// to the number of pins. -/// @param pin2node_map maps pin index to node index. -/// @param pin_offset_x pin offset in x direction -/// @param pin_offset_y pin offset in y direction -/// @param net_mask whether a net should be considered for wirelength -/// @param xl left edge of bounding box of layout area -/// @param yl bottom edge of bounding box of layout area -/// @param xh right edge of bounding box of layout area -/// @param yh top edge of bounding box of layout area -/// @param site_width width of a placement site -/// @param row_height height of a placement row -/// @param num_bins_x number of bins in horizontal direction -/// @param num_bins_y number of bins in vertical direction -/// @param num_nodes total number of nodes, including movable nodes, fixed -/// nodes, and filler nodes; fixed nodes are in the range of [num_movable_nodes, -/// num_nodes-num_filler_nodes) -/// @param num_movable_nodes number of movable nodes, movable nodes are in the -/// range of [0, num_movable_nodes) -/// @param num_terminal_NIs number of terminal_NIs, essential fixed IO pins, in -/// the range of [num_movable_nodes+num_terminal, num_nodes-num_filler_nodes) -/// @param num_filler_nodes number of filler nodes, filler nodes are in the -/// range of [num_nodes-num_filler_nodes, num_nodes) -template -DetailedPlaceDB make_placedb( - at::Tensor init_pos, at::Tensor pos, at::Tensor node_size_x, - at::Tensor node_size_y, at::Tensor flat_region_boxes, - at::Tensor flat_region_boxes_start, at::Tensor node2fence_region_map, - at::Tensor flat_net2pin_map, at::Tensor flat_net2pin_start_map, - at::Tensor pin2net_map, at::Tensor flat_node2pin_map, - at::Tensor flat_node2pin_start_map, at::Tensor pin2node_map, - at::Tensor pin_offset_x, at::Tensor pin_offset_y, at::Tensor net_mask, - double xl, double yl, double xh, double yh, double site_width, - double row_height, int num_bins_x, int num_bins_y, int num_movable_nodes, - int num_terminal_NIs, int num_filler_nodes) { - DetailedPlaceDB db; - int num_nodes = init_pos.numel() / 2; - - db.init_x = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T); - db.init_y = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T) + num_nodes; - db.node_size_x = DREAMPLACE_TENSOR_DATA_PTR(node_size_x, T); - db.node_size_y = DREAMPLACE_TENSOR_DATA_PTR(node_size_y, T); - db.flat_region_boxes = DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes, T); - db.flat_region_boxes_start = - DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes_start, int); - db.node2fence_region_map = - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int); - db.x = DREAMPLACE_TENSOR_DATA_PTR(pos, T); - db.y = DREAMPLACE_TENSOR_DATA_PTR(pos, T) + num_nodes; - db.flat_net2pin_map = DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_map, int); - db.flat_net2pin_start_map = - DREAMPLACE_TENSOR_DATA_PTR(flat_net2pin_start_map, int); - db.pin2net_map = DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int); - db.flat_node2pin_map = DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_map, int); - db.flat_node2pin_start_map = - DREAMPLACE_TENSOR_DATA_PTR(flat_node2pin_start_map, int); - db.pin2node_map = DREAMPLACE_TENSOR_DATA_PTR(pin2node_map, int); - db.pin_offset_x = DREAMPLACE_TENSOR_DATA_PTR(pin_offset_x, T); - db.pin_offset_y = DREAMPLACE_TENSOR_DATA_PTR(pin_offset_y, T); - db.net_mask = DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char); - db.xl = xl; - db.yl = yl; - db.xh = xh; - db.yh = yh; - db.site_width = site_width; - db.row_height = row_height; - db.bin_size_x = (xh - xl) / num_bins_x; - db.bin_size_y = (yh - yl) / num_bins_y; - db.num_bins_x = num_bins_x; - db.num_bins_y = num_bins_y; - db.num_sites_x = std::round((xh - xl) / site_width); - db.num_sites_y = std::round((yh - yl) / row_height); - db.num_nodes = num_nodes - num_filler_nodes - num_terminal_NIs; - db.num_movable_nodes = num_movable_nodes; - db.num_nets = flat_net2pin_start_map.numel() - 1; - db.num_pins = pin2net_map.numel(); - db.num_regions = flat_region_boxes_start.numel() - 1; - - return db; -} - -/// @brief make a database for detailed placement. -/// Only movable nodes will be moved. Fixed nodes and filler nodes are fixed. -/// -/// @param init_pos initial x/y location of nodes, including movable nodes, -/// fixed nodes, and filler nodes, [0, num_movable_nodes) are movable nodes, -/// [num_movable_nodes, num_nodes-num_filler_nodes) are fixed nodes, -/// [num_nodes-num_filler_nodes, num_nodes) are filler nodes. x values are at -/// the first half of the array, y values are at the second half of the array -/// @param pos x/y locations to write -/// @param node_size_x width of nodes, including movable nodes, fixed nodes, and -/// filler nodes, [0, num_movable_nodes) are movable nodes, [num_movable_nodes, -/// num_nodes-num_filler_nodes) are fixed nodes, [num_nodes-num_filler_nodes, -/// num_nodes) are filler nodes -/// @param node_size_y height of nodes, including movable nodes, fixed nodes, -/// and filler nodes, same as node_size_x -/// @param xl left edge of bounding box of layout area -/// @param yl bottom edge of bounding box of layout area -/// @param xh right edge of bounding box of layout area -/// @param yh top edge of bounding box of layout area -/// @param site_width width of a placement site -/// @param row_height height of a placement row -/// @param num_bins_x number of bins in horizontal direction -/// @param num_bins_y number of bins in vertical direction -/// @param num_movable_nodes number of movable nodes, movable nodes are in the -/// range of [0, num_movable_nodes) -/// @param number of filler nodes, filler nodes are in the range of -/// [num_nodes-num_filler_nodes, num_nodes) -template -LegalizationDB make_placedb( - at::Tensor init_pos, at::Tensor pos, at::Tensor node_size_x, - at::Tensor node_size_y, at::Tensor node_weights, - at::Tensor flat_region_boxes, at::Tensor flat_region_boxes_start, - at::Tensor node2fence_region_map, double xl, double yl, double xh, - double yh, double site_width, double row_height, int num_bins_x, - int num_bins_y, int num_movable_nodes, int num_terminal_NIs, - int num_filler_nodes) { - LegalizationDB db; - int num_nodes = init_pos.numel() / 2; - - db.init_x = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T); - db.init_y = DREAMPLACE_TENSOR_DATA_PTR(init_pos, T) + num_nodes; - db.node_size_x = DREAMPLACE_TENSOR_DATA_PTR(node_size_x, T); - db.node_size_y = DREAMPLACE_TENSOR_DATA_PTR(node_size_y, T); - db.node_weights = DREAMPLACE_TENSOR_DATA_PTR(node_weights, T); - db.flat_region_boxes = DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes, T); - db.flat_region_boxes_start = - DREAMPLACE_TENSOR_DATA_PTR(flat_region_boxes_start, int); - db.node2fence_region_map = - DREAMPLACE_TENSOR_DATA_PTR(node2fence_region_map, int); - db.x = DREAMPLACE_TENSOR_DATA_PTR(pos, T); - db.y = DREAMPLACE_TENSOR_DATA_PTR(pos, T) + num_nodes; - db.xl = xl; - db.yl = yl; - db.xh = xh; - db.yh = yh; - db.site_width = site_width; - db.row_height = row_height; - db.bin_size_x = (xh - xl) / num_bins_x; - db.bin_size_y = (yh - yl) / num_bins_y; - db.num_bins_x = num_bins_x; - db.num_bins_y = num_bins_y; - db.num_sites_x = std::round((xh - xl) / site_width); - db.num_sites_y = std::round((yh - yl) / row_height); - // ignore fillers and terminal_NIs - db.num_nodes = num_nodes - num_filler_nodes - num_terminal_NIs; - db.num_movable_nodes = num_movable_nodes; - db.num_regions = flat_region_boxes_start.numel() - 1; - - return db; -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h deleted file mode 100644 index e81a3707..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * File : math.h - * Author : Yibo Lin (DREAMPlace) - * Date : 05.05.2020 - */ - -#ifndef _DREAMPLACE_UTILITY_MATH_H -#define _DREAMPLACE_UTILITY_MATH_H - -#include -#include -#include "utility/src/defs.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// relative error tolerance for floating point floor/ceil -#define DREAMPLACE_RTOL 1e-3 - -template -inline DREAMPLACE_HOST_DEVICE T div(T a, V b) { - return a / b; -} - -/// @brief template specialization for non-integral types -template -inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type -floorDiv(T a, V b, T rtol = DREAMPLACE_RTOL) { - return floor(div(a + rtol * b, b)); -} - -/// @brief template specialization for integral types -template -inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type -floorDiv(T a, T b) { - return a / b; -} - -/// @brief template specialization for non-integral types -template -inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type -ceilDiv(T a, V b, T rtol = DREAMPLACE_RTOL) { - return ceil(div(a - rtol * b, b)); -} - -/// @brief template specialization for integral types -template -inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type -ceilDiv(T a, T b) { - return (a + b - 1) / b; -} - -/// @brief template specialization for non-integral types -template -inline DREAMPLACE_HOST_DEVICE typename std::enable_if::value, T>::type -roundDiv(T a, V b) { - return round(div(a, b)); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h deleted file mode 100644 index 28bce2ce..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @file Namespace.h - * @author Yibo Lin (DREAMPlace), Rachel Selina (DREAMPlaceFPGA) - * @date Jan 2019 - */ - -#ifndef DREAMPLACE_UTILITY_NAMESPACE_H -#define DREAMPLACE_UTILITY_NAMESPACE_H - -#define DREAMPLACE_NAMESPACE DreamPlaceFPGA -#define DREAMPLACE_BEGIN_NAMESPACE namespace DreamPlaceFPGA { -#define DREAMPLACE_END_NAMESPACE } - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh deleted file mode 100644 index b9749f40..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file print.cuh - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - */ -#ifndef _DREAMPLACE_UTILITY_PRINT_CUH -#define _DREAMPLACE_UTILITY_PRINT_CUH - -#include -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -void printArray(const T* x, const int n, const char* str) { - dreamplacePrint(kNONE, "%s[%d] = ", str, n); - std::vector host_x(n); - cudaMemcpy(host_x.data(), x, n * sizeof(T), cudaMemcpyDeviceToHost); - for (int i = 0; i < n; ++i) { - dreamplacePrint(kNONE, "%g ", double(host_x[i])); - } - dreamplacePrint(kNONE, "\n"); -} - -template -void printScalar(const T& x, const char* str) { - dreamplacePrint(kNONE, "%s = ", str); - T host_x = 0; - cudaMemcpy(&host_x, &x, sizeof(T), cudaMemcpyDeviceToHost); - dreamplacePrint(kNONE, "%g\n", double(host_x)); -} - -template -void print2DArray(const T* x, const int m, const int n, const char* str) { - dreamplacePrint(kNONE, "%s[%dx%d] = \n", str, m, n); - std::vector host_x(m * n, 0); - cudaMemcpy(host_x.data(), x, m * n * sizeof(T), cudaMemcpyDeviceToHost); - for (int i = 0; i < m * n; ++i) { - if (i && (i % n) == 0) { - dreamplacePrint(kNONE, "\n"); - } - dreamplacePrint(kNONE, "%g ", double(host_x[i])); - } - dreamplacePrint(kNONE, "\n"); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh deleted file mode 100644 index 985ccede..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file timer.cuh - * @author Yibo Lin (DREAMPlace) - * @date Apr 2020 - */ - -#ifndef DREAMPLACE_UTILITY_TIMER_CUH -#define DREAMPLACE_UTILITY_TIMER_CUH - -#include -#include "utility/src/namespace.h" - -DREAMPLACE_BEGIN_NAMESPACE - -struct CUDATimer { - typedef std::chrono::high_resolution_clock::rep hr_clock_rep; - - __device__ static inline long long int getGlobaltime(void) { - long long int ret; - - asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(ret)); - - return ret; - } - - // Returns the period in miliseconds - __device__ static inline double getTimerPeriod(void) { return 1.0e-6; } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h deleted file mode 100644 index 4b368bb7..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @file timer.h - * @author Yibo Lin (DREAMPlace) - * @date Apr 2020 - */ - -#ifndef DREAMPLACE_UTILITY_TIMER_H -#define DREAMPLACE_UTILITY_TIMER_H - -#include -#include "utility/src/namespace.h" - -DREAMPLACE_BEGIN_NAMESPACE - -struct CPUTimer { - typedef std::chrono::high_resolution_clock::rep hr_clock_rep; - - static inline hr_clock_rep getGlobaltime(void) { - return std::chrono::high_resolution_clock::now().time_since_epoch().count(); - } - - // Returns the period in miliseconds - static inline double getTimerPeriod(void) { - return 1000.0 * std::chrono::high_resolution_clock::period::num / - std::chrono::high_resolution_clock::period::den; - } -}; - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h deleted file mode 100644 index 9014efa1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h +++ /dev/null @@ -1,108 +0,0 @@ -//** -// * @file torch.h -// * @author Yibo Lin (DREAMPlace) -// * @date Mar 2019 -// * @brief Required heads from torch -// */ -#ifndef _DREAMPLACE_UTILITY_TORCH_H -#define _DREAMPLACE_UTILITY_TORCH_H - -/// As torch may change the header inclusion conventions, it is better to manage -/// it in a consistent way. -#if TORCH_VERSION_MAJOR >= 1 -#include - -#if TORCH_VERSION_MINOR >= 3 - -#define DREAMPLACE_TENSOR_DATA_PTR(TENSOR, TYPE) \ - ((TENSOR.defined())? TENSOR.data_ptr() : nullptr) -#define DREAMPLACE_TENSOR_SCALARTYPE(TENSOR) TENSOR.scalar_type() - -#else - -#define DREAMPLACE_TENSOR_DATA_PTR(TENSOR, TYPE) \ - ((TENSOR.defined())? TENSOR.data() : nullptr) -#define DREAMPLACE_TENSOR_SCALARTYPE(TENSOR) TENSOR.type().scalarType() - -#endif - -// torch version 1.8 or later -#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 8) - -// torch version 1.13 or later -#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 13) - -// AT_PRIVATE_CASE_TYPE was recently removed from the public dispatch API (look in the Dispatch.h) -#define AT_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ - case enum_type: { \ - using scalar_t = type; \ - return __VA_ARGS__(); \ - } - -#endif - -#define DREAMPLACE_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ - AT_PRIVATE_CASE_TYPE(NAME, enum_type, type, __VA_ARGS__) - -#else - -#define DREAMPLACE_PRIVATE_CASE_TYPE(NAME, enum_type, type, ...) \ - AT_PRIVATE_CASE_TYPE(enum_type, type, __VA_ARGS__) - -#endif - -#else - -#include - -#endif - -#include - -#define CHECK_CPU(x) AT_ASSERTM(!x.is_cuda(), #x " must be a tensor on CPU") -#define CHECK_CUDA(x) AT_ASSERTM(x.is_cuda(), #x " must be a tensor on CUDA") -#define CHECK_FLAT(x) AT_ASSERTM(x.ndimension() == 1, #x "must be a flat tensor") - -#define CHECK_FLAT_CPU(x) \ - CHECK_CPU(x); \ - CHECK_FLAT(x); -#define CHECK_FLAT_CUDA(x) \ - CHECK_CUDA(x); \ - CHECK_FLAT(x); - -#define CHECK_EVEN(x) \ - AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) \ - AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// As the API for torch changes, customize a DREAMPlace version to remove -/// warnings - -#include "utility/src/torch_fft_api.h" - -#define DREAMPLACE_DISPATCH_FLOATING_TYPES(TENSOR, NAME, ...) \ - [&] { \ - at::ScalarType _st = DREAMPLACE_TENSOR_SCALARTYPE(TENSOR); \ - switch (_st) { \ - DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Double, double, __VA_ARGS__) \ - DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Float, float, __VA_ARGS__) \ - default: \ - AT_ERROR(#NAME, " not implemented for '", toString(_st), "'"); \ - } \ - }() - -/// I remove the support to Char, since int8_t does not compile for CUDA -/// char does not compile for ATen either -#define DREAMPLACE_DISPATCH_INT_FLOAT_TYPES(TENSOR, NAME, ...) \ - [&] { \ - at::ScalarType _st = DREAMPLACE_TENSOR_SCALARTYPE(TENSOR); \ - switch (_st) { \ - DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Float, float, __VA_ARGS__) \ - DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Double, double, __VA_ARGS__) \ - DREAMPLACE_PRIVATE_CASE_TYPE(NAME, at::ScalarType::Int, int, __VA_ARGS__) \ - default: \ - AT_ERROR(#NAME, " not implemented for '", at::toString(_st), "'"); \ - } \ - }() - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h deleted file mode 100644 index e6a524a8..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h +++ /dev/null @@ -1,121 +0,0 @@ -/** - * File : torch_fft_api.h - * Author : Yihua Liu - * Date : 08.29.2022 - * Last Modified By : Yibo Lin (DREAMPlace) - * Comment : Copy from DREAMPlace (ASIC) to enable pytorch 1.13 support - */ - -#pragma once - -#if TORCH_VERSION_MAJOR > 1 || (TORCH_VERSION_MAJOR == 1 && TORCH_VERSION_MINOR >= 8) - -namespace at { - static inline Tensor rfft(const Tensor & input, int signal_ndim, bool normalized = false, bool onesided = true) { - at::Tensor y; - if (onesided) { - if (normalized) { - if (signal_ndim == 1) { - y = fft_rfft(input, c10::nullopt, -1, "ortho"); - } else if (signal_ndim == 2) { - y = fft_rfft2(input, c10::nullopt, {-2, -1}, "ortho"); - } else if (signal_ndim == 3) { - y = fft_rfftn(input, c10::nullopt, std::vector{-3, -2, -1}, "ortho"); - } else { - TORCH_CHECK_VALUE(false, "Ortho-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } else { - if (signal_ndim == 1) { - y = fft_rfft(input, c10::nullopt, -1, "backward"); - } else if (signal_ndim == 2) { - y = fft_rfft2(input, c10::nullopt, {-2, -1}, "backward"); - } else if (signal_ndim == 3) { - y = fft_rfftn(input, c10::nullopt, std::vector{-3, -2, -1}, "backward"); - } else { - TORCH_CHECK_VALUE(false, "Backward-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } - } else { - if (normalized) { - if (signal_ndim == 1) { - y = fft_fft(input, c10::nullopt, -1, "ortho"); - } else if (signal_ndim == 2) { - y = fft_fft2(input, c10::nullopt, {-2, -1}, "ortho"); - } else if (signal_ndim == 3) { - y = fft_fftn(input, c10::nullopt, std::vector{-3, -2, -1}, "ortho"); - } else { - TORCH_CHECK_VALUE(false, "Ortho-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } else { - if (signal_ndim == 1) { - y = fft_fft(input, c10::nullopt, -1, "backward"); - } else if (signal_ndim == 2) { - y = fft_fft2(input, c10::nullopt, {-2, -1}, "backward"); - } else if (signal_ndim == 3) { - y = fft_fftn(input, c10::nullopt, std::vector{-3, -2, -1}, "backward"); - } else { - TORCH_CHECK_VALUE(false, "Backward-normalized rfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } - } - - return view_as_real(y).contiguous(); - } - - static inline Tensor irfft(const Tensor & input, int signal_ndim, bool normalized = false, bool onesided = true, c10::optional signal_sizes = c10::nullopt) { - at::Tensor y; - // user controls onesided actually by the signal_sizes; - // in other words, the parameter onesided is not really used - TORCH_CHECK_VALUE(signal_sizes, "Parameter signal_sizes is required"); - - if (onesided) { - if (normalized) { - if (signal_ndim == 1) { - y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "ortho"); - } else if (signal_ndim == 2) { - y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "ortho"); - } else if (signal_ndim == 3) { - y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "ortho"); - } else { - TORCH_CHECK_VALUE(false, "Ortho-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } else { - if (signal_ndim == 1) { - y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "backward"); - } else if (signal_ndim == 2) { - y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "backward"); - } else if (signal_ndim == 3) { - y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "backward"); - } else { - TORCH_CHECK_VALUE(false, "Backward-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } - } else { - if (normalized) { - if (signal_ndim == 1) { - y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "ortho"); - } else if (signal_ndim == 2) { - y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "ortho"); - } else if (signal_ndim == 3) { - y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "ortho"); - } else { - TORCH_CHECK_VALUE(false, "Ortho-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } else { - if (signal_ndim == 1) { - y = fft_irfft(view_as_complex(input), signal_sizes.value()[0], -1, "backward"); - } else if (signal_ndim == 2) { - y = fft_irfft2(view_as_complex(input), signal_sizes, {-2, -1}, "backward"); - } else if (signal_ndim == 3) { - y = fft_irfftn(view_as_complex(input), signal_sizes, std::vector{-3, -2, -1}, "backward"); - } else { - TORCH_CHECK_VALUE(false, "Backward-normalized irfft() has illegal number of dimensions ", std::to_string(signal_ndim)); - } - } - } - - return y.contiguous(); - } -} - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh deleted file mode 100644 index 295dcba5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @file utils.cuh - * @author Yibo Lin (DREAMPlace) - * @date Jan 2019 - */ -#ifndef _DREAMPLACE_UTILITY_UTILS_CUH -#define _DREAMPLACE_UTILITY_UTILS_CUH - -#include -#include -#include -// namespace -#include "utility/src/namespace.h" -// macro definitions -#include "utility/src/defs.h" -// print utilities -#include "utility/src/Msg.h" -#include "utility/src/print.cuh" -// math utilities -#include "utility/src/math.h" -// timer utilities -#include "utility/src/timer.cuh" -#include "utility/src/timer.h" -// numeric limits -#include "utility/src/limits.h" -// simple data structures -#include "utility/src/Box.h" -#include "utility/src/ComplexNumber.h" -#include "utility/src/diamond_search.h" -#include "utility/src/FlatNestedVector.cuh" -#include "utility/src/PitchNestedVector.cuh" -// atomic ops -#include "utility/src/atomic_ops.cuh" - -DREAMPLACE_BEGIN_NAMESPACE -/// to replace thrust::swap -template -__host__ __device__ inline void host_device_swap(Assignable1 &a, Assignable2 &b) { - Assignable1 tmp = a; - a = b; - b = tmp; -} - -template -__global__ void iota(T* a, int n) { - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; - i += blockDim.x * gridDim.x) { - a[i] = i; - } -} - -template -__global__ void fill_array_kernel(T* array, int n, T v) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < n) { - array[i] = v; - } -} - -template -inline void fill_array(T* array, int n, T v) { - fill_array_kernel<<>>(array, n, v); -} - -template -__global__ void reset_element_set_sizes_kernel(int num_sets, - T* element_set_sizes) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_sets) { - element_set_sizes[i] = 0; - } -} - -template -__global__ void collect_element_sets_kernel(int n, int num_sets, - int max_set_size, const T* elements, - const int* element2partition_map, - T* element_sets, - int* element_set_sizes) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < n) { - const T& element = elements[i]; - int partition_id = element2partition_map[i]; - assert(partition_id < num_sets); - int& size = element_set_sizes[partition_id]; - int index = atomicAdd(&size, 1); - if (index < max_set_size) { - element_sets[partition_id * max_set_size + index] = element; - } - } -} - -template -__global__ void correct_element_set_sizes_kernel(int num_sets, T max_set_size, - T* element_set_sizes) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_sets) { - T& size = element_set_sizes[i]; - size = min(size, max_set_size); - } -} - -/// @brief gather elements into sets according to element2partition_map -/// For example, elements = {a0, a1, a2, a3, a4, a5} -/// element2partition_map = {1, 0, 0, 1, 2, 1} -/// expected result element_sets = {{a1, a2, null}, {a0, a3, a5}, {a4, null, -/// null}} Current implementation is not deterministic. If introducing sorting, -/// determinism is possible. -/// @param n length of elements -/// @param num_sets number of partitions -/// @param max_set_size maximum number of elements in a partition -/// @param elements array of elements -/// @param element2partition_map map element index to partition -/// @param element_sets output element sets in dimension num_sets x max_set_size -/// @param element_set_sizes size of each set in dimension num_sets x 1 -template -inline __host__ void gather(int n, int num_sets, int max_set_size, - const T* elements, const int* element2partition_map, - T* element_sets, int* element_set_sizes) { - fill_array(element_sets, num_sets * max_set_size, - std::numeric_limits::max()); - reset_element_set_sizes_kernel<<>>( - num_sets, element_set_sizes); - collect_element_sets_kernel<<>>( - n, num_sets, max_set_size, elements, element2partition_map, element_sets, - element_set_sizes); - correct_element_set_sizes_kernel<<>>( - num_sets, max_set_size, element_set_sizes); -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h deleted file mode 100644 index b4f56926..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file utils.h - * @author Yibo Lin (DREAMPlace) - * @date Nov 2019 - */ -#ifndef _DREAMPLACE_UTILITY_UTILS_H -#define _DREAMPLACE_UTILITY_UTILS_H - -// namespace -#include "utility/src/namespace.h" -// macro definitions -#include "utility/src/defs.h" -// print utilities -#include "utility/src/Msg.h" -// math utilities -#include "utility/src/math.h" -// timer utilities -#include "utility/src/timer.h" -// numeric limits -#include "utility/src/limits.h" -// simple data structures -#include "utility/src/Box.h" -#include "utility/src/ComplexNumber.h" -#include "utility/src/diamond_search.h" -// atomic ops -#include "utility/src/atomic_ops.h" - -// placement database -//#include "utility/src/detailed_place_db.h" -//#include "utility/src/legalization_db.h" -//#include "utility/src/make_placedb.h" - -DREAMPLACE_BEGIN_NAMESPACE - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh deleted file mode 100644 index 4a32182c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh +++ /dev/null @@ -1,21 +0,0 @@ -/** - * File : utils_cub.cuh - * Author : Yibo Lin (DREAMPlace) - * Date : 06.25.2021 - */ - -#ifndef _DREAMPLACE_UTILITY_UTILS_CUB_CUH -#define _DREAMPLACE_UTILITY_UTILS_CUB_CUH - -#include "utility/src/namespace.h" - -// include cub in a safe manner -#define CUB_NS_PREFIX namespace DREAMPLACE_NAMESPACE { -#define CUB_NS_POSTFIX } -#define CUB_NS_QUALIFIER DREAMPLACE_NAMESPACE::cub -#include "cub/cub.cuh" -#undef CUB_NS_QUALIFIER -#undef CUB_NS_POSTFIX -#undef CUB_NS_PREFIX - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore deleted file mode 100644 index 771a9fab..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.pkl -*.dat diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt deleted file mode 100644 index a160f81a..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt +++ /dev/null @@ -1,67 +0,0 @@ -set(OP_NAME weighted_average_wirelength) - -set(TARGET_NAME ${OP_NAME}) - -set(INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -set(LINK_LIBS utility_cxx - OpenMP::OpenMP_CXX - ) - -add_pytorch_extension(${TARGET_NAME}_cpp - src/weighted_average_wirelength.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cpp_atomic - src/weighted_average_wirelength_atomic.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cpp_merged - src/weighted_average_wirelength_merged.cpp - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cpp - ${TARGET_NAME}_cpp_atomic - ${TARGET_NAME}_cpp_merged - DESTINATION dreamplacefpga/ops/${OP_NAME}) - -if(TORCH_ENABLE_CUDA) - -add_torch_extension(integrate_net_weights_cuda - src/integrate_net_weights_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda - src/weighted_average_wirelength_cuda.cpp - src/weighted_average_wirelength_cuda_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda_atomic - src/weighted_average_wirelength_cuda_atomic.cpp - src/weighted_average_wirelength_cuda_atomic_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) - -add_pytorch_extension(${TARGET_NAME}_cuda_merged - src/weighted_average_wirelength_cuda_merged.cpp - src/weighted_average_wirelength_cuda_merged_kernel.cu - EXTRA_INCLUDE_DIRS ${INCLUDE_DIRS} - EXTRA_LINK_LIBRARIES integrate_net_weights_cuda ${LINK_LIBS}) - -install(TARGETS - ${TARGET_NAME}_cuda - ${TARGET_NAME}_cuda_atomic - ${TARGET_NAME}_cuda_merged - DESTINATION dreamplacefpga/ops/${OP_NAME}) -endif(TORCH_ENABLE_CUDA) - -file(GLOB INSTALL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) -install(FILES ${INSTALL_SRCS} DESTINATION dreamplacefpga/ops/${OP_NAME}) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py deleted file mode 100644 index 1d18f4f0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -## -# @file __init__.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# @brief ops -# diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore deleted file mode 100644 index a2258671..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -c -build -dist -*.egg-info -*.pyc diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h deleted file mode 100644 index 7dce67fc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h +++ /dev/null @@ -1,197 +0,0 @@ -#ifndef GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_H -#define GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_H - -#include "utility/src/torch.h" -#include "utility/src/utils.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -void integrateNetWeightsLauncher( - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - const T *net_weights, - const T *net_weights_x, - T *grad_x_tensor, T *grad_y_tensor, - int num_nets, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int net_id = 0; net_id < num_nets; ++net_id) - { - if (net_mask[net_id]) - { - T weight = net_weights[net_id]; - T weight_x = net_weights_x[net_id]; - for (int j = netpin_start[net_id]; j < netpin_start[net_id + 1]; ++j) - { - int pin_id = flat_netpin[j]; - grad_x_tensor[pin_id] *= weight_x; - grad_y_tensor[pin_id] *= weight; - } - } - } -} - -// V has to be int, or long long int -template -void computeMaxMinNetByNet( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - V *x_max_ptr, - V *x_min_ptr, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - if (net_mask[i]) - { - const int x_index = i; - const int y_index = i + num_nets; - - V x_max = x_max_ptr[x_index]; - V x_min = x_min_ptr[x_index]; - V y_max = x_max_ptr[y_index]; - V y_min = x_min_ptr[y_index]; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = DREAMPLACE_STD_NAMESPACE::max((V)xx, x_max); - x_min = DREAMPLACE_STD_NAMESPACE::min((V)xx, x_min); - - T yy = y[flat_netpin[j]]; - y_max = DREAMPLACE_STD_NAMESPACE::max((V)yy, y_max); - y_min = DREAMPLACE_STD_NAMESPACE::min((V)yy, y_min); - } - - x_max_ptr[x_index] = x_max; - x_min_ptr[x_index] = x_min; - x_max_ptr[y_index] = y_max; - x_min_ptr[y_index] = y_min; - } - } -} - -template -void computeABCKernelsPinByPin( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_x, T *exp_nx, - T *exp_x_sum, T *exp_nx_sum, - T *xexp_x_sum, T *xexp_nx_sum, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_pins / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_pins; ++i) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); - exp_nx[i] = exp((x_min[net_id] - x[i]) * (*inv_gamma)); - -#pragma omp atomic - exp_x_sum[net_id] += exp_x[i]; -#pragma omp atomic - exp_nx_sum[net_id] += exp_nx[i]; -#pragma omp atomic - xexp_x_sum[net_id] += x[i] * exp_x[i]; -#pragma omp atomic - xexp_nx_sum[net_id] += x[i] * exp_nx[i]; - - net_id += num_nets; - int pin_id = i + num_pins; - exp_x[pin_id] = exp((y[i] - x_max[net_id]) * (*inv_gamma)); - exp_nx[pin_id] = exp((x_min[net_id] - y[i]) * (*inv_gamma)); - -#pragma omp atomic - exp_x_sum[net_id] += exp_x[pin_id]; -#pragma omp atomic - exp_nx_sum[net_id] += exp_nx[pin_id]; -#pragma omp atomic - xexp_x_sum[net_id] += y[i] * exp_x[pin_id]; -#pragma omp atomic - xexp_nx_sum[net_id] += y[i] * exp_nx[pin_id]; - } - } -} - -template -void computeXExpSumByExpSumXY( - const T *xexp_x_sum, const T *xexp_nx_sum, - const T *exp_x_sum, const T *exp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - //T *partial_wl, - T *partial_wl_x, - T *partial_wl_y, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - if (net_mask[i]) - { - //T wl_x = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; - partial_wl_x[i] = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; - int y_index = i + num_nets; - //T wl_y = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; - partial_wl_y[i] = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; - - //partial_wl[i] = wl_x + wl_y; - } - } -} - -template -void computeWeightedAverageWirelengthGradPinByPin( - const T *x, const T *y, - const T *exp_x, const T *exp_nx, - const T *exp_x_sum, const T *exp_nx_sum, - const T *xexp_x_sum, const T *xexp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_pins / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_pins; ++i) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - grad_x_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[i] - ((1 - (*inv_gamma) * x[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[i]); - - net_id += num_nets; - int pin_id = i + num_pins; - grad_y_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * y[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); - } - } -} - -DREAMPLACE_END_NAMESPACE - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h deleted file mode 100644 index 89f2a5a2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h +++ /dev/null @@ -1,719 +0,0 @@ -/** - * @file functional_cuda.h - * @author Yibo Lin (DREAMPlace) - * @date Nov 2018 - */ - -#ifndef GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_CUDA_H -#define GPUPLACE_WEIGHTED_AVERAGE_WIRELENGTH_FUNCTIONAL_CUDA_H - -// V has to be int, or long long int -template -__global__ void computeMax( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - V *x_max) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicMax(&x_max[net_id], (V)(x[i])); - } - } -} - -// V has to be int, or long long int -template -__global__ void computeMin( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - V *x_min) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicMin(&x_min[net_id], (V)(x[i])); - } - } -} - -// V has to be int, or long long int -template -__global__ void computeMaxMinPinByPin( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - int num_nets, - V *x_max, - V *x_min) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicMax(&x_max[net_id], (V)(x[i])); - atomicMin(&x_min[net_id], (V)(x[i])); - - net_id += num_nets; - atomicMax(&x_max[net_id], (V)(y[i])); - atomicMin(&x_min[net_id], (V)(y[i])); - } - } -} - -// V has to be int, or long long int -template -__global__ void computeMaxMinInterleavePinByPin( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_pins, - int num_nets, - V *x_max, - V *x_min) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - net_id += threadIdx.y * num_nets; - int pin_id = i + threadIdx.y * num_pins; - - atomicMax(&x_max[net_id], (V)(x[pin_id])); - atomicMin(&x_min[net_id], (V)(x[pin_id])); - } - } -} - -// V has to be int, or long long int -template -__global__ void computeMaxMinNetByNet( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - V *x_max_ptr, - V *x_min_ptr) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - const int x_index = i; - const int y_index = i + num_nets; - - V x_max = x_max_ptr[x_index]; - V x_min = x_min_ptr[x_index]; - V y_max = x_max_ptr[y_index]; - V y_min = x_min_ptr[y_index]; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = max((V)xx, x_max); - x_min = min((V)xx, x_min); - - T yy = y[flat_netpin[j]]; - y_max = max((V)yy, y_max); - y_min = min((V)yy, y_min); - } - - x_max_ptr[x_index] = x_max; - x_min_ptr[x_index] = x_min; - x_max_ptr[y_index] = y_max; - x_min_ptr[y_index] = y_min; - } -} - -// V has to be int, or long long int -template -__global__ void computeMaxMinInterleaveNetByNet( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - V *pos_max_ptr, - V *pos_min_ptr) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - const T *pos_ptr = threadIdx.y == 0 ? x : y; - const int net_id = i + threadIdx.y * num_nets; - V pos_max = pos_max_ptr[net_id]; - V pos_min = pos_min_ptr[net_id]; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T pos = pos_ptr[flat_netpin[j]]; - pos_max = max((V)pos, pos_max); - pos_min = min((V)pos, pos_min); - } - pos_max_ptr[net_id] = pos_max; - pos_min_ptr[net_id] = pos_min; - } -} - -template -__global__ void computeExp( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, - T *exp_x) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); - } - } -} - -template -__global__ void computeNegExp( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_min, - T *exp_nx) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_nx[i] = exp(-(x[i] - x_min[net_id]) * (*inv_gamma)); - } - } -} - -template -__global__ void computeExpSum( - const T *exp_x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - T *exp_x_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicAdd(&exp_x_sum[net_id], exp_x[i]); - } - } -} - -template -__global__ void computeXExpSum( - const T *x, - const T *exp_x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - T *xexp_x_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - atomicAdd(&xexp_x_sum[net_id], x[i] * exp_x[i]); - } - } -} - -template -__global__ void computeABCKernelsPinByPin( - const T *x, const T *y, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_x, T *exp_nx, - T *exp_x_sum, T *exp_nx_sum, - T *xexp_x_sum, T *xexp_nx_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - exp_x[i] = exp((x[i] - x_max[net_id]) * (*inv_gamma)); - exp_nx[i] = exp((x_min[net_id] - x[i]) * (*inv_gamma)); - - atomicAdd(&exp_x_sum[net_id], exp_x[i]); - atomicAdd(&exp_nx_sum[net_id], exp_nx[i]); - atomicAdd(&xexp_x_sum[net_id], x[i] * exp_x[i]); - atomicAdd(&xexp_nx_sum[net_id], x[i] * exp_nx[i]); - - net_id += num_nets; - int pin_id = i + num_pins; - exp_x[pin_id] = exp((y[i] - x_max[net_id]) * (*inv_gamma)); - exp_nx[pin_id] = exp((x_min[net_id] - y[i]) * (*inv_gamma)); - - atomicAdd(&exp_x_sum[net_id], exp_x[pin_id]); - atomicAdd(&exp_nx_sum[net_id], exp_nx[pin_id]); - atomicAdd(&xexp_x_sum[net_id], y[i] * exp_x[pin_id]); - atomicAdd(&xexp_nx_sum[net_id], y[i] * exp_nx[pin_id]); - } - } -} - -template -__global__ void computeABCKernelsInterleavePinByPin( - const T *x, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_x, T *exp_nx, - T *exp_x_sum, T *exp_nx_sum, - T *xexp_x_sum, T *xexp_nx_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - net_id += threadIdx.y * num_nets; - int pin_id = i + threadIdx.y * num_pins; - - exp_x[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); - exp_nx[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); - - atomicAdd(&exp_x_sum[net_id], exp_x[pin_id]); - atomicAdd(&exp_nx_sum[net_id], exp_nx[pin_id]); - atomicAdd(&xexp_x_sum[net_id], x[pin_id] * exp_x[pin_id]); - atomicAdd(&xexp_nx_sum[net_id], x[pin_id] * exp_nx[pin_id]); - } - } -} - -template -__global__ void computeABCKernelsNetByNet( - const T *x, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int x_index = i; - int y_index = i + num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - exp_xy[pin_id] = exp((x[pin_id] - x_max[x_index]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[x_index] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[x_index] += exp_xy[pin_id]; - exp_nxy_sum[x_index] += exp_nxy[pin_id]; - xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; - - pin_id += num_pins; - exp_xy[pin_id] = exp((x[pin_id] - x_max[y_index]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[y_index] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[y_index] += exp_xy[pin_id]; - exp_nxy_sum[y_index] += exp_nxy[pin_id]; - xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; - } - } -} - -template -__global__ void computeABCKernelsInterleaveNetByNet( - const T *x, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int net_id = i + threadIdx.y * num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j] + threadIdx.y * num_pins; - - exp_xy[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[net_id] += exp_xy[pin_id]; - exp_nxy_sum[net_id] += exp_nxy[pin_id]; - xyexp_xy_sum[net_id] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[net_id] += x[pin_id] * exp_nxy[pin_id]; - } - } -} - -template -__global__ void computeABCKernelsAndWLNetByNet( - const T *x, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - T * partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int x_index = i; - int y_index = i + num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - exp_xy[pin_id] = exp((x[pin_id] - x_max[x_index]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[x_index] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[x_index] += exp_xy[pin_id]; - exp_nxy_sum[x_index] += exp_nxy[pin_id]; - xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; - - pin_id += num_pins; - exp_xy[pin_id] = exp((x[pin_id] - x_max[y_index]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[y_index] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[y_index] += exp_xy[pin_id]; - exp_nxy_sum[y_index] += exp_nxy[pin_id]; - xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; - } - partial_wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + - xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; - } -} - -template -__global__ void computeABCKernelsInterleaveAndWLNetByNet( - const T *x, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - V *x_max, V *x_min, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - T *partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int net_id = i + threadIdx.y * num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j] + threadIdx.y * num_pins; - - exp_xy[pin_id] = exp((x[pin_id] - x_max[net_id]) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min[net_id] - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[net_id] += exp_xy[pin_id]; - exp_nxy_sum[net_id] += exp_nxy[pin_id]; - xyexp_xy_sum[net_id] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[net_id] += x[pin_id] * exp_nxy[pin_id]; - } - atomicAdd(&partial_wl[i], xyexp_xy_sum[net_id] / exp_xy_sum[net_id] - xyexp_nxy_sum[net_id] / exp_nxy_sum[net_id]); - } -} - -template -__global__ void computeXExpSumByExpSum( - const T *xexp_x_sum, - const T *exp_x_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - T *partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - if (net_mask[i]) - { - partial_wl[i] = xexp_x_sum[i] / exp_x_sum[i]; - } - } -} - -template -__global__ void computeXNegExpSumByNegExpSum( - const T *xexp_nx_sum, - const T *exp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - T *partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets) - { - if (net_mask[i]) - { - partial_wl[i] = -xexp_nx_sum[i] / exp_nx_sum[i]; - } - } -} - -template -__global__ void computeXExpSumByExpSumXY( - const T *xexp_x_sum, const T *xexp_nx_sum, - const T *exp_x_sum, const T *exp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - T *partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - T wl_x = xexp_x_sum[i] / exp_x_sum[i] - xexp_nx_sum[i] / exp_nx_sum[i]; - int y_index = i + num_nets; - T wl_y = xexp_x_sum[y_index] / exp_x_sum[y_index] - xexp_nx_sum[y_index] / exp_nx_sum[y_index]; - - partial_wl[i] = wl_x + wl_y; - } -} - -template -__global__ void computeWeightedAverageWirelength( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - T *partial_wl) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - const int x_index = i; - const int y_index = i + num_nets; - - T x_max = -FLT_MAX; - T x_min = FLT_MAX; - T y_max = -FLT_MAX; - T y_min = FLT_MAX; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = max(xx, x_max); - x_min = min(xx, x_min); - - T yy = y[flat_netpin[j]]; - y_max = max(yy, y_max); - y_min = min(yy, y_min); - } - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - exp_xy[pin_id] = exp((x[pin_id] - x_max) * (*inv_gamma)); - exp_nxy[pin_id] = exp((x_min - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[x_index] += exp_xy[pin_id]; - exp_nxy_sum[x_index] += exp_nxy[pin_id]; - xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; - - pin_id += num_pins; - exp_xy[pin_id] = exp((x[pin_id] - y_max) * (*inv_gamma)); - exp_nxy[pin_id] = exp((y_min - x[pin_id]) * (*inv_gamma)); - exp_xy_sum[y_index] += exp_xy[pin_id]; - exp_nxy_sum[y_index] += exp_nxy[pin_id]; - xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; - } - - partial_wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + - xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; - } -} - -template -__global__ void computeWeightedAverageWirelengthGradPinByPin( - const T *x, const T *y, - const T *exp_x, const T *exp_nx, - const T *exp_x_sum, const T *exp_nx_sum, - const T *xexp_x_sum, const T *xexp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - grad_x_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[i] - ((1 - (*inv_gamma) * x[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[i]); - - net_id += num_nets; - int pin_id = i + num_pins; - grad_y_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * y[i]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); - } - } -} - -template -__global__ void computeWeightedAverageWirelengthGradInterleavePinByPin( - const T *x, const T *y, - const T *exp_x, const T *exp_nx, - const T *exp_x_sum, const T *exp_nx_sum, - const T *xexp_x_sum, const T *xexp_nx_sum, - const int *pin2net_map, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - net_id += threadIdx.y * num_nets; - int pin_id = i + threadIdx.y * num_pins; - - grad_x_tensor[pin_id] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); - } - } -} - -template -__global__ void computeWeightedAverageWirelengthGradNetByNet( - const T *x, const T *y, - const T *exp_x, const T *exp_nx, - const T *exp_x_sum, const T *exp_nx_sum, - const T *xexp_x_sum, const T *xexp_nx_sum, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int x_index = i; - int y_index = i + num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - grad_x_tensor[pin_id] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[x_index] - (*inv_gamma) * xexp_x_sum[x_index]) / (exp_x_sum[x_index] * exp_x_sum[x_index]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[x_index] + (*inv_gamma) * xexp_nx_sum[x_index]) / (exp_nx_sum[x_index] * exp_nx_sum[x_index]) * exp_nx[pin_id]); - - int pin_id_y = pin_id + num_pins; - grad_y_tensor[pin_id] = (*grad_tensor) * - (((1 + (*inv_gamma) * y[pin_id]) * exp_x_sum[y_index] - (*inv_gamma) * xexp_x_sum[y_index]) / (exp_x_sum[y_index] * exp_x_sum[y_index]) * exp_x[pin_id_y] - ((1 - (*inv_gamma) * y[pin_id]) * exp_nx_sum[y_index] + (*inv_gamma) * xexp_nx_sum[y_index]) / (exp_nx_sum[y_index] * exp_nx_sum[y_index]) * exp_nx[pin_id_y]); - } - } -} - -template -__global__ void computeWeightedAverageWirelengthGradInterleaveNetByNet( - const T *x, const T *y, - const T *exp_x, const T *exp_nx, - const T *exp_x_sum, const T *exp_nx_sum, - const T *xexp_x_sum, const T *xexp_nx_sum, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_nets && net_mask[i]) - { - int net_id = i + threadIdx.y * num_nets; - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j] + threadIdx.y * num_pins; - grad_x_tensor[pin_id] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[pin_id]) * exp_x_sum[net_id] - (*inv_gamma) * xexp_x_sum[net_id]) / (exp_x_sum[net_id] * exp_x_sum[net_id]) * exp_x[pin_id] - ((1 - (*inv_gamma) * x[pin_id]) * exp_nx_sum[net_id] + (*inv_gamma) * xexp_nx_sum[net_id]) / (exp_nx_sum[net_id] * exp_nx_sum[net_id]) * exp_nx[pin_id]); - } - } -} - -#endif diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu deleted file mode 100644 index da3ef8f0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file integrate_net_weights_cuda_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Jul 2019 - */ - -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -#include "weighted_average_wirelength/src/functional_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void integrateNetWeights( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - const T* net_weights_x, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < num_pins) - { - int net_id = pin2net_map[i]; - T weight = net_weights[net_id]; - T weight_x = net_weights_x[net_id]; - if (net_id >= 0 && net_mask[net_id]) - { - grad_x_tensor[i] *= weight_x; - grad_y_tensor[i] *= weight; - } - } -} - -template -void integrateNetWeightsCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - const T* net_weights_x, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ) -{ - integrateNetWeights<<>>(pin2net_map, net_mask, net_weights, net_weights_x, grad_x_tensor, grad_y_tensor, num_pins); -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template void integrateNetWeightsCudaLauncher(\ - const int* pin2net_map, \ - const unsigned char* net_mask, \ - const T* net_weights, \ - const T* net_weights_x, \ - T* grad_x_tensor, T* grad_y_tensor, \ - int num_pins\ - ); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp deleted file mode 100644 index 877ccffa..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/** - * @file src/weighted_average_wirelength.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute weighted-average wirelength and gradient according to e-place - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" -#include "weighted_average_wirelength/src/functional.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeWeightedAverageWirelengthLauncher( - const T *x, const T *y, - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - //T *wl, - T *wl_x, T* wl_y, - const T *grad_tensor, - int num_threads, - T *grad_x_tensor, T *grad_y_tensor); - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x " must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x " must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x " must be contiguous") - -/// @brief Compute weighted-average wirelength according to e-place -/// \sum(x*exp(x/gamma)) / \sum(exp(x/gamma)) - \sum(x*exp(-x/gamma)) / \sum(exp(-x/gamma)) -/// @param pos cell locations, array of x locations and then y locations -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma a scalar tensor for the parameter in the equation -std::vector weighted_average_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_weights_x, //Directional net weighting for carry chains - at::Tensor net_mask, - at::Tensor inv_gamma, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - at::Tensor exp_xy = at::empty_like(pos); - at::Tensor exp_nxy = at::empty_like(pos); - at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - //Update to compute x and y separately - //at::Tensor wl = at::zeros(num_nets, pos.options()); - at::Tensor wl_x = at::zeros(num_nets, pos.options()); - at::Tensor wl_y = at::zeros(num_nets, pos.options()); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthLauncher", [&] { - computeWeightedAverageWirelengthLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel() / 2, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - //DREAMPLACE_TENSOR_DATA_PTR(wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(wl_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(wl_y, scalar_t), - nullptr, - num_threads, - nullptr, nullptr); - }); - - if (net_weights.numel()) - { - //Apply different weights for x and y directions - //wl.mul_(net_weights); - wl_x.mul_(net_weights_x); - wl_y.mul_(net_weights); - } - - //return {wl.sum(), exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; - return {wl_x.sum() + wl_y.sum(), exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma a scalar tensor for the parameter in the equation -at::Tensor weighted_average_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor exp_xy, at::Tensor exp_nxy, - at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, - at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - at::Tensor inv_gamma, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - at::Tensor grad_out = at::zeros_like(pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthLauncher", [&] { - computeWeightedAverageWirelengthLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + pos.numel() / 2, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - netpin_start.numel() - 1, - pos.numel() / 2, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - nullptr, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - num_threads, - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2); - if (net_weights.numel()) - { - integrateNetWeightsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, - netpin_start.numel() - 1, - num_threads); - } - }); - return grad_out; -} - -template -int computeWeightedAverageWirelengthLauncher( - const T *x, const T *y, - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - //T *wl, - T *wl_x, - T *wl_y, - const T *grad_tensor, - int num_threads, - T *grad_x_tensor, T *grad_y_tensor) -{ - if (grad_tensor) - { - int chunk_size = std::max(int(num_pins / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_pins; ++i) - { - int net_id = pin2net_map[i]; - if (net_mask[net_id]) - { - grad_x_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * x[i]) * exp_xy_sum[net_id] - (*inv_gamma) * xyexp_xy_sum[net_id]) / (exp_xy_sum[net_id] * exp_xy_sum[net_id]) * exp_xy[i] - ((1 - (*inv_gamma) * x[i]) * exp_nxy_sum[net_id] + (*inv_gamma) * xyexp_nxy_sum[net_id]) / (exp_nxy_sum[net_id] * exp_nxy_sum[net_id]) * exp_nxy[i]); - - net_id += num_nets; - int pin_id = i + num_pins; - grad_y_tensor[i] = (*grad_tensor) * - (((1 + (*inv_gamma) * y[i]) * exp_xy_sum[net_id] - (*inv_gamma) * xyexp_xy_sum[net_id]) / (exp_xy_sum[net_id] * exp_xy_sum[net_id]) * exp_xy[pin_id] - ((1 - (*inv_gamma) * y[i]) * exp_nxy_sum[net_id] + (*inv_gamma) * xyexp_nxy_sum[net_id]) / (exp_nxy_sum[net_id] * exp_nxy_sum[net_id]) * exp_nxy[pin_id]); - } - } - } - else - { - int chunk_size = std::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - if (!net_mask[i]) - { - continue; - } - - int x_index = i; - int y_index = i + num_nets; - - //int degree = netpin_start[i+1]-netpin_start[i]; - T x_max = -std::numeric_limits::max(); - T x_min = std::numeric_limits::max(); - T y_max = -std::numeric_limits::max(); - T y_min = std::numeric_limits::max(); - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = std::max(xx, x_max); - x_min = std::min(xx, x_min); - T yy = y[flat_netpin[j]]; - y_max = std::max(yy, y_max); - y_min = std::min(yy, y_min); - } - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - int pin_id = flat_netpin[j]; - exp_xy[pin_id] = exp((x[pin_id] - x_max) * (*inv_gamma)); - exp_nxy[pin_id] = exp(-(x[pin_id] - x_min) * (*inv_gamma)); - exp_xy_sum[x_index] += exp_xy[pin_id]; - exp_nxy_sum[x_index] += exp_nxy[pin_id]; - xyexp_xy_sum[x_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[x_index] += x[pin_id] * exp_nxy[pin_id]; - - pin_id += num_pins; - exp_xy[pin_id] = exp((x[pin_id] - y_max) * (*inv_gamma)); - exp_nxy[pin_id] = exp(-(x[pin_id] - y_min) * (*inv_gamma)); - exp_xy_sum[y_index] += exp_xy[pin_id]; - exp_nxy_sum[y_index] += exp_nxy[pin_id]; - xyexp_xy_sum[y_index] += x[pin_id] * exp_xy[pin_id]; - xyexp_nxy_sum[y_index] += x[pin_id] * exp_nxy[pin_id]; - } - - //Split as x and y - //wl[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index] + - // xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; - wl_x[i] = xyexp_xy_sum[x_index] / exp_xy_sum[x_index] - xyexp_nxy_sum[x_index] / exp_nxy_sum[x_index]; - wl_y[i] = xyexp_xy_sum[y_index] / exp_xy_sum[y_index] - xyexp_nxy_sum[y_index] / exp_nxy_sum[y_index]; - } - } - - return 0; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp deleted file mode 100644 index 6d3a1fec..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "utility/src/Msg.h" -#include "utility/src/torch.h" -#include "weighted_average_wirelength/src/functional.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeWeightedAverageWirelengthAtomicLauncher( - const T *pos, const int *pin2net_map, const int *flat_netpin, - const int *netpin_start, const unsigned char *net_mask, int num_nets, - int num_pins, const T *inv_gamma, T *exp_xy, T *exp_nxy, T *exp_xy_sum, - T *exp_nxy_sum, T *xyexp_xy_sum, T *xyexp_nxy_sum, V *xy_max, V *xy_min, - //T *partial_wl, // wirelength of each net - T *partial_wl_x, T *partial_wl_y, // wirelength of each net - const T *grad_tensor, T *grad_x_tensor, - T *grad_y_tensor, // the gradient is partial total wirelength to partial pin position - int num_threads) -{ - const T *x = pos; - const T *y = pos + num_pins; - - if (grad_tensor) - { - computeWeightedAverageWirelengthGradPinByPin( - x, y, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, - xyexp_nxy_sum, pin2net_map, net_mask, num_nets, num_pins, inv_gamma, - grad_tensor, grad_x_tensor, grad_y_tensor, num_threads); - } - else - { - // PinByPin implementation needs atomic min/max operations, which are not supported by OpenMP. - computeMaxMinNetByNet( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - xy_max, - xy_min, - num_threads); - - // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA - // kernels (pin by pin) corresponding to the plus and minus a b c kernels in - // the DREAMPlace paper - computeABCKernelsPinByPin( - x, y, pin2net_map, net_mask, num_nets, num_pins, inv_gamma, xy_max, - xy_min, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, - xyexp_nxy_sum, num_threads); - - // compute partial wirelength - computeXExpSumByExpSumXY( - xyexp_xy_sum, xyexp_nxy_sum, exp_xy_sum, exp_nxy_sum, pin2net_map, - net_mask, num_nets, partial_wl_x, partial_wl_y, num_threads); - } - - return 0; -} - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -typedef int V; - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i -/// x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma inverse of gamma coefficient in weighted average -/// wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_atomic_forward( - at::Tensor pos, at::Tensor pin2net_map, at::Tensor flat_netpin, - at::Tensor netpin_start, at::Tensor net_weights, - at::Tensor net_weights_x, at::Tensor net_mask, - at::Tensor inv_gamma, int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for - // -y - at::Tensor exp_xy = at::empty_like(pos); - at::Tensor exp_nxy = at::empty_like(pos); - at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - //at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); - at::Tensor partial_wl_x = at::zeros({num_nets}, pos.options()); - at::Tensor partial_wl_y = at::zeros({num_nets}, pos.options()); - - // it is ok for xy_max and xy_min to be integer - // we do not really need accurate max/min, just some values to scale x/y - // therefore, there is no need to scale xy_max and xy_min to improve accuracy - at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::kInt); - at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::kInt); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeWeightedAverageWirelengthAtomicLauncher", [&] { - computeWeightedAverageWirelengthAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), num_nets, - num_pins, DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), - //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), nullptr, nullptr, nullptr, - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), - nullptr, nullptr, nullptr, - num_threads); - }); - - if (net_weights.numel()) - { - //partial_wl.mul_(net_weights.view({num_nets})); - partial_wl_x.mul_(net_weights_x.view({num_nets})); - partial_wl_y.mul_(net_weights.view({num_nets})); - } - // significant speedup is achieved by using summation in ATen - //auto wl = partial_wl.sum(); - auto wl = partial_wl_x.sum() + partial_wl_y.sum(); - return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) -/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) -/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then -/// \sum(exp(y/gamma)) -/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then -/// \sum(exp(-y/gamma)) -/// @param xyexp_xy_sum array of \sum(x*exp(x/gamma)) for each net and then -/// \sum(y*exp(y/gamma)) -/// @param xyexp_nxy_sum array of \sum(x*exp(-x/gamma)) for each net and then -/// \sum(y*exp(-y/gamma)) -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or -/// not -/// @param inv_gamma inverse of gamma, a scalar tensor for the parameter in the -/// equation -at::Tensor weighted_average_wirelength_atomic_backward( - at::Tensor grad_pos, at::Tensor pos, at::Tensor exp_xy, at::Tensor exp_nxy, - at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, at::Tensor xyexp_xy_sum, - at::Tensor xyexp_nxy_sum, at::Tensor pin2net_map, at::Tensor flat_netpin, - at::Tensor netpin_start, at::Tensor net_weights, - at::Tensor net_weights_x, at::Tensor net_mask, - at::Tensor inv_gamma, int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(exp_xy); - CHECK_EVEN(exp_xy); - CHECK_CONTIGUOUS(exp_xy); - CHECK_FLAT(exp_nxy); - CHECK_EVEN(exp_nxy); - CHECK_CONTIGUOUS(exp_nxy); - CHECK_FLAT(exp_xy_sum); - CHECK_EVEN(exp_xy_sum); - CHECK_CONTIGUOUS(exp_xy_sum); - CHECK_FLAT(exp_nxy_sum); - CHECK_EVEN(exp_nxy_sum); - CHECK_CONTIGUOUS(exp_nxy_sum); - CHECK_FLAT(xyexp_xy_sum); - CHECK_EVEN(xyexp_xy_sum); - CHECK_CONTIGUOUS(xyexp_xy_sum); - CHECK_FLAT(xyexp_nxy_sum); - CHECK_EVEN(xyexp_nxy_sum); - CHECK_CONTIGUOUS(xyexp_nxy_sum); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - at::Tensor grad_out = at::zeros_like(pos); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES( - pos, "computeWeightedAverageWirelengthAtomicLauncher", [&] { - computeWeightedAverageWirelengthAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), num_nets, - num_pins, DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), nullptr, nullptr, nullptr, nullptr, - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, - num_threads); - if (net_weights.numel()) - { - integrateNetWeightsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, - netpin_start.numel() - 1, - num_threads); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_forward, "WeightedAverageWirelength forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_backward, "WeightedAverageWirelength backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp deleted file mode 100644 index 70b2ad78..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/** - * @file hpwl_cuda.cpp - * @author Yibo Lin (DREAMPlace) - * @date Jun 2018 - * @brief Compute weighted-average wirelength and gradient according to e-place - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param x x location of pins. -/// @param y y location of pins. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_mask whether compute the wirelength for a net or not -/// @param net_weights weight of nets -/// @param num_nets number of nets. -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. -/// @param grad_tensor back-propagated gradient from previous stage. -/// @param grad_x_tensor gradient in x direction. -/// @param grad_y_tensor gradient in y direction. -/// @return 0 if successfully done. -template -int computeWeightedAverageWirelengthCudaLauncher( - const T *x, const T *y, - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - V *xy_max, V *xy_min, - T *partial_wl, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor); - -/// @brief add net weights to gradient -template -void integrateNetWeightsCudaLauncher( - const int *pin2net_map, - const unsigned char *net_mask, - const T *net_weights, - const T *net_weights_x, - T *grad_x_tensor, T *grad_y_tensor, - int num_pins); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -typedef int V; - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - at::Tensor exp_xy = at::empty_like(pos); - at::Tensor exp_nxy = at::empty_like(pos); - at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); - - // it is ok for xy_max and xy_min to be integer - // we do not really need accurate max/min, just some values to scale x/y - // therefore, there is no need to scale xy_max and xy_min to improve accuracy - at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); - at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaLauncher", [&] { - computeWeightedAverageWirelengthCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - nullptr, - nullptr, nullptr); - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({num_nets})); - } - }); - - auto wl = partial_wl.sum(); - return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma a scalar tensor for the parameter in the equation -at::Tensor weighted_average_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor exp_xy, at::Tensor exp_nxy, - at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, - at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - at::Tensor grad_out = at::zeros_like(pos); - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaLauncher", [&] { - computeWeightedAverageWirelengthCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - nullptr, nullptr, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins); - if (net_weights.numel()) - { - integrateNetWeightsCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t),//Use instead of net_weights_x - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, - num_pins); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp deleted file mode 100644 index 8030c254..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @file weighted_average_wirelength_cuda_atomic.cpp - * @author Yibo Lin (DREAMPlace) - * @date Aug 2018 - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeWeightedAverageWirelengthCudaAtomicLauncher( - const T *pos, - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - V *xy_max, V *xy_min, - T *partial_wl, // wirelength of each net - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor // the gradient is partial total wirelength to partial pin position -); - -/// @brief add net weights to gradient -template -void integrateNetWeightsCudaLauncher( - const int* pin2net_map, - const unsigned char* net_mask, - const T* net_weights, - const T* net_weights_x, - T* grad_x_tensor, T* grad_y_tensor, - int num_pins - ); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -typedef int V; - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma inverse of gamma coefficient in weighted average wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_atomic_forward( - at::Tensor pos, - at::Tensor pin2net_map, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - // log-sum-exp for x, log-sum-exp for -x, log-sum-exp for y, log-sum-exp for -y - at::Tensor exp_xy = at::empty_like(pos); - at::Tensor exp_nxy = at::empty_like(pos); - at::Tensor exp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor exp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_xy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor xyexp_nxy_sum = at::zeros({2, num_nets}, pos.options()); - at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); - - // it is ok for xy_max and xy_min to be integer - // we do not really need accurate max/min, just some values to scale x/y - // therefore, there is no need to scale xy_max and xy_min to improve accuracy - at::Tensor xy_max = at::full({2, num_nets}, std::numeric_limits::min(), at::CUDA(at::kInt)); - at::Tensor xy_min = at::full({2, num_nets}, std::numeric_limits::max(), at::CUDA(at::kInt)); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaAtomicLauncher", [&] { - computeWeightedAverageWirelengthCudaAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xy_max, V), DREAMPLACE_TENSOR_DATA_PTR(xy_min, V), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - nullptr, - nullptr, nullptr); - }); - - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({num_nets})); - } - // significant speedup is achieved by using summation in ATen - auto wl = partial_wl.sum(); - return {wl, exp_xy, exp_nxy, exp_xy_sum, exp_nxy_sum, xyexp_xy_sum, xyexp_nxy_sum}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param exp_xy array of exp(x/gamma) and then exp(y/gamma) -/// @param exp_nxy array of exp(-x/gamma) and then exp(-y/gamma) -/// @param exp_xy_sum array of \sum(exp(x/gamma)) for each net and then \sum(exp(y/gamma)) -/// @param exp_nxy_sum array of \sum(exp(-x/gamma)) for each net and then \sum(exp(-y/gamma)) -/// @param xyexp_xy_sum array of \sum(x*exp(x/gamma)) for each net and then \sum(y*exp(y/gamma)) -/// @param xyexp_nxy_sum array of \sum(x*exp(-x/gamma)) for each net and then \sum(y*exp(-y/gamma)) -/// @param pin2net_map map pin to net -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma inverse of gamma, a scalar tensor for the parameter in the equation -at::Tensor weighted_average_wirelength_atomic_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor exp_xy, at::Tensor exp_nxy, - at::Tensor exp_xy_sum, at::Tensor exp_nxy_sum, - at::Tensor xyexp_xy_sum, at::Tensor xyexp_nxy_sum, - at::Tensor pin2net_map, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(exp_xy); - CHECK_EVEN(exp_xy); - CHECK_CONTIGUOUS(exp_xy); - CHECK_FLAT(exp_nxy); - CHECK_EVEN(exp_nxy); - CHECK_CONTIGUOUS(exp_nxy); - CHECK_FLAT(exp_xy_sum); - CHECK_EVEN(exp_xy_sum); - CHECK_CONTIGUOUS(exp_xy_sum); - CHECK_FLAT(exp_nxy_sum); - CHECK_EVEN(exp_nxy_sum); - CHECK_CONTIGUOUS(exp_nxy_sum); - CHECK_FLAT(xyexp_xy_sum); - CHECK_EVEN(xyexp_xy_sum); - CHECK_CONTIGUOUS(xyexp_xy_sum); - CHECK_FLAT(xyexp_nxy_sum); - CHECK_EVEN(xyexp_nxy_sum); - CHECK_CONTIGUOUS(xyexp_nxy_sum); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - - at::Tensor grad_out = at::zeros_like(pos); - - int num_nets = net_mask.numel(); - int num_pins = pin2net_map.numel(); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaAtomicLauncher", [&] { - computeWeightedAverageWirelengthCudaAtomicLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), // x then y - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - num_pins, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(exp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(exp_nxy_sum, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(xyexp_xy_sum, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(xyexp_nxy_sum, scalar_t), - nullptr, nullptr, - nullptr, - DREAMPLACE_TENSOR_DATA_PTR(grad_pos, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins - ); - if (net_weights.numel()) - { - integrateNetWeightsCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), //Re-use for net_weights_x - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t)+num_pins, - num_pins - ); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_forward, "WeightedAverageWirelength forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_atomic_backward, "WeightedAverageWirelength backward (CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu deleted file mode 100644 index a17f07ae..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -#include "weighted_average_wirelength/src/functional_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeWeightedAverageWirelengthCudaAtomicLauncher( - const T *pos, // x then y - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - V *xy_max, V *xy_min, - T *partial_wl, // wirelength of each net - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor // the gradient is partial total wirelength to partial pin position -) -{ - int thread_count = 64; - int block_count_pins = (num_pins - 1 + thread_count) / thread_count; - int block_count_nets = (num_nets - 1 + thread_count) / thread_count; - dim3 block_size(thread_count, 2, 1); - - const T* x = pos; - const T* y = pos + num_pins; - - if (grad_tensor) - { - // computeWeightedAverageWirelengthGradInterleavePinByPin<<>>( - computeWeightedAverageWirelengthGradPinByPin<<>>( - x, y, - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - xyexp_xy_sum, xyexp_nxy_sum, - pin2net_map, - net_mask, - num_nets, - num_pins, - inv_gamma, - grad_tensor, - grad_x_tensor, grad_y_tensor); - } - else - { - #if 0 - // compute max and min in one kernel (pin by pin) - // computeMaxMinInterleavePinByPin<<>>( - computeMaxMinPinByPin<<>>( - x, y, - pin2net_map, - net_mask, - num_pins, - num_nets, - xy_max, - xy_min); - #else - // compute max and min in one kernel (net by net) - computeMaxMinInterleaveNetByNet<<>>( - // computeMaxMinNetByNet<<>>( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - xy_max, - xy_min); - #endif - - #if 1 - // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (pin by pin) - // corresponding to the plus and minus a b c kernels in the DREAMPlace paper - // computeABCKernelsInterleavePinByPin<<>>( - computeABCKernelsPinByPin<<>>( - // pos, - x, y, - pin2net_map, - net_mask, - num_nets, - num_pins, - inv_gamma, - xy_max, xy_min, - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - xyexp_xy_sum, xyexp_nxy_sum); - - // compute partial wirelength - computeXExpSumByExpSumXY<<>>( - xyexp_xy_sum, xyexp_nxy_sum, - exp_xy_sum, exp_nxy_sum, - pin2net_map, - net_mask, - num_nets, - partial_wl); - #else - // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (net by net) - // corresponding to the plus and minus a b c kernels in the DREAMPlace paper - // compute partial wirelength at the same time - // computeABCKernelsInterleaveAndWLNetByNet<<>>( - computeABCKernelsAndWLNetByNet<<>>( - pos, - flat_netpin, - netpin_start, - net_mask, - num_nets, - num_pins, - inv_gamma, - xy_max, xy_min, - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - xyexp_xy_sum, xyexp_nxy_sum, - partial_wl); - - #endif - - // Yibo: move out the summation to use ATen - // significant speedup is observed - //sumArray<<<1, 1>>>(partial_wl, num_nets, wl); - } - - return 0; -} - - -#define REGISTER_KERNEL_LAUNCHER(T, V) \ - template int computeWeightedAverageWirelengthCudaAtomicLauncher( \ - const T *pos, \ - const int *pin2net_map, \ - const int *flat_netpin, \ - const int *netpin_start, \ - const unsigned char *net_mask, \ - int num_nets, \ - int num_pins, \ - const T *inv_gamma, \ - T *exp_xy, T *exp_nxy, \ - T *exp_xy_sum, T *exp_nxy_sum, \ - T *xyexp_xy_sum, T *xyexp_nxy_sum, \ - V *xy_max, V *xy_min, \ - T* partial_wl, \ - const T *grad_tensor, \ - T *grad_x_tensor, T *grad_y_tensor); - -REGISTER_KERNEL_LAUNCHER(float, int); -REGISTER_KERNEL_LAUNCHER(double, int); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu deleted file mode 100644 index 3bd0822d..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" -#include "weighted_average_wirelength/src/functional_cuda.h" - -DREAMPLACE_BEGIN_NAMESPACE - -template -int computeWeightedAverageWirelengthCudaLauncher( - const T *x, const T *y, - const int *pin2net_map, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - int num_pins, - const T *inv_gamma, - T *exp_xy, T *exp_nxy, - T *exp_xy_sum, T *exp_nxy_sum, - T *xyexp_xy_sum, T *xyexp_nxy_sum, - V *xy_max, V *xy_min, - T *partial_wl, - const T *grad_tensor, - T *grad_x_tensor, T *grad_y_tensor) -{ - int thread_count = 64; - int block_count_pins = (num_pins - 1 + thread_count) / thread_count; - int block_count_nets = (num_nets - 1 + thread_count) / thread_count; - dim3 block_size(thread_count, 2, 1); - - if (grad_tensor) - { - // computeWeightedAverageWirelengthGradInterleaveNetByNet<<>>( - computeWeightedAverageWirelengthGradNetByNet<<>>( - x, y, - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - xyexp_xy_sum, xyexp_nxy_sum, - flat_netpin, - netpin_start, - net_mask, - num_nets, - num_pins, - inv_gamma, - grad_tensor, - grad_x_tensor, grad_y_tensor); - } - else - { - // compute max and min in one kernel (net by net) - // computeMaxMinInterleaveNetByNet<<>>( - computeMaxMinNetByNet<<>>( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - xy_max, - xy_min); - - // compute plus-minus exp, sum of plus-minus exp, sum of x*exp in one CUDA kernels (net by net) - // corresponding to the plus and minus a b c kernels in the DREAMPlace paper - // compute partial wirelength at the same time - computeABCKernelsInterleaveAndWLNetByNet<<>>( - // computeABCKernelsAndWLNetByNet<<>>( - x, - flat_netpin, - netpin_start, - net_mask, - num_nets, - num_pins, - inv_gamma, - xy_max, xy_min, - exp_xy, exp_nxy, - exp_xy_sum, exp_nxy_sum, - xyexp_xy_sum, xyexp_nxy_sum, - partial_wl); - } - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T, V) \ - template int computeWeightedAverageWirelengthCudaLauncher( \ - const T *x, const T *y, \ - const int *pin2net_map, \ - const int *flat_netpin, \ - const int *netpin_start, \ - const unsigned char *net_mask, \ - int num_nets, \ - int num_pins, \ - const T *inv_gamma, \ - T *exp_xy, T *exp_nxy, \ - T *exp_xy_sum, T *exp_nxy_sum, \ - T *xyexp_xy_sum, T *xyexp_nxy_sum, \ - V *xy_max, V *xy_min, \ - T *partial_wl, \ - const T *grad_tensor, \ - T *grad_x_tensor, T *grad_y_tensor); - -REGISTER_KERNEL_LAUNCHER(float, int); -REGISTER_KERNEL_LAUNCHER(double, int); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp deleted file mode 100644 index 09edca2c..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/** - * @file weighted_average_wirelength_cuda_merged.cpp - * @author Yibo Lin (DREAMPlace) - * @date Sep 2019 - * @brief Compute weighted-average wirelength and gradient according to e-place - */ -#include "utility/src/torch.h" -#include "utility/src/Msg.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param x x location of pins. -/// @param y y location of pins. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_mask whether compute the wirelength for a net or not -/// @param net_weights weight of nets -/// @param num_nets number of nets. -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. -/// @param grad_tensor back-propagated gradient from previous stage. -/// @param grad_x_tensor gradient in x direction. -/// @param grad_y_tensor gradient in y direction. -/// @return 0 if successfully done. -template -int computeWeightedAverageWirelengthCudaMergedLauncher( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* inv_gamma, - //T* partial_wl, - T* partial_wl_x, - T* partial_wl_y, - T* grad_intermediate_x, T* grad_intermediate_y - ); - - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param x x location of pins. -/// @param y y location of pins. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_mask whether compute the wirelength for a net or not -/// @param net_weights weight of nets -/// @param num_nets number of nets. -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. -/// @param grad_tensor back-propagated gradient from previous stage. -/// @param grad_x_tensor gradient in x direction. -/// @param grad_y_tensor gradient in y direction. -/// @return 0 if successfully done. -template -int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* inv_gamma, - const T* bbox_min_x, const T* bbox_min_y, - const T* bbox_max_x, const T* bbox_max_y, - T* partial_wl, - T* grad_intermediate_x, T* grad_intermediate_y - ); - -/// @brief add net weights to gradient -template -void integrateNetWeightsCudaLauncher( - const int *pin2net_map, - const unsigned char *net_mask, - const T *net_weights, - const T *net_weights_x, - T *grad_x_tensor, T *grad_y_tensor, - int num_pins); - -#define CHECK_FLAT(x) AT_ASSERTM(x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on GPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_weights_x); - CHECK_CONTIGUOUS(net_weights_x); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - // x, y interleave - //at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); - at::Tensor partial_wl_x = at::zeros({num_nets, 2}, pos.options()); - at::Tensor partial_wl_y = at::zeros({num_nets, 2}, pos.options()); - // timed with grad_in yet - at::Tensor grad_intermediate = at::zeros_like(pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncher", [&] { - computeWeightedAverageWirelengthCudaMergedLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins - ); - if (net_weights.numel()) - { - //partial_wl.mul_(net_weights.view({num_nets, 1})); - partial_wl_x.mul_(net_weights_x.view({num_nets, 1})); - partial_wl_y.mul_(net_weights.view({num_nets, 1})); - } - }); - - //auto wl = partial_wl.sum(); - auto wl = partial_wl_x.sum() + partial_wl_y.sum(); - //at::Tensor wl = at::zeros(1, pos.options()); - return {wl, grad_intermediate}; -} - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_forward_fpga( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_mask, - at::Tensor inv_gamma, - at::Tensor net_bounding_box_min, - at::Tensor net_bounding_box_max) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(net_bounding_box_min); - CHECK_EVEN(net_bounding_box_min); - CHECK_CONTIGUOUS(net_bounding_box_min); - CHECK_FLAT(net_bounding_box_max); - CHECK_EVEN(net_bounding_box_max); - CHECK_CONTIGUOUS(net_bounding_box_max); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - // x, y interleave - at::Tensor partial_wl = at::zeros({num_nets, 2}, pos.options()); - // timed with grad_in yet - at::Tensor grad_intermediate = at::zeros_like(pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncherFPGA", [&] { - computeWeightedAverageWirelengthCudaMergedLauncherFPGA( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_min, scalar_t) + num_nets, - DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(net_bounding_box_max, scalar_t) + num_nets, - DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins - ); - if (net_weights.numel()) - { - partial_wl.mul_(net_weights.view({num_nets, 1})); - } - }); - - auto wl = partial_wl.sum(); - //at::Tensor wl = at::zeros(1, pos.options()); - return {wl, grad_intermediate}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma a scalar tensor for the parameter in the equation -at::Tensor weighted_average_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor grad_intermediate, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - at::Tensor inv_gamma) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_weights_x); - CHECK_CONTIGUOUS(net_weights_x); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(grad_intermediate); - CHECK_EVEN(grad_intermediate); - CHECK_CONTIGUOUS(grad_intermediate); - - at::Tensor grad_out = grad_intermediate.mul_(grad_pos); - //int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthCudaMergedLauncher", [&] { - if (net_weights.numel()) - { - integrateNetWeightsCudaLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pin2net_map, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + num_pins, - num_pins); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward (CUDA)"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward (CUDA)"); - m.def("forward_fpga", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward_fpga, "WeightedAverageWirelength forward reuse net bbox(CUDA)"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu deleted file mode 100644 index 1d948fe4..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu +++ /dev/null @@ -1,264 +0,0 @@ -/** - * @file weighted_average_wirelength_cuda_merged_kernel.cu - * @author Yibo Lin (DREAMPlace) - * @date Sep 2019 - */ - -#include -#include -#include "assert.h" -#include "cuda_runtime.h" -#include "utility/src/utils.cuh" - -DREAMPLACE_BEGIN_NAMESPACE - -template -__global__ void computeWeightedAverageWirelength( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* inv_gamma, - //T* partial_wl, - T* partial_wl_x, - T* partial_wl_y, - T* grad_intermediate_x, T* grad_intermediate_y) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - int ii = i >> 1; - if (ii < num_nets && net_mask[ii]) - { - const T *values; - T *grads; - T *partial_wl; - if (i & 1) - { - values = y; - grads = grad_intermediate_y; - partial_wl = partial_wl_y; - } - else - { - values = x; - grads = grad_intermediate_x; - partial_wl = partial_wl_x; - } - - // int degree = netpin_start[ii+1]-netpin_start[ii]; - T x_max = -FLT_MAX; - T x_min = FLT_MAX; - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - x_max = max(xx, x_max); - x_min = min(xx, x_min); - } - - T xexp_x_sum = 0; - T xexp_nx_sum = 0; - T exp_x_sum = 0; - T exp_nx_sum = 0; - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - xexp_x_sum += xx * exp_x; - xexp_nx_sum += xx * exp_nx; - exp_x_sum += exp_x; - exp_nx_sum += exp_nx; - } - - partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; - - T b_x = (*inv_gamma) / (exp_x_sum); - T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; - T b_nx = -(*inv_gamma) / (exp_nx_sum); - T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; - - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - grads[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; - } - } -} - -template -__global__ void computeWeightedAverageWirelengthFPGA( - const T* x, const T* y, - const int* flat_netpin, - const int* netpin_start, - const unsigned char* net_mask, - int num_nets, - const T* inv_gamma, - const T* bbox_min_x, const T* bbox_min_y, - const T* bbox_max_x, const T* bbox_max_y, - T* partial_wl, - T* grad_intermediate_x, T* grad_intermediate_y) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - int ii = i >> 1; - if (ii < num_nets && net_mask[ii]) - { - const T *values; - const T *bbox_min; - const T *bbox_max; - T *grads; - if (i & 1) - { - values = y; - grads = grad_intermediate_y; - bbox_min = bbox_min_y; - bbox_max = bbox_max_y; - } - else - { - values = x; - grads = grad_intermediate_x; - bbox_min = bbox_min_x; - bbox_max = bbox_max_x; - } - - //// int degree = netpin_start[ii+1]-netpin_start[ii]; - //T x_max = -FLT_MAX; - //T x_min = FLT_MAX; - //for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - //{ - // T xx = values[flat_netpin[j]]; - // x_max = max(xx, x_max); - // x_min = min(xx, x_min); - //} - - T xexp_x_sum = 0; - T xexp_nx_sum = 0; - T exp_x_sum = 0; - T exp_nx_sum = 0; - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - //T exp_x = exp((xx - x_max) * (*inv_gamma)); - //T exp_nx = exp((x_min - xx) * (*inv_gamma)); - T exp_x = exp((xx - bbox_max[ii]) * (*inv_gamma)); - T exp_nx = exp((bbox_min[ii] - xx) * (*inv_gamma)); - - xexp_x_sum += xx * exp_x; - xexp_nx_sum += xx * exp_nx; - exp_x_sum += exp_x; - exp_nx_sum += exp_nx; - } - - // partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; - - T b_x = (*inv_gamma) / (exp_x_sum); - T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; - T b_nx = -(*inv_gamma) / (exp_nx_sum); - T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; - - for (int j = netpin_start[ii]; j < netpin_start[ii + 1]; ++j) - { - T xx = values[flat_netpin[j]]; - //T exp_x = exp((xx - x_max) * (*inv_gamma)); - //T exp_nx = exp((x_min - xx) * (*inv_gamma)); - T exp_x = exp((xx - bbox_max[ii]) * (*inv_gamma)); - T exp_nx = exp((bbox_min[ii] - xx) * (*inv_gamma)); - - grads[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; - } - } -} - -template -int computeWeightedAverageWirelengthCudaMergedLauncher( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - const T *inv_gamma, - //T *partial_wl, - T *partial_wl_x, - T *partial_wl_y, - T *grad_intermediate_x, T *grad_intermediate_y) -{ - int thread_count = 64; - int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y - - computeWeightedAverageWirelength<<>>( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - inv_gamma, - //partial_wl, - partial_wl_x, - partial_wl_y, - grad_intermediate_x, grad_intermediate_y); - - return 0; -} - -template -int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - const T *inv_gamma, - const T* bbox_min_x, const T* bbox_min_y, - const T* bbox_max_x, const T* bbox_max_y, - T *partial_wl, - T *grad_intermediate_x, T *grad_intermediate_y) -{ - int thread_count = 64; - int block_count = (num_nets * 2 + thread_count - 1) / thread_count; // separate x and y - - computeWeightedAverageWirelengthFPGA<<>>( - x, y, - flat_netpin, - netpin_start, - net_mask, - num_nets, - inv_gamma, - bbox_min_x, bbox_min_y, - bbox_max_x, bbox_max_y, - partial_wl, - grad_intermediate_x, grad_intermediate_y); - - return 0; -} - -#define REGISTER_KERNEL_LAUNCHER(T) \ - template int computeWeightedAverageWirelengthCudaMergedLauncher( \ - const T *x, const T *y, \ - const int *flat_netpin, \ - const int *netpin_start, \ - const unsigned char *net_mask, \ - int num_nets, \ - const T *inv_gamma, \ - T *partial_wl_x, T *partial_wl_y, \ - T *grad_intermediate_x, T *grad_intermediate_y); \ - \ - template int computeWeightedAverageWirelengthCudaMergedLauncherFPGA( \ - const T *x, const T *y, \ - const int *flat_netpin, \ - const int *netpin_start, \ - const unsigned char *net_mask, \ - int num_nets, \ - const T *inv_gamma, \ - const T *bbox_min_x, const T *bbox_min_y, \ - const T *bbox_max_x, const T *bbox_max_y, \ - T *partial_wl, \ - T *grad_intermediate_x, T *grad_intermediate_y); - -REGISTER_KERNEL_LAUNCHER(float); -REGISTER_KERNEL_LAUNCHER(double); - -DREAMPLACE_END_NAMESPACE diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp deleted file mode 100644 index bfee207f..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp +++ /dev/null @@ -1,269 +0,0 @@ -#include "utility/src/torch.h" -#include "utility/src/utils.h" -#include "weighted_average_wirelength/src/functional.h" - -DREAMPLACE_BEGIN_NAMESPACE - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param x x location of pins. -/// @param y y location of pins. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_mask whether compute the wirelength for a net or not -/// @param net_weights weight of nets -/// @param num_nets number of nets. -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @param partial_wl wirelength in x and y directions of each net. The first half is the wirelength in x direction, and the second half is the wirelength in y direction. -/// @param grad_tensor back-propagated gradient from previous stage. -/// @param grad_x_tensor gradient in x direction. -/// @param grad_y_tensor gradient in y direction. -/// @return 0 if successfully done. -template -int computeWeightedAverageWirelengthMergedLauncher( - const T *x, const T *y, - const int *flat_netpin, - const int *netpin_start, - const unsigned char *net_mask, - int num_nets, - const T *inv_gamma, - //T *partial_wl, - T *partial_wl_x, - T *partial_wl_y, - T *grad_intermediate_x, T *grad_intermediate_y, - int num_threads) -{ - int chunk_size = DREAMPLACE_STD_NAMESPACE::max(int(num_nets / num_threads / 16), 1); -#pragma omp parallel for num_threads(num_threads) schedule(dynamic, chunk_size) - for (int i = 0; i < num_nets; ++i) - { - if (net_mask[i]) - { - // int degree = netpin_start[ii+1]-netpin_start[ii]; - T x_max = -std::numeric_limits::max(); - T x_min = std::numeric_limits::max(); - T y_max = -std::numeric_limits::max(); - T y_min = std::numeric_limits::max(); - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - x_max = DREAMPLACE_STD_NAMESPACE::max(xx, x_max); - x_min = DREAMPLACE_STD_NAMESPACE::min(xx, x_min); - T yy = y[flat_netpin[j]]; - y_max = DREAMPLACE_STD_NAMESPACE::max(yy, y_max); - y_min = DREAMPLACE_STD_NAMESPACE::min(yy, y_min); - } - - T xexp_x_sum = 0; - T xexp_nx_sum = 0; - T exp_x_sum = 0; - T exp_nx_sum = 0; - - T yexp_y_sum = 0; - T yexp_ny_sum = 0; - T exp_y_sum = 0; - T exp_ny_sum = 0; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - xexp_x_sum += xx * exp_x; - xexp_nx_sum += xx * exp_nx; - exp_x_sum += exp_x; - exp_nx_sum += exp_nx; - - T yy = y[flat_netpin[j]]; - T exp_y = exp((yy - y_max) * (*inv_gamma)); - T exp_ny = exp((y_min - yy) * (*inv_gamma)); - - yexp_y_sum += yy * exp_y; - yexp_ny_sum += yy * exp_ny; - exp_y_sum += exp_y; - exp_ny_sum += exp_ny; - } - - //partial_wl[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum + - // yexp_y_sum / exp_y_sum - yexp_ny_sum / exp_ny_sum; - partial_wl_x[i] = xexp_x_sum / exp_x_sum - xexp_nx_sum / exp_nx_sum; - partial_wl_y[i] = yexp_y_sum / exp_y_sum - yexp_ny_sum / exp_ny_sum; - - T b_x = (*inv_gamma) / (exp_x_sum); - T a_x = (1.0 - b_x * xexp_x_sum) / exp_x_sum; - T b_nx = -(*inv_gamma) / (exp_nx_sum); - T a_nx = (1.0 - b_nx * xexp_nx_sum) / exp_nx_sum; - - T b_y = (*inv_gamma) / (exp_y_sum); - T a_y = (1.0 - b_y * yexp_y_sum) / exp_y_sum; - T b_ny = -(*inv_gamma) / (exp_ny_sum); - T a_ny = (1.0 - b_ny * yexp_ny_sum) / exp_ny_sum; - - for (int j = netpin_start[i]; j < netpin_start[i + 1]; ++j) - { - T xx = x[flat_netpin[j]]; - T exp_x = exp((xx - x_max) * (*inv_gamma)); - T exp_nx = exp((x_min - xx) * (*inv_gamma)); - - grad_intermediate_x[flat_netpin[j]] = (a_x + b_x * xx) * exp_x - (a_nx + b_nx * xx) * exp_nx; - - T yy = y[flat_netpin[j]]; - T exp_y = exp((yy - y_max) * (*inv_gamma)); - T exp_ny = exp((y_min - yy) * (*inv_gamma)); - - grad_intermediate_y[flat_netpin[j]] = (a_y + b_y * yy) * exp_y - (a_ny + b_ny * yy) * exp_ny; - } - } - } - - return 0; -} - -#define CHECK_FLAT(x) AT_ASSERTM(!x.is_cuda() && x.ndimension() == 1, #x "must be a flat tensor on CPU") -#define CHECK_EVEN(x) AT_ASSERTM((x.numel() & 1) == 0, #x "must have even number of elements") -#define CHECK_CONTIGUOUS(x) AT_ASSERTM(x.is_contiguous(), #x "must be contiguous") - -/// @brief Compute weighted average wirelength and gradient. -/// WL = \sum_i x_i*exp(x_i/gamma) / \sum_i exp(x_i/gamma) - \sum_i x_i*exp(-x_i/gamma) / \sum_i x_i*exp(-x_i/gamma), -/// where x_i is pin location. -/// -/// @param pos location of pins, x array followed by y array. -/// @param flat_netpin consists pins of each net, pins belonging to the same net are abutting to each other. -/// @param netpin_start bookmark for the starting index of each net in flat_netpin. The length is number of nets. The last entry equals to the number of pins. -/// @param net_weights weight of nets -/// @param net_mask whether compute the wirelength for a net or not -/// @param inv_gamma the inverse number of gamma coefficient in weighted average wirelength. -/// @return total wirelength cost. -std::vector weighted_average_wirelength_forward( - at::Tensor pos, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - at::Tensor inv_gamma, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_weights_x); - CHECK_CONTIGUOUS(net_weights_x); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - - int num_nets = netpin_start.numel() - 1; - int num_pins = pos.numel() / 2; - - // x, y interleave - //at::Tensor partial_wl = at::zeros({num_nets}, pos.options()); - at::Tensor partial_wl_x = at::zeros({num_nets}, pos.options()); - at::Tensor partial_wl_y = at::zeros({num_nets}, pos.options()); - // timed with grad_in yet - at::Tensor grad_intermediate = at::zeros_like(pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthMergedLauncher", [&] { - computeWeightedAverageWirelengthMergedLauncher( - DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(pos, scalar_t) + num_pins, - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - num_nets, - DREAMPLACE_TENSOR_DATA_PTR(inv_gamma, scalar_t), - //DREAMPLACE_TENSOR_DATA_PTR(partial_wl, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(partial_wl_y, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_intermediate, scalar_t) + num_pins, - num_threads); - if (net_weights.numel()) - { - //partial_wl.mul_(net_weights); - partial_wl_x.mul_(net_weights_x); - partial_wl_y.mul_(net_weights); - } - }); - - //auto wl = partial_wl.sum(); - auto wl = partial_wl_x.sum() + partial_wl_y.sum(); - //at::Tensor wl = at::zeros(1, pos.options()); - return {wl, grad_intermediate}; -} - -/// @brief Compute gradient -/// @param grad_pos input gradient from backward propagation -/// @param pos locations of pins -/// @param flat_netpin similar to the JA array in CSR format, which is flattened from the net2pin map (array of array) -/// @param netpin_start similar to the IA array in CSR format, IA[i+1]-IA[i] is the number of pins in each net, the length of IA is number of nets + 1 -/// @param net_weights weight of nets -/// @param net_mask an array to record whether compute the where for a net or not -/// @param inv_gamma a scalar tensor for the parameter in the equation -at::Tensor weighted_average_wirelength_backward( - at::Tensor grad_pos, - at::Tensor pos, - at::Tensor grad_intermediate, - at::Tensor flat_netpin, - at::Tensor netpin_start, - at::Tensor pin2net_map, - at::Tensor net_weights, - at::Tensor net_weights_x, - at::Tensor net_mask, - at::Tensor inv_gamma, - int num_threads) -{ - CHECK_FLAT(pos); - CHECK_EVEN(pos); - CHECK_CONTIGUOUS(pos); - CHECK_FLAT(flat_netpin); - CHECK_CONTIGUOUS(flat_netpin); - CHECK_FLAT(netpin_start); - CHECK_CONTIGUOUS(netpin_start); - CHECK_FLAT(net_weights); - CHECK_CONTIGUOUS(net_weights); - CHECK_FLAT(net_weights_x); - CHECK_CONTIGUOUS(net_weights_x); - CHECK_FLAT(net_mask); - CHECK_CONTIGUOUS(net_mask); - CHECK_FLAT(pin2net_map); - CHECK_CONTIGUOUS(pin2net_map); - CHECK_FLAT(grad_intermediate); - CHECK_EVEN(grad_intermediate); - CHECK_CONTIGUOUS(grad_intermediate); - - at::Tensor grad_out = grad_intermediate.mul_(grad_pos); - - DREAMPLACE_DISPATCH_FLOATING_TYPES(pos, "computeWeightedAverageWirelengthMergedLauncher", [&] { - if (net_weights.numel()) - { - integrateNetWeightsLauncher( - DREAMPLACE_TENSOR_DATA_PTR(flat_netpin, int), - DREAMPLACE_TENSOR_DATA_PTR(netpin_start, int), - DREAMPLACE_TENSOR_DATA_PTR(net_mask, unsigned char), - DREAMPLACE_TENSOR_DATA_PTR(net_weights, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(net_weights_x, scalar_t), - DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t), DREAMPLACE_TENSOR_DATA_PTR(grad_out, scalar_t) + pos.numel() / 2, - netpin_start.numel() - 1, - num_threads); - } - }); - return grad_out; -} - -DREAMPLACE_END_NAMESPACE - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) -{ - m.def("forward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_forward, "WeightedAverageWirelength forward"); - m.def("backward", &DREAMPLACE_NAMESPACE::weighted_average_wirelength_backward, "WeightedAverageWirelength backward"); -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py deleted file mode 100644 index 5fdaa133..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py +++ /dev/null @@ -1,352 +0,0 @@ -## -# @file weighted_average_wirelength.py -# @author Yibo Lin (DREAMPlace) -# @date Jun 2018 -# @brief Compute weighted-average wirelength according to e-place -# - -import time -import torch -from torch import nn -from torch.autograd import Function -import logging - -import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp as weighted_average_wirelength_cpp -import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp_atomic as weighted_average_wirelength_cpp_atomic -import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cpp_merged as weighted_average_wirelength_cpp_merged -import dreamplacefpga.configure as configure -if configure.compile_configurations["CUDA_FOUND"] == "TRUE": - import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda as weighted_average_wirelength_cuda - import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda_atomic as weighted_average_wirelength_cuda_atomic - import dreamplacefpga.ops.weighted_average_wirelength.weighted_average_wirelength_cuda_merged as weighted_average_wirelength_cuda_merged -import pdb - -logger = logging.getLogger(__name__) - -class WeightedAverageWirelengthFunction(Function): - """ - @brief compute weighted average wirelength. - """ - @staticmethod - def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, num_threads): - """ - @param pos pin location (x array, y array), not cell location - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore - @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute - @param inv_gamma 1/gamma, the larger, the closer to HPWL - """ - tt = time.time() - if pos.is_cuda: - output = weighted_average_wirelength_cuda.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, inv_gamma) - else: - output = weighted_average_wirelength_cpp.forward(pos.view(pos.numel()), flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) - ctx.num_threads = num_threads - ctx.flat_netpin = flat_netpin - ctx.netpin_start = netpin_start - ctx.pin2net_map = pin2net_map - ctx.net_weights = net_weights - ctx.net_weights_x = net_weights_x - ctx.net_mask = net_mask - ctx.pin_mask = pin_mask - ctx.inv_gamma = inv_gamma - ctx.pos = pos - ctx.exp_xy = output[1] - ctx.exp_nxy = output[2] - ctx.exp_xy_sum = output[3] - ctx.exp_nxy_sum = output[4] - ctx.xyexp_xy_sum = output[5] - ctx.xyexp_nxy_sum = output[6] - - if pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - tt = time.time() - if grad_pos.is_cuda: - output = weighted_average_wirelength_cuda.backward( - grad_pos, - ctx.pos, - ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), - ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), - ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_mask, - ctx.inv_gamma - ) - else: - output = weighted_average_wirelength_cpp.backward( - grad_pos, - ctx.pos, - ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), - ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), - ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_weights_x, - ctx.net_mask, - ctx.inv_gamma, - ctx.num_threads - ) - output[:output.numel()//2].masked_fill_(ctx.pin_mask, 0.0) - output[output.numel()//2:].masked_fill_(ctx.pin_mask, 0.0) - if grad_pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) - return output, None, None, None, None, None, None, None, None - -class WeightedAverageWirelengthAtomicFunction(Function): - """ - @brief compute weighted average wirelength. - """ - @staticmethod - def forward(ctx, pos, pin2net_map, flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, num_threads): - """ - @param pos pin location (x array, y array), not cell location - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength - @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute - @param inv_gamma 1/gamma, the larger, the closer to HPWL - """ - tt = time.time() - if pos.is_cuda: - output = weighted_average_wirelength_cuda_atomic.forward(pos.view(pos.numel()), pin2net_map, flat_netpin, netpin_start, net_weights, net_mask, inv_gamma) - else: - output = weighted_average_wirelength_cpp_atomic.forward(pos.view(pos.numel()), pin2net_map, flat_netpin, netpin_start, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) - ctx.num_threads = num_threads - ctx.pin2net_map = pin2net_map - ctx.flat_netpin = flat_netpin - ctx.netpin_start = netpin_start - ctx.net_weights = net_weights - ctx.net_weights_x = net_weights_x - ctx.net_mask = net_mask - ctx.pin_mask = pin_mask - ctx.inv_gamma = inv_gamma - ctx.exp_xy = output[1] - ctx.exp_nxy = output[2] - ctx.exp_xy_sum = output[3] - ctx.exp_nxy_sum = output[4] - ctx.xyexp_xy_sum = output[5] - ctx.xyexp_nxy_sum = output[6] - ctx.pos = pos - #if torch.isnan(ctx.exp_xy).any() or torch.isnan(ctx.exp_nxy).any() or torch.isnan(ctx.exp_xy_sum).any() or torch.isnan(ctx.exp_nxy_sum).any() or torch.isnan(output[0]).any(): - # pdb.set_trace() - if pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - tt = time.time() - if grad_pos.is_cuda: - output = weighted_average_wirelength_cuda_atomic.backward( - grad_pos, - ctx.pos, - ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), - ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), - ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), - ctx.pin2net_map, - ctx.flat_netpin, - ctx.netpin_start, - ctx.net_weights, - ctx.net_mask, - ctx.inv_gamma - ) - else: - output = weighted_average_wirelength_cpp_atomic.backward( - grad_pos, - ctx.pos, - ctx.exp_xy.view([-1]), ctx.exp_nxy.view([-1]), - ctx.exp_xy_sum.view([-1]), ctx.exp_nxy_sum.view([-1]), - ctx.xyexp_xy_sum.view([-1]), ctx.xyexp_nxy_sum.view([-1]), - ctx.pin2net_map, - ctx.flat_netpin, - ctx.netpin_start, - ctx.net_weights, - ctx.net_weights_x, - ctx.net_mask, - ctx.inv_gamma, - ctx.num_threads - ) - output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) - output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) - if grad_pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) - return output, None, None, None, None, None, None - -class WeightedAverageWirelengthMergedFunction(Function): - """ - @brief compute weighted average wirelength. - """ - @staticmethod - def forward(ctx, pos, flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, pin_mask, inv_gamma, net_bounding_box_min, net_bounding_box_max, num_threads): - """ - @param pos pin location (x array, y array), not cell location - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength - @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute - @param inv_gamma 1/gamma, the larger, the closer to HPWL - """ - tt = time.time() - if pos.is_cuda: - output = weighted_average_wirelength_cuda_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, inv_gamma) - #output = weighted_average_wirelength_cuda_merged.forward_fpga(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_mask, inv_gamma, net_bounding_box_min, net_bounding_box_max) - else: - output = weighted_average_wirelength_cpp_merged.forward(pos.view(pos.numel()), flat_netpin, netpin_start, pin2net_map, net_weights, net_weights_x, net_mask, inv_gamma, num_threads) - ctx.num_threads = num_threads - ctx.pin2net_map = pin2net_map - ctx.flat_netpin = flat_netpin - ctx.netpin_start = netpin_start - ctx.net_weights = net_weights - ctx.net_weights_x = net_weights_x - ctx.net_mask = net_mask - ctx.pin_mask = pin_mask - ctx.inv_gamma = inv_gamma - ctx.grad_intermediate = output[1] - ctx.pos = pos - if pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength forward %.3f ms" % ((time.time()-tt)*1000)) - return output[0] - - @staticmethod - def backward(ctx, grad_pos): - tt = time.time() - if grad_pos.is_cuda: - output = weighted_average_wirelength_cuda_merged.backward( - grad_pos, - ctx.pos, - ctx.grad_intermediate, - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_weights_x, - ctx.net_mask, - ctx.inv_gamma - ) - else: - output = weighted_average_wirelength_cpp_merged.backward( - grad_pos, - ctx.pos, - ctx.grad_intermediate, - ctx.flat_netpin, - ctx.netpin_start, - ctx.pin2net_map, - ctx.net_weights, - ctx.net_weights_x, - ctx.net_mask, - ctx.inv_gamma, - ctx.num_threads - ) - output[:int(output.numel()//2)].masked_fill_(ctx.pin_mask, 0.0) - output[int(output.numel()//2):].masked_fill_(ctx.pin_mask, 0.0) - if grad_pos.is_cuda: - torch.cuda.synchronize() - logger.debug("wirelength backward %.3f ms" % ((time.time()-tt)*1000)) - return output, None, None, None, None, None, None, None, None, None, None, None - -class WeightedAverageWirelength(nn.Module): - """ - @brief Compute weighted average wirelength. - CPU only supports net-by-net algorithm. - GPU supports three algorithms: net-by-net, atomic, merged. - Different parameters are required for different algorithms. - """ - def __init__(self, flat_netpin=None, netpin_start=None, pin2net_map=None, net_weights=None, num_carry_chains=None, cc_net_weight=None, dir_net_weight=None, net_mask=None, pin_mask=None, gamma=None, net_bounding_box_min=None, net_bounding_box_max=None, num_threads=None, algorithm='atomic'): - """ - @brief initialization - @param flat_netpin flat netpin map, length of #pins - @param netpin_start starting index in netpin map for each net, length of #nets+1, the last entry is #pins - @param pin2net_map pin2net map - @param net_weights weight of nets - @param net_mask whether to compute wirelength, 1 means to compute, 0 means to ignore - @param pin_mask whether compute gradient for a pin, 1 means to fill with zero, 0 means to compute - @param gamma the smaller, the closer to HPWL - @param algorithm must be net-by-net | atomic | merged - """ - super(WeightedAverageWirelength, self).__init__() - assert net_weights is not None \ - and net_mask is not None \ - and pin_mask is not None \ - and gamma is not None, "net_weights, net_mask, pin_mask, gamma are requried parameters" - if algorithm in ['net-by-net', 'merged']: - assert flat_netpin is not None and netpin_start is not None and pin2net_map is not None, "flat_netpin, netpin_start, pin2net_map are requried parameters for algorithm %s" % (algorithm) - elif algorithm == 'atomic': - assert pin2net_map is not None, "pin2net_map is required for algorithm atomic" - - self.flat_netpin = flat_netpin - self.netpin_start = netpin_start - self.netpin_values = None - self.pin2net_map = pin2net_map - self.net_weights = net_weights - self.net_weights_x = net_weights.detach().clone() - self.num_carry_chains = num_carry_chains - self.cc_net_weight = cc_net_weight - self.dir_net_weight = dir_net_weight - self.net_mask = net_mask - self.pin_mask = pin_mask - self.gamma = gamma - self.net_bounding_box_min = net_bounding_box_min - self.net_bounding_box_max = net_bounding_box_max - self.algorithm = algorithm - self.num_threads = num_threads - - #Weight x direction 50% more than y for carry chains - if self.num_carry_chains > 0 and self.cc_net_weight > 1.0 and self.dir_net_weight: - self.net_weights_x[self.net_weights_x == cc_net_weight] = 1.5*cc_net_weight - - def forward(self, pos): - if self.algorithm == 'net-by-net': - return WeightedAverageWirelengthFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.pin2net_map, - self.net_weights, - self.net_weights_x, - self.net_mask, - self.pin_mask, - 1.0/self.gamma, # do not store inv_gamma as gamma is changing - self.num_threads - ) - elif self.algorithm == 'atomic': - return WeightedAverageWirelengthAtomicFunction.apply(pos, - self.pin2net_map, - self.flat_netpin, - self.netpin_start, - self.net_weights, - self.net_weights_x, - self.net_mask, - self.pin_mask, - 1.0/self.gamma, # do not store inv_gamma as gamma is changing - self.num_threads - ) - elif self.algorithm == 'merged': - return WeightedAverageWirelengthMergedFunction.apply(pos, - self.flat_netpin, - self.netpin_start, - self.pin2net_map, - self.net_weights, - self.net_weights_x, - self.net_mask, - self.pin_mask, - 1.0/self.gamma, # do not store inv_gamma as gamma is changing - self.net_bounding_box_min, - self.net_bounding_box_max, - self.num_threads - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json deleted file mode 100644 index a26a03f9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json +++ /dev/null @@ -1,255 +0,0 @@ -{ -"scl_file" : { - "descripton" : "input .scl file", - "default" : "", - "required" : "required for Bookshelf" - }, -"instance_file" : { - "descripton" : "input .inst file", - "default" : "", - "required" : "required for Bookshelf" - }, -"pin_file" : { - "descripton" : "input .pin file", - "default" : "", - "required" : "required for Bookshelf" - }, -"net_file" : { - "descripton" : "input .net file", - "default" : "", - "required" : "required for Bookshelf" - }, -"routing_file" : { - "descripton" : "input .routingUtil file", - "default" : "", - "required" : "required for Bookshelf" - }, -"util_file" : { - "descripton" : "input .util file", - "default" : "", - "required" : "required for Bookshelf" - }, -"pickle_file" : { - "descripton" : "input .pklz file", - "default" : "", - "required" : "required for Bookshelf" - }, -"load_pickle" : { - "descripton" : "enable loading of pickle file", - "default" : 0 - }, -"aux_input" : { - "descripton" : "input .aux file", - "default" : "", - "required" : "required for Bookshelf" - }, -"gpu" : { - "descripton" : "enable gpu or not", - "default" : 1 - }, -"num_bins_x" : { - "descripton" : "number of bins in horizontal direction", - "default" : 512 - }, -"num_bins_y" : { - "descripton" : "number of bins in vertical direction", - "default" : 512 - }, -"global_place_stages" : { - "descripton" : "global placement configurations of each stage, a dictionary of {\"num_bins_x\", \"num_bins_y\", \"iteration\", \"learning_rate\", \"learning_rate_decay\", \"wirelength\", \"optimizer\", \"Llambda_density_weight_iteration\", \"Lsub_iteration\"}", - "default" : "", - "required" : "required" - }, -"target_density" : { - "descripton" : "target density", - "default" : 1.0 - }, -"density_weight" : { - "descripton" : "initial weight of density cost", - "default" : 8e-5 - }, -"random_seed" : { - "descripton" : "random seed", - "default" : 1000 - }, -"result_dir" : { - "descripton" : "result directory for output", - "default" : "results" - }, -"scale_factor" : { - "descripton" : "scale factor to avoid numerical overflow; 0.0 means not set", - "default" : 0.0 - }, -"ignore_net_degree" : { - "descripton" : "ignore net degree larger than some value", - "default" : 3000 - }, -"gp_noise_ratio" : { - "descripton" : "noise to initial positions for global placement", - "default" : 0.025 - }, -"enable_fillers" : { - "descripton" : "enable filler cells", - "default" : 1 - }, -"cc_net_weight" : { - "descripton" : "carry chain net weights", - "default" : 1.0 - }, -"dir_net_weight" : { - "descripton" : "directional carry chain net weights wt_x = 1.5*wt_y", - "default" : 0 - }, -"global_place_flag" : { - "descripton" : "whether to run global placement", - "default" : 1 - }, -"legalize_flag" : { - "descripton" : "whether to run packing-legalization", - "default" : 1 - }, -"stop_overflow" : { - "descripton" : "stopping criteria, consider stop when the overflow reaches to a ratio", - "default" : 0.1 - }, -"dtype" : { - "descripton" : "data type, float32 (or) float64", - "default" : "float32" - }, -"detailed_place_engine" : { - "descripton" : "external detailed placement engine to be called after placement", - "default" : "" - }, -"detailed_place_command" : { - "descripton" : "commands for external detailed placement engine", - "default" : "-nolegal -nodetail" - }, -"plot_flag" : { - "descripton" : "whether to plot solution or not (Increases runtime)", - "default" : 0 - }, -"RePlAce_ref_hpwl" : { - "descripton" : "reference HPWL used in RePlAce for updating density weight", - "default" : 350000 - }, -"RePlAce_LOWER_PCOF" : { - "descripton" : "lower bound ratio used in RePlAce for updating density weight", - "default" : 0.95 - }, -"RePlAce_UPPER_PCOF" : { - "descripton" : "upper bound ratio used in RePlAce for updating density weight", - "default" : 1.05 - }, -"gamma" : { - "descripton" : "elfPlace: The base wirelenGamma is * average bin size", - "default" : 5.0 - }, -"random_center_init_flag" : { - "descripton" : "whether perform random initialization around the center for global placement", - "default" : 1 - }, -"sort_nets_by_degree" : { - "descripton" : "whether sort nets by degree or not", - "default" : 0 - }, -"num_threads" : { - "descripton" : "number of CPU threads", - "default" : 8 - }, -"dump_global_place_solution_flag" : { - "descripton" : "whether dump intermediate global placement solution as a compressed pickle object", - "default" : 0 - }, -"dump_legalize_solution_flag" : { - "descripton" : "whether dump intermediate legalization solution as a compressed pickle object", - "default" : 0 - }, -"routability_opt_flag" : { - "descripton" : "whether enable routability optimization", - "default" : 0 - }, -"route_num_bins_x" : { - "descripton" : "number of routing grids/tiles", - "default" : 512 - }, -"route_num_bins_y" : { - "descripton" : "number of routing grids/tiles", - "default" : 512 - }, -"node_area_adjust_overflow" : { - "descripton" : "the overflow where to adjust node area", - "default" : 0.15 - }, -"max_num_area_adjust" : { - "descripton" : "maximum times to adjust node area", - "default" : 3 - }, -"adjust_resource_area_flag" : { - "descripton" : "Compute LUT/FF resource areas", - "default" : 1 - }, -"adjust_route_area_flag" : { - "descripton" : "whether use RUDY/RISA map to guide area adjustment", - "default" : 1 - }, -"adjust_pin_area_flag" : { - "descripton" : "whether use pin utilization map to guide area adjustment", - "default" : 1 - }, -"area_adjust_stop_ratio" : { - "descripton" : "area_adjust_stop_ratio", - "default" : 0.01 - }, -"route_area_adjust_stop_ratio" : { - "descripton" : "route_area_adjust_stop_ratio", - "default" : 0.01 - }, -"pin_area_adjust_stop_ratio" : { - "descripton" : "pin_area_adjust_stop_ratio", - "default" : 0.05 - }, -"unit_horizontal_capacity" : { - "descripton" : "number of horizontal routing tracks per unit distance for FPGA", - "default" : 209 - }, -"unit_vertical_capacity" : { - "descripton" : "number of vertical routing tracks per unit distance for FPGA", - "default" : 239 - }, -"unit_pin_capacity" : { - "descripton" : "number of pins per unit area for FPGA", - "default" : 50 - }, -"max_route_opt_adjust_rate" : { - "descripton" : "max_route_opt_adjust_rate", - "default" : 2.0 - }, -"route_opt_adjust_exponent" : { - "descripton" : "exponent to adjust the routing utilization map", - "default" : 2.0 - }, -"pin_stretch_ratio" : { - "descripton" : "pin_stretch_ratio", - "default" : 1.414213562 - }, -"max_pin_opt_adjust_rate" : { - "descripton" : "max_pin_opt_adjust_rate", - "default" : 1.5 - }, -"ffPinWeight" : { - "descripton" : "The weight of FF pins for pin density optimization", - "default" : 3.0 - }, -"deterministic_flag" : { - "descripton" : "Ensures reproducible run-to-run results (Slightly increases runtime)", - "default" : 1 - }, -"name_map_file_dump" : { - "descripton" : "Creates mapping to simplify long node and net names - Use for debug purposes", - "default" : 0 - }, -"enable_if" : { - "descripton" : "Enable IF Writer", - "default" : 0 - } -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json deleted file mode 100644 index a26a03f9..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json +++ /dev/null @@ -1,255 +0,0 @@ -{ -"scl_file" : { - "descripton" : "input .scl file", - "default" : "", - "required" : "required for Bookshelf" - }, -"instance_file" : { - "descripton" : "input .inst file", - "default" : "", - "required" : "required for Bookshelf" - }, -"pin_file" : { - "descripton" : "input .pin file", - "default" : "", - "required" : "required for Bookshelf" - }, -"net_file" : { - "descripton" : "input .net file", - "default" : "", - "required" : "required for Bookshelf" - }, -"routing_file" : { - "descripton" : "input .routingUtil file", - "default" : "", - "required" : "required for Bookshelf" - }, -"util_file" : { - "descripton" : "input .util file", - "default" : "", - "required" : "required for Bookshelf" - }, -"pickle_file" : { - "descripton" : "input .pklz file", - "default" : "", - "required" : "required for Bookshelf" - }, -"load_pickle" : { - "descripton" : "enable loading of pickle file", - "default" : 0 - }, -"aux_input" : { - "descripton" : "input .aux file", - "default" : "", - "required" : "required for Bookshelf" - }, -"gpu" : { - "descripton" : "enable gpu or not", - "default" : 1 - }, -"num_bins_x" : { - "descripton" : "number of bins in horizontal direction", - "default" : 512 - }, -"num_bins_y" : { - "descripton" : "number of bins in vertical direction", - "default" : 512 - }, -"global_place_stages" : { - "descripton" : "global placement configurations of each stage, a dictionary of {\"num_bins_x\", \"num_bins_y\", \"iteration\", \"learning_rate\", \"learning_rate_decay\", \"wirelength\", \"optimizer\", \"Llambda_density_weight_iteration\", \"Lsub_iteration\"}", - "default" : "", - "required" : "required" - }, -"target_density" : { - "descripton" : "target density", - "default" : 1.0 - }, -"density_weight" : { - "descripton" : "initial weight of density cost", - "default" : 8e-5 - }, -"random_seed" : { - "descripton" : "random seed", - "default" : 1000 - }, -"result_dir" : { - "descripton" : "result directory for output", - "default" : "results" - }, -"scale_factor" : { - "descripton" : "scale factor to avoid numerical overflow; 0.0 means not set", - "default" : 0.0 - }, -"ignore_net_degree" : { - "descripton" : "ignore net degree larger than some value", - "default" : 3000 - }, -"gp_noise_ratio" : { - "descripton" : "noise to initial positions for global placement", - "default" : 0.025 - }, -"enable_fillers" : { - "descripton" : "enable filler cells", - "default" : 1 - }, -"cc_net_weight" : { - "descripton" : "carry chain net weights", - "default" : 1.0 - }, -"dir_net_weight" : { - "descripton" : "directional carry chain net weights wt_x = 1.5*wt_y", - "default" : 0 - }, -"global_place_flag" : { - "descripton" : "whether to run global placement", - "default" : 1 - }, -"legalize_flag" : { - "descripton" : "whether to run packing-legalization", - "default" : 1 - }, -"stop_overflow" : { - "descripton" : "stopping criteria, consider stop when the overflow reaches to a ratio", - "default" : 0.1 - }, -"dtype" : { - "descripton" : "data type, float32 (or) float64", - "default" : "float32" - }, -"detailed_place_engine" : { - "descripton" : "external detailed placement engine to be called after placement", - "default" : "" - }, -"detailed_place_command" : { - "descripton" : "commands for external detailed placement engine", - "default" : "-nolegal -nodetail" - }, -"plot_flag" : { - "descripton" : "whether to plot solution or not (Increases runtime)", - "default" : 0 - }, -"RePlAce_ref_hpwl" : { - "descripton" : "reference HPWL used in RePlAce for updating density weight", - "default" : 350000 - }, -"RePlAce_LOWER_PCOF" : { - "descripton" : "lower bound ratio used in RePlAce for updating density weight", - "default" : 0.95 - }, -"RePlAce_UPPER_PCOF" : { - "descripton" : "upper bound ratio used in RePlAce for updating density weight", - "default" : 1.05 - }, -"gamma" : { - "descripton" : "elfPlace: The base wirelenGamma is * average bin size", - "default" : 5.0 - }, -"random_center_init_flag" : { - "descripton" : "whether perform random initialization around the center for global placement", - "default" : 1 - }, -"sort_nets_by_degree" : { - "descripton" : "whether sort nets by degree or not", - "default" : 0 - }, -"num_threads" : { - "descripton" : "number of CPU threads", - "default" : 8 - }, -"dump_global_place_solution_flag" : { - "descripton" : "whether dump intermediate global placement solution as a compressed pickle object", - "default" : 0 - }, -"dump_legalize_solution_flag" : { - "descripton" : "whether dump intermediate legalization solution as a compressed pickle object", - "default" : 0 - }, -"routability_opt_flag" : { - "descripton" : "whether enable routability optimization", - "default" : 0 - }, -"route_num_bins_x" : { - "descripton" : "number of routing grids/tiles", - "default" : 512 - }, -"route_num_bins_y" : { - "descripton" : "number of routing grids/tiles", - "default" : 512 - }, -"node_area_adjust_overflow" : { - "descripton" : "the overflow where to adjust node area", - "default" : 0.15 - }, -"max_num_area_adjust" : { - "descripton" : "maximum times to adjust node area", - "default" : 3 - }, -"adjust_resource_area_flag" : { - "descripton" : "Compute LUT/FF resource areas", - "default" : 1 - }, -"adjust_route_area_flag" : { - "descripton" : "whether use RUDY/RISA map to guide area adjustment", - "default" : 1 - }, -"adjust_pin_area_flag" : { - "descripton" : "whether use pin utilization map to guide area adjustment", - "default" : 1 - }, -"area_adjust_stop_ratio" : { - "descripton" : "area_adjust_stop_ratio", - "default" : 0.01 - }, -"route_area_adjust_stop_ratio" : { - "descripton" : "route_area_adjust_stop_ratio", - "default" : 0.01 - }, -"pin_area_adjust_stop_ratio" : { - "descripton" : "pin_area_adjust_stop_ratio", - "default" : 0.05 - }, -"unit_horizontal_capacity" : { - "descripton" : "number of horizontal routing tracks per unit distance for FPGA", - "default" : 209 - }, -"unit_vertical_capacity" : { - "descripton" : "number of vertical routing tracks per unit distance for FPGA", - "default" : 239 - }, -"unit_pin_capacity" : { - "descripton" : "number of pins per unit area for FPGA", - "default" : 50 - }, -"max_route_opt_adjust_rate" : { - "descripton" : "max_route_opt_adjust_rate", - "default" : 2.0 - }, -"route_opt_adjust_exponent" : { - "descripton" : "exponent to adjust the routing utilization map", - "default" : 2.0 - }, -"pin_stretch_ratio" : { - "descripton" : "pin_stretch_ratio", - "default" : 1.414213562 - }, -"max_pin_opt_adjust_rate" : { - "descripton" : "max_pin_opt_adjust_rate", - "default" : 1.5 - }, -"ffPinWeight" : { - "descripton" : "The weight of FF pins for pin density optimization", - "default" : 3.0 - }, -"deterministic_flag" : { - "descripton" : "Ensures reproducible run-to-run results (Slightly increases runtime)", - "default" : 1 - }, -"name_map_file_dump" : { - "descripton" : "Creates mapping to simplify long node and net names - Use for debug purposes", - "default" : 0 - }, -"enable_if" : { - "descripton" : "Enable IF Writer", - "default" : 0 - } -} diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt deleted file mode 100644 index 4aa41588..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -pyunpack>=0.1.2 -patool>=1.12 -matplotlib>=2.2.2 -cairocffi>=0.9.0 -pkgconfig>=1.4.0 -setuptools>=39.1.0 -scipy>=1.1.0 -numpy>=1.15.4 -torch>=1.6.0 -pycapnp diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt deleted file mode 100644 index 67e36ccc..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_subdirectory(ops) - -file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") -install(FILES ${INSTALL_SRCS} DESTINATION unittest) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore deleted file mode 100644 index 20837254..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.gds -*.png diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt deleted file mode 100644 index cf922d08..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB INSTALL_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") -install( - FILES ${INSTALL_SRCS} DESTINATION unitest/ops - ) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py deleted file mode 100644 index e6a39d55..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py +++ /dev/null @@ -1,194 +0,0 @@ -## -# @file adjust_node_area_unitest.py -# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import unittest -import torch -import numpy as np -import pdb - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.adjust_node_area import adjust_node_area -sys.path.pop() - -class AdjustNodeAreaUnittest(unittest.TestCase): - def test_adjust_node_area(self): - dtype = torch.float32 - pos = torch.Tensor([[1, 10], [2, 20], [3, 30]]).to(dtype) - pin_offset_x = torch.Tensor([0.1, 0.2, 0.3, 0.4, 0.5]).to(dtype) - pin_offset_y = torch.Tensor([0.01, 0.02, 0.03, 0.04, 0.05]).to(dtype) - node_size_x = torch.Tensor([0.5, 0.5, 0.5]).to(dtype) - node_size_y = torch.Tensor([0.05, 0.05, 0.05]).to(dtype) - - flop_lut_indices = torch.Tensor([0, 1, 2]).to(torch.int32) - flop_lut_mask = torch.Tensor([True, True, True]) - flop_mask = torch.Tensor([True, False, False]) - lut_mask = torch.Tensor([False, True, True]) - filler_start_map = torch.Tensor([0, 1, 2]).to(torch.int32) - resource_areas = torch.Tensor([0.1,0.1,0.1,0.1,0.1]).to(dtype) - - node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - num_movable_nodes = len(node2pin_map) - num_filler_nodes = 2 - # assume no terminals - - num_pins = 0 - for pins in node2pin_map: - num_pins += len(pins) - pin2node_map = np.zeros(num_pins, dtype=np.int32) - for node_id, pins in enumerate(node2pin_map): - for pin in pins: - pin2node_map[pin] = node_id - - # construct flat_node2pin_map and flat_node2pin_start_map - flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) - # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins - flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(node2pin_map)): - flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] - flat_node2pin_start_map[i] = count - count += len(node2pin_map[i]) - flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) - - flat_node2pin_start_map = torch.from_numpy(flat_node2pin_start_map) - flat_node2pin_map = torch.from_numpy(flat_node2pin_map) - - xl, xh = 0, 8 - yl, yh = 0, 64 - route_num_bins_x, route_num_bins_y = 8, 8 - pin_num_bins_x, pin_num_bins_y = 16, 16 - - total_place_area = (xh - xl) * (yh - yl) - total_whitespace_area = (total_place_area - (node_size_x[:num_movable_nodes] * node_size_y[:num_movable_nodes]).sum()).item() - - route_utilization_map = torch.ones([route_num_bins_x, route_num_bins_y]).uniform_(0.5, 2) - pin_utilization_map = torch.ones([pin_num_bins_x, pin_num_bins_y]).uniform_(0.5, 2) - - area_adjust_stop_ratio = 0.01 - route_area_adjust_stop_ratio = 0.01 - pin_area_adjust_stop_ratio = 0.05 - unit_pin_capacity = 0.5 - pin_weights = None - - max_route_opt_adjust_rate = 3.0 - max_pin_opt_adjust_rate = 2.5 - route_opt_adjust_exponent = 2.5 - - target_density = torch.Tensor([0.9]) - - # test cpu - adjust_node_area_op = adjust_node_area.AdjustNodeArea( - flat_node2pin_map=flat_node2pin_map, - flat_node2pin_start_map=flat_node2pin_start_map, - pin_weights=pin_weights, - flop_lut_indices=flop_lut_indices, - flop_lut_mask=flop_lut_mask, - flop_mask=flop_mask, - lut_mask=lut_mask, - filler_start_map=filler_start_map, - xl=xl, - yl=yl, - xh=xh, - yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - route_num_bins_x=route_num_bins_x, - route_num_bins_y=route_num_bins_y, - pin_num_bins_x=pin_num_bins_x, - pin_num_bins_y=pin_num_bins_y, - total_place_area=total_place_area, - total_whitespace_area=total_whitespace_area, - max_route_opt_adjust_rate=max_route_opt_adjust_rate, - route_opt_adjust_exponent=route_opt_adjust_exponent, - max_pin_opt_adjust_rate=max_pin_opt_adjust_rate, - area_adjust_stop_ratio=area_adjust_stop_ratio, - route_area_adjust_stop_ratio=route_area_adjust_stop_ratio, - pin_area_adjust_stop_ratio=pin_area_adjust_stop_ratio, - unit_pin_capacity=unit_pin_capacity - ) - - pos_cpu = pos.clone().t().contiguous().view(-1) - node_size_x_cpu = node_size_x.clone() - node_size_y_cpu = node_size_y.clone() - pin_offset_x_cpu = pin_offset_x.clone() - pin_offset_y_cpu = pin_offset_y.clone() - resource_areas_cpu = resource_areas.clone() - flag1_cpu, flag2_cpu, flag3_cpu, flag4_cpu = adjust_node_area_op.forward( - pos_cpu, - node_size_x_cpu, - node_size_y_cpu, - pin_offset_x_cpu, - pin_offset_y_cpu, - target_density, - resource_areas_cpu, - route_utilization_map.clone(), - pin_utilization_map.clone()) - - if torch.cuda.device_count(): - adjust_node_area_op_cuda = adjust_node_area.AdjustNodeArea( - flat_node2pin_map=flat_node2pin_map.cuda(), - flat_node2pin_start_map=flat_node2pin_start_map.cuda(), - pin_weights=pin_weights, - flop_lut_indices=flop_lut_indices.cuda(), - flop_lut_mask=flop_lut_mask.cuda(), - flop_mask=flop_mask.cuda(), - lut_mask=lut_mask.cuda(), - filler_start_map=filler_start_map.cuda(), - xl=xl, - yl=yl, - xh=xh, - yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - route_num_bins_x=route_num_bins_x, - route_num_bins_y=route_num_bins_y, - pin_num_bins_x=pin_num_bins_x, - pin_num_bins_y=pin_num_bins_y, - total_place_area=total_place_area, - total_whitespace_area=total_whitespace_area, - max_route_opt_adjust_rate=max_route_opt_adjust_rate, - route_opt_adjust_exponent=route_opt_adjust_exponent, - max_pin_opt_adjust_rate=max_pin_opt_adjust_rate, - area_adjust_stop_ratio=area_adjust_stop_ratio, - route_area_adjust_stop_ratio=route_area_adjust_stop_ratio, - pin_area_adjust_stop_ratio=pin_area_adjust_stop_ratio, - unit_pin_capacity=unit_pin_capacity - ) - pos_cuda = pos.t().contiguous().view(-1).cuda() - node_size_x_cuda = node_size_x.cuda() - node_size_y_cuda = node_size_y.cuda() - pin_offset_x_cuda = pin_offset_x.cuda() - pin_offset_y_cuda = pin_offset_y.cuda() - resource_areas_cuda = resource_areas.cuda() - flag1_cuda, flag2_cuda, flag3_cuda, flag4_cuda = adjust_node_area_op_cuda.forward( - pos_cuda, - node_size_x_cuda, - node_size_y_cuda, - pin_offset_x_cuda, - pin_offset_y_cuda, - target_density.cuda(), - resource_areas_cuda, - route_utilization_map.cuda(), - pin_utilization_map.cuda()) - - assert (flag1_cpu == flag1_cuda) and \ - (flag2_cpu == flag2_cuda) and \ - (flag3_cpu == flag3_cuda) and \ - (flag4_cpu == flag4_cuda), "the flags via CPU and GPU are different" - - if flag1_cpu: - assert torch.allclose(pos_cuda.cpu(), pos_cpu) and \ - torch.allclose(node_size_x_cuda.cpu(), node_size_x_cpu) and \ - torch.allclose(node_size_y_cuda.cpu(), node_size_y_cpu) and \ - torch.allclose(pin_offset_x_cuda.cpu(), pin_offset_x_cpu) and \ - torch.allclose(pin_offset_y_cuda.cpu(), pin_offset_y_cpu), \ - "the results via CPU and GPU are different" - - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py deleted file mode 100644 index 0bcacd59..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py +++ /dev/null @@ -1,121 +0,0 @@ -## -# @file dct_electrical_potential_unitest.py -# @author Zixuan Jiang, Jiaqi Gu -# @date Mar 2019 -# @brief compare two different transforms to calculate the electric potential -# The fitst apporach is used in electric_potential_backup.py -# The second approach is used in electric_potential.py - -import torch -import os -import sys -import numpy as np -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.dct import dct, discrete_spectral_transform, dct2_fft2 -sys.path.pop() - - -def compare_different_methods(cuda_flag, M=1024, N=1024, dtype=torch.float64): - density_map = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0) - if cuda_flag: - density_map = density_map.cuda() - expkM = discrete_spectral_transform.get_expk(M, dtype, density_map.device) - expkN = discrete_spectral_transform.get_expk(N, dtype, density_map.device) - exact_expkM = discrete_spectral_transform.get_exact_expk(M, dtype, density_map.device) - exact_expkN = discrete_spectral_transform.get_exact_expk(N, dtype, density_map.device) - print("M = {}, N = {}".format(M, N)) - - wu = torch.arange(M, dtype=density_map.dtype, device=density_map.device).mul(2 * np.pi / M).view([M, 1]) - wv = torch.arange(N, dtype=density_map.dtype, device=density_map.device).mul(2 * np.pi / N).view([1, N]) - wu2_plus_wv2 = wu.pow(2) + wv.pow(2) - wu2_plus_wv2[0, 0] = 1.0 # avoid zero-division, it will be zeroed out - - inv_wu2_plus_wv2_2X = 2.0 / wu2_plus_wv2 - inv_wu2_plus_wv2_2X[0, 0] = 0.0 - wu_by_wu2_plus_wv2_2X = wu.mul(inv_wu2_plus_wv2_2X) - wv_by_wu2_plus_wv2_2X = wv.mul(inv_wu2_plus_wv2_2X) - - # the first approach is used as the ground truth - auv_golden = dct.dct2(density_map, expk0=expkM, expk1=expkN) - auv = auv_golden.clone() - auv[0, :].mul_(0.5) - auv[:, 0].mul_(0.5) - auv_by_wu2_plus_wv2_wu = auv.mul(wu_by_wu2_plus_wv2_2X) - auv_by_wu2_plus_wv2_wv = auv.mul(wv_by_wu2_plus_wv2_2X) - field_map_x_golden = dct.idsct2(auv_by_wu2_plus_wv2_wu, expkM, expkN) - field_map_y_golden = dct.idcst2(auv_by_wu2_plus_wv2_wv, expkM, expkN) - # compute potential phi - # auv / (wu**2 + wv**2) - auv_by_wu2_plus_wv2 = auv.mul(inv_wu2_plus_wv2_2X).mul_(2) - #potential_map = discrete_spectral_transform.idcct2(auv_by_wu2_plus_wv2, expkM, expkN) - potential_map_golden = dct.idcct2(auv_by_wu2_plus_wv2, expkM, expkN) - # compute energy - energy_golden = potential_map_golden.mul(density_map).sum() - - if density_map.is_cuda: - torch.cuda.synchronize() - - # the second approach uses the idxst_idct and idct_idxst - dct2 = dct2_fft2.DCT2(exact_expkM, exact_expkN) - idct2 = dct2_fft2.IDCT2(exact_expkM, exact_expkN) - idct_idxst = dct2_fft2.IDCT_IDXST(exact_expkM, exact_expkN) - idxst_idct = dct2_fft2.IDXST_IDCT(exact_expkM, exact_expkN) - - inv_wu2_plus_wv2 = 1.0 / wu2_plus_wv2 - inv_wu2_plus_wv2[0, 0] = 0.0 - wu_by_wu2_plus_wv2_half = wu.mul(inv_wu2_plus_wv2).mul_(0.5) - wv_by_wu2_plus_wv2_half = wv.mul(inv_wu2_plus_wv2).mul_(0.5) - - buv = dct2.forward(density_map) - - buv_by_wu2_plus_wv2_wu = buv.mul(wu_by_wu2_plus_wv2_half) - buv_by_wu2_plus_wv2_wv = buv.mul(wv_by_wu2_plus_wv2_half) - field_map_x = idxst_idct.forward(buv_by_wu2_plus_wv2_wu) - field_map_y = idct_idxst.forward(buv_by_wu2_plus_wv2_wv) - buv_by_wu2_plus_wv2 = buv.mul(inv_wu2_plus_wv2) - potential_map = idct2.forward(buv_by_wu2_plus_wv2) - energy = potential_map.mul(density_map).sum() - - if density_map.is_cuda: - torch.cuda.synchronize() - - # compare results - np.testing.assert_allclose(buv.data.cpu().numpy(), auv_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(field_map_x.data.cpu().numpy(), field_map_x_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(field_map_y.data.cpu().numpy(), field_map_y_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(potential_map.data.cpu().numpy(), potential_map_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(energy.data.cpu().numpy(), energy_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - - # the third approach uses the dct.idxst_idct and dct.idxst_idct - dct2 = dct.DCT2(expkM, expkN) - idct2 = dct.IDCT2(expkM, expkN) - idct_idxst = dct.IDCT_IDXST(expkM, expkN) - idxst_idct = dct.IDXST_IDCT(expkM, expkN) - - cuv = dct2.forward(density_map) - - cuv_by_wu2_plus_wv2_wu = cuv.mul(wu_by_wu2_plus_wv2_half) - cuv_by_wu2_plus_wv2_wv = cuv.mul(wv_by_wu2_plus_wv2_half) - field_map_x = idxst_idct.forward(cuv_by_wu2_plus_wv2_wu) - field_map_y = idct_idxst.forward(cuv_by_wu2_plus_wv2_wv) - cuv_by_wu2_plus_wv2 = cuv.mul(inv_wu2_plus_wv2) - potential_map = idct2.forward(cuv_by_wu2_plus_wv2) - energy = potential_map.mul(density_map).sum() - - if density_map.is_cuda: - torch.cuda.synchronize() - - # compare results - np.testing.assert_allclose(cuv.data.cpu().numpy(), auv_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(field_map_x.data.cpu().numpy(), field_map_x_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(field_map_y.data.cpu().numpy(), field_map_y_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(potential_map.data.cpu().numpy(), potential_map_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - np.testing.assert_allclose(energy.data.cpu().numpy(), energy_golden.data.cpu().numpy(), rtol=1e-6, atol=1e-5) - - -if __name__ == "__main__": - compare_different_methods(cuda_flag=False, M=1024, N=1024, dtype=torch.float64) - print("Verified different methods in CPU") - if torch.cuda.device_count(): - compare_different_methods(cuda_flag=True, M=1024, N=1024, dtype=torch.float64) - print("Verified different methods in CUDA") diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py deleted file mode 100644 index 22812f32..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py +++ /dev/null @@ -1,1089 +0,0 @@ -## -# @file dct_unitest.py -# @author Yibo Lin -# @date Mar 2019 -# - -import pdb -import os -import sys -import numpy as np -import unittest - -import torch -from torch.autograd import Function, Variable -import time -import scipy -from scipy import fftpack - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.dct import dct2_fft2 -from dreamplacefpga.ops.dct import discrete_spectral_transform -from dreamplacefpga.ops.dct import dct_lee -from dreamplacefpga.ops.dct import dct -sys.path.pop() - -dtype = torch.float32 - - -class DCTOpTest(unittest.TestCase): - def test_dctRandom(self): - N = 4 - x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) - #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) - - golden_value = discrete_spectral_transform.dct_2N(x).data.numpy() - print("golden_value") - print(golden_value) - - # test cpu using N-FFT - # pdb.set_trace() - custom = dct.DCT(algorithm='N') - dct_value = custom.forward(x) - print("dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using 2N-FFT - # pdb.set_trace() - custom = dct.DCT(algorithm='2N') - dct_value = custom.forward(x) - print("dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using dct_lee - # pdb.set_trace() - custom = dct_lee.DCT() - dct_value = custom.forward(x) - print("dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.DCT(algorithm='N') - dct_value = custom.forward(x.cuda()).cpu() - print("dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct.DCT(algorithm='2N') - dct_value = custom.forward(x.cuda()).cpu() - print("dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct_lee.DCT() - dct_value = custom.forward(x.cuda()).cpu() - print("dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - #golden_value = discrete_spectral_transform.dct2_2N(x).data.numpy() - #print("2D golden_value") - # print(golden_value) - - #custom = dct.DCT() - #dct2_value = custom.forward(dct_value.cuda().t().contiguous()).cpu() - #dct2_value = dct2_value.t().contiguous() - #print("dct2_value cuda") - # print(dct2_value.data.numpy()) - - #np.testing.assert_allclose(dct2_value.data.numpy(), golden_value) - - def test_idctRandom(self): - N = 4 - x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) - #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) - print("x") - print(x) - - y = discrete_spectral_transform.dct_N(x) - print("y") - print(y.data.numpy()) - - golden_value = discrete_spectral_transform.idct_2N(y).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu use N-FFT - # pdb.set_trace() - custom = dct.IDCT(algorithm='N') - dct_value = custom.forward(y) - print("idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - # test cpu use 2N-FFT - # pdb.set_trace() - custom = dct.IDCT(algorithm='2N') - dct_value = custom.forward(y) - print("idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - # test cpu use dct_lee - # pdb.set_trace() - custom = dct_lee.IDCT() - dct_value = custom.forward(y) - print("idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDCT(algorithm='N') - dct_value = custom.forward(y.cuda()).cpu() - print("idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - # test gpu - custom = dct.IDCT(algorithm='2N') - dct_value = custom.forward(y.cuda()).cpu() - print("idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - # test gpu - custom = dct_lee.IDCT() - dct_value = custom.forward(y.cuda()).cpu() - print("idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-5) - - def test_dct2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0) - expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) - expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) - - golden_value = discrete_spectral_transform.dct2_N(x).data.numpy() - print("2D DCT golden_value") - print(golden_value) - - # test cpu using N-FFT - # pdb.set_trace() - custom = dct.DCT2(algorithm='N') - dct_value = custom.forward(x) - print("2D dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using 2N-FFT - # pdb.set_trace() - custom = dct.DCT2(algorithm='2N') - dct_value = custom.forward(x) - print("2D dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using dct_lee - # pdb.set_trace() - custom = dct_lee.DCT2() - dct_value = custom.forward(x) - print("2D dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using fft2 - custom = dct2_fft2.DCT2(expkM, expkN) - dct_value = custom.forward(x) - print("2D dct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.DCT2(algorithm='N') - dct_value = custom.forward(x.cuda()).cpu() - print("2D dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct.DCT2(algorithm='2N') - dct_value = custom.forward(x.cuda()).cpu() - print("2D dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct_lee.DCT2() - dct_value = custom.forward(x.cuda()).cpu() - print("2D dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu using fft2 - custom = dct2_fft2.DCT2(expkM.cuda(), expkN.cuda()) - dct_value = custom.forward(x.cuda()).cpu() - print("2D dct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - def test_idct2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) - expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) - - y = discrete_spectral_transform.dct2_2N(x) - - golden_value = discrete_spectral_transform.idct2_2N(y).data.numpy() - print("2D idct golden_value") - print(golden_value) - - # test cpu using N-FFT - # pdb.set_trace() - custom = dct.IDCT2(algorithm='N') - dct_value = custom.forward(y) - print("2D idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using 2N-FFT - # pdb.set_trace() - custom = dct.IDCT2(algorithm='2N') - dct_value = custom.forward(y) - print("2D idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using dct_lee - # pdb.set_trace() - custom = dct_lee.IDCT2() - dct_value = custom.forward(y) - print("2D idct_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test cpu using fft2 - custom = dct2_fft2.IDCT2(expkM, expkN) - dct_value = custom.forward(y) - print("2D idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDCT2(algorithm='N') - dct_value = custom.forward(y.cuda()).cpu() - print("2D idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct.IDCT2(algorithm='2N') - dct_value = custom.forward(y.cuda()).cpu() - print("2D idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu - custom = dct_lee.IDCT2() - dct_value = custom.forward(y.cuda()).cpu() - print("2D idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - # test gpu using ifft2 - custom = dct2_fft2.IDCT2(expkM.cuda(), expkN.cuda()) - dct_value = custom.forward(y.cuda()).cpu() - print("2D idct_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, rtol=1e-6, atol=1e-5) - - def test_idxct2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - golden_value = discrete_spectral_transform.idxt(x, 0).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDXCT() - dct_value = custom.forward(x) - print("dxt_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDXCT() - dct_value = custom.forward(x) - print("dxt_value") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDXCT() - dct_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) - - # test gpu - custom = dct_lee.IDXCT() - dct_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dct_value.data.numpy()) - - np.testing.assert_allclose(dct_value.data.numpy(), golden_value, atol=1e-14) - - -class DSTOpTest(unittest.TestCase): - def test_dstRandom(self): - N = 4 - x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) - #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) - import scipy - from scipy import fftpack - - #golden_value = discrete_spectral_transform.dst(x).data.numpy() - golden_value = torch.from_numpy(fftpack.dst(x.data.numpy())).data.numpy() / N - print("golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.DST() - dst_value = custom.forward(x) - print("dst_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - # test cpu - # pdb.set_trace() - custom = dct_lee.DST() - dst_value = custom.forward(x) - print("dst_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.DST() - dst_value = custom.forward(x.cuda()).cpu() - print("dst_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - # test gpu - custom = dct_lee.DST() - dst_value = custom.forward(x.cuda()).cpu() - print("dst_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - def test_idstRandom(self): - N = 4 - x = torch.empty(N, N, dtype=dtype).uniform_(0, 10.0) - #x = Variable(torch.tensor([[1, 2, 7, 9, 20, 31], [4, 5, 9, 2, 1, 6]], dtype=dtype)) - print("x") - print(x) - import scipy - from scipy import fftpack - - #y = discrete_spectral_transform.dst(x) - y = torch.from_numpy(fftpack.dst(x.data.numpy())) - print("y") - print(y.data.numpy()) - - #golden_value = discrete_spectral_transform.idst(y).data.numpy() - golden_value = torch.from_numpy(fftpack.idst(y.data.numpy())).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDST() - dst_value = custom.forward(y) - print("idst_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDST() - dst_value = custom.forward(y) - print("idst_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDST() - dst_value = custom.forward(y.cuda()).cpu() - print("idst_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - # test gpu - custom = dct_lee.IDST() - dst_value = custom.forward(y.cuda()).cpu() - print("idst_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, rtol=1e-5) - - def test_idxst2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - golden_value = discrete_spectral_transform.idxt(x, 1).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDXST() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDXST() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDXST() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test gpu - custom = dct_lee.IDXST() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - -class DXTOpTest(unittest.TestCase): - def test_idcct2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - golden_value = discrete_spectral_transform.idcct2(x).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDCCT2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDCCT2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDCCT2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test gpu - custom = dct_lee.IDCCT2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - def test_idcst2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - golden_value = discrete_spectral_transform.idcst2(x).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDCST2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDCST2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDCST2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test gpu - custom = dct_lee.IDCST2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - def test_idsct2Random(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - golden_value = discrete_spectral_transform.idsct2(x).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - # pdb.set_trace() - custom = dct.IDSCT2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test cpu - # pdb.set_trace() - custom = dct_lee.IDSCT2() - dst_value = custom.forward(x) - print("dxt_value") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDSCT2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - # test gpu - custom = dct_lee.IDSCT2() - dst_value = custom.forward(x.cuda()).cpu() - print("dxt_value cuda") - print(dst_value.data.numpy()) - - np.testing.assert_allclose(dst_value.data.numpy(), golden_value, atol=1e-14) - - def test_idct_idxstRandom(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) - expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) - - golden_value = discrete_spectral_transform.idct_idxst(x).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - custom = dct.IDCT_IDXST() - idct_idxst_value = custom.forward(x) - print("2D dct.idct_idxst") - print(idct_idxst_value.data.numpy()) - - np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) - - # test gpu - custom = dct2_fft2.IDCT_IDXST(expkM, expkN) - idct_idxst_value = custom.forward(x) - print("2D dct2_fft2.idct_idxst cuda") - print(idct_idxst_value.data.numpy()) - - # note the scale factor - np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDCT_IDXST() - idct_idxst_value = custom.forward(x.cuda()).cpu() - print("2D dct.idct_idxst cuda") - print(idct_idxst_value.data.numpy()) - - np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) - - # test gpu - custom = dct2_fft2.IDCT_IDXST(expkM.cuda(), expkN.cuda()) - idct_idxst_value = custom.forward(x.cuda()).cpu() - print("2D dct2_fft2.idct_idxst cuda") - print(idct_idxst_value.data.numpy()) - - # note the scale factor - np.testing.assert_allclose(idct_idxst_value.data.numpy(), golden_value * 2, atol=1e-14) - - def test_idxst_idctRandom(self): - torch.manual_seed(10) - M = 4 - N = 8 - x = torch.empty(M, N, dtype=torch.int32).random_(0, 10).double() - print("2D x") - print(x) - - expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) - expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) - - golden_value = discrete_spectral_transform.idxst_idct(x).data.numpy() - print("2D golden_value") - print(golden_value) - - # test cpu - custom = dct.IDXST_IDCT() - idxst_idct_value = custom.forward(x) - print("2D dct.idxst_idct") - print(idxst_idct_value.data.numpy()) - - np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value * 2, atol=1e-14) - - # test cpu - custom = dct2_fft2.IDXST_IDCT(expkM, expkN) - idxst_idct_value = custom.forward(x) - print("2D dct2_fft2.idxst_idct cuda") - print(idxst_idct_value.data.numpy()) - - # note the scale factor - np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value* 2, atol=1e-14) - - if torch.cuda.device_count(): - # test gpu - custom = dct.IDXST_IDCT() - idxst_idct_value = custom.forward(x.cuda()).cpu() - print("2D dct.idxst_idct cuda") - print(idxst_idct_value.data.numpy()) - - np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value * 2, atol=1e-14) - - # test gpu - custom = dct2_fft2.IDXST_IDCT(expkM.cuda(), expkN.cuda()) - idxst_idct_value = custom.forward(x.cuda()).cpu() - print("2D dct2_fft2.idxst_idct cuda") - print(idxst_idct_value.data.numpy()) - - # note the scale factor - np.testing.assert_allclose(idxst_idct_value.data.numpy(), golden_value* 2, atol=1e-14) - -def eval_torch_rfft1d(x, runs): - for i in range(100): - a = torch.rfft(x, signal_ndim=1, onesided=True) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - a = torch.rfft(x, signal_ndim=1, onesided=True) - torch.cuda.synchronize() - print("torch.rfft1d takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - b = torch.irfft(a, signal_ndim=1, onesided=True, signal_sizes=x.shape[1:]) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - b = torch.irfft(a, signal_ndim=1, onesided=True, signal_sizes=x.shape[1:]) - torch.cuda.synchronize() - print("torch.irfft1d takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_torch_rfft2d(x, runs): - for i in range(100): - a = torch.rfft(x, signal_ndim=2, onesided=True) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - a = torch.rfft(x, signal_ndim=2, onesided=True) - torch.cuda.synchronize() - print("torch.rfft2d takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - b = torch.irfft(a, signal_ndim=2, onesided=True, signal_sizes=x.shape) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - b = torch.irfft(a, signal_ndim=2, onesided=True, signal_sizes=x.shape) - torch.cuda.synchronize() - print("torch.irfft2d takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_dct2d(x, expk0, expk1, expkM, expkN, runs): - x_numpy = x.data.cpu().numpy() - torch.cuda.synchronize() - tt = time.time() - y = fftpack.dct(fftpack.dct(x_numpy.T, norm=None).T/x.size(1), norm=None)/x.size(0) - torch.cuda.synchronize() - print("CPU scipy.fftpack.dct2d takes %.7f ms" % ((time.time()-tt)*1000)) - - # 9s for 200 iterations 1024x1024 on GTX 1080 - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_2N = discrete_spectral_transform.dct2_2N(x, expk0=expk0, expk1=expk1) - torch.cuda.synchronize() - # print(prof) - print("PyTorch: dct2d_2N takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - # 11s for 200 iterations 1024x1024 on GTX 1080 - perm0 = discrete_spectral_transform.get_perm(x.size(-2), dtype=torch.int64, device=x.device) - perm1 = discrete_spectral_transform.get_perm(x.size(-1), dtype=torch.int64, device=x.device) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = discrete_spectral_transform.dct2_N(x, perm0=perm0, expk0=expk0, perm1=perm1, expk1=expk1) - torch.cuda.synchronize() - # print(prof) - print("PyTorch: dct2d_N takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct.DCT2(expk0, expk1, algorithm='2N') - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_2N = dct2func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("DCT2d_2N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct.DCT2(expk0, expk1, algorithm='N') - y_N = dct2func.forward(x) - torch.cuda.synchronize() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - tt = time.time() - for i in range(runs): - y_N = dct2func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("DCT2d_N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - # The implementation below only supports float64 by now - dct2func = dct_lee.DCT2(expk0, expk1) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = dct2func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("DCT2d_Lee Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct2_fft2.DCT2(expkM, expkN) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("DCT2_FFT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_idct2d(x, expk0, expk1, expkM, expkN, runs): - y_N = discrete_spectral_transform.idct2_N(x, expk0=expk0, expk1=expk1) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_N = discrete_spectral_transform.idct2_N(x, expk0=expk0, expk1=expk1) - torch.cuda.synchronize() - print("PyTorch idct2_N takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - idct2func = dct.IDCT2(expk0, expk1, algorithm='2N') - y_N = idct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_N = idct2func.forward(x) - torch.cuda.synchronize() - print("IDCT2_2N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - idct2func = dct.IDCT2(expk0, expk1, algorithm='N') - y_N = idct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = idct2func.forward(x)/x.size(0)/x.size(1)/4 - torch.cuda.synchronize() - # print(prof) - print("IDCT2_N Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct2_fft2.IDCT2(expkM, expkN) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("IDCT2_FFT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_idxt2d(x, expk0, expk1, expkM, expkN, runs): - dct2func = dct.IDXST_IDCT(expk0, expk1) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("dct.IDXST_IDCT Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - y_N = discrete_spectral_transform.idxst_idct(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_N = discrete_spectral_transform.idxst_idct(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - print("PyTorch: idxst_idct takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct2_fft2.IDXST_IDCT(expkM, expkN) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("dct2_fft2.IDXST_IDCT takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct.IDCT_IDXST(expk0, expk1) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("dct.IDCT_IDXST takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - y_N = discrete_spectral_transform.idct_idxst(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_N = discrete_spectral_transform.idct_idxst(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - print("PyTorch: idct_idxst takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - dct2func = dct2_fft2.IDCT_IDXST(expkM, expkN) - y = dct2func.forward(x) - torch.cuda.synchronize() - tt = time.time() - for i in range(runs): - y_test = dct2func.forward(x) - torch.cuda.synchronize() - print("dct2_fft2.IDCT_IDXST takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_others(x, expk0, expk1, expkM, expkN, runs): - y_N = discrete_spectral_transform.idcct2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = discrete_spectral_transform.idcct2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - # print(prof) - print("idcct2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - func = dct.IDCCT2(expk0, expk1) - y_N = func.forward(x) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("IDCCT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - y_N = discrete_spectral_transform.idcst2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = discrete_spectral_transform.idcst2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - # print(prof) - print("idcst2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - func = dct.IDCST2(expk0, expk1) - y_N = func.forward(x) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("IDCST2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - y_N = discrete_spectral_transform.idsct2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = discrete_spectral_transform.idsct2(x, expk_0=expk0, expk_1=expk1) - torch.cuda.synchronize() - # print(prof) - print("idsct2 takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - func = dct.IDSCT2(expk0, expk1) - y_N = func.forward(x) - torch.cuda.synchronize() - tt = time.time() - # with torch.autograd.profiler.profile(use_cuda=True) as prof: - for i in range(runs): - y_N = func.forward(x) - torch.cuda.synchronize() - # print(prof) - print("IDSCT2 Function takes %.7f ms" % ((time.time()-tt)/runs*1000)) - - print("") - - -def eval_runtime(): - runs = 100 - - M = 1024 - N = 1024 - dtype = torch.float64 - x = torch.empty(M, N, dtype=dtype).uniform_(0, 10.0).cuda() - - print("M = {}, N = {}".format(M, N)) - - # 2cos(), 2sin() - expk0 = discrete_spectral_transform.get_expk(M, dtype=x.dtype, device=x.device) - expk1 = discrete_spectral_transform.get_expk(N, dtype=x.dtype, device=x.device) - # cos(), -sin() - expkM = discrete_spectral_transform.get_exact_expk(M, dtype=x.dtype, device=x.device) - expkN = discrete_spectral_transform.get_exact_expk(N, dtype=x.dtype, device=x.device) - - eval_torch_rfft1d(x, runs) - eval_torch_rfft2d(x, runs) - eval_dct2d(x, expk0, expk1, expkM, expkN, runs) - eval_idct2d(x, expk0, expk1, expkM, expkN, runs) - eval_idxt2d(x, expk0, expk1, expkM, expkN, runs) - eval_others(x, expk0, expk1, expkM, expkN, runs) - - -if __name__ == '__main__': - torch.manual_seed(10) - np.random.seed(10) - - print("usage: python dct_unitest.py test|eval") - - if len(sys.argv) > 1 and sys.argv[1] == "eval": - eval_runtime() - else: - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py deleted file mode 100644 index 3d3d1faf..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py +++ /dev/null @@ -1,210 +0,0 @@ -## -# @file density_potential_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import numpy as np -import unittest - -import torch -from torch.autograd import Function, Variable -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.density_potential import density_potential -sys.path.pop() -import inspect -import pdb - -class DensityPotentialOpTest(unittest.TestCase): - def test_densityOverflowRandom(self): - dtype = np.float32 - xx = np.array([1.0, 2.0]).astype(dtype) - yy = np.array([3.0, 1.5]).astype(dtype) - node_size_x = np.array([0.5, 1.0]).astype(dtype) - node_size_y = np.array([1.0, 1.0]).astype(dtype) - #xx = np.array([2.0]).astype(dtype) - #yy = np.array([1.5]).astype(dtype) - #node_size_x = np.array([1.0]).astype(dtype) - #node_size_y = np.array([1.0]).astype(dtype) - num_nodes = len(xx) - - scale_factor = 1.0 - - xl = 0.0 - yl = 0.0 - xh = 5.0 - yh = 5.0 - bin_size_x = 1.0 - bin_size_y = 1.0 - target_density = 0.1 - num_bins_x = int(np.ceil((xh-xl)/bin_size_x)) - num_bins_y = int(np.ceil((yh-yl)/bin_size_y)) - - ax = (4 / (node_size_x + 2*bin_size_x) / (node_size_x + 4*bin_size_x)).astype(dtype) - bx = (2 / bin_size_x / (node_size_x + 4*bin_size_x)).astype(dtype) - ay = (4 / (node_size_y + 2*bin_size_y) / (node_size_y + 4*bin_size_y)).astype(dtype) - by = (2 / bin_size_y / (node_size_y + 4*bin_size_y)).astype(dtype) - - #cx = np.zeros(num_nodes) - #cy = np.zeros(num_nodes) - #for i in range(num_nodes): - # sum_potential = 0.0 - # count = 0 - # for dist in np.arange(-(node_size_x[i]/2+2*bin_size_x)+node_size_x[i]/2, node_size_x[i]/2+2*bin_size_x, bin_size_x): - # if np.absolute(dist) < node_size_x[i]/2+bin_size_x: - # print("dist1 = %g, add %g" % (dist, 1 - ax[i]*dist*dist)) - # sum_potential += 1 - ax[i]*dist*dist - # else: - # print("dist2 = %g, add %g" % (dist, bx[i]*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x))) - # print("dddd = %g" % (np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)) - # sum_potential += bx[i]*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x)*(np.absolute(dist)-node_size_x[i]/2-2*bin_size_x) - # count += 1 - # #if count > num_bins_x: - # # break - # print("sum_potential = ", sum_potential) - # cx[i] = node_size_x[i]/sum_potential - #for i in range(num_nodes): - # sum_potential = 0.0 - # count = 0 - # for dist in np.arange(-(node_size_y[i]/2+2*bin_size_y)+node_size_y[i]/2, node_size_y[i]/2+2*bin_size_y, bin_size_y): - # if np.absolute(dist) < node_size_y[i]/2+bin_size_y: - # sum_potential += 1 - ay[i]*dist*dist - # else: - # sum_potential += by[i]*(np.absolute(dist)-node_size_y[i]/2-2*bin_size_y)*(np.absolute(dist)-node_size_y[i]/2-2*bin_size_y) - # count += 1 - # #if count > num_bins_x: - # # break - # cy[i] = node_size_y[i]/sum_potential - #print("cx = ", cx) - #print("cy = ", cy) - - # bell shape overlap function - def npfx1(dist): - # ax will be broadcast from num_nodes*1 to num_nodes*num_bins_x - return 1.0-ax.reshape([num_nodes, 1])*np.square(dist) - def npfx2(dist): - # bx will be broadcast from num_nodes*1 to num_nodes*num_bins_x - return bx.reshape([num_nodes, 1])*np.square(dist-node_size_x/2-2*bin_size_x).reshape([num_nodes, 1]) - def npfy1(dist): - # ay will be broadcast from num_nodes*1 to num_nodes*num_bins_y - return 1.0-ay.reshape([num_nodes, 1])*np.square(dist) - def npfy2(dist): - # by will be broadcast from num_nodes*1 to num_nodes*num_bins_y - return by.reshape([num_nodes, 1])*np.square(dist-node_size_y/2-2*bin_size_y).reshape([num_nodes, 1]) - # should not use integral, but sum; basically sample 5 distances, -2wb, -wb, 0, wb, 2wb; the sum does not change much when shifting cells - integral_potential_x = npfx1(0) + 2*npfx1(bin_size_x) + 2*npfx2(2*bin_size_x) - print("integral_potential_x = ", integral_potential_x) - cx = (node_size_x.reshape([num_nodes, 1]) / integral_potential_x).reshape([num_nodes, 1]) - # should not use integral, but sum; basically sample 5 distances, -2wb, -wb, 0, wb, 2wb; the sum does not change much when shifting cells - integral_potential_y = npfy1(0) + 2*npfy1(bin_size_y) + 2*npfy2(2*bin_size_y) - cy = (node_size_y.reshape([num_nodes, 1]) / integral_potential_y).reshape([num_nodes, 1]) - - """ - return bin xl - """ - def bin_xl(id_x): - return xl+id_x*bin_size_x - - """ - return bin xh - """ - def bin_xh(id_x): - return min(bin_xl(id_x)+bin_size_x, xh) - - """ - return bin yl - """ - def bin_yl(id_y): - return yl+id_y*bin_size_y - - """ - return bin yh - """ - def bin_yh(id_y): - return min(bin_yl(id_y)+bin_size_y, yh) - - bin_center_x = np.zeros(num_bins_x, dtype=dtype) - for id_x in range(num_bins_x): - bin_center_x[id_x] = (bin_xl(id_x)+bin_xh(id_x))/2*scale_factor - - bin_center_y = np.zeros(num_bins_y, dtype=dtype) - for id_y in range(num_bins_y): - bin_center_y[id_y] = (bin_yl(id_y)+bin_yh(id_y))/2*scale_factor - - print("target_area = ", target_density*bin_size_x*bin_size_y) - - sigma = 0.25 - delta = 2.0 - - # test cpu - custom = density_potential.DensityPotential( - torch.tensor(node_size_x, requires_grad=False), - torch.tensor(node_size_y, requires_grad=False), - torch.tensor(ax, requires_grad=False), - torch.tensor(bx, requires_grad=False), - torch.tensor(cx, requires_grad=False), - torch.tensor(ay, requires_grad=False), - torch.tensor(by, requires_grad=False), - torch.tensor(cy, requires_grad=False), - torch.tensor(bin_center_x, requires_grad=False), - torch.tensor(bin_center_y, requires_grad=False), - target_density=torch.tensor(target_density, requires_grad=False), - xl=torch.tensor(xl, requires_grad=False), - yl=torch.tensor(yl, requires_grad=False), - xh=torch.tensor(xh, requires_grad=False), - yh=torch.tensor(yh, requires_grad=False), - bin_size_x=torch.tensor(bin_size_x, requires_grad=False), - bin_size_y=torch.tensor(bin_size_y, requires_grad=False), - num_movable_nodes=torch.tensor(num_nodes, requires_grad=False), - num_terminals=0, - num_filler_nodes=0, - padding=torch.tensor(0, dtype=torch.int32, requires_grad=False), - sigma=sigma, delta=delta, num_threads=1) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy])), requires_grad=True) - result = custom.forward(pos) - print("custom_result = ", result) - result.backward() - grad = pos.grad.clone() - print("custom_grad = ", grad) - - # test cuda - if torch.cuda.device_count(): - custom_cuda = density_potential.DensityPotential( - torch.tensor(node_size_x, requires_grad=False).cuda(), - torch.tensor(node_size_y, requires_grad=False).cuda(), - torch.tensor(ax, requires_grad=False).cuda(), - torch.tensor(bx, requires_grad=False).cuda(), - torch.tensor(cx, requires_grad=False).cuda(), - torch.tensor(ay, requires_grad=False).cuda(), - torch.tensor(by, requires_grad=False).cuda(), - torch.tensor(cy, requires_grad=False).cuda(), - torch.tensor(bin_center_x, requires_grad=False).cuda(), - torch.tensor(bin_center_y, requires_grad=False).cuda(), - target_density=torch.tensor(target_density, requires_grad=False).cuda(), - xl=torch.tensor(xl, requires_grad=False).cuda(), - yl=torch.tensor(yl, requires_grad=False).cuda(), - xh=torch.tensor(xh, requires_grad=False).cuda(), - yh=torch.tensor(yh, requires_grad=False).cuda(), - bin_size_x=torch.tensor(bin_size_x, requires_grad=False).cuda(), - bin_size_y=torch.tensor(bin_size_y, requires_grad=False).cuda(), - num_movable_nodes=torch.tensor(num_nodes, requires_grad=False).cuda(), - num_terminals=0, - num_filler_nodes=0, - padding=torch.tensor(0, dtype=torch.int32, requires_grad=False).cuda(), - sigma=sigma, delta=delta, num_threads=1) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy])).cuda(), requires_grad=True) - #pos.grad.zero_() - result_cuda = custom_cuda.forward(pos) - print("custom_result_cuda = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pos.grad.clone() - print("custom_grad_cuda = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result.detach().numpy(), result_cuda.data.cpu().detach().numpy()) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py deleted file mode 100644 index 12e986f1..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py +++ /dev/null @@ -1,68 +0,0 @@ -## -# @file draw_place_unitest.py -# @author Yibo Lin -# @date Mar 2019 -# - -import os -import sys -import time -import numpy as np -import unittest -import gzip -import pdb - -import torch -from torch.autograd import Function, Variable -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.draw_place import draw_place -sys.path.pop() - -class DrawPlaceOpTest(unittest.TestCase): - def test_drawPlaceRandom(self): - dtype = np.float32 - np.random.seed(100) - xx = np.array([1.0, 2.0]).astype(dtype) - yy = np.array([3.0, 1.5]).astype(dtype) - node_size_x = np.array([0.5, 1.0]).astype(dtype) - node_size_y = np.array([1.0, 1.0]).astype(dtype) - pin2node_map = np.array([0, 0, 1, 1]).astype(np.int32) - pin_offset_x = np.array([0.1, 0.3, 0.2, 0.6]).astype(dtype) - pin_offset_y = np.array([0.1, 0.8, 0.2, 0.6]).astype(dtype) - num_nodes = len(xx) - - xl = 1.0 - yl = 1.0 - xh = 5.0 - yh = 5.0 - bin_size_x = 2.0 - bin_size_y = 2.0 - site_width = 1.0 - row_height = 2.0 - num_bins_x = int(np.ceil((xh-xl)/bin_size_x)) - num_bins_y = int(np.ceil((yh-yl)/bin_size_y)) - num_movable_nodes = len(xx) - num_terminals = 0 - num_filler_nodes = 0 - - # test cpu - custom = draw_place.DrawPlaceFunction.forward( - torch.from_numpy(np.concatenate([xx, yy])), - torch.from_numpy(node_size_x), torch.from_numpy(node_size_y), - torch.from_numpy(pin_offset_x), torch.from_numpy(pin_offset_y), - torch.from_numpy(pin2node_map), - xl, yl, xh, yh, - site_width, row_height, - bin_size_x, bin_size_y, - num_movable_nodes, - num_filler_nodes, - "test.png" # png, jpg, eps, pdf - ) - print(custom) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py deleted file mode 100644 index 7473dac0..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py +++ /dev/null @@ -1,326 +0,0 @@ -## -# @file electric_potential_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import time -import numpy as np -import unittest -import logging -import random - -import torch -from torch.autograd import Function, Variable -import os -import sys -import gzip - -sys.path.append( - os.path.dirname(os.path.dirname(os.path.dirname( - os.path.abspath(__file__))))) -from dreamplacefpga.ops.dct import dct -from dreamplacefpga.ops.dct import discrete_spectral_transform -from dreamplacefpga.ops.electric_potential import electric_potential -sys.path.pop() - -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle -import inspect -import pdb -from scipy import fftpack - -import matplotlib -matplotlib.use('Agg') -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt - -class PlaceDB (object): - def __init__(self): - self.num_nodes = 0 - self.num_terminals = 0 - self.num_movable_nodes = 0 - self.num_filler_nodes = 0 - self.xWirelenWt = 1.0 - self.yWirelenWt = 1.0 - self.filler_start_map = [] - self.overflowInstDensityStretchRatio = [] - -class ElectricPotentialOpTest(unittest.TestCase): - def test_densityOverflowRandom(self): - dtype = np.float64 - - stretchRatio = np.sqrt(2.0) - ## Create a database - placedb = PlaceDB() - placedb.num_nodes = 84 - placedb.num_terminals = 1 - placedb.num_movable_nodes = placedb.num_nodes - placedb.num_terminals - placedb.filler_start_map = np.array([0, 0, 0, 0, 0, 0], dtype=np.int32) - placedb.overflowInstDensityStretchRatio = np.array([stretchRatio, stretchRatio, 1.0, 1.0, 0], dtype=dtype) - - xl = 0.0 - yl = 0.0 - xh = 100.0 - yh = 360.0 - - xx = np.array([random.randint(int(xl),int(xh)) for i in range(placedb.num_nodes)]).astype(dtype) - yy = np.array([random.randint(int(yl),int(yh)) for i in range(placedb.num_nodes)]).astype(dtype) - node2fence_region_map = np.array([0 for i in range(placedb.num_nodes)]).astype(np.int32) - #Keep last entry as fixed - xx[-1] = 0 - yy[-1] = 0 - node2fence_region_map[-1] = 4 - - node_size_x = np.array([ - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1.0 - ]).astype(dtype) - node_size_y = np.array([ - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, - 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1.0 - ]).astype(dtype) - - region_id = 0 - - num_bins_x = 256 - num_bins_y = 256 - bin_size_x = (xh - xl) / num_bins_x - bin_size_y = (yh - yl) / num_bins_y - initial_density_map = np.zeros([num_bins_x, num_bins_y], dtype=dtype) - fixed_cols = int(num_bins_x/(xh-xl)) + 1 - for val in range(fixed_cols): - initial_density_map[val] = bin_size_x*bin_size_y - initial_density_map[:,val] = bin_size_x*bin_size_y - - print("target_area = ", bin_size_x * bin_size_y) - - if dtype == np.float64: - dtype = torch.float64 - elif dtype == np.float32: - dtype = torch.float32 - - movable_size_x = node_size_x[:placedb.num_movable_nodes] - _, sorted_node_map = torch.sort( - torch.tensor(movable_size_x, requires_grad=False, dtype=dtype)) - sorted_node_map = sorted_node_map.to(torch.int32).contiguous() - - # test cpu - custom = electric_potential.ElectricPotential( - torch.tensor(node_size_x, requires_grad=False, dtype=dtype), - torch.tensor(node_size_y, requires_grad=False, dtype=dtype), - xl=xl, - yl=yl, - xh=xh, - yh=yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=0, - deterministic_flag=True, - sorted_node_map=sorted_node_map, - region_id=region_id, - fence_regions=torch.tensor(initial_density_map, requires_grad=False, dtype=dtype), - node2fence_region_map=torch.tensor(node2fence_region_map, requires_grad=False, dtype=torch.int32), - placedb=placedb, - stretchRatio=stretchRatio) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy])), - requires_grad=True) - result = custom.forward(pos) - print("custom_result = ", result) - print(result.type()) - result.backward() - grad = pos.grad.clone() - print("custom_grad = ", grad) - - # test cuda - if torch.cuda.device_count(): - custom_cuda = electric_potential.ElectricPotential( - torch.tensor(node_size_x, requires_grad=False, - dtype=dtype).cuda(), - torch.tensor(node_size_y, requires_grad=False, - dtype=dtype).cuda(), - xl=xl, - yl=yl, - xh=xh, - yh=yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=0, - deterministic_flag=False, - sorted_node_map=sorted_node_map.cuda(), - region_id=region_id, - fence_regions=torch.tensor(initial_density_map, requires_grad=False, - dtype=dtype).cuda(), - node2fence_region_map=torch.tensor(node2fence_region_map, requires_grad=False, - dtype=torch.int32).cuda(), - placedb=placedb, - stretchRatio=stretchRatio) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy])).cuda(), - requires_grad=True) - #pos.grad.zero_() - result_cuda = custom_cuda.forward(pos) - print("custom_result_cuda = ", result_cuda.data.cpu()) - print(result_cuda.type()) - result_cuda.backward() - grad_cuda = pos.grad.clone() - print("custom_grad_cuda = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result.detach().numpy(), - result_cuda.data.cpu().detach().numpy()) - np.testing.assert_allclose(grad.detach().numpy(), - grad_cuda.data.cpu().detach().numpy()) - - -def plot(plot_count, density_map, padding, name): - """ - density map contour and heat map - """ - density_map = density_map[padding:density_map.shape[0] - padding, - padding:density_map.shape[1] - padding] - print("max density = %g" % (np.amax(density_map))) - print("mean density = %g" % (np.mean(density_map))) - - fig = plt.figure() - ax = fig.gca(projection='3d') - - x = np.arange(density_map.shape[0]) - y = np.arange(density_map.shape[1]) - - x, y = np.meshgrid(x, y) - # looks like x and y should be swapped - ax.plot_surface(y, x, density_map, alpha=0.8) - - ax.set_xlabel('x') - ax.set_ylabel('y') - ax.set_zlabel('density') - - # plt.tight_layout() - plt.savefig(name + ".3d.png") - plt.close() - - # plt.clf() - - #fig, ax = plt.subplots() - - # ax.pcolor(density_map) - - # Loop over data dimensions and create text annotations. - # for i in range(density_map.shape[0]): - # for j in range(density_map.shape[1]): - # text = ax.text(j, i, density_map[i, j], - # ha="center", va="center", color="w") - # fig.tight_layout() - #plt.savefig(name+".2d.%d.png" % (plot_count)) - # plt.close() - - -def eval_runtime(design): - ## e.g., adaptec1_density.pklz - #with gzip.open(design, "rb") as f: - # node_size_x, node_size_y, bin_center_x, bin_center_y, xl, yl, xh, yh, bin_size_x, bin_size_y, num_movable_nodes, num_terminals, num_filler_nodes = pickle.load( - # f) - - dtype = torch.float64 - num_threads = 10 - torch.set_num_threads(num_threads) - print("num_threads = %d" % (torch.get_num_threads())) - movable_size_x = node_size_x[:placedb.num_movable_nodes] - _, sorted_node_map = torch.sort( - torch.tensor(movable_size_x, requires_grad=False, dtype=dtype).cuda()) - sorted_node_map = sorted_node_map.to(torch.int32).contiguous() - node2fence_region_map = node2fence_region_map.to(torch.int32).contiguous() - initial_density_map = initial_density_map.to(dtype).contiguous() - - pos_var = Variable(torch.empty(len(node_size_x) * 2, - dtype=dtype).uniform_(xl, xh), - requires_grad=True) - custom = electric_potential.ElectricPotential( - torch.tensor(node_size_x, requires_grad=False, dtype=dtype).cpu(), - torch.tensor(node_size_y, requires_grad=False, dtype=dtype).cpu(), - xl=xl, - yl=yl, - xh=xh, - yh=yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=num_filler_nodes, - deterministic_flag=True, - sorted_node_map=sorted_node_map.cpu(), - region_id=region_id, - fence_regions=initial_density_map.cpu(), - node2fence_region_map=node2fence_region_map.cpu(), - placedb=placedb, - stretchRatio=stretchRatio) - - custom_cuda = electric_potential.ElectricPotential( - torch.tensor(node_size_x, requires_grad=False, dtype=dtype).cuda(), - torch.tensor(node_size_y, requires_grad=False, dtype=dtype).cuda(), - xl=xl, - yl=yl, - xh=xh, - yh=yh, - bin_size_x=bin_size_x, - bin_size_y=bin_size_y, - num_movable_nodes=placedb.num_movable_nodes, - num_terminals=placedb.num_terminals, - num_filler_nodes=num_filler_nodes, - deterministic_flag=False, - sorted_node_map=sorted_node_map, - region_id=region_id, - fence_regions=initial_density_map, - node2fence_region_map=node2fence_region_map, - placedb=placedb, - stretchRatio=stretchRatio) - - torch.cuda.synchronize() - iters = 100 - tbackward = 0 - tt = time.time() - for i in range(iters): - result = custom.forward(pos_var) - ttb = time.time() - result.backward() - tbackward += time.time() - ttb - torch.cuda.synchronize() - print("custom takes %.3f ms, backward %.3f ms" % - ((time.time() - tt) / iters * 1000, (tbackward / iters * 1000))) - - pos_var = pos_var.cuda() - tt = time.time() - for i in range(iters): - result = custom_cuda.forward(pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_cuda takes %.3f ms" % ((time.time() - tt) / iters * 1000)) - - -if __name__ == '__main__': - logging.root.name = 'DREAMPlaceFPGA' - logging.basicConfig(level=logging.DEBUG, - format='[%(levelname)-7s] %(name)s - %(message)s', - stream=sys.stdout) - if len(sys.argv) < 2: - unittest.main() - else: - design = sys.argv[1] - eval_runtime(design) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py deleted file mode 100644 index 7cb6f2dd..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py +++ /dev/null @@ -1,152 +0,0 @@ -## -# @file hpwl_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import numpy as np -import unittest - -import torch -from torch.autograd import Function, Variable -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.hpwl import hpwl -sys.path.pop() -import pdb - -""" -return hpwl of a net -""" -def net_hpwl(x, y, net2pin_map, net_weights, net_id): - pins = net2pin_map[net_id] - hpwl_x = np.amax(x[pins]) - np.amin(x[pins]) - hpwl_y = np.amax(y[pins]) - np.amin(y[pins]) - - return (hpwl_x+hpwl_y)*net_weights[net_id] - -""" -return hpwl of all nets -""" -def all_hpwl(x, y, net2pin_map, net_weights): - wl = 0 - for net_id in range(len(net2pin_map)): - wl += net_hpwl(x, y, net2pin_map, net_weights, net_id) - return wl - -class HPWLOpTest(unittest.TestCase): - def test_hpwlRandom(self): - pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32) - net2pin_map = [[0, 4], [1, 2, 3]] - num_nets = 2 - # net weights - net_weights = np.array([1, 2], dtype=np.float32) - print("net_weights = ", net_weights) - - pin_x = pin_pos[:, 0] - pin_y = pin_pos[:, 1] - - # construct flat_net2pin_map and flat_net2pin_start_map - # flat netpin map, length of #pins - flat_net2pin_map = np.array([0, 4, 1, 2, 3], dtype=np.int32) - # starting index in netpin map for each net, length of #nets+1, the last entry is #pins - flat_net2pin_start_map = np.array([0, 2, 5], dtype=np.int32) - count = 0 - #for i in range(len(net2pin_map)): - # flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] - # flat_net2pin_start_map[i] = count - # count += len(net2pin_map[i]) - #flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) - - print("flat_net2pin_map = ", flat_net2pin_map) - print("flat_net2pin_start_map = ", flat_net2pin_start_map) - - # construct pin2net_map - pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) - for i in range(num_nets): - for pin_id in net2pin_map[i]: - pin2net_map[pin_id] = i - print("pin2net_map = ", pin2net_map) - - # net degrees - net_degrees = np.array([2, 3], dtype=np.int32) - net_mask = (net_degrees <= np.amax(net_degrees)).astype(np.uint8) - print("net_mask = ", net_mask) - - golden_value = all_hpwl(pin_x, pin_y, net2pin_map, net_weights) - print("golden_value = ", golden_value) - - # test cpu - print(np.transpose(pin_pos)) - pin_pos_var = Variable(torch.from_numpy(pin_pos)) - print(pin_pos_var) - # clone is very important, because the custom op cannot deep copy the data - pin_pos_var = torch.t(pin_pos_var).contiguous() - custom = hpwl.HPWL( - xWeight=1.0, - yWeight=1.0, - flat_netpin=torch.from_numpy(flat_net2pin_map), - netpin_start=torch.from_numpy(flat_net2pin_start_map), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - num_threads=1, - algorithm='net-by-net' - ) - hpwl_value = custom.forward(pin_pos_var) - print("hpwl_value = ", hpwl_value.data.numpy()) - np.testing.assert_allclose(hpwl_value.data.numpy(), golden_value) - - # test gpu - if torch.cuda.device_count(): - custom_cuda = hpwl.HPWL( - xWeight=1.0, - yWeight=1.0, - flat_netpin=torch.from_numpy(flat_net2pin_map).cuda(), - netpin_start=torch.from_numpy(flat_net2pin_start_map).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - num_threads=1, - algorithm='net-by-net' - ) - hpwl_value = custom_cuda.forward(pin_pos_var.cuda()) - print("hpwl_value cuda = ", hpwl_value.data.cpu().numpy()) - np.testing.assert_allclose(hpwl_value.data.cpu().numpy(), golden_value) - - # test atomic cpu - custom_atomic = hpwl.HPWL( - xWeight=1.0, - yWeight=1.0, - flat_netpin=torch.from_numpy(flat_net2pin_map), - netpin_start=torch.from_numpy(flat_net2pin_start_map), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - num_threads=1, - algorithm='atomic' - ) - hpwl_value = custom_atomic.forward(pin_pos_var) - print("hpwl_value atomic = ", hpwl_value.data.numpy()) - np.testing.assert_allclose(hpwl_value.data.numpy(), golden_value) - - # test atomic gpu - if torch.cuda.device_count(): - custom_cuda_atomic = hpwl.HPWL( - xWeight=1.0, - yWeight=1.0, - flat_netpin=torch.from_numpy(flat_net2pin_map).cuda(), - netpin_start=torch.from_numpy(flat_net2pin_start_map).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - num_threads=1, - algorithm='atomic' - ) - hpwl_value = custom_cuda_atomic.forward(pin_pos_var.cuda()) - print("hpwl_value cuda atomic = ", hpwl_value.data.cpu().numpy()) - np.testing.assert_allclose(hpwl_value.data.cpu().numpy(), golden_value) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py deleted file mode 100644 index 77a00049..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py +++ /dev/null @@ -1,197 +0,0 @@ -## -# @file logsumexp_wirelength_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import numpy as np -import unittest -import pdb - -import torch -from torch.autograd import Function, Variable - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.logsumexp_wirelength import logsumexp_wirelength -sys.path.pop() - -def print_val(array, name): - temp = "Contents of " + name + ": " - for el in array: - if hasattr(el, "__len__"): - temp += "[" - for subEl in el: - temp += str(subEl.item()) + " " - temp += "] " - else: - temp += str(el.item()) + " " - print(temp) - -def unsorted_segment_max(pin_x, pin2net_map, num_nets): - result = torch.zeros(num_nets, dtype=pin_x.dtype) - for i in range(len(pin2net_map)): - result[pin2net_map[i]] = result[pin2net_map[i]].max(pin_x[i]) - return result - -def unsorted_segment_min(pin_x, pin2net_map, num_nets): - result = torch.zeros(num_nets, dtype=pin_x.dtype) - for i in range(len(pin2net_map)): - result[pin2net_map[i]] = result[pin2net_map[i]].min(pin_x[i]) - return result - -def unsorted_segment_sum(pin_x, pin2net_map, num_nets): - result = torch.zeros(num_nets, dtype=pin_x.dtype) - for i in range(len(pin2net_map)): - result[pin2net_map[i]] += pin_x[i] - return result - -def build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights): - # wirelength cost - # log-sum-exp - # ignore_net_degree is not supported yet - - # temporily store exp(x) - scaled_pin_x = pin_x/gamma - scaled_pin_y = pin_y/gamma - - exp_pin_x = torch.exp(scaled_pin_x) - exp_pin_y = torch.exp(scaled_pin_y) - nexp_pin_x = torch.exp(-scaled_pin_x) - nexp_pin_y = torch.exp(-scaled_pin_y) - - # sum of exp(x) - sum_exp_pin_x = unsorted_segment_sum(exp_pin_x, pin2net_map, len(net2pin_map)) - sum_exp_pin_y = unsorted_segment_sum(exp_pin_y, pin2net_map, len(net2pin_map)) - sum_nexp_pin_x = unsorted_segment_sum(nexp_pin_x, pin2net_map, len(net2pin_map)) - sum_nexp_pin_y = unsorted_segment_sum(nexp_pin_y, pin2net_map, len(net2pin_map)) - - wl = (torch.log(sum_exp_pin_x) + torch.log(sum_nexp_pin_x) + torch.log(sum_exp_pin_y) + torch.log(sum_nexp_pin_y))*gamma - wl *= torch.from_numpy(net_weights) - - wirelength = torch.sum(wl) - - return wirelength - -class LogSumExpWirelengthOpTest(unittest.TestCase): - def test_logsumexp_wirelength_random(self): - dtype = torch.float32 - pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32)*10 - net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) - for net_id, pins in enumerate(net2pin_map): - for pin in pins: - pin2net_map[pin] = net_id - net_weights = np.array([1, 2], dtype=np.float32) - - pin_x = pin_pos[:, 0] - pin_y = pin_pos[:, 1] - gamma = torch.tensor(0.5, dtype=torch.float32) - ignore_net_degree = 4 - pin_mask = np.zeros(len(pin2net_map), dtype=np.uint8) - - # net mask - net_mask = np.ones(len(net2pin_map), dtype=np.uint8) - for i in range(len(net2pin_map)): - if len(net2pin_map[i]) >= ignore_net_degree: - net_mask[i] = 0 - - # construct flat_net2pin_map and flat_net2pin_start_map - # flat netpin map, length of #pins - flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) - # starting index in netpin map for each net, length of #nets+1, the last entry is #pins - flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(net2pin_map)): - flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] - flat_net2pin_start_map[i] = count - count += len(net2pin_map[i]) - flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) - - #print("flat_net2pin_map = ", flat_net2pin_map) - print_val(flat_net2pin_map, "flat_net2pin_map") - #print("flat_net2pin_start_map = ", flat_net2pin_start_map) - print_val(flat_net2pin_start_map, "flat_net2pin_start_map") - - #print(np.transpose(pin_pos)) - print_val(np.transpose(pin_pos), "pin_pos_transpose") - pin_pos_var = Variable(torch.from_numpy(np.transpose(pin_pos)).reshape([-1]), requires_grad=True) - #pin_pos_var = torch.nn.Parameter(torch.from_numpy(np.transpose(pin_pos)).reshape([-1])) - print(pin_pos_var) - - golden = build_wirelength(pin_pos_var[:pin_pos_var.numel()//2], pin_pos_var[pin_pos_var.numel()//2:], pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights) - print("golden_value = ", golden.item()) - golden.backward() - golden_grad = pin_pos_var.grad.clone() - print("golden_grad = ", golden_grad.data) - - # test cpu - # clone is very important, because the custom op cannot deep copy the data - pin_pos_var.grad.zero_() - custom = logsumexp_wirelength.LogSumExpWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - algorithm='merged' - ) - result = custom.forward(pin_pos_var) - print("custom = ", result.item()) - result.backward() - grad = pin_pos_var.grad.clone() - print("custom_grad = ", grad) - - np.testing.assert_allclose(result.data.numpy(), golden.data.detach().numpy()) - np.testing.assert_allclose(grad.data.numpy(), golden_grad.data.numpy()) - - # test gpu - if torch.cuda.device_count(): - pin_pos_var.grad.zero_() - custom_cuda = logsumexp_wirelength.LogSumExpWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - algorithm='merged' - ) - result_cuda = custom_cuda.forward(pin_pos_var.cuda()) - print("custom_cuda_result = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pin_pos_var.grad.clone() - print("custom_grad_cuda = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden.data.detach().numpy()) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-7, atol=1e-11) - - # test gpu atomic - if torch.cuda.device_count(): - pin_pos_var.grad.zero_() - custom_cuda = logsumexp_wirelength.LogSumExpWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=gamma.cuda(), - algorithm='atomic' - ) - result_cuda = custom_cuda.forward(pin_pos_var.cuda()) - print("custom_cuda_result atomic = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pin_pos_var.grad.clone() - print("custom_grad_cuda atomic = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden.data.detach().numpy()) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-7, atol=1e-15) - - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py deleted file mode 100644 index 947404b2..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py +++ /dev/null @@ -1,75 +0,0 @@ -## -# @file move_boundary_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import numpy as np -import unittest - -import torch -from torch.autograd import Function, Variable - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.move_boundary import move_boundary -sys.path.pop() - -class MoveBoundaryOpTest(unittest.TestCase): - def test_densityOverflowRandom(self): - dtype = np.float32 - xx = np.array([1.0, 4.6]).astype(dtype) - yy = np.array([0.5, 4.1]).astype(dtype) - node_size_x = np.array([0.5, 1.0]).astype(dtype) - node_size_y = np.array([1.0, 1.0]).astype(dtype) - #xx = np.array([2.0]).astype(dtype) - #yy = np.array([1.5]).astype(dtype) - #node_size_x = np.array([1.0]).astype(dtype) - #node_size_y = np.array([1.0]).astype(dtype) - num_nodes = len(xx) - scale_factor = 1.0 - - xl = 1.0 - yl = 1.0 - xh = 5.0 - yh = 5.0 - num_movable_nodes = len(xx) - num_terminals = 0 - num_filler_nodes = 0 - - # test cpu - custom = move_boundary.MoveBoundary( - torch.from_numpy(node_size_x), torch.from_numpy(node_size_y), - xl=xl, yl=yl, xh=xh, yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - num_threads=1) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy]))) - result = custom(pos) - print("custom_result = ", result) - - #result.retain_grad() - #result.sum().backward() - #print("custom_result.grad = ", result.grad) - - # test cuda - if torch.cuda.device_count(): - custom_cuda = move_boundary.MoveBoundary( - torch.from_numpy(node_size_x).cuda(), - torch.from_numpy(node_size_y).cuda(), - xl=xl, yl=yl, xh=xh, yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - num_threads=1) - - pos = Variable(torch.from_numpy(np.concatenate([xx, yy]))).cuda() - result_cuda = custom_cuda(pos) - print("custom_result = ", result_cuda.data.cpu()) - - - np.testing.assert_allclose(result, result_cuda.data.cpu()) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py deleted file mode 100644 index 10a80049..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py +++ /dev/null @@ -1,128 +0,0 @@ -## -# @file pin_pos_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import time -import numpy as np -import unittest -#import pickle -import gzip -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.pin_pos import pin_pos -sys.path.pop() - -import pdb - -import torch -from torch.autograd import Function, Variable - -def build_pin_pos(pos, pin_offset_x, pin_offset_y, pin2node_map, num_physical_nodes): - num_nodes = pos.numel()//2 - pin_x = pin_offset_x.add(torch.index_select(pos[0:num_physical_nodes], dim=0, index=pin2node_map.long())) - pin_y = pin_offset_y.add(torch.index_select(pos[num_nodes:num_nodes+num_physical_nodes], dim=0, index=pin2node_map.long())) - pin_pos = torch.cat([pin_x, pin_y], dim=0) - return pin_pos - -class WeightedAverageWirelengthOpTest(unittest.TestCase): - def test_pin_pos_random(self): - dtype = torch.float32 - num_threads = 1 - pos = np.array([[1, 10], [2, 20], [3, 30]], dtype=np.float32) - node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - num_physical_nodes = len(node2pin_map) - num_pins = 0 - for pins in node2pin_map: - num_pins += len(pins) - pin2node_map = np.zeros(num_pins, dtype=np.int32) - for node_id, pins in enumerate(node2pin_map): - for pin in pins: - pin2node_map[pin] = node_id - - pin_offset_x = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=pos.dtype) - pin_offset_y = np.array([0.01, 0.02, 0.03, 0.04, 0.05], dtype=pos.dtype) - - # construct flat_node2pin_map and flat_node2pin_start_map - # flat nodepin map, length of #pins - flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) - # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins - flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(node2pin_map)): - flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] - flat_node2pin_start_map[i] = count - count += len(node2pin_map[i]) - flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) - - print("flat_node2pin_map = ", flat_node2pin_map) - print("flat_node2pin_start_map = ", flat_node2pin_start_map) - - pos_var = Variable(torch.from_numpy(pos).reshape([-1]), requires_grad=True) - - golden_value = build_pin_pos(pos_var, torch.from_numpy(pin_offset_x), - torch.from_numpy(pin_offset_y), - torch.from_numpy(pin2node_map), num_physical_nodes) - golden_loss = golden_value.sum() - print("golden_value = ", golden_value) - print("golden_loss = ", golden_loss) - golden_loss.backward() - golden_grad = pos_var.grad.clone() - print("golden grad = ", golden_grad) - golden_value = golden_value.detach().numpy() - golden_grad = golden_grad.detach().numpy() - - # test cpu - print(pos_var) - # clone is very important, because the custom op cannot deep copy the data - custom = pin_pos.PinPos( - pin_offset_x=torch.from_numpy(pin_offset_x), - pin_offset_y=torch.from_numpy(pin_offset_y), - pin2node_map=torch.from_numpy(pin2node_map), - flat_node2pin_map=torch.from_numpy(flat_node2pin_map), - flat_node2pin_start_map=torch.from_numpy(flat_node2pin_start_map), - num_physical_nodes=num_physical_nodes, - num_threads=num_threads - ) - result = custom.forward(pos_var) - custom_loss = result.sum() - print("custom = ", result) - pos_var.grad.zero_() - custom_loss.backward() - grad = pos_var.grad.clone() - print("custom_grad = ", grad) - - np.testing.assert_allclose(result.data.detach().numpy(), golden_value, atol=1e-6) - np.testing.assert_allclose(grad.data.detach().numpy(), golden_grad, atol=1e-6) - - # test gpu - if torch.cuda.device_count(): - pos_var.grad.zero_() - custom_cuda = pin_pos.PinPos( - pin_offset_x=torch.from_numpy(pin_offset_x).cuda(), - pin_offset_y=torch.from_numpy(pin_offset_y).cuda(), - pin2node_map=torch.from_numpy(pin2node_map).cuda(), - flat_node2pin_map=torch.from_numpy(flat_node2pin_map).cuda(), - flat_node2pin_start_map=torch.from_numpy(flat_node2pin_start_map).cuda(), - num_physical_nodes=num_physical_nodes, - num_threads=num_threads - ) - result_cuda = custom_cuda.forward(pos_var.cuda()) - custom_cuda_loss = result_cuda.sum() - print("custom_cuda_result = ", result_cuda.data.cpu()) - custom_cuda_loss.backward() - grad_cuda = pos_var.grad.clone() - print("custom_grad_cuda = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-6) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-6, atol=1e-6) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py deleted file mode 100644 index 2963b607..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py +++ /dev/null @@ -1,112 +0,0 @@ -## -# @file pin_utilization_unitest.py -# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import unittest -import torch -import numpy as np -import math - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.pin_utilization import pin_utilization -sys.path.pop() - -class PinUtilizationUnittest(unittest.TestCase): - def test_pin_utilization(self): - # the data of nodes are from unitest/ops/pin_pos_unitest.py - dtype = torch.float32 - - pos = torch.Tensor([[1, 10], [2, 20], [3, 30]]).to(dtype) - node2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - num_movable_nodes = len(node2pin_map) - num_filler_nodes = 1 - num_nodes = num_movable_nodes + num_filler_nodes - - - num_pins = 0 - for pins in node2pin_map: - num_pins += len(pins) - pin2node_map = np.zeros(num_pins, dtype=np.int32) - for node_id, pins in enumerate(node2pin_map): - for pin in pins: - pin2node_map[pin] = node_id - - # construct flat_node2pin_map and flat_node2pin_start_map - # flat nodepin map, length of #pins - flat_node2pin_map = np.zeros(num_pins, dtype=np.int32) - # starting index in nodepin map for each node, length of #nodes+1, the last entry is #pins - flat_node2pin_start_map = np.zeros(len(node2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(node2pin_map)): - flat_node2pin_map[count:count+len(node2pin_map[i])] = node2pin_map[i] - flat_node2pin_start_map[i] = count - count += len(node2pin_map[i]) - flat_node2pin_start_map[len(node2pin_map)] = len(pin2node_map) - flat_node2pin_start_map = torch.from_numpy(flat_node2pin_start_map) - - node_size_x = torch.Tensor([3, 3, 3]).to(dtype) - node_size_y = torch.Tensor([6, 6, 3]).to(dtype) - xl, xh = 0, 8 - yl, yh = 0, 64 - num_bins_x, num_bins_y = 2, 16 - bin_size_x = (xh - xl) / num_bins_x - bin_size_y = (yh - yl) / num_bins_y - - pin_weights = None - unit_pin_capacity = 0.5 - pin_stretch_ratio = math.sqrt(2) - - # test cpu - pin_utilization_op = pin_utilization.PinUtilization( - node_size_x=node_size_x, - node_size_y=node_size_y, - pin_weights=pin_weights, - flat_node2pin_start_map=flat_node2pin_start_map, - xl=xl, - xh=xh, - yl=yl, - yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - num_bins_x=num_bins_x, - num_bins_y=num_bins_y, - unit_pin_capacity=unit_pin_capacity, - pin_stretch_ratio=pin_stretch_ratio, - deterministic_flag=True, - num_threads=1 - ) - - result_cpu = pin_utilization_op.forward(pos.t().contiguous().view(-1)) - print("Test on CPU. pin_utilization map = ", result_cpu) - - if torch.cuda.device_count(): - # test gpu - pin_utilization_op_cuda = pin_utilization.PinUtilization( - node_size_x=node_size_x.cuda(), - node_size_y=node_size_y.cuda(), - pin_weights=pin_weights, - flat_node2pin_start_map=flat_node2pin_start_map.cuda(), - xl=xl, - xh=xh, - yl=yl, - yh=yh, - num_movable_nodes=num_movable_nodes, - num_filler_nodes=num_filler_nodes, - num_bins_x=num_bins_x, - num_bins_y=num_bins_y, - unit_pin_capacity=unit_pin_capacity, - pin_stretch_ratio=pin_stretch_ratio, - deterministic_flag=False, - num_threads=1 - ) - - result_cuda = pin_utilization_op_cuda.forward(pos.t().contiguous().view(-1).cuda()) - print("Test on GPU. pin_utilization map = ", result_cuda) - np.testing.assert_allclose(result_cpu, result_cuda.cpu()) - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py deleted file mode 100644 index 47eea2da..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py +++ /dev/null @@ -1,99 +0,0 @@ -## -# @file rudy_unitest.py -# @author Zixuan Jiang, Jiaqi Gu (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import unittest -import torch -import numpy as np - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.rudy import rudy -sys.path.pop() - -class RudyUnittest(unittest.TestCase): - def test_rudy(self): - # the data of net and pin are from unitest/ops/weighted_average_wirelength_unitest.py - dtype = torch.float32 - pin_pos = torch.Tensor([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]]).to(dtype) - net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - net_weights = torch.Tensor([1, 2]).to(dtype) - - # construct flat_net2pin_map and flat_net2pin_start_map - # flat netpin map, length of #pins - flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) - # starting index in netpin map for each net, length of #nets+1, the last entry is #pins - flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(net2pin_map)): - flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] - flat_net2pin_start_map[i] = count - count += len(net2pin_map[i]) - flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) - - print("flat_net2pin_map = ", flat_net2pin_map) - print("flat_net2pin_start_map = ", flat_net2pin_start_map) - flat_net2pin_map = torch.from_numpy(flat_net2pin_map) - flat_net2pin_start_map = torch.from_numpy(flat_net2pin_start_map) - - # parameters for this test - xl, xh = 0.0, 2.0 - yl, yh = 0.0, 4.0 - num_bins_x = 8 - num_bins_y = 8 - unit_horizontal_capacity = 0.1 - unit_vertical_capacity = 0.2 - - # test cpu - rudy_op = rudy.Rudy( - netpin_start=flat_net2pin_start_map, - flat_netpin=flat_net2pin_map, - net_weights=net_weights, - xl=xl, - xh=xh, - yl=yl, - yh=yh, - num_bins_x=num_bins_x, - num_bins_y=num_bins_y, - unit_horizontal_capacity=unit_horizontal_capacity, - unit_vertical_capacity=unit_vertical_capacity, - deterministic_flag=True, - initial_horizontal_utilization_map=None, - initial_vertical_utilization_map=None, - num_threads=1 - ) - - result_cpu = rudy_op.forward(pin_pos.t().contiguous().view(-1)) - print("Test on CPU. rudy map = ", result_cpu) - - if torch.cuda.device_count(): - # test gpu - rudy_op_cuda = rudy.Rudy( - netpin_start=flat_net2pin_start_map.cuda(), - flat_netpin=flat_net2pin_map.cuda(), - net_weights=net_weights.cuda(), - xl=xl, - xh=xh, - yl=yl, - yh=yh, - num_bins_x=num_bins_x, - num_bins_y=num_bins_y, - unit_horizontal_capacity=unit_horizontal_capacity, - unit_vertical_capacity=unit_vertical_capacity, - deterministic_flag=False, - initial_horizontal_utilization_map=None, - initial_vertical_utilization_map=None, - num_threads=1 - ) - - result_cuda = rudy_op_cuda.forward(pin_pos.t().contiguous().view(-1).cuda()) - print("Test on GPU. rudy map = ", result_cuda) - - np.testing.assert_allclose(result_cpu, result_cuda.cpu()) - - -if __name__ == '__main__': - unittest.main() diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py deleted file mode 100644 index 70b86012..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py +++ /dev/null @@ -1,433 +0,0 @@ -## -# @file weighted_average_wirelength_unitest.py -# @author Yibo Lin (DREAMPlace) Rachel Selina (DREAMPlaceFPGA) -# @date Mar 2024 -# - -import os -import sys -import time -import numpy as np -import unittest -#import pickle -import gzip -if sys.version_info[0] < 3: - import cPickle as pickle -else: - import _pickle as pickle - -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -from dreamplacefpga.ops.weighted_average_wirelength import weighted_average_wirelength -sys.path.pop() - -import pdb - -import torch -from torch.autograd import Function, Variable - -def unsorted_segment_sum(pin_x, pin2net_map, num_nets): - result = np.zeros(num_nets, dtype=pin_x.dtype) - for i in range(len(pin2net_map)): - result[pin2net_map[i]] += pin_x[i] - return result - -def build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights): - # wirelength cost - # weighted-average - - # temporily store exp(x) - scaled_pin_x = pin_x/gamma - scaled_pin_y = pin_y/gamma - - exp_pin_x = np.exp(scaled_pin_x) - exp_pin_y = np.exp(scaled_pin_y) - nexp_pin_x = np.exp(-scaled_pin_x) - nexp_pin_y = np.exp(-scaled_pin_y) - - # sum of exp(x) - sum_exp_pin_x = unsorted_segment_sum(exp_pin_x, pin2net_map, len(net2pin_map)) - sum_exp_pin_y = unsorted_segment_sum(exp_pin_y, pin2net_map, len(net2pin_map)) - sum_nexp_pin_x = unsorted_segment_sum(nexp_pin_x, pin2net_map, len(net2pin_map)) - sum_nexp_pin_y = unsorted_segment_sum(nexp_pin_y, pin2net_map, len(net2pin_map)) - - # sum of x*exp(x) - sum_x_exp_pin_x = unsorted_segment_sum(pin_x*exp_pin_x, pin2net_map, len(net2pin_map)) - sum_y_exp_pin_y = unsorted_segment_sum(pin_y*exp_pin_y, pin2net_map, len(net2pin_map)) - sum_x_nexp_pin_x = unsorted_segment_sum(pin_x*nexp_pin_x, pin2net_map, len(net2pin_map)) - sum_y_nexp_pin_y = unsorted_segment_sum(pin_y*nexp_pin_y, pin2net_map, len(net2pin_map)) - - sum_exp_pin_x = sum_exp_pin_x - sum_x_exp_pin_x = sum_x_exp_pin_x - - wl = sum_x_exp_pin_x / sum_exp_pin_x - sum_x_nexp_pin_x / sum_nexp_pin_x \ - + sum_y_exp_pin_y / sum_exp_pin_y - sum_y_nexp_pin_y / sum_nexp_pin_y - - for i in range(len(net2pin_map)): - if len(net2pin_map[i]) >= ignore_net_degree: - wl[i] = 0 - - wl *= net_weights - - wirelength = np.sum(wl) - - return wirelength - -class WeightedAverageWirelengthOpTest(unittest.TestCase): - def test_weighted_average_wirelength_random(self): - dtype = torch.float32 - #pin_pos = np.array([[0.0, 0.0], [1.0, 2.0], [1.5, 0.2], [0.5, 3.1], [0.6, 1.1]], dtype=np.float32) - - #net2pin_map = np.array([np.array([0, 4]), np.array([1, 2, 3])]) - #pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) - #for net_id, pins in enumerate(net2pin_map): - # for pin in pins: - # pin2net_map[pin] = net_id - #net_weights = np.array([1, 1], dtype=np.float32) - - #pin_x = pin_pos[:, 0] - #pin_y = pin_pos[:, 1] - #gamma = 0.5 - #ignore_net_degree = 4 - - pin_pos = np.array([[103.7478, 127.4158], [103.5688, 127.7118], [103.6268, 127.5448], [103.804, 127.548], [103.462, 127.399], [103.502, 127.211], [103.4118, 127.4038], [103.6178, 127.8198]], dtype=np.float32) - - net2pin_map = np.array([np.array([0, 1, 2, 3]), np.array([4, 5, 6, 7])]) - pin2net_map = np.zeros(len(pin_pos), dtype=np.int32) - for net_id, pins in enumerate(net2pin_map): - for pin in pins: - pin2net_map[pin] = net_id - net_weights = np.array([1, 1], dtype=np.float32) - - pin_x = pin_pos[:, 0] - pin_y = pin_pos[:, 1] - gamma = 31.6409 - ignore_net_degree = 8 - - pin_mask = np.zeros(len(pin2net_map), dtype=np.uint8) - - #elfPlace Impl - xy_max = [pin_pos[net2pin_map[i]].max(axis=0) for i in range(2)] - xy_min = [pin_pos[net2pin_map[i]].min(axis=0) for i in range(2)] - - expNegX = [] - expPosX = [] - expNegY = [] - expPosY = [] - xexpNegX = [] - xexpPosX = [] - yexpNegY = [] - yexpPosY = [] - wl = [] - for i, pin in enumerate(net2pin_map): - expNegX.append(np.e**(-(pin_pos[pin][:,0] - xy_min[i][0])/gamma)) - expPosX.append(np.e**((pin_pos[pin][:,0] - xy_max[i][0])/gamma)) - expNegY.append(np.e**(-(pin_pos[pin][:,1] - xy_min[i][1])/gamma)) - expPosY.append(np.e**((pin_pos[pin][:,1] - xy_max[i][1])/gamma)) - xexpNegX.append(pin_pos[pin][:,0] * expNegX[i]) - xexpPosX.append(pin_pos[pin][:,0] * expPosX[i]) - yexpNegY.append(pin_pos[pin][:,1] * expNegY[i]) - yexpPosY.append(pin_pos[pin][:,1] * expPosY[i]) - - #pdb.set_trace() - for i in range(2): - wl.append(xexpPosX[i].sum()/expPosX[i].sum() - xexpNegX[i].sum()/expNegX[i].sum() + yexpPosY[i].sum()/expPosY[i].sum() - yexpNegY[i].sum()/expNegY[i].sum()) - - totalWl = np.sum([np.sum(i) for i in wl]) - print("elfPlace WL = ", totalWl) - - - # net mask - net_mask = np.ones(len(net2pin_map), dtype=np.uint8) - for i in range(len(net2pin_map)): - if len(net2pin_map[i]) >= ignore_net_degree: - net_mask[i] = 0 - - net_bounding_box_min = np.ones_like(net_mask, dtype=np.float32) - net_bounding_box_min *= pin_pos.max() - net_bounding_box_max = np.zeros_like(net_mask, dtype=np.float32) - # construct flat_net2pin_map and flat_net2pin_start_map - # flat netpin map, length of #pins - flat_net2pin_map = np.zeros(len(pin_pos), dtype=np.int32) - # starting index in netpin map for each net, length of #nets+1, the last entry is #pins - flat_net2pin_start_map = np.zeros(len(net2pin_map)+1, dtype=np.int32) - count = 0 - for i in range(len(net2pin_map)): - flat_net2pin_map[count:count+len(net2pin_map[i])] = net2pin_map[i] - flat_net2pin_start_map[i] = count - count += len(net2pin_map[i]) - flat_net2pin_start_map[len(net2pin_map)] = len(pin_pos) - - print("flat_net2pin_map = ", flat_net2pin_map) - print("flat_net2pin_start_map = ", flat_net2pin_start_map) - - golden_value = np.array([build_wirelength(pin_x, pin_y, pin2net_map, net2pin_map, gamma, ignore_net_degree, net_weights)]) - print("golden_value = ", golden_value) - - print(np.transpose(pin_pos)) - pin_pos_var = Variable(torch.tensor(np.transpose(pin_pos), dtype=dtype).reshape([-1]), requires_grad=True) - #pin_pos_var = torch.nn.Parameter(torch.from_numpy(np.transpose(pin_pos)).reshape([-1])) - print(pin_pos_var) - # clone is very important, because the custom op cannot deep copy the data - - # test cpu net-by-net - custom = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=torch.from_numpy(flat_net2pin_map), - netpin_start=torch.from_numpy(flat_net2pin_start_map), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='net-by-net' - ) - result = custom.forward(pin_pos_var) - print("custom = ", result) - result.backward() - grad = pin_pos_var.grad.clone() - print("custom_grad = ", grad) - - np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) - - # test cpu atomic - pin_pos_var.grad.zero_() - custom = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='atomic' - ) - result = custom.forward(pin_pos_var) - print("custom_cpu_result atomic = ", result.data) - result.backward() - grad_atomic = pin_pos_var.grad.clone() - print("custom_grad_cpu atomic = ", grad_atomic.data) - - np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) - np.testing.assert_allclose(grad_atomic.data.numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) - - # test cpu merged - pin_pos_var.grad.zero_() - custom = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=torch.from_numpy(net_weights), - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='merged' - ) - result = custom.forward(pin_pos_var) - print("custom_cpu_result merged = ", result.data) - result.backward() - grad_merged = pin_pos_var.grad.clone() - print("custom_grad_cpu merged = ", grad_merged.data) - - np.testing.assert_allclose(result.data.numpy(), golden_value, atol=1e-4) - np.testing.assert_allclose(grad_merged.data.numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) - - # test gpu - if torch.cuda.device_count(): - pin_pos_var.grad.zero_() - custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='net-by-net' - ) - result_cuda = custom_cuda.forward(pin_pos_var.cuda()) - print("custom_cuda_result = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pin_pos_var.grad.clone() - print("custom_grad_cuda = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-6) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-6, atol=1e-6) - - # test gpu atomic - if torch.cuda.device_count(): - pin_pos_var.grad.zero_() - custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='atomic' - ) - result_cuda = custom_cuda.forward(pin_pos_var.cuda()) - print("custom_cuda_result atomic = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pin_pos_var.grad.clone() - print("custom_grad_cuda atomic = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-4) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) - - # test gpu merged - if torch.cuda.device_count(): - pin_pos_var.grad.zero_() - custom_cuda = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=torch.from_numpy(net_weights).cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - num_threads=1, - algorithm='merged' - ) - result_cuda = custom_cuda.forward(pin_pos_var.cuda()) - print("custom_cuda_result merged = ", result_cuda.data.cpu()) - result_cuda.backward() - grad_cuda = pin_pos_var.grad.clone() - print("custom_grad_cuda merged = ", grad_cuda.data.cpu()) - - np.testing.assert_allclose(result_cuda.data.cpu().numpy(), golden_value, atol=1e-4) - np.testing.assert_allclose(grad_cuda.data.cpu().numpy(), grad.data.numpy(), rtol=1e-4, atol=1e-4) - -def eval_runtime(design): - ## e.g,. adaptec1_wirelength.pklz - #with gzip.open(design, "rb") as f: - # flat_net2pin_map, flat_net2pin_start_map, pin2net_map, net_mask, pin_mask, gamma = pickle.load(f) - dtype = torch.float64 - net_weights = torch.Tensor() - pin_pos_var = Variable(torch.empty(len(pin2net_map)*2, dtype=dtype).uniform_(0, 1000), requires_grad=True) - custom_net_by_net_cpu = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=net_weights, - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - algorithm='net-by-net', - num_threads=10 - ) - custom_atomic_cpu = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)), - pin2net_map=torch.from_numpy(pin2net_map), - net_weights=net_weights, - net_mask=torch.from_numpy(net_mask), - pin_mask=torch.from_numpy(pin_mask), - gamma=torch.tensor(gamma, dtype=dtype), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - algorithm='atomic', - num_threads=10 - ) - custom_net_by_net = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=net_weights.cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - algorithm='net-by-net' - ) - custom_atomic = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=net_weights.cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - algorithm='atomic' - ) - custom_sparse = weighted_average_wirelength.WeightedAverageWirelength( - flat_netpin=Variable(torch.from_numpy(flat_net2pin_map)).cuda(), - netpin_start=Variable(torch.from_numpy(flat_net2pin_start_map)).cuda(), - pin2net_map=torch.from_numpy(pin2net_map).cuda(), - net_weights=net_weights.cuda(), - net_mask=torch.from_numpy(net_mask).cuda(), - pin_mask=torch.from_numpy(pin_mask).cuda(), - gamma=torch.tensor(gamma, dtype=dtype).cuda(), - net_bounding_box_min=torch.from_numpy(net_bounding_box_min), - net_bounding_box_max=torch.from_numpy(net_bounding_box_max), - algorithm='sparse' - ) - - torch.cuda.synchronize() - iters = 100 - tt = time.time() - for i in range(iters): - result = custom_net_by_net_cpu.forward(pin_pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_net_by_net cpu takes %.3f ms" % ((time.time()-tt)/iters*1000)) - - tt = time.time() - for i in range(iters): - result = custom_atomic_cpu.forward(pin_pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_atomic cpu takes %.3f ms" % ((time.time()-tt)/iters*1000)) - - pin_pos_var = pin_pos_var.cuda() - torch.cuda.synchronize() - tt = time.time() - for i in range(iters): - result = custom_net_by_net.forward(pin_pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_net_by_net takes %.3f ms" % ((time.time()-tt)/iters*1000)) - - tt = time.time() - for i in range(iters): - result = custom_atomic.forward(pin_pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_atomic takes %.3f ms" % ((time.time()-tt)/iters*1000)) - - tt = time.time() - for i in range(iters): - result = custom_sparse.forward(pin_pos_var) - result.backward() - torch.cuda.synchronize() - print("custom_sparse takes %.3f ms" % ((time.time()-tt)/iters*1000)) - - -if __name__ == '__main__': - - if len(sys.argv) < 2: - unittest.main() - else: - design = sys.argv[1] - eval_runtime(design) From 85fb143d6bb864ec22fe63fb3fc289803f9d212c Mon Sep 17 00:00:00 2001 From: y-ji24 Date: Tue, 21 Jul 2026 15:41:38 +0800 Subject: [PATCH 4/4] Address PR review: restrict EVOLVE-BLOCK to placement algorithm only, add boundary validation - Move EVOLVE-BLOCK to wrap only the placement algorithm function (lines 119-248) - Keep CLI entry point, argument parsing, and Bookshelf I/O outside the block as read-only - Add validate_evolve_boundary() to evaluator with protected function checks - Remove .lc references and baseline/aug-elfPlace/ path from Task.md and Task_zh-CN.md - Add solution.pl to .gitignore - Clean up README dependencies (remove scipy) --- .gitignore | 1 + .../FPGA/FPGAPlacementOptimization/README.md | 10 +- .../FPGAPlacementOptimization/README_zh-CN.md | 4 +- .../FPGA/FPGAPlacementOptimization/Task.md | 27 ++- .../FPGAPlacementOptimization/Task_zh-CN.md | 14 +- .../FPGAPlacementOptimization/scripts/init.py | 104 +++++----- .../verification/evaluator.py | 192 ++++++++++++------ 7 files changed, 223 insertions(+), 129 deletions(-) diff --git a/.gitignore b/.gitignore index bf3b45cf..d308f52b 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ debug-*.log **/outputs/ **/artifacts/ **/last_eval.json +**/solution.pl diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/README.md b/benchmarks/FPGA/FPGAPlacementOptimization/README.md index b61d0b8b..768166d1 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/README.md +++ b/benchmarks/FPGA/FPGAPlacementOptimization/README.md @@ -38,9 +38,10 @@ The only constraints are: 2. The program must produce `solution.pl` in the same format. 3. The placement must satisfy the three legality gates (G1, G2, G3). -Everything inside the `EVOLVE-BLOCK` in `scripts/init.py` — including parsers, -data structures, and the placement strategy — may be modified, replaced, or -removed. +Everything inside the `EVOLVE-BLOCK` in `scripts/init.py` — the placement +algorithm functions — may be modified, replaced, or removed. The benchmark +parsers (.nodes, .pl, .scl), output writer, and CLI entry point are +outside the EVOLVE-BLOCK and are **frozen**. ## Baseline @@ -171,7 +172,7 @@ FPGAPlacementOptimization/ ### 1. Dependencies ```bash -pip install numpy scipy +pip install numpy ``` ### 2. Run the Initial Solver @@ -233,3 +234,4 @@ python -m frontier_eval task=unified task.benchmark=FPGA/FPGAPlacementOptimizati + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md b/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md index 8df3bdb4..ac14a5c5 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md +++ b/benchmarks/FPGA/FPGAPlacementOptimization/README_zh-CN.md @@ -33,7 +33,7 @@ agent 拥有**完全自由**来重新设计布局算法。唯一约束是: 2. 程序必须按相同格式输出 solution.pl。 3. 布局必须满足三个合法性门(G1, G2, G3)。 -scripts/init.py 中 EVOLVE-BLOCK 内的所有内容——包括解析器、数据结构和布局策略——均可修改、替换或删除。 +scripts/init.py 中 EVOLVE-BLOCK内的代码——布局算法函数——可以修改、替换或删除。benchmark 解析器(.nodes、.pl、.scl)、输出写入器和 CLI 入口点位于 EVOLVE-BLOCK 之外,是冻结的。 ## 基线(Baseline) @@ -101,7 +101,7 @@ FPGAPlacementOptimization/ ### 1. 安装依赖 ` -pip install numpy scipy +pip install numpy ` ### 2. 运行初始求解器 diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/Task.md b/benchmarks/FPGA/FPGAPlacementOptimization/Task.md index 1e08e69a..73feedcc 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/Task.md +++ b/benchmarks/FPGA/FPGAPlacementOptimization/Task.md @@ -4,7 +4,7 @@ Improve the placement algorithm implemented in `scripts/init.py`. This program reads an FPGA benchmark circuit (in ISPD Bookshelf format) and assigns each logic instance (LUT, FF, DSP, BRAM) to a legal site on the FPGA grid. The goal is to **minimize half-perimeter wirelength (HPWL)** while satisfying all FPGA-specific legality constraints. -This benchmark is based on the **ISPD 2016 FPGA Placement Contest** benchmarks and the **FPGA Bookshelf format** (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`, `.lc`). +This benchmark is based on the **ISPD 2016 FPGA Placement Contest** benchmarks and the **FPGA Bookshelf format** (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`). The task: **improve the placement algorithm in `scripts/init.py` to produce lower-wirelength placements.** The file contains a naive row-scan placer that produces a legal but high-wirelength placement. An improved algorithm should reduce HPWL while maintaining legality. @@ -14,12 +14,18 @@ This benchmark intentionally does **not** prescribe any specific placement algor Possible approaches include, but are not limited to: -- **Constructive placement** place instances greedily using heuristics (row-scan, quadratic assignment, or partitioning-based methods). -- **Analytical placement** formulate placement as a continuous optimization problem with differentiable wirelength proxies and density penalties. -- **Simulated annealing** start from an initial placement and iteratively perturb and improve. -- **Reinforcement learning** train a policy to place instances sequentially. -- **Integer programming** formulate legality and wirelength as an exact optimization problem. -- **Hybrid approaches** combine multiple strategies (e.g., analytical global placement followed by legalization). +- **Constructive placement** �� place instances greedily using heuristics (row-scan, quadratic assignment, or partitioning-based methods). +- **Analytical placement** �� formulate placement as a continuous optimization problem with differentiable wirelength proxies and density penalties. +- **Simulated annealing** �� start from an initial placement and iteratively perturb and improve. +- **Reinforcement learning** �� train a policy to place instances sequentially. +- **Integer programming** �� formulate legality and wirelength as an exact optimization problem. +- **Hybrid approaches** �� combine multiple strategies (e.g., analytical global placement followed by legalization). + +The EVOLVE-BLOCK in scripts/init.py wraps only the placement algorithm function. +The benchmark parsers (.nodes, .pl, .scl), output writer, and CLI entry point +are outside the EVOLVE-BLOCK and are **frozen**. The evaluator validates this +boundary at runtime — if a candidate modifies any frozen code, it receives an +invalid score. The only requirements are: @@ -27,7 +33,7 @@ The only requirements are: 2. The program must produce `solution.pl` in the Bookshelf format described in Section 7 (Submission Contract). 3. The placement must satisfy all three legality gates (G1, G2, G3) described in Section 6 (Constraints). -The evaluator judges **only the generated placement quality and legality**, not the internal implementation. An agent that replaces the entire placement algorithm with a completely different approach is treated identically to one that makes incremental modifications to the row-scan placer both are scored solely by the resulting HPWL and legality of the output placement. +The evaluator judges **only the generated placement quality and legality**, not the internal implementation. An agent that replaces the entire placement algorithm with a completely different approach is treated identically to one that makes incremental modifications to the row-scan placer �� both are scored solely by the resulting HPWL and legality of the output placement. ## 3. Input Format (ISPD Bookshelf for FPGA) @@ -42,7 +48,6 @@ Benchmark designs use the **Bookshelf format** extended for FPGA placement. Each | SCL | `.scl` | Site/clock layout: site definitions, resources per site, and site map grid | | Library | `.lib` | Cell library: pin definitions, directions (INPUT/OUTPUT), clock/control attributes | | Weights | `.wts` | Net weights (typically all 1.0) | -| Legality Constraints | `.lc` | Architecture-specific legality constraint parameters | The `.aux` file is the entry point: @@ -151,7 +156,7 @@ Carry-chain instances (CARRY4/CARRY8) must: Your candidate program (`scripts/init.py`) must: -1. Read the benchmark input files (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`, `.lc`) +1. Read the benchmark input files (`.nodes`, `.nets`, `.pl`, `.scl`, `.lib`) 2. Compute a placement for all movable instances 3. Write a `.pl` file to the specified output path @@ -226,7 +231,7 @@ python verification/evaluator.py scripts/init.py ## 11. References - **ISPD 2016 FPGA Placement Contest**: http://www.ispd.cc/contests/16/FAQ.html -- **aug-elfPlace**: Rachel Selina Rajarathnam et al., "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA) (reference implementation available at the repository root `baseline/aug-elfPlace/`). +- **aug-elfPlace**: Rachel Selina Rajarathnam et al., "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA) (reference implementation). - **ISPD 2016 Benchmark Format**: `references/README` describes the FPGA Bookshelf format extensions. - **Benchmark data**: references/ (fpga-example1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md b/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md index 5eb5e373..72caf551 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md +++ b/benchmarks/FPGA/FPGAPlacementOptimization/Task_zh-CN.md @@ -4,7 +4,7 @@ 改进 `scripts/init.py` 中实现的布局算法。该程序读取 FPGA benchmark 电路(ISPD Bookshelf 格式),将每个逻辑实例(LUT、FF、DSP、BRAM)分配到 FPGA 网格上的合法站点。目标是**最小化半周长线长(Half-Perimeter Wirelength, HPWL)**,同时满足所有 FPGA 特定的合法性约束。 -本 benchmark 基于 **ISPD 2016 FPGA Placement Contest** 基准测试集和 **FPGA Bookshelf 格式**(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`、`.lc`)。 +本 benchmark 基于 **ISPD 2016 FPGA Placement Contest** 基准测试集和 **FPGA Bookshelf 格式**(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`)。 任务:**改进 `scripts/init.py` 中的布局算法以产生更低线长的布局。** 该文件包含一个朴素的逐行扫描布局器(row-scan placer),可生成合法但线长较高的布局。改进后的算法应在保持合法性的前提下降低 HPWL。 @@ -21,6 +21,12 @@ - **整数规划(Integer programming)** — 将合法性和线长表述为精确优化问题。 - **混合方法(Hybrid approaches)** — 结合多种策略(例如,解析全局布局后接合法化)。 + +scripts/init.py 中的 EVOLVE-BLOCK 仅包含布局算法函数。 +benchmark 解析器(.nodes、.pl、.scl)、输出写入器和 CLI 入口点 +位于 EVOLVE-BLOCK 之外,是“冻结”的。评测器在运行时验证该边界—— +如果候选程序修改了任何冻结代码,它将获得无效分数。 + 唯一要求: 1. 程序必须接受相同的命令行接口(`--nodes`、`--pl`、`--scl`、`--output`)。 @@ -42,7 +48,7 @@ Benchmark 设计使用针对 FPGA 布局扩展的 **Bookshelf 格式**。每个 | SCL | `.scl` | 站点/时钟布局:站点定义、每站点资源及站点地图网格 | | Library | `.lib` | 单元库:引脚定义、方向(INPUT/OUTPUT)、时钟/控制属性 | | Weights | `.wts` | 网权重(通常均为 1.0) | -| Legality Constraints | `.lc` | 架构特定的合法性约束参数 | + 架构特定的合法性约束参数 | `.aux` 文件是入口点: @@ -151,7 +157,7 @@ HPWL 根据**最终合法布局**计算,以每个实例所在站点的中心 候选程序(`scripts/init.py`)必须: -1. 读取 benchmark 输入文件(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`、`.lc`) +1. 读取 benchmark 输入文件(`.nodes`、`.nets`、`.pl`、`.scl`、`.lib`) 2. 为所有可移动实例计算布局 3. 将 `.pl` 文件写入指定输出路径 @@ -226,7 +232,7 @@ python verification/evaluator.py scripts/init.py ## 11. 参考文献(References) - **ISPD 2016 FPGA Placement Contest**:http://www.ispd.cc/contests/16/FAQ.html -- **aug-elfPlace**:Rachel Selina Rajarathnam 等人, "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA)(参考实现,位于仓库根目录 `baseline/aug-elfPlace/`) +- **aug-elfPlace**:Rachel Selina Rajarathnam 等人, "Better Together: Combining Analytical and Annealing Methods for FPGA Placement," FPL 2024. [GitHub](https://github.com/rachelselinar/DREAMPlaceFPGA)(参考实现,位于仓库根目录 `aug-elfPlace` (separate repository)) - **ISPD 2016 Benchmark 格式**:`references/README` 描述了 FPGA Bookshelf 格式的扩展内容。 - **Benchmark 数据**:references/(fpga-example1) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py b/benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py index 574c7d6f..221297fd 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py +++ b/benchmarks/FPGA/FPGAPlacementOptimization/scripts/init.py @@ -1,5 +1,4 @@ -# EVOLVE-BLOCK-START -"""FPGA Placement Optimization - Initial Solver. +"""FPGA Placement Optimization - Initial Solver. Pipeline -------- @@ -16,10 +15,6 @@ The agent is expected to evolve the placement strategy (step 2). All benchmark I/O contracts (steps 1 and 3) should remain unchanged. - -- ALLOWED TO MODIFY: Everything inside EVOLVE-BLOCK. -- DO NOT MODIFY: The command-line interface, the output file format - (solution.pl), and the contract with evaluator.py. """ from __future__ import annotations @@ -31,9 +26,9 @@ from typing import Any -# ?????????????????????????????????????????????????????????????????????? -# BENCHMARK PARSING -# ?????????????????????????????????????????????????????????????????????? +# ============================================================================ +# READ-ONLY: Benchmark Parsing (frozen -- do not modify) +# ============================================================================ def parse_nodes(path: str) -> list[tuple[str, str]]: """Parse .nodes file. Returns list of (instance_name, cell_type).""" @@ -69,7 +64,7 @@ def parse_scl(path: str) -> dict[str, Any]: result: dict[str, Any] = { "site_capacities": {}, "resources": {}, - "site_map": {}, # (col, row) -> site_type + "site_map": {}, "num_cols": 0, "num_rows": 0, } @@ -93,10 +88,6 @@ def parse_scl(path: str) -> dict[str, Any]: cap = int(parts[1]) if parts[1].isdigit() else 1 result["site_capacities"][current_site][parts[0]] = cap - elif line.startswith("RESOURCES"): - pass - elif line == "END RESOURCES": - pass elif line.startswith("SITEMAP"): parts = line.split() if len(parts) >= 3: @@ -111,8 +102,6 @@ def parse_scl(path: str) -> dict[str, Any]: col, row, stype = int(parts[0]), int(parts[1]), parts[2] result["site_map"][(col, row)] = stype - # Build reverse resource map: cell_type -> site_type - # (simplified mapping derived from standard FPGA architecture) result["cell_to_site"] = { "LUT1": "SLICE", "LUT2": "SLICE", "LUT3": "SLICE", "LUT4": "SLICE", "LUT5": "SLICE", "LUT6": "SLICE", @@ -126,61 +115,69 @@ def parse_scl(path: str) -> dict[str, Any]: return result -# ?????????????????????????????????????????????????????????????????????? -# INITIAL PLACER: Row-scan -# ?????????????????????????????????????????????????????????????????????? +# ============================================================================ +# EVOLVE-BLOCK-START +# The agent may redesign or replace everything below this line. +# ============================================================================ def is_slice_cell(cell_type: str) -> bool: - return cell_type.startswith("LUT") or cell_type == "FDRE" or cell_type.startswith("CARRY") + """Check if a cell type belongs to a SLICE site.""" + return (cell_type.startswith("LUT") or cell_type in ("FDRE",) + or cell_type.startswith("CARRY")) -def get_site_type(cell_type: str, cell_to_site: dict[str, str]) -> str: - """Determine which site type a cell requires.""" - if is_slice_cell(cell_type): - return "SLICE" - return cell_to_site.get(cell_type, "SLICE") +def get_site_type(cell_type: str, cell_to_site: dict[str, str]) -> str | None: + """Return the site type required for a given cell type.""" + return cell_to_site.get(cell_type) def row_scan_place( instances: list[tuple[str, str]], - fixed_placements: dict[str, tuple[int, int, int]], + fixed_pl: dict[str, tuple[int, int, int]], scl_data: dict[str, Any], ) -> dict[str, tuple[int, int, int]]: - """Assign each movable instance to a legal site via row-scan. - - Algorithm: - 1. Separate instances by site type (SLICE, DSP, BRAM). - 2. For each site type, scan the site map left-to-right, bottom-to-top. - 3. Assign instances to sites, respecting per-site capacity. - 4. Fixed instances are placed at their input locations. + """Naive row-scan placement: assign each movable instance to the first + available legal site. Guarantees legality by construction. + + Parameters + ---------- + instances : list of (name, cell_type) from .nodes + fixed_pl : dict of fixed placements from .pl + scl_data : parsed .scl data + + Returns + ------- + placements : dict name -> (x, y, z) """ - placements: dict[str, tuple[int, int, int]] = dict(fixed_placements) - cell_to_site = scl_data["cell_to_site"] - site_map = scl_data["site_map"] + placements: dict[str, tuple[int, int, int]] = {} capacities = scl_data["site_capacities"] + site_map = scl_data["site_map"] + cell_to_site = scl_data["cell_to_site"] + + # Collect fixed placements + for name, (x, y, z) in fixed_pl.items(): + placements[name] = (x, y, z) # Group movable instances by required site type - groups: dict[str, list[str]] = {"SLICE": [], "DSP": [], "BRAM": []} + groups: dict[str, list[tuple[str, str]]] = {} for name, ctype in instances: - if name in fixed_placements: - continue # skip fixed IOs + if name in placements: + continue # already fixed stype = get_site_type(ctype, cell_to_site) + if stype is None: + continue if stype not in groups: groups[stype] = [] groups[stype].append((name, ctype)) - # Collect sites by type + # Collect sites of each type (sorted by column, then row for deterministic order) sites_by_type: dict[str, list[tuple[int, int]]] = {} - for (col, row), stype in site_map.items(): + for (col, row), stype in sorted(site_map.items()): if stype not in sites_by_type: sites_by_type[stype] = [] sites_by_type[stype].append((col, row)) - # Sort sites by column, then row (left-to-right, bottom-to-top) - for stype in sites_by_type: - sites_by_type[stype].sort(key=lambda p: (p[0], p[1])) - - # Per-site resource usage + # Per-site usage tracker site_usage: dict[tuple[int, int], dict[str, int]] = {} def get_usage(site_key: tuple[int, int]) -> dict[str, int]: @@ -217,7 +214,7 @@ def place_group( usage = get_usage(site) if usage[usage_key] < max_per_site: usage[usage_key] += 1 - z = usage[usage_key] - 1 # BEL index 0-based + z = usage[usage_key] - 1 placements[name] = (site[0], site[1], z) bel_idx += 1 break @@ -247,6 +244,16 @@ def place_group( return placements +# ============================================================================ +# EVOLVE-BLOCK-END +# The agent must NOT modify anything below this line. +# ============================================================================ + + +# ============================================================================ +# READ-ONLY: Output Writer and CLI Entry Point (frozen -- do not modify) +# ============================================================================ + def write_pl(placements: dict[str, tuple[int, int, int]], output_path: str) -> None: """Write placements to a .pl file in Bookshelf format.""" with open(output_path, "w") as f: @@ -257,7 +264,7 @@ def write_pl(placements: dict[str, tuple[int, int, int]], output_path: str) -> N def main() -> None: - parser = argparse.ArgumentParser(description="FPGA Placement Optimization ? initial solver") + parser = argparse.ArgumentParser(description="FPGA Placement Optimization -- initial solver") parser.add_argument("--nodes", default=None, help="Path to .nodes file") parser.add_argument("--pl", default=None, help="Path to input .pl file (fixed instances)") parser.add_argument("--scl", default=None, help="Path to .scl file") @@ -311,4 +318,3 @@ def main() -> None: if __name__ == "__main__": main() -# EVOLVE-BLOCK-END diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py b/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py index d04381a6..191ddaa3 100644 --- a/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py +++ b/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py @@ -1,11 +1,12 @@ -"""Evaluator for FPGA Placement Optimization. +"""Evaluator for FPGA Placement Optimization. This evaluator: 1. Runs the candidate program (scripts/init.py) to produce solution.pl -2. Parses the output solution.pl -3. Checks legality gates (G1: site-type, G2: capacity, G3: carry-chain) -4. Computes HPWL -5. Returns metrics dict with combined_score +2. Validates EVOLVE-BLOCK boundary integrity +3. Parses the output solution.pl +4. Checks legality gates (G1: site-type, G2: capacity, G3: carry-chain) +5. Computes HPWL +6. Returns metrics dict with combined_score Benchmark selection: Default: references/design.* (fpga-example1, fast smoke test) @@ -49,6 +50,62 @@ # Known ISPD 2016 benchmark designs ISPD2016_DESIGNS = {f"FPGA{i:02d}" for i in range(1, 13)} +# Protected function names that must remain OUTSIDE the EVOLVE-BLOCK +_PROTECTED_FUNCTIONS = [ + "def parse_nodes", + "def parse_pl", + "def parse_scl", + "def write_pl", + "def main()", +] + + +def validate_evolve_boundary(program_path: str) -> tuple[bool, str]: + """Verify that critical read-only code is OUTSIDE the EVOLVE-BLOCK. + + The EVOLVE-BLOCK should only wrap the placement algorithm functions. + Parser functions, output writer, and CLI entry point must remain + outside the block to preserve the benchmark contract. + + Returns (passes, error_message). + """ + try: + with open(program_path, "r", encoding="utf-8") as f: + lines = f.readlines() + except Exception as e: + return False, f"cannot read candidate program: {e}" + + # Locate EVOLVE-BLOCK boundaries + start_line = None + end_line = None + for i, line in enumerate(lines): + if "# EVOLVE-BLOCK-START" in line: + start_line = i # 0-indexed + if "# EVOLVE-BLOCK-END" in line: + end_line = i + + if start_line is None: + return False, "EVOLVE-BLOCK-START marker not found in candidate program" + if end_line is None: + return False, "EVOLVE-BLOCK-END marker not found in candidate program" + if end_line <= start_line: + return False, "EVOLVE-BLOCK-END appears before EVOLVE-BLOCK-START" + + # Check that each protected function is OUTSIDE the block + for line_num, line_text in enumerate(lines): + stripped = line_text.strip() + for pattern in _PROTECTED_FUNCTIONS: + if stripped.startswith(pattern): + if start_line <= line_num <= end_line: + return False, ( + f"protected function '{pattern}' found inside EVOLVE-BLOCK " + f"(line {line_num + 1}). This function is part of the frozen " + f"benchmark contract and must remain outside the EVOLVE-BLOCK." + ) + break # matched one pattern, move to next line + + return True, "" + def _find_repo_root(start: Path | None = None) -> Path: if "FRONTIER_ENGINEERING_ROOT" in os.environ: @@ -104,6 +161,8 @@ def _resolve_benchmark_dir(repo_root: Path, benchmark: str | None) -> Path: ) raise FileNotFoundError(msg) return design_dir + + def _tail(text: str, limit: int = 8000) -> str: return text if len(text) <= limit else text[-limit:] @@ -119,11 +178,12 @@ def _truncate_middle(text: str, limit: int = 200_000) -> str: def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path | None = None) -> Any: """Full evaluation pipeline. - 1. Run candidate program to produce solution.pl - 2. Parse benchmark files and candidate placement - 3. Check legality gates (G1, G2, G3) - 4. Compute HPWL - 5. Return metrics dict + 1. Validate EVOLVE-BLOCK boundary integrity + 2. Run candidate program to produce solution.pl + 3. Parse benchmark files and candidate placement + 4. Check legality gates (G1, G2, G3) + 5. Compute HPWL + 6. Return metrics dict Args: program_path: Path to the candidate program (scripts/init.py). @@ -132,57 +192,64 @@ def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path repo_root: Repository root. Auto-detected if not provided. """ start = time.time() - repo_root = _find_repo_root() if repo_root is None else repo_root.expanduser().resolve() - program_path_resolved = str(Path(program_path).expanduser().resolve()) - - # Resolve benchmark directory + repo_root = _find_repo_root() if repo_root is None else repo_root benchmark_dir = _resolve_benchmark_dir(repo_root, benchmark) - benchmark_name = benchmark or "fpga-example1" - work_dir = Path(tempfile.mkdtemp(prefix="fe_fpga_")).resolve() + # Benchmark input files + nodes_path = benchmark_dir / "design.nodes" + nets_path = benchmark_dir / "design.nets" + pl_path = benchmark_dir / "design.pl" + scl_path = benchmark_dir / "design.scl" + artifacts: dict[str, str] = {} + metrics: dict[str, float] = {} + + # ------------------------------------------------------------------ + # Step 0: Validate EVOLVE-BLOCK boundary + # ------------------------------------------------------------------ + boundary_ok, boundary_msg = validate_evolve_boundary(program_path) + if not boundary_ok: + artifacts["evolve_boundary_error"] = boundary_msg + metrics["gate_site_type"] = 0.0 + metrics["gate_capacity"] = 0.0 + metrics["gate_carry_chain"] = 0.0 + metrics["hpwl"] = 0.0 + metrics["valid"] = 0.0 + metrics["combined_score"] = INVALID_COMBINED_SCORE + metrics["runtime_s"] = float(time.time() - start) + return _wrap(metrics, artifacts) - metrics: dict[str, float] = { - "combined_score": INVALID_COMBINED_SCORE, - "hpwl": 0.0, - "valid": 0.0, - "gate_site_type": 0.0, - "gate_capacity": 0.0, - "gate_carry_chain": 0.0, - "runtime_s": 0.0, - "timeout": 0.0, - } + # ------------------------------------------------------------------ + # Step 1: Run candidate program in a temporary directory + # ------------------------------------------------------------------ + work_dir = Path(tempfile.mkdtemp(prefix="fpga_eval_")) try: - # 1. Copy benchmark files to work dir - nodes_path = benchmark_dir / "design.nodes" - nets_path = benchmark_dir / "design.nets" - pl_path = benchmark_dir / "design.pl" - scl_path = benchmark_dir / "design.scl" - - missing = [p.name for p in [nodes_path, nets_path, pl_path, scl_path] if not p.is_file()] - if missing: - artifacts["error_message"] = ( - f"Missing benchmark files in {benchmark_name}: {', '.join(missing)}" - ) - metrics["runtime_s"] = float(time.time() - start) - return _wrap(metrics, artifacts) - - for src in [nodes_path, nets_path, pl_path, scl_path]: - shutil.copy2(str(src), str(work_dir / src.name)) - - # 2. Run candidate program + # Copy benchmark files to work dir so candidate can resolve them + # (candidate falls back to references/ automatically) + pass + + # Run the candidate program + cmd = [ + sys.executable, + os.path.abspath(program_path), + "--output", str(work_dir / "solution.pl"), + ] + + # Pass benchmark paths explicitly if they differ from references/ + if benchmark is not None: + cmd.extend(["--nodes", str(nodes_path)]) + cmd.extend(["--pl", str(pl_path)]) + cmd.extend(["--scl", str(scl_path)]) + + env = os.environ.copy() try: proc = subprocess.run( - [sys.executable, program_path_resolved, - "--nodes", str(work_dir / "design.nodes"), - "--pl", str(work_dir / "design.pl"), - "--scl", str(work_dir / "design.scl"), - "--output", str(work_dir / "solution.pl")], - cwd=str(work_dir), + cmd, capture_output=True, text=True, - timeout=300, + timeout=3600, + env=env, ) except subprocess.TimeoutExpired as e: metrics["timeout"] = 1.0 @@ -201,13 +268,17 @@ def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path artifacts["error_message"] = f"crashed with return code {proc.returncode}" return _wrap(metrics, artifacts) - # 3. Read benchmark data (from original paths, copies in workdir) + # ------------------------------------------------------------------ + # Step 2: Read benchmark data + # ------------------------------------------------------------------ instances = parse_nodes(str(nodes_path)) netlist = parse_nets(str(nets_path)) scl_data = parse_scl(str(scl_path)) ref_pl = parse_pl(str(pl_path)) - # 4. Read candidate solution + # ------------------------------------------------------------------ + # Step 3: Read candidate solution + # ------------------------------------------------------------------ solution_path = work_dir / "solution.pl" if not solution_path.is_file(): artifacts["error_message"] = "solution.pl not generated by candidate" @@ -217,7 +288,9 @@ def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path candidate_pl = parse_pl(str(solution_path)) artifacts["num_placed"] = str(len(candidate_pl)) - # 5. Validate completeness + # ------------------------------------------------------------------ + # Step 4: Validate completeness + # ------------------------------------------------------------------ missing_movable: list[str] = [] for name, ctype in instances: if name not in ref_pl and name not in candidate_pl: @@ -241,7 +314,9 @@ def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path metrics["runtime_s"] = float(time.time() - start) return _wrap(metrics, artifacts) - # 6. Check legality gates + # ------------------------------------------------------------------ + # Step 5: Check legality gates + # ------------------------------------------------------------------ g1_ok, g1_violations = check_site_type_compatibility(candidate_pl, instances, scl_data) g2_ok, g2_violations = check_site_capacity(candidate_pl, instances, scl_data) g3_ok, g3_violations = check_carry_chain_integrity(candidate_pl, instances) @@ -259,7 +334,9 @@ def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path all_gates_pass = g1_ok and g2_ok and g3_ok - # 7. Compute HPWL + # ------------------------------------------------------------------ + # Step 6: Compute HPWL + # ------------------------------------------------------------------ hpwl = compute_hpwl_canonical(candidate_pl, netlist) metrics["hpwl"] = float(hpwl) @@ -307,6 +384,3 @@ def _wrap(metrics: dict[str, float], artifacts: dict[str, str]) -> Any: else: output = result print(json.dumps(output, indent=2)) - - -

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 kbt}QD?v5Ny#@?=jjpc^?VLa?@V({ list[tuple[str, str]]: + """Parse .nodes file. Returns list of (instance_name, cell_type).""" + instances: list[tuple[str, str]] = [] + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split() + if len(parts) >= 2: + instances.append((parts[0], parts[1])) + return instances + + +def parse_pl(path: str) -> dict[str, tuple[int, int, int]]: + """Parse .pl file. Returns dict: name -> (x, y, z).""" + placements: dict[str, tuple[int, int, int]] = {} + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split() + if len(parts) >= 4: + name, x_str, y_str, z_str = parts[0], parts[1], parts[2], parts[3] + placements[name] = (int(float(x_str)), int(float(y_str)), int(float(z_str))) + return placements + + +def parse_scl(path: str) -> dict[str, Any]: + """Parse .scl file. Returns dict with site map, capacities, resource mapping.""" + result: dict[str, Any] = { + "site_capacities": {}, + "resources": {}, + "site_map": {}, # (col, row) -> site_type + "num_cols": 0, + "num_rows": 0, + } + current_site = None + in_sitemap = False + + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + + if line.startswith("SITE "): + current_site = line.split(None, 1)[1] + result["site_capacities"][current_site] = {} + elif current_site and line.upper() == "END SITE": + current_site = None + elif current_site and " " in line: + parts = line.split() + if len(parts) >= 2 and parts[0].isalpha(): + cap = int(parts[1]) if parts[1].isdigit() else 1 + result["site_capacities"][current_site][parts[0]] = cap + + elif line.startswith("RESOURCES"): + pass + elif line == "END RESOURCES": + pass + elif line.startswith("SITEMAP"): + parts = line.split() + if len(parts) >= 3: + result["num_cols"] = int(parts[1]) + result["num_rows"] = int(parts[2]) + in_sitemap = True + elif line == "END SITEMAP": + in_sitemap = False + elif in_sitemap: + parts = line.split() + if len(parts) >= 3: + col, row, stype = int(parts[0]), int(parts[1]), parts[2] + result["site_map"][(col, row)] = stype + + # Build reverse resource map: cell_type -> site_type + # (simplified mapping derived from standard FPGA architecture) + result["cell_to_site"] = { + "LUT1": "SLICE", "LUT2": "SLICE", "LUT3": "SLICE", + "LUT4": "SLICE", "LUT5": "SLICE", "LUT6": "SLICE", + "FDRE": "SLICE", + "CARRY4": "SLICE", "CARRY8": "SLICE", + "DSP48E1": "DSP", "DSP48E2": "DSP", + "RAMB18E1": "BRAM", "RAMB18E2": "BRAM", + "RAMB36E1": "BRAM", "RAMB36E2": "BRAM", + "IBUF": "IO", "OBUF": "IO", "BUFGCE": "IO", + } + return result + + +# ?????????????????????????????????????????????????????????????????????? +# INITIAL PLACER: Row-scan +# ?????????????????????????????????????????????????????????????????????? + +def is_slice_cell(cell_type: str) -> bool: + return cell_type.startswith("LUT") or cell_type == "FDRE" or cell_type.startswith("CARRY") + + +def get_site_type(cell_type: str, cell_to_site: dict[str, str]) -> str: + """Determine which site type a cell requires.""" + if is_slice_cell(cell_type): + return "SLICE" + return cell_to_site.get(cell_type, "SLICE") + + +def row_scan_place( + instances: list[tuple[str, str]], + fixed_placements: dict[str, tuple[int, int, int]], + scl_data: dict[str, Any], +) -> dict[str, tuple[int, int, int]]: + """Assign each movable instance to a legal site via row-scan. + + Algorithm: + 1. Separate instances by site type (SLICE, DSP, BRAM). + 2. For each site type, scan the site map left-to-right, bottom-to-top. + 3. Assign instances to sites, respecting per-site capacity. + 4. Fixed instances are placed at their input locations. + """ + placements: dict[str, tuple[int, int, int]] = dict(fixed_placements) + cell_to_site = scl_data["cell_to_site"] + site_map = scl_data["site_map"] + capacities = scl_data["site_capacities"] + + # Group movable instances by required site type + groups: dict[str, list[str]] = {"SLICE": [], "DSP": [], "BRAM": []} + for name, ctype in instances: + if name in fixed_placements: + continue # skip fixed IOs + stype = get_site_type(ctype, cell_to_site) + if stype not in groups: + groups[stype] = [] + groups[stype].append((name, ctype)) + + # Collect sites by type + sites_by_type: dict[str, list[tuple[int, int]]] = {} + for (col, row), stype in site_map.items(): + if stype not in sites_by_type: + sites_by_type[stype] = [] + sites_by_type[stype].append((col, row)) + + # Sort sites by column, then row (left-to-right, bottom-to-top) + for stype in sites_by_type: + sites_by_type[stype].sort(key=lambda p: (p[0], p[1])) + + # Per-site resource usage + site_usage: dict[tuple[int, int], dict[str, int]] = {} + + def get_usage(site_key: tuple[int, int]) -> dict[str, int]: + if site_key not in site_usage: + site_usage[site_key] = {"LUT": 0, "FF": 0} + return site_usage[site_key] + + slc_cap = capacities.get("SLICE", {}) + max_lut_per_slice = slc_cap.get("LUT", 16) + max_ff_per_slice = slc_cap.get("FF", 16) + + # Place SLICE instances (LUTs and FFs) with capacity tracking + slice_instances = groups.get("SLICE", []) + slice_sites = sites_by_type.get("SLICE", []) + site_idx = 0 + bel_idx = 0 + + # Separate LUT and FF instances for capacity-aware placement + luts = [(n, t) for n, t in slice_instances if t.startswith("LUT")] + ffs = [(n, t) for n, t in slice_instances if t == "FDRE"] + + def place_group( + group: list[tuple[str, str]], + sites: list[tuple[int, int]], + max_per_site: int, + usage_key: str, + ) -> None: + nonlocal site_idx, bel_idx + site_idx = 0 + bel_idx = 0 + for name, ctype in group: + while site_idx < len(sites): + site = sites[site_idx] + usage = get_usage(site) + if usage[usage_key] < max_per_site: + usage[usage_key] += 1 + z = usage[usage_key] - 1 # BEL index 0-based + placements[name] = (site[0], site[1], z) + bel_idx += 1 + break + site_idx += 1 + bel_idx = 0 + + # Place LUTs first, then FFs (they share the same SLICE sites) + site_idx = 0 + place_group(luts, slice_sites, max_lut_per_slice, "LUT") + site_idx = 0 + place_group(ffs, slice_sites, max_ff_per_slice, "FF") + + # Place DSP instances (1 per DSP site) + dsp_instances = groups.get("DSP", []) + dsp_sites = sites_by_type.get("DSP", []) + for i, (name, ctype) in enumerate(dsp_instances): + if i < len(dsp_sites): + placements[name] = (dsp_sites[i][0], dsp_sites[i][1], 0) + + # Place BRAM instances (1 per BRAM site) + bram_instances = groups.get("BRAM", []) + bram_sites = sites_by_type.get("BRAM", []) + for i, (name, ctype) in enumerate(bram_instances): + if i < len(bram_sites): + placements[name] = (bram_sites[i][0], bram_sites[i][1], 0) + + return placements + + +def write_pl(placements: dict[str, tuple[int, int, int]], output_path: str) -> None: + """Write placements to a .pl file in Bookshelf format.""" + with open(output_path, "w") as f: + for name in sorted(placements.keys()): + x, y, z = placements[name] + f.write(f"{name} {x} {y} {z}\n") + print(f"Placement written to {output_path}: {len(placements)} instances") + + +def main() -> None: + parser = argparse.ArgumentParser(description="FPGA Placement Optimization ? initial solver") + parser.add_argument("--nodes", default=None, help="Path to .nodes file") + parser.add_argument("--pl", default=None, help="Path to input .pl file (fixed instances)") + parser.add_argument("--scl", default=None, help="Path to .scl file") + parser.add_argument("--output", default="solution.pl", help="Output .pl file path") + parser.add_argument("--aux", default=None, help="Path to .aux file (alternative to individual args)") + args = parser.parse_args() + + # Resolve input paths + ref_dir = Path(__file__).resolve().parent.parent / "references" + + if args.aux: + aux_dir = Path(args.aux).resolve().parent + nodes_path = aux_dir / "design.nodes" + pl_path = aux_dir / "design.pl" + scl_path = aux_dir / "design.scl" + else: + nodes_path = Path(args.nodes) if args.nodes else ref_dir / "design.nodes" + pl_path = Path(args.pl) if args.pl else ref_dir / "design.pl" + scl_path = Path(args.scl) if args.scl else ref_dir / "design.scl" + + if not nodes_path.is_file(): + # Fall back to references/ + nodes_path = ref_dir / "design.nodes" + pl_path = ref_dir / "design.pl" + scl_path = ref_dir / "design.scl" + + print(f"Nodes: {nodes_path}") + print(f"PL: {pl_path}") + print(f"SCL: {scl_path}") + + # Parse + instances = parse_nodes(str(nodes_path)) + fixed_pl = parse_pl(str(pl_path)) + scl_data = parse_scl(str(scl_path)) + + print(f"Instances: {len(instances)} total, {len(fixed_pl)} fixed, {len(instances) - len(fixed_pl)} movable") + + # Place + placements = row_scan_place(instances, fixed_pl, scl_data) + + # Write output + output_path = Path(args.output).resolve() + output_path.parent.mkdir(parents=True, exist_ok=True) + write_pl(placements, str(output_path)) + + # Verify all instances placed + missing = [n for n, _ in instances if n not in placements] + if missing: + print(f"WARNING: {len(missing)} instances were not placed!", file=sys.stderr) + + +if __name__ == "__main__": + main() +# EVOLVE-BLOCK-END diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/verification/canonical.py b/benchmarks/FPGA/FPGAPlacementOptimization/verification/canonical.py new file mode 100644 index 00000000..2f856e09 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/verification/canonical.py @@ -0,0 +1,309 @@ +"""Canonical reference implementations for FPGA Placement Optimization. + +This module provides the independent evaluator functions used by the +verification pipeline. All functions are pure Python + NumPy. + +Functions: + parse_nodes, parse_nets, parse_pl, parse_scl + compute_hpwl_canonical + check_site_type_compatibility + check_site_capacity + check_carry_chain_integrity +""" + +from __future__ import annotations + +import numpy as np +from pathlib import Path +from typing import Any + + +# ?????????????????????????????????????????????????????????????????????? +# PARSERS +# ?????????????????????????????????????????????????????????????????????? + +def parse_nodes(path: str) -> list[tuple[str, str]]: + """Parse .nodes file. Returns list of (instance_name, cell_type).""" + instances: list[tuple[str, str]] = [] + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split() + if len(parts) >= 2: + instances.append((parts[0], parts[1])) + return instances + + +def parse_nets(path: str) -> list[dict[str, Any]]: + """Parse .nets file. Returns list of net dicts with name and pin list.""" + nets: list[dict[str, Any]] = [] + current_net: dict[str, Any] | None = None + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("net "): + parts = line.split() + net_name = parts[1] if len(parts) > 1 else "" + current_net = {"name": net_name, "pins": []} + elif line == "endnet" and current_net is not None: + nets.append(current_net) + current_net = None + elif current_net is not None and " " in line: + pin_parts = line.split() + if len(pin_parts) >= 1: + current_net["pins"].append(pin_parts[0]) + return nets + + +def parse_pl(path: str) -> dict[str, tuple[int, int, int]]: + """Parse .pl file. Returns dict: name -> (x, y, z).""" + placements: dict[str, tuple[int, int, int]] = {} + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split() + if len(parts) >= 4: + name = parts[0] + x, y, z = int(float(parts[1])), int(float(parts[2])), int(float(parts[3])) + placements[name] = (x, y, z) + return placements + + +def parse_scl(path: str) -> dict[str, Any]: + """Parse .scl file. Returns dict with site info.""" + result: dict[str, Any] = { + "site_capacities": {}, + "site_map": {}, + "num_cols": 0, + "num_rows": 0, + } + current_site = None + in_sitemap = False + + with open(path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("SITE "): + current_site = line.split(None, 1)[1] + result["site_capacities"][current_site] = {} + elif current_site and line.upper() == "END SITE": + current_site = None + elif current_site and " " in line: + parts = line.split() + if len(parts) >= 2 and parts[0].isalpha(): + cap = int(parts[1]) if parts[1].isdigit() else 1 + result["site_capacities"][current_site][parts[0]] = cap + elif line.startswith("SITEMAP"): + parts = line.split() + if len(parts) >= 3: + result["num_cols"] = int(parts[1]) + result["num_rows"] = int(parts[2]) + in_sitemap = True + elif line == "END SITEMAP": + in_sitemap = False + elif in_sitemap: + parts = line.split() + if len(parts) >= 3: + col, row, stype = int(parts[0]), int(parts[1]), parts[2] + result["site_map"][(col, row)] = stype + + result["cell_to_site"] = { + "LUT1": "SLICE", "LUT2": "SLICE", "LUT3": "SLICE", + "LUT4": "SLICE", "LUT5": "SLICE", "LUT6": "SLICE", + "FDRE": "SLICE", "FDCE": "SLICE", "FDPE": "SLICE", "LDCE": "SLICE", + "CARRY4": "SLICE", "CARRY8": "SLICE", + "DSP48E1": "DSP", "DSP48E2": "DSP", + "RAMB18E1": "BRAM", "RAMB18E2": "BRAM", + "RAMB36E1": "BRAM", "RAMB36E2": "BRAM", + "IBUF": "IO", "OBUF": "IO", "BUFGCE": "IO", + } + return result + + +def is_slice_cell(cell_type: str) -> bool: + return (cell_type.startswith("LUT") or cell_type in ("FDRE", "FDCE", "FDPE", "LDCE") + or cell_type.startswith("CARRY")) + + +def get_site_type(cell_type: str, cell_to_site: dict[str, str]) -> str: + if is_slice_cell(cell_type): + return "SLICE" + return cell_to_site.get(cell_type, "SLICE") + + +# ?????????????????????????????????????????????????????????????????????? +# HPWL COMPUTATION +# ?????????????????????????????????????????????????????????????????????? + +def compute_hpwl_canonical( + placements: dict[str, tuple[int, int, int]], + netlist: list[dict[str, Any]], +) -> float: + """Compute half-perimeter wirelength using pure NumPy. + + For each net, HPWL = (max_x - min_x) + (max_y - min_y) + where x, y are site coordinates of each instance in the net. + + Returns total HPWL across all nets. + """ + total_hpwl = 0.0 + for net in netlist: + pin_instances = net["pins"] + xs: list[float] = [] + ys: list[float] = [] + for inst_name in pin_instances: + if inst_name in placements: + x, y, _ = placements[inst_name] + xs.append(float(x)) + ys.append(float(y)) + if xs: + hpwl = (max(xs) - min(xs)) + (max(ys) - min(ys)) + total_hpwl += hpwl + return total_hpwl + + +# ?????????????????????????????????????????????????????????????????????? +# LEGALITY GATES +# ?????????????????????????????????????????????????????????????????????? + +def check_site_type_compatibility( + placements: dict[str, tuple[int, int, int]], + instances: list[tuple[str, str]], + scl_data: dict[str, Any], +) -> tuple[bool, list[str]]: + """G1: Verify each instance is placed on a compatible site type. + + Returns (passes, list_of_violations). + """ + site_map = scl_data["site_map"] + cell_to_site = scl_data["cell_to_site"] + violations: list[str] = [] + + name_to_type = {name: ctype for name, ctype in instances} + + for name, (x, y, z) in placements.items(): + if name not in name_to_type: + continue + cell_type = name_to_type[name] + required_site = get_site_type(cell_type, cell_to_site) + actual_site = site_map.get((x, y)) + if actual_site is None: + violations.append(f"{name} at ({x},{y}): no site exists at that location") + elif actual_site != required_site: + violations.append( + f"{name} (type={cell_type}) at ({x},{y}): " + f"requires {required_site} site, found {actual_site}" + ) + + return len(violations) == 0, violations + + +def check_site_capacity( + placements: dict[str, tuple[int, int, int]], + instances: list[tuple[str, str]], + scl_data: dict[str, Any], +) -> tuple[bool, list[str]]: + """G2: Verify each site does not exceed its resource capacity. + + For SLICE sites: LUT count and FF count must not exceed capacity. + For DSP sites: at most 1 DSP instance. + For BRAM sites: at most 1 BRAM instance. + + Returns (passes, list_of_violations). + """ + capacities = scl_data["site_capacities"] + slc_cap = capacities.get("SLICE", {}) + max_lut = slc_cap.get("LUT", 16) + max_ff = slc_cap.get("FF", 16) + violations: list[str] = [] + + name_to_type = {name: ctype for name, ctype in instances} + + # Per-site resource usage + site_usage: dict[tuple[int, int], dict[str, int]] = {} + + for name, (x, y, z) in placements.items(): + if name not in name_to_type: + continue + cell_type = name_to_type[name] + site_key = (x, y) + if site_key not in site_usage: + site_usage[site_key] = {"LUT": 0, "FF": 0, "DSP": 0, "BRAM": 0} + + if cell_type.startswith("LUT"): + site_usage[site_key]["LUT"] += 1 + elif cell_type in ("FDRE", "FDCE", "FDPE", "LDCE"): + site_usage[site_key]["FF"] += 1 + elif cell_type.startswith("DSP"): + site_usage[site_key]["DSP"] += 1 + elif cell_type.startswith("RAMB"): + site_usage[site_key]["BRAM"] += 1 + + for site_key, usage in site_usage.items(): + if usage["LUT"] > max_lut: + violations.append( + f"Site ({site_key[0]},{site_key[1]}): {usage['LUT']} LUTs exceeds capacity {max_lut}" + ) + if usage["FF"] > max_ff: + violations.append( + f"Site ({site_key[0]},{site_key[1]}): {usage['FF']} FFs exceeds capacity {max_ff}" + ) + if usage["DSP"] > 1: + violations.append( + f"Site ({site_key[0]},{site_key[1]}): {usage['DSP']} DSPs exceeds capacity 1" + ) + if usage["BRAM"] > 1: + violations.append( + f"Site ({site_key[0]},{site_key[1]}): {usage['BRAM']} BRAMs exceeds capacity 1" + ) + + return len(violations) == 0, violations + + +def check_carry_chain_integrity( + placements: dict[str, tuple[int, int, int]], + instances: list[tuple[str, str]], +) -> tuple[bool, list[str]]: + """G3: Verify carry-chain instances (CARRY4/CARRY8) maintain adjacency. + + This is a simplified check: CARRY instances placed in a column must be + vertically adjacent in the correct order (by name suffix or position). + + Returns (passes, list_of_violations). + """ + violations: list[str] = [] + + # Find all carry chain instances + carry_names: list[str] = [] + for name, ctype in instances: + if ctype.startswith("CARRY") and name in placements: + carry_names.append(name) + + if not carry_names: + return True, [] + + # Sort by name for deterministic ordering + carry_sorted = sorted(carry_names) + prev_x, prev_y = None, None + for name in carry_sorted: + x, y, z = placements[name] + if prev_x is not None: + if x != prev_x: + violations.append( + f"Carry chain broken: {name} at col {x}, previous at col {prev_x}" + ) + if y != prev_y + 1: + violations.append( + f"Carry chain gap: {name} at row {y}, expected row {prev_y + 1}" + ) + prev_x, prev_y = x, y + + return len(violations) == 0, violations diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py b/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py new file mode 100644 index 00000000..d04381a6 --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/verification/evaluator.py @@ -0,0 +1,312 @@ +"""Evaluator for FPGA Placement Optimization. + +This evaluator: +1. Runs the candidate program (scripts/init.py) to produce solution.pl +2. Parses the output solution.pl +3. Checks legality gates (G1: site-type, G2: capacity, G3: carry-chain) +4. Computes HPWL +5. Returns metrics dict with combined_score + +Benchmark selection: + Default: references/design.* (fpga-example1, fast smoke test) + --benchmark FPGA01: references/ispd2016/FPGA01/ (ISPD 2016 design) + --benchmark FPGA02..FPGA12: corresponding ISPD 2016 design +""" + +from __future__ import annotations + +import argparse +import json +import os +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path +from typing import Any + +import numpy as np + +# Ensure canonical.py is importable when running from any working directory +_evaluator_dir = Path(__file__).resolve().parent +if str(_evaluator_dir) not in sys.path: + sys.path.insert(0, str(_evaluator_dir)) + +from canonical import ( + parse_nodes, + parse_nets, + parse_pl, + parse_scl, + compute_hpwl_canonical, + check_site_type_compatibility, + check_site_capacity, + check_carry_chain_integrity, +) + +INVALID_COMBINED_SCORE = -1e18 + +# Known ISPD 2016 benchmark designs +ISPD2016_DESIGNS = {f"FPGA{i:02d}" for i in range(1, 13)} + + +def _find_repo_root(start: Path | None = None) -> Path: + if "FRONTIER_ENGINEERING_ROOT" in os.environ: + return Path(os.environ["FRONTIER_ENGINEERING_ROOT"]).expanduser().resolve() + here = (start or Path(__file__)).resolve() + for parent in [here, *here.parents]: + if (parent / "frontier_eval").is_dir() and (parent / "benchmarks").is_dir(): + return parent + return Path.cwd().resolve() + + +def _resolve_benchmark_dir(repo_root: Path, benchmark: str | None) -> Path: + benchmark_rel = Path("benchmarks") / "FPGA" / "FPGAPlacementOptimization" + base_dir = repo_root / benchmark_rel / "references" + if not base_dir.is_dir(): + for prefix in [Path.cwd().resolve(), repo_root]: + candidate = prefix / benchmark_rel / "references" + if candidate.is_dir(): + base_dir = candidate + break + if benchmark is None: + return base_dir + design_upper = benchmark.upper() + if design_upper not in ISPD2016_DESIGNS: + valid = sorted(ISPD2016_DESIGNS) + raise ValueError(f"Unknown ISPD 2016 design: '{benchmark}'. Valid options: {', '.join(valid)}") + design_dir = base_dir / "ispd2016" / design_upper + if not design_dir.is_dir(): + nl = chr(10) + msg = ( + "ISPD 2016 benchmark dataset not found for '" + design_upper + "'." + + nl + nl + + " Expected location: " + str(design_dir) + + nl + nl + + " The ISPD 2016 benchmark suite is NOT bundled with this repository" + + nl + " due to its size (~1 GB). To use --benchmark, download and set up" + + nl + " the dataset manually:" + + nl + nl + + " 1. Download the official ISPD 2016 FPGA Placement Contest benchmarks from:" + + nl + " http://www.ispd.cc/contests/16/benchmarks.html" + + nl + nl + + " 2. Extract each design into:" + + nl + " " + str(base_dir / "ispd2016") + + nl + nl + + " 3. The directory structure should be:" + + nl + " references/ispd2016/FPGA01/design.nodes (and .nets, .pl, .scl, ...)" + + nl + " references/ispd2016/FPGA02/design.nodes" + + nl + " ..." + + nl + nl + + " See the Dataset Setup section in README.md for detailed instructions." + + nl + " To run without downloading, omit --benchmark to use the bundled" + + nl + " fpga-example1 design instead." + ) + raise FileNotFoundError(msg) + return design_dir +def _tail(text: str, limit: int = 8000) -> str: + return text if len(text) <= limit else text[-limit:] + + +def _truncate_middle(text: str, limit: int = 200_000) -> str: + if len(text) <= limit: + return text + keep = max(0, (limit - 128) // 2) + omitted = len(text) - 2 * keep + return text[:keep] + f"\n\n[... truncated {omitted} chars ...]\n\n" + text[-keep:] + + +def evaluate(program_path: str, *, benchmark: str | None = None, repo_root: Path | None = None) -> Any: + """Full evaluation pipeline. + + 1. Run candidate program to produce solution.pl + 2. Parse benchmark files and candidate placement + 3. Check legality gates (G1, G2, G3) + 4. Compute HPWL + 5. Return metrics dict + + Args: + program_path: Path to the candidate program (scripts/init.py). + benchmark: Optional ISPD 2016 design name (e.g. 'FPGA01'). + Defaults to fpga-example1 (references/). + repo_root: Repository root. Auto-detected if not provided. + """ + start = time.time() + repo_root = _find_repo_root() if repo_root is None else repo_root.expanduser().resolve() + program_path_resolved = str(Path(program_path).expanduser().resolve()) + + # Resolve benchmark directory + benchmark_dir = _resolve_benchmark_dir(repo_root, benchmark) + benchmark_name = benchmark or "fpga-example1" + + work_dir = Path(tempfile.mkdtemp(prefix="fe_fpga_")).resolve() + artifacts: dict[str, str] = {} + + metrics: dict[str, float] = { + "combined_score": INVALID_COMBINED_SCORE, + "hpwl": 0.0, + "valid": 0.0, + "gate_site_type": 0.0, + "gate_capacity": 0.0, + "gate_carry_chain": 0.0, + "runtime_s": 0.0, + "timeout": 0.0, + } + + try: + # 1. Copy benchmark files to work dir + nodes_path = benchmark_dir / "design.nodes" + nets_path = benchmark_dir / "design.nets" + pl_path = benchmark_dir / "design.pl" + scl_path = benchmark_dir / "design.scl" + + missing = [p.name for p in [nodes_path, nets_path, pl_path, scl_path] if not p.is_file()] + if missing: + artifacts["error_message"] = ( + f"Missing benchmark files in {benchmark_name}: {', '.join(missing)}" + ) + metrics["runtime_s"] = float(time.time() - start) + return _wrap(metrics, artifacts) + + for src in [nodes_path, nets_path, pl_path, scl_path]: + shutil.copy2(str(src), str(work_dir / src.name)) + + # 2. Run candidate program + try: + proc = subprocess.run( + [sys.executable, program_path_resolved, + "--nodes", str(work_dir / "design.nodes"), + "--pl", str(work_dir / "design.pl"), + "--scl", str(work_dir / "design.scl"), + "--output", str(work_dir / "solution.pl")], + cwd=str(work_dir), + capture_output=True, + text=True, + timeout=300, + ) + except subprocess.TimeoutExpired as e: + metrics["timeout"] = 1.0 + metrics["runtime_s"] = float(time.time() - start) + artifacts["error_message"] = f"program timeout: {e}" + return _wrap(metrics, artifacts) + + artifacts["program_stdout"] = _tail(proc.stdout) + artifacts["program_stderr"] = _tail(proc.stderr) + artifacts["program_stdout_full"] = _truncate_middle(proc.stdout) + artifacts["program_stderr_full"] = _truncate_middle(proc.stderr) + metrics["program_returncode"] = float(proc.returncode) + + if proc.returncode != 0: + metrics["runtime_s"] = float(time.time() - start) + artifacts["error_message"] = f"crashed with return code {proc.returncode}" + return _wrap(metrics, artifacts) + + # 3. Read benchmark data (from original paths, copies in workdir) + instances = parse_nodes(str(nodes_path)) + netlist = parse_nets(str(nets_path)) + scl_data = parse_scl(str(scl_path)) + ref_pl = parse_pl(str(pl_path)) + + # 4. Read candidate solution + solution_path = work_dir / "solution.pl" + if not solution_path.is_file(): + artifacts["error_message"] = "solution.pl not generated by candidate" + metrics["runtime_s"] = float(time.time() - start) + return _wrap(metrics, artifacts) + + candidate_pl = parse_pl(str(solution_path)) + artifacts["num_placed"] = str(len(candidate_pl)) + + # 5. Validate completeness + missing_movable: list[str] = [] + for name, ctype in instances: + if name not in ref_pl and name not in candidate_pl: + missing_movable.append(name) + + moved_fixed: list[str] = [] + for name, (rx, ry, rz) in ref_pl.items(): + if name in candidate_pl: + cx, cy, cz = candidate_pl[name] + if (cx, cy, cz) != (rx, ry, rz): + moved_fixed.append(name) + + errors: list[str] = [] + if missing_movable: + errors.append(f"{len(missing_movable)} movable instances missing from solution") + if moved_fixed: + errors.append(f"{len(moved_fixed)} fixed instances were moved") + + if errors: + artifacts["validation_errors"] = ", ".join(errors) + metrics["runtime_s"] = float(time.time() - start) + return _wrap(metrics, artifacts) + + # 6. Check legality gates + g1_ok, g1_violations = check_site_type_compatibility(candidate_pl, instances, scl_data) + g2_ok, g2_violations = check_site_capacity(candidate_pl, instances, scl_data) + g3_ok, g3_violations = check_carry_chain_integrity(candidate_pl, instances) + + metrics["gate_site_type"] = 1.0 if g1_ok else 0.0 + metrics["gate_capacity"] = 1.0 if g2_ok else 0.0 + metrics["gate_carry_chain"] = 1.0 if g3_ok else 0.0 + + if not g1_ok: + artifacts["gate_site_type_violations"] = "\n".join(g1_violations[:20]) + if not g2_ok: + artifacts["gate_capacity_violations"] = "\n".join(g2_violations[:20]) + if not g3_ok: + artifacts["gate_carry_chain_violations"] = "\n".join(g3_violations[:20]) + + all_gates_pass = g1_ok and g2_ok and g3_ok + + # 7. Compute HPWL + hpwl = compute_hpwl_canonical(candidate_pl, netlist) + metrics["hpwl"] = float(hpwl) + + runtime_s = time.time() - start + metrics["runtime_s"] = float(runtime_s) + + if all_gates_pass: + metrics["combined_score"] = -float(hpwl) + metrics["valid"] = 1.0 + else: + metrics["combined_score"] = INVALID_COMBINED_SCORE + metrics["valid"] = 0.0 + + return _wrap(metrics, artifacts) + + finally: + shutil.rmtree(work_dir, ignore_errors=True) + + +def _wrap(metrics: dict[str, float], artifacts: dict[str, str]) -> Any: + try: + from openevolve.evaluation_result import EvaluationResult + return EvaluationResult(metrics=metrics, artifacts=artifacts) + except Exception: + return metrics + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="FPGA Placement Optimization Evaluator") + parser.add_argument("program_path", help="Path to the candidate program (scripts/init.py)") + parser.add_argument( + "--benchmark", "-b", default=None, + help="ISPD 2016 design name (e.g. FPGA01..FPGA12). Default: fpga-example1 (references/)." + ) + args = parser.parse_args() + + try: + result = evaluate(args.program_path, benchmark=args.benchmark) + except (ValueError, FileNotFoundError) as e: + print(json.dumps({"error": str(e)}, indent=2)) + sys.exit(1) + + if hasattr(result, "metrics"): + output = {"metrics": result.metrics, "artifacts": result.artifacts} + else: + output = result + print(json.dumps(output, indent=2)) + + + diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/verification/requirements.txt b/benchmarks/FPGA/FPGAPlacementOptimization/verification/requirements.txt new file mode 100644 index 00000000..24ce15ab --- /dev/null +++ b/benchmarks/FPGA/FPGAPlacementOptimization/verification/requirements.txt @@ -0,0 +1 @@ +numpy From 86f973537bee0bbc9d20654dd9037bd2ac1537f1 Mon Sep 17 00:00:00 2001 From: y-ji24 Date: Mon, 13 Jul 2026 11:18:44 +0800 Subject: [PATCH 3/4] Clean up aug-elfPlace artifacts from baseline/ --- .../baseline/CMakeLists.txt | 138 - .../baseline/README.md | 230 - .../baseline/cmake/FindCairo.cmake | 81 - .../baseline/cmake/TorchExtension.cmake | 132 - .../baseline/dreamplacefpga/BasicPlace.py | 781 -- .../baseline/dreamplacefpga/CMakeLists.txt | 6 - .../baseline/dreamplacefpga/EvalMetrics.py | 198 - .../baseline/dreamplacefpga/IFWriter.py | 2152 ----- .../NesterovAcceleratedGradientOptimizer.py | 152 - .../baseline/dreamplacefpga/NonLinearPlace.py | 1044 --- .../baseline/dreamplacefpga/Params.py | 179 - .../baseline/dreamplacefpga/PlaceDB.py | 1156 --- .../baseline/dreamplacefpga/PlaceObj.py | 876 -- .../baseline/dreamplacefpga/Placer.py | 130 - .../baseline/dreamplacefpga/__init__.py | 5 - .../baseline/dreamplacefpga/configure.py | 26 - .../baseline/dreamplacefpga/configure.py.in | 26 - .../dreamplacefpga/ops/CMakeLists.txt | 30 - .../baseline/dreamplacefpga/ops/__init__.py | 6 - .../ops/adjust_node_area/CMakeLists.txt | 46 - .../ops/adjust_node_area/__init__.py | 0 .../ops/adjust_node_area/adjust_node_area.py | 407 - .../ops/adjust_node_area/src/.gitignore | 5 - .../adjust_node_area/src/adjust_node_area.cpp | 114 - .../src/adjust_node_area_cuda.cpp | 72 - .../src/adjust_node_area_cuda_kernel.cu | 105 - .../adjust_node_area/src/scaling_function.h | 67 - .../src/update_pin_offset.cpp | 72 - .../src/update_pin_offset_cuda.cpp | 55 - .../src/update_pin_offset_cuda_kernel.cu | 67 - .../clustering_compatibility/CMakeLists.txt | 36 - .../ops/clustering_compatibility/__init__.py | 6 - .../clustering_compatibility.py | 237 - .../clustering_compatibility/src/.gitignore | 5 - .../src/flop_compatibility.cpp | 328 - .../src/flop_compatibility_cuda.cpp | 151 - .../src/flop_compatibility_cuda_kernel.cu | 330 - .../clustering_compatibility/src/functions.h | 131 - .../src/lut_compatibility.cpp | 283 - .../src/lut_compatibility_cuda.cpp | 165 - .../src/lut_compatibility_cuda_kernel.cu | 309 - .../dreamplacefpga/ops/dct/CMakeLists.txt | 68 - .../dreamplacefpga/ops/dct/__init__.py | 5 - .../baseline/dreamplacefpga/ops/dct/dct.py | 399 - .../dreamplacefpga/ops/dct/dct2_fft2.py | 150 - .../dreamplacefpga/ops/dct/dct_lee.py | 426 - .../ops/dct/discrete_spectral_transform.py | 430 - .../baseline/dreamplacefpga/ops/dct/naive.py | 106 - .../dreamplacefpga/ops/dct/src/.gitignore | 5 - .../dreamplacefpga/ops/dct/src/dct.cpp | 255 - .../baseline/dreamplacefpga/ops/dct/src/dct.h | 427 - .../dreamplacefpga/ops/dct/src/dct2_fft2.cpp | 148 - .../dreamplacefpga/ops/dct/src/dct2_fft2.h | 613 -- .../ops/dct/src/dct2_fft2_cuda.cpp | 148 - .../ops/dct/src/dct2_fft2_cuda.h | 70 - .../ops/dct/src/dct2_fft2_cuda_kernel.cu | 728 -- .../dreamplacefpga/ops/dct/src/dct_2N.cpp | 187 - .../ops/dct/src/dct_2N_cuda.cpp | 188 - .../dreamplacefpga/ops/dct/src/dct_cuda.cpp | 265 - .../dreamplacefpga/ops/dct/src/dct_cuda.h | 119 - .../ops/dct/src/dct_cuda_kernel.cu | 523 -- .../dreamplacefpga/ops/dct/src/dct_lee.cpp | 337 - .../dreamplacefpga/ops/dct/src/dct_lee_cpu.h | 309 - .../ops/dct/src/dct_lee_cuda.cpp | 330 - .../dreamplacefpga/ops/dct/src/dct_lee_cuda.h | 32 - .../ops/dct/src/dct_lee_cuda_kernel.cu | 294 - .../ops/dct/src/dct_lee_cuda_kernel.h | 169 - .../dreamplacefpga/ops/dct/src/dst.cpp | 83 - .../dreamplacefpga/ops/dct/src/dst_cuda.cpp | 75 - .../ops/dct/src/dst_cuda_kernel.cu | 131 - .../dreamplacefpga/ops/dct/src/dxt.cpp | 448 - .../dreamplacefpga/ops/dct/src/dxt_cuda.cpp | 465 -- .../dreamplacefpga/ops/dct/torch_fft_api.py | 148 - .../ops/demandMap/CMakeLists.txt | 34 - .../dreamplacefpga/ops/demandMap/__init__.py | 5 - .../dreamplacefpga/ops/demandMap/demandMap.py | 105 - .../ops/demandMap/src/.gitignore | 5 - .../ops/demandMap/src/demandMap.cpp | 149 - .../ops/demandMap/src/demandMap_cuda.cpp | 71 - .../demandMap/src/demandMap_cuda_kernel.cu | 148 - .../ops/demandMap/src/demand_function.h | 23 - .../ops/density_map/CMakeLists.txt | 35 - .../ops/density_map/density_map.py | 211 - .../ops/density_map/src/.gitignore | 5 - .../ops/density_map/src/density_map.cpp | 246 - .../ops/density_map/src/density_map_cuda.cpp | 175 - .../src/density_map_cuda_kernel.cu | 146 - .../ops/density_potential/CMakeLists.txt | 36 - .../ops/density_potential/__init__.py | 5 - .../density_potential/density_potential.py | 408 - .../ops/density_potential/src/.gitignore | 5 - .../src/density_overflow_cuda_kernel.cu | 159 - .../src/density_potential.cpp | 615 -- .../src/density_potential_cuda.cpp | 414 - .../src/density_potential_cuda_kernel.cu | 317 - .../dreamplacefpga/ops/draw_place/.gitignore | 6 - .../ops/draw_place/CMakeLists.txt | 33 - .../ops/draw_place/PlaceDrawer.py | 218 - .../dreamplacefpga/ops/draw_place/__init__.py | 5 - .../ops/draw_place/draw_place.py | 238 - .../ops/draw_place/src/.gitignore | 5 - .../ops/draw_place/src/PlaceDrawFPGA.h | 437 - .../ops/draw_place/src/PlaceDrawer.h | 462 -- .../ops/draw_place/src/draw_place.cpp | 150 - .../ops/draw_place/src/draw_place.h | 132 - .../ops/dsp_ram_legalization/CMakeLists.txt | 22 - .../ops/dsp_ram_legalization/__init__.py | 6 - .../dsp_ram_legalization.py | 109 - .../ops/dsp_ram_legalization/src/legalize.cpp | 271 - .../src/legalize_auction.h | 164 - .../ops/electric_potential/.gitignore | 2 - .../ops/electric_potential/CMakeLists.txt | 38 - .../ops/electric_potential/__init__.py | 5 - .../electric_potential/electric_overflow.py | 210 - .../electric_potential/electric_potential.py | 430 - .../ops/electric_potential/src/.gitignore | 5 - .../electric_potential/src/density_function.h | 24 - .../src/electric_density_map.cpp | 247 - .../src/electric_density_map_cuda.cpp | 169 - .../src/electric_density_map_cuda_kernel.cu | 182 - .../electric_potential/src/electric_force.cpp | 168 - .../src/electric_force_cuda.cpp | 119 - .../src/electric_force_cuda_kernel.cu | 131 - .../dreamplacefpga/ops/hpwl/CMakeLists.txt | 48 - .../dreamplacefpga/ops/hpwl/__init__.py | 5 - .../baseline/dreamplacefpga/ops/hpwl/hpwl.py | 128 - .../dreamplacefpga/ops/hpwl/src/.gitignore | 5 - .../dreamplacefpga/ops/hpwl/src/hpwl.cpp | 130 - .../ops/hpwl/src/hpwl_atomic.cpp | 122 - .../dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp | 148 - .../ops/hpwl/src/hpwl_cuda_atomic.cpp | 98 - .../ops/hpwl/src/hpwl_cuda_atomic_kernel.cu | 103 - .../ops/hpwl/src/hpwl_cuda_kernel.cu | 238 - .../ops/logsumexp_wirelength/CMakeLists.txt | 45 - .../ops/logsumexp_wirelength/__init__.py | 5 - .../logsumexp_wirelength.py | 200 - .../ops/logsumexp_wirelength/src/.gitignore | 5 - .../src/integrate_net_weights_cuda_kernel.cu | 103 - .../src/logsumexp_wirelength_cuda_atomic.cpp | 194 - ...logsumexp_wirelength_cuda_atomic_kernel.cu | 461 -- .../src/logsumexp_wirelength_cuda_merged.cpp | 167 - ...logsumexp_wirelength_cuda_merged_kernel.cu | 124 - .../src/logsumexp_wirelength_merged.cpp | 231 - .../ops/lut_ff_legalization/CMakeLists.txt | 37 - .../ops/lut_ff_legalization/__init__.py | 6 - .../lut_ff_legalization.py | 946 --- .../ops/lut_ff_legalization/src/.gitignore | 5 - .../src/graph_matching.cuh | 1297 --- .../src/lut_ff_legalization.cpp | 7319 ----------------- .../src/lut_ff_legalization_cuda.cpp | 760 -- .../src/lut_ff_legalization_cuda_kernel.cu | 4322 ---------- .../ops/move_boundary/CMakeLists.txt | 35 - .../ops/move_boundary/__init__.py | 5 - .../ops/move_boundary/move_boundary.py | 90 - .../ops/move_boundary/src/.gitignore | 5 - .../ops/move_boundary/src/move_boundary.cpp | 91 - .../move_boundary/src/move_boundary_cuda.cpp | 61 - .../src/move_boundary_cuda_kernel.cu | 90 - .../dreamplacefpga/ops/pin_pos/CMakeLists.txt | 43 - .../dreamplacefpga/ops/pin_pos/__init__.py | 4 - .../dreamplacefpga/ops/pin_pos/pin_pos.py | 217 - .../ops/pin_pos/src/pin_pos.cpp | 221 - .../ops/pin_pos/src/pin_pos_cuda.cpp | 121 - .../ops/pin_pos/src/pin_pos_cuda_kernel.cu | 134 - .../ops/pin_pos/src/pin_pos_cuda_segment.cpp | 124 - .../src/pin_pos_cuda_segment_kernel.cu | 140 - .../ops/pin_utilization/CMakeLists.txt | 35 - .../ops/pin_utilization/__init__.py | 6 - .../ops/pin_utilization/pin_utilization.py | 106 - .../ops/pin_utilization/src/.gitignore | 5 - .../src/pin_utilization_map.cpp | 170 - .../src/pin_utilization_map_cuda.cpp | 112 - .../src/pin_utilization_map_cuda_kernel.cu | 164 - .../ops/place_io/CMakeLists.txt | 59 - .../dreamplacefpga/ops/place_io/__init__.py | 5 - .../dreamplacefpga/ops/place_io/place_io.py | 52 - .../ops/place_io/src/.gitignore | 5 - .../ops/place_io/src/BenchMetrics.cpp | 38 - .../ops/place_io/src/BenchMetrics.h | 50 - .../dreamplacefpga/ops/place_io/src/Bin.h | 197 - .../ops/place_io/src/BinMap.cpp | 51 - .../dreamplacefpga/ops/place_io/src/BinMap.h | 114 - .../ops/place_io/src/BookshelfWriter.cpp | 83 - .../ops/place_io/src/BookshelfWriter.h | 51 - .../dreamplacefpga/ops/place_io/src/Box.h | 451 - .../dreamplacefpga/ops/place_io/src/Enums.cpp | 133 - .../dreamplacefpga/ops/place_io/src/Enums.h | 222 - .../ops/place_io/src/GeometryApi.h | 87 - .../dreamplacefpga/ops/place_io/src/Group.cpp | 41 - .../dreamplacefpga/ops/place_io/src/Group.h | 53 - .../ops/place_io/src/HrchyList.h | 262 - .../dreamplacefpga/ops/place_io/src/Index.h | 69 - .../ops/place_io/src/Interval.h | 266 - .../ops/place_io/src/Iterators.h | 403 - .../dreamplacefpga/ops/place_io/src/LibCell.h | 171 - .../dreamplacefpga/ops/place_io/src/Macro.h | 160 - .../ops/place_io/src/MacroObs.h | 102 - .../ops/place_io/src/MacroPin.h | 205 - .../dreamplacefpga/ops/place_io/src/Net.cpp | 43 - .../dreamplacefpga/ops/place_io/src/Net.h | 62 - .../dreamplacefpga/ops/place_io/src/Node.cpp | 57 - .../dreamplacefpga/ops/place_io/src/Node.h | 88 - .../dreamplacefpga/ops/place_io/src/Object.h | 47 - .../ops/place_io/src/Params.cpp | 29 - .../dreamplacefpga/ops/place_io/src/Params.h | 33 - .../dreamplacefpga/ops/place_io/src/Pin.h | 85 - .../ops/place_io/src/PlaceDB.cpp | 1235 --- .../dreamplacefpga/ops/place_io/src/PlaceDB.h | 682 -- .../ops/place_io/src/PlaceWriter.h | 30 - .../dreamplacefpga/ops/place_io/src/Point.h | 189 - .../ops/place_io/src/PyPlaceDB.cpp | 580 -- .../ops/place_io/src/PyPlaceDB.h | 228 - .../ops/place_io/src/PybindPlaceDB.cpp | 258 - .../ops/place_io/src/PybindPyPlaceDB.cpp | 161 - .../ops/place_io/src/Region.cpp | 38 - .../dreamplacefpga/ops/place_io/src/Region.h | 58 - .../dreamplacefpga/ops/place_io/src/Row.h | 239 - .../ops/place_io/src/RowMap.cpp | 206 - .../dreamplacefpga/ops/place_io/src/RowMap.h | 208 - .../dreamplacefpga/ops/place_io/src/Site.h | 87 - .../ops/place_io/src/SubRowNodeMap.cpp | 260 - .../ops/place_io/src/SubRowNodeMap.h | 169 - .../ops/place_io/src/SubRowNodeMapHelper.h | 133 - .../dreamplacefpga/ops/place_io/src/Util.h | 214 - .../ops/place_io/src/place_io.cpp | 143 - .../ops/precondWL/CMakeLists.txt | 33 - .../dreamplacefpga/ops/precondWL/__init__.py | 5 - .../dreamplacefpga/ops/precondWL/precondWL.py | 73 - .../ops/precondWL/src/.gitignore | 5 - .../ops/precondWL/src/precondWL.cpp | 105 - .../ops/precondWL/src/precondWL_cuda.cpp | 73 - .../precondWL/src/precondWL_cuda_kernel.cu | 78 - .../dreamplacefpga/ops/rmst_wl/CMakeLists.txt | 24 - .../dreamplacefpga/ops/rmst_wl/__init__.py | 5 - .../dreamplacefpga/ops/rmst_wl/rmst_wl.py | 50 - .../dreamplacefpga/ops/rmst_wl/src/.gitignore | 5 - .../ops/rmst_wl/src/rmst_wl.cpp | 120 - .../dreamplacefpga/ops/rudy/CMakeLists.txt | 36 - .../dreamplacefpga/ops/rudy/__init__.py | 6 - .../baseline/dreamplacefpga/ops/rudy/rudy.py | 106 - .../dreamplacefpga/ops/rudy/src/.gitignore | 5 - .../dreamplacefpga/ops/rudy/src/parameters.h | 49 - .../dreamplacefpga/ops/rudy/src/rudy.cpp | 188 - .../dreamplacefpga/ops/rudy/src/rudy_cuda.cpp | 93 - .../ops/rudy/src/rudy_cuda_kernel.cu | 190 - .../ops/sortNode2Pin/CMakeLists.txt | 35 - .../ops/sortNode2Pin/__init__.py | 5 - .../ops/sortNode2Pin/sortNode2Pin.py | 58 - .../ops/sortNode2Pin/src/sortNode2Pin.cpp | 78 - .../sortNode2Pin/src/sortNode2Pin_cuda.cpp | 60 - .../src/sortNode2Pin_cuda_kernel.cu | 83 - .../dreamplacefpga/ops/utility/CMakeLists.txt | 19 - .../dreamplacefpga/ops/utility/src/Box.h | 120 - .../ops/utility/src/ComplexNumber.h | 92 - .../ops/utility/src/DetailedPlaceDB.cuh | 566 -- .../ops/utility/src/DetailedPlaceDB.h | 393 - .../ops/utility/src/FlatNestedVector.cuh | 142 - .../ops/utility/src/LegalizationDB.h | 87 - .../dreamplacefpga/ops/utility/src/Msg.cpp | 112 - .../dreamplacefpga/ops/utility/src/Msg.h | 72 - .../ops/utility/src/PitchNestedVector.cuh | 146 - .../ops/utility/src/atomic_ops.cuh | 61 - .../ops/utility/src/atomic_ops.h | 56 - .../dreamplacefpga/ops/utility/src/defs.h | 78 - .../ops/utility/src/diamond_search.h | 141 - .../dreamplacefpga/ops/utility/src/limits.h | 316 - .../ops/utility/src/make_placedb.h | 194 - .../dreamplacefpga/ops/utility/src/math.h | 61 - .../ops/utility/src/namespace.h | 14 - .../dreamplacefpga/ops/utility/src/print.cuh | 49 - .../dreamplacefpga/ops/utility/src/timer.cuh | 32 - .../dreamplacefpga/ops/utility/src/timer.h | 31 - .../dreamplacefpga/ops/utility/src/torch.h | 108 - .../ops/utility/src/torch_fft_api.h | 121 - .../dreamplacefpga/ops/utility/src/utils.cuh | 133 - .../dreamplacefpga/ops/utility/src/utils.h | 37 - .../ops/utility/src/utils_cub.cuh | 21 - .../weighted_average_wirelength/.gitignore | 2 - .../CMakeLists.txt | 67 - .../weighted_average_wirelength/__init__.py | 6 - .../src/.gitignore | 5 - .../src/functional.h | 197 - .../src/functional_cuda.h | 719 -- .../src/integrate_net_weights_cuda_kernel.cu | 66 - .../src/weighted_average_wirelength.cpp | 290 - .../weighted_average_wirelength_atomic.cpp | 245 - .../src/weighted_average_wirelength_cuda.cpp | 217 - ...eighted_average_wirelength_cuda_atomic.cpp | 228 - ...d_average_wirelength_cuda_atomic_kernel.cu | 155 - ...weighted_average_wirelength_cuda_kernel.cu | 106 - ...eighted_average_wirelength_cuda_merged.cpp | 302 - ...d_average_wirelength_cuda_merged_kernel.cu | 264 - .../weighted_average_wirelength_merged.cpp | 269 - .../weighted_average_wirelength.py | 352 - .../baseline/dreamplacefpga/paramsFPGA.json | 255 - .../baseline/paramsFPGA.json | 255 - .../baseline/requirements.txt | 10 - .../baseline/unitest/CMakeLists.txt | 4 - .../baseline/unitest/ops/.gitignore | 2 - .../baseline/unitest/ops/CMakeLists.txt | 4 - .../unitest/ops/adjust_node_area_unittest.py | 194 - .../ops/dct_electric_potential_unitest.py | 121 - .../baseline/unitest/ops/dct_unitest.py | 1089 --- .../unitest/ops/density_potential_unitest.py | 210 - .../unitest/ops/draw_place_unitest.py | 68 - .../unitest/ops/electric_potential_unitest.py | 326 - .../baseline/unitest/ops/hpwl_unitest.py | 152 - .../ops/logsumexp_wirelength_unitest.py | 197 - .../unitest/ops/move_boundary_unitest.py | 75 - .../baseline/unitest/ops/pin_pos_unitest.py | 128 - .../unitest/ops/pin_utilization_unittest.py | 112 - .../baseline/unitest/ops/rudy_unittest.py | 99 - .../weighted_average_wirelength_unitest.py | 433 - 313 files changed, 65842 deletions(-) delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/FindCairo.cmake delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/cmake/TorchExtension.cmake delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/BasicPlace.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/EvalMetrics.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/IFWriter.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NesterovAcceleratedGradientOptimizer.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/NonLinearPlace.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Params.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceDB.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/PlaceObj.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/Placer.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/configure.py.in delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/adjust_node_area.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/adjust_node_area_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/scaling_function.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/adjust_node_area/src/update_pin_offset_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/clustering_compatibility.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/flop_compatibility_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/functions.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/clustering_compatibility/src/lut_compatibility_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct2_fft2.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/dct_lee.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/discrete_spectral_transform.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/naive.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct2_fft2_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_2N_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cpu.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dct_lee_cuda_kernel.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dst_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/src/dxt_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dct/torch_fft_api.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/demandMap.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demandMap_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/demandMap/src/demand_function.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/density_map.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_map/src/density_map_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/density_potential.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_overflow_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/density_potential/src/density_potential_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/PlaceDrawer.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/draw_place.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawFPGA.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/PlaceDrawer.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/draw_place/src/draw_place.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/dsp_ram_legalization.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/dsp_ram_legalization/src/legalize_auction.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_overflow.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/electric_potential.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/density_function.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_density_map_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/electric_potential/src/electric_force_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/hpwl.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_atomic.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_atomic_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/hpwl/src/hpwl_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/logsumexp_wirelength.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/integrate_net_weights_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_atomic_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_cuda_merged_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/logsumexp_wirelength/src/logsumexp_wirelength_merged.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/lut_ff_legalization.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/graph_matching.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/lut_ff_legalization/src/lut_ff_legalization_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/move_boundary.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/move_boundary/src/move_boundary_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/pin_pos.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_pos/src/pin_pos_cuda_segment_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/pin_utilization.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/pin_utilization/src/pin_utilization_map_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/place_io.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BenchMetrics.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Bin.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BinMap.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/BookshelfWriter.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Box.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Enums.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/GeometryApi.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Group.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/HrchyList.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Index.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Interval.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Iterators.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/LibCell.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Macro.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroObs.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/MacroPin.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Net.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Node.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Object.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Params.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Pin.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceDB.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PlaceWriter.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Point.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PyPlaceDB.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPlaceDB.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/PybindPyPlaceDB.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Region.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Row.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/RowMap.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Site.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMap.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/SubRowNodeMapHelper.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/Util.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/place_io/src/place_io.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/precondWL.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/precondWL/src/precondWL_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/rmst_wl.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rmst_wl/src/rmst_wl.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/rudy.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/parameters.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/rudy/src/rudy_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/sortNode2Pin.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/sortNode2Pin/src/sortNode2Pin_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Box.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/ComplexNumber.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/DetailedPlaceDB.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/FlatNestedVector.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/LegalizationDB.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/Msg.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/PitchNestedVector.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/atomic_ops.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/defs.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/diamond_search.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/limits.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/make_placedb.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/math.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/namespace.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/print.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/timer.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/torch_fft_api.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/utility/src/utils_cub.cuh delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/__init__.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/functional_cuda.h delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/integrate_net_weights_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_atomic.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_atomic_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_cuda_merged_kernel.cu delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/src/weighted_average_wirelength_merged.cpp delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/ops/weighted_average_wirelength/weighted_average_wirelength.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/dreamplacefpga/paramsFPGA.json delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/paramsFPGA.json delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/requirements.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/.gitignore delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/CMakeLists.txt delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/adjust_node_area_unittest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_electric_potential_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/dct_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/density_potential_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/draw_place_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/electric_potential_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/hpwl_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/logsumexp_wirelength_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/move_boundary_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_pos_unitest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/pin_utilization_unittest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/rudy_unittest.py delete mode 100644 benchmarks/FPGA/FPGAPlacementOptimization/baseline/unitest/ops/weighted_average_wirelength_unitest.py diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt deleted file mode 100644 index 0a409ec5..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/CMakeLists.txt +++ /dev/null @@ -1,138 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -project(DREAMPlaceFPGA LANGUAGES CXX) -set(CMAKE_VERBOSE_MAKEFILE ON) - -if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set (CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "Prefix prepended to install directories" FORCE ) -endif() -message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING - "Choose the type of build, options are: Debug Release." - FORCE) -endif(NOT CMAKE_BUILD_TYPE) -message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") - -# This is a dirty fix for CMake on some machines that generates default CXX standard and override the custom settings, -# because CUDA does not support C++17 and higher. -# You can check the CMAKE_CXX_FLAGS in CMakeCache.txt to verify the issue. -if(CMAKE_CXX_FLAGS) -string(REGEX REPLACE "-std=c\\+\\+([0-9a-z]+)" " " CMAKE_CXX_FLAGS_INIT ${CMAKE_CXX_FLAGS}) -endif(CMAKE_CXX_FLAGS) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT}" CACHE STRING "Flags used by the compiler during all build types." FORCE) - -if(NOT CMAKE_CXX_ABI) - set(CMAKE_CXX_ABI 0 CACHE STRING - "Choose the value for _GLIBCXX_USE_CXX11_ABI, options are: 0|1." - FORCE) -endif(NOT CMAKE_CXX_ABI) -message(STATUS "CMAKE_CXX_ABI: _GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}") -add_definitions(-D_GLIBCXX_USE_CXX11_ABI=${CMAKE_CXX_ABI}) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -# for CUDA, must be put before finding PyTorch -# link dynamic libraries rather than static ones -set(CUDA_USE_STATIC_CUDA_RUNTIME OFF) -# critical for cuda_add_library, as we need to turn off -O flags -# to make sure the symbols generated by nvcc and gcc are the same -set(CUDA_PROPAGATE_HOST_FLAGS ON) -#set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};--expt-extended-lambda) -# required for executable to run at the install directory -# it will change the RPATH when installing -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -include(cmake/TorchExtension.cmake) - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# without this, clang will complain about linking -#set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set_property(GLOBAL PROPERTY POSITION_INDEPENDENT_CODE TRUE) - -find_package(OpenMP REQUIRED) -#find_program(PYTHON "python" REQUIRED) -find_package(ZLIB REQUIRED) -set(Boost_NO_BOOST_CMAKE TRUE) -find_package(Boost 1.55.0 REQUIRED) -message(STATUS "Boost_INCLUDE_DIRS = ${Boost_INCLUDE_DIRS}") - -#find_package(CUDA 9.0) -find_package(Cairo) -message(STATUS "Cairo: ${CAIRO_INCLUDE_DIRS}") -message(STATUS "Cairo: ${CAIRO_LIBRARIES}") - -get_filename_component(OPS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dreamplacefpga/ops ABSOLUTE) -get_filename_component(UTILITY_LIBRARY_DIRS ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/ops/utility ABSOLUTE) -message(STATUS "OPS_DIR ${OPS_DIR}") -message(STATUS "UTILITY_LIBRARY_DIRS ${UTILITY_LIBRARY_DIRS}") - -# thirdparty libraries -# flute for steiner tree generation -find_path(FLUTE_INCLUDE_DIRS flute.h PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/*) -string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} FLUTE_LINK_DIRS ${FLUTE_INCLUDE_DIRS}) -message(STATUS "FLUTE_INCLUDE_DIRS ${FLUTE_INCLUDE_DIRS}") -message(STATUS "FLUTE_LINK_DIRS ${FLUTE_LINK_DIRS}") - -# Limbo for parsers -set(LIMBO_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo) -message(STATUS "LIMBO_SOURCE_DIR ${LIMBO_SOURCE_DIR}") -set(LIMBO_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/Limbo) -message(STATUS "LIMBO_BINARY_DIR ${LIMBO_BINARY_DIR}") -if(CUDA_FOUND) - if (${CUDA_VERSION_MAJOR} VERSION_GREATER_EQUAL "11") - set(CUB_DIR ${CUDA_INCLUDE_DIRS}) - else() - set(CUB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cub) - endif() -endif() -message(STATUS "CUB_DIR ${CUB_DIR}") - -# munkres-cpp for Hungarian algorithm -set(MUNKRES_CPP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/munkres-cpp/src) -set(MUNKRES_CPP_LINK_DIRS ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/munkres-cpp) -message(STATUS "MUNKRES_CPP_INCLUDE_DIRS ${MUNKRES_CPP_INCLUDE_DIRS}") -message(STATUS "MUNKRES_CPP_LINK_DIRS ${MUNKRES_CPP_LINK_DIRS}") - -# lemon for network flow algorithms -find_path(LEMON_SOURCE_DIR lemon PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/Limbo/limbo/thirdparty/* - NO_DEFAULT_PATH - ) -set(LEMON_INCLUDE_DIRS "${LEMON_SOURCE_DIR}") -string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} LEMON_BINARY_DIR ${LEMON_SOURCE_DIR}) -list(APPEND LEMON_INCLUDE_DIRS "${LEMON_BINARY_DIR}") -set(LEMON_LINK_DIRS ${LEMON_BINARY_DIR}/lemon) -message(STATUS "LEMON_INCLUDE_DIRS ${LEMON_INCLUDE_DIRS}") -message(STATUS "LEMON_LINK_DIRS ${LEMON_LINK_DIRS}") - -if (CUDA_FOUND) - if (NOT CMAKE_CUDA_ARCHITECTURES) - set(CMAKE_CUDA_ARCHITECTURES 6.0 6.1 7.0) - if (${CUDA_VERSION_MAJOR} VERSION_GREATER "9") - list(APPEND CMAKE_CUDA_ARCHITECTURES 7.5) - endif(${CUDA_VERSION_MAJOR} VERSION_GREATER "9") - if (${CUDA_VERSION_MAJOR} VERSION_GREATER "10") - list(APPEND CMAKE_CUDA_ARCHITECTURES 8.0) - endif(${CUDA_VERSION_MAJOR} VERSION_GREATER "10") - endif(NOT CMAKE_CUDA_ARCHITECTURES) - - # for cuda_add_library - cuda_select_nvcc_arch_flags(CUDA_ARCH_FLAGS ${CMAKE_CUDA_ARCHITECTURES}) - message(STATUS "CUDA_ARCH_FLAGS: ${CUDA_ARCH_FLAGS}") - list(APPEND CUDA_NVCC_FLAGS ${CUDA_ARCH_FLAGS} --compiler-options;-fPIC) -endif(CUDA_FOUND) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dreamplacefpga/configure.py.in ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/configure.py) - -add_subdirectory(thirdparty) -add_subdirectory(dreamplacefpga) -add_subdirectory(unitest) -#add_subdirectory(benchmarks) -#add_subdirectory(test) - -#install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_INSTALL_PREFIX}/__init__.py)") - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dreamplacefpga/configure.py DESTINATION dreamplacefpga) diff --git a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md b/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md deleted file mode 100644 index 65ab2838..00000000 --- a/benchmarks/FPGA/FPGAPlacementOptimization/baseline/README.md +++ /dev/null @@ -1,230 +0,0 @@ -## Table of Contents - -* [aug-elfPlace](#aug-elfPlace) - - [Hybrid Placement Framework](#overview) - - [Target Architecture](#target_arch) -* [Publication(s)](#publications) -* [Developer(s)](#developers) -* [Cloning the Repository](#cloning) -* [Build Instructions](#build) - - [To install Python dependency](#python_dependency) - - [To install with Docker](#Docker) - - [To Build](#build_dreamplacefpga) -* [Benchmarks](#benchmarks) -* [Running aug-elfPlace](#running) - - [Integration with VPR](#integrate_vpr) -* [Bug Report](#bug) -* [Copyright](#copyright) - -# ``aug-elfPlace`` -``aug-elfPlace``, built on the [DREAMPlaceFPGA (commit 9b86a09)](https://github.com/rachelselinar/DREAMPlaceFPGA/tree/9b86a09437e08947fb65c2a0cd351d004256bcc5) framework, is a wirelength-driven generalizable flat analytical FPGA placer that consists of a global placer and packer-legalizer. -The main features of ``aug-elfPlace`` include: - - a generalized architecture modeling that reads in the architecture-specific details and legality constraints as an additional bookshelf input file (`design.lc`); - - employing the scalable auction algorithm to legalize a large number of digital signal processors (DSPs) and memory blocks in the design; - - using placeholder fillers to effectively handle memory logic array block (MLAB) instances in the design; - - employ a *partial macro representation* for carry chains; - - enhance the instance area update and packer-legalizer algorithms for look-up tables (LUTs) and flip-flops (FFs) to ensure legal placement for different architectures; and - - [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)-compatible flat output placement format. - -``aug-elfPlace`` can integrate with the open-source [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/) CAD tool to further improve the quality of results through annealing as part of the hybrid placement framework. Please refer to our [paper](#publications) for detailed information. - -### Hybrid Placement Framework -The hybrid placement framework integrates a flat analytical placer such as ``aug-elfPlace`` with [**VPR**](https://docs.verilogtorouting.org/en/latest/vpr/)'s place and route tool using the [VPR legalizer](https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#flat-placement-file-format-flat-place). - -