diff --git a/.gitignore b/.gitignore index 6cb6d8194e..80996b025d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,7 @@ metals.sbt # ElasticMiter puts external files in this directory /ext + +# Characterization tool scratch dirs +tools/backend/synth-characterization/tmp/ +tools/backend/synth-characterization-spec/tmp/ diff --git a/data/spec-timing.json b/data/spec-timing.json new file mode 100644 index 0000000000..28c147d91b --- /dev/null +++ b/data/spec-timing.json @@ -0,0 +1,6127 @@ +{ + "handshake.speculator": { + "pin2pin": [ + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.338 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.338 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.338 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.338 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.622 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.216 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.216 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.79 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.79 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.79 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.918 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.297 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.531 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.609 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.688 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.688 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.307 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.858 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.961 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 4.168 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 4.168 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.686 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.968 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.497 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.747 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.301 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.301 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ctrl_issue", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.847 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.97 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.996 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.152 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.152 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.413 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.413 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.413 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.413 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.417 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.704 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.704 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ctrl_commit", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.407 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.952 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.952 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.604 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.73 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.908 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.377 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.861 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.906 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.046 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.046 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.307 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.357 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.514 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.418 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.418 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.044 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.166 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.299 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.551 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.603 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.633 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.633 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.25 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.25 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.25 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.25 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.25 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.205 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.205 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.205 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.205 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.205 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.403 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.403 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.844 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.844 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.117 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.117 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.117 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.123 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.123 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.451 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_commit", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.407 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.54 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.54 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.54 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.97 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.97 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.374 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.877 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.906 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.231 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.043 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.043 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.304 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.514 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.009 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.009 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.041 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.166 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.166 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.166 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.299 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.299 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.398 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.63 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.63 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.047 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.047 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.047 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.047 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.047 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.841 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.841 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.913 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.913 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.913 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.968 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.968 + } + ] + }, + { + "from": { + "port": "trigger", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.448 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.661 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.661 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.71 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.71 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.002 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.002 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.285 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.706 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.706 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_issue", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.121 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.121 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.121 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.276 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.276 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_issue", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.532 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.532 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.532 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.701 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.701 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.53 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.53 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.736 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.66 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.66 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.71 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.71 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.002 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.002 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.285 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.706 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.706 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.199 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.413 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.707 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.707 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.078 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.078 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.078 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.078 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.078 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.126 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.126 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.126 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.53 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.53 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.482 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.135 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.135 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "trigger", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.392 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.485 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.604 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.61 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.61 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.61 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.61 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.62 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.62 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.62 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.799 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.945 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.711 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.711 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.622 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "ctrl_issue", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.125 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.276 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.276 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "ctrl_issue", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.621 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.621 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.621 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.626 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.769 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.769 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.769 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "ready" + }, + "to": { + "port": "ctrl_commit", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.427 + } + ] + }, + { + "from": { + "port": "ctrl_commit", + "signal": "ready" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.66 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.66 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.717 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.135 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.135 + } + ] + }, + { + "from": { + "port": "ctrl_commit", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.735 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ctrl_issue", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.202 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.727 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.727 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "ctrl_history", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.069 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.069 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.069 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.069 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.069 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.873 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.873 + } + ] + } + ], + "pin2reg": [ + { + "port": "ins", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.832 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.832 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.832 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.855 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.478 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.478 + } + ] + }, + { + "port": "ins", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.882 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.882 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.932 + } + ] + }, + { + "port": "trigger", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.879 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.913 + } + ] + }, + { + "port": "outs", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.724 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.724 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.984 + } + ] + }, + { + "port": "ctrl_issue", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.724 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.724 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.773 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.984 + } + ] + }, + { + "port": "ctrl_history", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 0.997 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 0.997 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.243 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.243 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.243 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.397 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.397 + } + ] + }, + { + "port": "ctrl_commit", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.09 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.09 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.353 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.353 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.353 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.397 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.397 + } + ] + } + ], + "reg2pin": [ + { + "port": "ins", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 3.029 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 3.029 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.232 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.232 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.688 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 4.123 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 4.123 + } + ] + }, + { + "port": "trigger", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.582 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.582 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.611 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.706 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.437 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.539 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.634 + } + ] + }, + { + "port": "outs", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.382 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.382 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.205 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.646 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 4.101 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 5.176 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 5.176 + } + ] + }, + { + "port": "outs", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.38 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.742 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.098 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.286 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.887 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 4.309 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 4.309 + } + ] + }, + { + "port": "ctrl_issue", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.783 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.783 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.136 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.136 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.136 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.735 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.735 + } + ] + }, + { + "port": "ctrl_issue", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.541 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.744 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.93 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.078 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.422 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 4.16 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 4.16 + } + ] + }, + { + "port": "ctrl_history", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.994 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.994 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.003 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.003 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.003 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.881 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.881 + } + ] + }, + { + "port": "ctrl_history", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 3.103 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 3.103 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 3.103 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 3.103 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.483 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.712 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.712 + } + ] + }, + { + "port": "ctrl_commit", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + } + ] + }, + { + "port": "ctrl_commit", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.83 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.184 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.886 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.886 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 3.67 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.727 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.922 + } + ] + } + ] + }, + "handshake.spec_save_commit": { + "pin2pin": [ + { + "from": { + "port": "ins", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.259 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.614 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.597 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.31 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.31 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.045 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.294 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.036 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.349 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.082 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.566 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.875 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.224 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.092 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.33 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.813 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.586 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.036 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.723 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.638 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.113 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.558 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.667 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.36 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.348 + } + ] + }, + { + "from": { + "port": "ins", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.172 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.3 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.7 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.57 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.624 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.514 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "data" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.045 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.294 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.036 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.349 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.277 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.41 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.219 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "data" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.876 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.982 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.875 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.219 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.975 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.232 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.584 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.296 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.518 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.668 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.098 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.436 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.172 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.3 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.169 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.896 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.995 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "data" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.56 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.702 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.546 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.322 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.626 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.487 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.613 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.045 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.294 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.036 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.349 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.277 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.566 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.032 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.876 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.982 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.875 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.804 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.036 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.973 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.713 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.296 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.185 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.518 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.383 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.36 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.435 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.172 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.3 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.169 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.485 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.624 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.995 + } + ] + }, + { + "from": { + "port": "ctrl_issue", + "signal": "valid" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.56 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.702 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.546 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.322 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.487 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.613 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "data" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.661 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.094 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.997 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.617 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.186 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.303 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.463 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "data" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.113 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.092 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.875 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.504 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.975 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.404 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.316 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.296 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.137 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.518 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.711 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.098 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.348 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "data" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.704 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.1 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.169 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.615 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.896 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.995 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "data" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.56 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.702 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.546 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.322 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.117 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.487 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "valid" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.819 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.094 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.997 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.754 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.183 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.303 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.463 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "valid" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.113 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.092 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.289 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.875 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.504 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.975 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.404 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "data" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.713 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.296 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.137 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.518 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.711 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.098 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.348 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "valid" + }, + "to": { + "port": "outs", + "signal": "valid" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.102 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.1 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.169 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.758 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.896 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.361 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.995 + } + ] + }, + { + "from": { + "port": "ctrl_history", + "signal": "valid" + }, + "to": { + "port": "ins", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.56 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.702 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.546 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.322 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.117 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.487 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_issue", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.117 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.277 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.633 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.032 + } + ] + }, + { + "from": { + "port": "outs", + "signal": "ready" + }, + "to": { + "port": "ctrl_history", + "signal": "ready" + }, + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.639 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.52 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.737 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.416 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.805 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.614 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.973 + } + ] + } + ], + "pin2reg": [ + { + "port": "ins", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 0.537 + } + ] + }, + { + "port": "ins", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.676 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.55 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.597 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.592 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.573 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.82 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.533 + } + ] + }, + { + "port": "ctrl_issue", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.748 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.052 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.894 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.744 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.992 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.942 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.106 + } + ] + }, + { + "port": "ctrl_issue", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.748 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.052 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.894 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.744 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.769 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.942 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.051 + } + ] + }, + { + "port": "ctrl_history", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.748 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.052 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.894 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.744 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.216 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.942 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.959 + } + ] + }, + { + "port": "ctrl_history", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.748 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.052 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.894 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.744 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.216 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.942 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.959 + } + ] + }, + { + "port": "outs", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.092 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.248 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.172 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.167 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.769 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.184 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.618 + } + ] + } + ], + "reg2pin": [ + { + "port": "ctrl_issue", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.276 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.401 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.08 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.78 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.349 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.773 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.973 + } + ] + }, + { + "port": "ctrl_history", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.509 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.195 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.551 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.936 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.57 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.243 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.915 + } + ] + }, + { + "port": "outs", + "signal": "data", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.998 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.616 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 2.672 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.837 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 2.965 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 3.567 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 3.684 + } + ] + }, + { + "port": "outs", + "signal": "valid", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 2.403 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 2.407 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.906 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 2.792 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.66 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 2.831 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 2.2 + } + ] + }, + { + "port": "ins", + "signal": "ready", + "samples": [ + { + "params": { + "BITWIDTH": 1, + "FIFO_DEPTH": 4 + }, + "delay": 1.308 + }, + { + "params": { + "BITWIDTH": 2, + "FIFO_DEPTH": 4 + }, + "delay": 1.208 + }, + { + "params": { + "BITWIDTH": 4, + "FIFO_DEPTH": 4 + }, + "delay": 1.257 + }, + { + "params": { + "BITWIDTH": 8, + "FIFO_DEPTH": 4 + }, + "delay": 1.628 + }, + { + "params": { + "BITWIDTH": 16, + "FIFO_DEPTH": 4 + }, + "delay": 1.296 + }, + { + "params": { + "BITWIDTH": 32, + "FIFO_DEPTH": 4 + }, + "delay": 1.316 + }, + { + "params": { + "BITWIDTH": 64, + "FIFO_DEPTH": 4 + }, + "delay": 1.831 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/docs/DeveloperGuide/CompilerIntrinsics/TimingInformation.md b/docs/DeveloperGuide/CompilerIntrinsics/TimingInformation.md index 3dd16413ab..93cbd5bc56 100644 --- a/docs/DeveloperGuide/CompilerIntrinsics/TimingInformation.md +++ b/docs/DeveloperGuide/CompilerIntrinsics/TimingInformation.md @@ -202,13 +202,9 @@ The TimingModel provides several core methods: 1. **[LogicalResult getTotalDataDelay(unsigned bitwidth, double &delay)]()**: queries the total data delay at a certain bitwidth `bitwidth` and it saves the delay as a double (in nanoseconds) in the `delay` variable. -2. **[double getTotalValidDelay()]()**: returns the total valid delay as a double (in nanoseconds). +2. **[bool fromJSON(const llvm::json::Value &jsonValue, TimingModel &model, llvm::json::Path path)]()**: extracts the TimingModel information from the JSON fragment `jsonValue` located at the specified path `path` relative to the root of the full JSON structure, and stores it in the variable `model`. -3. **[double getTotalReadyDelay()]()**: returns the total ready delay as a double (in nanoseconds). - -4. **[bool fromJSON(const llvm::json::Value &jsonValue, TimingModel &model, llvm::json::Path path)]()**: extracts the TimingModel information from the JSON fragment `jsonValue` located at the specified path `path` relative to the root of the full JSON structure, and stores it in the variable `model`. - -5. **[bool fromJSON(const llvm::json::Value &jsonValue, TimingModel::PortModel &model, llvm::json::Path path)]()**: extracts the PortModel information from the JSON fragment `jsonValue` located at the specified path `path` relative to the root of the full JSON structure, and stores it in the variable `model`. +3. **[bool fromJSON(const llvm::json::Value &jsonValue, TimingModel::PortModel &model, llvm::json::Path path)]()**: extracts the PortModel information from the JSON fragment `jsonValue` located at the specified path `path` relative to the root of the full JSON structure, and stores it in the variable `model`. The LogicalResult or boolean types of these functions represent the successful or unsuccessful execution of the function. diff --git a/include/dynamatic/Support/MILP.h b/include/dynamatic/Support/MILP.h index 39b376ea17..bcee52d131 100644 --- a/include/dynamatic/Support/MILP.h +++ b/include/dynamatic/Support/MILP.h @@ -179,6 +179,10 @@ class MILP { /// Gurobi model holding the MILP's state. std::unique_ptr model; + /// Path to a file at which to store the MILP's model and its solution after + /// optimization. Empty disables logging. + std::string writeTo; + /// Fills in the argument with the desired results extract from the MILP's /// solution. Called by `MILP::getResult` after checking that the underlying /// MILP model was optimized successfully. This cannot fail. @@ -199,11 +203,6 @@ class MILP { /// MILP's state, which changes during the object's lifetime. State state = State::FAILED_TO_SETUP; - /// Path to a file at which to store the MILP's model and its solution after - /// optimization. The model will be stored under `writeTo`_model.lp and the - /// solution under `writeTo`_solution.json. Nothing will be stored if the - /// string is empty. - std::string writeTo; /// Returns a description of the MILP's current state. StringRef getStateMessage() { @@ -223,18 +222,6 @@ class MILP { } }; -/// Creates, optimizes, and extract results from an MILP in one go. Fails and -/// displays an error message to stderr if any step along the process fails. -/// Otherwise succeeds and stores the MILP's results in the first function -/// argument. -template -LogicalResult solveMILP(MILPRes &milpResult, Args &&...args) { - MILP milp = MILP(std::forward(args)...); - if (failed(milp.optimize()) || failed(milp.getResult(milpResult))) - return failure(); - return success(); -} - } // namespace dynamatic #endif // DYNAMATIC_SUPPORT_MILP_H diff --git a/include/dynamatic/Support/TimingModels.h b/include/dynamatic/Support/TimingModels.h index 01f0a0d2b0..a9266328c6 100644 --- a/include/dynamatic/Support/TimingModels.h +++ b/include/dynamatic/Support/TimingModels.h @@ -269,16 +269,6 @@ struct TimingModel { /// bitwidth in the model. On success, sets the last argument to the data /// delay. LogicalResult getTotalDataDelay(unsigned bitwidth, double &delay) const; - - /// Returns the total valid delay (input + internal + output delays). - double getTotalValidDelay() const { - return inputModel.validDelay + validDelay + outputModel.validDelay; - }; - - /// Returns the total ready delay (input + internal + output delays). - double getTotalReadyDelay() const { - return inputModel.readyDelay + readyDelay + outputModel.readyDelay; - }; }; /// Deserializes a JSON value into a TimingModel. See ::llvm::json::Value's @@ -291,6 +281,56 @@ bool fromJSON(const llvm::json::Value &jsonValue, TimingModel &model, bool fromJSON(const llvm::json::Value &jsonValue, TimingModel::PortModel &model, llvm::json::Path path); +/// Input or output of a unit. +/// signal is stored separately from the rest of the port name +/// since it affects which CPVar the delay applies to +struct SpecTimingEndpoint { + std::string port; + std::string signal; +}; + +/// One measurement of an internal delay at one combination of unit parameters +/// (e.g., BITWIDTH=16, FIFO_DEPTH=4). +struct SpecTimingSample { + llvm::StringMap params; + double delay; +}; + +/// One characterised combinational path between two pins of an op, +/// loaded from the spec-timing JSON. +/// Used for ops whose internal timing requires more expressivity +struct SpecTimingEdge { + /// input to the unit + SpecTimingEndpoint from; + + /// output of the unit + SpecTimingEndpoint to; + + /// List of sampled parameters + delay + /// present from the JSON file + std::vector samples; +}; + +/// Per-port boundary delay +/// (input pin -> register or register -> output pin). +struct SpecTimingPortDelay { + SpecTimingEndpoint port; + + /// List of sampled parameters + delay + /// present from the JSON file + std::vector samples; +}; + +/// A full timing model for an operation +/// with full expressivity of +/// internal, input, and output delay +/// for parameterized units +struct SpecTimingModel { + std::vector pin2pin; + std::vector pin2reg; + std::vector reg2pin; +}; + /// Holds the timing models for a set of operations (internally identified by /// their unique timing model key), usually parsed from a JSON file. The class /// provides accessor methods to quickly get specific information from the @@ -341,10 +381,27 @@ class TimingDatabase { static LogicalResult readFromJSON(std::string &jsonPath, TimingDatabase &timingDB); + /// Parses a JSON file whose path is given as argument and adds all the + /// spec timing models it contains to the passed timing database. + static LogicalResult readSpecTimingFromJSON(std::string &jsonPath, + TimingDatabase &timingDB); + + /// Returns the spec timing model for the given op-name, + /// or nullptr if none was loaded. + const SpecTimingModel *getSpecModel(StringRef timingModelKey) const; + + /// Convenience overload returning the spec timing model corresponding to + /// the op (looked up by op->getName().getStringRef()). + const SpecTimingModel *getSpecModel(Operation *op) const; + private: /// Maps from an operation's timing key to their timing model. /// Timing keys are generated based on operation name and implementation llvm::StringMap models; + + /// Maps from an operation's timing key to their timing model. + /// for operations which require more expressivity + llvm::StringMap specModels; }; /// Deserializes a JSON value into a TimingDatabase. See ::llvm::json::Value's diff --git a/include/dynamatic/Transforms/BufferPlacement/FPGA20Buffers.h b/include/dynamatic/Transforms/BufferPlacement/FPGA20Buffers.h index 201f11043f..58bb248ac4 100644 --- a/include/dynamatic/Transforms/BufferPlacement/FPGA20Buffers.h +++ b/include/dynamatic/Transforms/BufferPlacement/FPGA20Buffers.h @@ -68,6 +68,8 @@ class FPGA20Buffers : public BufferPlacementMILP { /// the system's objective. Called by the constructor in the absence of prior /// failures, after which the MILP is ready to be optimized. void setup(); + + void addSpecUnitDataPathConstraints(Operation *unit); }; } // namespace fpga20 diff --git a/include/dynamatic/Transforms/BufferPlacement/FPL22Buffers.h b/include/dynamatic/Transforms/BufferPlacement/FPL22Buffers.h index 1a7cbab027..c4ba897c62 100644 --- a/include/dynamatic/Transforms/BufferPlacement/FPL22Buffers.h +++ b/include/dynamatic/Transforms/BufferPlacement/FPL22Buffers.h @@ -40,7 +40,7 @@ class FPL22BuffersBase : public BufferPlacementMILP { FuncInfo &funcInfo, const TimingDatabase &timingDB, double targetPeriod, StringRef writeTo = "") : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, - targetPeriod, writeTo) {}; + targetPeriod, Algorithm::FPL22, writeTo) {}; /// Interprets the MILP solution to derive buffer placement decisions. Since /// the MILP cannot encode the placement of both opaque and transparent slots @@ -69,6 +69,14 @@ class FPL22BuffersBase : public BufferPlacementMILP { /// `filter` function. void addUnitMixedPathConstraints(Operation *unit, ChannelFilter filter = nullFilter); + + /// Adds all combinational delay constraints for a unit whose timing comes + /// from a `SpecTimingModel` in the spec-timing JSON (per-port-pair edges). + /// Replaces the per-signal `addUnitTimingConstraints` + mixed-domain + /// `addUnitMixedPathConstraints` calls for that unit. The sample with the + /// closest sweep-parameter match to the unit's instance is used. + void addSpecUnitPathConstraints(Operation *unit, + ChannelFilter filter = nullFilter); }; /// This MILP operates on the channels and units from a single CFDFC union diff --git a/include/dynamatic/Transforms/BufferPlacement/Utils/BufferPlacementMILP.h b/include/dynamatic/Transforms/BufferPlacement/Utils/BufferPlacementMILP.h index 2d725b0d67..fed996e620 100644 --- a/include/dynamatic/Transforms/BufferPlacement/Utils/BufferPlacementMILP.h +++ b/include/dynamatic/Transforms/BufferPlacement/Utils/BufferPlacementMILP.h @@ -140,11 +140,15 @@ struct MILPVars { /// some pre/post-processind steps and verification they are likely to need. class BufferPlacementMILP : public MILP { public: + // Enum representation of algorithm class + enum class Algorithm { FPGA20, FPL22, FPGA24, CostAware, MAPBUF }; + /// Contains timing characterizations for dataflow components required to /// create the MILP constraints. const TimingDatabase &timingDB; /// Target clock period. const double targetPeriod; + const Algorithm algorithm; /// Starts setting up a the buffer placement MILP for a Handshake function /// (with its CFDFCs) with specific component timing models. The constructor @@ -154,7 +158,19 @@ class BufferPlacementMILP : public MILP { /// constructor sets the `unsatisfiable` flag to true. BufferPlacementMILP(CPSolver::SolverKind solverKind, int timeout, FuncInfo &funcInfo, const TimingDatabase &timingDB, - double targetPeriod, llvm::StringRef writeTo = ""); + double targetPeriod, Algorithm algorithm, + llvm::StringRef writeTo = ""); + + /// Creates, optimizes, and extract results from an MILP in one go. Fails and + /// displays an error message to stderr if any step along the process fails. + /// Otherwise succeeds and stores the MILP's results in the first function + /// argument. + /// + /// if calculatePathDelays is true, + /// it asks the MILP to lock in the buffering decisions, and re-run to + /// calculate only the path delays. Useful for evaluation of modelling + /// accuracy. + LogicalResult solve(BufferPlacement &placement, bool calculatePathDelays); protected: /// Represents a list of signals that are buffered together by a single @@ -490,6 +506,14 @@ class BufferPlacementMILP : public MILP { /// properties mapping and defines a large constant used for elasticity /// constraints. void initialize(); + + /// Re-run the MILP with the buffering decisions locked-in, + /// in order to calculate the actual delays the MILP sees + /// from the characterization approach + /// + /// What buffering decisions to fix is algorithm dependent. + /// Currently only implemented for FPGA'20 and FPL'22. + LogicalResult calculatePathDelays(); }; } // namespace buffer diff --git a/include/dynamatic/Transforms/Passes.td b/include/dynamatic/Transforms/Passes.td index 9da4ca4229..7db0101a3a 100644 --- a/include/dynamatic/Transforms/Passes.td +++ b/include/dynamatic/Transforms/Passes.td @@ -280,6 +280,10 @@ def HandshakePlaceBuffers : Pass<"handshake-place-buffers"> { "Timeout (in seconds) for the buffer placement MILP (0 for no timeout)">, Option<"dumpMILPModels", "dump-milp-models", "bool", "false", "If true, dump MILP models into a directory named 'buffer-placement'.">, + Option<"calculatePathDelays", "calculate-path-delays", "bool", "false", + "After buffer placement, re-run the MILP with the buffering decisions " + "locked in to calculate the path delays the MILP believes are present in " + "the circuit.">, Option<"blifFiles", "blif-files", "std::string", "", "Path to AND-Inverter Graphs of dataflow components in Berkeley " "Logic Interchange Format (BLIF).">, diff --git a/integration-test/fixed/fixed.c b/integration-test/fixed/fixed.c index 42b76c4950..ea4c3b5e6c 100644 --- a/integration-test/fixed/fixed.c +++ b/integration-test/fixed/fixed.c @@ -15,7 +15,7 @@ float fixed(in_float_t y) { c = x0 - x1; x0 = x1; loopAgain = c >= a; - #pragma DYN speculate variable=loopAgain max_predictions=3 style=standard + #pragma DYN speculate variable=loopAgain max_predictions=4 style=standard } while (loopAgain); return x1; } diff --git a/integration-test/if_convert/if_convert.c b/integration-test/if_convert/if_convert.c index 4f7aeca5bf..030568607a 100644 --- a/integration-test/if_convert/if_convert.c +++ b/integration-test/if_convert/if_convert.c @@ -9,7 +9,7 @@ void if_convert(in_int_t a[N], inout_int_t b[N]) { do { int tmp = a[i]; bool ifPred = i * tmp < 10000; - #pragma DYN speculate variable=ifPred max_predictions=7 style=standard + #pragma DYN speculate variable=ifPred max_predictions=8 style=standard if (ifPred) { i++; } diff --git a/integration-test/loop_path/loop_path.c b/integration-test/loop_path/loop_path.c index a563821d93..935cb25b9d 100644 --- a/integration-test/loop_path/loop_path.c +++ b/integration-test/loop_path/loop_path.c @@ -15,7 +15,7 @@ void loop_path(in_int_t a[N], in_int_t b[N], inout_int_t c[N]) { i++; break_flag = (1000 - temp) <= x * temp; loopAgain = !break_flag && i < N; - #pragma DYN speculate variable=loopAgain max_predictions=3 style=standard + #pragma DYN speculate variable=loopAgain max_predictions=4 style=standard } while (loopAgain); } diff --git a/integration-test/nested_loop/cf.mlir b/integration-test/nested_loop/cf.mlir deleted file mode 100644 index d49c02ba82..0000000000 --- a/integration-test/nested_loop/cf.mlir +++ /dev/null @@ -1,34 +0,0 @@ -module { - func.func @nested_loop(%arg0: memref<1000xi32> {handshake.arg_name = "a"}, %arg1: memref<1000xi32> {handshake.arg_name = "b"}, %arg2: memref<1000xi32> {handshake.arg_name = "c"}) { - %c1_i32 = arith.constant {handshake.name = "constant0"} 1 : i32 - %c400_i32 = arith.constant {handshake.name = "constant1"} 400 : i32 - %c1000_i32 = arith.constant {handshake.name = "constant2"} 1000 : i32 - %c0_i32 = arith.constant {handshake.name = "constant3"} 0 : i32 - %c0 = arith.constant {handshake.name = "constant4"} 0 : index - %c2 = arith.constant {handshake.name = "constant5"} 2 : index - %c1 = arith.constant {handshake.name = "constant6"} 1 : index - cf.br ^bb1(%c0 : index) {handshake.name = "br0"} - ^bb1(%0: index): // 2 preds: ^bb0, ^bb4 - %1 = arith.index_cast %0 {handshake.name = "index_cast0"} : index to i32 - %2 = arith.muli %1, %c400_i32 {handshake.name = "muli0"} : i32 - cf.br ^bb2(%c0_i32 : i32) {handshake.name = "br1"} - ^bb2(%3: i32): // 2 preds: ^bb1, ^bb3 - %4 = arith.index_cast %3 {handshake.name = "index_cast1"} : i32 to index - %5 = memref.load %arg0[%4] {handshake.name = "load0"} : memref<1000xi32> - %6 = memref.load %arg1[%4] {handshake.name = "load1"} : memref<1000xi32> - %7 = arith.muli %5, %6 {handshake.name = "muli1"} : i32 - %8 = arith.addi %3, %2 {handshake.name = "addi0"} : i32 - %9 = arith.index_cast %8 {handshake.name = "index_cast2"} : i32 to index - memref.store %7, %arg2[%9] {handshake.deps = #handshake, handshake.name = "store0"} : memref<1000xi32> - %10 = arith.cmpi slt, %7, %c1000_i32 {handshake.name = "cmpi0"} : i32 - %12 = arith.addi %3, %c1_i32 {handshake.name = "addi1"} : i32 - cf.cond_br %10, ^bb2(%12 : i32), ^bb4 {handshake.name = "cond_br0"} - ^bb4: // pred: ^bb2 - %13 = arith.addi %0, %c1 {handshake.name = "addi2"} : index - %14 = arith.cmpi ult, %13, %c2 {handshake.name = "cmpi1"} : index - cf.cond_br %14, ^bb1(%13 : index), ^bb5 {handshake.name = "cond_br1"} - ^bb5: // pred: ^bb4 - return {handshake.name = "return0"} - } -} - diff --git a/integration-test/nested_loop/nested_loop.c b/integration-test/nested_loop/nested_loop.c index 3f7f72edcb..1e2601254a 100644 --- a/integration-test/nested_loop/nested_loop.c +++ b/integration-test/nested_loop/nested_loop.c @@ -15,7 +15,7 @@ void nested_loop(in_int_t a[N], in_int_t b[N], inout_int_t c[N]) { c[i + j * 400] = sum; i++; loopAgain = sum < bound; - #pragma DYN speculate variable=loopAgain max_predictions=6 style=standard + #pragma DYN speculate variable=loopAgain max_predictions=8 style=standard } while (loopAgain); } } diff --git a/integration-test/single_loop/cf.mlir b/integration-test/single_loop/cf.mlir deleted file mode 100644 index 4b7fd6ef91..0000000000 --- a/integration-test/single_loop/cf.mlir +++ /dev/null @@ -1,20 +0,0 @@ -module { - func.func @single_loop(%arg0: memref<1000xi32> {handshake.arg_name = "a"}, %arg1: memref<1000xi32> {handshake.arg_name = "b"}, %arg2: memref<1000xi32> {handshake.arg_name = "c"}) { - %c1000_i32 = arith.constant {handshake.name = "constant0"} 1000 : i32 - %c0_i32 = arith.constant {handshake.name = "constant1"} 0 : i32 - %c1_i32 = arith.constant {handshake.name = "constant2"} 1 : i32 - cf.br ^bb1(%c0_i32 : i32) {handshake.name = "br0"} - ^bb1(%0: i32): // 2 preds: ^bb0, ^bb2 - %1 = arith.index_cast %0 {handshake.name = "index_cast0"} : i32 to index - %2 = memref.load %arg0[%1] {handshake.name = "load0"} : memref<1000xi32> - %3 = memref.load %arg1[%1] {handshake.name = "load1"} : memref<1000xi32> - %4 = arith.muli %2, %3 {handshake.name = "muli0"} : i32 - memref.store %4, %arg2[%1] {handshake.deps = #handshake, handshake.name = "store0"} : memref<1000xi32> - %5 = arith.cmpi slt, %4, %c1000_i32 {handshake.name = "cmpi0"} : i32 - %6 = arith.addi %0, %c1_i32 {handshake.name = "addi0"} : i32 - cf.cond_br %5, ^bb1(%6 : i32), ^bb3 {handshake.name = "cond_br0"} - ^bb3: // pred: ^bb1 - return {handshake.name = "return0"} - } -} - diff --git a/integration-test/single_loop/single_loop.c b/integration-test/single_loop/single_loop.c index 4a4e510e5e..f795142a2e 100644 --- a/integration-test/single_loop/single_loop.c +++ b/integration-test/single_loop/single_loop.c @@ -16,7 +16,7 @@ void single_loop(in_int_t a[N], in_int_t b[N], inout_int_t c[N]) { i++; loopAgain = sum < bound; -#pragma DYN speculate variable = loopAgain max_predictions = 6 style = standard +#pragma DYN speculate variable = loopAgain max_predictions = 7 style = standard } while (loopAgain); } diff --git a/integration-test/sparse/sparse.c b/integration-test/sparse/sparse.c index f712338e91..74fbdcd74e 100644 --- a/integration-test/sparse/sparse.c +++ b/integration-test/sparse/sparse.c @@ -14,7 +14,7 @@ float sparse(in_float_t a[N], in_float_t x[N]) { sum += mul; i++; loopAgain = sum >= 0.0f; - #pragma DYN speculate variable=loopAgain max_predictions=2 style=standard + #pragma DYN speculate variable=loopAgain max_predictions=3 style=standard } while (loopAgain); return sum; } diff --git a/integration-test/subdiag/subdiag.c b/integration-test/subdiag/subdiag.c index 6ad1582973..91a2f12105 100644 --- a/integration-test/subdiag/subdiag.c +++ b/integration-test/subdiag/subdiag.c @@ -14,7 +14,7 @@ int subdiag(in_float_t d[N], in_float_t e[N]) { i++; cond_break = (e[i]) <= x * dd; loop_again = i < N_DEC && !cond_break; - #pragma DYN speculate variable = loop_again max_predictions = 8 style = standard + #pragma DYN speculate variable = loop_again max_predictions = 9 style = standard } while (loop_again); return i; } diff --git a/integration-test/subdiag_fast/subdiag_fast.c b/integration-test/subdiag_fast/subdiag_fast.c index 2c23595732..2df77fed62 100644 --- a/integration-test/subdiag_fast/subdiag_fast.c +++ b/integration-test/subdiag_fast/subdiag_fast.c @@ -14,7 +14,7 @@ int subdiag_fast(in_float_t d1[N], in_float_t d2[N], in_float_t e[N]) { i++; cond_break = (e[i]) <= x * dd; loop_again = !cond_break && i < N_DEC; - #pragma DYN speculate variable=loop_again max_predictions=16 style=standard + #pragma DYN speculate variable=loop_again max_predictions=17 style=standard } while (loop_again); return i; } diff --git a/lib/Support/TimingModels.cpp b/lib/Support/TimingModels.cpp index 5ce4ca2dba..f9cdea21b0 100644 --- a/lib/Support/TimingModels.cpp +++ b/lib/Support/TimingModels.cpp @@ -90,19 +90,7 @@ LogicalResult TimingModel::getTotalDataDelay(unsigned bitwidth, auto unitDelayOrFail = dataDelay.select(bitwidth); if (failed(unitDelayOrFail)) return failure(); - double unitDelay = unitDelayOrFail->get(); - - auto inPortDelayOrFail = inputModel.dataDelay.select(bitwidth); - if (failed(inPortDelayOrFail)) - return failure(); - double inPortDelay = inPortDelayOrFail->get(); - - auto outPortDelayOrFail = outputModel.dataDelay.select(bitwidth); - if (failed(outPortDelayOrFail)) - return failure(); - double outPortDelay = outPortDelayOrFail->get(); - - delay = unitDelay + inPortDelay + outPortDelay; + delay = unitDelayOrFail->get(); return success(); } @@ -274,14 +262,132 @@ LogicalResult TimingDatabase::getTotalDelay(Operation *op, case SignalType::DATA: return model->getTotalDataDelay(getOpDatawidth(op), delay); case SignalType::VALID: - delay = model->getTotalValidDelay(); + delay = model->validDelay; return success(); case SignalType::READY: - delay = model->getTotalReadyDelay(); + delay = model->readyDelay; return success(); } } +const SpecTimingModel * +TimingDatabase::getSpecModel(StringRef timingModelKey) const { + auto it = specModels.find(timingModelKey); + if (it == specModels.end()) + return nullptr; + return &it->second; +} + +const SpecTimingModel *TimingDatabase::getSpecModel(Operation *op) const { + if (!op) + return nullptr; + return getSpecModel(op->getName().getStringRef()); +} + +LogicalResult TimingDatabase::readSpecTimingFromJSON(std::string &jsonpath, + TimingDatabase &timingDB) { + std::ifstream inputFile(jsonpath); + if (!inputFile.is_open()) { + llvm::errs() << "Failed to open spec timing JSON at \"" << jsonpath + << "\"\n"; + return failure(); + } + + std::string jsonString; + std::string line; + while (std::getline(inputFile, line)) + jsonString += line; + + llvm::Expected value = ljson::parse(jsonString); + if (!value) { + llvm::errs() << "Failed to parse spec timing JSON in \"" << jsonpath + << "\"\n"; + return failure(); + } + + const ljson::Object *root = value->getAsObject(); + if (!root) { + llvm::errs() << "Spec timing JSON root must be an object\n"; + return failure(); + } + + for (const auto &unitEntry : *root) { + StringRef unitKey = unitEntry.first; + const ljson::Object *unitObj = unitEntry.second.getAsObject(); + if (!unitObj) + continue; + SpecTimingModel model; + + auto parseSamples = [](const ljson::Array *samplesArr, + std::vector &out) { + if (!samplesArr) + return; + for (const ljson::Value &sampleVal : *samplesArr) { + const ljson::Object *sampleObj = sampleVal.getAsObject(); + if (!sampleObj) + continue; + SpecTimingSample sample; + if (auto d = sampleObj->getNumber("delay")) + sample.delay = *d; + else + continue; + if (const ljson::Object *paramObj = sampleObj->getObject("params")) { + for (const auto ¶mEntry : *paramObj) { + if (auto pv = paramEntry.second.getAsInteger()) + sample.params[paramEntry.first] = *pv; + } + } + out.push_back(std::move(sample)); + } + }; + + if (const ljson::Array *pin2pinArr = unitObj->getArray("pin2pin")) { + for (const ljson::Value &edgeVal : *pin2pinArr) { + const ljson::Object *edgeObj = edgeVal.getAsObject(); + if (!edgeObj) + continue; + SpecTimingEdge edge; + for (const auto &which : {std::make_pair("from", &edge.from), + std::make_pair("to", &edge.to)}) { + const ljson::Object *endObj = edgeObj->getObject(which.first); + if (!endObj) + continue; + if (auto p = endObj->getString("port")) + which.second->port = p->str(); + if (auto s = endObj->getString("signal")) + which.second->signal = s->str(); + } + parseSamples(edgeObj->getArray("samples"), edge.samples); + model.pin2pin.push_back(std::move(edge)); + } + } + + auto parsePortDelayArr = [&](const char *key, + std::vector &out) { + const ljson::Array *arr = unitObj->getArray(key); + if (!arr) + return; + for (const ljson::Value &v : *arr) { + const ljson::Object *o = v.getAsObject(); + if (!o) + continue; + SpecTimingPortDelay pd; + if (auto p = o->getString("port")) + pd.port.port = p->str(); + if (auto s = o->getString("signal")) + pd.port.signal = s->str(); + parseSamples(o->getArray("samples"), pd.samples); + out.push_back(std::move(pd)); + } + }; + parsePortDelayArr("pin2reg", model.pin2reg); + parsePortDelayArr("reg2pin", model.reg2pin); + + timingDB.specModels[unitKey] = std::move(model); + } + return success(); +} + LogicalResult TimingDatabase::readFromJSON(std::string &jsonpath, TimingDatabase &timingDB) { // Open the timing database diff --git a/lib/Transforms/BufferPlacement/CostAwareBuffers.cpp b/lib/Transforms/BufferPlacement/CostAwareBuffers.cpp index 70bd283050..f66427cd59 100644 --- a/lib/Transforms/BufferPlacement/CostAwareBuffers.cpp +++ b/lib/Transforms/BufferPlacement/CostAwareBuffers.cpp @@ -34,7 +34,7 @@ CostAwareBuffers::CostAwareBuffers(CPSolver::SolverKind solverKind, int timeout, const TimingDatabase &timingDB, double targetPeriod, StringRef writeTo) : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, targetPeriod, - writeTo) { + Algorithm::CostAware, writeTo) { if (!unsatisfiable) setup(); } diff --git a/lib/Transforms/BufferPlacement/FPGA20Buffers.cpp b/lib/Transforms/BufferPlacement/FPGA20Buffers.cpp index 8dadc3d5b4..551294263c 100644 --- a/lib/Transforms/BufferPlacement/FPGA20Buffers.cpp +++ b/lib/Transforms/BufferPlacement/FPGA20Buffers.cpp @@ -17,6 +17,8 @@ #include "dynamatic/Support/TimingModels.h" #include "dynamatic/Transforms/BufferPlacement/Utils/BufferingSupport.h" #include "mlir/IR/Value.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/TypeSwitch.h" // NOTE: The code wrapped in LLVM_DEBUG(...) is executed when // - Dynamatic is built in debug mode @@ -33,11 +35,168 @@ FPGA20Buffers::FPGA20Buffers(CPSolver::SolverKind solverKind, int timeout, FuncInfo &funcInfo, const TimingDatabase &timingDB, double targetPeriod, StringRef writeTo) : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, targetPeriod, - writeTo) { + Algorithm::FPGA20, writeTo) { if (!unsatisfiable) setup(); } +void FPGA20Buffers::addSpecUnitDataPathConstraints(Operation *unit) { + const SpecTimingModel *specModel = timingDB.getSpecModel(unit); + if (!specModel) { + unit->emitError() << "addSpecUnitDataPathConstraints: no spec timing model " + << "loaded for op '" << unit->getName().getStringRef() + << "'"; + return; + } + + auto namedIO = dyn_cast(unit); + if (!namedIO) { + unit->emitError() << "addSpecUnitDataPathConstraints: op does not " + << "implement NamedIOInterface"; + return; + } + + llvm::StringMap portToValue; + for (unsigned i = 0, e = unit->getNumOperands(); i < e; ++i) + portToValue[namedIO.getOperandName(i)] = unit->getOperand(i); + for (unsigned i = 0, e = unit->getNumResults(); i < e; ++i) + portToValue[namedIO.getResultName(i)] = unit->getResult(i); + + Value bitwidthChannel = + llvm::TypeSwitch(unit) + .Case( + [](handshake::SpeculatorOp op) { return op.getDataIn(); }) + .Case( + [](handshake::SpecSaveCommitOp op) { return op.getDataIn(); }) + .Default([&](Operation *op) { + op->emitError() << "addSpecUnitDataPathConstraints called on op " + << "type with no bitwidth accessor registered"; + return Value(); + }); + if (!bitwidthChannel) + return; + + llvm::StringMap currentParams; + if (auto chTy = dyn_cast(bitwidthChannel.getType())) + currentParams["BITWIDTH"] = chTy.getDataBitWidth(); + else + currentParams["BITWIDTH"] = 0; + + auto pickClosest = + [&](const std::vector &samples) -> double { + assert(!samples.empty() && "spec timing edge has no samples"); + const SpecTimingSample *best = &samples.front(); + int64_t bestDist = std::numeric_limits::max(); + for (const auto &s : samples) { + int64_t dist = 0; + for (const auto &targetKV : currentParams) { + auto it = s.params.find(targetKV.first()); + if (it == s.params.end()) + continue; + int64_t d = it->second - targetKV.second; + dist += d * d; + } + if (dist < bestDist) { + bestDist = dist; + best = &s; + } + } + return best->delay; + }; + + StringRef unitName = getUniqueName(unit); + unsigned idx = 0; + for (const SpecTimingEdge &edge : specModel->pin2pin) { + if (edge.from.signal != "data" || edge.to.signal != "data") + continue; + auto fromIt = portToValue.find(edge.from.port); + auto toIt = portToValue.find(edge.to.port); + if (fromIt == portToValue.end()) { + unit->emitError() << "spec timing edge references unknown port '" + << edge.from.port << "'"; + return; + } + if (toIt == portToValue.end()) { + unit->emitError() << "spec timing edge references unknown port '" + << edge.to.port << "'"; + return; + } + Value fromVal = fromIt->second; + Value toVal = toIt->second; + if (vars.channelVars.find(fromVal) == vars.channelVars.end() || + vars.channelVars.find(toVal) == vars.channelVars.end()) + continue; + if (edge.samples.empty()) { + unit->emitError() << "spec timing edge " << edge.from.port << "." + << edge.from.signal << " -> " << edge.to.port << "." + << edge.to.signal << " has no samples"; + return; + } + double delay = pickClosest(edge.samples); + CPVar &tFrom = + vars.channelVars[fromVal].signalVars[SignalType::DATA].path.tOut; + CPVar &tTo = vars.channelVars[toVal].signalVars[SignalType::DATA].path.tIn; + std::string consName = + "path_spec_p2p_" + unitName.str() + "_" + std::to_string(idx++); + model->addConstr(tFrom + delay <= tTo, consName); + } + + auto unitSideDataVar = [&](Value v) -> CPVar & { + bool unitIsProducer = (v.getDefiningOp() == unit); + if (unitIsProducer) + return vars.channelVars[v].signalVars[SignalType::DATA].path.tIn; + return vars.channelVars[v].signalVars[SignalType::DATA].path.tOut; + }; + + for (const SpecTimingPortDelay &pd : specModel->pin2reg) { + if (pd.port.signal != "data") + continue; + auto it = portToValue.find(pd.port.port); + if (it == portToValue.end()) { + unit->emitError() << "spec pin2reg references unknown port '" + << pd.port.port << "'"; + return; + } + Value v = it->second; + if (vars.channelVars.find(v) == vars.channelVars.end()) + continue; + if (pd.samples.empty()) { + unit->emitError() << "spec pin2reg " << pd.port.port << "." + << pd.port.signal << " has no samples"; + return; + } + double delay = pickClosest(pd.samples); + CPVar &tArr = unitSideDataVar(v); + std::string consName = + "path_spec_p2r_" + unitName.str() + "_" + std::to_string(idx++); + model->addConstr(tArr + delay <= targetPeriod, consName); + } + + for (const SpecTimingPortDelay &pd : specModel->reg2pin) { + if (pd.port.signal != "data") + continue; + auto it = portToValue.find(pd.port.port); + if (it == portToValue.end()) { + unit->emitError() << "spec reg2pin references unknown port '" + << pd.port.port << "'"; + return; + } + Value v = it->second; + if (vars.channelVars.find(v) == vars.channelVars.end()) + continue; + if (pd.samples.empty()) { + unit->emitError() << "spec reg2pin " << pd.port.port << "." + << pd.port.signal << " has no samples"; + return; + } + double delay = pickClosest(pd.samples); + CPVar &tDep = unitSideDataVar(v); + std::string consName = + "path_spec_r2p_" + unitName.str() + "_" + std::to_string(idx++); + model->addConstr(tDep >= delay, consName); + } +} + void FPGA20Buffers::extractResult(BufferPlacement &placement) { // Iterate over all channels in the circuit for (auto &[channel, chVars] : vars.channelVars) { @@ -181,6 +340,10 @@ void FPGA20Buffers::setup() { // Add path and elasticity constraints over all units in the function for (Operation &op : funcInfo.funcOp.getOps()) { + if (isa(&op)) { + addSpecUnitDataPathConstraints(&op); + continue; + } addUnitTimingConstraints(&op, SignalType::DATA); } diff --git a/lib/Transforms/BufferPlacement/FPGA24Buffers.cpp b/lib/Transforms/BufferPlacement/FPGA24Buffers.cpp index fc8fda484b..a6cdfba55f 100644 --- a/lib/Transforms/BufferPlacement/FPGA24Buffers.cpp +++ b/lib/Transforms/BufferPlacement/FPGA24Buffers.cpp @@ -49,8 +49,8 @@ LatencyBalancingMILP::LatencyBalancingMILP( ArrayRef reconvergentPaths, ArrayRef syncCyclePairs, const SynchronizingCyclesFinderGraph &syncGraph, ArrayRef cfdfcs) - : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, - targetPeriod), + : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, targetPeriod, + Algorithm::FPGA24), reconvergentPaths(reconvergentPaths.begin(), reconvergentPaths.end()), syncCyclePairs(syncCyclePairs.begin(), syncCyclePairs.end()), syncGraph(syncGraph), cfdfcs(cfdfcs.begin(), cfdfcs.end()) { @@ -122,8 +122,8 @@ OccupancyBalancingLP::OccupancyBalancingLP( const LatencyBalancingResult &latencyResult, ArrayRef reconvergentPaths, ArrayRef cfdfcs) - : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, - targetPeriod), + : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, targetPeriod, + Algorithm::FPGA24), latencyResult(latencyResult), reconvergentPaths(reconvergentPaths.begin(), reconvergentPaths.end()), cfdfcs(cfdfcs.begin(), cfdfcs.end()) { diff --git a/lib/Transforms/BufferPlacement/FPL22Buffers.cpp b/lib/Transforms/BufferPlacement/FPL22Buffers.cpp index 2c4ccc2fdb..687ba7ccd9 100644 --- a/lib/Transforms/BufferPlacement/FPL22Buffers.cpp +++ b/lib/Transforms/BufferPlacement/FPL22Buffers.cpp @@ -20,6 +20,7 @@ #include "dynamatic/Transforms/BufferPlacement/Utils/CFDFC.h" #include "llvm/ADT/TypeSwitch.h" #include +#include #include // NOTE: The code wrapped in LLVM_DEBUG(...) is executed when @@ -301,6 +302,185 @@ void FPL22BuffersBase::addUnitMixedPathConstraints(Operation *unit, } } +void FPL22BuffersBase::addSpecUnitPathConstraints(Operation *unit, + ChannelFilter filter) { + const SpecTimingModel *specModel = timingDB.getSpecModel(unit); + if (!specModel) { + unit->emitError() << "addSpecUnitPathConstraints: no spec timing model " + << "loaded for op '" << unit->getName().getStringRef() + << "'"; + return; + } + + auto namedIO = dyn_cast(unit); + if (!namedIO) { + unit->emitError() << "addSpecUnitPathConstraints: op does not implement " + << "NamedIOInterface"; + return; + } + + // Build the per-instance port-name -> SSA Value map by asking the op for + // each operand/result's name. + llvm::StringMap portToValue; + for (unsigned i = 0, e = unit->getNumOperands(); i < e; ++i) + portToValue[namedIO.getOperandName(i)] = unit->getOperand(i); + for (unsigned i = 0, e = unit->getNumResults(); i < e; ++i) + portToValue[namedIO.getResultName(i)] = unit->getResult(i); + + // Determine the current sweep-parameter targets for this op instance. Each + // op type names its bitwidth-carrying channel differently; dispatch. + Value bitwidthChannel = + llvm::TypeSwitch(unit) + .Case( + [](handshake::SpeculatorOp op) { return op.getDataIn(); }) + .Case( + [](handshake::SpecSaveCommitOp op) { return op.getDataIn(); }) + .Default([&](Operation *op) { + op->emitError() << "addSpecUnitPathConstraints called on op type " + << "with no bitwidth accessor registered"; + return Value(); + }); + if (!bitwidthChannel) + return; + llvm::StringMap currentParams; + if (auto chTy = dyn_cast(bitwidthChannel.getType())) + currentParams["BITWIDTH"] = chTy.getDataBitWidth(); + else + currentParams["BITWIDTH"] = 0; + + auto parseSignal = [](StringRef s) -> std::optional { + if (s == "data") + return SignalType::DATA; + if (s == "valid") + return SignalType::VALID; + if (s == "ready") + return SignalType::READY; + return std::nullopt; + }; + + auto pickClosest = + [&](const std::vector &samples) -> double { + assert(!samples.empty() && "spec timing edge has no samples"); + const SpecTimingSample *best = &samples.front(); + int64_t bestDist = std::numeric_limits::max(); + for (const auto &s : samples) { + int64_t dist = 0; + for (const auto &targetKV : currentParams) { + auto it = s.params.find(targetKV.first()); + if (it == s.params.end()) + continue; + int64_t d = it->second - targetKV.second; + dist += d * d; + } + if (dist < bestDist) { + bestDist = dist; + best = &s; + } + } + return best->delay; + }; + + StringRef unitName = getUniqueName(unit); + unsigned idx = 0; + for (const SpecTimingEdge &edge : specModel->pin2pin) { + auto fromIt = portToValue.find(edge.from.port); + auto toIt = portToValue.find(edge.to.port); + if (fromIt == portToValue.end()) { + unit->emitError() << "spec timing edge references unknown port '" + << edge.from.port << "'"; + return; + } + if (toIt == portToValue.end()) { + unit->emitError() << "spec timing edge references unknown port '" + << edge.to.port << "'"; + return; + } + std::optional fromSig = parseSignal(edge.from.signal); + std::optional toSig = parseSignal(edge.to.signal); + if (!fromSig || !toSig) { + unit->emitError() << "spec timing edge has unrecognised signal kind '" + << edge.from.signal << "' or '" << edge.to.signal + << "'"; + return; + } + + Value fromVal = fromIt->second; + Value toVal = toIt->second; + if (!filter(fromVal) || !filter(toVal)) + continue; + if (edge.samples.empty()) { + unit->emitError() << "spec timing edge " << edge.from.port << "." + << edge.from.signal << " -> " << edge.to.port << "." + << edge.to.signal << " has no samples"; + return; + } + + double delay = pickClosest(edge.samples); + + CPVar &tFrom = vars.channelVars[fromVal].signalVars[*fromSig].path.tOut; + CPVar &tTo = vars.channelVars[toVal].signalVars[*toSig].path.tIn; + std::string consName = + "path_spec_p2p_" + unitName.str() + "_" + std::to_string(idx++); + model->addConstr(tFrom + delay <= tTo, consName); + } + + for (const SpecTimingPortDelay &pd : specModel->pin2reg) { + auto it = portToValue.find(pd.port.port); + if (it == portToValue.end()) { + unit->emitError() << "spec pin2reg references unknown port '" + << pd.port.port << "'"; + return; + } + std::optional sig = parseSignal(pd.port.signal); + if (!sig) { + unit->emitError() << "spec pin2reg has unrecognised signal kind '" + << pd.port.signal << "'"; + return; + } + Value v = it->second; + if (!filter(v)) + continue; + if (pd.samples.empty()) { + unit->emitError() << "spec pin2reg " << pd.port.port << "." + << pd.port.signal << " has no samples"; + return; + } + double delay = pickClosest(pd.samples); + std::string consName = + "path_spec_p2r_" + unitName.str() + "_" + std::to_string(idx++); + CPVar &tInPort = vars.channelVars[v].signalVars[*sig].path.tOut; + model->addConstr(tInPort + delay <= targetPeriod, consName); + } + + for (const SpecTimingPortDelay &pd : specModel->reg2pin) { + auto it = portToValue.find(pd.port.port); + if (it == portToValue.end()) { + unit->emitError() << "spec reg2pin references unknown port '" + << pd.port.port << "'"; + return; + } + std::optional sig = parseSignal(pd.port.signal); + if (!sig) { + unit->emitError() << "spec reg2pin has unrecognised signal kind '" + << pd.port.signal << "'"; + return; + } + Value v = it->second; + if (!filter(v)) + continue; + if (pd.samples.empty()) { + unit->emitError() << "spec reg2pin " << pd.port.port << "." + << pd.port.signal << " has no samples"; + return; + } + double delay = pickClosest(pd.samples); + std::string consName = + "path_spec_r2p_" + unitName.str() + "_" + std::to_string(idx++); + CPVar &tOutPort = vars.channelVars[v].signalVars[*sig].path.tIn; + model->addConstr(tOutPort >= delay, consName); + } +} + CFDFCUnionBuffers::CFDFCUnionBuffers(CPSolver::SolverKind solverKind, int timeout, FuncInfo &funcInfo, const TimingDatabase &timingDB, @@ -369,6 +549,10 @@ void CFDFCUnionBuffers::setup() { // Add single-domain and mixed-domain path constraints as well as elasticity // constraints over all units in the CFDFC union for (Operation *unit : cfUnion.units) { + if (isa(unit)) { + addSpecUnitPathConstraints(unit, channelFilter); + continue; + } addUnitTimingConstraints(unit, SignalType::DATA, channelFilter); addUnitTimingConstraints(unit, SignalType::VALID, channelFilter); addUnitTimingConstraints(unit, SignalType::READY, channelFilter); @@ -483,6 +667,10 @@ void OutOfCycleBuffers::setup() { if (cfUnion.units.contains(&unit)) continue; + if (isa(&unit)) { + addSpecUnitPathConstraints(&unit, channelFilter); + continue; + } addUnitTimingConstraints(&unit, SignalType::DATA, channelFilter); addUnitTimingConstraints(&unit, SignalType::VALID, channelFilter); addUnitTimingConstraints(&unit, SignalType::READY, channelFilter); diff --git a/lib/Transforms/BufferPlacement/HandshakePlaceBuffers.cpp b/lib/Transforms/BufferPlacement/HandshakePlaceBuffers.cpp index e8230b7cf0..456544c7d7 100644 --- a/lib/Transforms/BufferPlacement/HandshakePlaceBuffers.cpp +++ b/lib/Transforms/BufferPlacement/HandshakePlaceBuffers.cpp @@ -213,6 +213,12 @@ LogicalResult HandshakePlaceBuffersPass::placeUsingMILP() { TimingDatabase timingDB; if (failed(TimingDatabase::readFromJSON(timingModels, timingDB))) return failure(); + // Optional per-port-pair timing for ops like SpeculatorOp / + // SpecSaveCommitOp; missing file is OK and leaves spec models empty. + std::string specTimingPath = + llvm::sys::path::parent_path(timingModels).str() + "/spec-timing.json"; + if (failed(TimingDatabase::readSpecTimingFromJSON(specTimingPath, timingDB))) + return failure(); auto &cfdfcAnalysis = getAnalysis(); @@ -499,8 +505,9 @@ LogicalResult HandshakePlaceBuffersPass::solveBufferPlacementMILP( if (dumpMILPModels) { writeTo = dumpDir + sep + funcName + "-fpga20-buffers"; } - return solveMILP(placement, solverKind, timeout, - info, timingDB, targetCP, writeTo); + fpga20::FPGA20Buffers milp(solverKind, timeout, info, timingDB, targetCP, + writeTo); + return milp.solve(placement, calculatePathDelays); } if (algorithm == FPL22) { // Create disjoint block unions of all CFDFCs @@ -519,9 +526,9 @@ LogicalResult HandshakePlaceBuffersPass::solveBufferPlacementMILP( if (dumpMILPModels) { writeTo = dumpDir + sep + funcName + "-cfunion" + std::to_string(idx); } - if (failed(solveMILP( - placement, solverKind, timeout, info, timingDB, targetCP, cfUnion, - writeTo))) + fpl22::CFDFCUnionBuffers milp(solverKind, timeout, info, timingDB, + targetCP, cfUnion, writeTo); + if (failed(milp.solve(placement, calculatePathDelays))) return failure(); } @@ -530,8 +537,9 @@ LogicalResult HandshakePlaceBuffersPass::solveBufferPlacementMILP( } // Solve last MILP on channels/units that are not part of any CFDFC - return solveMILP( - placement, solverKind, timeout, info, timingDB, targetCP, writeTo); + fpl22::OutOfCycleBuffers milp(solverKind, timeout, info, timingDB, targetCP, + writeTo); + return milp.solve(placement, calculatePathDelays); } if (algorithm == FPGA24) { @@ -544,8 +552,9 @@ LogicalResult HandshakePlaceBuffersPass::solveBufferPlacementMILP( writeTo = dumpDir + sep + funcName + "-cost-aware"; } // Create and solve the MILP - return solveMILP( - placement, solverKind, timeout, info, timingDB, targetCP, writeTo); + costaware::CostAwareBuffers milp(solverKind, timeout, info, timingDB, + targetCP, writeTo); + return milp.solve(placement, calculatePathDelays); } if (algorithm == MAPBUF) { @@ -553,9 +562,10 @@ LogicalResult HandshakePlaceBuffersPass::solveBufferPlacementMILP( writeTo = dumpDir + sep + funcName + "-mapbuf"; } // Create and solve the MILP - return solveMILP( - placement, solverKind, timeout, info, timingDB, targetCP, blifFiles, - lutDelay, lutSize, acyclicType, writeTo); + mapbuf::MAPBUFBuffers milp(solverKind, timeout, info, timingDB, targetCP, + blifFiles, lutDelay, lutSize, acyclicType, + writeTo); + return milp.solve(placement, calculatePathDelays); } llvm_unreachable("unknown algorithm"); diff --git a/lib/Transforms/BufferPlacement/HandshakeSetBufferingProperties.cpp b/lib/Transforms/BufferPlacement/HandshakeSetBufferingProperties.cpp index 51fc5a7348..f697b42b33 100644 --- a/lib/Transforms/BufferPlacement/HandshakeSetBufferingProperties.cpp +++ b/lib/Transforms/BufferPlacement/HandshakeSetBufferingProperties.cpp @@ -95,13 +95,9 @@ setSpeculatorBufferingProperties(handshake::FuncOp funcOp) { Channel resolveChannel(historyCtrl, true); resolveChannel.props->minTrans = std::max(resolveChannel.props->minTrans, 1U); - // The speculator's KILL_ONLY_DATA state stalls the data input for - // 1 cycle during misspeculation recovery. A transparent buffer on - // the data input absorbs this stall and prevents it from propagating - // upstream and causing throughput loss. - Value dataIn = specOp.getDataIn(); - Channel dataInChannel(dataIn, true); - dataInChannel.props->minTrans = std::max(dataInChannel.props->minTrans, 1U); + Value trigger = specOp.getTrigger(); + Channel triggerChannel(trigger, true); + triggerChannel.props->minTrans = std::max(triggerChannel.props->minTrans, 1U); return success(); } diff --git a/lib/Transforms/BufferPlacement/MAPBUFBuffers.cpp b/lib/Transforms/BufferPlacement/MAPBUFBuffers.cpp index 4e9a5ed12e..504bdbc0f7 100644 --- a/lib/Transforms/BufferPlacement/MAPBUFBuffers.cpp +++ b/lib/Transforms/BufferPlacement/MAPBUFBuffers.cpp @@ -46,7 +46,7 @@ MAPBUFBuffers::MAPBUFBuffers(CPSolver::SolverKind solverKind, int timeout, double lutDelay, int lutSize, bool acyclicType, StringRef writeTo) : BufferPlacementMILP(solverKind, timeout, funcInfo, timingDB, targetPeriod, - writeTo), + Algorithm::MAPBUF, writeTo), acyclicType(acyclicType), lutSize(lutSize), lutDelay(lutDelay), blifFiles(blifFiles) { if (!unsatisfiable) diff --git a/lib/Transforms/BufferPlacement/Utils/BufferPlacementMILP.cpp b/lib/Transforms/BufferPlacement/Utils/BufferPlacementMILP.cpp index 7c617b3a96..a256e282cc 100644 --- a/lib/Transforms/BufferPlacement/Utils/BufferPlacementMILP.cpp +++ b/lib/Transforms/BufferPlacement/Utils/BufferPlacementMILP.cpp @@ -227,9 +227,9 @@ double BufferPlacementMILP::BufferingGroup::getCombinationalDelay( (void)bufModel->getTotalDataDelay(bitwidth, delay); return delay; case SignalType::VALID: - return bufModel->getTotalValidDelay(); + return bufModel->validDelay; case SignalType::READY: - return bufModel->getTotalReadyDelay(); + return bufModel->readyDelay; } } @@ -237,12 +237,24 @@ BufferPlacementMILP::BufferPlacementMILP(CPSolver::SolverKind solverKind, int timeout, FuncInfo &funcInfo, const TimingDatabase &timingDB, double targetPeriod, + Algorithm algorithm, llvm::StringRef writeTo) : MILP(solverKind, timeout, writeTo), timingDB(timingDB), - targetPeriod(targetPeriod), funcInfo(funcInfo) { + targetPeriod(targetPeriod), algorithm(algorithm), funcInfo(funcInfo) { initialize(); } +LogicalResult BufferPlacementMILP::solve(BufferPlacement &placement, + bool calculatePathDelays) { + if (failed(optimize())) + return failure(); + if (calculatePathDelays && failed(this->calculatePathDelays())) + return failure(); + if (failed(getResult(placement))) + return failure(); + return success(); +} + void BufferPlacementMILP::addChannelVars(Value channel, ArrayRef signalTypes) { @@ -1663,3 +1675,51 @@ void BufferPlacementMILP::initialize() { auto ops = funcInfo.funcOp.getOps(); largeCst = std::distance(ops.begin(), ops.end()) + 2; } + +LogicalResult BufferPlacementMILP::calculatePathDelays() { + if (algorithm != Algorithm::FPGA20 && algorithm != Algorithm::FPL22) { + llvm::errs() << "BufferPlacementMILP::calculatePathDelays() only supports " + << "FPGA20 and FPL22 buffering algorithms\n"; + return failure(); + } + + // Pin all buffer-decision variables to their currently-solved values so the + // re-solve cannot move them. + // Pin only the variables used by FPGA'20 and FPL'22 + auto pinVar = [&](CPVar &v) { + model->addConstr(v == model->getValue(v), "pin_buffers"); + }; + for (auto &[channel, chVars] : vars.channelVars) { + pinVar(chVars.bufPresent); + pinVar(chVars.bufNumSlots); + for (auto &[sigType, sigVars] : chVars.signalVars) + pinVar(sigVars.bufPresent); + } + + // Replace objective with minimizing path delay + // since buffer positions are fixed this is equivalent to + // calculating path delays + LinExpr minSum; + for (auto &[channel, chVars] : vars.channelVars) { + for (auto &[sigType, sigVars] : chVars.signalVars) { + minSum += sigVars.path.tIn; + minSum += sigVars.path.tOut; + } + } + model->setMaximizeObjective(-minSum); + + // actually solve + model->optimize(); + if (model->status != CPSolver::OPTIMAL && + model->status != CPSolver::NONOPTIMAL) { + llvm::errs() << "calculatePathDelays: re-solve failed status=" + << model->status << "\n"; + return failure(); + } + + // and store the MILP solution but this time + // with accurate path delay numbers + if (!writeTo.empty()) + model->writeSol(writeTo + "_accurate_delays.log"); + return success(); +} diff --git a/spec-only.json b/spec-only.json new file mode 100644 index 0000000000..f5b68c33ab --- /dev/null +++ b/spec-only.json @@ -0,0 +1,22 @@ +[ + { "generic": "$DYNAMATIC/data/vhdl/support/types.vhd" }, + { "generic": "$DYNAMATIC/data/vhdl/support/spec_types.vhd" }, + { + "name": "handshake.speculator", + "parameters": [ + { "name": "BITWIDTH", "type": "int" }, + { "name": "FIFO_DEPTH", "type": "unsigned" } + ], + "generator": "python $DYNAMATIC/tools/unit-generators/vhdl/vhdl-unit-generator.py -n $MODULE_NAME -o $OUTPUT_DIR/$MODULE_NAME.vhd -t speculator -p fifo_depth=$FIFO_DEPTH bitwidth=$BITWIDTH extra_signals='{\"spec\":1}'", + "dependencies": ["types", "spec_types"] + }, + { + "name": "handshake.spec_save_commit", + "parameters": [ + { "name": "BITWIDTH", "type": "int" }, + { "name": "FIFO_DEPTH", "type": "unsigned" } + ], + "generator": "python $DYNAMATIC/tools/unit-generators/vhdl/vhdl-unit-generator.py -n $MODULE_NAME -o $OUTPUT_DIR/$MODULE_NAME.vhd -t spec_save_commit -p fifo_depth=$FIFO_DEPTH bitwidth=$BITWIDTH extra_signals='{\"spec\":1}'", + "dependencies": ["types", "spec_types"] + } +] diff --git a/tools/backend/synth-characterization-spec/hdl_manager.py b/tools/backend/synth-characterization-spec/hdl_manager.py new file mode 100644 index 0000000000..17b6c69a29 --- /dev/null +++ b/tools/backend/synth-characterization-spec/hdl_manager.py @@ -0,0 +1,84 @@ +# This script is used to move the HDL files in the hdl_out_dir and generate the necessary HDL files for the unit. +import os + + +def copy_dependency_rtl_files(unit_name, dependency_dict, hdl_out_dir, dynamatic_dir): + """ + Copy the RTL files of the dependencies of the given unit to the output directory. + + Args: + unit_name (str): Name of the unit for which dependencies are to be copied. + dependency_dict (dict): Dictionary containing the list of dependencies for all units. + hdl_out_dir (str): Directory where HDL files should be stored. + dynamatic_dir (str): Path to the Dynamatic directory. + """ + # Add dependency RTL files to the output directory + remaining_dependencies = dependency_dict[unit_name]["dependencies"].copy() + while remaining_dependencies: + dependency_unit = remaining_dependencies.pop(0) + # Find the dependecy unit location + assert dependency_unit in dependency_dict, f"Dependency {dependency_unit} not found in dependency list." + dependency_info = dependency_dict[dependency_unit] + dependency_rtl = dependency_info["RTL"] + dependency_rtl = dependency_rtl.replace("$DYNAMATIC", dynamatic_dir) + if "_dataless" in dependency_unit: + # If the dependency is a dataless unit, we copy it with a different name + rtl_filename = dependency_rtl.split( + "/")[-1].replace(".vhd", "_dataless.vhd") + os.system(f"cp {dependency_rtl} {hdl_out_dir}/{rtl_filename}") + else: + os.system(f"cp {dependency_rtl} {hdl_out_dir}") + # Add the dependencies of this dependency to the remaining dependencies + if "dependencies" in dependency_info: + remaining_dependencies.extend(dependency_info["dependencies"]) + + extra_dependencies = [ + ("types", "$DYNAMATIC/data/vhdl/support/types.vhd"), + ("logic", "$DYNAMATIC/data/vhdl/support/logic.vhd"), + ("oehb", "$DYNAMATIC/data/vhdl/handshake/oehb.vhd"), + ("oehb_dataless", "$DYNAMATIC/data/vhdl/handshake/dataless/oehb.vhd"), + ("br_dataless", "$DYNAMATIC/data/vhdl/handshake/dataless/br.vhd"), + ] + for extra_name, extra_rtl in extra_dependencies: + extra_rtl = extra_rtl.replace("$DYNAMATIC", dynamatic_dir) + rtl_filename = extra_rtl.split("/")[-1] + if "_dataless" in extra_name: + os.system( + f"cp {extra_rtl} {hdl_out_dir}/{rtl_filename.replace('.vhd', '_dataless.vhd')}") + else: + os.system(f"cp {extra_rtl} {hdl_out_dir}") + + +def get_hdl_files(unit_name, generic, generator, hdl_out_dir, dynamatic_dir, dependency_dict): + """ + Generate or copy the HDL files for the given unit. + + Args: + unit_name (str): Name of the unit. + generic (str): Generic information for the unit. + generator (str): Generator information for the unit. + hdl_out_dir (str): Directory where HDL files should be stored. + dynamatic_dir (str): Path to the Dynamatic directory. + dependency_dict (dict): Dictionary containing the list of dependencies for all units. + + Returns: + str: Path to the generated or copied HDL file. + """ + # Ensure the output directory exists + if not os.path.exists(hdl_out_dir): + os.makedirs(hdl_out_dir) + # Copy the RTL files of the dependencies to the output directory + copy_dependency_rtl_files( + unit_name, dependency_dict, hdl_out_dir, dynamatic_dir) + # For generic-only units (no generator), copy the RTL file once. + # For generator-driven units, the generator is invoked per parameter combo + # by run_unit_characterization, not here. + if generic: + rtl_file = generic.replace("$DYNAMATIC", dynamatic_dir) + os.system(f"cp {rtl_file} {hdl_out_dir}") + return rtl_file + + assert generator, "Unit must have either a generic RTL file or a generator." + rtl_file = None + + return rtl_file diff --git a/tools/backend/synth-characterization-spec/main.py b/tools/backend/synth-characterization-spec/main.py new file mode 100644 index 0000000000..53ae8f16f4 --- /dev/null +++ b/tools/backend/synth-characterization-spec/main.py @@ -0,0 +1,208 @@ +# Script to run characterization of dataflow units +import argparse +import json +import os +from report_parser import extract_rpt_data +from hdl_manager import get_hdl_files +from utils import VhdlInterfaceInfo, parameters_ranges, skipping_units +from unit_characterization import run_unit_characterization + + +def extract_rtl_info(unit_info): + """ + Extract RTL information from the unit_info dictionary. + + Args: + unit_info (dict): Dictionary containing unit information. + + Returns: + tuple: A tuple containing the unit name, list of parameters, generic, generator, and dependencies. + """ + unit_name = unit_info.get("name") + list_params = unit_info.get("parameters", []) + generic = unit_info.get("generic", None) + generator = unit_info.get("generator", None) + dependencies = unit_info.get("dependencies", []) + + return unit_name, list_params, generic, generator, dependencies + +# Extract dependencies from the dataflow units +# This function is essential to move the right RTL files +# Example of a dependency dictionary: +# { +# "unit_name": {"RTL": "path/to/rtl_file.vhd", "dependencies": ["dep1", "dep2"]}, +# "dep1": {"RTL": "path/to/dep1.vhd", "dependencies": []}, +# "dep2": {"RTL": "path/to/dep2.vhd", "dependencies": []}, +# ... +# } +# The dependencies are used to copy the necessary RTL files to the output directory + + +def get_dependency_dict(dataflow_units): + """ + Extract the RTLs of all possible dependencies from the dataflow units. + Args: + dataflow_units (list): List of dataflow unit dictionaries. + Returns: + list: A list of unique dependencies. + """ + dependency_dict = {} + for unit_info in dataflow_units: + if not "name" in unit_info: + rtl_file = unit_info.get("generic") + assert rtl_file, "Unit info must contain a 'generic' field for RTL file." + unit_name = rtl_file.split("/")[-1].split(".")[0] + if "dependencies" in unit_info: + dependencies = unit_info["dependencies"] + else: + dependencies = [] + dependency_dict[unit_name] = { + "RTL": rtl_file, "dependencies": dependencies} + elif "module-name" in unit_info: + unit_name = unit_info["module-name"] + rtl_file = unit_info["generic"] + if "dependencies" in unit_info: + dependencies = unit_info["dependencies"] + else: + dependencies = [] + dependency_dict[unit_name] = { + "RTL": rtl_file, "dependencies": dependencies} + else: + rtl_file = unit_info.get("generic") + if not rtl_file: + rtl_file = unit_info.get("generator") + dependencies = unit_info.get("dependencies", []) + unit_name = unit_info["name"] + dependency_dict[unit_name] = { + "RTL": rtl_file, "dependencies": dependencies} + return dependency_dict + + +def run_characterization(json_input, json_output, dynamatic_dir, synth_tool, clock_period, clean=False): + """ + Run characterization of dataflow units based on the provided JSON input. + + Args: + json_input (str): Path to the input JSON file containing dataflow unit RTL information. + json_output (str): Path to the output JSON file where characterization results will be saved. + dynamatic_dir (str): Path to the DYNAMATIC home directory. + synth_tool (str): Synthesis tool to use for characterization (e.g., 'vivado'). + """ + # Load the input JSON file + with open(json_input, 'r') as f: + dataflow_units = json.load(f) + + tmp_dir = f"{dynamatic_dir}/tools/backend/synth-characterization-spec/tmp" + if clean and os.path.exists(tmp_dir): + os.system(f"rm -rf {tmp_dir}") + os.makedirs(tmp_dir, exist_ok=True) + # Generate hdl directory + hdl_dir = f"{tmp_dir}/hdl" + if not os.path.exists(hdl_dir): + os.makedirs(hdl_dir) + # Generate tcl directory + tcl_dir = f"{tmp_dir}/tcl" + if not os.path.exists(tcl_dir): + os.makedirs(tcl_dir) + # Generate report directory + rpt_dir = f"{tmp_dir}/reports" + if not os.path.exists(rpt_dir): + os.makedirs(rpt_dir) + # Generate logs directory + log_dir = f"{tmp_dir}/logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + all_dependencies_dict = get_dependency_dict(dataflow_units) + + map_unit_to_list_unit_chars = {} + + for unit_info in dataflow_units: + # Extract the unit name and its RTL information + unit_name, list_params, generic, generator, dependencies = extract_rtl_info( + unit_info) + if unit_name == None: + print("Skipping unit with no name.") + continue + if unit_name in skipping_units: + print(f"Skipping unit {unit_name} as it is in the skipping list.") + continue + # We assume that units with no unit_name are just for dependencies + if unit_name == None: + continue + # If the unit has a "DATA_TYPE" parameter with data = 0, skip since it's a dataless unit + if len(list_params) > 0: + skip_unit = False + for param in list_params: + if param["name"] == "DATA_TYPE" and "data-eq" in param and param["data-eq"] == 0: + skip_unit = True + break + if skip_unit: + continue + # Note: hdl_dir / tcl_dir are no longer wiped between units. With + # incremental mode each combo's files are uniquely named (e.g. + # speculator_0.vhd) so leftover files from prior units don't collide. + # Use --clean to wipe everything from scratch. + # Copy the RTL files or generate them if necessary + print(f"Processing unit: {unit_name}") + top_def_file = get_hdl_files( + unit_name, generic, generator, hdl_dir, dynamatic_dir, all_dependencies_dict) + # After generating the HDL files, we can proceed with characterization + list_unit_chars = run_unit_characterization(unit_name, list_params, hdl_dir, synth_tool, top_def_file, + tcl_dir, rpt_dir, log_dir, clock_period, generator=generator, dynamatic_dir=dynamatic_dir) + # Store the results in the map_unit_to_list_unit_chars dictionary + map_unit_to_list_unit_chars[unit_name] = list_unit_chars + + # Save the results to the output JSON file + extract_rpt_data(map_unit_to_list_unit_chars, json_output) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Run characterization of dataflow units") + parser.add_argument( + "--json-input", + type=str, + default=None, + help="Path to the JSON file containing the dataflow unit RTL information (if unspecified, $DYNAMATIC_DIR/data/rtl-config-vhdl-vivado.json)", + ) + parser.add_argument( + "--json-output", + type=str, + required=True, + help="Path to the output JSON file where the characterization results will be saved", + ) + parser.add_argument( + "--dynamatic-dir", + type=str, + required=True, + help="Path to the DYNAMATIC home directory", + ) + parser.add_argument( + "--synth-tool", + type=str, + default="vivado", + help="Synthesis tool to use for characterization (default: vivado)", + ) + parser.add_argument( + "--clock-period", + type=float, + default=4.0, + help="Clock period in nanoseconds to use for synthesis (default: 4.0 ns)", + ) + parser.add_argument( + "--clean", + action="store_true", + help="Wipe the tmp directory before running. Default behaviour is incremental: combos whose report files already exist are skipped (re-runs only fill holes).", + ) + args = parser.parse_args() + json_input = args.json_input + json_output = args.json_output + dynamatic_dir = args.dynamatic_dir + synth_tool = args.synth_tool + clock_period = args.clock_period + if not json_input: + json_input = f"{dynamatic_dir}/data/rtl-config-vhdl-vivado.json" + run_characterization(json_input, json_output, + dynamatic_dir, synth_tool, clock_period, + clean=args.clean) diff --git a/tools/backend/synth-characterization-spec/report_parser.py b/tools/backend/synth-characterization-spec/report_parser.py new file mode 100644 index 0000000000..038ffcebd9 --- /dev/null +++ b/tools/backend/synth-characterization-spec/report_parser.py @@ -0,0 +1,189 @@ +# Extract per-(in_port, out_port) timing data and save as a JSON matrix. +import os +import re +import json + +PATTERN_DELAY_INFO = "Data Path Delay:" + + +def extract_delay(line): + match = re.search(r'Data Path Delay:\s+([\d.]+)ns', line) + assert match, f"Could not find data path delay in line: {line}" + return float(match.group(1)) + + +def extract_single_rpt(rpt_file): + """ + Returns the max Data Path Delay in ns. If the file is present but contains + no Data Path Delay line (Vivado found no timing path between this in/out + pair), returns 0.0. If the file is missing (synth failed), returns None. + """ + if not os.path.exists(rpt_file): + return None + max_delay = 0.0 + with open(rpt_file, 'r') as f: + for line in f: + if PATTERN_DELAY_INFO in line: + max_delay = max(max_delay, extract_delay(line)) + return max_delay + + +def render_matrix(unit_name, delays, bw): + """Render the (in_port x out_port) matrix at one bitwidth as a string.""" + in_ports = list(delays.keys()) + out_ports = [] + seen = set() + for ip in in_ports: + for op in delays[ip]: + if op not in seen: + seen.add(op) + out_ports.append(op) + col_w = max((len(p) for p in out_ports), default=4) + 1 + row_w = max((len(p) for p in in_ports), default=4) + lines = [f"=== {unit_name} delay matrix (bw={bw}) [ns] ==="] + lines.append(" " * row_w + " " + "".join(p.rjust(col_w) + for p in out_ports)) + for ip in in_ports: + cells = [] + for op in out_ports: + v = delays[ip].get(op, {}).get(bw) + cells.append("---" if v is None else f"{v:.3f}") + lines.append(ip.rjust(row_w) + " " + "".join(c.rjust(col_w) + for c in cells)) + return "\n".join(lines) + + +def split_physical_port(phys): + """ + Split a physical VHDL port name into (logical_port, signal). + Examples: + "dataIn" -> ("dataIn", "data") + "dataIn_valid" -> ("dataIn", "valid") + "dataOut_ready" -> ("dataOut", "ready") + "issueCtrl_valid" -> ("issueCtrl", "valid") + "trigger_valid" -> ("trigger", "valid") + """ + for suffix, sig in (("_valid", "valid"), ("_ready", "ready"), ("_spec", "spec")): + if phys.endswith(suffix): + return phys[: -len(suffix)], sig + return phys, "data" + + +def extract_rpt_data(map_unit_to_list_unit_chars, json_output): + """ + Produce two artifacts: + 1. JSON (json_output) in the edges-list shape: + output[unit_name]["delays"] = [ + { "from": {"port", "signal"}, + "to": {"port", "signal"}, + "samples": [ {"params": {...}, "delay": ns}, ... ] }, + ... + ] + spec signals and all-zero edges are dropped. + 2. A .matrix.txt next to it: per-bitwidth physical-port matrices for + human inspection. + """ + matrix_by_unit = {} + edges_by_unit = {} + in_port_by_unit = {} + out_port_by_unit = {} + + for unit_name, list_unit_chars in map_unit_to_list_unit_chars.items(): + matrix = {} + edges = {} + in_ports = {} + out_ports = {} + for unit_char in list_unit_chars: + sample_params = dict(unit_char.params) + bw = str(sample_params.get("BITWIDTH", "")) + pair_to_rpt = getattr(unit_char, "map_pair_to_rpt", {}) + if not pair_to_rpt: + print( + "\033[91m" + f"[ERROR] No pair_to_rpt for unit {unit_name} at params={sample_params}." + "\033[0m") + continue + for (iport, oport), rpt_path in pair_to_rpt.items(): + delay = extract_single_rpt(rpt_path) + if delay is None: + continue + matrix.setdefault(iport, {}).setdefault(oport, {})[bw] = delay + from_port, from_sig = split_physical_port(iport) + to_port, to_sig = split_physical_port(oport) + if from_sig == "spec" or to_sig == "spec": + continue + if delay == 0.0: + continue + edge_key = (from_port, from_sig, to_port, to_sig) + edges.setdefault(edge_key, []).append( + {"params": sample_params, "delay": delay}) + + in_port_to_rpt = getattr(unit_char, "map_in_port_to_rpt", {}) + for iport, rpt_path in in_port_to_rpt.items(): + delay = extract_single_rpt(rpt_path) + if delay is None: + continue + port, sig = split_physical_port(iport) + if sig == "spec": + continue + if delay == 0.0: + continue + in_ports.setdefault((port, sig), []).append( + {"params": sample_params, "delay": delay}) + + out_port_to_rpt = getattr(unit_char, "map_out_port_to_rpt", {}) + for oport, rpt_path in out_port_to_rpt.items(): + delay = extract_single_rpt(rpt_path) + if delay is None: + continue + port, sig = split_physical_port(oport) + if sig == "spec": + continue + if delay == 0.0: + continue + out_ports.setdefault((port, sig), []).append( + {"params": sample_params, "delay": delay}) + + matrix_by_unit[unit_name] = matrix + edges_by_unit[unit_name] = edges + in_port_by_unit[unit_name] = in_ports + out_port_by_unit[unit_name] = out_ports + + output_data = {} + for unit_name, edges in edges_by_unit.items(): + edge_list = [] + for (fp, fs, tp, ts), samples in edges.items(): + edge_list.append({ + "from": {"port": fp, "signal": fs}, + "to": {"port": tp, "signal": ts}, + "samples": samples, + }) + pin2reg_list = [] + for (port, sig), samples in in_port_by_unit.get(unit_name, {}).items(): + pin2reg_list.append({ + "port": port, "signal": sig, "samples": samples, + }) + reg2pin_list = [] + for (port, sig), samples in out_port_by_unit.get(unit_name, {}).items(): + reg2pin_list.append({ + "port": port, "signal": sig, "samples": samples, + }) + output_data[unit_name] = { + "pin2pin": edge_list, + "pin2reg": pin2reg_list, + "reg2pin": reg2pin_list, + } + + # Render matrices per unit per bitwidth. + matrix_path = json_output + ".matrix.txt" + chunks = [] + for unit_name, matrix in matrix_by_unit.items(): + bws = sorted( + {bw for ip in matrix for op in matrix[ip] for bw in matrix[ip][op]}, key=int) + for bw in bws: + block = render_matrix(unit_name, matrix, bw) + print("\n" + block) + chunks.append(block) + with open(matrix_path, 'w') as f: + f.write("\n\n".join(chunks) + "\n") + + with open(json_output, 'w') as f: + json.dump(output_data, f, indent=2) diff --git a/tools/backend/synth-characterization-spec/run_synthesis.py b/tools/backend/synth-characterization-spec/run_synthesis.py new file mode 100644 index 0000000000..afeee4bdf9 --- /dev/null +++ b/tools/backend/synth-characterization-spec/run_synthesis.py @@ -0,0 +1,31 @@ +import os +from multiprocessing import Pool +from utils import VhdlInterfaceInfo, NUM_CORES + +def _synth_worker(args): + synth_tool, tcl_file, log_file = args + os.system(f"{synth_tool} -mode batch -source {tcl_file} > {log_file}") + +def run_synthesis(tcl_files, synth_tool, log_file): + """ + Run synthesis for the given TCL files using the specified synthesis tool in parallel (if NUM_CORES > 1). + Args: + tcl_files (list): List of TCL files to run synthesis on. + synth_tool (str): Synthesis tool to use (e.g., 'vivado'). + """ + # Run synthesis in parallel using Vivado + args_list = [(synth_tool, tcl_file, f"{log_file}{i}") for i, tcl_file in enumerate(tcl_files)] + with Pool(processes=NUM_CORES) as pool: + pool.map(_synth_worker, args_list) + +def write_sdc_constraints(sdc_file, period_ns): + """ + Write the SDC constraints file with the specified period. + + Args: + sdc_file (str): Path to the SDC file. + period_ns (float): Period in nanoseconds. + """ + with open(sdc_file, 'w') as f: + f.write(f"create_clock -name clk -period {period_ns} -waveform {{0.000 {period_ns/2}}} [get_ports clk]\n") + f.write("set_property HD.CLK_SRC BUFGCTRL_X0Y0 [get_ports clk]\n") diff --git a/tools/backend/synth-characterization-spec/unit_characterization.py b/tools/backend/synth-characterization-spec/unit_characterization.py new file mode 100644 index 0000000000..9ddf26f0ef --- /dev/null +++ b/tools/backend/synth-characterization-spec/unit_characterization.py @@ -0,0 +1,295 @@ +from run_synthesis import run_synthesis, write_sdc_constraints +import os +import re +from itertools import product +from utils import parameters_ranges, VhdlInterfaceInfo, UnitCharacterization +from typing import List, Tuple + + +def extract_generics_ports(vhdl_code, entity_name): + """ + Extract generics and ports from a VHDL entity block. + Args: + vhdl_code (str): VHDL code as a string. + Returns: + Tuple[List[str], List[str]]: A tuple containing two lists: + - List of generics + - List of ports + """ + + # Remove comments + vhdl_code = re.sub(r'--.*', '', vhdl_code) + + # Match the entity block + entity_match = re.search( + r'entity\s+(\w+)\s+is(.*?)end\s+entity', vhdl_code, re.DOTALL | re.IGNORECASE) + if not entity_match: + raise ValueError("Could not find VHDL entity block.") + + # If there are multiple entities, we remove the one that does not match the entity_name + is_right_entity = entity_match.group(1) in entity_name + while not is_right_entity and entity_match: + entity_to_remove = entity_match.group(1) + # Remove the first entity block that does not match the entity_name + first_entity = re.search( + fr'entity\s+{entity_to_remove}\s+is(.*?)end\s+architecture', vhdl_code, re.DOTALL | re.IGNORECASE) + assert first_entity, f"Could not find VHDL entity block for {entity_to_remove} despite being in the code." + vhdl_code = vhdl_code.replace(first_entity.group(0), "") + entity_match = re.search( + r'entity\s+(\w+)\s+is(.*?)end\s+entity', vhdl_code, re.DOTALL | re.IGNORECASE) + entity_extracted = entity_match.group(1) + # Check if the entity name matches the one we are looking for # Selector is a special case since its handshake name is handshake.select + is_right_entity = entity_extracted in entity_name if entity_extracted != "selector" else True + + assert entity_match, f"Entity {entity_name} not found in the VHDL code." + + entity_name = entity_match.group(1) if entity_match else "unknown_entity" + entity_block = entity_match.group(2) + + # Extract generics and ports + generics_match = re.search( + r'generic\s*\((.*?)\)\s*;', entity_block, re.DOTALL | re.IGNORECASE) + ports_match = re.search( + r'port\s*\(((?:[^()]*|\([^()]*\))*)\)\s*;', entity_block, re.DOTALL | re.IGNORECASE) + + generics_raw = generics_match.group(1).strip() if generics_match else '' + ports_raw = ports_match.group(1).strip() if ports_match else '' + + # Split on semicolon while keeping line breaks (in case of multiple declarations) + def split_definitions(raw: str) -> List[str]: + lines = re.split(r';\s*\n', raw) + return [line.strip() for line in lines if line.strip()] + + def expand_grouped(defs: List[str]) -> List[str]: + # Expand "clk, rst : in std_logic" into one entry per name so each + # gets its own port_map line. + out = [] + for d in defs: + if ":" not in d: + out.append(d) + continue + lhs, rhs = d.split(":", 1) + names = [n.strip() for n in lhs.split(",") if n.strip()] + if len(names) <= 1: + out.append(d) + continue + for n in names: + out.append(f"{n} :{rhs}") + return out + + generics = expand_grouped(split_definitions(generics_raw)) + ports = expand_grouped(split_definitions(ports_raw)) + + return entity_name, VhdlInterfaceInfo(generics, ports) + + +def generate_wrapper_top(entity_name, vhdl_interface_info, param_names): + """ + Generate the wrapper for the top file from the given top definition file. + + Args: + entity_name (str): Name of the top entity. + vhdl_interface_info (VhdlInterfaceInfo): VHDL interface information containing generics and ports. + param_names (List[str]): List of parameter names to be used in the wrapper. + + Returns: + str: The wrapper for the top file. + """ + generics = vhdl_interface_info.get_list_generics() + ports = vhdl_interface_info.get_list_ports() + for _generic in generics: + # Get the generic name before the colon + generic = _generic.split(":")[0].strip() + assert generic in param_names, f"Generic `{generic}` not found in parameter names." + + # Create ports for the top file + tb_ports = [] + for _port in ports: + port = _port + for param in param_names: + port = port.replace(f"{param}", f"{param}_const_value") + tb_ports.append(port) + # Create the wrapper for the top file + wrapper_top = f"""library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; +use work.types.all; +entity tb is +port ( +{';\n'.join(tb_ports)} +); +end entity; +architecture tb_arch of tb is +begin +dut: entity work.{entity_name} +generic map ( +""" + + for param in param_names: + if param != "PREDICATE": + wrapper_top += f"{param} => {param}_const_value,\n" + wrapper_top = wrapper_top.rstrip(",\n") + "\n" \ + ")\n" \ + "port map (\n" + for port in ports: + # Get the port name before the colon + port_name = port.split(":")[0].strip() + wrapper_top += f"{port_name} => {port_name},\n" + wrapper_top = wrapper_top.rstrip(",\n") + "\n" \ + ");\n" \ + "end architecture;\n" + + return wrapper_top, entity_name + + +def generate_wrapper_top_no_generics(entity_name, vhdl_interface_info): + """ + Wrapper used when the unit's VHDL has no generics (modern self-contained generator output). + The tb just wires every dut port to a top-level port of the same name. + Entity is uniquely named (tb_) so multiple combos can coexist + in the same hdl directory without colliding. + """ + tb_entity_name = f"tb_{entity_name}" + ports = vhdl_interface_info.get_list_ports() + tb_ports = list(ports) + + wrapper_top = f"""library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; +use work.types.all; +entity {tb_entity_name} is +port ( +{';\n'.join(tb_ports)} +); +end entity; +architecture arch of {tb_entity_name} is +begin +dut: entity work.{entity_name} +port map ( +""" + for port in ports: + port_name = port.split(":")[0].strip() + wrapper_top += f"{port_name} => {port_name},\n" + wrapper_top = wrapper_top.rstrip(",\n") + "\n);\nend architecture;\n" + return wrapper_top + + +def run_generator_for_combo(generator, dynamatic_dir, hdl_out_dir, module_name, params_dict): + """ + Substitute $DYNAMATIC / $OUTPUT_DIR / $MODULE_NAME / each $ into the + generator command template and invoke it. Returns the path of the produced .vhd. + """ + cmd = generator + cmd = cmd.replace("$DYNAMATIC", dynamatic_dir) + cmd = cmd.replace("$OUTPUT_DIR", hdl_out_dir) + cmd = cmd.replace("$MODULE_NAME", module_name) + for param_name, param_value in params_dict.items(): + cmd = cmd.replace(f"${param_name}", str(param_value)) + print(f"Running generator command: {cmd}") + rc = os.system(cmd) + rtl_file = f"{hdl_out_dir}/{module_name}.vhd" + assert rc == 0, f"Generator command failed for {module_name}: {cmd}" + assert os.path.exists(rtl_file), f"Generator did not produce {rtl_file}" + return rtl_file + + +def run_unit_characterization(unit_name, list_params, hdl_out_dir, synth_tool, top_def_file, tcl_dir, rpt_dir, log_dir, clock_period, generator=None, dynamatic_dir=None): + """ + Run characterization for a single unit using the specified synthesis tool. + + When `generator` is provided, the generator is re-invoked per parameter + combination with that combo's values substituted into the command, producing + a fresh fully-specialised .vhd for each combo. Otherwise (generic-only + units), the existing top_def_file is used and the legacy VHDL-generic + sweeping path is taken. + """ + support_hdl_files = [f"{hdl_out_dir}/{file}" for file in os.listdir( + hdl_out_dir) if os.path.isfile(os.path.join(hdl_out_dir, file))] + + params_charact = {} + for param in list_params: + param_name = param["name"] + # Only sweep params that the generator command actually references; + # avoids a redundant cartesian product when the JSON declares a param + # the generator does not use (e.g. DATA_TYPE alongside BITWIDTH). + if generator is not None and f"${param_name}" not in generator: + continue + assert param_name in parameters_ranges, f"Parameter {param_name} not found in parameters_ranges." + params_charact[param_name] = parameters_ranges[param_name] + param_combinations = list( + product(*params_charact.values())) if params_charact else [()] + param_names = list(params_charact.keys()) + + sdc_file = f"{tcl_dir}/period.sdc" + write_sdc_constraints(sdc_file, clock_period) + + list_tcls = [] + unit_characterization_list = [] + + if generator is not None: + for id, combination in enumerate(param_combinations): + params_dict = dict(zip(param_names, combination)) + module_name = f"{unit_name.split('.')[-1]}_{id}" + unit_vhd = run_generator_for_combo( + generator, dynamatic_dir, hdl_out_dir, module_name, params_dict) + with open(unit_vhd, 'r') as f: + vhdl_code = f.read() + top_entity_name, vhdl_interface_info = extract_generics_ports( + vhdl_code, module_name) + tb_text = generate_wrapper_top_no_generics( + top_entity_name, vhdl_interface_info) + tb_file = f"{hdl_out_dir}/{module_name}_tb.vhd" + with open(tb_file, 'w') as f: + f.write(tb_text) + hdl_files = [tb_file, unit_vhd] + support_hdl_files + unit_char_obj = UnitCharacterization( + unit_name, top_entity_name, params_dict, hdl_files, vhdl_interface_info, id) + tcl_file = unit_char_obj.generate_tcl(tcl_dir, rpt_dir, sdc_file) + unit_characterization_list.append(unit_char_obj) + all_rpts = list(unit_char_obj.map_pair_to_rpt.values()) + \ + list(unit_char_obj.map_in_port_to_rpt.values()) + \ + list(unit_char_obj.map_out_port_to_rpt.values()) + if all(os.path.exists(p) for p in all_rpts): + print( + f"[skip] {unit_name} combo {id} {params_dict}: reports already present") + continue + list_tcls.append(tcl_file) + else: + print(f"Extracting generics and ports from {top_def_file}") + with open(top_def_file, 'r') as f: + vhdl_code = f.read() + top_entity_name, vhdl_interface_info = extract_generics_ports( + vhdl_code, unit_name) + wrapper_top, top_entity_name = generate_wrapper_top( + top_entity_name, vhdl_interface_info, param_names) + for id, combination in enumerate(param_combinations): + top_file = f"{hdl_out_dir}/{top_entity_name}_top_{id}.vhd" + wrapper_top_combined = wrapper_top + for param_name, param_value in zip(param_names, combination): + wrapper_top_combined = wrapper_top_combined.replace( + f"{param_name}_const_value", str(param_value)) + with open(top_file, 'w') as f: + f.write(wrapper_top_combined) + unit_char_obj = UnitCharacterization(unit_name, top_entity_name, dict(zip( + param_names, combination)), [top_file] + support_hdl_files, vhdl_interface_info, id) + tcl_file = unit_char_obj.generate_tcl(tcl_dir, rpt_dir, sdc_file) + unit_characterization_list.append(unit_char_obj) + all_rpts = list(unit_char_obj.map_pair_to_rpt.values()) + \ + list(unit_char_obj.map_in_port_to_rpt.values()) + \ + list(unit_char_obj.map_out_port_to_rpt.values()) + if all(os.path.exists(p) for p in all_rpts): + print( + f"[skip] {unit_name} combo {id}: reports already present") + continue + list_tcls.append(tcl_file) + + log_file = f"{log_dir}/synth_{unit_name}_log.txt" + if list_tcls: + run_synthesis(list_tcls, synth_tool, log_file) + else: + print( + f"[skip] {unit_name}: all combos already characterized; nothing to synth") + + return unit_characterization_list diff --git a/tools/backend/synth-characterization-spec/utils.py b/tools/backend/synth-characterization-spec/utils.py new file mode 100644 index 0000000000..bfaecc400f --- /dev/null +++ b/tools/backend/synth-characterization-spec/utils.py @@ -0,0 +1,383 @@ +import os +import re +from typing import List, Tuple, Dict + +# Constants for the characterization process + +NUM_CORES = 4 # Number of cores to use for parallel synthesis (if applicable) + +# List of units to skip during characterization +# These units are either empty, unused, or characterized by other scripts +skipping_units = [ + # empty units + "handshake.constant", + "handshake.br", + "handshake.source", + "handshake.extsi", + "handshake.extui", + "handshake.trunci", + "handshake.truncf", + "handshake.sink", + "handshake.store", + "handshake.maximumf", + "handshake.minimumf", + "handshake.extf", + "handshake.divsi", + "handshake.divui", + # unused units + "handshake.join", + "handshake.sharing_wrapper", + "handshake.ready_remover", + "handshake.valid_merger", + "handshake.ndwire", + "handshake.mem_controller", + "mem_to_bram", + # units characterized by other scripts + "handshake.fork", + "handshake.lazy_fork", + "handshake.lsq", + "handshake.mulf", + "handshake.negf", + "handshake.buffer", + "handshake.addf", + "handshake.cmpf", + "handshake.divf", + "handshake.subf", + "handshake.not"] + +# List of parameters and their ranges for characterization +# This is used to generate the top files for characterization +parameters_ranges = { + "DATA_TYPE": [1, 2, 4, 8, 16, 32, 64], + "BITWIDTH": [1, 2, 4, 8, 16, 32, 64], + "FIFO_DEPTH": [4], + "SIZE": [2], + "SELECT_TYPE": [2], + "INDEX_TYPE": [2], + "ADDR_TYPE": [64], + "PREDICATE": ["ne"], + "EXTRA_SIGNALS": ["{}"], +} + +# Function to write the TCL file for synthesis + + +def write_tcl_multiport(top_entity_name, hdl_files, tcl_file, sdc_file, + pair_to_rpt, in_port_to_rpt, out_port_to_rpt, + is_vector): + """ + Synthesise then emit three classes of report_timing: + - per (input_port, output_port) pair: pin-to-pin path delay + - per input_port (no -to): worst path starting at this pin + - per output_port (no -from): worst path ending at this pin + + `is_vector` is a callable (port_name) -> bool; chooses between + `[get_ports {name[*]}]` (vector) and `[get_ports name]` (scalar). + """ + def _gp(p): + if is_vector(p): + return f"[get_ports {{{p}[*]}}]" + return f"[get_ports {p}]" + with open(tcl_file, 'w') as f: + for hdl_file in hdl_files: + f.write(f"read_vhdl -vhdl2008 {hdl_file}\n") + f.write(f"read_xdc {sdc_file}\n") + f.write( + f"synth_design -top tb_{top_entity_name} -part xc7k160tfbg484-2 -no_iobuf -mode out_of_context\n") + f.write("opt_design\n") + f.write("place_design\n") + f.write("phys_opt_design\n") + f.write("route_design\n") + f.write("phys_opt_design\n") + f.write("puts \"PORTS_AVAILABLE: [get_ports]\"\n") + for (iport, oport), rpt_path in pair_to_rpt.items(): + f.write( + f"report_timing -from {_gp(iport)} -to {_gp(oport)} > {rpt_path}\n") + for iport, rpt_path in in_port_to_rpt.items(): + f.write(f"report_timing -from {_gp(iport)} > {rpt_path}\n") + for oport, rpt_path in out_port_to_rpt.items(): + f.write(f"report_timing -to {_gp(oport)} > {rpt_path}\n") + + +# Class to hold VHDL interface information +# This class is used to store the generics and ports of a VHDL entity. +class VhdlInterfaceInfo: + """ + Class to hold VHDL interface information. + This class is used to store the generics and ports of a VHDL entity. + """ + generics: List[str] # List of generics in the VHDL entity + ports: List[str] # List of ports in the VHDL entity + ins_per_type: Dict[str, str] # Dictionary of input ports with their types + # Dictionary of output ports with their types + outs_per_type: Dict[str, str] + + def __init__(self, generics: List[str], ports: List[str]): + self.generics = generics + self.ports = ports + ins, outs = self.extract_ins_outs() + self.ins_per_type = self.categorize_ports(ins) + self.outs_per_type = self.categorize_ports(outs) + self.is_vector_map = {} + for raw in ports: + if ":" not in raw: + continue + name = raw.split(":")[0].strip() + self.is_vector_map[name] = "std_logic_vector" in raw + + def is_vector(self, port_name: str) -> bool: + return self.is_vector_map.get(port_name, False) + + def __repr__(self): + return f"VhdlInterfaceInfo(generics={self.generics}, ports={self.ports})" + + def extract_ins_outs(self) -> Tuple[List[str], List[str]]: + """ + Extract input and output ports from the VHDL interface. + + Skips clk/rst ports since they are not characterized. + + Returns: + Tuple[List[str], List[str]]: A tuple containing two lists: + - List of input ports + - List of output ports + """ + def is_clock_or_reset(port_name: str) -> bool: + n = port_name.strip() + return n in ("clk", "clock", "rst", "reset") or n.startswith(("clk_", "rst_")) + + # Match the mode after the colon, not anywhere in the port string — + # otherwise a port like `outs_ready : in std_logic` matches both "in" + # and "out" via its name. + mode_in = re.compile(r":\s*in\b", re.IGNORECASE) + mode_out = re.compile(r":\s*out\b", re.IGNORECASE) + ins = [port.split(":")[0].strip() + for port in self.ports + if mode_in.search(port) and "data_array" not in port] + ins = [p for p in ins if not is_clock_or_reset(p)] + ins.extend(add_2d_ports(self.ports, "in")) + outs = [port.split(":")[0].strip() + for port in self.ports + if mode_out.search(port) and "data_array" not in port] + outs = [p for p in outs if not is_clock_or_reset(p)] + outs.extend(add_2d_ports(self.ports, "out")) + return ins, outs + + def categorize_ports(self, ports: List[str]) -> Dict[str, str]: + """ + Categorize ports based on their types. + + Args: + ports (List[str]): List of ports to categorize. + + Returns: + Dict[str, str]: Dictionary with port names as keys and their types as values. + """ + categorized_ports = {} + for port in ports: + if "_valid" in port: + categorized_ports[port] = "valid" + elif "_ready" in port: + categorized_ports[port] = "ready" + elif "condition" in port or "index" in port: + categorized_ports[port] = "condition" + elif "lhs" in port or "rhs" in port or "trueValue" in port or "falseValue" in port or "ins" in port or "data" in port or "addrIn" in port: + categorized_ports[port] = "data" + elif "result" in port or "outs" in port or "trueOut" in port or "falseOut" in port or "addrOut" in port or "dataOut" in port: + categorized_ports[port] = "data" + elif "clk" in port or "clock" in port or "rst" in port or "reset" in port: + categorized_ports[port] = "control_signal" + else: + categorized_ports[port] = "other" + return categorized_ports + + def get_input_ports(self) -> List[str]: + """ + Get the input ports of the VHDL interface. + + Returns: + List[str]: List of input ports. + """ + return list(self.ins_per_type.keys()) + + def get_input_ports_by_type(self, port_type: str) -> List[str]: + """ + Get the input ports of a specific type from the VHDL interface. + + Args: + port_type (str): Type of the input ports to retrieve (e.g., "valid", "ready", "data", "condition"). + + Returns: + List[str]: List of input ports of the specified type. + """ + assert port_type in ["valid", "ready", "data", + "condition"], f"Invalid port type: {port_type}. Valid types are 'valid', 'ready', 'data', 'condition'." + # Return ports that match the specified type + return [port for port, ptype in self.ins_per_type.items() if ptype == port_type] + + def get_output_ports_by_type(self, port_type: str) -> List[str]: + """ + Get the output ports of a specific type from the VHDL interface. + + Args: + port_type (str): Type of the output ports to retrieve (e.g., "valid", "ready", "data", "condition"). + + Returns: + List[str]: List of output ports of the specified type. + """ + assert port_type in ["valid", "ready", "data", + "condition"], f"Invalid port type: {port_type}. Valid types are 'valid', 'ready', 'data', 'condition'." + # Return ports that match the specified type + return [port for port, ptype in self.outs_per_type.items() if ptype == port_type] + + def get_output_ports(self) -> List[str]: + """ + Get the output ports of the VHDL interface. + + Returns: + List[str]: List of output ports. + """ + return list(self.outs_per_type.keys()) + + def get_list_ports(self) -> List[str]: + """ + Get the list of all ports (input and output) of the VHDL interface. + + Returns: + List[str]: List of all ports. + """ + return self.ports + + def get_list_generics(self) -> List[str]: + """ + Get the list of generics of the VHDL interface. + + Returns: + List[str]: List of generics. + """ + return self.generics + + +def add_2d_ports(ports, direction): + """ + Add 2D data_array ports to the list of ports based on the direction. + + Args: + ports (list): List of ports to process. + direction (str): Direction of the ports ('in' or 'out'). + Returns: + list: List of 2D data_array ports. + """ + + result = [] + for port in ports: + if direction in port and "data_array" in port: + match = re.search( + r'data_array\((\w+)\s*-\s*1\s*downto\s*0\)', port) + assert match, f"Could not find data_array port {port}." + size_name = match.group(1) + # Get corresponding size # Assuming only one value in parameters allowed + size_value = parameters_ranges[size_name][0] + for i in range(size_value): + result.append(f"{port.split(':')[0].strip()}[{i}]") + return result + +# Class that contains all information for a single unit characterization + + +class UnitCharacterization: + """ + Class to hold the characterization information for a single unit. + This class is used to store the unit name, its VHDL interface information, and the parameters used for characterization. + """ + unit_name: str # Name of the unit being characterized + top_entity_name: str # Name of the top entity for this unit + params: dict # Dictionary of parameters used for characterization + # List to hold HDL files generated or copied for this unit + hdl_files: List[str] + # Dictionary to hold delay reports for each signal + map_signals_type_to_delay_rpt: dict + # VHDL interface information containing generics and ports + vhdl_interface_info: VhdlInterfaceInfo + unique_id: int # Unique identifier for the characterization instance + tcl_file: str # Path to the last generated TCL file for synthesis + + # List of delay types to be characterized + # Each tuple contains the input and output port types to be characterized in this exact order + list_delay_types = [("data", "data"), ("valid", "valid"), ("ready", "ready"), + ("valid", "ready"), ("condition", + "valid"), ("condition", "ready"), + ("valid", "condition"), ("valid", "data")] + + def __init__(self, unit_name: str, top_entity_name: str, params: dict, hdl_files: List[str], vhdl_interface_info: VhdlInterfaceInfo, unique_id: int): + """ + Initialize the UnitCharacterization object. + + Args: + unit_name (str): Name of the unit being characterized. + top_entity_name (str): Name of the top entity for this unit. + params (dict): Dictionary of parameters used for characterization. + """ + self.unit_name = unit_name + self.top_entity_name = top_entity_name + self.params = params + self.hdl_files = hdl_files + self.unique_id = unique_id + self.vhdl_interface_info = vhdl_interface_info + self.map_signals_type_to_delay_rpt = {} + self.tcl_file = None + + def generate_tcl(self, tcl_dir: str, rpt_dir: str, sdc_file: str) -> List[str]: + """ + Generate a TCL file for synthesis. After synthesis, runs one + `report_timing` per (input_port, output_port) pair, each to its own + report file. Stashes the (in, out) -> rpt mapping on the object so the + parser can later read every pair. + """ + tcl_file = f"{tcl_dir}/synth_{self.top_entity_name}_top_{self.unique_id}.tcl" + input_ports = self.vhdl_interface_info.get_input_ports() + output_ports = self.vhdl_interface_info.get_output_ports() + pair_to_rpt = {} + for iport in input_ports: + for oport in output_ports: + rpt_path = f"{rpt_dir}/rpt_{self.top_entity_name}_top_{self.unique_id}__{iport}__{oport}.txt" + pair_to_rpt[(iport, oport)] = rpt_path + in_port_to_rpt = {} + for iport in input_ports: + rpt_path = f"{rpt_dir}/rpt_{self.top_entity_name}_top_{self.unique_id}__in__{iport}.txt" + in_port_to_rpt[iport] = rpt_path + out_port_to_rpt = {} + for oport in output_ports: + rpt_path = f"{rpt_dir}/rpt_{self.top_entity_name}_top_{self.unique_id}__out__{oport}.txt" + out_port_to_rpt[oport] = rpt_path + self.map_pair_to_rpt = pair_to_rpt + self.map_in_port_to_rpt = in_port_to_rpt + self.map_out_port_to_rpt = out_port_to_rpt + write_tcl_multiport(self.top_entity_name, self.hdl_files, + tcl_file, sdc_file, pair_to_rpt, + in_port_to_rpt, out_port_to_rpt, + self.vhdl_interface_info.is_vector) + self.tcl_file = tcl_file + return tcl_file + + def get_signals_type_to_rpt(self) -> dict: + """ + Get the dictionary mapping signal types to their delays. + + Returns: + dict: Dictionary mapping signal types to their delays. + """ + return self.map_signals_type_to_delay_rpt + + def get_parameter_value(self, param_name: str) -> str: + """ + Get the value of a specific parameter. + + Args: + param_name (str): Name of the parameter to retrieve. + + Returns: + str: Value of the specified parameter. + """ + assert param_name in self.params, f"Parameter {param_name} not found in parameters." + return self.params[param_name] diff --git a/tools/dynamatic/dynamatic.cpp b/tools/dynamatic/dynamatic.cpp index 10ad85486b..254e34223f 100644 --- a/tools/dynamatic/dynamatic.cpp +++ b/tools/dynamatic/dynamatic.cpp @@ -304,6 +304,8 @@ class Compile : public Command { static constexpr llvm::StringLiteral ENABLE_SHORT_CIRCUIT = "enable-short-circuit"; static constexpr llvm::StringLiteral SPECULATION = "speculation"; + static constexpr llvm::StringLiteral CALCULATE_PATH_DELAYS = + "calculate-path-delays"; Compile(FrontendState &state) : Command("compile", @@ -340,6 +342,10 @@ class Compile : public Command { addFlag({SPECULATION, "Enable speculation. Requires a #pragma DYN speculate " "`in the source code file."}); + addFlag({CALCULATE_PATH_DELAYS, + "After buffer placement, re-run the MILP with the buffering " + "decisions locked in to calculate the path delays the MILP " + "believes are present in the circuit."}); } CommandResult execute(CommandArguments &args) override; @@ -782,13 +788,15 @@ CommandResult Compile::execute(CommandArguments &args) { std::string enableShortCircuit = args.flags.contains(ENABLE_SHORT_CIRCUIT) ? "1" : "0"; std::string speculation = args.flags.contains(SPECULATION) ? "1" : "0"; + std::string calculatePathDelays = + args.flags.contains(CALCULATE_PATH_DELAYS) ? "1" : "0"; return execCmd(script, state.dynamaticPath, state.getKernelDir(), state.getOutputDir(), state.getKernelName(), buffers, floatToString(state.targetCP, 3), sharing, state.fpUnitsGenerator, rigidification, kInduction, disableLSQ, fastTokenDelivery, milpSolver, straightToQueue, speculation, - enableShortCircuit); + enableShortCircuit, calculatePathDelays); } CommandResult WriteHDL::execute(CommandArguments &args) { diff --git a/tools/dynamatic/scripts/compile.sh b/tools/dynamatic/scripts/compile.sh index 1ec506f265..27bd8981da 100755 --- a/tools/dynamatic/scripts/compile.sh +++ b/tools/dynamatic/scripts/compile.sh @@ -23,6 +23,7 @@ MILP_SOLVER=${13} STRAIGHT_TO_QUEUE=${14} SPECULATION=${15} ENABLE_SHORT_CIRCUIT=${16} +CALCULATE_PATH_DELAYS=${17} LLVM=$DYNAMATIC_DIR/llvm-project DYNAMATIC_BINS=$DYNAMATIC_DIR/bin @@ -109,7 +110,7 @@ export_cfg() { # ============================================================================ # # Reset output directory -rm -rf "$COMP_DIR" && mkdir -p "$COMP_DIR" +rm -rf "$OUTPUT_DIR" && mkdir -p "$COMP_DIR" cp "$F_C_SOURCE" "$F_C_REWRITTEN" exit_on_fail "Failed to copy C source into $COMP_DIR" "Copied C source" @@ -354,13 +355,18 @@ else # Smart buffer placement echo_info "Running smart buffer placement with CP = $TARGET_CP and algorithm = '$BUFFER_ALGORITHM'" cd "$COMP_DIR" + if [[ "$CALCULATE_PATH_DELAYS" == "1" ]]; then + CALC_PD_FLAG="calculate-path-delays" + else + CALC_PD_FLAG="" + fi # To enable debug information, make sure that Dynamatic is built with Debug # mode and add "--debug-only=" to the binary call below. Check # out the value of in the cpp source files. "$DYNAMATIC_OPT_BIN" "$F_HANDSHAKE_TRANSFORMED" \ --handshake-set-unit-impl-attr="target-period=$TARGET_CP timing-models=$DYNAMATIC_DIR/data/components.json impl=$FPUNITS_GEN" \ --handshake-set-buffering-properties="version=fpga20" \ - --handshake-place-buffers="algorithm=$BUFFER_ALGORITHM solver=$MILP_SOLVER frequencies=$F_FREQUENCIES timing-models=$DYNAMATIC_DIR/data/components.json target-period=$TARGET_CP timeout=300 dump-milp-models \ + --handshake-place-buffers="algorithm=$BUFFER_ALGORITHM solver=$MILP_SOLVER frequencies=$F_FREQUENCIES timing-models=$DYNAMATIC_DIR/data/components.json target-period=$TARGET_CP timeout=300 dump-milp-models $CALC_PD_FLAG \ blif-files=$DYNAMATIC_DIR/data/aig/ lut-delay=0.55 lut-size=6 acyclic-type" \ ${SHARING_PASS:+"$SHARING_PASS"} \ > "$F_HANDSHAKE_BUFFERED" diff --git a/tools/export-dot/export-dot.cpp b/tools/export-dot/export-dot.cpp index 86f7cfb176..949e53c597 100644 --- a/tools/export-dot/export-dot.cpp +++ b/tools/export-dot/export-dot.cpp @@ -253,7 +253,10 @@ static StringRef getNodeColor(Operation *op) { .Case( [&](auto) { return "lavender"; }) .Case([&](auto) { return "cyan"; }) - .Case([&](auto) { return "palegreen"; }) + .Case( + [&](handshake::BufferOp bufferOp) -> StringRef { + return bufferOp.isBypassDV() ? "palegreen" : "mediumseagreen"; + }) .Case([&](auto) { return "gold"; }) .Case( [&](auto) { return "gainsboro"; }) diff --git a/tools/integration/TEST_SUITE.cpp b/tools/integration/TEST_SUITE.cpp index cc062239c7..89034238c0 100644 --- a/tools/integration/TEST_SUITE.cpp +++ b/tools/integration/TEST_SUITE.cpp @@ -379,8 +379,8 @@ TEST_P(SpecFixture, spec) { .useSharing = false, .useSpeculation = true, .milpSolver = "gurobi", - .bufferAlgorithm = "fpga20", - .clockPeriod = 7, + .bufferAlgorithm = "fpl22", + .clockPeriod = 8, .simTime = -1 // clang-format on };