Merge remote-tracking branch 'lnis_origin/dev' into ganesh_dev
This commit is contained in:
commit
1c4acff79b
177
.travis.yml
177
.travis.yml
|
@ -1,87 +1,116 @@
|
|||
language: cpp
|
||||
|
||||
# cache results
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- "$TRAVIS_BUILD_DIR/abc"
|
||||
- "$TRAVIS_BUILD_DIR/yosys"
|
||||
- "$TRAVIS_BUILD_DIR/ace2"
|
||||
- "$TRAVIS_BUILD_DIR/libs"
|
||||
- "$HOME/.ccache"
|
||||
- "$HOME/deps"
|
||||
- $TRAVIS_BUILD_DIR/abc
|
||||
- $TRAVIS_BUILD_DIR/yosys
|
||||
- $TRAVIS_BUILD_DIR/ace2
|
||||
- $TRAVIS_BUILD_DIR/libs
|
||||
- $HOME/.ccache
|
||||
# - $HOME/deps
|
||||
|
||||
# Currently sudo is not required, NO ENV is used
|
||||
|
||||
# Supported Operating systems
|
||||
#os:
|
||||
# - linux
|
||||
# - osx
|
||||
# Create a matrix to branch the building environment
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
sudo: false
|
||||
dist: bionic
|
||||
compiler: g++-8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: ppa:ubuntu-toolchain-r/test
|
||||
packages:
|
||||
- autoconf
|
||||
- automake
|
||||
- bash
|
||||
- bison
|
||||
- build-essential
|
||||
- cmake
|
||||
- ctags
|
||||
- curl
|
||||
- doxygen
|
||||
- flex
|
||||
- fontconfig
|
||||
- g++-8
|
||||
- gcc-8
|
||||
- gdb
|
||||
- git
|
||||
- gperf
|
||||
- iverilog
|
||||
- libcairo2-dev
|
||||
- libevent-dev
|
||||
- libfontconfig1-dev
|
||||
- liblist-moreutils-perl
|
||||
- libncurses5-dev
|
||||
- libx11-dev
|
||||
- libxft-dev
|
||||
- libxml++2.6-dev
|
||||
- perl
|
||||
- python
|
||||
- texinfo
|
||||
- time
|
||||
- valgrind
|
||||
- zip
|
||||
- qt5-default
|
||||
- os: linux
|
||||
# Compiler is specified in ./travis/common.sh
|
||||
sudo: false
|
||||
dist: bionic
|
||||
compiler: g++-8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test # For newer GCC
|
||||
- george-edison55-precise-backports # For cmake
|
||||
packages:
|
||||
- autoconf
|
||||
- automake
|
||||
- bash
|
||||
- bison
|
||||
- build-essential
|
||||
- cmake
|
||||
- ctags
|
||||
- curl
|
||||
- doxygen
|
||||
- flex
|
||||
- fontconfig
|
||||
- g++-8
|
||||
- gcc-8
|
||||
- gdb
|
||||
- git
|
||||
- gperf
|
||||
- iverilog
|
||||
- libcairo2-dev
|
||||
- libevent-dev
|
||||
- libfontconfig1-dev
|
||||
- liblist-moreutils-perl
|
||||
- libncurses5-dev
|
||||
- libx11-dev
|
||||
- libxft-dev
|
||||
- libxml++2.6-dev
|
||||
- perl
|
||||
- python
|
||||
- texinfo
|
||||
- time
|
||||
- valgrind
|
||||
- zip
|
||||
- qt5-default
|
||||
# - os: osx
|
||||
# osx_image: xcode10.2 # we target latest MacOS Mojave
|
||||
# sudo: true
|
||||
# compiler: gcc-4.9 # Use clang instead of gcc in MacOS
|
||||
# addons:
|
||||
# homebrew:
|
||||
# packages:
|
||||
# - bison
|
||||
# - cmake
|
||||
# - ctags
|
||||
# - flex
|
||||
# - fontconfig
|
||||
# - git
|
||||
# - gcc@6
|
||||
# - gcc@4.9
|
||||
# - gawk
|
||||
# - icarus-verilog
|
||||
# - libxml++
|
||||
# - qt5
|
||||
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_6f6cf68308be_key -iv $encrypted_6f6cf68308be_iv -in deploy_key.enc -out ./deploy_key -d
|
||||
- eval "$(ssh-agent -s)"
|
||||
- chmod 600 ./deploy_key
|
||||
- cp $TRAVIS_BUILD_DIR/deploy_key ~/.ssh/id_rsa
|
||||
- echo -e "Host *\n StrictHostKeyChecking no\n" >> ~/.ssh/config
|
||||
- ssh u1249762@lab1-1.eng.utah.edu pwd
|
||||
- ssh u1249762@lab1-1.eng.utah.edu "mkdir /var/tmp/travis_bc/$TRAVIS_JOB_ID"
|
||||
- echo $TRAVIS_JOB_ID >> build_id.txt
|
||||
- scp build_id.txt u1249762@lab1-1.eng.utah.edu:/var/tmp/travis_bc/$TRAVIS_JOB_ID/
|
||||
- source .travis/common.sh
|
||||
- source .travis/common.sh
|
||||
|
||||
install:
|
||||
- DEPS_DIR="${HOME}/deps"
|
||||
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
|
||||
- |
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
CMAKE_URL="https://cmake.org/files/v3.12/cmake-3.12.4-Linux-x86_64.tar.gz"
|
||||
mkdir -p cmake && travis_retry wget --no-clobber --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
|
||||
export PATH=${DEPS_DIR}/cmake/bin:${PATH}
|
||||
echo ${PATH}
|
||||
else
|
||||
brew install cmake || brew upgrade cmake
|
||||
fi
|
||||
- cmake --version
|
||||
- cd -
|
||||
- source .travis/install.sh
|
||||
- DEPS_DIR="${HOME}/deps"
|
||||
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
|
||||
- |
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
CMAKE_URL="https://cmake.org/files/v3.12/cmake-3.12.4-Linux-x86_64.tar.gz"
|
||||
mkdir -p cmake && travis_retry wget --no-clobber --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
|
||||
export PATH=${DEPS_DIR}/cmake/bin:${PATH}
|
||||
echo ${PATH}
|
||||
else
|
||||
brew install cmake || brew upgrade cmake
|
||||
fi
|
||||
- cmake --version
|
||||
- cd -
|
||||
- source .travis/install.sh
|
||||
|
||||
script:
|
||||
- ".travis/script.sh"
|
||||
- .travis/script.sh
|
||||
#- .travis/regression.sh
|
||||
|
||||
after_failure:
|
||||
- ".travis/after_failure.sh"
|
||||
- .travis/after_failure.sh
|
||||
|
||||
after_success:
|
||||
- ".travis/after_success.sh"
|
||||
- .travis/after_success.sh
|
||||
notifications:
|
||||
slack:
|
||||
secure: L8tzicFh+EKcK21GBA2m3rQ3jmnDdqiRXIZcb0iqYlhT0V5asYvCqwlpPDUDV1wmBXqPgRJBI/jitAJlKFWu74pLTVc6FscUIDYM7S0DJfHEcufLknZx88lMmmV0IsYLQe3/s89tWoudMf1bNBo/8YWzLDffqUQ7s/rTPD9SWLppb01X0Xm158oDlA0rWETs35nuNFgJxWcSyIyIvnRNE3dHjzmBETUR9mYDsUSYlcOI44FMD8rE6emicdkqdn1zVxScobrl4Dt2bPsMfKopgIKK1x+38AuaqQa7t5F5ICnF0WfxmQ6/TcRNwIij0fDu68w/fcU8SyV+Ex5aZBKYUU7PG7ELTOq+q1geDoTlbguvFSIT4EzqErc4hbJmcUn79BKLhdjshZtGihKatntJx2faXYNYGFjwmnPFRYpqsozydztgMjzv4prZ5yoh7jhoDiGj44QcpXlQ9otM17JdfqveowMLHBYzATsxIRG93irZfXG/E3S8FvXg8mYOIEn8UK7H6i8VWL3JHlw8RbpLdNLswZOUlpEaDAeTm5tvYcw7FGH2nlZ2e5aXLxN6dTovSSRztQHbWdJTGG0N+xldBXcCiChmok4nXGReIkMc+99nZjRsiCB0R16tCNb25/p7NAVkItfVe1qRTzdnhi1hdE7LPURK4kxoFRJ6sFVuYjw=
|
||||
|
|
|
@ -19,24 +19,19 @@ end_section "OpenFPGA.build"
|
|||
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
|
||||
cd -
|
||||
echo -e "Testing single-mode architectures";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py single_mode
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py single_mode --remove_run_dir all
|
||||
#python3 openfpga_flow/scripts/run_fpga_task.py s298
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py single_mode --debug --show_thread_logs
|
||||
#python3 openfpga_flow/scripts/run_fpga_task.py s298
|
||||
|
||||
echo -e "Testing multi-mode architectures";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py blif_vpr_flow --maxthreads 2
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py blif_vpr_flow --remove_run_dir all
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py multi_mode --maxthreads 4 --debug --show_thread_logs
|
||||
|
||||
echo -e "Testing compact routing techniques";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py compact_routing
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py compact_routing --remove_run_dir all
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py compact_routing --debug --show_thread_logs
|
||||
|
||||
echo -e "Testing tileable architectures";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py tileable_routing
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py tileable_routing --remove_run_dir all
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py tileable_routing --debug --show_thread_logs
|
||||
|
||||
echo -e "Testing Verilog generation with explicit port mapping ";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py explicit_verilog
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py explicit_verilog --remove_run_dir all
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py explicit_verilog --debug --show_thread_logs
|
||||
|
||||
end_section "OpenFPGA.TaskTun"
|
||||
|
|
|
@ -182,6 +182,13 @@ set_target_properties(libace ace
|
|||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2")
|
||||
|
||||
# Set output locations to be in the main source tree under the relevant folder
|
||||
set_target_properties(libini
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/external/libini")
|
||||
|
||||
# Set output locations to be in the main source tree under the relevant folder
|
||||
set_target_properties(libvtrutil
|
||||
PROPERTIES
|
||||
|
|
|
@ -7,6 +7,7 @@ files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
|||
|
||||
#Create the library
|
||||
add_library(libini STATIC
|
||||
${LIB_HEADERS})
|
||||
${LIB_HEADERS})
|
||||
|
||||
target_include_directories(libini PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(libini PROPERTIES PREFIX "" LINKER_LANGUAGE CXX)
|
||||
set_target_properties(libini PROPERTIES PREFIX "" LINKER_LANGUAGE CXX)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
//-----------------------------------------------------
|
||||
// Design Name : dual_port_ram_32x512
|
||||
// File Name : dpram.v
|
||||
// Function : Dual port RAM 32x512
|
||||
// Coder : Aurelien Alacchi
|
||||
//-----------------------------------------------------
|
||||
|
||||
module dual_port_ram_32x512 (
|
||||
input clk,
|
||||
input wen,
|
||||
input ren,
|
||||
input[0:8] waddr,
|
||||
input[0:8] raddr,
|
||||
input[0:31] d_in,
|
||||
output[0:31] d_out );
|
||||
|
||||
dual_port_sram_32x512 memory_0 (
|
||||
.wclk (clk),
|
||||
.wen (wen),
|
||||
.waddr (waddr),
|
||||
.data_in (d_in),
|
||||
.rclk (clk),
|
||||
.ren (ren),
|
||||
.raddr (raddr),
|
||||
.d_out (d_out) );
|
||||
|
||||
endmodule
|
||||
|
||||
module dual_port_sram_32x512 (
|
||||
input wclk,
|
||||
input wen,
|
||||
input[0:8] waddr,
|
||||
input[0:31] data_in,
|
||||
input rclk,
|
||||
input ren,
|
||||
input[0:8] raddr,
|
||||
output[0:31] d_out );
|
||||
|
||||
reg[0:31] ram[0:511];
|
||||
reg[0:31] internal;
|
||||
|
||||
assign d_out = internal;
|
||||
|
||||
always @(posedge wclk) begin
|
||||
if(wen) begin
|
||||
ram[waddr] <= data_in;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge rclk) begin
|
||||
if(ren) begin
|
||||
internal <= ram[raddr];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -76,9 +76,9 @@ endmodule //End Of Module static_dff
|
|||
//-----------------------------------------------------
|
||||
module sc_dff_compact (
|
||||
/* Global ports go first */
|
||||
input pReset, // Reset input
|
||||
input reset, // Reset input
|
||||
//input set, // set input
|
||||
input prog_clk, // Clock Input
|
||||
input clk, // Clock Input
|
||||
/* Local ports follow */
|
||||
input D, // Data Input
|
||||
output Q, // Q output
|
||||
|
@ -88,8 +88,8 @@ output Qb // Q output
|
|||
reg q_reg;
|
||||
|
||||
//-------------Code Starts Here---------
|
||||
always @ ( posedge prog_clk or posedge pReset /*or posedge set*/)
|
||||
if (pReset) begin
|
||||
always @ ( posedge clk or posedge reset /*or posedge set*/)
|
||||
if (reset) begin
|
||||
q_reg <= 1'b0;
|
||||
//end else if (set) begin
|
||||
// q_reg <= 1'b1;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//-----------------------------------------------------
|
||||
// Design Name : MUX2
|
||||
// File Name : mux2.v
|
||||
// Function : Standard cell (static gate) implementation
|
||||
// of 2-input multiplexers
|
||||
// Coder : Xifan Tang
|
||||
//-----------------------------------------------------
|
||||
|
||||
module MUX2(
|
||||
input A, // Data input 0
|
||||
input B, // Data input 1
|
||||
input S0, // Select port
|
||||
output Y // Data output
|
||||
);
|
||||
|
||||
assign Y = S0 ? B : A;
|
||||
|
||||
// Note:
|
||||
// MUX2 appears will appear in LUTs, routing multiplexers,
|
||||
// being a component in combinational loops
|
||||
// To help convergence in simulation
|
||||
// i.e., to avoid the X (undetermined) signals,
|
||||
// the following timing constraints and signal initialization
|
||||
// has to be added!
|
||||
|
||||
`ifdef ENABLE_TIMING
|
||||
// ------ BEGIN Pin-to-pin Timing constraints -----
|
||||
specify
|
||||
(A => Y) = (0.001, 0.001);
|
||||
(B => Y) = (0.001, 0.001);
|
||||
(S0 => Y) = (0.001, 0.001);
|
||||
endspecify
|
||||
// ------ END Pin-to-pin Timing constraints -----
|
||||
`endif
|
||||
|
||||
`ifdef ENABLE_SIGNAL_INITIALIZATION
|
||||
// ------ BEGIN driver initialization -----
|
||||
initial begin
|
||||
`ifdef ENABLE_FORMAL_VERIFICATION
|
||||
$deposit(A, 1'b0);
|
||||
$deposit(B, 1'b0);
|
||||
$deposit(S0, 1'b0);
|
||||
`else
|
||||
$deposit(A, $random);
|
||||
$deposit(B, $random);
|
||||
$deposit(S0, $random);
|
||||
`endif
|
||||
|
||||
end
|
||||
// ------ END driver initialization -----
|
||||
`endif
|
||||
|
||||
endmodule
|
|
@ -321,12 +321,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" lib_name="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" lib_name="D" size="1"/>
|
||||
<port type="output" prefix="Q" lib_name="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" lib_name="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
<port name="Q"/>
|
||||
</output_ports>
|
||||
</model>
|
||||
<model name="dpram">
|
||||
<model name="dual_port_ram_32x512">
|
||||
<input_ports>
|
||||
<port name="wen"/> <!-- control -->
|
||||
<port name="ren"/> <!-- control -->
|
||||
|
@ -163,8 +163,8 @@
|
|||
<!-- ODIN II specific config ends -->
|
||||
|
||||
<!-- Physical descriptions begin -->
|
||||
<layout auto="1.0" tileable_routing="off"/>
|
||||
<!--layout width="12" height="12" tileable_routing="off"/-->
|
||||
<layout auto="1.0" tileable_routing="on"/>
|
||||
<!--layout width="5" height="2" tileable_routing="on"/-->
|
||||
<spice_settings>
|
||||
<parameters>
|
||||
<options sim_temp="25" post="off" captab="off" fast="on"/>
|
||||
|
@ -344,14 +344,14 @@
|
|||
<port type="output" prefix="out" size="1"/>
|
||||
</circuit_model>
|
||||
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ff" name="static_dff" prefix="dff" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/ff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerlogNetlists/ff.v">
|
||||
<circuit_model type="ff" name="static_dff" prefix="dff" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/ff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/ff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="input" prefix="Set" size="1" is_global="true" default_val="0" is_set="true"/>
|
||||
<port type="input" prefix="Reset" size="1" is_global="true" default_val="0" is_reset="true"/>
|
||||
<port type="input" prefix="set" size="1" is_global="true" default_val="0" is_set="true"/>
|
||||
<port type="input" prefix="reset" size="1" is_global="true" default_val="0" is_reset="true"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="clock" prefix="clk" size="1" is_global="true" default_val="0" />
|
||||
</circuit_model>
|
||||
|
@ -386,17 +386,17 @@
|
|||
<port type="sram" prefix="mode" size="2" mode_select="true" circuit_model_name="sc_dff_compact" default_val="1"/>
|
||||
</circuit_model>
|
||||
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="sff" name="sc_dff_compact" prefix="scff" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/ff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerlogNetlists/ff.v">
|
||||
<circuit_model type="sff" name="sc_dff_compact" prefix="scff" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/ff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/ff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
@ -421,16 +421,16 @@
|
|||
<port type="output" prefix="sumout" size="1"/>
|
||||
<port type="output" prefix="cout" size="1"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="hard_logic" name="dpram" prefix="dpram" dump_explicit_port_map="true" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/spram.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/memory_wrapper.v">
|
||||
<circuit_model type="hard_logic" name="dual_port_ram_32x512" prefix="dual_port_ram_32x512" dump_explicit_port_map="true" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/spram.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/dpram.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="on" circuit_model_name="buf1"/>
|
||||
<output_buffer exist="on" circuit_model_name="buf1"/>
|
||||
<port type="input" prefix="waddr" size="11"/>
|
||||
<port type="input" prefix="raddr" size="11"/>
|
||||
<port type="input" prefix="d_in" size="64"/>
|
||||
<port type="input" prefix="waddr" size="9"/>
|
||||
<port type="input" prefix="raddr" size="9"/>
|
||||
<port type="input" prefix="d_in" size="32"/>
|
||||
<port type="input" prefix="wen" size="1"/>
|
||||
<port type="input" prefix="ren" size="1"/>
|
||||
<port type="output" prefix="d_out" size="64"/>
|
||||
<port type="output" prefix="d_out" size="32"/>
|
||||
<port type="clock" prefix="clk" size="1" is_global="true" default_val="0"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="sram" name="sram6T" prefix="sram" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/sram.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/sram.v">
|
||||
|
@ -613,31 +613,31 @@
|
|||
|
||||
<!-- SPRAM instanciation -->
|
||||
|
||||
<pb_type name="memory_dp" height="6" area="548000">
|
||||
<pb_type name="memory_dp" height="2" area="548000">
|
||||
|
||||
<input name="waddr" num_pins="11" equivalent="false"/>
|
||||
<input name="raddr" num_pins="11" equivalent="false"/>
|
||||
<input name="d_in" num_pins="64" equivalent="false"/>
|
||||
<input name="waddr" num_pins="9" equivalent="false"/>
|
||||
<input name="raddr" num_pins="9" equivalent="false"/>
|
||||
<input name="d_in" num_pins="32" equivalent="false"/>
|
||||
<input name="wen" num_pins="1"/>
|
||||
<input name="ren" num_pins="1"/>
|
||||
<output name="d_out" num_pins="64" equivalent="false"/>
|
||||
<output name="d_out" num_pins="32" equivalent="false"/>
|
||||
<clock name="clk" num_pins="1"/>
|
||||
|
||||
<pb_type name="memory" num_pb="1">
|
||||
<input name="waddr" num_pins="11"/>
|
||||
<input name="raddr" num_pins="11"/>
|
||||
<input name="d_in" num_pins="64"/>
|
||||
<input name="waddr" num_pins="9"/>
|
||||
<input name="raddr" num_pins="9"/>
|
||||
<input name="d_in" num_pins="32"/>
|
||||
<input name="wen" num_pins="1"/>
|
||||
<input name="ren" num_pins="1"/>
|
||||
<output name="d_out" num_pins="64"/>
|
||||
<output name="d_out" num_pins="32"/>
|
||||
<clock name="clk" num_pins="1"/>
|
||||
<pb_type name="dpram" num_pb="1" blif_model=".subckt dpram" circuit_model_name="dpram">
|
||||
<input name="waddr" num_pins="11" port_class="address"/>
|
||||
<input name="raddr" num_pins="11" port_class="address"/>
|
||||
<input name="d_in" num_pins="64" port_class="data_in"/>
|
||||
<pb_type name="dpram" num_pb="1" blif_model=".subckt dual_port_ram_32x512" circuit_model_name="dual_port_ram_32x512">
|
||||
<input name="waddr" num_pins="9" port_class="address"/>
|
||||
<input name="raddr" num_pins="9" port_class="address"/>
|
||||
<input name="d_in" num_pins="32" port_class="data_in"/>
|
||||
<input name="wen" num_pins="1" port_class="write_en"/>
|
||||
<input name="ren" num_pins="1" port_class="write_en"/>
|
||||
<output name="d_out" num_pins="64" port_class="data_out"/>
|
||||
<input name="ren" num_pins="1" port_class="read_en"/>
|
||||
<output name="d_out" num_pins="32" port_class="data_out"/>
|
||||
<clock name="clk" num_pins="1" port_class="clock"/>
|
||||
</pb_type>
|
||||
<interconnect>
|
||||
|
@ -647,8 +647,8 @@
|
|||
<direct name="address2" input="memory.raddr" output="dpram.raddr">
|
||||
<delay_constant max="132e-12" in_port="memory.raddr" out_port="dpram.raddr"/>
|
||||
</direct>
|
||||
<direct name="data1" input="memory.d_in[63:0]" output="dpram.d_in">
|
||||
<delay_constant max="132e-12" in_port="memory.d_in[63:0]" out_port="dpram.d_in"/>
|
||||
<direct name="data1" input="memory.d_in[31:0]" output="dpram.d_in">
|
||||
<delay_constant max="132e-12" in_port="memory.d_in[31:0]" out_port="dpram.d_in"/>
|
||||
</direct>
|
||||
<direct name="writeen1" input="memory.wen" output="dpram.wen">
|
||||
<delay_constant max="132e-12" in_port="memory.wen" out_port="dpram.wen"/>
|
||||
|
@ -656,8 +656,8 @@
|
|||
<direct name="readen1" input="memory.ren" output="dpram.ren">
|
||||
<delay_constant max="132e-12" in_port="memory.ren" out_port="dpram.ren"/>
|
||||
</direct>
|
||||
<direct name="dataout1" input="dpram.d_out" output="memory.d_out[63:0]">
|
||||
<delay_constant max="40e-12" in_port="dpram.d_out" out_port="memory.d_out[63:0]"/>
|
||||
<direct name="dataout1" input="dpram.d_out" output="memory.d_out[31:0]">
|
||||
<delay_constant max="40e-12" in_port="dpram.d_out" out_port="memory.d_out[31:0]"/>
|
||||
</direct>
|
||||
<direct name="clk" input="memory.clk" output="dpram.clk">
|
||||
</direct>
|
||||
|
@ -674,13 +674,13 @@
|
|||
<direct name="clk" input="memory_dp.clk" output="memory.clk"/>
|
||||
</interconnect>
|
||||
<fc default_in_type="frac" default_in_val="0.15" default_out_type="frac" default_out_val="0.10"/>
|
||||
<pinlocations pattern="spread"/>
|
||||
<!--pinlocations pattern="custom">
|
||||
<!--pinlocations pattern="spread"/-->
|
||||
<pinlocations pattern="custom">
|
||||
<loc side="left">memory_dp.d_in</loc>
|
||||
<loc side="top" offset="5">memory_dp.clk memory_dp.wen memory_dp.waddr</loc>
|
||||
<loc side="top" offset="1">memory_dp.clk memory_dp.wen memory_dp.waddr</loc>
|
||||
<loc side="right">memory_dp.d_out</loc>
|
||||
<loc side="bottom">memory_dp.ren memory_dp.raddr</loc>
|
||||
</pinlocations-->
|
||||
</pinlocations>
|
||||
|
||||
<!-- Place this memory block every 8 columns from (and including) the second column -->
|
||||
<gridlocations>
|
||||
|
|
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -377,12 +377,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -379,12 +379,12 @@
|
|||
<input_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<output_buffer exist="on" circuit_model_name="INV1X"/>
|
||||
<pass_gate_logic circuit_model_name="TGATEX1"/>
|
||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<!-- <port type="input" prefix="pSet" size="1" is_global="true" default_val="0" is_set="true" is_prog="true"/> -->
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="Qb" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||
<design_technology type="cmos"/>
|
||||
|
|
|
@ -13,7 +13,6 @@ i_11_ 0.495600 0.504600
|
|||
i_12_ 0.502800 0.507600
|
||||
i_13_ 0.494600 0.500600
|
||||
i_14_ 0.504800 0.502800
|
||||
i_15_ 0.487600 0.495200
|
||||
i_16_ 0.504000 0.505200
|
||||
i_17_ 0.497400 0.512600
|
||||
i_18_ 0.502200 0.502200
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Benchmark "apex2" written by ABC on Tue Mar 12 09:34:21 2019
|
||||
.model apex2
|
||||
.inputs i_0_ i_1_ i_2_ i_3_ i_4_ i_5_ i_6_ i_7_ i_8_ i_9_ i_10_ i_11_ i_12_ \
|
||||
i_13_ i_14_ i_15_ i_16_ i_17_ i_18_ i_19_ i_20_ i_21_ i_22_ i_23_ i_24_ \
|
||||
i_13_ i_14_ i_16_ i_17_ i_18_ i_19_ i_20_ i_21_ i_22_ i_23_ i_24_ \
|
||||
i_25_ i_26_ i_27_ i_28_ i_29_ i_30_ i_31_ i_32_ i_33_ i_34_ i_35_ i_36_ \
|
||||
i_37_ i_38_
|
||||
.outputs o_0_ o_1_ o_2_
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
/* Generated by Yosys 0.8+133 (git sha1 2a2e0a4, gcc 7.3.0 -fPIC -Os) */
|
||||
|
||||
module apex2(i_0_, i_1_, i_2_, i_3_, i_4_, i_5_, i_6_, i_7_, i_8_, i_9_, i_10_, i_11_, i_12_, i_13_, i_14_, i_15_, i_16_, i_17_, i_18_, i_19_, i_20_, i_21_, i_22_, i_23_, i_24_, i_25_, i_26_, i_27_, i_28_, i_29_, i_30_, i_31_, i_32_, i_33_, i_34_, i_35_, i_36_, i_37_, i_38_, o_0_, o_1_, o_2_);
|
||||
module apex2(i_0_, i_1_, i_2_, i_3_, i_4_, i_5_, i_6_, i_7_, i_8_, i_9_, i_10_, i_11_, i_12_, i_13_, i_14_, i_16_, i_17_, i_18_, i_19_, i_20_, i_21_, i_22_, i_23_, i_24_, i_25_, i_26_, i_27_, i_28_, i_29_, i_30_, i_31_, i_32_, i_33_, i_34_, i_35_, i_36_, i_37_, i_38_, o_0_, o_1_, o_2_);
|
||||
input i_0_;
|
||||
input i_10_;
|
||||
input i_11_;
|
||||
input i_12_;
|
||||
input i_13_;
|
||||
input i_14_;
|
||||
input i_15_;
|
||||
input i_16_;
|
||||
input i_17_;
|
||||
input i_18_;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
clk 0.5 0.2
|
||||
wen 0.5 0.2
|
||||
wen_st0 0.5 0.2
|
||||
wen_st1 0.5 0.2
|
||||
ren 0.5 0.2
|
||||
raddr_0_ 0.5 0.2
|
||||
raddr_1_ 0.5 0.2
|
||||
raddr_2_ 0.5 0.2
|
||||
raddr_3_ 0.5 0.2
|
||||
raddr_4_ 0.5 0.2
|
||||
raddr_5_ 0.5 0.2
|
||||
waddr_0_ 0.5 0.2
|
||||
waddr_1_ 0.5 0.2
|
||||
waddr_2_ 0.5 0.2
|
||||
waddr_3_ 0.5 0.2
|
||||
waddr_4_ 0.5 0.2
|
||||
waddr_5_ 0.5 0.2
|
||||
waddr_st0_0_ 0.5 0.2
|
||||
waddr_st0_1_ 0.5 0.2
|
||||
waddr_st0_2_ 0.5 0.2
|
||||
waddr_st0_3_ 0.5 0.2
|
||||
waddr_st0_4_ 0.5 0.2
|
||||
waddr_st0_5_ 0.5 0.2
|
||||
waddr_st1_0_ 0.5 0.2
|
||||
waddr_st1_1_ 0.5 0.2
|
||||
waddr_st1_2_ 0.5 0.2
|
||||
waddr_st1_3_ 0.5 0.2
|
||||
waddr_st1_4_ 0.5 0.2
|
||||
waddr_st1_5_ 0.5 0.2
|
||||
a_0_ 0.5 0.2
|
||||
a_1_ 0.5 0.2
|
||||
a_2_ 0.5 0.2
|
||||
a_3_ 0.5 0.2
|
||||
a_4_ 0.5 0.2
|
||||
a_5_ 0.5 0.2
|
||||
a_6_ 0.5 0.2
|
||||
a_st0_0_ 0.5 0.2
|
||||
a_st0_1_ 0.5 0.2
|
||||
a_st0_2_ 0.5 0.2
|
||||
a_st0_3_ 0.5 0.2
|
||||
a_st0_4_ 0.5 0.2
|
||||
a_st0_5_ 0.5 0.2
|
||||
a_st0_6_ 0.5 0.2
|
||||
a_st1_0_ 0.5 0.2
|
||||
a_st1_1_ 0.5 0.2
|
||||
a_st1_2_ 0.5 0.2
|
||||
a_st1_3_ 0.5 0.2
|
||||
a_st1_4_ 0.5 0.2
|
||||
a_st1_5_ 0.5 0.2
|
||||
a_st1_6_ 0.5 0.2
|
||||
b_0_ 0.5 0.2
|
||||
b_1_ 0.5 0.2
|
||||
b_2_ 0.5 0.2
|
||||
b_3_ 0.5 0.2
|
||||
b_4_ 0.5 0.2
|
||||
b_5_ 0.5 0.2
|
||||
b_6_ 0.5 0.2
|
||||
b_st0_0_ 0.5 0.2
|
||||
b_st0_1_ 0.5 0.2
|
||||
b_st0_2_ 0.5 0.2
|
||||
b_st0_3_ 0.5 0.2
|
||||
b_st0_4_ 0.5 0.2
|
||||
b_st0_5_ 0.5 0.2
|
||||
b_st0_6_ 0.5 0.2
|
||||
b_st1_0_ 0.5 0.2
|
||||
b_st1_1_ 0.5 0.2
|
||||
b_st1_2_ 0.5 0.2
|
||||
b_st1_3_ 0.5 0.2
|
||||
b_st1_4_ 0.5 0.2
|
||||
b_st1_5_ 0.5 0.2
|
||||
b_st1_6_ 0.5 0.2
|
||||
q_0_ 0.5 0.2
|
||||
q_1_ 0.5 0.2
|
||||
q_2_ 0.5 0.2
|
||||
q_3_ 0.5 0.2
|
||||
q_4_ 0.5 0.2
|
||||
q_5_ 0.5 0.2
|
||||
q_6_ 0.5 0.2
|
||||
q_7_ 0.5 0.2
|
||||
AplusB_0_ 0.5 0.2
|
||||
AplusB_1_ 0.5 0.2
|
||||
AplusB_2_ 0.5 0.2
|
||||
AplusB_3_ 0.5 0.2
|
||||
AplusB_4_ 0.5 0.2
|
||||
AplusB_5_ 0.5 0.2
|
||||
AplusB_6_ 0.5 0.2
|
||||
AplusB_7_ 0.5 0.2
|
||||
cint01 0.5 0.2
|
||||
cint02 0.5 0.2
|
||||
cint03 0.5 0.2
|
||||
cint04 0.5 0.2
|
||||
cint05 0.5 0.2
|
||||
cint06 0.5 0.2
|
||||
cint07 0.5 0.2
|
||||
zero00 0 0
|
|
@ -0,0 +1,126 @@
|
|||
# Benchmark pipelined_32b_adder
|
||||
.model pipelined_32b_adder
|
||||
.inputs clk wen ren raddr_0_ raddr_1_ raddr_2_ raddr_3_ raddr_4_ raddr_5_ waddr_0_ waddr_1_ waddr_2_ waddr_3_ waddr_4_ waddr_5_ a_0_ a_1_ a_2_ a_3_ a_4_ a_5_ a_6_ b_0_ b_1_ b_2_ b_3_ b_4_ b_5_ b_6_
|
||||
.outputs q_0_ q_1_ q_2_ q_3_ q_4_ q_5_ q_6_ q_7_
|
||||
|
||||
# Start pipeline
|
||||
# Pipeline a
|
||||
.subckt shift D=a_0_ clk=clk Q=a_st0_0_
|
||||
.subckt shift D=a_st0_0_ clk=clk Q=a_st1_0_
|
||||
.subckt shift D=a_1_ clk=clk Q=a_st0_1_
|
||||
.subckt shift D=a_st0_1_ clk=clk Q=a_st1_1_
|
||||
.subckt shift D=a_2_ clk=clk Q=a_st0_2_
|
||||
.subckt shift D=a_st0_2_ clk=clk Q=a_st1_2_
|
||||
.subckt shift D=a_3_ clk=clk Q=a_st0_3_
|
||||
.subckt shift D=a_st0_3_ clk=clk Q=a_st1_3_
|
||||
.subckt shift D=a_4_ clk=clk Q=a_st0_4_
|
||||
.subckt shift D=a_st0_4_ clk=clk Q=a_st1_4_
|
||||
.subckt shift D=a_5_ clk=clk Q=a_st0_5_
|
||||
.subckt shift D=a_st0_5_ clk=clk Q=a_st1_5_
|
||||
.subckt shift D=a_6_ clk=clk Q=a_st0_6_
|
||||
.subckt shift D=a_st0_6_ clk=clk Q=a_st1_6_
|
||||
|
||||
# Pipeline b
|
||||
.subckt shift D=b_0_ clk=clk Q=b_st0_0_
|
||||
.subckt shift D=b_st0_0_ clk=clk Q=b_st1_0_
|
||||
.subckt shift D=b_1_ clk=clk Q=b_st0_1_
|
||||
.subckt shift D=b_st0_1_ clk=clk Q=b_st1_1_
|
||||
.subckt shift D=b_2_ clk=clk Q=b_st0_2_
|
||||
.subckt shift D=b_st0_2_ clk=clk Q=b_st1_2_
|
||||
.subckt shift D=b_3_ clk=clk Q=b_st0_3_
|
||||
.subckt shift D=b_st0_3_ clk=clk Q=b_st1_3_
|
||||
.subckt shift D=b_4_ clk=clk Q=b_st0_4_
|
||||
.subckt shift D=b_st0_4_ clk=clk Q=b_st1_4_
|
||||
.subckt shift D=b_5_ clk=clk Q=b_st0_5_
|
||||
.subckt shift D=b_st0_5_ clk=clk Q=b_st1_5_
|
||||
.subckt shift D=b_6_ clk=clk Q=b_st0_6_
|
||||
.subckt shift D=b_st0_6_ clk=clk Q=b_st1_6_
|
||||
|
||||
# Pipeline waddr
|
||||
.subckt shift D=waddr_0_ clk=clk Q=waddr_st0_0_
|
||||
.subckt shift D=waddr_st0_0_ clk=clk Q=waddr_st1_0_
|
||||
.subckt shift D=waddr_1_ clk=clk Q=waddr_st0_1_
|
||||
.subckt shift D=waddr_st0_1_ clk=clk Q=waddr_st1_1_
|
||||
.subckt shift D=waddr_2_ clk=clk Q=waddr_st0_2_
|
||||
.subckt shift D=waddr_st0_2_ clk=clk Q=waddr_st1_2_
|
||||
.subckt shift D=waddr_3_ clk=clk Q=waddr_st0_3_
|
||||
.subckt shift D=waddr_st0_3_ clk=clk Q=waddr_st1_3_
|
||||
.subckt shift D=waddr_4_ clk=clk Q=waddr_st0_4_
|
||||
.subckt shift D=waddr_st0_4_ clk=clk Q=waddr_st1_4_
|
||||
.subckt shift D=waddr_5_ clk=clk Q=waddr_st0_5_
|
||||
.subckt shift D=waddr_st0_5_ clk=clk Q=waddr_st1_5_
|
||||
# Pipeline wen
|
||||
.subckt shift D=wen clk=clk Q=wen_st0
|
||||
.subckt shift D=wen_st0 clk=clk Q=wen_st1
|
||||
# End pipeline
|
||||
|
||||
# Start adder
|
||||
.subckt adder a=a_st1_0_ b=b_st1_0_ cin=zero00 cout=cint01 sumout=AplusB_0_
|
||||
.subckt adder a=a_st1_1_ b=b_st1_1_ cin=cint01 cout=cint02 sumout=AplusB_1_
|
||||
.subckt adder a=a_st1_2_ b=b_st1_2_ cin=cint02 cout=cint03 sumout=AplusB_2_
|
||||
.subckt adder a=a_st1_3_ b=b_st1_3_ cin=cint03 cout=cint04 sumout=AplusB_3_
|
||||
.subckt adder a=a_st1_4_ b=b_st1_4_ cin=cint04 cout=cint05 sumout=AplusB_4_
|
||||
.subckt adder a=a_st1_5_ b=b_st1_5_ cin=cint05 cout=cint06 sumout=AplusB_5_
|
||||
.subckt adder a=a_st1_6_ b=b_st1_6_ cin=cint06 cout=cint07 sumout=AplusB_6_
|
||||
.subckt adder a=zero00 b=zero00 cin=cint07 cout=unconn sumout=AplusB_7_
|
||||
# End adder
|
||||
|
||||
# Start DPRAM
|
||||
.subckt dual_port_ram_32x512 clk=clk wen=wen_st1 ren=ren \
|
||||
waddr[0]=waddr_st1_0_ waddr[1]=waddr_st1_1_ waddr[2]=waddr_st1_2_ waddr[3]=waddr_st1_3_ waddr[4]=waddr_st1_4_ \
|
||||
waddr[5]=waddr_st1_5_ waddr[6]=zero00 waddr[7]=zero00 waddr[8]=zero00 \
|
||||
raddr[0]=raddr_0_ raddr[1]=raddr_1_ raddr[2]=raddr_2_ raddr[3]=raddr_3_ raddr[4]=raddr_4_ raddr[5]=raddr_5_ \
|
||||
raddr[6]=zero00 raddr[7]=zero00 raddr[8]=zero00 \
|
||||
d_in[0]=AplusB_0_ d_in[1]=AplusB_1_ d_in[2]=AplusB_2_ d_in[3]=AplusB_3_ d_in[4]=AplusB_4_ d_in[5]=AplusB_5_ \
|
||||
d_in[6]=AplusB_6_ d_in[7]=AplusB_7_ d_in[8]=zero00 d_in[9]=zero00 d_in[10]=zero00 d_in[11]=zero00 \
|
||||
d_in[12]=zero00 d_in[13]=zero00 d_in[14]=zero00 d_in[15]=zero00 d_in[16]=zero00 d_in[17]=zero00 \
|
||||
d_in[18]=zero00 d_in[19]=zero00 d_in[20]=zero00 d_in[21]=zero00 d_in[22]=zero00 d_in[23]=zero00 \
|
||||
d_in[24]=zero00 d_in[25]=zero00 d_in[26]=zero00 d_in[27]=zero00 d_in[28]=zero00 d_in[29]=zero00 \
|
||||
d_in[30]=zero00 d_in[31]=zero00 \
|
||||
d_out[0]=q_0_ d_out[1]=q_1_ d_out[2]=q_2_ d_out[3]=q_3_ d_out[4]=q_4_ d_out[5]=q_5_ \
|
||||
d_out[6]=q_6_ d_out[7]=q_7_ d_out[8]=unconn d_out[9]=unconn d_out[10]=unconn \
|
||||
d_out[11]=unconn d_out[12]=unconn d_out[13]=unconn d_out[14]=unconn d_out[15]=unconn \
|
||||
d_out[16]=unconn d_out[17]=unconn d_out[18]=unconn d_out[19]=unconn d_out[20]=unconn \
|
||||
d_out[21]=unconn d_out[22]=unconn d_out[23]=unconn d_out[24]=unconn d_out[25]=unconn \
|
||||
d_out[26]=unconn d_out[27]=unconn d_out[28]=unconn d_out[29]=unconn d_out[30]=unconn d_out[31]=unconn
|
||||
# End DPRAM
|
||||
|
||||
# Start global variable
|
||||
.names zero00
|
||||
0
|
||||
# End global variable
|
||||
|
||||
|
||||
.end
|
||||
|
||||
# Start blackbox definition
|
||||
.model dual_port_ram_32x512
|
||||
.inputs clk ren wen waddr[0] waddr[1] waddr[2] waddr[3] waddr[4] waddr[5] \
|
||||
waddr[6] waddr[7] waddr[8] raddr[0] raddr[1] raddr[2] \
|
||||
raddr[3] raddr[4] raddr[5] raddr[6] raddr[7] raddr[8] \
|
||||
d_in[0] d_in[1] d_in[2] d_in[3] d_in[4] d_in[5] d_in[6] d_in[7] d_in[8] \
|
||||
d_in[9] d_in[10] d_in[11] d_in[12] d_in[13] d_in[14] d_in[15] d_in[16] \
|
||||
d_in[17] d_in[18] d_in[19] d_in[20] d_in[21] d_in[22] d_in[23] d_in[24] \
|
||||
d_in[25] d_in[26] d_in[27] d_in[28] d_in[29] d_in[30] d_in[31]
|
||||
.outputs d_out[0] d_out[1] d_out[2] d_out[3] d_out[4] d_out[5] d_out[6] \
|
||||
d_out[7] d_out[8] d_out[9] d_out[10] d_out[11] d_out[12] d_out[13] \
|
||||
d_out[14] d_out[15] d_out[16] d_out[17] d_out[18] d_out[19] d_out[20] \
|
||||
d_out[21] d_out[22] d_out[23] d_out[24] d_out[25] d_out[26] d_out[27] \
|
||||
d_out[28] d_out[29] d_out[30] d_out[31]
|
||||
.blackbox
|
||||
.end
|
||||
|
||||
|
||||
.model adder
|
||||
.inputs a b cin
|
||||
.outputs cout sumout
|
||||
.blackbox
|
||||
.end
|
||||
|
||||
|
||||
.model shift
|
||||
.inputs D clk
|
||||
.outputs Q
|
||||
.blackbox
|
||||
.end
|
||||
# End blackbox definition
|
|
@ -0,0 +1,107 @@
|
|||
//-----------------------------------------------------
|
||||
// Design Name : pipelined_8bit_adder
|
||||
// File Name : pipelined_8bit_adder.v
|
||||
// Function : Pipelined 8-bit adders, whose sum and carry outputs
|
||||
// are cached in a memory
|
||||
// Coder : Aurelien Alacchi
|
||||
//-----------------------------------------------------
|
||||
|
||||
`timescale 1 ns/ 1 ps
|
||||
|
||||
// To match the port definition in BLIF format, so that we can do verification
|
||||
// Each input/output bus is expanded here.
|
||||
// In future, we should be able to support buses in verification!
|
||||
|
||||
module pipelined_8bit_adder(
|
||||
input clk,
|
||||
input ren,
|
||||
input wen,
|
||||
input raddr_0_,
|
||||
input raddr_1_,
|
||||
input raddr_2_,
|
||||
input raddr_3_,
|
||||
input raddr_4_,
|
||||
input raddr_5_,
|
||||
input waddr_0_,
|
||||
input waddr_1_,
|
||||
input waddr_2_,
|
||||
input waddr_3_,
|
||||
input waddr_4_,
|
||||
input waddr_5_,
|
||||
input a_0_,
|
||||
input a_1_,
|
||||
input a_2_,
|
||||
input a_3_,
|
||||
input a_4_,
|
||||
input a_5_,
|
||||
input a_6_,
|
||||
input b_0_,
|
||||
input b_1_,
|
||||
input b_2_,
|
||||
input b_3_,
|
||||
input b_4_,
|
||||
input b_5_,
|
||||
input b_6_,
|
||||
output q_0_,
|
||||
output q_1_,
|
||||
output q_2_,
|
||||
output q_3_,
|
||||
output q_4_,
|
||||
output q_5_,
|
||||
output q_6_,
|
||||
output q_7_);
|
||||
|
||||
wire [5:0] raddr;
|
||||
wire [5:0] waddr;
|
||||
wire [6:0] a;
|
||||
wire [6:0] b;
|
||||
wire [7:0] q;
|
||||
|
||||
|
||||
assign raddr = { raddr_5_, raddr_4_, raddr_3_, raddr_2_, raddr_1_, raddr_0_ };
|
||||
assign waddr = { waddr_5_, waddr_4_, waddr_3_, waddr_2_, waddr_1_, waddr_0_ };
|
||||
assign a = { a_6_, a_5_, a_4_, a_3_, a_2_, a_1_, a_0_ };
|
||||
assign b = { b_6_, b_5_, b_4_, b_3_, b_2_, b_1_, b_0_ };
|
||||
assign q_7_ = q[7];
|
||||
assign q_6_ = q[6];
|
||||
assign q_5_ = q[5];
|
||||
assign q_4_ = q[4];
|
||||
assign q_3_ = q[3];
|
||||
assign q_2_ = q[2];
|
||||
assign q_1_ = q[1];
|
||||
assign q_0_ = q[0];
|
||||
|
||||
reg[7:0] ram[63:0];
|
||||
reg[6:0] a_st0;
|
||||
reg[6:0] a_st1;
|
||||
reg[6:0] b_st0;
|
||||
reg[6:0] b_st1;
|
||||
reg[8:0] waddr_st0;
|
||||
reg[8:0] waddr_st1;
|
||||
reg wen_st0;
|
||||
reg wen_st1;
|
||||
reg[7:0] q_int;
|
||||
|
||||
wire[7:0] AplusB;
|
||||
|
||||
assign AplusB = a_st1 + b_st1;
|
||||
assign q = q_int;
|
||||
|
||||
always@(posedge clk) begin
|
||||
waddr_st0 <= waddr;
|
||||
waddr_st1 <= waddr_st0;
|
||||
a_st0 <= a;
|
||||
a_st1 <= a_st0;
|
||||
b_st0 <= b;
|
||||
b_st1 <= b_st0;
|
||||
wen_st0 <= wen;
|
||||
wen_st1 <= wen_st0;
|
||||
if(wen_st1) begin
|
||||
ram[waddr_st1] <= AplusB;
|
||||
end
|
||||
if(ren) begin
|
||||
q_int <= ram[raddr];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,267 @@
|
|||
//-----------------------------------------------------
|
||||
// Design Name : pipelined_8bit_adder_top_formal_verification_random_tb
|
||||
// File Name : pipelined_8bit_adder.v
|
||||
// Function : Testbench for pipelined 8-bit adders, whose sum and carry outputs
|
||||
// are cached in a dual-port memory.
|
||||
// This testbench will do a verification of FPGA implementation
|
||||
// of the pipelined 8-bit adders against the reference
|
||||
// (original) Verilog netlist
|
||||
// To provide a practical testing, this testbench will :
|
||||
// - Instantiate a pre-programmed FPGA and the proper
|
||||
// benchmark
|
||||
// - Load memories with random values
|
||||
// - Randomly write and read from the memories
|
||||
// - Watch for any difference between FPGA and Benchmark
|
||||
// outputs, after memories are fully loaded
|
||||
//
|
||||
// PLEASE USE this testbench instead of the auto-generated test
|
||||
// benches when performing verification!
|
||||
// Coder : Aurelien Alacchi and Xifan Tang
|
||||
//-----------------------------------------------------
|
||||
|
||||
`timescale 1 ns/ 100 ps
|
||||
|
||||
module pipelined_8bit_adder_top_formal_verification_random_tb();
|
||||
reg clk;
|
||||
reg[5:0] raddr;
|
||||
reg[5:0] waddr;
|
||||
reg ren;
|
||||
reg wen;
|
||||
reg[6:0] a;
|
||||
reg[6:0] b;
|
||||
wire[7:0] q_gfpga;
|
||||
wire[7:0] q_bench;
|
||||
reg[7:0] q_flag;
|
||||
|
||||
pipelined_8bit_adder_top_formal_verification DUT(
|
||||
.clk_fm (clk),
|
||||
.ren_fm (ren),
|
||||
.wen_fm (wen),
|
||||
.raddr_0__fm (raddr[0]),
|
||||
.raddr_1__fm (raddr[1]),
|
||||
.raddr_2__fm (raddr[2]),
|
||||
.raddr_3__fm (raddr[3]),
|
||||
.raddr_4__fm (raddr[4]),
|
||||
.raddr_5__fm (raddr[5]),
|
||||
.waddr_0__fm (waddr[0]),
|
||||
.waddr_1__fm (waddr[1]),
|
||||
.waddr_2__fm (waddr[2]),
|
||||
.waddr_3__fm (waddr[3]),
|
||||
.waddr_4__fm (waddr[4]),
|
||||
.waddr_5__fm (waddr[5]),
|
||||
.a_0__fm (a[0]),
|
||||
.a_1__fm (a[1]),
|
||||
.a_2__fm (a[2]),
|
||||
.a_3__fm (a[3]),
|
||||
.a_4__fm (a[4]),
|
||||
.a_5__fm (a[5]),
|
||||
.a_6__fm (a[6]),
|
||||
.b_0__fm (b[0]),
|
||||
.b_1__fm (b[1]),
|
||||
.b_2__fm (b[2]),
|
||||
.b_3__fm (b[3]),
|
||||
.b_4__fm (b[4]),
|
||||
.b_5__fm (b[5]),
|
||||
.b_6__fm (b[6]),
|
||||
.out_q_0__fm (q_gfpga[0]),
|
||||
.out_q_1__fm (q_gfpga[1]),
|
||||
.out_q_2__fm (q_gfpga[2]),
|
||||
.out_q_3__fm (q_gfpga[3]),
|
||||
.out_q_4__fm (q_gfpga[4]),
|
||||
.out_q_5__fm (q_gfpga[5]),
|
||||
.out_q_6__fm (q_gfpga[6]),
|
||||
.out_q_7__fm (q_gfpga[7])
|
||||
);
|
||||
|
||||
pipelined_8bit_adder ref0(
|
||||
.clk (clk),
|
||||
.ren (ren),
|
||||
.wen (wen),
|
||||
.raddr_0_ (raddr[0]),
|
||||
.raddr_1_ (raddr[1]),
|
||||
.raddr_2_ (raddr[2]),
|
||||
.raddr_3_ (raddr[3]),
|
||||
.raddr_4_ (raddr[4]),
|
||||
.raddr_5_ (raddr[5]),
|
||||
.waddr_0_ (waddr[0]),
|
||||
.waddr_1_ (waddr[1]),
|
||||
.waddr_2_ (waddr[2]),
|
||||
.waddr_3_ (waddr[3]),
|
||||
.waddr_4_ (waddr[4]),
|
||||
.waddr_5_ (waddr[5]),
|
||||
.a_0_ (a[0]),
|
||||
.a_1_ (a[1]),
|
||||
.a_2_ (a[2]),
|
||||
.a_3_ (a[3]),
|
||||
.a_4_ (a[4]),
|
||||
.a_5_ (a[5]),
|
||||
.a_6_ (a[6]),
|
||||
.b_0_ (b[0]),
|
||||
.b_1_ (b[1]),
|
||||
.b_2_ (b[2]),
|
||||
.b_3_ (b[3]),
|
||||
.b_4_ (b[4]),
|
||||
.b_5_ (b[5]),
|
||||
.b_6_ (b[6]),
|
||||
.q_0_ (q_bench[0]),
|
||||
.q_1_ (q_bench[1]),
|
||||
.q_2_ (q_bench[2]),
|
||||
.q_3_ (q_bench[3]),
|
||||
.q_4_ (q_bench[4]),
|
||||
.q_5_ (q_bench[5]),
|
||||
.q_6_ (q_bench[6]),
|
||||
.q_7_ (q_bench[7])
|
||||
);
|
||||
|
||||
integer nb_error = 0;
|
||||
integer count = 0;
|
||||
integer lim_max = 64 - 1;
|
||||
integer write_complete = 0;
|
||||
|
||||
//----- Initialization
|
||||
initial begin
|
||||
clk <= 1'b0;
|
||||
a <= 7'h00;
|
||||
b <= 7'h00;
|
||||
wen <= 1'b0;
|
||||
ren <= 1'b0;
|
||||
waddr <= 6'h00;
|
||||
raddr <= 6'h00;
|
||||
while(1) begin
|
||||
#2.5
|
||||
clk <= !clk;
|
||||
end
|
||||
end
|
||||
|
||||
//----- Input Stimulis
|
||||
always@(negedge clk) begin
|
||||
if(write_complete == 0) begin
|
||||
wen <= 1'b1;
|
||||
ren <= 1'b0;
|
||||
count <= count + 1;
|
||||
waddr <= waddr + 1;
|
||||
if(count == lim_max) begin
|
||||
write_complete = 1;
|
||||
end
|
||||
end else begin
|
||||
wen <= $random;
|
||||
ren <= $random;
|
||||
waddr <= $random;
|
||||
raddr <= $random;
|
||||
end
|
||||
a <= $random;
|
||||
b <= $random;
|
||||
end
|
||||
|
||||
|
||||
always@(negedge clk) begin
|
||||
if(!(q_gfpga[0] === q_bench[0]) && !(q_bench[0] === 1'bx)) begin
|
||||
q_flag[0] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[0] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[1] === q_bench[1]) && !(q_bench[1] === 1'bx)) begin
|
||||
q_flag[1] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[1] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[2] === q_bench[2]) && !(q_bench[2] === 1'bx)) begin
|
||||
q_flag[2] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[2] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[3] === q_bench[3]) && !(q_bench[3] === 1'bx)) begin
|
||||
q_flag[3] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[3] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[4] === q_bench[4]) && !(q_bench[4] === 1'bx)) begin
|
||||
q_flag[4] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[4] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[5] === q_bench[5]) && !(q_bench[5] === 1'bx)) begin
|
||||
q_flag[5] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[5] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[6] === q_bench[6]) && !(q_bench[6] === 1'bx)) begin
|
||||
q_flag[6] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[6] <= 1'b0;
|
||||
end
|
||||
if(!(q_gfpga[7] === q_bench[7]) && !(q_bench[7] === 1'bx)) begin
|
||||
q_flag[7] <= 1'b1;
|
||||
end else begin
|
||||
q_flag[7] <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
always@(posedge q_flag[0]) begin
|
||||
if(q_flag[0]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[0] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[1]) begin
|
||||
if(q_flag[1]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[1] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[2]) begin
|
||||
if(q_flag[2]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[2] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[3]) begin
|
||||
if(q_flag[3]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[3] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[4]) begin
|
||||
if(q_flag[4]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[4] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[5]) begin
|
||||
if(q_flag[5]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[5] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[6]) begin
|
||||
if(q_flag[6]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[6] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
always@(posedge q_flag[7]) begin
|
||||
if(q_flag[7]) begin
|
||||
nb_error = nb_error + 1;
|
||||
$display("Mismatch on q_gfpga[7] at time = %t", $realtime);
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile("pipelined_8bit_adder_formal.vcd");
|
||||
$dumpvars(1, pipelined_8bit_adder_top_formal_verification_random_tb);
|
||||
end
|
||||
|
||||
initial begin
|
||||
$timeformat(-9, 2, "ns", 20);
|
||||
$display("Simulation start");
|
||||
#1500 // Can be changed by the user for his need
|
||||
if(nb_error == 0) begin
|
||||
$display("Simulation Succeed");
|
||||
end else begin
|
||||
$display("Simulation Failed with %d error(s)", nb_error);
|
||||
end
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,71 @@
|
|||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# Script Name : regression.py
|
||||
# Description : This script designed to run:
|
||||
# openfpga_flow tasks
|
||||
# run_{simulator}.py
|
||||
# Args : python3 regression.py --help
|
||||
# Author : Aurelien Alacchi
|
||||
# Email : aurelien.alacchi@utah.edu
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import time
|
||||
from datetime import timedelta
|
||||
import shlex
|
||||
import argparse
|
||||
from configparser import ConfigParser, ExtendedInterpolation
|
||||
import logging
|
||||
import glob
|
||||
import subprocess
|
||||
import threading
|
||||
import csv
|
||||
from string import Template
|
||||
import pprint
|
||||
from importlib import util
|
||||
from collections import OrderedDict
|
||||
|
||||
modelsim="modelsim"
|
||||
vcs="vcs"
|
||||
formality="formality"
|
||||
modelsim_file="simulation_deck_info.ini"
|
||||
ini_list=""
|
||||
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# Parse commandline arguments
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('tasks', nargs='+')
|
||||
parser.add_argument('--maxthreads', type=int, default=2,
|
||||
help="Number of fpga_flow threads to run default = 2," +
|
||||
"Typically <= Number of processors on the system")
|
||||
parser.add_argument('--simulator', type=str, default=modelsim,
|
||||
help="Simulator to use. Set at \"" + modelsim + "\" by default. Can also be \"" + vcs + "\" or \"" + formality + "\"")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
args.tasks=str(args.tasks).strip('[]')
|
||||
#print(args.tasks)
|
||||
#print(args.maxthreads)
|
||||
|
||||
command="python3 openfpga_flow/scripts/run_fpga_task.py " + args.tasks + " --maxthreads " + str(args.maxthreads) + " --debug --show_thread_logs"
|
||||
|
||||
print(command)
|
||||
|
||||
os.system(command)
|
||||
|
||||
if(args.simulator == modelsim):
|
||||
command="python3 openfpga_flow/scripts/run_modelsim.py"
|
||||
os.system("grep \"INFO - Run directory :\" openfpga_flow/tasks/" + args.tasks + "/latest/*.log > paths_ini.txt")
|
||||
arguments = " --skip_prompt --run_sim";
|
||||
fp = open("paths_ini.txt")
|
||||
line = fp.readline()
|
||||
while line:
|
||||
ini_list= ini_list + line + modelsim_file
|
||||
line = fp.readline()
|
||||
ini_list = ini_list.replace("INFO - Run directory :", "")
|
||||
ini_list = ini_list.replace("\n", "/")
|
||||
fp.close()
|
||||
print(command + ini_list + arguments)
|
||||
os.system(command + ini_list + arguments)
|
|
@ -1,66 +0,0 @@
|
|||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# Configuration file for running experiments
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs
|
||||
# Each job execute fpga_flow script on combination of architecture & benchmark
|
||||
# timeout_each_job is timeout for each job
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
|
||||
[GENERAL]
|
||||
power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml
|
||||
power_analysis = true
|
||||
spice_output=false
|
||||
verilog_output=true
|
||||
timeout_each_job = 20*60
|
||||
fpga_flow=vpr_blif
|
||||
|
||||
[ARCHITECTURES]
|
||||
# arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml
|
||||
arch1=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml
|
||||
arch2=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml
|
||||
arch3=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml
|
||||
arch4=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml
|
||||
arch5=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml
|
||||
#arch5=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml
|
||||
#arch4=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k8_N10_sram_chain_FC_template.xml
|
||||
|
||||
[BENCHMARKS]
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.blif
|
||||
|
||||
[SYNTHESIS_PARAM]
|
||||
bench0_top = test_modes
|
||||
bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.act
|
||||
bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.v
|
||||
bench0_chan_width = 300
|
||||
|
||||
#[SCRIPT_PARAM_FIX_ROUTE_CHAN_WIDTH]
|
||||
#fix_route_chan_width=300
|
||||
#vpr_fpga_verilog_include_icarus_simulator=
|
||||
#vpr_fpga_verilog_formal_verification_top_netlist=
|
||||
#vpr_fpga_verilog_include_timing=
|
||||
#vpr_fpga_verilog_include_signal_init=
|
||||
#vpr_fpga_verilog_print_autocheck_top_testbench=
|
||||
#vpr_fpga_bitstream_generator=
|
||||
#vpr_fpga_verilog_print_user_defined_template=
|
||||
#vpr_fpga_verilog_print_report_timing_tcl=
|
||||
#vpr_fpga_verilog_print_sdc_pnr=
|
||||
#vpr_fpga_verilog_print_sdc_analysis=
|
||||
##vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
#end_flow_with_test=
|
||||
|
||||
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]
|
||||
min_route_chan_width=1.3
|
||||
vpr_fpga_verilog_include_icarus_simulator=
|
||||
vpr_fpga_verilog_formal_verification_top_netlist=
|
||||
vpr_fpga_verilog_include_timing=
|
||||
vpr_fpga_verilog_include_signal_init=
|
||||
vpr_fpga_verilog_print_autocheck_top_testbench=
|
||||
vpr_fpga_bitstream_generator=
|
||||
vpr_fpga_verilog_print_user_defined_template=
|
||||
vpr_fpga_verilog_print_report_timing_tcl=
|
||||
vpr_fpga_verilog_print_sdc_pnr=
|
||||
vpr_fpga_verilog_print_sdc_analysis=
|
||||
#vpr_fpga_verilog_explicit_mapping=
|
||||
#vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
end_flow_with_test=
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# Configuration file for running experiments
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs
|
||||
# Each job execute fpga_flow script on combination of architecture & benchmark
|
||||
# timeout_each_job is timeout for each job
|
||||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
|
||||
[GENERAL]
|
||||
power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml
|
||||
power_analysis = true
|
||||
spice_output=false
|
||||
verilog_output=true
|
||||
timeout_each_job = 20*60
|
||||
fpga_flow=vpr_blif
|
||||
|
||||
[ARCHITECTURES]
|
||||
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml
|
||||
|
||||
[BENCHMARKS]
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif
|
||||
|
||||
[SYNTHESIS_PARAM]
|
||||
bench0_top = pipelined_8bit_adder
|
||||
bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act
|
||||
bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v
|
||||
bench0_chan_width = 100
|
||||
|
||||
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]
|
||||
min_route_chan_width=1.3
|
||||
vpr_fpga_verilog_include_icarus_simulator=
|
||||
vpr_fpga_verilog_formal_verification_top_netlist=
|
||||
vpr_fpga_verilog_include_timing=
|
||||
vpr_fpga_verilog_include_signal_init=
|
||||
vpr_fpga_verilog_print_autocheck_top_testbench=
|
||||
vpr_fpga_bitstream_generator=
|
||||
#vpr_fpga_verilog_print_user_defined_template=
|
||||
#vpr_fpga_verilog_print_report_timing_tcl=
|
||||
#vpr_fpga_verilog_print_sdc_pnr=
|
||||
#vpr_fpga_verilog_print_sdc_analysis=
|
||||
vpr_fpga_verilog_explicit_mapping=
|
||||
vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
end_flow_with_test=
|
|
@ -15,15 +15,19 @@ timeout_each_job = 20*60
|
|||
fpga_flow=vpr_blif
|
||||
|
||||
[ARCHITECTURES]
|
||||
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml
|
||||
#arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml
|
||||
#arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k4_N4_sram_chain_FC_behavioral_verilog_template.xml
|
||||
#arch1=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml
|
||||
arch1=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml
|
||||
|
||||
[BENCHMARKS]
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/alu4/alu4.blif
|
||||
# Pass
|
||||
#bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/alu4/alu4.blif
|
||||
# Pass, but port does not match, i_15_ is dangling
|
||||
#bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.blif
|
||||
# Pass
|
||||
#bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/apex4/apex4.blif
|
||||
#bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/bigkey/bigkey.blif
|
||||
bench3=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/bigkey/bigkey.blif
|
||||
# To be tested
|
||||
#bench4=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/clma/clma.blif
|
||||
#bench5=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/des/des.blif
|
||||
#bench6=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/diffeq/diffeq.blif
|
||||
|
@ -34,8 +38,10 @@ bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/alu4/alu4.blif
|
|||
#bench11=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/frisc/frisc.blif
|
||||
#bench12=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/misex3/misex3.blif
|
||||
#bench13=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/pdc/pdc.blif
|
||||
#bench14=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/s298/s298.blif # Pass
|
||||
#bench15=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/s38417/s38417.blif # Multi-mode support fails to repack
|
||||
# Pass
|
||||
#bench14=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/s298/s298.blif
|
||||
# Multi-mode support fails to repack
|
||||
#bench15=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/s38417/s38417.blif
|
||||
#bench16=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/s38584/s38584.blif
|
||||
#bench17=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/seq/seq.blif
|
||||
#bench18=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/mcnc_big20/spla/spla.blif
|
||||
|
@ -147,10 +153,10 @@ vpr_fpga_verilog_include_signal_init=
|
|||
vpr_fpga_verilog_print_autocheck_top_testbench=
|
||||
vpr_fpga_bitstream_generator=
|
||||
vpr_fpga_verilog_print_user_defined_template=
|
||||
vpr_fpga_verilog_print_report_timing_tcl=
|
||||
vpr_fpga_verilog_print_sdc_pnr=
|
||||
vpr_fpga_verilog_print_sdc_analysis=
|
||||
#vpr_fpga_verilog_print_report_timing_tcl=
|
||||
#vpr_fpga_verilog_print_sdc_pnr=
|
||||
#vpr_fpga_verilog_print_sdc_analysis=
|
||||
vpr_fpga_verilog_explicit_mapping=
|
||||
vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
end_flow_with_test=
|
||||
#end_flow_with_test=
|
||||
|
||||
|
|
|
@ -16,15 +16,21 @@ fpga_flow=vpr_blif
|
|||
|
||||
[ARCHITECTURES]
|
||||
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml
|
||||
arch1=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml
|
||||
arch2=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml
|
||||
arch3=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml
|
||||
arch4=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml
|
||||
arch5=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml
|
||||
arch6=${PATH:OPENFPGA_PATH}/openfpga_flow/arch/template/k6_N10_sram_chain_HC_stdcell_mux2_template.xml
|
||||
|
||||
[BENCHMARKS]
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.blif
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.blif
|
||||
|
||||
[SYNTHESIS_PARAM]
|
||||
bench0_top = K4n4_test
|
||||
bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.act
|
||||
bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k4_N4/K4N4_test_modes.v
|
||||
bench0_chan_width = 100
|
||||
bench0_top = test_modes
|
||||
bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.act
|
||||
bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/test_modes/k6_N10/K6N10_test_modes.v
|
||||
bench0_chan_width = 300
|
||||
|
||||
#[SCRIPT_PARAM_FIX_ROUTE_CHAN_WIDTH]
|
||||
#fix_route_chan_width=300
|
||||
|
@ -38,10 +44,10 @@ bench0_chan_width = 100
|
|||
#vpr_fpga_verilog_print_report_timing_tcl=
|
||||
#vpr_fpga_verilog_print_sdc_pnr=
|
||||
#vpr_fpga_verilog_print_sdc_analysis=
|
||||
#vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
##vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
#end_flow_with_test=
|
||||
|
||||
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH_0]
|
||||
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]
|
||||
min_route_chan_width=1.3
|
||||
vpr_fpga_verilog_include_icarus_simulator=
|
||||
vpr_fpga_verilog_formal_verification_top_netlist=
|
||||
|
@ -56,3 +62,4 @@ vpr_fpga_verilog_print_sdc_analysis=
|
|||
#vpr_fpga_verilog_explicit_mapping=
|
||||
#vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
end_flow_with_test=
|
||||
|
||||
|
|
|
@ -55,4 +55,5 @@ vpr_fpga_verilog_print_sdc_pnr=
|
|||
vpr_fpga_verilog_print_sdc_analysis=
|
||||
vpr_fpga_verilog_explicit_mapping=
|
||||
vpr_fpga_x2p_compact_routing_hierarchy=
|
||||
#vpr_fpga_verilog_print_simulation_ini=
|
||||
end_flow_with_test=
|
||||
|
|
|
@ -410,7 +410,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
for (size_t iport = 0; iport < global_ports.size() - 1; ++iport) {
|
||||
for (size_t jport = iport + 1; jport < global_ports.size(); ++jport) {
|
||||
/* Bypass those do not share the same name */
|
||||
if (0 != circuit_lib.port_lib_name(global_ports[iport]).compare(circuit_lib.port_lib_name(global_ports[jport]))) {
|
||||
if (0 != circuit_lib.port_prefix(global_ports[iport]).compare(circuit_lib.port_prefix(global_ports[jport]))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -421,7 +421,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
if (circuit_lib.port_default_value(global_ports[iport]) != circuit_lib.port_default_value(global_ports[jport])) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"Global ports %s from circuit model %s and %s share the same name but have different dfefault values(%lu and %lu)!\n",
|
||||
circuit_lib.port_lib_name(global_ports[iport]).c_str(),
|
||||
circuit_lib.port_prefix(global_ports[iport]).c_str(),
|
||||
circuit_lib.model_name(iport_parent_model).c_str(),
|
||||
circuit_lib.model_name(jport_parent_model).c_str(),
|
||||
circuit_lib.port_default_value(global_ports[iport]),
|
||||
|
@ -433,7 +433,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
if (circuit_lib.port_is_reset(global_ports[iport]) != circuit_lib.port_is_reset(global_ports[jport])) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"Global ports %s from circuit model %s and %s share the same name but have different is_reset attributes!\n",
|
||||
circuit_lib.port_lib_name(global_ports[iport]).c_str(),
|
||||
circuit_lib.port_prefix(global_ports[iport]).c_str(),
|
||||
circuit_lib.model_name(iport_parent_model).c_str(),
|
||||
circuit_lib.model_name(jport_parent_model).c_str()
|
||||
);
|
||||
|
@ -442,7 +442,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
if (circuit_lib.port_is_set(global_ports[iport]) != circuit_lib.port_is_set(global_ports[jport])) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"Global ports %s from circuit model %s and %s share the same name but have different is_set attributes!\n",
|
||||
circuit_lib.port_lib_name(global_ports[iport]).c_str(),
|
||||
circuit_lib.port_prefix(global_ports[iport]).c_str(),
|
||||
circuit_lib.model_name(iport_parent_model).c_str(),
|
||||
circuit_lib.model_name(jport_parent_model).c_str()
|
||||
);
|
||||
|
@ -451,7 +451,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
if (circuit_lib.port_is_config_enable(global_ports[iport]) != circuit_lib.port_is_config_enable(global_ports[jport])) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"Global ports %s from circuit model %s and %s share the same name but have different is_config_enable attributes!\n",
|
||||
circuit_lib.port_lib_name(global_ports[iport]).c_str(),
|
||||
circuit_lib.port_prefix(global_ports[iport]).c_str(),
|
||||
circuit_lib.model_name(iport_parent_model).c_str(),
|
||||
circuit_lib.model_name(jport_parent_model).c_str()
|
||||
);
|
||||
|
@ -460,7 +460,7 @@ size_t check_circuit_library_ports(const CircuitLibrary& circuit_lib) {
|
|||
if (circuit_lib.port_is_prog(global_ports[iport]) != circuit_lib.port_is_prog(global_ports[jport])) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"Global ports %s from circuit model %s and %s share the same name but have different is_prog attributes!\n",
|
||||
circuit_lib.port_lib_name(global_ports[iport]).c_str(),
|
||||
circuit_lib.port_prefix(global_ports[iport]).c_str(),
|
||||
circuit_lib.model_name(iport_parent_model).c_str(),
|
||||
circuit_lib.model_name(jport_parent_model).c_str()
|
||||
);
|
||||
|
@ -579,14 +579,17 @@ void check_circuit_library(const CircuitLibrary& circuit_lib) {
|
|||
num_err += check_required_default_circuit_model(circuit_lib, SPICE_MODEL_WIRE);
|
||||
|
||||
/* If we have any errors, exit */
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Finished checking circuit library with %d errors!\n",
|
||||
num_err);
|
||||
|
||||
if (0 < num_err) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Finished checking circuit library with %d errors!\n",
|
||||
num_err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Checking circuit library passed.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -226,7 +226,7 @@ std::vector<CircuitPortId> find_circuit_library_global_ports(const CircuitLibrar
|
|||
/* Check if a same port with the same name has already been in the list */
|
||||
bool add_to_list = true;
|
||||
for (const auto& global_port : global_ports) {
|
||||
if (0 == circuit_lib.port_lib_name(port).compare(circuit_lib.port_lib_name(global_port))) {
|
||||
if (0 == circuit_lib.port_prefix(port).compare(circuit_lib.port_prefix(global_port))) {
|
||||
/* Same name, skip list update */
|
||||
add_to_list = false;
|
||||
break;
|
||||
|
@ -240,3 +240,26 @@ std::vector<CircuitPortId> find_circuit_library_global_ports(const CircuitLibrar
|
|||
|
||||
return global_ports;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A generic function to find all the unique user-defined
|
||||
* Verilog netlists in a circuit library
|
||||
* Netlists with same names will be considered as one
|
||||
*******************************************************************/
|
||||
std::vector<std::string> find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib) {
|
||||
std::vector<std::string> netlists;
|
||||
|
||||
for (const CircuitModelId& model : circuit_lib.models()) {
|
||||
/* Skip empty netlist names */
|
||||
if (true == circuit_lib.model_verilog_netlist(model).empty()) {
|
||||
continue;
|
||||
}
|
||||
/* See if the netlist name is already in the list */
|
||||
std::vector<std::string>::iterator it = std::find(netlists.begin(), netlists.end(), circuit_lib.model_verilog_netlist(model));
|
||||
if (it == netlists.end()) {
|
||||
netlists.push_back(circuit_lib.model_verilog_netlist(model));
|
||||
}
|
||||
}
|
||||
|
||||
return netlists;
|
||||
}
|
||||
|
|
|
@ -29,4 +29,6 @@ size_t find_circuit_num_config_bits(const CircuitLibrary& circuit_lib,
|
|||
|
||||
std::vector<CircuitPortId> find_circuit_library_global_ports(const CircuitLibrary& circuit_lib);
|
||||
|
||||
std::vector<std::string> find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -65,14 +65,14 @@ if (ENABLE_VPR_GRAPHIC_CXX_FLAG)
|
|||
libarchfpga
|
||||
X11
|
||||
libvtrutil
|
||||
readline
|
||||
libini)
|
||||
libini
|
||||
readline)
|
||||
else ()
|
||||
target_link_libraries(libvpr
|
||||
libarchfpga
|
||||
libvtrutil
|
||||
readline
|
||||
libini)
|
||||
libini
|
||||
readline)
|
||||
endif()
|
||||
|
||||
#Create the executables
|
||||
|
|
|
@ -100,6 +100,8 @@ struct s_TokenPair OptionBaseTokenList[] = {
|
|||
{ "fpga_verilog_report_timing_rpt_path", OT_FPGA_VERILOG_SYN_REPORT_TIMING_RPT_PATH }, /* Specify the simulator path for Verilog netlists */
|
||||
{ "fpga_verilog_print_sdc_pnr", OT_FPGA_VERILOG_SYN_PRINT_SDC_PNR }, /* Specify the simulator path for Verilog netlists */
|
||||
{ "fpga_verilog_print_sdc_analysis", OT_FPGA_VERILOG_SYN_PRINT_SDC_ANALYSIS }, /* Specify the simulator path for Verilog netlists */
|
||||
{ "fpga_verilog_print_simulation_ini", OT_FPGA_VERILOG_SYN_PRINT_SIMULATION_INI }, /* Specify the simulator path for Verilog netlists */
|
||||
{ "fpga_verilog_simulation_ini_file", OT_FPGA_VERILOG_SYN_SIMULATION_INI_FILE }, /* Specify the simulator path for Verilog netlists */
|
||||
/* Xifan Tang: Bitstream generator */
|
||||
{ "fpga_bitstream_generator", OT_FPGA_BITSTREAM_GENERATOR }, /* turn on bitstream generator, and specify the output file */
|
||||
// { "fpga_bitstream_output_file", OT_FPGA_BITSTREAM_OUTPUT_FILE }, /* turn on bitstream generator, and specify the output file */ // AA: temporarily deprecated
|
||||
|
|
|
@ -117,6 +117,8 @@ enum e_OptionBaseToken {
|
|||
OT_FPGA_VERILOG_SYN_REPORT_TIMING_RPT_PATH,
|
||||
OT_FPGA_VERILOG_SYN_PRINT_SDC_PNR,
|
||||
OT_FPGA_VERILOG_SYN_PRINT_SDC_ANALYSIS,
|
||||
OT_FPGA_VERILOG_SYN_PRINT_SIMULATION_INI,
|
||||
OT_FPGA_VERILOG_SYN_SIMULATION_INI_FILE,
|
||||
/* Xifan Tang: Bitstream generator */
|
||||
OT_FPGA_BITSTREAM_GENERATOR,
|
||||
OT_FPGA_BITSTREAM_OUTPUT_FILE,
|
||||
|
|
|
@ -559,6 +559,10 @@ ProcessOption(INP char **Args, INOUTP t_options * Options) {
|
|||
return Args;
|
||||
case OT_FPGA_VERILOG_SYN_PRINT_SDC_ANALYSIS:
|
||||
return Args;
|
||||
case OT_FPGA_VERILOG_SYN_PRINT_SIMULATION_INI:
|
||||
return Args;
|
||||
case OT_FPGA_VERILOG_SYN_SIMULATION_INI_FILE:
|
||||
return ReadString(Args, &Options->fpga_verilog_simulation_ini_path);
|
||||
/* Xifan TANG: Bitstream generator */
|
||||
case OT_FPGA_BITSTREAM_GENERATOR:
|
||||
return Args;
|
||||
|
|
|
@ -107,6 +107,7 @@ struct s_options {
|
|||
char* fpga_verilog_reference_benchmark_file;
|
||||
char* fpga_verilog_modelsim_ini_path;
|
||||
char* fpga_verilog_report_timing_path;
|
||||
char* fpga_verilog_simulation_ini_path;
|
||||
/* Xifan TANG: Bitstream generator */
|
||||
char* fpga_bitstream_file;
|
||||
};
|
||||
|
|
|
@ -1113,6 +1113,8 @@ static void SetupSynVerilogOpts(t_options Options,
|
|||
syn_verilog_opts->print_sdc_pnr = FALSE;
|
||||
syn_verilog_opts->print_sdc_analysis = FALSE;
|
||||
syn_verilog_opts->include_icarus_simulator = FALSE;
|
||||
syn_verilog_opts->print_simulation_ini = FALSE;
|
||||
syn_verilog_opts->simulation_ini_path = NULL;
|
||||
|
||||
/* Turn on Syn_verilog options */
|
||||
if (Options.Count[OT_FPGA_VERILOG_SYN]) {
|
||||
|
@ -1183,6 +1185,14 @@ static void SetupSynVerilogOpts(t_options Options,
|
|||
syn_verilog_opts->print_sdc_analysis = TRUE;
|
||||
}
|
||||
|
||||
if (Options.Count[OT_FPGA_VERILOG_SYN_PRINT_SIMULATION_INI]) {
|
||||
syn_verilog_opts->print_simulation_ini = TRUE;
|
||||
|
||||
if (Options.Count[OT_FPGA_VERILOG_SYN_SIMULATION_INI_FILE]) {
|
||||
syn_verilog_opts->simulation_ini_path = my_strdup(Options.fpga_verilog_simulation_ini_path);
|
||||
}
|
||||
}
|
||||
|
||||
/* SynVerilog needs the input from spice modeling */
|
||||
if (FALSE == arch->read_xml_spice) {
|
||||
arch->read_xml_spice = syn_verilog_opts->dump_syn_verilog;
|
||||
|
|
|
@ -211,6 +211,8 @@ void vpr_print_usage(void) {
|
|||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_report_timing_rpt_path <path_to_generate_reports>\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_print_sdc_pnr\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_print_sdc_analysis\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_print_simulation_ini\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_simulation_ini_file <ini_file_path>\n");
|
||||
/* Xifan Tang: Bitstream generator */
|
||||
vpr_printf(TIO_MESSAGE_INFO, "Bitstream Generator Options:\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_bitstream_generator\n");
|
||||
|
|
|
@ -1281,6 +1281,8 @@ struct s_syn_verilog_opts {
|
|||
boolean print_report_timing_tcl;
|
||||
boolean print_sdc_pnr;
|
||||
boolean print_sdc_analysis;
|
||||
boolean print_simulation_ini;
|
||||
char* simulation_ini_path;
|
||||
};
|
||||
|
||||
/* Xifan TANG: bitstream generator */
|
||||
|
|
|
@ -0,0 +1,624 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to write SDC commands
|
||||
* to disable unused ports of grids, such as Configurable Logic Block
|
||||
* (CLBs), heterogeneous blocks, etc.
|
||||
*******************************************************************/
|
||||
#include "vtr_assert.h"
|
||||
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_pbtypes_utils.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
#include "analysis_sdc_grid_writer.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
/********************************************************************
|
||||
* Recursively visit all the pb_types in the hierarchy
|
||||
* and disable all the ports
|
||||
*
|
||||
* Note: it is a must to disable all the ports in all the child pb_types!
|
||||
* This can prohibit timing analyzer to consider any FF-to-FF path or
|
||||
* combinatinal path inside an unused grid, when finding critical paths!!!
|
||||
*******************************************************************/
|
||||
static
|
||||
void rec_print_analysis_sdc_disable_unused_pb_graph_nodes(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
const e_side& border_side) {
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Disable all the ports of current module (parent_module)!
|
||||
* Hierarchy name already includes the instance name of parent_module
|
||||
*/
|
||||
fp << "set_disable_timing ";
|
||||
fp << hierarchy_name;
|
||||
fp << "/*";
|
||||
fp << std::endl;
|
||||
|
||||
/* Return if this is the primitive pb_type */
|
||||
if (TRUE == is_primitive_pb_type(physical_pb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go recursively */
|
||||
int physical_mode_index = find_pb_type_physical_mode_index(*physical_pb_type);
|
||||
|
||||
/* Disable all the ports by iterating over its instance in the parent module */
|
||||
for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string child_module_name = generate_physical_block_module_name(child_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild]));
|
||||
|
||||
ModuleId child_module = module_manager.find_module(child_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(child_module));
|
||||
|
||||
/* Each child may exist multiple times in the hierarchy*/
|
||||
for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) {
|
||||
std::string child_instance_name = module_manager.instance_name(parent_module, child_module, module_manager.child_module_instances(parent_module, child_module)[inst]);
|
||||
/* Must have a valid instance name!!! */
|
||||
VTR_ASSERT(false == child_instance_name.empty());
|
||||
|
||||
std::string updated_hierarchy_name = hierarchy_name + std::string("/") + child_instance_name + std::string("/");
|
||||
|
||||
rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, module_manager, child_module, hierarchy_name,
|
||||
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]),
|
||||
border_side);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable an unused pin of a pb_graph_node (parent_module)
|
||||
*******************************************************************/
|
||||
static
|
||||
void disable_pb_graph_node_unused_pin(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
const t_pb_graph_pin& pb_graph_pin,
|
||||
t_phy_pb* block_physical_pb) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
int rr_node_index = pb_graph_pin.rr_node_index_physical_pb;
|
||||
|
||||
/* Identify if the net has been used or not */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(&(block_physical_pb->rr_graph->rr_node[rr_node_index]))) {
|
||||
/* Used pin; Nothing to do */
|
||||
return;
|
||||
}
|
||||
/* Reach here, it means that this pin is not used. Disable timing analysis for the pin */
|
||||
/* Find the module port by name */
|
||||
std::string module_port_name = generate_pb_type_port_name(pb_graph_pin.port);
|
||||
ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port));
|
||||
BasicPort port_to_disable = module_manager.module_port(parent_module, module_port);
|
||||
port_to_disable.set_width(pb_graph_pin.pin_number, pb_graph_pin.pin_number);
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << hierarchy_name;
|
||||
fp << "/";
|
||||
fp << generate_sdc_port(port_to_disable);
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable unused input ports and output ports of this pb_graph_node (parent_module)
|
||||
* This function will iterate over all the input pins, output pins
|
||||
* of the physical_pb_graph_node, and check if they are mapped
|
||||
* For unused pins, we will find the port in parent_module
|
||||
* and then print SDC commands to disable them
|
||||
*******************************************************************/
|
||||
static
|
||||
void disable_pb_graph_node_unused_pins(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
t_phy_pb* block_physical_pb) {
|
||||
|
||||
/* Disable unused input pins */
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) {
|
||||
disable_pb_graph_node_unused_pin(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
physical_pb_graph_node->input_pins[iport][ipin],
|
||||
block_physical_pb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable unused output pins */
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_output_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) {
|
||||
disable_pb_graph_node_unused_pin(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
physical_pb_graph_node->output_pins[iport][ipin],
|
||||
block_physical_pb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable unused clock pins */
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_clock_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) {
|
||||
disable_pb_graph_node_unused_pin(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
physical_pb_graph_node->clock_pins[iport][ipin],
|
||||
block_physical_pb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable unused inputs of routing multiplexers of this pb_graph_node
|
||||
* This function will first cache the nets for each input and output pins
|
||||
* and store the results in a mux_name-to-net mapping
|
||||
*******************************************************************/
|
||||
static
|
||||
void disable_pb_graph_node_unused_mux_inputs(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
t_phy_pb* block_physical_pb,
|
||||
const e_side& border_side) {
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
int physical_mode_index = find_pb_type_physical_mode_index(*physical_pb_type);
|
||||
|
||||
std::map<std::string, int> mux_instance_to_net_map;
|
||||
|
||||
/* Cache the nets for each input pins of each child pb_graph_node */
|
||||
for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) {
|
||||
for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) {
|
||||
|
||||
t_pb_graph_node* child_pb_graph_node = &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]);
|
||||
|
||||
/* Cache the nets for input pins of the child pb_graph_node */
|
||||
for (int iport = 0; iport < child_pb_graph_node->num_input_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < child_pb_graph_node->num_input_pins[iport]; ++ipin) {
|
||||
int rr_node_index = child_pb_graph_node->input_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
/* Generate the mux name */
|
||||
std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(child_pb_graph_node->input_pins[iport][ipin]), std::string(""));
|
||||
/* Cache the net */
|
||||
mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache the nets for clock pins of the child pb_graph_node */
|
||||
for (int iport = 0; iport < child_pb_graph_node->num_clock_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < child_pb_graph_node->num_clock_pins[iport]; ++ipin) {
|
||||
int rr_node_index = child_pb_graph_node->clock_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
/* Generate the mux name */
|
||||
std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(child_pb_graph_node->clock_pins[iport][ipin]), std::string(""));
|
||||
/* Cache the net */
|
||||
mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache the nets for each output pins of this pb_graph_node */
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_output_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) {
|
||||
int rr_node_index = physical_pb_graph_node->output_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
/* Generate the mux name */
|
||||
std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(physical_pb_graph_node->output_pins[iport][ipin]), std::string(""));
|
||||
/* Cache the net */
|
||||
mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now disable unused inputs of routing multiplexers, by tracing from input pins of the parent_module */
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) {
|
||||
/* Find the module port by name */
|
||||
std::string module_port_name = generate_pb_type_port_name(physical_pb_graph_node->input_pins[iport][ipin].port);
|
||||
ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port));
|
||||
|
||||
int rr_node_index = physical_pb_graph_node->input_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
t_rr_node* input_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]);
|
||||
|
||||
disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
module_port, ipin,
|
||||
input_rr_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iport = 0; iport < physical_pb_graph_node->num_clock_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) {
|
||||
/* Find the module port by name */
|
||||
std::string module_port_name = generate_pb_type_port_name(physical_pb_graph_node->clock_pins[iport][ipin].port);
|
||||
ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port));
|
||||
|
||||
int rr_node_index = physical_pb_graph_node->clock_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
t_rr_node* input_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]);
|
||||
|
||||
disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
module_port, ipin,
|
||||
input_rr_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now disable unused inputs of routing multiplexers, by tracing from output pins of the child_module */
|
||||
for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string child_module_name = generate_physical_block_module_name(child_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild]));
|
||||
|
||||
ModuleId child_module = module_manager.find_module(child_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(child_module));
|
||||
|
||||
for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) {
|
||||
|
||||
t_pb_graph_node* child_pb_graph_node = &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]);
|
||||
|
||||
for (int iport = 0; iport < child_pb_graph_node->num_output_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < child_pb_graph_node->num_output_pins[iport]; ++ipin) {
|
||||
/* Find the module port by name */
|
||||
std::string module_port_name = generate_pb_type_port_name(child_pb_graph_node->output_pins[iport][ipin].port);
|
||||
ModulePortId module_port = module_manager.find_module_port(child_module, module_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(child_module, module_port));
|
||||
|
||||
int rr_node_index = child_pb_graph_node->output_pins[iport][ipin].rr_node_index_physical_pb;
|
||||
t_rr_node* output_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]);
|
||||
|
||||
/* Corner case: if the rr node has no fan-out we will skip this pin */
|
||||
if (0 == output_rr_node->num_edges) {
|
||||
continue;
|
||||
}
|
||||
|
||||
disable_analysis_module_output_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
child_module, inst,
|
||||
module_port, ipin,
|
||||
output_rr_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Recursively visit all the pb_types in the hierarchy
|
||||
* and disable all the unused resources, including:
|
||||
* 1. input ports
|
||||
* 2. output ports
|
||||
* 3. unused inputs of routing multiplexers
|
||||
*
|
||||
* As this function is executed in a recursive way.
|
||||
* To avoid repeated disable timing for ports, during each run of this function,
|
||||
* only the unused input ports, output ports of the parent module will be disabled.
|
||||
* In addition, we will cache all the net ids mapped to the input ports of
|
||||
* child modules, and the net ids mapped to the output ports of parent module.
|
||||
* As such, we can trace from
|
||||
* 1. the input ports of parent module to disable unused inputs of routing multiplexer
|
||||
* which drives the inputs of child modules
|
||||
*
|
||||
* Parent_module
|
||||
* +---------------------------------------------
|
||||
* | MUX child_module
|
||||
* | +-------------+ +--------
|
||||
* input_pin0(netA) --->|-------->| Routing |------>|
|
||||
* input_pin1(netB) --->|----x--->| Multiplexer | netA |
|
||||
* | +-------------+ |
|
||||
* | |
|
||||
*
|
||||
* 2. the output ports of child module to disable unused inputs of routing multiplexer
|
||||
* which drives the outputs of parent modules
|
||||
*
|
||||
* Case 1:
|
||||
* parent_module
|
||||
* --------------------------------------+
|
||||
* child_module |
|
||||
* -------------+ |
|
||||
* | +-------------+ |
|
||||
* output_pin0 (netA) |--->| Routing |----->|---->
|
||||
* output_pin1 (netB) |-x->| Multiplexer | netA |
|
||||
* | +-------------+ |
|
||||
*
|
||||
* Case 2:
|
||||
*
|
||||
* Parent_module
|
||||
* +---------------------------------------------
|
||||
* |
|
||||
* | +--------------------------------------------+
|
||||
* | | MUX child_module |
|
||||
* | | +-------------+ +-----------+ |
|
||||
* | +--->| Routing |------>| | |
|
||||
* input_pin0(netA) --->|----x--->| Multiplexer | netA | output_pin|-----+
|
||||
* | +-------------+ | | netA
|
||||
* | | |
|
||||
*
|
||||
*
|
||||
* Note: it is a must to disable all the ports in all the child pb_types!
|
||||
* This can prohibit timing analyzer to consider any FF-to-FF path or
|
||||
* combinatinal path inside an unused grid, when finding critical paths!!!
|
||||
*******************************************************************/
|
||||
static
|
||||
void rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
t_phy_pb* block_physical_pb,
|
||||
const e_side& border_side) {
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
/* Disable unused input ports and output ports of this pb_graph_node (parent_module) */
|
||||
disable_pb_graph_node_unused_pins(fp, module_manager, parent_module,
|
||||
hierarchy_name, physical_pb_graph_node, block_physical_pb);
|
||||
|
||||
/* Return if this is the primitive pb_type
|
||||
* Note: this must return before we disable any unused inputs of routing multiplexer!
|
||||
* This is due to that primitive pb_type does NOT contain any routing multiplexers inside!!!
|
||||
*/
|
||||
if (TRUE == is_primitive_pb_type(physical_pb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable unused inputs of routing multiplexers of this pb_graph_node */
|
||||
disable_pb_graph_node_unused_mux_inputs(fp, module_manager, parent_module,
|
||||
hierarchy_name, physical_pb_graph_node, block_physical_pb,
|
||||
border_side);
|
||||
|
||||
|
||||
int physical_mode_index = find_pb_type_physical_mode_index(*physical_pb_type);
|
||||
|
||||
/* Disable all the ports by iterating over its instance in the parent module */
|
||||
for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string child_module_name = generate_physical_block_module_name(child_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild]));
|
||||
|
||||
ModuleId child_module = module_manager.find_module(child_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(child_module));
|
||||
|
||||
/* Each child may exist multiple times in the hierarchy*/
|
||||
for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) {
|
||||
std::string child_instance_name = module_manager.instance_name(parent_module, child_module, module_manager.child_module_instances(parent_module, child_module)[inst]);
|
||||
/* Must have a valid instance name!!! */
|
||||
VTR_ASSERT(false == child_instance_name.empty());
|
||||
|
||||
std::string updated_hierarchy_name = hierarchy_name + std::string("/") + child_instance_name + std::string("/");
|
||||
|
||||
rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, module_manager, child_module, hierarchy_name,
|
||||
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]),
|
||||
block_physical_pb, border_side);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function can work in two differnt modes:
|
||||
* 1. For partially unused pb blocks
|
||||
* ---------------------------------
|
||||
* Disable the timing for only unused resources in a physical block
|
||||
* We have to walk through pb_graph node, port by port and pin by pin.
|
||||
* Identify which pins have not been used, and then disable the timing
|
||||
* for these ports.
|
||||
* Plus, for input ports, we will trace the routing multiplexers
|
||||
* and disable the timing for unused inputs.
|
||||
*
|
||||
* 2. For fully unused pb_blocks
|
||||
* -----------------------------
|
||||
* Disable the timing for a fully unused grid!
|
||||
* This is very straightforward!
|
||||
* Just walk through each pb_type and disable all the ports using wildcards
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_pb_block_unused_resources(std::fstream& fp,
|
||||
t_type_ptr grid_type,
|
||||
const vtr::Point<size_t>& grid_coordinate,
|
||||
const ModuleManager& module_manager,
|
||||
const std::string& grid_instance_name,
|
||||
const size_t& grid_z,
|
||||
const e_side& border_side,
|
||||
t_phy_pb* block_physical_pb,
|
||||
const bool& unused_block) {
|
||||
/* Check code: if this is an IO block, the border side MUST be valid */
|
||||
if (IO_TYPE == grid_type) {
|
||||
VTR_ASSERT(NUM_SIDES != border_side);
|
||||
}
|
||||
|
||||
/* If the block is partially unused, we should have a physical pb */
|
||||
if (false == unused_block) {
|
||||
VTR_ASSERT(NULL != block_physical_pb);
|
||||
}
|
||||
|
||||
/* Find an unique name to the pb instance in this grid
|
||||
* Note: this must be consistent with the instance name we used in build_grid_module()!!!
|
||||
*/
|
||||
/* TODO: validate that the instance name is used in module manager!!! */
|
||||
std::string pb_module_name_prefix(GRID_MODULE_NAME_PREFIX);
|
||||
std::string pb_module_name = generate_grid_physical_block_module_name(pb_module_name_prefix, grid_type->pb_graph_head->pb_type, border_side);
|
||||
std::string pb_instance_name = generate_grid_physical_block_instance_name(pb_module_name_prefix, grid_type->pb_graph_head->pb_type, border_side, grid_z);
|
||||
|
||||
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
if (true == unused_block) {
|
||||
fp << "# Disable Timing for unused grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "][" << grid_z << "]" << std::endl;
|
||||
} else {
|
||||
VTR_ASSERT_SAFE(false == unused_block);
|
||||
fp << "# Disable Timing for unused resources in grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "][" << grid_z << "]" << std::endl;
|
||||
}
|
||||
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
std::string hierarchy_name = grid_instance_name + std::string("/") + pb_instance_name + std::string("/");
|
||||
|
||||
/* Go recursively through the pb_graph hierarchy, and disable all the ports level by level */
|
||||
if (true == unused_block) {
|
||||
rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, module_manager, pb_module, hierarchy_name, grid_type->pb_graph_head, border_side);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE(false == unused_block);
|
||||
rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, module_manager, pb_module, hierarchy_name, grid_type->pb_graph_head, block_physical_pb, border_side);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable the timing for a fully unused grid!
|
||||
* This is very straightforward!
|
||||
* Just walk through each pb_type and disable all the ports using wildcards
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_unused_grid(std::fstream& fp,
|
||||
const vtr::Point<size_t>& grid_coordinate,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const e_side& border_side) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
t_type_ptr grid_type = L_grids[grid_coordinate.x()][grid_coordinate.y()].type;
|
||||
/* Bypass conditions for grids :
|
||||
* 1. EMPTY type, which is by nature unused
|
||||
* 2. Offset > 0, which has already been processed when offset = 0
|
||||
*/
|
||||
if ( (NULL == grid_type)
|
||||
|| (EMPTY_TYPE == grid_type)
|
||||
|| (0 < L_grids[grid_coordinate.x()][grid_coordinate.y()].offset) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find an unique name to the grid instane
|
||||
* Note: this must be consistent with the instance name we used in build_top_module()!!!
|
||||
*/
|
||||
/* TODO: validate that the instance name is used in module manager!!! */
|
||||
std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX);
|
||||
std::string grid_module_name = generate_grid_block_module_name(grid_module_name_prefix, std::string(grid_type->name), IO_TYPE == grid_type, border_side);
|
||||
std::string grid_instance_name = generate_grid_block_instance_name(grid_module_name_prefix, std::string(grid_type->name), IO_TYPE == grid_type, border_side, grid_coordinate);
|
||||
|
||||
ModuleId grid_module = module_manager.find_module(grid_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
|
||||
|
||||
/* Now we need to find the usage of this grid */
|
||||
std::vector<bool> grid_usage(grid_type->capacity, false);
|
||||
|
||||
/* Print comments */
|
||||
fp << "#######################################" << std::endl;
|
||||
fp << "# Disable Timing for grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "]" << std::endl;
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
/* For used grid, find the unused rr_node in the local rr_graph
|
||||
* and then disable each port which is not used
|
||||
* as well as the unused inputs of routing multiplexers!
|
||||
*/
|
||||
for (int iblk = 0; iblk < L_grids[grid_coordinate.x()][grid_coordinate.y()].usage; ++iblk) {
|
||||
int blk_id = L_grids[grid_coordinate.x()][grid_coordinate.y()].blocks[iblk];
|
||||
VTR_ASSERT( (OPEN < L_blocks[blk_id].z) && (L_blocks[blk_id].z < grid_type->capacity) );
|
||||
/* Mark the grid_usage */
|
||||
grid_usage[L_blocks[blk_id].z] = true;
|
||||
/* TODO:
|
||||
verilog_generate_sdc_disable_one_unused_block(fp, &(L_blocks[blk_id]));
|
||||
*/
|
||||
t_phy_pb* block_phy_pb = (t_phy_pb*) L_blocks[blk_id].phy_pb;
|
||||
print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate, module_manager, grid_instance_name, iblk, border_side, block_phy_pb, false);
|
||||
}
|
||||
|
||||
/* For unused grid, disable all the pins in the physical_pb_type */
|
||||
for (int iblk = 0; iblk < grid_type->capacity; ++iblk) {
|
||||
/* Bypass used blocks */
|
||||
if (true == grid_usage[iblk]) {
|
||||
continue;
|
||||
}
|
||||
print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate, module_manager, grid_instance_name, iblk, border_side, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function writes SDC commands to disable unused ports
|
||||
* of grids, such as Configurable Logic Block (CLBs), heterogeneous blocks, etc.
|
||||
*
|
||||
* This function will iterate over all the grids available in the FPGA fabric
|
||||
* It will disable the timing analysis for
|
||||
* 1. Grids, which are totally not used (no logic has been mapped to)
|
||||
* 2. Unused part of grids, including the ports, inputs of routing multiplexers
|
||||
*
|
||||
* Note that it is a must to disable the unused inputs of routing multiplexers
|
||||
* because it will cause unexpected paths in timing analysis
|
||||
* For example:
|
||||
* +---------------------+
|
||||
* inputA (net0) ------->| |
|
||||
* | Routing multiplexer |----> output (net0)
|
||||
* inputB (net1) ------->| |
|
||||
* +---------------------+
|
||||
*
|
||||
* During timing analysis, the path from inputA to output should be considered
|
||||
* while the path from inputB to output should NOT be considered!!!
|
||||
*
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_grids(std::fstream& fp,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager) {
|
||||
|
||||
/* Process unused core grids */
|
||||
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
|
||||
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
|
||||
/* We should not meet any I/O grid */
|
||||
VTR_ASSERT(IO_TYPE != L_grids[ix][iy].type);
|
||||
|
||||
print_analysis_sdc_disable_unused_grid(fp, vtr::Point<size_t>(ix, iy),
|
||||
L_grids, L_blocks, module_manager, NUM_SIDES);
|
||||
}
|
||||
}
|
||||
|
||||
/* Instanciate I/O grids */
|
||||
/* Create the coordinate range for each side of FPGA fabric */
|
||||
std::vector<e_side> io_sides{TOP, RIGHT, BOTTOM, LEFT};
|
||||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coordinates;
|
||||
|
||||
/* TOP side*/
|
||||
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
|
||||
io_coordinates[TOP].push_back(vtr::Point<size_t>(ix, device_size.y() - 1));
|
||||
}
|
||||
|
||||
/* RIGHT side */
|
||||
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
|
||||
io_coordinates[RIGHT].push_back(vtr::Point<size_t>(device_size.x() - 1, iy));
|
||||
}
|
||||
|
||||
/* BOTTOM side*/
|
||||
for (size_t ix = 1; ix < device_size.x() - 1; ++ix) {
|
||||
io_coordinates[BOTTOM].push_back(vtr::Point<size_t>(ix, 0));
|
||||
}
|
||||
|
||||
/* LEFT side */
|
||||
for (size_t iy = 1; iy < device_size.y() - 1; ++iy) {
|
||||
io_coordinates[LEFT].push_back(vtr::Point<size_t>(0, iy));
|
||||
}
|
||||
|
||||
/* Add instances of I/O grids to top_module */
|
||||
for (const e_side& io_side : io_sides) {
|
||||
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
|
||||
/* We should not meet any I/O grid */
|
||||
VTR_ASSERT(IO_TYPE == L_grids[io_coordinate.x()][io_coordinate.y()].type);
|
||||
|
||||
print_analysis_sdc_disable_unused_grid(fp, io_coordinate,
|
||||
L_grids, L_blocks, module_manager, io_side);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef ANALYSIS_SDC_GRID_WRITER_H
|
||||
#define ANALYSIS_SDC_GRID_WRITER_H
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "vtr_geometry.h"
|
||||
#include "vpr_types.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
void print_analysis_sdc_disable_unused_grids(std::fstream& fp,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,549 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to output a SDC file
|
||||
* that constrain routing modules of a FPGA fabric (P&Red netlist)
|
||||
* using a benchmark
|
||||
*******************************************************************/
|
||||
#include <map>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "device_port.h"
|
||||
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_types.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
#include "analysis_sdc_routing_writer.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
/********************************************************************
|
||||
* This function will disable
|
||||
* 1. all the unused port (unmapped by a benchmark) of a connection block
|
||||
* 2. all the unused inputs (unmapped by a benchmark) of routing multiplexers
|
||||
* in a connection block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_cb_unused_resources(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
|
||||
std::string cb_instance_name = generate_connection_block_module_name(cb_type, gsb_coordinate);
|
||||
|
||||
/* If we use the compact routing hierarchy, we need to find the module name !*/
|
||||
vtr::Point<size_t> cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
if (true == compact_routing_hierarchy) {
|
||||
DeviceCoordinator cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type));
|
||||
cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type));
|
||||
}
|
||||
|
||||
std::string cb_module_name = generate_connection_block_module_name(cb_type, cb_coordinate);
|
||||
|
||||
ModuleId cb_module = module_manager.find_module(cb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for Connection block " << cb_module_name << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Disable all the input port (routing tracks), which are not used by benchmark */
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
t_rr_node* chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
port_coord.set_x(unique_mirror.get_cb_x(cb_type));
|
||||
port_coord.set_y(unique_mirror.get_cb_y(cb_type));
|
||||
}
|
||||
std::string port_name = generate_routing_track_port_name(cb_type,
|
||||
port_coord, itrack,
|
||||
IN_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Disable all the output port (routing tracks), which are not used by benchmark */
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
t_rr_node* chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
port_coord.set_x(unique_mirror.get_cb_x(cb_type));
|
||||
port_coord.set_y(unique_mirror.get_cb_y(cb_type));
|
||||
}
|
||||
std::string port_name = generate_routing_track_port_name(cb_type,
|
||||
port_coord, itrack,
|
||||
OUT_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Build a map between mux_instance name and net_num */
|
||||
std::map<std::string, int> mux_instance_to_net_map;
|
||||
|
||||
/* Disable all the output port (grid input pins), which are not used by benchmark */
|
||||
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
|
||||
|
||||
for (size_t side = 0; side < cb_sides.size(); ++side) {
|
||||
enum e_side cb_ipin_side = cb_sides[side];
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) {
|
||||
t_rr_node* ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode);
|
||||
|
||||
/* Find the MUX instance that drives the IPIN! */
|
||||
std::string mux_instance_name = generate_cb_mux_instance_name(CONNECTION_BLOCK_MUX_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, inode), inode, std::string(""));
|
||||
mux_instance_to_net_map[mux_instance_name] = ipin_node->vpack_net_num;
|
||||
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(ipin_node)) {
|
||||
continue;
|
||||
}
|
||||
if (0 == ipin_node->fan_in) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vtr::Point<size_t> port_coord(ipin_node->xlow, ipin_node->ylow);
|
||||
std::string port_name = generate_grid_side_port_name(grids,
|
||||
port_coord,
|
||||
rr_gsb.get_ipin_node_grid_side(cb_ipin_side, inode),
|
||||
ipin_node->ptc_num);
|
||||
|
||||
/* Find the port in unique mirror! */
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
t_rr_node* unique_mirror_ipin_node = unique_mirror.get_ipin_node(cb_ipin_side, inode);
|
||||
port_coord.set_x(unique_mirror_ipin_node->xlow);
|
||||
port_coord.set_y(unique_mirror_ipin_node->ylow);
|
||||
port_name = generate_grid_side_port_name(grids,
|
||||
port_coord,
|
||||
unique_mirror.get_ipin_node_grid_side(cb_ipin_side, inode),
|
||||
unique_mirror_ipin_node->ptc_num);
|
||||
}
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all the unused inputs of routing multiplexers, which are not used by benchmark
|
||||
* Here, we start from each input of the Connection Blocks, and traverse forward to the sink
|
||||
* port of the module net whose source is the input
|
||||
* We will find the instance name which is the parent of the sink port, and search the
|
||||
* net id through the instance_name_to_net_map
|
||||
* The the net id does not match the net id of this input, we will disable the sink port!
|
||||
*
|
||||
* cb_module
|
||||
* +-----------------------
|
||||
* | MUX instance A
|
||||
* | +-----------
|
||||
* input_port--->|--+---x-->| sink port (disable!)
|
||||
* | | +----------
|
||||
* | | MUX instance B
|
||||
* | | +----------
|
||||
* | +------>| sink port (do not disable!)
|
||||
*/
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
t_rr_node* chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
port_coord.set_x(unique_mirror.get_cb_x(cb_type));
|
||||
port_coord.set_y(unique_mirror.get_cb_y(cb_type));
|
||||
}
|
||||
std::string port_name = generate_routing_track_port_name(cb_type,
|
||||
port_coord, itrack,
|
||||
OUT_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp,
|
||||
module_manager, cb_module,
|
||||
cb_instance_name,
|
||||
module_port,
|
||||
chan_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_unused_cb_ports(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Build unique X-direction connection block modules */
|
||||
DeviceCoordinator cb_range = L_device_rr_gsb.get_gsb_range();
|
||||
|
||||
for (size_t ix = 0; ix < cb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < cb_range.get_y(); ++iy) {
|
||||
/* Check if the connection block exists in the device!
|
||||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
print_analysis_sdc_disable_cb_unused_resources(fp, grids,
|
||||
module_manager,
|
||||
L_device_rr_gsb,
|
||||
rr_gsb,
|
||||
cb_type,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_cbs(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
|
||||
print_analysis_sdc_disable_unused_cb_ports(fp, grids, module_manager,
|
||||
L_device_rr_gsb,
|
||||
CHANX, compact_routing_hierarchy);
|
||||
|
||||
print_analysis_sdc_disable_unused_cb_ports(fp, grids, module_manager,
|
||||
L_device_rr_gsb,
|
||||
CHANY, compact_routing_hierarchy);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function will disable
|
||||
* 1. all the unused port (unmapped by a benchmark) of a switch block
|
||||
* 2. all the unused inputs (unmapped by a benchmark) of routing multiplexers
|
||||
* in a switch block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_sb_unused_resources(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
|
||||
std::string sb_instance_name = generate_switch_block_module_name(gsb_coordinate);
|
||||
|
||||
/* If we use the compact routing hierarchy, we need to find the module name !*/
|
||||
vtr::Point<size_t> sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
if (true == compact_routing_hierarchy) {
|
||||
DeviceCoordinator sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
sb_coordinate.set_x(unique_mirror.get_sb_x());
|
||||
sb_coordinate.set_y(unique_mirror.get_sb_y());
|
||||
}
|
||||
|
||||
std::string sb_module_name = generate_switch_block_module_name(sb_coordinate);
|
||||
|
||||
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for Switch block " << sb_module_name << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Build a map between mux_instance name and net_num */
|
||||
std::map<std::string, int> mux_instance_to_net_map;
|
||||
|
||||
/* Disable all the input/output port (routing tracks), which are not used by benchmark */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
DeviceCoordinator port_coordinate = rr_gsb.get_side_block_coordinator(side_manager.get_side());
|
||||
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
t_rr_node* chan_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||
|
||||
vtr::Point<size_t> port_coord(port_coordinate.get_x(), port_coordinate.get_y());
|
||||
std::string port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(side_manager.get_side(), itrack)->type,
|
||||
port_coord, itrack,
|
||||
rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
DeviceCoordinator unique_port_coordinate = unique_mirror.get_side_block_coordinator(side_manager.get_side());
|
||||
port_coord.set_x(unique_port_coordinate.get_x());
|
||||
port_coord.set_y(unique_port_coordinate.get_y());
|
||||
port_name = generate_routing_track_port_name(unique_mirror.get_chan_node(side_manager.get_side(), itrack)->type,
|
||||
port_coord, itrack,
|
||||
unique_mirror.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
}
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
/* Cache the net name for routing tracks which are outputs of the switch block */
|
||||
if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
/* Generate the name of mux instance related to this output node */
|
||||
std::string mux_instance_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MUX_INSTANCE_PREFIX, side_manager.get_side(), itrack, std::string(""));
|
||||
mux_instance_to_net_map[mux_instance_name] = chan_node->vpack_net_num;
|
||||
}
|
||||
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << sb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(sb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all the input port (grid output pins), which are not used by benchmark */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) {
|
||||
t_rr_node* opin_node = rr_gsb.get_opin_node(side_manager.get_side(), inode);
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_opin_node(side_manager.get_side(), inode)->xlow,
|
||||
rr_gsb.get_opin_node(side_manager.get_side(), inode)->ylow);
|
||||
|
||||
std::string port_name = generate_grid_side_port_name(grids, port_coord,
|
||||
rr_gsb.get_opin_node_grid_side(side_manager.get_side(), inode),
|
||||
opin_node->ptc_num);
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
port_coord.set_x(unique_mirror.get_opin_node(side_manager.get_side(), inode)->xlow);
|
||||
port_coord.set_y(unique_mirror.get_opin_node(side_manager.get_side(), inode)->ylow);
|
||||
|
||||
port_name = generate_grid_side_port_name(grids, port_coord,
|
||||
unique_mirror.get_opin_node_grid_side(side_manager.get_side(), inode),
|
||||
unique_mirror.get_opin_node(side_manager.get_side(), inode)->ptc_num);
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(opin_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << sb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(sb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all the unused inputs of routing multiplexers, which are not used by benchmark
|
||||
* Here, we start from each input of the Switch Blocks, and traverse forward to the sink
|
||||
* port of the module net whose source is the input
|
||||
* We will find the instance name which is the parent of the sink port, and search the
|
||||
* net id through the instance_name_to_net_map
|
||||
* The the net id does not match the net id of this input, we will disable the sink port!
|
||||
*
|
||||
* sb_module
|
||||
* +-----------------------
|
||||
* | MUX instance A
|
||||
* | +-----------
|
||||
* input_port--->|--+---x-->| sink port (disable! net_id = Y)
|
||||
* (net_id = X) | | +----------
|
||||
* | | MUX instance B
|
||||
* | | +----------
|
||||
* | +------>| sink port (do not disable! net_id = X)
|
||||
*
|
||||
* Because the input ports of a SB module come from
|
||||
* 1. Grid output pins
|
||||
* 2. routing tracks
|
||||
* We will walk through these ports and do conditionally disable_timing
|
||||
*/
|
||||
|
||||
/* Iterate over input ports coming from grid output pins */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) {
|
||||
t_rr_node* opin_node = rr_gsb.get_opin_node(side_manager.get_side(), inode);
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_opin_node(side_manager.get_side(), inode)->xlow,
|
||||
rr_gsb.get_opin_node(side_manager.get_side(), inode)->ylow);
|
||||
|
||||
std::string port_name = generate_grid_side_port_name(grids, port_coord,
|
||||
rr_gsb.get_opin_node_grid_side(side_manager.get_side(), inode),
|
||||
opin_node->ptc_num);
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
port_coord.set_x(unique_mirror.get_opin_node(side_manager.get_side(), inode)->xlow);
|
||||
port_coord.set_y(unique_mirror.get_opin_node(side_manager.get_side(), inode)->ylow);
|
||||
|
||||
port_name = generate_grid_side_port_name(grids, port_coord,
|
||||
unique_mirror.get_opin_node_grid_side(side_manager.get_side(), inode),
|
||||
unique_mirror.get_opin_node(side_manager.get_side(), inode)->ptc_num);
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp, module_manager,
|
||||
sb_module,
|
||||
sb_instance_name,
|
||||
module_port,
|
||||
opin_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over input ports coming from routing tracks */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
DeviceCoordinator port_coordinate = rr_gsb.get_side_block_coordinator(side_manager.get_side());
|
||||
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
/* Skip output ports, they have already been disabled or not */
|
||||
if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t_rr_node* chan_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||
|
||||
vtr::Point<size_t> port_coord(port_coordinate.get_x(), port_coordinate.get_y());
|
||||
std::string port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(side_manager.get_side(), itrack)->type,
|
||||
port_coord, itrack,
|
||||
rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
DeviceCoordinator sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
DeviceCoordinator unique_port_coordinate = unique_mirror.get_side_block_coordinator(side_manager.get_side());
|
||||
port_coord.set_x(unique_port_coordinate.get_x());
|
||||
port_coord.set_y(unique_port_coordinate.get_y());
|
||||
|
||||
port_name = generate_routing_track_port_name(unique_mirror.get_chan_node(side_manager.get_side(), itrack)->type,
|
||||
port_coord, itrack,
|
||||
unique_mirror.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp, module_manager,
|
||||
sb_module,
|
||||
sb_instance_name,
|
||||
module_port,
|
||||
chan_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_sbs(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
|
||||
/* Build unique X-direction connection block modules */
|
||||
DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range();
|
||||
|
||||
for (size_t ix = 0; ix < sb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < sb_range.get_y(); ++iy) {
|
||||
/* Check if the connection block exists in the device!
|
||||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
|
||||
print_analysis_sdc_disable_sb_unused_resources(fp, grids,
|
||||
module_manager,
|
||||
L_device_rr_gsb,
|
||||
rr_gsb,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef ANALYSIS_SDC_ROUTING_WRITER_H
|
||||
#define ANALYSIS_SDC_ROUTING_WRITER_H
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "module_manager.h"
|
||||
#include "rr_blocks.h"
|
||||
#include "vpr_types.h"
|
||||
|
||||
void print_analysis_sdc_disable_unused_cbs(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
void print_analysis_sdc_disable_unused_sbs(std::fstream& fp,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,271 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to output a SDC file
|
||||
* that constrain a FPGA fabric (P&Red netlist) using a benchmark
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "device_port.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_benchmark_utils.h"
|
||||
|
||||
#include "sdc_writer_naming.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "sdc_memory_utils.h"
|
||||
|
||||
#include "analysis_sdc_grid_writer.h"
|
||||
#include "analysis_sdc_routing_writer.h"
|
||||
#include "analysis_sdc_writer.h"
|
||||
|
||||
/********************************************************************
|
||||
* Generate SDC constaints for inputs and outputs
|
||||
* We consider the top module in formal verification purpose here
|
||||
* which is easier
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_io_delays(std::fstream& fp,
|
||||
const std::vector<t_logical_block>& L_logical_blocks,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const float& critical_path_delay) {
|
||||
/* Validate the file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create clock " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Get clock port from the global port */
|
||||
std::vector<BasicPort> operating_clock_ports;
|
||||
for (const CircuitPortId& clock_port : global_ports) {
|
||||
if (SPICE_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) {
|
||||
continue;
|
||||
}
|
||||
/* We only constrain operating clock here! */
|
||||
if (true == circuit_lib.port_is_prog(clock_port)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the module port and Update the operating port list */
|
||||
ModulePortId module_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(clock_port));
|
||||
operating_clock_ports.push_back(module_manager.module_port(top_module, module_port));
|
||||
}
|
||||
|
||||
for (const BasicPort& operating_clock_port : operating_clock_ports) {
|
||||
/* Reach here, it means a clock port and we need print constraints */
|
||||
fp << "create_clock ";
|
||||
fp << generate_sdc_port(operating_clock_port);
|
||||
fp << " -period " << std::setprecision(10) << critical_path_delay;
|
||||
fp << " -waveform {0 " << std::setprecision(10) << critical_path_delay / 2 << "}";
|
||||
fp << std::endl;
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* There should be only one operating clock!
|
||||
* TODO: this should be changed when developing multi-clock support!!!
|
||||
*/
|
||||
VTR_ASSERT(1 == operating_clock_ports.size());
|
||||
|
||||
/* In this function, we support only 1 type of I/Os */
|
||||
VTR_ASSERT(1 == module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT).size());
|
||||
BasicPort module_io_port = module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)[0];
|
||||
|
||||
/* Keep tracking which I/Os have been used */
|
||||
std::vector<bool> io_used(module_io_port.get_width(), false);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create input and output delays for used I/Os " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
for (const t_logical_block& io_lb : L_logical_blocks) {
|
||||
/* We only care I/O logical blocks !*/
|
||||
if ( (VPACK_INPAD != io_lb.type) && (VPACK_OUTPAD != io_lb.type) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* clock net or constant generator should be disabled in timing analysis */
|
||||
if (TRUE == io_lb.is_clock) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the index of the mapped GPIO in top-level FPGA fabric */
|
||||
size_t io_index = find_benchmark_io_index(io_lb, device_size, L_grids, L_blocks);
|
||||
|
||||
/* Ensure that IO index is in range */
|
||||
BasicPort module_mapped_io_port = module_io_port;
|
||||
/* Set the port pin index */
|
||||
VTR_ASSERT(io_index < module_mapped_io_port.get_width());
|
||||
module_mapped_io_port.set_width(io_index, io_index);
|
||||
|
||||
/* For input I/O, we set an input delay constraint correlated to the operating clock
|
||||
* For output I/O, we set an output delay constraint correlated to the operating clock
|
||||
*/
|
||||
if (VPACK_INPAD == io_lb.type) {
|
||||
print_sdc_set_port_input_delay(fp, module_mapped_io_port,
|
||||
operating_clock_ports[0], critical_path_delay);
|
||||
} else {
|
||||
VTR_ASSERT(VPACK_OUTPAD == io_lb.type);
|
||||
print_sdc_set_port_output_delay(fp, module_mapped_io_port,
|
||||
operating_clock_ports[0], critical_path_delay);
|
||||
}
|
||||
|
||||
/* Mark this I/O has been used/wired */
|
||||
io_used[io_index] = true;
|
||||
}
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for unused I/Os " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Wire the unused iopads to a constant */
|
||||
for (size_t io_index = 0; io_index < io_used.size(); ++io_index) {
|
||||
/* Bypass used iopads */
|
||||
if (true == io_used[io_index]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wire to a contant */
|
||||
BasicPort module_unused_io_port = module_io_port;
|
||||
/* Set the port pin index */
|
||||
module_unused_io_port.set_width(io_index, io_index);
|
||||
print_sdc_disable_port_timing(fp, module_unused_io_port);
|
||||
}
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable the timing for all the global port except the operating clock ports
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_global_ports(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for global ports " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
for (const CircuitPortId& global_port : global_ports) {
|
||||
/* Skip operating clock here! */
|
||||
if ( (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port))
|
||||
&& (false == circuit_lib.port_is_prog(global_port)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModulePortId module_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(global_port));
|
||||
BasicPort port_to_disable = module_manager.module_port(top_module, module_port);
|
||||
|
||||
print_sdc_disable_port_timing(fp, port_to_disable);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function outputs a SDC file
|
||||
* that constrain a FPGA fabric (P&Red netlist) using a benchmark
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc(const std::string& sdc_dir,
|
||||
const float& critical_path_delay,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<t_logical_block>& L_logical_blocks,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_ANALYSIS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for Timing/Power analysis on the mapped FPGA: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Constrain for Timing/Power analysis on the mapped FPGA"));
|
||||
|
||||
/* Find the top_module */
|
||||
ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name());
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||
|
||||
/* Create clock and set I/O ports with input/output delays */
|
||||
print_analysis_sdc_io_delays(fp,
|
||||
L_logical_blocks, device_size, L_grids, L_blocks,
|
||||
module_manager, top_module,
|
||||
circuit_lib, global_ports,
|
||||
critical_path_delay);
|
||||
|
||||
/* Disable the timing for global ports */
|
||||
print_analysis_sdc_disable_global_ports(fp,
|
||||
module_manager, top_module,
|
||||
circuit_lib, global_ports);
|
||||
|
||||
/* Disable the timing for configuration cells */
|
||||
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp,
|
||||
module_manager, top_module,
|
||||
format_dir_path(module_manager.module_name(top_module)));
|
||||
|
||||
|
||||
/* Disable timing for unused routing resources in connection blocks */
|
||||
print_analysis_sdc_disable_unused_cbs(fp, L_grids,
|
||||
module_manager,
|
||||
L_device_rr_gsb,
|
||||
compact_routing_hierarchy);
|
||||
|
||||
/* Disable timing for unused routing resources in switch blocks */
|
||||
print_analysis_sdc_disable_unused_sbs(fp, L_grids,
|
||||
module_manager,
|
||||
L_device_rr_gsb,
|
||||
compact_routing_hierarchy);
|
||||
|
||||
/* Disable timing for unused routing resources in grids (programmable blocks) */
|
||||
print_analysis_sdc_disable_unused_grids(fp, device_size, L_grids, L_blocks, module_manager);
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef ANALYSIS_SDC_WRITER_H
|
||||
#define ANALYSIS_SDC_WRITER_H
|
||||
|
||||
#include <string>
|
||||
#include "vpr_types.h"
|
||||
#include "rr_blocks.h"
|
||||
#include "module_manager.h"
|
||||
#include "bitstream_manager.h"
|
||||
|
||||
void print_analysis_sdc(const std::string& sdc_dir,
|
||||
const float& critical_path_delay,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<t_logical_block>& L_logical_blocks,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,235 @@
|
|||
/********************************************************************
|
||||
* This file includes most utilized functions
|
||||
* that are used to output a SDC file
|
||||
* in order to constrain a FPGA fabric (P&Red netlist) mapped to a benchmark
|
||||
*******************************************************************/
|
||||
#include "vtr_assert.h"
|
||||
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
/********************************************************************
|
||||
* Identify if a node should be disabled during analysis SDC generation
|
||||
*******************************************************************/
|
||||
bool is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node) {
|
||||
/* Conditions to enable timing analysis for a node
|
||||
* 1st condition: it have a valid vpack_net_number
|
||||
* 2nd condition: it is not an parasitic net
|
||||
* 3rd condition: it is not a global net
|
||||
*/
|
||||
if ( (OPEN != cur_rr_node->vpack_net_num)
|
||||
&& (FALSE == cur_rr_node->is_parasitic_net)
|
||||
&& (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_global)
|
||||
&& (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_const_gen) ){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable all the unused inputs of routing multiplexers, which are not used by benchmark
|
||||
* Here, we start from each input of a routing module, and traverse forward to the sink
|
||||
* port of the module net whose source is the input
|
||||
* We will find the instance name which is the parent of the sink port, and search the
|
||||
* net id through the instance_name_to_net_map
|
||||
* The the net id does not match the net id of this input, we will disable the sink port!
|
||||
*
|
||||
* parent_module
|
||||
* +-----------------------
|
||||
* | MUX instance A
|
||||
* | +-----------
|
||||
* input_port--->|--+---x-->| sink port (disable! net_id = Y)
|
||||
* (net_id = X) | | +----------
|
||||
* | | MUX instance B
|
||||
* | | +----------
|
||||
* | +------>| sink port (do not disable! net_id = X)
|
||||
*
|
||||
*******************************************************************/
|
||||
void disable_analysis_module_input_pin_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModulePortId& module_input_port,
|
||||
const size_t& module_input_pin,
|
||||
t_rr_node* input_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Find the module net which sources from this port! */
|
||||
ModuleNetId module_net = module_manager.module_instance_port_net(parent_module, parent_module, 0, module_input_port, module_input_pin);
|
||||
VTR_ASSERT(true == module_manager.valid_module_net_id(parent_module, module_net));
|
||||
|
||||
/* Touch each sink of the net! */
|
||||
for (const ModuleNetSinkId& sink_id : module_manager.module_net_sinks(parent_module, module_net)) {
|
||||
ModuleId sink_module = module_manager.net_sink_modules(parent_module, module_net)[sink_id];
|
||||
size_t sink_instance = module_manager.net_sink_instances(parent_module, module_net)[sink_id];
|
||||
|
||||
/* Skip when sink module is the parent module,
|
||||
* the output ports of parent modules have been disabled/enabled already!
|
||||
*/
|
||||
if (sink_module == parent_module) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sink_instance_name = module_manager.instance_name(parent_module, sink_module, sink_instance);
|
||||
bool disable_timing = false;
|
||||
/* Check if this node is used by benchmark */
|
||||
if (true == is_rr_node_to_be_disable_for_analysis(input_rr_node)) {
|
||||
/* Disable all the sinks! */
|
||||
disable_timing = true;
|
||||
} else {
|
||||
std::map<std::string, int>::const_iterator it = mux_instance_to_net_map.find(sink_instance_name);
|
||||
if (it != mux_instance_to_net_map.end()) {
|
||||
/* See if the net id matches. If does not match, we should disable! */
|
||||
if (input_rr_node->vpack_net_num != mux_instance_to_net_map.at(sink_instance_name)) {
|
||||
disable_timing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to write SDC command to disable timing or not */
|
||||
if (false == disable_timing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicPort sink_port = module_manager.module_port(sink_module, module_manager.net_sink_ports(parent_module, module_net)[sink_id]);
|
||||
sink_port.set_width(module_manager.net_sink_pins(parent_module, module_net)[sink_id],
|
||||
module_manager.net_sink_pins(parent_module, module_net)[sink_id]);
|
||||
/* Get the input id that is used! Disable the unused inputs! */
|
||||
fp << "set_disable_timing ";
|
||||
fp << parent_instance_name << "/";
|
||||
fp << sink_instance_name << "/";
|
||||
fp << generate_sdc_port(sink_port);
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Disable all the unused inputs of routing multiplexers, which are not used by benchmark
|
||||
* Here, we start from each input of a routing module, and traverse forward to the sink
|
||||
* port of the module net whose source is the input
|
||||
* We will find the instance name which is the parent of the sink port, and search the
|
||||
* net id through the instance_name_to_net_map
|
||||
* The the net id does not match the net id of this input, we will disable the sink port!
|
||||
*
|
||||
* parent_module
|
||||
* +-----------------------
|
||||
* | MUX instance A
|
||||
* | +-----------
|
||||
* input_port--->|--+---x-->| sink port (disable! net_id = Y)
|
||||
* (net_id = X) | | +----------
|
||||
* | | MUX instance B
|
||||
* | | +----------
|
||||
* | +------>| sink port (do not disable! net_id = X)
|
||||
*
|
||||
*******************************************************************/
|
||||
void disable_analysis_module_input_port_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModulePortId& module_input_port,
|
||||
t_rr_node* input_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Find the module net which sources from this port! */
|
||||
for (const size_t& pin : module_manager.module_port(parent_module, module_input_port).pins()) {
|
||||
disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module,
|
||||
parent_instance_name,
|
||||
module_input_port, pin,
|
||||
input_rr_node,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable all the unused inputs of routing multiplexers, which are not used by benchmark
|
||||
* Here, we start from each output of a child module, and traverse forward to the sink
|
||||
* port of the module net whose source is the input
|
||||
* We will find the instance name which is the parent of the sink port, and search the
|
||||
* net id through the instance_name_to_net_map
|
||||
* The the net id does not match the net id of this input, we will disable the sink port!
|
||||
*
|
||||
* Parent_module
|
||||
* +---------------------------------------------
|
||||
* |
|
||||
* | +--------------------------------------------+
|
||||
* | | MUX child_module |
|
||||
* | | +-------------+ +-----------+ |
|
||||
* | +--->| Routing |------>| | |
|
||||
* input_pin0(netA) --->|----x--->| Multiplexer | netA | output_pin|-----+
|
||||
* | +-------------+ | | netA
|
||||
* | | |
|
||||
*
|
||||
|
||||
*
|
||||
*******************************************************************/
|
||||
void disable_analysis_module_output_pin_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModuleId& child_module,
|
||||
const size_t& child_instance,
|
||||
const ModulePortId& child_module_port,
|
||||
const size_t& child_module_pin,
|
||||
t_rr_node* output_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Find the module net which sources from this port! */
|
||||
ModuleNetId module_net = module_manager.module_instance_port_net(parent_module, child_module, child_instance, child_module_port, child_module_pin);
|
||||
VTR_ASSERT(true == module_manager.valid_module_net_id(parent_module, module_net));
|
||||
|
||||
/* Touch each sink of the net! */
|
||||
for (const ModuleNetSinkId& sink_id : module_manager.module_net_sinks(parent_module, module_net)) {
|
||||
ModuleId sink_module = module_manager.net_sink_modules(parent_module, module_net)[sink_id];
|
||||
size_t sink_instance = module_manager.net_sink_instances(parent_module, module_net)[sink_id];
|
||||
|
||||
/* Skip when sink module is the parent module,
|
||||
* the output ports of parent modules have been disabled/enabled already!
|
||||
*/
|
||||
if (sink_module == parent_module) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sink_instance_name = module_manager.instance_name(parent_module, sink_module, sink_instance);
|
||||
bool disable_timing = false;
|
||||
/* Check if this node is used by benchmark */
|
||||
if (true == is_rr_node_to_be_disable_for_analysis(output_rr_node)) {
|
||||
/* Disable all the sinks! */
|
||||
disable_timing = true;
|
||||
} else {
|
||||
std::map<std::string, int>::const_iterator it = mux_instance_to_net_map.find(sink_instance_name);
|
||||
if (it != mux_instance_to_net_map.end()) {
|
||||
/* See if the net id matches. If does not match, we should disable! */
|
||||
if (output_rr_node->vpack_net_num != mux_instance_to_net_map.at(sink_instance_name)) {
|
||||
disable_timing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to write SDC command to disable timing or not */
|
||||
if (false == disable_timing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicPort sink_port = module_manager.module_port(sink_module, module_manager.net_sink_ports(parent_module, module_net)[sink_id]);
|
||||
sink_port.set_width(module_manager.net_sink_pins(parent_module, module_net)[sink_id],
|
||||
module_manager.net_sink_pins(parent_module, module_net)[sink_id]);
|
||||
/* Get the input id that is used! Disable the unused inputs! */
|
||||
fp << "set_disable_timing ";
|
||||
fp << parent_instance_name << "/";
|
||||
fp << sink_instance_name << "/";
|
||||
fp << generate_sdc_port(sink_port);
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef ANALYSIS_SDC_WRITER_UTILS_H
|
||||
#define ANALYSIS_SDC_WRITER_UTILS_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "module_manager.h"
|
||||
#include "vpr_types.h"
|
||||
|
||||
bool is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node);
|
||||
|
||||
void disable_analysis_module_input_pin_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModulePortId& module_input_port,
|
||||
const size_t& module_input_pin,
|
||||
t_rr_node* input_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map);
|
||||
|
||||
void disable_analysis_module_input_port_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModulePortId& module_input_port,
|
||||
t_rr_node* input_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map) ;
|
||||
|
||||
void disable_analysis_module_output_pin_net_sinks(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_instance_name,
|
||||
const ModuleId& child_module,
|
||||
const size_t& child_instance,
|
||||
const ModulePortId& child_module_port,
|
||||
const size_t& child_module_pin,
|
||||
t_rr_node* output_rr_node,
|
||||
const std::map<std::string, int> mux_instance_to_net_map);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,361 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||
* The SDC files are used to constrain the physical design for each grid
|
||||
* (CLBs, heterogeneous blocks etc.)
|
||||
*
|
||||
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "device_port.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_pbtypes_utils.h"
|
||||
|
||||
#include "sdc_writer_naming.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "pnr_sdc_grid_writer.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
/********************************************************************
|
||||
* Print pin-to-pin timing constraints for a given interconnection
|
||||
* at an output port of a pb_graph node
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_pb_pin_interc_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const e_side& border_side,
|
||||
t_pb_graph_pin* des_pb_graph_pin,
|
||||
t_mode* physical_mode) {
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* 1. identify pin interconnection type,
|
||||
* 2. Identify the number of fan-in (Consider interconnection edges of only selected mode)
|
||||
* 3. Print SDC timing constraints
|
||||
*/
|
||||
int fan_in = 0;
|
||||
t_interconnect* cur_interc = NULL;
|
||||
find_interc_fan_in_des_pb_graph_pin(des_pb_graph_pin, physical_mode, &cur_interc, &fan_in);
|
||||
if ((NULL == cur_interc) || (0 == fan_in)) {
|
||||
/* No interconnection matched */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print pin-to-pin SDC contraint here */
|
||||
/* For more than one mode defined, the direct interc has more than one input_edge ,
|
||||
* We need to find which edge is connected the pin we want
|
||||
*/
|
||||
for (int iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
|
||||
if (cur_interc != des_pb_graph_pin->input_edges[iedge]->interconnect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Source pin, node, pb_type*/
|
||||
t_pb_graph_pin* src_pb_graph_pin = des_pb_graph_pin->input_edges[iedge]->input_pins[0];
|
||||
t_pb_graph_node* src_pb_graph_node = src_pb_graph_pin->parent_node;
|
||||
/* Des pin, node, pb_type */
|
||||
t_pb_graph_node* des_pb_graph_node = des_pb_graph_pin->parent_node;
|
||||
|
||||
/* Find the src module in module manager */
|
||||
std::string src_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string src_module_name = generate_physical_block_module_name(src_module_name_prefix, src_pb_graph_pin->parent_node->pb_type);
|
||||
ModuleId src_module = module_manager.find_module(src_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(src_module));
|
||||
|
||||
ModulePortId src_module_port_id = module_manager.find_module_port(src_module, generate_pb_type_port_name(src_pb_graph_pin->port));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(src_module, src_module_port_id));
|
||||
|
||||
/* Generate the name of the des instance name
|
||||
* If des module is not the parent module, it is a child module.
|
||||
* We should find the instance id
|
||||
*/
|
||||
std::string src_instance_name = src_module_name;
|
||||
if (parent_module != src_module) {
|
||||
src_instance_name = module_manager.module_name(parent_module) + std::string("/");
|
||||
/* Instance id is actually the placement index */
|
||||
size_t instance_id = src_pb_graph_node->placement_index;
|
||||
if (true == module_manager.instance_name(parent_module, src_module, instance_id).empty()) {
|
||||
src_instance_name += src_module_name;
|
||||
src_instance_name += "_";
|
||||
src_instance_name += std::to_string(instance_id);
|
||||
src_instance_name += "_";
|
||||
} else {
|
||||
src_instance_name += module_manager.instance_name(parent_module, src_module, instance_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate src port information */
|
||||
BasicPort src_port = module_manager.module_port(src_module, src_module_port_id);
|
||||
src_port.set_width(src_pb_graph_pin->pin_number, src_pb_graph_pin->pin_number);
|
||||
|
||||
/* Find the des module in module manager */
|
||||
std::string des_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string des_module_name = generate_physical_block_module_name(des_module_name_prefix, des_pb_graph_pin->parent_node->pb_type);
|
||||
ModuleId des_module = module_manager.find_module(des_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(des_module));
|
||||
ModulePortId des_module_port_id = module_manager.find_module_port(des_module, generate_pb_type_port_name(des_pb_graph_pin->port));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(des_module, des_module_port_id));
|
||||
|
||||
/* Generate the name of the des instance name
|
||||
* If des module is not the parent module, it is a child module.
|
||||
* We should find the instance id
|
||||
*/
|
||||
std::string des_instance_name = des_module_name;
|
||||
if (parent_module != des_module) {
|
||||
des_instance_name = module_manager.module_name(parent_module) + std::string("/");
|
||||
/* Instance id is actually the placement index */
|
||||
size_t instance_id = des_pb_graph_node->placement_index;
|
||||
if (true == module_manager.instance_name(parent_module, des_module, instance_id).empty()) {
|
||||
des_instance_name += des_module_name;
|
||||
des_instance_name += "_";
|
||||
des_instance_name += std::to_string(instance_id);
|
||||
des_instance_name += "_";
|
||||
} else {
|
||||
des_instance_name += module_manager.instance_name(parent_module, des_module, instance_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate des port information */
|
||||
BasicPort des_port = module_manager.module_port(des_module, des_module_port_id);
|
||||
des_port.set_width(des_pb_graph_pin->pin_number, des_pb_graph_pin->pin_number);
|
||||
|
||||
/* Print a SDC timing constraint */
|
||||
print_pnr_sdc_constrain_max_delay(fp,
|
||||
src_instance_name,
|
||||
generate_sdc_port(src_port),
|
||||
des_instance_name,
|
||||
generate_sdc_port(des_port),
|
||||
des_pb_graph_pin->input_edges[iedge]->delay_max);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print port-to-port timing constraints which source from
|
||||
* an output port of a pb_graph node
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const e_side& border_side,
|
||||
t_pb_graph_node* des_pb_graph_node,
|
||||
const e_spice_pb_port_type& pb_port_type,
|
||||
t_mode* physical_mode) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
switch (pb_port_type) {
|
||||
case SPICE_PB_PORT_INPUT: {
|
||||
for (int iport = 0; iport < des_pb_graph_node->num_input_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < des_pb_graph_node->num_input_pins[iport]; ++ipin) {
|
||||
/* If this is a idle block, we set 0 to the selected edge*/
|
||||
/* Get the selected edge of current pin*/
|
||||
print_pnr_sdc_constrain_pb_pin_interc_timing(fp, module_manager, parent_module, border_side,
|
||||
&(des_pb_graph_node->input_pins[iport][ipin]),
|
||||
physical_mode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPICE_PB_PORT_OUTPUT: {
|
||||
for (int iport = 0; iport < des_pb_graph_node->num_output_ports; ++iport) {
|
||||
for (int ipin = 0; ipin < des_pb_graph_node->num_output_pins[iport]; ++ipin) {
|
||||
print_pnr_sdc_constrain_pb_pin_interc_timing(fp, module_manager, parent_module, border_side,
|
||||
&(des_pb_graph_node->output_pins[iport][ipin]),
|
||||
physical_mode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPICE_PB_PORT_CLOCK: {
|
||||
/* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d]) Invalid pb port type!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function will generate a SDC file for each pb_type,
|
||||
* constraining the pin-to-pin timing between
|
||||
* 1. input port of parent_pb_graph_node and input port of child_pb_graph_nodes
|
||||
* 2. output port of parent_pb_graph_node and output port of child_pb_graph_nodes
|
||||
* 3. output port of child_pb_graph_node and input port of child_pb_graph_nodes
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
t_pb_graph_node* parent_pb_graph_node,
|
||||
const int& physical_mode_index,
|
||||
const e_side& border_side) {
|
||||
|
||||
/* Get the pb_type definition related to the node */
|
||||
t_pb_type* physical_pb_type = parent_pb_graph_node->pb_type;
|
||||
std::string pb_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side);
|
||||
std::string pb_module_name = generate_physical_block_module_name(pb_module_name_prefix, physical_pb_type);
|
||||
|
||||
/* Find the pb module in module manager */
|
||||
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
||||
|
||||
/* Create the file name for SDC */
|
||||
std::string sdc_fname(sdc_dir + pb_module_name + std::string(SDC_FILE_NAME_POSTFIX));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Timing constraints for Grid " + pb_module_name + " in PnR"));
|
||||
|
||||
t_mode* physical_mode = &(parent_pb_graph_node->pb_type->modes[physical_mode_index]);
|
||||
|
||||
/* We check output_pins of cur_pb_graph_node and its the input_edges
|
||||
* Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node
|
||||
* child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins
|
||||
* /|\
|
||||
* |
|
||||
* input_pins, edges, output_pins
|
||||
*/
|
||||
print_pnr_sdc_constrain_pb_interc_timing(fp, module_manager, pb_module, border_side,
|
||||
parent_pb_graph_node,
|
||||
SPICE_PB_PORT_OUTPUT,
|
||||
physical_mode);
|
||||
|
||||
/* We check input_pins of child_pb_graph_node and its the input_edges
|
||||
* Built the interconnections between inputs of cur_pb_graph_node and inputs of child_pb_graph_node
|
||||
* cur_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins
|
||||
* /|\
|
||||
* |
|
||||
* input_pins, edges, output_pins
|
||||
*/
|
||||
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
||||
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
||||
t_pb_graph_node* child_pb_graph_node = &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ipb][jpb]);
|
||||
/* For each child_pb_graph_node input pins*/
|
||||
print_pnr_sdc_constrain_pb_interc_timing(fp, module_manager, pb_module, border_side,
|
||||
child_pb_graph_node,
|
||||
SPICE_PB_PORT_INPUT,
|
||||
physical_mode);
|
||||
/* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Recursively print SDC timing constraints for a pb_type
|
||||
* This function will generate a SDC file for each pb_type,
|
||||
* constraining the pin-to-pin timing
|
||||
*******************************************************************/
|
||||
static
|
||||
void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
t_pb_graph_node* parent_pb_graph_node,
|
||||
const e_side& border_side) {
|
||||
/* Validate pb_graph node */
|
||||
if (NULL == parent_pb_graph_node) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d]) Invalid parent_pb_graph_node.\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Get the pb_type */
|
||||
t_pb_type* parent_pb_type = parent_pb_graph_node->pb_type;
|
||||
|
||||
/* No need to constrain the primitive node */
|
||||
if (TRUE == is_primitive_pb_type(parent_pb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note we only go through the graph through the physical modes.
|
||||
* which we build the modules
|
||||
*/
|
||||
int physical_mode_index = find_pb_type_physical_mode_index((*parent_pb_type));
|
||||
|
||||
/* Write a SDC file for this pb_type */
|
||||
print_pnr_sdc_constrain_pb_graph_node_timing(sdc_dir, module_manager,
|
||||
parent_pb_graph_node, physical_mode_index,
|
||||
border_side);
|
||||
|
||||
/* Go recursively to the lower level in the pb_graph
|
||||
* Note that we assume a full hierarchical P&R, we will only visit pb_graph_node of unique pb_type
|
||||
*/
|
||||
for (int ipb = 0; ipb < parent_pb_type->modes[physical_mode_index].num_pb_type_children; ++ipb) {
|
||||
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||
&(parent_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ipb][0]),
|
||||
border_side);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to print timing constraints for pb_types
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager) {
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constraining grid timing for P&R flow...");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
for (int itype = 0; itype < num_types; itype++) {
|
||||
/* Bypass EMPTY types */
|
||||
if (EMPTY_TYPE == &type_descriptors[itype]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* For IO_TYPE, we have four types of I/Os */
|
||||
if (IO_TYPE == &type_descriptors[itype]) {
|
||||
/* Special for I/O block, generate one module for each border side */
|
||||
for (int iside = 0; iside < NUM_SIDES; iside++) {
|
||||
Side side_manager(iside);
|
||||
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||
type_descriptors[itype].pb_graph_head,
|
||||
side_manager.get_side());
|
||||
}
|
||||
} else if (FILL_TYPE == &type_descriptors[itype]) {
|
||||
/* For CLB */
|
||||
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||
type_descriptors[itype].pb_graph_head,
|
||||
NUM_SIDES);
|
||||
} else {
|
||||
/* For heterogenenous blocks */
|
||||
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||
type_descriptors[itype].pb_graph_head,
|
||||
NUM_SIDES);
|
||||
}
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef PNR_SDC_GRID_WRITER_H
|
||||
#define PNR_SDC_GRID_WRITER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "vpr_types.h"
|
||||
|
||||
void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,452 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||
* The SDC files are used to constrain the physical design for each routing modules
|
||||
* in FPGA fabric, such as Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||
*
|
||||
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "device_port.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
#include "build_routing_module_utils.h"
|
||||
|
||||
#include "sdc_writer_naming.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "pnr_sdc_routing_writer.h"
|
||||
|
||||
/********************************************************************
|
||||
* Find the timing constraints between the inputs and outputs of a routing
|
||||
* multiplexer in a Switch Block
|
||||
*******************************************************************/
|
||||
static
|
||||
float find_pnr_sdc_switch_tmax(const t_switch_inf& switch_inf) {
|
||||
return switch_inf.R * switch_inf.Cout + switch_inf.Tdel;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Set timing constraints between the inputs and outputs of a routing
|
||||
* multiplexer in a Switch Block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const e_side& output_node_side,
|
||||
t_rr_node* output_rr_node) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
VTR_ASSERT( ( CHANX == output_rr_node->type )
|
||||
|| ( CHANY == output_rr_node->type ));
|
||||
|
||||
/* Find the module port corresponding to the output rr_node */
|
||||
ModulePortId module_output_port = find_switch_block_module_chan_port(module_manager,
|
||||
sb_module,
|
||||
rr_gsb,
|
||||
output_node_side,
|
||||
output_rr_node,
|
||||
OUT_PORT);
|
||||
|
||||
/* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */
|
||||
std::vector<t_rr_node*> input_rr_nodes;
|
||||
for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) {
|
||||
input_rr_nodes.push_back(output_rr_node->drive_rr_nodes[iedge]);
|
||||
}
|
||||
|
||||
std::vector<ModulePortId> module_input_ports = find_switch_block_module_input_ports(module_manager,
|
||||
sb_module,
|
||||
rr_gsb,
|
||||
grids,
|
||||
input_rr_nodes);
|
||||
|
||||
/* Find timing constraints for each path (edge) */
|
||||
std::map<ModulePortId, float> switch_delays;
|
||||
for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) {
|
||||
/* Get the switch delay */
|
||||
int switch_id = output_rr_node->drive_switches[iedge];
|
||||
switch_delays[module_input_ports[iedge]] = find_pnr_sdc_switch_tmax(switches[switch_id]);
|
||||
}
|
||||
|
||||
/* Find the starting points */
|
||||
for (const ModulePortId& module_input_port : module_input_ports) {
|
||||
/* Constrain a path */
|
||||
print_pnr_sdc_constrain_module_port2port_timing(fp,
|
||||
module_manager,
|
||||
sb_module, module_input_port,
|
||||
sb_module, module_output_port,
|
||||
switch_delays[module_input_port]);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Set timing constraints between the inputs and outputs of SBs,
|
||||
* which are connected by routing multiplexers with the given delays
|
||||
* specified in architectural XML file
|
||||
*
|
||||
* To enable block by block timing constraining, we generate the SDC
|
||||
* file for each unique SB module
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const RRGSB& rr_gsb) {
|
||||
|
||||
/* Create the file name for Verilog netlist */
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
std::string sdc_fname(sdc_dir + generate_switch_block_module_name(gsb_coordinate) + std::string(SDC_FILE_NAME_POSTFIX));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate);
|
||||
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Constrain timing of Switch Block " + sb_module_name + " for PnR"));
|
||||
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
Side side_manager(side);
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
t_rr_node* chan_rr_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||
/* We only care the output port and it should indicate a SB mux */
|
||||
if (OUT_PORT != rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
continue;
|
||||
}
|
||||
/* Constrain thru wires */
|
||||
if (false != rr_gsb.is_sb_node_passing_wire(side_manager.get_side(), itrack)) {
|
||||
continue;
|
||||
}
|
||||
/* This is a MUX, constrain all the paths from an input to an output */
|
||||
print_pnr_sdc_constrain_sb_mux_timing(fp,
|
||||
module_manager, sb_module,
|
||||
rr_gsb,
|
||||
grids, switches,
|
||||
side_manager.get_side(),
|
||||
chan_rr_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print SDC timing constraints for Switch blocks
|
||||
* This function is designed for flatten routing hierarchy
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constrain Switch Block timing for P&R flow...");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Get the range of SB array */
|
||||
DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range();
|
||||
/* Go for each SB */
|
||||
for (size_t ix = 0; ix < sb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < sb_range.get_y(); ++iy) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
print_pnr_sdc_constrain_sb_timing(sdc_dir,
|
||||
module_manager,
|
||||
grids, switches,
|
||||
rr_gsb);
|
||||
}
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print SDC timing constraints for Switch blocks
|
||||
* This function is designed for compact routing hierarchy
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constrain Switch Block timing for P&R flow...");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_sb_unique_module(isb);
|
||||
print_pnr_sdc_constrain_sb_timing(sdc_dir,
|
||||
module_manager,
|
||||
grids, switches,
|
||||
rr_gsb);
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Set timing constraints between the inputs and outputs of a routing
|
||||
* multiplexer in a Connection Block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
t_rr_node* output_rr_node) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
VTR_ASSERT(IPIN == output_rr_node->type);
|
||||
|
||||
/* We have OPINs since we may have direct connections:
|
||||
* These connections should be handled by other functions in the compact_netlist.c
|
||||
* So we just return here for OPINs
|
||||
*/
|
||||
if ( (1 == output_rr_node->num_drive_rr_nodes)
|
||||
&& (OPIN == output_rr_node->drive_rr_nodes[0]->type) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the module port corresponding to the output rr_node */
|
||||
ModulePortId module_output_port = find_connection_block_module_ipin_port(module_manager,
|
||||
cb_module,
|
||||
rr_gsb,
|
||||
grids, output_rr_node);
|
||||
|
||||
/* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */
|
||||
std::vector<t_rr_node*> input_rr_nodes;
|
||||
for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) {
|
||||
/* Skip OPINs which should be handled in direct connection */
|
||||
input_rr_nodes.push_back(output_rr_node->drive_rr_nodes[iedge]);
|
||||
}
|
||||
|
||||
std::vector<ModulePortId> module_input_ports = find_connection_block_module_input_ports(module_manager,
|
||||
cb_module,
|
||||
rr_gsb,
|
||||
cb_type,
|
||||
input_rr_nodes);
|
||||
|
||||
/* Find timing constraints for each path (edge) */
|
||||
std::map<ModulePortId, float> switch_delays;
|
||||
for (int iedge = 0; iedge < output_rr_node->num_drive_rr_nodes; iedge++) {
|
||||
/* Get the switch delay */
|
||||
int switch_id = output_rr_node->drive_switches[iedge];
|
||||
switch_delays[module_input_ports[iedge]] = find_pnr_sdc_switch_tmax(switches[switch_id]);
|
||||
}
|
||||
|
||||
/* Find the starting points */
|
||||
for (const ModulePortId& module_input_port : module_input_ports) {
|
||||
/* Constrain a path */
|
||||
print_pnr_sdc_constrain_module_port2port_timing(fp,
|
||||
module_manager,
|
||||
cb_module, module_input_port,
|
||||
cb_module, module_output_port,
|
||||
switch_delays[module_input_port]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Print SDC timing constraints for a Connection block
|
||||
* This function is designed for compact routing hierarchy
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches) {
|
||||
/* Create the netlist */
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
|
||||
/* Find the module name and create a SDC file for it */
|
||||
std::string sdc_fname(sdc_dir + generate_connection_block_module_name(cb_type, gsb_coordinate) + std::string(SDC_FILE_NAME_POSTFIX));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
std::string cb_module_name = generate_connection_block_module_name(cb_type, gsb_coordinate);
|
||||
ModuleId cb_module = module_manager.find_module(cb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Constrain timing of Connection Block " + cb_module_name + " for PnR"));
|
||||
|
||||
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
|
||||
|
||||
for (size_t side = 0; side < cb_sides.size(); ++side) {
|
||||
enum e_side cb_ipin_side = cb_sides[side];
|
||||
Side side_manager(cb_ipin_side);
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) {
|
||||
t_rr_node* ipin_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, inode);
|
||||
print_pnr_sdc_constrain_cb_mux_timing(fp,
|
||||
module_manager, cb_module,
|
||||
rr_gsb, cb_type,
|
||||
grids, switches,
|
||||
ipin_rr_node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and print SDC file for each of them
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const t_rr_type& cb_type) {
|
||||
/* Build unique X-direction connection block modules */
|
||||
DeviceCoordinator cb_range = L_device_rr_gsb.get_gsb_range();
|
||||
|
||||
for (size_t ix = 0; ix < cb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < cb_range.get_y(); ++iy) {
|
||||
/* Check if the connection block exists in the device!
|
||||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||
module_manager,
|
||||
rr_gsb,
|
||||
cb_type,
|
||||
grids, switches);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and print SDC file for each of them
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constrain Connection Block timing for P&R flow...");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager,
|
||||
L_device_rr_gsb,
|
||||
grids,
|
||||
switches,
|
||||
CHANX);
|
||||
|
||||
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager,
|
||||
L_device_rr_gsb,
|
||||
grids,
|
||||
switches,
|
||||
CHANY);
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print SDC timing constraints for Connection blocks
|
||||
* This function is designed for compact routing hierarchy
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constrain Connection Block timing for P&R flow...");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Print SDC for unique X-direction connection block modules */
|
||||
for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) {
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANX, icb);
|
||||
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||
module_manager,
|
||||
unique_mirror,
|
||||
CHANX,
|
||||
grids, switches);
|
||||
}
|
||||
|
||||
/* Print SDC for unique Y-direction connection block modules */
|
||||
for (size_t icb = 0; icb < L_device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) {
|
||||
const RRGSB& unique_mirror = L_device_rr_gsb.get_cb_unique_module(CHANY, icb);
|
||||
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||
module_manager,
|
||||
unique_mirror,
|
||||
CHANY,
|
||||
grids, switches);
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef PNR_SDC_ROUTING_WRITER_H
|
||||
#define PNR_SDC_ROUTING_WRITER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "module_manager.h"
|
||||
#include "rr_blocks.h"
|
||||
#include "vpr_types.h"
|
||||
|
||||
void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb);
|
||||
|
||||
void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb);
|
||||
|
||||
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches);
|
||||
|
||||
void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,473 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||
* The SDC files are used to constrain the physical design for each module
|
||||
* in FPGA fabric, such as Configurable Logic Blocks (CLBs),
|
||||
* Heterogeneous blocks, Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||
*
|
||||
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "device_port.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
#include "sdc_writer_naming.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "sdc_memory_utils.h"
|
||||
#include "pnr_sdc_routing_writer.h"
|
||||
#include "pnr_sdc_grid_writer.h"
|
||||
#include "pnr_sdc_writer.h"
|
||||
|
||||
/********************************************************************
|
||||
* Local variables
|
||||
*******************************************************************/
|
||||
constexpr float SDC_FIXED_PROG_CLOCK_PERIOD = 100;
|
||||
constexpr float SDC_FIXED_CLOCK_PERIOD = 10;
|
||||
|
||||
/********************************************************************
|
||||
* Print a SDC file to constrain the global ports of FPGA fabric
|
||||
* in particular clock ports
|
||||
*
|
||||
* For programming clock, we give a fixed period, while for operating
|
||||
* clock, we constrain with critical path delay
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_global_ports(const std::string& sdc_dir,
|
||||
const float& critical_path_delay,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports) {
|
||||
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_GLOBAL_PORTS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for constraining clocks for P&R flow: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Clock contraints for PnR"));
|
||||
|
||||
/* Get clock port from the global port */
|
||||
for (const CircuitPortId& clock_port : global_ports) {
|
||||
if (SPICE_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, it means a clock port and we need print constraints */
|
||||
float clock_period = critical_path_delay;
|
||||
|
||||
/* For programming clock, we give a fixed period */
|
||||
if (true == circuit_lib.port_is_prog(clock_port)) {
|
||||
clock_period = SDC_FIXED_PROG_CLOCK_PERIOD;
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create programmable clock " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
} else {
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create clock " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
}
|
||||
|
||||
for (const size_t& pin : circuit_lib.pins(clock_port)) {
|
||||
BasicPort port_to_constrain(circuit_lib.port_prefix(clock_port), pin, pin);
|
||||
|
||||
fp << "create_clock ";
|
||||
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||
fp << std::setprecision(10) << clock_period;
|
||||
fp << " -waveform {0 ";
|
||||
fp << std::setprecision(10) << clock_period / 2;
|
||||
fp << "}" << std::endl;
|
||||
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* For non-clock port from the global port: give a fixed period */
|
||||
for (const CircuitPortId& global_port : global_ports) {
|
||||
if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Constrain other global ports " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Reach here, it means a non-clock global port and we need print constraints */
|
||||
float clock_period = SDC_FIXED_CLOCK_PERIOD;
|
||||
for (const size_t& pin : circuit_lib.pins(global_port)) {
|
||||
BasicPort port_to_constrain(circuit_lib.port_prefix(global_port), pin, pin);
|
||||
fp << "create_clock ";
|
||||
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||
fp << std::setprecision(10) << clock_period;
|
||||
fp << " -waveform {0 ";
|
||||
fp << std::setprecision(10) << clock_period / 2;
|
||||
fp << "} ";
|
||||
fp << "[list [get_ports { " << generate_sdc_port(port_to_constrain) << "}]]" << std::endl;
|
||||
|
||||
fp << "set_drive 0 " << generate_sdc_port(port_to_constrain) << std::endl;
|
||||
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Break combinational loops in FPGA fabric, which mainly come from
|
||||
* configurable memory cells.
|
||||
* To handle this, we disable the outputs of memory cells
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_constrain_configurable_memory_outputs(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module) {
|
||||
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for disable configurable memory outputs for P&R flow: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Disable configurable memory outputs for PnR"));
|
||||
|
||||
/* Go recursively in the module manager, starting from the top-level module: instance id of the top-level module is 0 by default */
|
||||
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager, top_module,
|
||||
format_dir_path(module_manager.module_name(top_module)));
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Break combinational loops in FPGA fabric, which mainly come from
|
||||
* loops of multiplexers.
|
||||
* To handle this, we disable the timing at outputs of routing multiplexers
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const ModuleManager& module_manager) {
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_MUX_OUTPUTS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for disable routing multiplexer outputs for P&R flow: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR"));
|
||||
|
||||
/* Iterate over the MUX modules */
|
||||
for (const MuxId& mux_id : mux_lib.muxes()) {
|
||||
const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id);
|
||||
|
||||
/* Skip LUTs, we only care about multiplexers here */
|
||||
if (SPICE_MODEL_MUX != circuit_lib.model_type(mux_model)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id);
|
||||
std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model,
|
||||
find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()),
|
||||
std::string(""));
|
||||
/* Find the module name in module manager */
|
||||
ModuleId mux_module = module_manager.find_module(mux_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
|
||||
|
||||
/* Disable the timing for the output ports */
|
||||
for (const BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||
fp << "set_disable_timing [get_pins -filter \"name =~ " << output_port.get_name() << "*\" ";
|
||||
fp << "-of [get_cells -hier -filter \"ref_lib_cell_name == " << mux_module_name << "\"]]" << std::endl;
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Break combinational loops in FPGA fabric, which mainly come from
|
||||
* loops of multiplexers.
|
||||
* To handle this, we disable the timing at outputs of Switch blocks
|
||||
* This function is designed for flatten routing hierarchy
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_flatten_routing_disable_switch_block_outputs(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for disable Switch Block outputs for P&R flow: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR"));
|
||||
|
||||
/* Get the range of SB array */
|
||||
DeviceCoordinator sb_range = L_device_rr_gsb.get_gsb_range();
|
||||
/* Go for each SB */
|
||||
for (size_t ix = 0; ix < sb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < sb_range.get_y(); ++iy) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
std::string sb_instance_name = generate_switch_block_module_name(gsb_coordinate);
|
||||
|
||||
ModuleId sb_module = module_manager.find_module(sb_instance_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Disable the outputs of the module */
|
||||
for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||
fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl;
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Break combinational loops in FPGA fabric, which mainly come from
|
||||
* loops of multiplexers.
|
||||
* To handle this, we disable the timing at outputs of Switch blocks
|
||||
* This function is designed for compact routing hierarchy
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_pnr_sdc_compact_routing_disable_switch_block_outputs(const std::string& sdc_dir,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
const DeviceRRGSB& L_device_rr_gsb) {
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME));
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Generating SDC for disable Switch Block outputs for P&R flow: %s ...",
|
||||
sdc_fname.c_str());
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR"));
|
||||
|
||||
/* Build unique switch block modules */
|
||||
for (size_t isb = 0; isb < L_device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_sb_unique_module(isb);
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate);
|
||||
|
||||
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Find all the instances in the top-level module */
|
||||
for (const size_t& instance_id : module_manager.child_module_instances(top_module, sb_module)) {
|
||||
std::string sb_instance_name = module_manager.instance_name(top_module, sb_module, instance_id);
|
||||
/* Disable the outputs of the module */
|
||||
for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||
fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl;
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to print a number of SDC files in different purpose
|
||||
* This function will generate files upon the options provided by users
|
||||
* 1. Design constraints for CLBs
|
||||
* 2. Design constraints for Switch Blocks
|
||||
* 3. Design constraints for Connection Blocks
|
||||
* 4. Design constraints for breaking the combinational loops in FPGA fabric
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc(const SdcOption& sdc_options,
|
||||
const float& critical_path_delay,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
|
||||
/* Constrain global ports */
|
||||
if (true == sdc_options.constrain_global_port()) {
|
||||
print_pnr_sdc_global_ports(sdc_options.sdc_dir(), critical_path_delay, circuit_lib, global_ports);
|
||||
}
|
||||
|
||||
std::string top_module_name = generate_fpga_top_module_name();
|
||||
ModuleId top_module = module_manager.find_module(top_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||
|
||||
/* Output Design Constraints to disable outputs of memory cells */
|
||||
if (true == sdc_options.constrain_configurable_memory_outputs()) {
|
||||
print_pnr_sdc_constrain_configurable_memory_outputs(sdc_options.sdc_dir(), module_manager, top_module);
|
||||
}
|
||||
|
||||
/* Break loops from Multiplexer Output */
|
||||
if (true == sdc_options.constrain_routing_multiplexer_outputs()) {
|
||||
print_sdc_disable_routing_multiplexer_outputs(sdc_options.sdc_dir(),
|
||||
mux_lib, circuit_lib,
|
||||
module_manager);
|
||||
}
|
||||
|
||||
/* Break loops from any SB output */
|
||||
if (true == sdc_options.constrain_switch_block_outputs()) {
|
||||
if (true == compact_routing_hierarchy) {
|
||||
print_pnr_sdc_compact_routing_disable_switch_block_outputs(sdc_options.sdc_dir(),
|
||||
module_manager, top_module,
|
||||
L_device_rr_gsb);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||
print_pnr_sdc_flatten_routing_disable_switch_block_outputs(sdc_options.sdc_dir(),
|
||||
module_manager,
|
||||
L_device_rr_gsb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Output routing constraints for Switch Blocks */
|
||||
if (true == sdc_options.constrain_sb()) {
|
||||
if (true == compact_routing_hierarchy) {
|
||||
print_pnr_sdc_compact_routing_constrain_sb_timing(sdc_options.sdc_dir(),
|
||||
module_manager,
|
||||
grids, switches,
|
||||
L_device_rr_gsb);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||
print_pnr_sdc_flatten_routing_constrain_sb_timing(sdc_options.sdc_dir(),
|
||||
module_manager,
|
||||
grids, switches,
|
||||
L_device_rr_gsb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Output routing constraints for Connection Blocks */
|
||||
if (true == sdc_options.constrain_cb()) {
|
||||
if (true == compact_routing_hierarchy) {
|
||||
print_pnr_sdc_compact_routing_constrain_cb_timing(sdc_options.sdc_dir(),
|
||||
module_manager,
|
||||
grids,
|
||||
switches,
|
||||
L_device_rr_gsb);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_options.sdc_dir(),
|
||||
module_manager,
|
||||
L_device_rr_gsb,
|
||||
grids,
|
||||
switches);
|
||||
}
|
||||
}
|
||||
|
||||
/* Output Timing constraints for Programmable blocks */
|
||||
if (true == sdc_options.constrain_grid()) {
|
||||
print_pnr_sdc_constrain_grid_timing(sdc_options.sdc_dir(),
|
||||
module_manager);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef PNR_SDC_WRITER_H
|
||||
#define PNR_SDC_WRITER_H
|
||||
|
||||
#include <string>
|
||||
#include "vtr_geometry.h"
|
||||
#include "vpr_types.h"
|
||||
#include "rr_blocks.h"
|
||||
#include "module_manager.h"
|
||||
#include "mux_library.h"
|
||||
#include "circuit_library.h"
|
||||
#include "sdc_option.h"
|
||||
|
||||
void print_pnr_sdc(const SdcOption& sdc_options,
|
||||
const float& critical_path_delay,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
/********************************************************************
|
||||
* Useful APIs for SDC generator
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include "pnr_sdc_writer.h"
|
||||
#include "analysis_sdc_writer.h"
|
||||
|
||||
#include "sdc_api.h"
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to launch SDC generator
|
||||
*******************************************************************/
|
||||
void fpga_sdc_generator(const SdcOption& sdc_options,
|
||||
const float& critical_path_delay,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<t_logical_block>& L_logical_blocks,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"SDC generator starts...\n");
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
if (true == sdc_options.generate_sdc_pnr()) {
|
||||
print_pnr_sdc(sdc_options, critical_path_delay,
|
||||
grids, rr_switches, L_device_rr_gsb,
|
||||
module_manager, mux_lib,
|
||||
circuit_lib, global_ports,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
|
||||
if (true == sdc_options.generate_sdc_analysis()) {
|
||||
print_analysis_sdc(sdc_options.sdc_dir(),
|
||||
critical_path_delay,
|
||||
L_device_rr_gsb,
|
||||
L_logical_blocks, device_size, L_grids,
|
||||
L_blocks,
|
||||
module_manager,
|
||||
circuit_lib, global_ports,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"SDC generation took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef SDC_API_H
|
||||
#define SDC_API_H
|
||||
|
||||
#include <vector>
|
||||
#include "sdc_option.h"
|
||||
#include "circuit_library.h"
|
||||
#include "mux_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
void fpga_sdc_generator(const SdcOption& sdc_options,
|
||||
const float& critical_path_delay,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_switch_inf>& rr_switches,
|
||||
const DeviceRRGSB& L_device_rr_gsb,
|
||||
const std::vector<t_logical_block>& L_logical_blocks,
|
||||
const vtr::Point<size_t>& device_size,
|
||||
const std::vector<std::vector<t_grid_tile>>& L_grids,
|
||||
const std::vector<t_block>& L_blocks,
|
||||
const ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
/********************************************************************
|
||||
* Most utilized function used to constrain memory cells in FPGA
|
||||
* fabric using SDC commands
|
||||
*******************************************************************/
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
|
||||
#include "sdc_memory_utils.h"
|
||||
|
||||
/********************************************************************
|
||||
* Print SDC commands to disable outputs of all the configurable memory modules
|
||||
* in a given module
|
||||
* This function will be executed in a recursive way,
|
||||
* using a Depth-First Search (DFS) strategy
|
||||
* It will iterate over all the configurable children under each module
|
||||
* and print a SDC command to disable its outputs
|
||||
*******************************************************************/
|
||||
void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_module_path) {
|
||||
|
||||
/* For each configurable child, we will go one level down in priority */
|
||||
for (size_t child_index = 0; child_index < module_manager.configurable_children(parent_module).size(); ++child_index) {
|
||||
std::string child_module_path = parent_module_path;
|
||||
ModuleId child_module_id = module_manager.configurable_children(parent_module)[child_index];
|
||||
size_t child_instance_id = module_manager.configurable_child_instances(parent_module)[child_index];
|
||||
if (true == module_manager.instance_name(parent_module, child_module_id, child_instance_id).empty()) {
|
||||
/* Give a default name <module_name>_<instance_id>_ */
|
||||
child_module_path += module_manager.module_name(child_module_id);
|
||||
child_module_path += "_";
|
||||
child_module_path += std::to_string(child_instance_id);
|
||||
child_module_path += "_";
|
||||
} else {
|
||||
child_module_path += module_manager.instance_name(parent_module, child_module_id, child_instance_id);
|
||||
}
|
||||
child_module_path = format_dir_path(child_module_path);
|
||||
|
||||
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager,
|
||||
child_module_id,
|
||||
child_module_path);
|
||||
}
|
||||
|
||||
/* If there is no configurable children any more, this is a leaf module, print a SDC command for disable timing */
|
||||
if (0 < module_manager.configurable_children(parent_module).size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Disable timing for each output port of this module */
|
||||
for (const BasicPort& output_port : module_manager.module_ports_by_type(parent_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||
for (const size_t& pin : output_port.pins()) {
|
||||
BasicPort output_pin(output_port.get_name(), pin, pin);
|
||||
fp << "set_disable_timing ";
|
||||
fp << parent_module_path << generate_sdc_port(output_pin);
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SDC_MEMORY_UTILS_H
|
||||
#define SDC_MEMORY_UTILS_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "module_manager.h"
|
||||
|
||||
void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& parent_module_path);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,121 @@
|
|||
/********************************************************************
|
||||
* Member functions for a data structure which includes all the options for the SDC generator
|
||||
********************************************************************/
|
||||
#include "sdc_option.h"
|
||||
|
||||
/********************************************************************
|
||||
* Public Constructors
|
||||
********************************************************************/
|
||||
SdcOption::SdcOption(const std::string& sdc_dir) {
|
||||
sdc_dir_ = sdc_dir;
|
||||
constrain_global_port_ = false;
|
||||
constrain_grid_ = false;
|
||||
constrain_sb_ = false;
|
||||
constrain_cb_ = false;
|
||||
constrain_configurable_memory_outputs_ = false;
|
||||
constrain_routing_multiplexer_outputs_ = false;
|
||||
constrain_switch_block_outputs_ = false;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Public accessors
|
||||
********************************************************************/
|
||||
std::string SdcOption::sdc_dir() const {
|
||||
return sdc_dir_;
|
||||
}
|
||||
|
||||
bool SdcOption::generate_sdc() const {
|
||||
return generate_sdc_pnr() && generate_sdc_analysis_;
|
||||
}
|
||||
|
||||
bool SdcOption::generate_sdc_pnr() const {
|
||||
return constrain_global_port_
|
||||
|| constrain_grid_
|
||||
|| constrain_sb_
|
||||
|| constrain_cb_
|
||||
|| constrain_configurable_memory_outputs_
|
||||
|| constrain_routing_multiplexer_outputs_
|
||||
|| constrain_switch_block_outputs_;
|
||||
}
|
||||
|
||||
bool SdcOption::generate_sdc_analysis() const {
|
||||
return generate_sdc_analysis_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_global_port() const {
|
||||
return constrain_global_port_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_grid() const {
|
||||
return constrain_grid_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_sb() const {
|
||||
return constrain_sb_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_cb() const {
|
||||
return constrain_cb_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_configurable_memory_outputs() const {
|
||||
return constrain_configurable_memory_outputs_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_routing_multiplexer_outputs() const {
|
||||
return constrain_routing_multiplexer_outputs_;
|
||||
}
|
||||
|
||||
bool SdcOption::constrain_switch_block_outputs() const {
|
||||
return constrain_switch_block_outputs_;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Public mutators
|
||||
********************************************************************/
|
||||
void SdcOption::set_sdc_dir(const std::string& sdc_dir) {
|
||||
sdc_dir_ = sdc_dir;
|
||||
}
|
||||
|
||||
void SdcOption::set_generate_sdc_pnr(const bool& generate_sdc_pnr) {
|
||||
constrain_global_port_ = generate_sdc_pnr;
|
||||
constrain_grid_ = generate_sdc_pnr;
|
||||
constrain_sb_ = generate_sdc_pnr;
|
||||
constrain_cb_ = generate_sdc_pnr;
|
||||
constrain_configurable_memory_outputs_ = generate_sdc_pnr;
|
||||
constrain_routing_multiplexer_outputs_ = generate_sdc_pnr;
|
||||
constrain_switch_block_outputs_ = generate_sdc_pnr;
|
||||
}
|
||||
|
||||
void SdcOption::set_generate_sdc_analysis(const bool& generate_sdc_analysis) {
|
||||
generate_sdc_analysis_ = generate_sdc_analysis;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_global_port(const bool& constrain_global_port) {
|
||||
constrain_global_port_ = constrain_global_port;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_grid(const bool& constrain_grid) {
|
||||
constrain_grid_ = constrain_grid;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_sb(const bool& constrain_sb) {
|
||||
constrain_sb_ = constrain_sb;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_cb(const bool& constrain_cb) {
|
||||
constrain_cb_ = constrain_cb;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs) {
|
||||
constrain_configurable_memory_outputs_ = constrain_config_mem_outputs;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs) {
|
||||
constrain_routing_multiplexer_outputs_ = constrain_routing_mux_outputs;
|
||||
}
|
||||
|
||||
void SdcOption::set_constrain_switch_block_outputs(const bool& constrain_sb_outputs) {
|
||||
constrain_switch_block_outputs_ = constrain_sb_outputs;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef SDC_OPTION_H
|
||||
#define SDC_OPTION_H
|
||||
|
||||
/********************************************************************
|
||||
* A data structure to include all the options for the SDC generator
|
||||
********************************************************************/
|
||||
|
||||
#include <string>
|
||||
|
||||
class SdcOption {
|
||||
public: /* Public Constructors */
|
||||
SdcOption(const std::string& sdc_dir);
|
||||
public: /* Public accessors */
|
||||
std::string sdc_dir() const;
|
||||
bool generate_sdc() const;
|
||||
bool generate_sdc_pnr() const;
|
||||
bool generate_sdc_analysis() const;
|
||||
bool constrain_global_port() const;
|
||||
bool constrain_grid() const;
|
||||
bool constrain_sb() const;
|
||||
bool constrain_cb() const;
|
||||
bool constrain_configurable_memory_outputs() const;
|
||||
bool constrain_routing_multiplexer_outputs() const;
|
||||
bool constrain_switch_block_outputs() const;
|
||||
public: /* Public mutators */
|
||||
void set_sdc_dir(const std::string& sdc_dir);
|
||||
void set_generate_sdc_pnr(const bool& generate_sdc_pnr);
|
||||
void set_generate_sdc_analysis(const bool& generate_sdc_analysis);
|
||||
void set_constrain_global_port(const bool& constrain_global_port);
|
||||
void set_constrain_grid(const bool& constrain_grid);
|
||||
void set_constrain_sb(const bool& constrain_sb);
|
||||
void set_constrain_cb(const bool& constrain_cb);
|
||||
void set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs);
|
||||
void set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs);
|
||||
void set_constrain_switch_block_outputs(const bool& constrain_sb_outputs);
|
||||
private: /* Internal data */
|
||||
std::string sdc_dir_;
|
||||
bool constrain_global_port_;
|
||||
bool constrain_grid_;
|
||||
bool constrain_sb_;
|
||||
bool constrain_cb_;
|
||||
bool constrain_configurable_memory_outputs_;
|
||||
bool constrain_routing_multiplexer_outputs_;
|
||||
bool constrain_switch_block_outputs_;
|
||||
bool generate_sdc_analysis_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef SDC_WRITER_NAMING_H
|
||||
#define SDC_WRITER_NAMING_H
|
||||
|
||||
constexpr char* SDC_FILE_NAME_POSTFIX = ".sdc";
|
||||
|
||||
constexpr char* SDC_GLOBAL_PORTS_FILE_NAME = "global_ports.sdc";
|
||||
constexpr char* SDC_BENCHMARK_ANALYSIS_FILE_NAME= "fpga_top_analysis.sdc";
|
||||
constexpr char* SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME = "disable_configurable_memory_outputs.sdc";
|
||||
constexpr char* SDC_DISABLE_MUX_OUTPUTS_FILE_NAME = "disable_routing_multiplexer_outputs.sdc";
|
||||
constexpr char* SDC_DISABLE_SB_OUTPUTS_FILE_NAME = "disable_sb_outputs.sdc";
|
||||
constexpr char* SDC_CB_FILE_NAME = "cb.sdc";
|
||||
|
||||
constexpr char* SDC_ANALYSIS_FILE_NAME = "fpga_top_analysis.sdc";
|
||||
|
||||
#endif
|
|
@ -0,0 +1,172 @@
|
|||
/********************************************************************
|
||||
* This file include most utilized functions to be used in SDC writers
|
||||
*******************************************************************/
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
|
||||
/********************************************************************
|
||||
* Write a head (description) in SDC file
|
||||
*******************************************************************/
|
||||
void print_sdc_file_header(std::fstream& fp,
|
||||
const std::string& usage) {
|
||||
|
||||
check_file_handler(fp);
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
|
||||
|
||||
fp << "#############################################" << std::endl;
|
||||
fp << "#\tSynopsys Design Constraints (SDC)" << std::endl;
|
||||
fp << "#\tFor FPGA fabric " << std::endl;
|
||||
fp << "#\tDescription: " << usage << std::endl;
|
||||
fp << "#\tAuthor: Xifan TANG " << std::endl;
|
||||
fp << "#\tOrganization: University of Utah " << std::endl;
|
||||
fp << "#\tDate: " << std::ctime(&end_time);
|
||||
fp << "#############################################" << std::endl;
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Write a port in SDC format
|
||||
*******************************************************************/
|
||||
std::string generate_sdc_port(const BasicPort& port) {
|
||||
std::string sdc_line;
|
||||
|
||||
std::string size_str = "[" + std::to_string(port.get_lsb()) + ":" + std::to_string(port.get_msb()) + "]";
|
||||
|
||||
/* Only connection require a format of <port_name>[<lsb>:<msb>]
|
||||
* others require a format of <port_type> [<lsb>:<msb>] <port_name>
|
||||
*/
|
||||
/* When LSB == MSB, we can use a simplified format <port_type>[<lsb>]*/
|
||||
if ( 1 == port.get_width()) {
|
||||
size_str = "[" + std::to_string(port.get_lsb()) + "]";
|
||||
}
|
||||
sdc_line = port.get_name() + size_str;
|
||||
|
||||
return sdc_line;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Constrain a path between two ports of a module with a given maximum timing value
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_constrain_max_delay(std::fstream& fp,
|
||||
const std::string& src_instance_name,
|
||||
const std::string& src_port_name,
|
||||
const std::string& des_instance_name,
|
||||
const std::string& des_port_name,
|
||||
const float& delay) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
fp << "set_max_delay";
|
||||
|
||||
fp << " -from ";
|
||||
fp << src_instance_name << "/";
|
||||
fp << src_port_name;
|
||||
|
||||
fp << " -to ";
|
||||
|
||||
fp << des_instance_name << "/";
|
||||
fp << des_port_name;
|
||||
|
||||
fp << " " << std::setprecision(10) << delay;
|
||||
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Constrain a path between two ports of a module with a given timing value
|
||||
* Note: this function uses set_max_delay !!!
|
||||
*******************************************************************/
|
||||
void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& input_parent_module_id,
|
||||
const ModulePortId& module_input_port_id,
|
||||
const ModuleId& output_parent_module_id,
|
||||
const ModulePortId& module_output_port_id,
|
||||
const float& tmax) {
|
||||
print_pnr_sdc_constrain_max_delay(fp,
|
||||
module_manager.module_name(input_parent_module_id),
|
||||
generate_sdc_port(module_manager.module_port(input_parent_module_id, module_input_port_id)),
|
||||
module_manager.module_name(output_parent_module_id),
|
||||
generate_sdc_port(module_manager.module_port(output_parent_module_id, module_output_port_id)),
|
||||
tmax);
|
||||
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable timing for a port
|
||||
*******************************************************************/
|
||||
void print_sdc_disable_port_timing(std::fstream& fp,
|
||||
const BasicPort& port) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
|
||||
fp << generate_sdc_port(port);
|
||||
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Set the input delay for a port in SDC format
|
||||
* Note that the input delay will be bounded by a clock port
|
||||
*******************************************************************/
|
||||
void print_sdc_set_port_input_delay(std::fstream& fp,
|
||||
const BasicPort& port,
|
||||
const BasicPort& clock_port,
|
||||
const float& delay) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
fp << "set_input_delay ";
|
||||
|
||||
fp << "-clock ";
|
||||
|
||||
fp << generate_sdc_port(clock_port);
|
||||
|
||||
fp << " -max ";
|
||||
|
||||
fp << std::setprecision(10) << delay;
|
||||
|
||||
fp << " ";
|
||||
|
||||
fp << generate_sdc_port(port);
|
||||
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Set the output delay for a port in SDC format
|
||||
* Note that the output delay will be bounded by a clock port
|
||||
*******************************************************************/
|
||||
void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||
const BasicPort& port,
|
||||
const BasicPort& clock_port,
|
||||
const float& delay) {
|
||||
/* Validate file stream */
|
||||
check_file_handler(fp);
|
||||
|
||||
fp << "set_output_delay ";
|
||||
|
||||
fp << "-clock ";
|
||||
|
||||
fp << generate_sdc_port(clock_port);
|
||||
|
||||
fp << " -max ";
|
||||
|
||||
fp << std::setprecision(10) << delay;
|
||||
|
||||
fp << " ";
|
||||
|
||||
fp << generate_sdc_port(port);
|
||||
|
||||
fp << std::endl;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef SDC_WRITER_UTILS_H
|
||||
#define SDC_WRITER_UTILS_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "device_port.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
void print_sdc_file_header(std::fstream& fp,
|
||||
const std::string& usage);
|
||||
|
||||
std::string generate_sdc_port(const BasicPort& port);
|
||||
|
||||
void print_pnr_sdc_constrain_max_delay(std::fstream& fp,
|
||||
const std::string& src_instance_name,
|
||||
const std::string& src_port_name,
|
||||
const std::string& des_instance_name,
|
||||
const std::string& des_port_name,
|
||||
const float& delay);
|
||||
|
||||
void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& input_parent_module_id,
|
||||
const ModulePortId& module_input_port_id,
|
||||
const ModuleId& output_parent_module_id,
|
||||
const ModulePortId& module_output_port_id,
|
||||
const float& tmax);
|
||||
|
||||
void print_sdc_disable_port_timing(std::fstream& fp,
|
||||
const BasicPort& port);
|
||||
|
||||
void print_sdc_set_port_input_delay(std::fstream& fp,
|
||||
const BasicPort& port,
|
||||
const BasicPort& clock_port,
|
||||
const float& delay);
|
||||
|
||||
void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||
const BasicPort& port,
|
||||
const BasicPort& clock_port,
|
||||
const float& delay);
|
||||
|
||||
#endif
|
|
@ -24,9 +24,11 @@
|
|||
|
||||
/* Include spice support headers*/
|
||||
#include "linkedlist.h"
|
||||
#include "circuit_library_utils.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_backannotate_utils.h"
|
||||
#include "fpga_x2p_setup.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
||||
#include "mux_library_builder.h"
|
||||
#include "build_device_module.h"
|
||||
|
@ -36,6 +38,7 @@
|
|||
|
||||
#include "spice_api.h"
|
||||
#include "verilog_api.h"
|
||||
#include "sdc_api.h"
|
||||
#include "fpga_bitstream.h"
|
||||
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
|
@ -139,6 +142,30 @@ void vpr_fpga_x2p_tool_suites(t_vpr_setup vpr_setup,
|
|||
vpr_setup, Arch, vpr_setup.FileNameOpts.CircuitName);
|
||||
}
|
||||
|
||||
|
||||
/* Run SDC Generator */
|
||||
std::string src_dir = find_path_dir_name(std::string(vpr_setup.FileNameOpts.CircuitName));
|
||||
|
||||
/* Use current directory if there is not dir path given */
|
||||
if (false == src_dir.empty()) {
|
||||
src_dir = format_dir_path(src_dir);
|
||||
}
|
||||
SdcOption sdc_options(format_dir_path(src_dir + std::string(FPGA_X2P_DEFAULT_SDC_DIR)));
|
||||
sdc_options.set_generate_sdc_pnr(TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_sdc_pnr);
|
||||
sdc_options.set_generate_sdc_analysis(TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_sdc_analysis);
|
||||
|
||||
if (true == sdc_options.generate_sdc()) {
|
||||
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(Arch.spice->circuit_lib);
|
||||
/* TODO: the critical path delay unit should be explicit! */
|
||||
fpga_sdc_generator(sdc_options,
|
||||
Arch.spice->spice_params.stimulate_params.vpr_crit_path_delay / 1e-9,
|
||||
grids, rr_switches, device_rr_gsb,
|
||||
L_logical_blocks, device_size, grids, L_blocks,
|
||||
module_manager, mux_lib,
|
||||
Arch.spice->circuit_lib, global_ports,
|
||||
TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy);
|
||||
}
|
||||
|
||||
/* Xifan Tang: Bitstream Generator */
|
||||
if ((TRUE == vpr_setup.FPGA_SPICE_Opts.BitstreamGenOpts.gen_bitstream)
|
||||
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice)
|
||||
|
|
|
@ -1480,6 +1480,10 @@ void update_one_grid_pack_net_num(int x, int y) {
|
|||
assert ((NULL != type)
|
||||
&& (EMPTY_TYPE != type)
|
||||
&& (IO_TYPE != type));
|
||||
/* Bypass grids whose offset is larger than 0 ! They have been processed! */
|
||||
if (0 < grid[x][y].offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (iblk = 0; iblk < grid[x][y].usage; iblk++) {
|
||||
blk_id = grid[x][y].blocks[iblk];
|
||||
|
|
|
@ -758,6 +758,25 @@ std::string generate_grid_block_module_name(const std::string& prefix,
|
|||
return module_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a programmable routing multiplexer module
|
||||
* in a Switch Block
|
||||
* To keep a unique name in each module and also consider unique routing modules,
|
||||
* please do NOT include any coordinates in the naming!!!
|
||||
* Consider only relative coordinate, such as side!
|
||||
********************************************************************/
|
||||
std::string generate_sb_mux_instance_name(const std::string& prefix,
|
||||
const e_side& sb_side,
|
||||
const size_t& track_id,
|
||||
const std::string& postfix) {
|
||||
std::string instance_name(prefix);
|
||||
instance_name += Side(sb_side).to_string();
|
||||
instance_name += std::string("_track_") + std::to_string(track_id);
|
||||
instance_name += postfix;
|
||||
|
||||
return instance_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a configurable memory module in a Switch Block
|
||||
* To keep a unique name in each module and also consider unique routing modules,
|
||||
|
@ -776,6 +795,26 @@ std::string generate_sb_memory_instance_name(const std::string& prefix,
|
|||
return instance_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a programmable routing multiplexer module
|
||||
* in a Connection Block
|
||||
* To keep a unique name in each module and also consider unique routing modules,
|
||||
* please do NOT include any coordinates in the naming!!!
|
||||
* Consider only relative coordinate, such as side!
|
||||
********************************************************************/
|
||||
std::string generate_cb_mux_instance_name(const std::string& prefix,
|
||||
const e_side& cb_side,
|
||||
const size_t& pin_id,
|
||||
const std::string& postfix) {
|
||||
std::string instance_name(prefix);
|
||||
|
||||
instance_name += Side(cb_side).to_string();
|
||||
instance_name += std::string("_ipin_") + std::to_string(pin_id);
|
||||
instance_name += postfix;
|
||||
|
||||
return instance_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a configurable memory module in a Connection Block
|
||||
* To keep a unique name in each module and also consider unique routing modules,
|
||||
|
@ -795,6 +834,38 @@ std::string generate_cb_memory_instance_name(const std::string& prefix,
|
|||
return instance_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a programmable routing multiplexer
|
||||
* module in a physical block of a grid
|
||||
* To guarentee a unique name for pb_graph pin,
|
||||
* the instance name includes the index of parent node
|
||||
* as well as the port name and pin index of this pin
|
||||
*
|
||||
* Exceptions:
|
||||
* For OUTPUT ports, due to hierarchical module organization,
|
||||
* their parent nodes will be uniquified
|
||||
* So, we should not add any index here
|
||||
********************************************************************/
|
||||
std::string generate_pb_mux_instance_name(const std::string& prefix,
|
||||
t_pb_graph_pin* pb_graph_pin,
|
||||
const std::string& postfix) {
|
||||
std::string instance_name(prefix);
|
||||
instance_name += std::string(pb_graph_pin->parent_node->pb_type->name);
|
||||
|
||||
if (IN_PORT == pb_graph_pin->port->type) {
|
||||
instance_name += std::string("_");
|
||||
instance_name += std::to_string(pb_graph_pin->parent_node->placement_index);
|
||||
}
|
||||
|
||||
instance_name += std::string("_");
|
||||
instance_name += std::string(pb_graph_pin->port->name);
|
||||
instance_name += std::string("_");
|
||||
instance_name += std::to_string(pb_graph_pin->pin_number);
|
||||
instance_name += postfix;
|
||||
|
||||
return instance_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the instance name for a configurable memory module in a
|
||||
* physical block of a grid
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "circuit_library.h"
|
||||
#include "vpr_types.h"
|
||||
|
||||
constexpr char* FPGA_X2P_DEFAULT_SDC_DIR = "SDC";
|
||||
|
||||
std::string generate_mux_node_name(const size_t& node_level,
|
||||
const bool& add_buffer_postfix);
|
||||
|
||||
|
@ -76,16 +78,30 @@ std::string generate_switch_block_module_name(const vtr::Point<size_t>& coordina
|
|||
std::string generate_connection_block_module_name(const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coordinate);
|
||||
|
||||
std::string generate_sb_mux_instance_name(const std::string& prefix,
|
||||
const e_side& sb_side,
|
||||
const size_t& track_id,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_sb_memory_instance_name(const std::string& prefix,
|
||||
const e_side& sb_side,
|
||||
const size_t& track_id,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_cb_mux_instance_name(const std::string& prefix,
|
||||
const e_side& cb_side,
|
||||
const size_t& pin_id,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_cb_memory_instance_name(const std::string& prefix,
|
||||
const e_side& cb_side,
|
||||
const size_t& pin_id,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_pb_mux_instance_name(const std::string& prefix,
|
||||
t_pb_graph_pin* pb_graph_pin,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_pb_memory_instance_name(const std::string& prefix,
|
||||
t_pb_graph_pin* pb_graph_pin,
|
||||
const std::string& postfix);
|
||||
|
|
|
@ -16,6 +16,10 @@ constexpr char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
|
|||
constexpr char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
|
||||
constexpr char* MEMORY_MODULE_POSTFIX = "_mem";
|
||||
|
||||
/* Multiplexer naming constant strings */
|
||||
constexpr char* GRID_MUX_INSTANCE_PREFIX = "mux_";
|
||||
constexpr char* SWITCH_BLOCK_MUX_INSTANCE_PREFIX = "mux_";
|
||||
constexpr char* CONNECTION_BLOCK_MUX_INSTANCE_PREFIX = "mux_";
|
||||
|
||||
/* Bitstream file strings */
|
||||
constexpr char* BITSTREAM_XML_FILE_NAME_POSTFIX = "_bitstream.xml";
|
||||
|
|
|
@ -1119,6 +1119,22 @@ RRGSB build_rr_gsb(DeviceCoordinator& device_range,
|
|||
if (0 == rr_gsb.get_chan_width(chan_side)) {
|
||||
continue;
|
||||
}
|
||||
/* For bottom side: Skip IPIN collection if the offset of the grid is not zero!
|
||||
* (it means this CB is in the middle of a grid (whose height > 1)
|
||||
*
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | Grid | | Grid |
|
||||
* +------------+ | |
|
||||
* IPIN nodes IPIN nodes
|
||||
* exist do NOT exist
|
||||
*/
|
||||
if ((BOTTOM == ipin_rr_node_grid_side) && (0 < grid[ix][iy].offset)) {
|
||||
continue;
|
||||
}
|
||||
if ((TOP == ipin_rr_node_grid_side) && (grid[ix][iy].offset != grid[ix][iy].type->height - 1)) {
|
||||
continue;
|
||||
}
|
||||
/* Collect IPIN rr_nodes*/
|
||||
temp_ipin_rr_node = get_grid_side_pin_rr_nodes(&(num_temp_ipin_rr_nodes),
|
||||
IPIN, ix, iy, ipin_rr_node_grid_side,
|
||||
|
|
|
@ -85,26 +85,6 @@ char* format_dir_path(char* dir_path) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Format a path of directory:
|
||||
* 1. Replace "\" with "/"
|
||||
* 2. add a "/" if the string does not end with a "/"
|
||||
***********************************************/
|
||||
std::string format_dir_path(const std::string& dir_path) {
|
||||
std::string ret = dir_path;
|
||||
|
||||
/* Replace "\" with "/" */
|
||||
std::replace(ret.begin(), ret.end(), '\\', '/');
|
||||
|
||||
/* Complete the string with a "/" if it does not end with that */
|
||||
if ('/' != ret.back()) {
|
||||
ret.push_back('/');
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int try_access_file(char* file_path) {
|
||||
/* F_OK checks existence and also R_OK, W_OK, X_OK,
|
||||
* for readable, writable, excutable
|
||||
|
@ -251,18 +231,6 @@ char* chomp_file_name_postfix(char* file_name) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void check_file_handler(std::fstream& fp) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
if (!fp.is_open() || !fp.good()) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(FILE:%s,LINE[%d])Failure in create file!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Print SRAM bits, typically in a comment line */
|
||||
void fprint_commented_sram_bits(FILE* fp,
|
||||
int num_sram_bits, int* sram_bits) {
|
||||
|
@ -633,21 +601,6 @@ char* my_ito1hot(int in_int, int bin_len) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Convert an integer to an one-hot encoding integer array */
|
||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len) {
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int <= bin_len) );
|
||||
|
||||
/* Initialize */
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
if (bin_len == in_int) {
|
||||
return ret; /* all zero case */
|
||||
}
|
||||
ret[in_int] = 1; /* Keep a good sequence of bits */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Converter an integer to a binary string */
|
||||
int* my_itobin_int(int in_int, int bin_len) {
|
||||
|
@ -669,24 +622,6 @@ int* my_itobin_int(int in_int, int bin_len) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Converter an integer to a binary vector */
|
||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int < pow(2., bin_len)) );
|
||||
|
||||
size_t temp = in_int;
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
if (1 == temp % 2) {
|
||||
ret[i] = 1; /* Keep a good sequence of bits */
|
||||
}
|
||||
temp = temp / 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Converter an integer to a binary string */
|
||||
char* my_itobin(int in_int, int bin_len) {
|
||||
char* ret = (char*) my_calloc (bin_len + 1, sizeof(char));
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/********************************************************************
|
||||
* Most utilized functions in FPGA X2P framework
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
||||
/********************************************************************
|
||||
* Format a directory path:
|
||||
* 1. Replace "\" with "/"
|
||||
* 2. add a "/" if the string does not end with a "/"
|
||||
*******************************************************************/
|
||||
std::string format_dir_path(const std::string& dir_path_to_format) {
|
||||
std::string formatted_dir_path = dir_path_to_format;
|
||||
|
||||
char illegal_back_slash = '\\';
|
||||
char legal_back_slash = '/';
|
||||
|
||||
#ifdef _WIN32
|
||||
/* For windows OS, replace any '/' with '\' */
|
||||
char illegal_back_slash = '/';
|
||||
char legal_back_slash = '\\';
|
||||
#endif
|
||||
|
||||
/* Replace "\" with "/" */
|
||||
std::replace(formatted_dir_path.begin(), formatted_dir_path.end(), illegal_back_slash, legal_back_slash);
|
||||
|
||||
/* Add a back slash the string is not ended like this! */
|
||||
if (legal_back_slash != formatted_dir_path.back()) {
|
||||
formatted_dir_path.push_back(legal_back_slash);
|
||||
}
|
||||
|
||||
return formatted_dir_path;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Extract full file name from a full path of file
|
||||
* For example: <dir_path>/<file_name>
|
||||
* This function will return <file_name>
|
||||
********************************************************************/
|
||||
std::string find_path_file_name(const std::string& file_name) {
|
||||
|
||||
char back_slash = '/';
|
||||
|
||||
#ifdef _WIN32
|
||||
/* For windows OS, replace any '/' with '\' */
|
||||
char back_slash = '\\';
|
||||
#endif
|
||||
|
||||
/* Find the last '/' in the string and return the left part */
|
||||
size_t found = file_name.rfind(back_slash);
|
||||
if (found != std::string::npos) {
|
||||
return file_name.substr(found + 1);
|
||||
}
|
||||
/* Not found, return an empty string */
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Extract full directory path from a full path of file
|
||||
* For example: <dir_path>/<file_name>
|
||||
* This function will return <dir_path>
|
||||
********************************************************************/
|
||||
std::string find_path_dir_name(const std::string& file_name) {
|
||||
|
||||
char back_slash = '/';
|
||||
|
||||
#ifdef _WIN32
|
||||
/* For windows OS, replace any '/' with '\' */
|
||||
char back_slash = '\\';
|
||||
#endif
|
||||
|
||||
/* Find the last '/' in the string and return the left part */
|
||||
size_t found = file_name.rfind(back_slash);
|
||||
if (found != std::string::npos) {
|
||||
return file_name.substr(0, found);
|
||||
}
|
||||
/* Not found, return an empty string */
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Check if the file stream is valid
|
||||
********************************************************************/
|
||||
void check_file_handler(std::fstream& fp) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
if (!fp.is_open() || !fp.good()) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(FILE:%s,LINE[%d])Failure in create file!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Convert an integer to an one-hot encoding integer array
|
||||
********************************************************************/
|
||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len) {
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int <= bin_len) );
|
||||
|
||||
/* Initialize */
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
if (bin_len == in_int) {
|
||||
return ret; /* all zero case */
|
||||
}
|
||||
ret[in_int] = 1; /* Keep a good sequence of bits */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Converter an integer to a binary vector
|
||||
********************************************************************/
|
||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int < pow(2., bin_len)) );
|
||||
|
||||
size_t temp = in_int;
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
if (1 == temp % 2) {
|
||||
ret[i] = 1; /* Keep a good sequence of bits */
|
||||
}
|
||||
temp = temp / 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -3,16 +3,28 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "my_free_fwd.h"
|
||||
#include "rr_blocks_naming.h"
|
||||
|
||||
std::string format_dir_path(const std::string& dir_path_to_format);
|
||||
|
||||
void check_file_handler(std::fstream& fp);
|
||||
|
||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len);
|
||||
|
||||
std::string find_path_dir_name(const std::string& file_name);
|
||||
|
||||
std::string find_path_file_name(const std::string& file_name);
|
||||
|
||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len);
|
||||
|
||||
/* Old functions */
|
||||
|
||||
char* my_gettime();
|
||||
|
||||
char* format_dir_path(char* dir_path); /* TODO: TO BE REMOVED !!! */
|
||||
std::string format_dir_path(const std::string& dir_path);
|
||||
|
||||
int try_access_file(char* file_path);
|
||||
|
||||
|
@ -66,14 +78,10 @@ t_spice_transistor_type* find_mosfet_tech_lib(t_spice_tech_lib tech_lib,
|
|||
|
||||
char* my_ito1hot(int in_int, int bin_len);
|
||||
|
||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len);
|
||||
|
||||
char* my_itobin(int in_int, int bin_len);
|
||||
|
||||
int* my_itobin_int(int in_int, int bin_len);
|
||||
|
||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len);
|
||||
|
||||
char* my_itoa(int input);
|
||||
|
||||
char* fpga_spice_create_one_subckt_filename(const char* file_name_prefix,
|
||||
|
|
|
@ -462,6 +462,15 @@ ModulePortId ModuleManager::add_port(const ModuleId& module,
|
|||
return port;
|
||||
}
|
||||
|
||||
/* Set a name for a module port */
|
||||
void ModuleManager::set_module_port_name(const ModuleId& module, const ModulePortId& module_port,
|
||||
const std::string& port_name) {
|
||||
/* Validate the id of module port */
|
||||
VTR_ASSERT( valid_module_port_id(module, module_port) );
|
||||
|
||||
ports_[module][module_port].set_name(port_name);
|
||||
}
|
||||
|
||||
/* Set a name for a module */
|
||||
void ModuleManager::set_module_name(const ModuleId& module, const std::string& name) {
|
||||
/* Validate the id of module */
|
||||
|
|
|
@ -127,6 +127,8 @@ class ModuleManager {
|
|||
/* Add a port to a module */
|
||||
ModulePortId add_port(const ModuleId& module,
|
||||
const BasicPort& port_info, const enum e_module_port_type& port_type);
|
||||
/* Set a name for a module port */
|
||||
void set_module_port_name(const ModuleId& module, const ModulePortId& module_port, const std::string& port_name);
|
||||
/* Set a name for a module */
|
||||
void set_module_name(const ModuleId& module, const std::string& name);
|
||||
/* Set a port to be a wire */
|
||||
|
|
|
@ -36,7 +36,7 @@ ModuleId add_circuit_model_to_module_manager(ModuleManager& module_manager,
|
|||
/* Add ports */
|
||||
/* Find global ports and add one by one */
|
||||
for (const auto& port : circuit_lib.model_global_ports(circuit_model, false)) {
|
||||
BasicPort port_info(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort port_info(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(module, port_info, ModuleManager::MODULE_GLOBAL_PORT);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ ModuleId add_circuit_model_to_module_manager(ModuleManager& module_manager,
|
|||
/* Input ports (ignore all the global ports when searching the circuit_lib */
|
||||
for (const auto& kv : port_type2type_map) {
|
||||
for (const auto& port : circuit_lib.model_ports_by_type(circuit_model, kv.first, true)) {
|
||||
BasicPort port_info(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort port_info(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(module, port_info, kv.second);
|
||||
}
|
||||
}
|
||||
|
@ -304,6 +304,35 @@ bool module_net_is_local_wire(const ModuleManager& module_manager,
|
|||
return true;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Identify if a net is an output short connection inside a module:
|
||||
* The short connection is defined as the direct connection
|
||||
* between two outputs port of the module
|
||||
*
|
||||
* module
|
||||
* +-----------------------------+
|
||||
* |
|
||||
* src------>+--------------->|--->outputA
|
||||
* | |
|
||||
* | |
|
||||
* +--------------->|--->outputB
|
||||
* +-----------------------------+
|
||||
|
||||
*******************************************************************/
|
||||
bool module_net_include_output_short_connection(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const ModuleNetId& module_net) {
|
||||
/* Check all the sink modules of the net */
|
||||
size_t contain_num_module_output = 0;
|
||||
for (ModuleId sink_module : module_manager.net_sink_modules(module_id, module_net)) {
|
||||
if (module_id == sink_module) {
|
||||
contain_num_module_output++;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have found more than 1 module outputs, it indicated output short connection! */
|
||||
return (1 < contain_num_module_output);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Identify if a net is a local short connection inside a module:
|
||||
* The short connection is defined as the direct connection
|
||||
|
@ -368,7 +397,7 @@ void add_primitive_pb_type_module_nets(ModuleManager& module_manager,
|
|||
BasicPort src_port = module_manager.module_port(pb_type_module, src_module_port_id);
|
||||
|
||||
/* Get the des module port id */
|
||||
std::string des_module_port_name = circuit_lib.port_lib_name(pb_type_port->circuit_model_port);
|
||||
std::string des_module_port_name = circuit_lib.port_prefix(pb_type_port->circuit_model_port);
|
||||
ModulePortId des_module_port_id = module_manager.find_module_port(child_module, des_module_port_name);
|
||||
VTR_ASSERT(ModulePortId::INVALID() != des_module_port_id);
|
||||
BasicPort des_port = module_manager.module_port(child_module, des_module_port_id);
|
||||
|
@ -529,11 +558,11 @@ void add_module_nets_between_logic_and_memory_sram_bus(ModuleManager& module_man
|
|||
std::vector<std::string> logic_model_sram_port_names;
|
||||
/* Regular sram port goes first */
|
||||
for (CircuitPortId regular_sram_port : find_circuit_regular_sram_ports(circuit_lib, logic_model)) {
|
||||
logic_model_sram_port_names.push_back(circuit_lib.port_lib_name(regular_sram_port));
|
||||
logic_model_sram_port_names.push_back(circuit_lib.port_prefix(regular_sram_port));
|
||||
}
|
||||
/* Mode-select sram port goes first */
|
||||
for (CircuitPortId mode_select_sram_port : find_circuit_mode_select_sram_ports(circuit_lib, logic_model)) {
|
||||
logic_model_sram_port_names.push_back(circuit_lib.port_lib_name(mode_select_sram_port));
|
||||
logic_model_sram_port_names.push_back(circuit_lib.port_prefix(mode_select_sram_port));
|
||||
}
|
||||
/* Find the port ids in the memory */
|
||||
std::vector<ModulePortId> logic_module_sram_port_ids;
|
||||
|
@ -565,11 +594,11 @@ void add_module_nets_between_logic_and_memory_sram_bus(ModuleManager& module_man
|
|||
std::vector<std::string> logic_model_sramb_port_names;
|
||||
/* Regular sram port goes first */
|
||||
for (CircuitPortId regular_sram_port : find_circuit_regular_sram_ports(circuit_lib, logic_model)) {
|
||||
logic_model_sramb_port_names.push_back(circuit_lib.port_lib_name(regular_sram_port) + std::string("_inv"));
|
||||
logic_model_sramb_port_names.push_back(circuit_lib.port_prefix(regular_sram_port) + std::string("_inv"));
|
||||
}
|
||||
/* Mode-select sram port goes first */
|
||||
for (CircuitPortId mode_select_sram_port : find_circuit_mode_select_sram_ports(circuit_lib, logic_model)) {
|
||||
logic_model_sramb_port_names.push_back(circuit_lib.port_lib_name(mode_select_sram_port) + std::string("_inv"));
|
||||
logic_model_sramb_port_names.push_back(circuit_lib.port_prefix(mode_select_sram_port) + std::string("_inv"));
|
||||
}
|
||||
/* Find the port ids in the memory */
|
||||
std::vector<ModulePortId> logic_module_sramb_port_ids;
|
||||
|
|
|
@ -50,6 +50,9 @@ void add_pb_type_ports_to_module_manager(ModuleManager& module_manager,
|
|||
bool module_net_is_local_wire(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const ModuleNetId& module_net);
|
||||
|
||||
bool module_net_include_output_short_connection(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const ModuleNetId& module_net);
|
||||
|
||||
bool module_net_include_local_short_connection(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const ModuleNetId& module_net);
|
||||
|
||||
|
|
|
@ -334,4 +334,25 @@ size_t find_switch_block_num_shared_conf_bits(t_sram_orgz_info* cur_sram_orgz_in
|
|||
return num_shared_conf_bits;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Find if a X-direction or Y-direction Connection Block contains
|
||||
* routing tracks only (zero configuration bits and routing multiplexers)
|
||||
*******************************************************************/
|
||||
bool connection_block_contain_only_routing_tracks(const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type) {
|
||||
bool routing_track_only = true;
|
||||
|
||||
/* Find routing multiplexers on the sides of a Connection block where IPIN nodes locate */
|
||||
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
|
||||
|
||||
for (size_t side = 0; side < cb_sides.size(); ++side) {
|
||||
enum e_side cb_ipin_side = cb_sides[side];
|
||||
Side side_manager(cb_ipin_side);
|
||||
if (0 < rr_gsb.get_num_ipin_nodes(cb_ipin_side)) {
|
||||
routing_track_only = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return routing_track_only;
|
||||
}
|
||||
|
|
|
@ -51,4 +51,7 @@ size_t find_switch_block_num_shared_conf_bits(t_sram_orgz_info* cur_sram_orgz_in
|
|||
const std::vector<t_switch_inf>& rr_switches,
|
||||
const RRGSB& rr_gsb);
|
||||
|
||||
bool connection_block_contain_only_routing_tracks(const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "vtr_assert.h"
|
||||
#include "util.h"
|
||||
#include "mux_utils.h"
|
||||
#include "rr_blocks_utils.h"
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
#include "fpga_x2p_types.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
@ -247,7 +248,7 @@ void build_connection_block_interc_bitstream(BitstreamManager& bitstream_manager
|
|||
/* No bitstream generation required by a special direct connection*/
|
||||
} else if (1 < src_rr_node->fan_in) {
|
||||
/* Create the block denoting the memory instances that drives this node in Switch Block */
|
||||
std::string mem_block_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, ipin_index), src_rr_node->ptc_num, std::string(""));
|
||||
std::string mem_block_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, ipin_index), ipin_index, std::string(""));
|
||||
ConfigBlockId mux_mem_block = bitstream_manager.add_block(mem_block_name);
|
||||
bitstream_manager.add_child_block(cb_configurable_block, mux_mem_block);
|
||||
/* This is a routing multiplexer! Generate bitstream */
|
||||
|
@ -318,8 +319,11 @@ void build_connection_block_bitstreams(BitstreamManager& bitstream_manager,
|
|||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
if ( (TRUE != is_cb_exist(cb_type, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
/* Skip if the cb does not contain any configuration bits! */
|
||||
if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) {
|
||||
continue;
|
||||
}
|
||||
/* Create a block for the bitstream which corresponds to the Switch block */
|
||||
|
@ -376,13 +380,13 @@ void build_routing_bitstream(BitstreamManager& bitstream_manager,
|
|||
* To organize the bitstream in blocks, we create a block for each connection block
|
||||
* and give names which are same as they are in top-level module managers
|
||||
*/
|
||||
vpr_printf(TIO_MESSAGE_INFO,"Generating bitstream for X-directionConnection blocks ...\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO,"Generating bitstream for X-direction Connection blocks ...\n");
|
||||
|
||||
build_connection_block_bitstreams(bitstream_manager, top_configurable_block, module_manager,
|
||||
circuit_lib, mux_lib, rr_switches, L_rr_node,
|
||||
L_device_rr_gsb, CHANX);
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,"Generating bitstream for Y-directionConnection blocks ...\n");
|
||||
vpr_printf(TIO_MESSAGE_INFO,"Generating bitstream for Y-direction Connection blocks ...\n");
|
||||
|
||||
build_connection_block_bitstreams(bitstream_manager, top_configurable_block, module_manager,
|
||||
circuit_lib, mux_lib, rr_switches, L_rr_node,
|
||||
|
|
|
@ -75,12 +75,6 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
arch.sram_inf.verilog_sram_inf_orgz->spice_model);
|
||||
config_circuit_models_sram_port_to_default_sram_model(arch.spice->circuit_lib, sram_model);
|
||||
|
||||
/* Create a vector of segments. TODO: should come from DeviceContext */
|
||||
std::vector<t_segment_inf> L_segment_vec;
|
||||
for (int i = 0; i < arch.num_segments; ++i) {
|
||||
L_segment_vec.push_back(arch.Segments[i]);
|
||||
}
|
||||
|
||||
/* Add constant generator modules: VDD and GND */
|
||||
build_constant_generator_modules(module_manager);
|
||||
|
||||
|
@ -88,7 +82,7 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
* This should be done prior to other steps in this function,
|
||||
* because they will be instanciated by other primitive modules
|
||||
*/
|
||||
build_user_defined_modules(module_manager, arch.spice->circuit_lib, L_segment_vec);
|
||||
build_user_defined_modules(module_manager, arch.spice->circuit_lib);
|
||||
|
||||
/* Build elmentary modules */
|
||||
build_essential_modules(module_manager, arch.spice->circuit_lib);
|
||||
|
@ -103,7 +97,7 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
build_lut_modules(module_manager, arch.spice->circuit_lib);
|
||||
|
||||
/* Build wire modules */
|
||||
build_wire_modules(module_manager, arch.spice->circuit_lib, L_segment_vec);
|
||||
build_wire_modules(module_manager, arch.spice->circuit_lib);
|
||||
|
||||
/* Build memory modules */
|
||||
build_memory_modules(module_manager, mux_lib, arch.spice->circuit_lib,
|
||||
|
@ -132,6 +126,15 @@ ModuleManager build_device_module_graph(const t_vpr_setup& vpr_setup,
|
|||
arch.sram_inf.verilog_sram_inf_orgz->type, sram_model,
|
||||
TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy);
|
||||
|
||||
/* Now a critical correction has to be done!
|
||||
* In the module construction, we always use prefix of ports because they are binded
|
||||
* to the ports in architecture description (logic blocks etc.)
|
||||
* To interface with standard cell, we should
|
||||
* rename the ports of primitive modules using lib_name instead of prefix
|
||||
* (which have no children and are probably linked to a standard cell!)
|
||||
*/
|
||||
rename_primitive_module_port_names(module_manager, arch.spice->circuit_lib);
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
|
|
|
@ -155,6 +155,12 @@ void build_essential_modules(ModuleManager& module_manager,
|
|||
"Building essential (inverter/buffer/logic gate) modules...");
|
||||
|
||||
for (const auto& circuit_model : circuit_lib.models()) {
|
||||
/* Add essential modules upon on demand: only when it is not yet in the module library */
|
||||
ModuleId module = module_manager.find_module(circuit_lib.model_name(circuit_model));
|
||||
if (true == module_manager.valid_module_id(module)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SPICE_MODEL_INVBUF == circuit_lib.model_type(circuit_model)) {
|
||||
build_invbuf_module(module_manager, circuit_lib, circuit_model);
|
||||
continue;
|
||||
|
@ -184,8 +190,7 @@ void build_essential_modules(ModuleManager& module_manager,
|
|||
* to the module_manager
|
||||
********************************************************************/
|
||||
void build_user_defined_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<t_segment_inf>& routing_segments) {
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
|
@ -196,7 +201,7 @@ void build_user_defined_modules(ModuleManager& module_manager,
|
|||
for (const auto& model : circuit_lib.models()) {
|
||||
/* We only care about user-defined models */
|
||||
if ( (true == circuit_lib.model_verilog_netlist(model).empty())
|
||||
&& (true == circuit_lib.model_verilog_netlist(model).empty()) ) {
|
||||
&& (true == circuit_lib.model_spice_netlist(model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
|
@ -209,33 +214,6 @@ void build_user_defined_modules(ModuleManager& module_manager,
|
|||
add_circuit_model_to_module_manager(module_manager, circuit_lib, model);
|
||||
}
|
||||
|
||||
/* Register the routing channel wires */
|
||||
for (const auto& seg : routing_segments) {
|
||||
VTR_ASSERT( CircuitModelId::INVALID() != seg.circuit_model);
|
||||
VTR_ASSERT( SPICE_MODEL_CHAN_WIRE == circuit_lib.model_type(seg.circuit_model));
|
||||
/* We care only user-defined circuit models */
|
||||
if ( (circuit_lib.model_verilog_netlist(seg.circuit_model).empty())
|
||||
&& (circuit_lib.model_verilog_netlist(seg.circuit_model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Give a unique name for subckt of wire_model of segment,
|
||||
* circuit_model name is unique, and segment id is unique as well
|
||||
*/
|
||||
std::string segment_wire_subckt_name = generate_segment_wire_subckt_name(circuit_lib.model_name(seg.circuit_model), &seg - &routing_segments[0]);
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = add_circuit_model_to_module_manager(module_manager, circuit_lib, seg.circuit_model, segment_wire_subckt_name);
|
||||
|
||||
/* Find the output port*/
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(seg.circuit_model, SPICE_MODEL_PORT_OUTPUT, true);
|
||||
/* Make sure the port size is what we want */
|
||||
VTR_ASSERT (1 == circuit_lib.port_size(output_ports[0]));
|
||||
|
||||
/* Add a mid-output port to the module */
|
||||
BasicPort module_mid_output_port(generate_segment_wire_mid_output_name(circuit_lib.port_lib_name(output_ports[0])), circuit_lib.port_size(output_ports[0]));
|
||||
module_manager.add_port(module_id, module_mid_output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
|
@ -288,3 +266,40 @@ void build_constant_generator_modules(ModuleManager& module_manager) {
|
|||
"took %.2g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* This function will rename the ports of primitive modules
|
||||
* using lib_name instead of prefix
|
||||
* Primitive modules are defined as those modules in the module manager
|
||||
* which have user defined netlists
|
||||
********************************************************************/
|
||||
void rename_primitive_module_port_names(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
for (const CircuitModelId& model : circuit_lib.models()) {
|
||||
/* We only care about user-defined models */
|
||||
if ( (true == circuit_lib.model_verilog_netlist(model).empty())
|
||||
&& (true == circuit_lib.model_spice_netlist(model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
if (SPICE_MODEL_CHAN_WIRE == circuit_lib.model_type(model)) {
|
||||
continue;
|
||||
}
|
||||
/* Find the module in module manager */
|
||||
ModuleId module = module_manager.find_module(circuit_lib.model_name(model));
|
||||
/* We must find one! */
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(module));
|
||||
|
||||
/* Rename all the ports to use lib_name! */
|
||||
for (const CircuitPortId& model_port : circuit_lib.model_ports(model)) {
|
||||
/* Find the module port in module manager. We used prefix when creating the ports */
|
||||
ModulePortId module_port = module_manager.find_module_port(module, circuit_lib.port_prefix(model_port));
|
||||
/* We must find one! */
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(module, module_port));
|
||||
/* Name it with lib_name */
|
||||
module_manager.set_module_port_name(module, module_port, circuit_lib.port_lib_name(model_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@ void build_essential_modules(ModuleManager& module_manager,
|
|||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
void build_user_defined_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<t_segment_inf>& routing_segments);
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
void build_constant_generator_modules(ModuleManager& module_manager);
|
||||
|
||||
void rename_primitive_module_port_names(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -369,7 +369,7 @@ void build_primitive_block_module(ModuleManager& module_manager,
|
|||
for (auto port : primitive_model_inout_ports) {
|
||||
BasicPort module_port(generate_fpga_global_io_port_name(std::string(gio_inout_prefix), circuit_lib, primitive_model), circuit_lib.port_size(port));
|
||||
ModulePortId primitive_gpio_port_id = module_manager.add_port(primitive_module, module_port, ModuleManager::MODULE_GPIO_PORT);
|
||||
ModulePortId logic_gpio_port_id = module_manager.find_module_port(logic_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId logic_gpio_port_id = module_manager.find_module_port(logic_module, circuit_lib.port_prefix(port));
|
||||
BasicPort logic_gpio_port = module_manager.module_port(logic_module, logic_gpio_port_id);
|
||||
VTR_ASSERT(logic_gpio_port.get_width() == module_port.get_width());
|
||||
|
||||
|
@ -483,7 +483,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
}
|
||||
|
||||
/* Initialize the interconnection type that will be physically implemented in module */
|
||||
enum e_interconnect verilog_interc_type = determine_actual_pb_interc_type(cur_interc, fan_in);
|
||||
enum e_interconnect interc_type = determine_actual_pb_interc_type(cur_interc, fan_in);
|
||||
|
||||
/* Find input ports of the wire module */
|
||||
std::vector<CircuitPortId> interc_model_inputs = circuit_lib.model_ports_by_type(cur_interc->circuit_model, SPICE_MODEL_PORT_INPUT, true); /* the last argument to guarantee that we ignore any global inputs */
|
||||
|
@ -497,7 +497,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Branch on the type of physical implementation,
|
||||
* We add instances of programmable interconnection
|
||||
*/
|
||||
switch (verilog_interc_type) {
|
||||
switch (interc_type) {
|
||||
case DIRECT_INTERC: {
|
||||
/* Ensure direct interc has only one fan-in */
|
||||
VTR_ASSERT(1 == fan_in);
|
||||
|
@ -521,7 +521,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Get the instance id and add an instance of wire */
|
||||
size_t wire_instance = module_manager.num_instance(pb_module, wire_module);
|
||||
module_manager.add_child_module(pb_module, wire_module);
|
||||
|
||||
|
||||
/* Ensure input and output ports of the wire model has only 1 pin respectively */
|
||||
VTR_ASSERT(1 == circuit_lib.port_size(interc_model_inputs[0]));
|
||||
VTR_ASSERT(1 == circuit_lib.port_size(interc_model_outputs[0]));
|
||||
|
@ -530,7 +530,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* First net is to connect input of src_pb_graph_node to input of the wire module */
|
||||
add_module_pb_graph_pin2pin_net(module_manager, pb_module,
|
||||
wire_module, wire_instance,
|
||||
circuit_lib.port_lib_name(interc_model_inputs[0]),
|
||||
circuit_lib.port_prefix(interc_model_inputs[0]),
|
||||
0, /* wire input port has only 1 pin */
|
||||
module_name_prefix,
|
||||
src_pb_graph_pin,
|
||||
|
@ -539,7 +539,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Second net is to connect output of the wire module to output of des_pb_graph_pin */
|
||||
add_module_pb_graph_pin2pin_net(module_manager, pb_module,
|
||||
wire_module, wire_instance,
|
||||
circuit_lib.port_lib_name(interc_model_outputs[0]),
|
||||
circuit_lib.port_prefix(interc_model_outputs[0]),
|
||||
0, /* wire output port has only 1 pin */
|
||||
module_name_prefix,
|
||||
des_pb_graph_pin,
|
||||
|
@ -559,6 +559,11 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Instanciate the MUX */
|
||||
size_t mux_instance = module_manager.num_instance(pb_module, mux_module);
|
||||
module_manager.add_child_module(pb_module, mux_module);
|
||||
/* Give an instance name: this name should be consistent with the block name given in SDC generator,
|
||||
* If you want to bind the SDC generation to modules
|
||||
*/
|
||||
std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, des_pb_graph_pin, std::string(""));
|
||||
module_manager.set_child_instance_name(pb_module, mux_module, mux_instance, mux_instance_name);
|
||||
|
||||
/* Instanciate a memory module for the MUX */
|
||||
std::string mux_mem_module_name = generate_mux_subckt_name(circuit_lib,
|
||||
|
@ -610,7 +615,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Add a net, set its source and sink */
|
||||
add_module_pb_graph_pin2pin_net(module_manager, pb_module,
|
||||
mux_module, mux_instance,
|
||||
circuit_lib.port_lib_name(interc_model_inputs[0]),
|
||||
circuit_lib.port_prefix(interc_model_inputs[0]),
|
||||
mux_input_pin_id,
|
||||
module_name_prefix,
|
||||
src_pb_graph_pin,
|
||||
|
@ -623,7 +628,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
/* Add a net to wire the output of the multiplexer to des_pb_graph_pin */
|
||||
add_module_pb_graph_pin2pin_net(module_manager, pb_module,
|
||||
mux_module, mux_instance,
|
||||
circuit_lib.port_lib_name(interc_model_outputs[0]),
|
||||
circuit_lib.port_prefix(interc_model_outputs[0]),
|
||||
0, /* MUX should have only 1 pin in its output port */
|
||||
module_name_prefix,
|
||||
des_pb_graph_pin,
|
||||
|
@ -921,7 +926,7 @@ void rec_build_physical_block_modules(ModuleManager& module_manager,
|
|||
/* Add the memory module as a child of primitive module */
|
||||
module_manager.add_child_module(pb_module, child_pb_module);
|
||||
|
||||
/* Set an instance name to bind to a block in bitstream generation */
|
||||
/* Set an instance name to bind to a block in bitstream generation and SDC generation! */
|
||||
std::string child_pb_instance_name = generate_physical_block_instance_name(pb_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild]), inst);
|
||||
module_manager.set_child_instance_name(pb_module, child_pb_module, child_instance_id, child_pb_instance_name);
|
||||
|
||||
|
|
|
@ -82,36 +82,36 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
/* Add each global port */
|
||||
for (const auto& port : lut_global_ports) {
|
||||
/* Configure each global port */
|
||||
BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, global_port, ModuleManager::MODULE_GLOBAL_PORT);
|
||||
}
|
||||
/* Add each input port */
|
||||
for (const auto& port : lut_input_ports) {
|
||||
BasicPort input_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort input_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* Set the port to be wire-connection */
|
||||
module_manager.set_port_is_wire(lut_module, input_port.get_name(), true);
|
||||
}
|
||||
/* Add each output port */
|
||||
for (const auto& port : lut_output_ports) {
|
||||
BasicPort output_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort output_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
/* Set the port to be wire-connection */
|
||||
module_manager.set_port_is_wire(lut_module, output_port.get_name(), true);
|
||||
}
|
||||
/* Add each regular (not mode select) SRAM port */
|
||||
for (const auto& port : lut_regular_sram_ports) {
|
||||
BasicPort mem_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort mem_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, mem_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_lib_name(port) + "_inv"), circuit_lib.port_size(port));
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_prefix(port) + "_inv"), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, mem_inv_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
|
||||
/* Add each mode-select SRAM port */
|
||||
for (const auto& port : lut_mode_select_sram_ports) {
|
||||
BasicPort mem_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort mem_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, mem_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_lib_name(port) + "_inv"), circuit_lib.port_size(port));
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_prefix(port) + "_inv"), circuit_lib.port_size(port));
|
||||
module_manager.add_port(lut_module, mem_inv_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
std::string tri_state_map = circuit_lib.port_tri_state_map(lut_input_ports[0]);
|
||||
size_t mode_select_port_lsb = 0;
|
||||
for (const auto& pin : circuit_lib.pins(lut_input_ports[0])) {
|
||||
ModulePortId lut_module_input_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_lib_name(lut_input_ports[0]));
|
||||
ModulePortId lut_module_input_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_prefix(lut_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(lut_module, lut_module_input_port_id));
|
||||
|
||||
/* Create a module net for the connection */
|
||||
|
@ -222,13 +222,13 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
ModuleNetId gate_sram_net = module_manager.create_module_net(lut_module);
|
||||
|
||||
/* Find the module port id of the SRAM port of LUT module */
|
||||
ModulePortId lut_module_mode_select_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_lib_name(lut_mode_select_sram_ports[0]));
|
||||
ModulePortId lut_module_mode_select_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_prefix(lut_mode_select_sram_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(lut_module, lut_module_mode_select_port_id));
|
||||
/* Set the source of the net to an mode-select SRAM port of the LUT module */
|
||||
module_manager.add_module_net_source(lut_module, gate_sram_net, lut_module, 0, lut_module_mode_select_port_id, mode_select_port_lsb);
|
||||
|
||||
/* Find the module port id of the SRAM port of LUT module */
|
||||
ModulePortId gate_module_input0_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_lib_name(gate_input_ports[0]));
|
||||
ModulePortId gate_module_input0_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_prefix(gate_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(gate_module, gate_module_input0_port_id));
|
||||
/* Set the sink of the net to an input[0] port of the gate module */
|
||||
VTR_ASSERT(1 == module_manager.module_port(gate_module, gate_module_input0_port_id).get_width());
|
||||
|
@ -237,7 +237,7 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
}
|
||||
|
||||
/* Use the existing net to connect to the input[1] port of the gate module */
|
||||
ModulePortId gate_module_input1_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_lib_name(gate_input_ports[1]));
|
||||
ModulePortId gate_module_input1_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_prefix(gate_input_ports[1]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(gate_module, gate_module_input1_port_id));
|
||||
VTR_ASSERT(1 == module_manager.module_port(gate_module, gate_module_input1_port_id).get_width());
|
||||
for (const size_t& gate_pin : module_manager.module_port(gate_module, gate_module_input1_port_id).pins()) {
|
||||
|
@ -246,7 +246,7 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
|
||||
/* Create a module net for the output connection */
|
||||
ModuleNetId gate_output_net = module_manager.create_module_net(lut_module);
|
||||
ModulePortId gate_module_output_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_lib_name(gate_output_ports[0]));
|
||||
ModulePortId gate_module_output_port_id = module_manager.find_module_port(gate_module, circuit_lib.port_prefix(gate_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(gate_module, gate_module_output_port_id));
|
||||
BasicPort gate_module_output_port = module_manager.module_port(gate_module, gate_module_output_port_id);
|
||||
VTR_ASSERT(1 == gate_module_output_port.get_width());
|
||||
|
@ -326,7 +326,7 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
* 3. Data input of LUT MUX module to SRAM port of LUT
|
||||
* 4. Data output of LUT MUX module to output ports of LUT
|
||||
*/
|
||||
ModulePortId lut_mux_sram_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_lib_name(lut_regular_sram_ports[0]));
|
||||
ModulePortId lut_mux_sram_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_prefix(lut_regular_sram_ports[0]));
|
||||
BasicPort lut_mux_sram_port = module_manager.module_port(lut_mux_module, lut_mux_sram_port_id);
|
||||
VTR_ASSERT(lut_mux_sram_port.get_width() == lut_mux_sram_nets.size());
|
||||
/* Wire the port to lut_mux_sram_net */
|
||||
|
@ -334,7 +334,7 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
module_manager.add_module_net_sink(lut_module, lut_mux_sram_nets[pin], lut_mux_module, lut_mux_instance, lut_mux_sram_port_id, pin);
|
||||
}
|
||||
|
||||
ModulePortId lut_mux_sram_inv_port_id = module_manager.find_module_port(lut_mux_module, std::string(circuit_lib.port_lib_name(lut_regular_sram_ports[0]) + "_inv"));
|
||||
ModulePortId lut_mux_sram_inv_port_id = module_manager.find_module_port(lut_mux_module, std::string(circuit_lib.port_prefix(lut_regular_sram_ports[0]) + "_inv"));
|
||||
BasicPort lut_mux_sram_inv_port = module_manager.module_port(lut_mux_module, lut_mux_sram_inv_port_id);
|
||||
VTR_ASSERT(lut_mux_sram_inv_port.get_width() == lut_mux_sram_inv_nets.size());
|
||||
/* Wire the port to lut_mux_sram_net */
|
||||
|
@ -351,9 +351,9 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
* |
|
||||
* net
|
||||
*/
|
||||
ModulePortId lut_sram_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_lib_name(lut_regular_sram_ports[0]));
|
||||
ModulePortId lut_sram_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_prefix(lut_regular_sram_ports[0]));
|
||||
BasicPort lut_sram_port = module_manager.module_port(lut_module, lut_sram_port_id);
|
||||
ModulePortId lut_mux_input_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_lib_name(lut_input_ports[0]));
|
||||
ModulePortId lut_mux_input_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_prefix(lut_input_ports[0]));
|
||||
BasicPort lut_mux_input_port = module_manager.module_port(lut_mux_module, lut_mux_input_port_id);
|
||||
VTR_ASSERT(lut_mux_input_port.get_width() == lut_sram_port.get_width());
|
||||
/* Wire the port to lut_mux_sram_net */
|
||||
|
@ -364,9 +364,9 @@ void build_lut_module(ModuleManager& module_manager,
|
|||
}
|
||||
|
||||
for (const auto& port : lut_output_ports) {
|
||||
ModulePortId lut_output_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId lut_output_port_id = module_manager.find_module_port(lut_module, circuit_lib.port_prefix(port));
|
||||
BasicPort lut_output_port = module_manager.module_port(lut_module, lut_output_port_id);
|
||||
ModulePortId lut_mux_output_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId lut_mux_output_port_id = module_manager.find_module_port(lut_mux_module, circuit_lib.port_prefix(port));
|
||||
BasicPort lut_mux_output_port = module_manager.module_port(lut_mux_module, lut_mux_output_port_id);
|
||||
VTR_ASSERT(lut_mux_output_port.get_width() == lut_output_port.get_width());
|
||||
/* Wire the port to lut_mux_sram_net */
|
||||
|
|
|
@ -48,8 +48,8 @@ void add_module_input_nets_to_mem_modules(ModuleManager& module_manager,
|
|||
const size_t& child_instance) {
|
||||
/* Wire inputs of parent module to inputs of child modules */
|
||||
for (const auto& port : circuit_ports) {
|
||||
ModulePortId src_port_id = module_manager.find_module_port(mem_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId sink_port_id = module_manager.find_module_port(child_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId src_port_id = module_manager.find_module_port(mem_module, circuit_lib.port_prefix(port));
|
||||
ModulePortId sink_port_id = module_manager.find_module_port(child_module, circuit_lib.port_prefix(port));
|
||||
for (size_t pin_id = 0; pin_id < module_manager.module_port(mem_module, sink_port_id).pins().size(); ++pin_id) {
|
||||
ModuleNetId net = module_manager.create_module_net(mem_module);
|
||||
/* Source pin is shifted by the number of memories */
|
||||
|
@ -82,8 +82,8 @@ void add_module_output_nets_to_mem_modules(ModuleManager& module_manager,
|
|||
const size_t& child_instance) {
|
||||
/* Wire inputs of parent module to inputs of child modules */
|
||||
for (const auto& port : circuit_ports) {
|
||||
ModulePortId src_port_id = module_manager.find_module_port(child_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId sink_port_id = module_manager.find_module_port(mem_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId src_port_id = module_manager.find_module_port(child_module, circuit_lib.port_prefix(port));
|
||||
ModulePortId sink_port_id = module_manager.find_module_port(mem_module, circuit_lib.port_prefix(port));
|
||||
for (size_t pin_id = 0; pin_id < module_manager.module_port(child_module, src_port_id).pins().size(); ++pin_id) {
|
||||
ModuleNetId net = module_manager.create_module_net(mem_module);
|
||||
/* Source pin is shifted by the number of memories */
|
||||
|
@ -106,18 +106,22 @@ void add_module_output_nets_to_mem_modules(ModuleManager& module_manager,
|
|||
* j-th pin of output port of the i-th child module is wired to the j + i*W -th
|
||||
* pin of output port of the memory module, where W is the size of port
|
||||
* 3. It assumes fixed port name for output ports
|
||||
*
|
||||
* We cache the module nets that have been created because they will be used later
|
||||
********************************************************************/
|
||||
static
|
||||
void add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
|
||||
const ModuleId& mem_module,
|
||||
const std::string& mem_module_output_name,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitPortId& circuit_port,
|
||||
const ModuleId& child_module,
|
||||
const size_t& child_index,
|
||||
const size_t& child_instance) {
|
||||
std::vector<ModuleNetId> add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
|
||||
const ModuleId& mem_module,
|
||||
const std::string& mem_module_output_name,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitPortId& circuit_port,
|
||||
const ModuleId& child_module,
|
||||
const size_t& child_index,
|
||||
const size_t& child_instance) {
|
||||
std::vector<ModuleNetId> module_nets;
|
||||
|
||||
/* Wire inputs of parent module to inputs of child modules */
|
||||
ModulePortId src_port_id = module_manager.find_module_port(child_module, circuit_lib.port_lib_name(circuit_port));
|
||||
ModulePortId src_port_id = module_manager.find_module_port(child_module, circuit_lib.port_prefix(circuit_port));
|
||||
ModulePortId sink_port_id = module_manager.find_module_port(mem_module, mem_module_output_name);
|
||||
for (size_t pin_id = 0; pin_id < module_manager.module_port(child_module, src_port_id).pins().size(); ++pin_id) {
|
||||
ModuleNetId net = module_manager.create_module_net(mem_module);
|
||||
|
@ -128,7 +132,12 @@ void add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
|
|||
/* Sink node of the input net is the input of sram module */
|
||||
size_t sink_pin_id = child_index * circuit_lib.port_size(circuit_port) + module_manager.module_port(mem_module, sink_port_id).pins()[pin_id];
|
||||
module_manager.add_module_net_sink(mem_module, net, mem_module, 0, sink_port_id, sink_pin_id);
|
||||
|
||||
/* Cache the nets */
|
||||
module_nets.push_back(net);
|
||||
}
|
||||
|
||||
return module_nets;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -155,9 +164,13 @@ void add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
|
|||
static
|
||||
void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::vector<ModuleNetId>& output_nets,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitPortId& model_input_port,
|
||||
const CircuitPortId& model_output_port) {
|
||||
/* Counter for the nets */
|
||||
size_t net_counter = 0;
|
||||
|
||||
for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size(); ++mem_index) {
|
||||
ModuleId net_src_module_id;
|
||||
size_t net_src_instance_id;
|
||||
|
@ -175,19 +188,19 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager,
|
|||
net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
|
||||
|
||||
/* Find the port name of next memory module */
|
||||
std::string sink_port_name = circuit_lib.port_lib_name(model_input_port);
|
||||
std::string sink_port_name = circuit_lib.port_prefix(model_input_port);
|
||||
net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index];
|
||||
net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index];
|
||||
net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name);
|
||||
} else {
|
||||
/* Find the port name of previous memory module */
|
||||
std::string src_port_name = circuit_lib.port_lib_name(model_output_port);
|
||||
std::string src_port_name = circuit_lib.port_prefix(model_output_port);
|
||||
net_src_module_id = module_manager.configurable_children(parent_module)[mem_index - 1];
|
||||
net_src_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index - 1];
|
||||
net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
|
||||
|
||||
/* Find the port name of next memory module */
|
||||
std::string sink_port_name = circuit_lib.port_lib_name(model_input_port);
|
||||
std::string sink_port_name = circuit_lib.port_prefix(model_input_port);
|
||||
net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index];
|
||||
net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index];
|
||||
net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name);
|
||||
|
@ -203,11 +216,21 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager,
|
|||
/* Create a net for each pin */
|
||||
for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) {
|
||||
/* Create a net and add source and sink to it */
|
||||
ModuleNetId net = module_manager.create_module_net(parent_module);
|
||||
ModuleNetId net;
|
||||
if (0 == mem_index) {
|
||||
net = module_manager.create_module_net(parent_module);
|
||||
} else {
|
||||
net = output_nets[net_counter];
|
||||
}
|
||||
/* Add net source */
|
||||
module_manager.add_module_net_source(parent_module, net, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]);
|
||||
/* Add net sink */
|
||||
module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]);
|
||||
|
||||
/* Update net counter */
|
||||
if (0 < mem_index) {
|
||||
net_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +239,7 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager,
|
|||
* net sink is the configuration chain tail of the primitive module
|
||||
*/
|
||||
/* Find the port name of previous memory module */
|
||||
std::string src_port_name = circuit_lib.port_lib_name(model_output_port);
|
||||
std::string src_port_name = circuit_lib.port_prefix(model_output_port);
|
||||
ModuleId net_src_module_id = module_manager.configurable_children(parent_module).back();
|
||||
size_t net_src_instance_id = module_manager.configurable_child_instances(parent_module).back();
|
||||
ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
|
||||
|
@ -237,12 +260,17 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager,
|
|||
/* Create a net for each pin */
|
||||
for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) {
|
||||
/* Create a net and add source and sink to it */
|
||||
ModuleNetId net = module_manager.create_module_net(parent_module);
|
||||
ModuleNetId net = output_nets[net_counter];
|
||||
/* Add net source */
|
||||
module_manager.add_module_net_source(parent_module, net, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]);
|
||||
/* Add net sink */
|
||||
module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]);
|
||||
|
||||
/* Update net counter */
|
||||
net_counter++;
|
||||
}
|
||||
|
||||
VTR_ASSERT(net_counter == output_nets.size());
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
|
@ -283,12 +311,12 @@ void build_memory_standalone_module(ModuleManager& module_manager,
|
|||
|
||||
/* Add each input port */
|
||||
for (const auto& port : sram_input_ports) {
|
||||
BasicPort input_port(circuit_lib.port_lib_name(port), num_mems);
|
||||
BasicPort input_port(circuit_lib.port_prefix(port), num_mems);
|
||||
module_manager.add_port(mem_module, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
/* Add each output port: port width should match the number of memories */
|
||||
for (const auto& port : sram_output_ports) {
|
||||
BasicPort output_port(circuit_lib.port_lib_name(port), num_mems);
|
||||
BasicPort output_port(circuit_lib.port_prefix(port), num_mems);
|
||||
module_manager.add_port(mem_module, output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
}
|
||||
|
||||
|
@ -381,6 +409,9 @@ void build_memory_chain_module(ModuleManager& module_manager,
|
|||
/* Find the sram module in the module manager */
|
||||
ModuleId sram_mem_module = module_manager.find_module(circuit_lib.model_name(sram_model));
|
||||
|
||||
/* Cache the output nets for non-inverted data output */
|
||||
std::vector<ModuleNetId> mem_output_nets;
|
||||
|
||||
/* Instanciate each submodule */
|
||||
for (size_t i = 0; i < num_mems; ++i) {
|
||||
size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module);
|
||||
|
@ -396,13 +427,18 @@ void build_memory_chain_module(ModuleManager& module_manager,
|
|||
VTR_ASSERT( 1 == iport);
|
||||
port_name = generate_configuration_chain_inverted_data_out_name();
|
||||
}
|
||||
add_module_output_nets_to_chain_mem_modules(module_manager, mem_module, port_name, circuit_lib, sram_output_ports[iport],
|
||||
sram_mem_module, i, sram_mem_instance);
|
||||
std::vector<ModuleNetId> output_nets = add_module_output_nets_to_chain_mem_modules(module_manager, mem_module,
|
||||
port_name, circuit_lib, sram_output_ports[iport],
|
||||
sram_mem_module, i, sram_mem_instance);
|
||||
/* Cache only for regular data outputs */
|
||||
if (0 == iport) {
|
||||
mem_output_nets.insert(mem_output_nets.end(), output_nets.begin(), output_nets.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Build module nets to wire the configuration chain */
|
||||
add_module_nets_to_cmos_memory_chain_module(module_manager, mem_module,
|
||||
add_module_nets_to_cmos_memory_chain_module(module_manager, mem_module, mem_output_nets,
|
||||
circuit_lib, sram_input_ports[0], sram_output_ports[0]);
|
||||
|
||||
|
||||
|
@ -464,29 +500,29 @@ void build_memory_bank_module(ModuleManager& module_manager,
|
|||
/* Add module ports: the ports come from the SRAM modules */
|
||||
/* Add each input port */
|
||||
for (const auto& port : sram_input_ports) {
|
||||
BasicPort input_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort input_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
/* Add each output port: port width should match the number of memories */
|
||||
for (const auto& port : sram_output_ports) {
|
||||
BasicPort output_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort output_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
}
|
||||
/* Add each output port: port width should match the number of memories */
|
||||
for (const auto& port : sram_bl_ports) {
|
||||
BasicPort bl_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort bl_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, bl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
for (const auto& port : sram_blb_ports) {
|
||||
BasicPort blb_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort blb_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
for (const auto& port : sram_wl_ports) {
|
||||
BasicPort wl_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort wl_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
for (const auto& port : sram_wlb_ports) {
|
||||
BasicPort wlb_port(circuit_lib.port_lib_name(port), num_mems * circuit_lib.port_size(port));
|
||||
BasicPort wlb_port(circuit_lib.port_prefix(port), num_mems * circuit_lib.port_size(port));
|
||||
module_manager.add_port(mem_module, wlb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ ModulePortId find_inverter_buffer_module_port(const ModuleManager& module_manage
|
|||
VTR_ASSERT(1 == model_ports.size());
|
||||
|
||||
/* Find the input and output module ports */
|
||||
ModulePortId module_port_id = module_manager.find_module_port(module_id, circuit_lib.port_lib_name(model_ports[0]));
|
||||
ModulePortId module_port_id = module_manager.find_module_port(module_id, circuit_lib.port_prefix(model_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(module_id, module_port_id));
|
||||
|
||||
return module_port_id;
|
||||
|
|
|
@ -74,25 +74,25 @@ void build_cmos_mux_branch_body(ModuleManager& module_manager,
|
|||
|
||||
/* Find the module ports of tgate module */
|
||||
/* Input port is the data path input of the tgate, whose size must be 1 ! */
|
||||
ModulePortId tgate_module_input = module_manager.find_module_port(tgate_module_id, circuit_lib.port_lib_name(tgate_input_ports[0]));
|
||||
ModulePortId tgate_module_input = module_manager.find_module_port(tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_input));
|
||||
BasicPort tgate_module_input_port = module_manager.module_port(tgate_module_id, tgate_module_input);
|
||||
VTR_ASSERT(1 == tgate_module_input_port.get_width());
|
||||
|
||||
/* Mem port is the memory of the tgate, whose size must be 1 ! */
|
||||
ModulePortId tgate_module_mem = module_manager.find_module_port(tgate_module_id, circuit_lib.port_lib_name(tgate_input_ports[1]));
|
||||
ModulePortId tgate_module_mem = module_manager.find_module_port(tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[1]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_mem));
|
||||
BasicPort tgate_module_mem_port = module_manager.module_port(tgate_module_id, tgate_module_mem);
|
||||
VTR_ASSERT(1 == tgate_module_mem_port.get_width());
|
||||
|
||||
/* Mem inv port is the inverted memory of the tgate, whose size must be 1 ! */
|
||||
ModulePortId tgate_module_mem_inv = module_manager.find_module_port(tgate_module_id, circuit_lib.port_lib_name(tgate_input_ports[2]));
|
||||
ModulePortId tgate_module_mem_inv = module_manager.find_module_port(tgate_module_id, circuit_lib.port_prefix(tgate_input_ports[2]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_mem_inv));
|
||||
BasicPort tgate_module_mem_inv_port = module_manager.module_port(tgate_module_id, tgate_module_mem_inv);
|
||||
VTR_ASSERT(1 == tgate_module_mem_inv_port.get_width());
|
||||
|
||||
/* Output port is the data path output of the tgate, whose size must be 1 ! */
|
||||
ModulePortId tgate_module_output = module_manager.find_module_port(tgate_module_id, circuit_lib.port_lib_name(tgate_output_ports[0]));
|
||||
ModulePortId tgate_module_output = module_manager.find_module_port(tgate_module_id, circuit_lib.port_prefix(tgate_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(tgate_module_id, tgate_module_output));
|
||||
BasicPort tgate_module_output_port = module_manager.module_port(tgate_module_id, tgate_module_output);
|
||||
VTR_ASSERT(1 == tgate_module_output_port.get_width());
|
||||
|
@ -300,26 +300,26 @@ void build_rram_mux_branch_module(ModuleManager& module_manager,
|
|||
std::vector<CircuitPortId> prog_enable_ports = circuit_lib.model_global_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true, true);
|
||||
for (const auto& port : prog_enable_ports) {
|
||||
/* Configure each global port */
|
||||
BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(mux_module, global_port, ModuleManager::MODULE_GLOBAL_PORT);
|
||||
}
|
||||
|
||||
/* Add each input port */
|
||||
BasicPort input_port(circuit_lib.port_lib_name(mux_input_ports[0]), num_inputs);
|
||||
BasicPort input_port(circuit_lib.port_prefix(mux_input_ports[0]), num_inputs);
|
||||
module_manager.add_port(mux_module, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
/* Add each output port */
|
||||
BasicPort output_port(circuit_lib.port_lib_name(mux_output_ports[0]), num_outputs);
|
||||
BasicPort output_port(circuit_lib.port_prefix(mux_output_ports[0]), num_outputs);
|
||||
module_manager.add_port(mux_module, output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
|
||||
/* Add RRAM programming ports,
|
||||
* RRAM MUXes require one more pair of BLB and WL
|
||||
* to configure the memories. See schematic for details
|
||||
*/
|
||||
BasicPort blb_port(circuit_lib.port_lib_name(mux_blb_ports[0]), num_mems + 1);
|
||||
BasicPort blb_port(circuit_lib.port_prefix(mux_blb_ports[0]), num_mems + 1);
|
||||
module_manager.add_port(mux_module, blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
BasicPort wl_port(circuit_lib.port_lib_name(mux_wl_ports[0]), num_mems + 1);
|
||||
BasicPort wl_port(circuit_lib.port_prefix(mux_wl_ports[0]), num_mems + 1);
|
||||
module_manager.add_port(mux_module, wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
/* Note: we do not generate the internal structure of the ReRAM-based MUX
|
||||
|
@ -398,20 +398,20 @@ void build_cmos_mux_module_mux2_multiplexing_structure(ModuleManager& module_man
|
|||
std::vector<BasicPort> std_cell_module_input_ports;
|
||||
/* Input 0 port is the first data path input of the tgate, whose size must be 1 ! */
|
||||
for (size_t port_id = 0; port_id < 2; ++port_id) {
|
||||
std_cell_module_inputs.push_back(module_manager.find_module_port(std_cell_module_id, circuit_lib.port_lib_name(std_cell_input_ports[port_id])));
|
||||
std_cell_module_inputs.push_back(module_manager.find_module_port(std_cell_module_id, circuit_lib.port_prefix(std_cell_input_ports[port_id])));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(std_cell_module_id, std_cell_module_inputs[port_id]));
|
||||
std_cell_module_input_ports.push_back(module_manager.module_port(std_cell_module_id, std_cell_module_inputs[port_id]));
|
||||
VTR_ASSERT(1 == std_cell_module_input_ports[port_id].get_width());
|
||||
}
|
||||
|
||||
/* Mem port is the memory of the standard cell MUX2, whose size must be 1 ! */
|
||||
ModulePortId std_cell_module_mem = module_manager.find_module_port(std_cell_module_id, circuit_lib.port_lib_name(std_cell_input_ports[2]));
|
||||
ModulePortId std_cell_module_mem = module_manager.find_module_port(std_cell_module_id, circuit_lib.port_prefix(std_cell_input_ports[2]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(std_cell_module_id, std_cell_module_mem));
|
||||
BasicPort std_cell_module_mem_port = module_manager.module_port(std_cell_module_id, std_cell_module_mem);
|
||||
VTR_ASSERT(1 == std_cell_module_mem_port.get_width());
|
||||
|
||||
/* Output port is the data path output of the standard cell MUX2, whose size must be 1 ! */
|
||||
ModulePortId std_cell_module_output = module_manager.find_module_port(std_cell_module_id, circuit_lib.port_lib_name(std_cell_output_ports[0]));
|
||||
ModulePortId std_cell_module_output = module_manager.find_module_port(std_cell_module_id, circuit_lib.port_prefix(std_cell_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(std_cell_module_id, std_cell_module_output));
|
||||
BasicPort std_cell_module_output_port = module_manager.module_port(std_cell_module_id, std_cell_module_output);
|
||||
VTR_ASSERT(1 == std_cell_module_output_port.get_width());
|
||||
|
@ -745,7 +745,7 @@ vtr::vector<MuxInputId, ModuleNetId> build_mux_module_input_buffers(ModuleManage
|
|||
VTR_ASSERT(1 == mux_input_ports.size());
|
||||
|
||||
/* Get the input port from MUX module */
|
||||
ModulePortId module_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_input_ports[0]));
|
||||
ModulePortId module_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_input_ports[0]));
|
||||
VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id);
|
||||
/* Get the port from module */
|
||||
BasicPort module_input_port = module_manager.module_port(mux_module, module_input_port_id);
|
||||
|
@ -860,7 +860,7 @@ vtr::vector<MuxOutputId, ModuleNetId> build_mux_module_output_buffers(ModuleMana
|
|||
/* Iterate over all the outputs in the MUX module */
|
||||
for (const auto& output_port : mux_output_ports) {
|
||||
/* Get the output port from MUX module */
|
||||
ModulePortId module_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(output_port));
|
||||
ModulePortId module_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(output_port));
|
||||
VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id);
|
||||
/* Get the port from module */
|
||||
BasicPort module_output_port = module_manager.module_port(mux_module, module_output_port_id);
|
||||
|
@ -968,7 +968,7 @@ void build_mux_module_local_encoders_and_memory_nets(ModuleManager& module_manag
|
|||
/* Add mem and mem_inv nets here */
|
||||
size_t mem_net_cnt = 0;
|
||||
for (const auto& port : mux_sram_ports) {
|
||||
ModulePortId mem_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(port));
|
||||
ModulePortId mem_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(port));
|
||||
BasicPort mem_port = module_manager.module_port(mux_module, mem_port_id);
|
||||
for (const size_t& pin : mem_port.pins()) {
|
||||
MuxMemId mem_id = MuxMemId(mem_net_cnt);
|
||||
|
@ -983,7 +983,7 @@ void build_mux_module_local_encoders_and_memory_nets(ModuleManager& module_manag
|
|||
/* Add mem and mem_inv nets here */
|
||||
size_t mem_inv_net_cnt = 0;
|
||||
for (const auto& port : mux_sram_ports) {
|
||||
ModulePortId mem_inv_port_id = module_manager.find_module_port(mux_module, std::string(circuit_lib.port_lib_name(port) + "_inv"));
|
||||
ModulePortId mem_inv_port_id = module_manager.find_module_port(mux_module, std::string(circuit_lib.port_prefix(port) + "_inv"));
|
||||
BasicPort mem_inv_port = module_manager.module_port(mux_module, mem_inv_port_id);
|
||||
for (const size_t& pin : mem_inv_port.pins()) {
|
||||
MuxMemId mem_id = MuxMemId(mem_inv_net_cnt);
|
||||
|
@ -1003,9 +1003,9 @@ void build_mux_module_local_encoders_and_memory_nets(ModuleManager& module_manag
|
|||
BasicPort decoder_data_inv_port(generate_mux_local_decoder_data_inv_port_name(), mux_graph.num_memory_bits());
|
||||
|
||||
/* Local port to record the LSB and MSB of each level, here, we deposite (0, 0) */
|
||||
ModulePortId mux_module_sram_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_sram_ports[0]));
|
||||
ModulePortId mux_module_sram_inv_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_sram_ports[0]) + "_inv");
|
||||
BasicPort lvl_addr_port(circuit_lib.port_lib_name(mux_sram_ports[0]), 0);
|
||||
ModulePortId mux_module_sram_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_sram_ports[0]));
|
||||
ModulePortId mux_module_sram_inv_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_sram_ports[0]) + "_inv");
|
||||
BasicPort lvl_addr_port(circuit_lib.port_prefix(mux_sram_ports[0]), 0);
|
||||
BasicPort lvl_data_port(decoder_data_port.get_name(), 0);
|
||||
BasicPort lvl_data_inv_port(decoder_data_inv_port.get_name(), 0);
|
||||
|
||||
|
@ -1161,7 +1161,7 @@ void build_cmos_mux_module(ModuleManager& module_manager,
|
|||
*/
|
||||
size_t input_port_cnt = 0;
|
||||
for (const auto& port : mux_input_ports) {
|
||||
BasicPort input_port(circuit_lib.port_lib_name(port), num_inputs);
|
||||
BasicPort input_port(circuit_lib.port_prefix(port), num_inputs);
|
||||
module_manager.add_port(mux_module, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* Update counter */
|
||||
input_port_cnt++;
|
||||
|
@ -1173,7 +1173,7 @@ void build_cmos_mux_module(ModuleManager& module_manager,
|
|||
vtr::vector<MuxInputId, ModuleNetId> mux_input_nets = build_mux_module_input_buffers(module_manager, circuit_lib, mux_module, mux_model, mux_graph);
|
||||
|
||||
for (const auto& port : mux_output_ports) {
|
||||
BasicPort output_port(circuit_lib.port_lib_name(port), num_outputs);
|
||||
BasicPort output_port(circuit_lib.port_prefix(port), num_outputs);
|
||||
if (SPICE_MODEL_LUT == circuit_lib.model_type(mux_model)) {
|
||||
output_port.set_width(circuit_lib.port_size(port));
|
||||
}
|
||||
|
@ -1185,9 +1185,9 @@ void build_cmos_mux_module(ModuleManager& module_manager,
|
|||
|
||||
size_t sram_port_cnt = 0;
|
||||
for (const auto& port : mux_sram_ports) {
|
||||
BasicPort mem_port(circuit_lib.port_lib_name(port), num_mems);
|
||||
BasicPort mem_port(circuit_lib.port_prefix(port), num_mems);
|
||||
module_manager.add_port(mux_module, mem_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_lib_name(port) + "_inv"), num_mems);
|
||||
BasicPort mem_inv_port(std::string(circuit_lib.port_prefix(port) + "_inv"), num_mems);
|
||||
module_manager.add_port(mux_module, mem_inv_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* Update counter */
|
||||
sram_port_cnt++;
|
||||
|
@ -1305,13 +1305,13 @@ void build_rram_mux_module(ModuleManager& module_manager,
|
|||
/* Add each global port */
|
||||
for (const auto& port : mux_global_ports) {
|
||||
/* Configure each global port */
|
||||
BasicPort global_port(circuit_lib.port_lib_name(port), circuit_lib.port_size(port));
|
||||
BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT);
|
||||
}
|
||||
/* Add each input port */
|
||||
size_t input_port_cnt = 0;
|
||||
for (const auto& port : mux_input_ports) {
|
||||
BasicPort input_port(circuit_lib.port_lib_name(port), num_inputs);
|
||||
BasicPort input_port(circuit_lib.port_prefix(port), num_inputs);
|
||||
module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* Update counter */
|
||||
input_port_cnt++;
|
||||
|
@ -1320,7 +1320,7 @@ void build_rram_mux_module(ModuleManager& module_manager,
|
|||
VTR_ASSERT(1 == input_port_cnt);
|
||||
|
||||
for (const auto& port : mux_output_ports) {
|
||||
BasicPort output_port(circuit_lib.port_lib_name(port), num_outputs);
|
||||
BasicPort output_port(circuit_lib.port_prefix(port), num_outputs);
|
||||
if (SPICE_MODEL_LUT == circuit_lib.model_type(circuit_model)) {
|
||||
output_port.set_width(circuit_lib.port_size(port));
|
||||
}
|
||||
|
@ -1332,7 +1332,7 @@ void build_rram_mux_module(ModuleManager& module_manager,
|
|||
/* IMPORTANT: RRAM-based MUX has an additional BLB pin per level
|
||||
* So, the actual port width of BLB should be added by the number of levels of the MUX graph
|
||||
*/
|
||||
BasicPort blb_port(circuit_lib.port_lib_name(port), num_mems + mux_graph.num_levels());
|
||||
BasicPort blb_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels());
|
||||
module_manager.add_port(module_id, blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
|
||||
|
@ -1341,7 +1341,7 @@ void build_rram_mux_module(ModuleManager& module_manager,
|
|||
/* IMPORTANT: RRAM-based MUX has an additional WL pin per level
|
||||
* So, the actual port width of WL should be added by the number of levels of the MUX graph
|
||||
*/
|
||||
BasicPort wl_port(circuit_lib.port_lib_name(port), num_mems + mux_graph.num_levels());
|
||||
BasicPort wl_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels());
|
||||
module_manager.add_port(module_id, wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/********************************************************************
|
||||
* This file includes most utilized functions that are used to build modules
|
||||
* for global routing architecture of a FPGA fabric
|
||||
* Covering:
|
||||
* 1. Connection blocks
|
||||
* 2. Switch blocks
|
||||
*******************************************************************/
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_geometry.h"
|
||||
#include "device_coordinator.h"
|
||||
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
||||
#include "build_routing_module_utils.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a port for a routing track of a swtich block
|
||||
********************************************************************/
|
||||
ModulePortId find_switch_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const e_side& chan_side,
|
||||
t_rr_node* cur_rr_node,
|
||||
const PORTS& cur_rr_node_direction) {
|
||||
/* Get the index in sb_info of cur_rr_node */
|
||||
int index = rr_gsb.get_node_index(cur_rr_node, chan_side, cur_rr_node_direction);
|
||||
/* Make sure this node is included in this sb_info */
|
||||
VTR_ASSERT((-1 != index)&&(NUM_SIDES != chan_side));
|
||||
|
||||
DeviceCoordinator chan_rr_node_coordinator = rr_gsb.get_side_block_coordinator(chan_side);
|
||||
|
||||
vtr::Point<size_t> chan_port_coord(chan_rr_node_coordinator.get_x(), chan_rr_node_coordinator.get_y());
|
||||
std::string chan_port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(chan_side, index)->type,
|
||||
chan_port_coord, index,
|
||||
rr_gsb.get_chan_node_direction(chan_side, index));
|
||||
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
ModulePortId chan_port_id = module_manager.find_module_port(sb_module, chan_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, chan_port_id));
|
||||
return chan_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate an input port for routing multiplexer inside the switch block
|
||||
* In addition to give the Routing Resource node of the input
|
||||
* Users should provide the side of input, which is different case by case:
|
||||
* 1. When the input is a pin of a CLB/Logic Block, the input_side should
|
||||
* be the side of the node on its grid!
|
||||
* For example, the input pin is on the top side of a switch block
|
||||
* but on the right side of a switch block
|
||||
* +--------+
|
||||
* | |
|
||||
* | Grid |---+
|
||||
* | | |
|
||||
* +--------+ v input_pin
|
||||
* +----------------+
|
||||
* | Switch Block |
|
||||
* +----------------+
|
||||
* 2. When the input is a routing track, the input_side should be
|
||||
* the side of the node locating on the switch block
|
||||
********************************************************************/
|
||||
ModulePortId find_switch_block_module_input_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const e_side& input_side,
|
||||
t_rr_node* input_rr_node) {
|
||||
/* Deposit an invalid value */
|
||||
ModulePortId input_port_id = ModulePortId::INVALID();
|
||||
/* Generate the input port object */
|
||||
switch (input_rr_node->type) {
|
||||
/* case SOURCE: */
|
||||
case OPIN: {
|
||||
/* Find the coordinator (grid_x and grid_y) for the input port */
|
||||
vtr::Point<size_t> input_port_coord(input_rr_node->xlow, input_rr_node->ylow);
|
||||
std::string input_port_name = generate_grid_side_port_name(grids,
|
||||
input_port_coord,
|
||||
input_side,
|
||||
input_rr_node->ptc_num);
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
input_port_id = module_manager.find_module_port(sb_module, input_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, input_port_id));
|
||||
break;
|
||||
}
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
input_port_id = find_switch_block_module_chan_port(module_manager, sb_module,
|
||||
rr_gsb, input_side, input_rr_node, IN_PORT);
|
||||
break;
|
||||
}
|
||||
default: /* SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return input_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a list of input ports for routing multiplexer inside the switch block
|
||||
********************************************************************/
|
||||
std::vector<ModulePortId> find_switch_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes) {
|
||||
std::vector<ModulePortId> input_ports;
|
||||
|
||||
for (auto input_rr_node : input_rr_nodes) {
|
||||
enum e_side input_pin_side = NUM_SIDES;
|
||||
switch (input_rr_node->type) {
|
||||
case OPIN:
|
||||
input_pin_side = rr_gsb.get_opin_node_grid_side(input_rr_node);
|
||||
break;
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
/* The input could be at any side of the switch block, find it */
|
||||
int index = -1;
|
||||
rr_gsb.get_node_side_and_index(input_rr_node, IN_PORT, &input_pin_side, &index);
|
||||
VTR_ASSERT(NUM_SIDES != input_pin_side);
|
||||
break;
|
||||
}
|
||||
default: /* SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
input_ports.push_back(find_switch_block_module_input_port(module_manager, sb_module, rr_gsb, grids, input_pin_side, input_rr_node));
|
||||
}
|
||||
|
||||
return input_ports;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate an input port for routing multiplexer inside the connection block
|
||||
* which is the middle output of a routing track
|
||||
********************************************************************/
|
||||
ModulePortId find_connection_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
t_rr_node* chan_rr_node) {
|
||||
ModulePortId input_port_id;
|
||||
/* Generate the input port object */
|
||||
switch (chan_rr_node->type) {
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
/* Create port description for the routing track middle output */
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
int chan_node_track_id = rr_gsb.get_cb_chan_node_index(cb_type, chan_rr_node);
|
||||
/* Create a port description for the middle output */
|
||||
std::string input_port_name = generate_routing_track_port_name(cb_type,
|
||||
port_coord, chan_node_track_id,
|
||||
IN_PORT);
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
input_port_id = module_manager.find_module_port(cb_module, input_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, input_port_id));
|
||||
break;
|
||||
}
|
||||
default: /* OPIN, SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return input_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a port for a routing track of a swtich block
|
||||
********************************************************************/
|
||||
ModulePortId find_connection_block_module_ipin_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
t_rr_node* src_rr_node) {
|
||||
|
||||
/* Ensure the src_rr_node is an input pin of a CLB */
|
||||
VTR_ASSERT(IPIN == src_rr_node->type);
|
||||
/* Create port description for input pin of a CLB */
|
||||
vtr::Point<size_t> port_coord(src_rr_node->xlow, src_rr_node->ylow);
|
||||
/* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */
|
||||
enum e_side cb_ipin_side = NUM_SIDES;
|
||||
int cb_ipin_index = -1;
|
||||
rr_gsb.get_node_side_and_index(src_rr_node, OUT_PORT, &cb_ipin_side, &cb_ipin_index);
|
||||
/* We need to be sure that drive_rr_node is part of the CB */
|
||||
VTR_ASSERT((-1 != cb_ipin_index)&&(NUM_SIDES != cb_ipin_side));
|
||||
std::string port_name = generate_grid_side_port_name(grids,
|
||||
port_coord,
|
||||
rr_gsb.get_ipin_node_grid_side(cb_ipin_side, cb_ipin_index),
|
||||
rr_gsb.get_ipin_node(cb_ipin_side, cb_ipin_index)->ptc_num);
|
||||
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
ModulePortId ipin_port_id = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, ipin_port_id));
|
||||
return ipin_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a list of routing track middle output ports
|
||||
* for routing multiplexer inside the connection block
|
||||
********************************************************************/
|
||||
std::vector<ModulePortId> find_connection_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes) {
|
||||
std::vector<ModulePortId> input_ports;
|
||||
|
||||
for (auto input_rr_node : input_rr_nodes) {
|
||||
input_ports.push_back(find_connection_block_module_chan_port(module_manager, cb_module, rr_gsb, cb_type, input_rr_node));
|
||||
}
|
||||
|
||||
return input_ports;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef BUILD_ROUTING_MODULE_UTILS_H
|
||||
#define BUILD_ROUTING_MODULE_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
#include "rr_blocks.h"
|
||||
#include "module_manager.h"
|
||||
#include "sides.h"
|
||||
#include "vpr_types.h"
|
||||
|
||||
ModulePortId find_switch_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const e_side& chan_side,
|
||||
t_rr_node* cur_rr_node,
|
||||
const PORTS& cur_rr_node_direction);
|
||||
|
||||
ModulePortId find_switch_block_module_input_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const e_side& input_side,
|
||||
t_rr_node* input_rr_node);
|
||||
|
||||
std::vector<ModulePortId> find_switch_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes);
|
||||
|
||||
ModulePortId find_connection_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
t_rr_node* chan_rr_node);
|
||||
|
||||
ModulePortId find_connection_block_module_ipin_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
t_rr_node* src_rr_node);
|
||||
|
||||
std::vector<ModulePortId> find_connection_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes);
|
||||
|
||||
#endif
|
|
@ -21,135 +21,11 @@
|
|||
#include "fpga_x2p_utils.h"
|
||||
#include "module_manager_utils.h"
|
||||
#include "build_module_graph_utils.h"
|
||||
#include "build_routing_module_utils.h"
|
||||
|
||||
#include "build_routing_modules.h"
|
||||
#include "verilog_global.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a port for a routing track of a swtich block
|
||||
********************************************************************/
|
||||
static
|
||||
ModulePortId find_switch_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const e_side& chan_side,
|
||||
t_rr_node* cur_rr_node,
|
||||
const PORTS& cur_rr_node_direction) {
|
||||
/* Get the index in sb_info of cur_rr_node */
|
||||
int index = rr_gsb.get_node_index(cur_rr_node, chan_side, cur_rr_node_direction);
|
||||
/* Make sure this node is included in this sb_info */
|
||||
VTR_ASSERT((-1 != index)&&(NUM_SIDES != chan_side));
|
||||
|
||||
DeviceCoordinator chan_rr_node_coordinator = rr_gsb.get_side_block_coordinator(chan_side);
|
||||
|
||||
vtr::Point<size_t> chan_port_coord(chan_rr_node_coordinator.get_x(), chan_rr_node_coordinator.get_y());
|
||||
std::string chan_port_name = generate_routing_track_port_name(rr_gsb.get_chan_node(chan_side, index)->type,
|
||||
chan_port_coord, index,
|
||||
rr_gsb.get_chan_node_direction(chan_side, index));
|
||||
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
ModulePortId chan_port_id = module_manager.find_module_port(sb_module, chan_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, chan_port_id));
|
||||
return chan_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate an input port for routing multiplexer inside the switch block
|
||||
* In addition to give the Routing Resource node of the input
|
||||
* Users should provide the side of input, which is different case by case:
|
||||
* 1. When the input is a pin of a CLB/Logic Block, the input_side should
|
||||
* be the side of the node on its grid!
|
||||
* For example, the input pin is on the top side of a switch block
|
||||
* but on the right side of a switch block
|
||||
* +--------+
|
||||
* | |
|
||||
* | Grid |---+
|
||||
* | | |
|
||||
* +--------+ v input_pin
|
||||
* +----------------+
|
||||
* | Switch Block |
|
||||
* +----------------+
|
||||
* 2. When the input is a routing track, the input_side should be
|
||||
* the side of the node locating on the switch block
|
||||
********************************************************************/
|
||||
static
|
||||
ModulePortId find_switch_block_module_input_port(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const e_side& input_side,
|
||||
t_rr_node* input_rr_node) {
|
||||
/* Deposit an invalid value */
|
||||
ModulePortId input_port_id = ModulePortId::INVALID();
|
||||
/* Generate the input port object */
|
||||
switch (input_rr_node->type) {
|
||||
/* case SOURCE: */
|
||||
case OPIN: {
|
||||
/* Find the coordinator (grid_x and grid_y) for the input port */
|
||||
vtr::Point<size_t> input_port_coord(input_rr_node->xlow, input_rr_node->ylow);
|
||||
std::string input_port_name = generate_grid_side_port_name(grids,
|
||||
input_port_coord,
|
||||
input_side,
|
||||
input_rr_node->ptc_num);
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
input_port_id = module_manager.find_module_port(sb_module, input_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, input_port_id));
|
||||
break;
|
||||
}
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
input_port_id = find_switch_block_module_chan_port(module_manager, sb_module,
|
||||
rr_gsb, input_side, input_rr_node, IN_PORT);
|
||||
break;
|
||||
}
|
||||
default: /* SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return input_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a list of input ports for routing multiplexer inside the switch block
|
||||
********************************************************************/
|
||||
static
|
||||
std::vector<ModulePortId> find_switch_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& sb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes) {
|
||||
std::vector<ModulePortId> input_ports;
|
||||
|
||||
for (auto input_rr_node : input_rr_nodes) {
|
||||
enum e_side input_pin_side = NUM_SIDES;
|
||||
switch (input_rr_node->type) {
|
||||
case OPIN:
|
||||
input_pin_side = rr_gsb.get_opin_node_grid_side(input_rr_node);
|
||||
break;
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
/* The input could be at any side of the switch block, find it */
|
||||
int index = -1;
|
||||
rr_gsb.get_node_side_and_index(input_rr_node, IN_PORT, &input_pin_side, &index);
|
||||
VTR_ASSERT(NUM_SIDES != input_pin_side);
|
||||
break;
|
||||
}
|
||||
default: /* SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
input_ports.push_back(find_switch_block_module_input_port(module_manager, sb_module, rr_gsb, grids, input_pin_side, input_rr_node));
|
||||
}
|
||||
|
||||
return input_ports;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a short interconneciton in switch box
|
||||
* There are two cases should be noticed.
|
||||
|
@ -252,6 +128,12 @@ void build_switch_block_mux_module(ModuleManager& module_manager,
|
|||
/* Instanciate the MUX Module */
|
||||
module_manager.add_child_module(sb_module, mux_module);
|
||||
|
||||
/* Give an instance name: this name should be consistent with the block name given in SDC manager,
|
||||
* If you want to bind the SDC generation to modules
|
||||
*/
|
||||
std::string mux_instance_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MUX_INSTANCE_PREFIX, chan_side, chan_node_id, std::string(""));
|
||||
module_manager.set_child_instance_name(sb_module, mux_module, mux_instance_id, mux_instance_name);
|
||||
|
||||
/* Generate input ports that are wired to the input bus of the routing multiplexer */
|
||||
std::vector<ModulePortId> sb_input_port_ids = find_switch_block_module_input_ports(module_manager, sb_module, rr_gsb, grids, drive_rr_nodes);
|
||||
|
||||
|
@ -259,7 +141,7 @@ void build_switch_block_mux_module(ModuleManager& module_manager,
|
|||
std::vector<CircuitPortId> mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true);
|
||||
VTR_ASSERT(1 == mux_model_input_ports.size());
|
||||
/* Find the module port id of the input port */
|
||||
ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_input_ports[0]));
|
||||
ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id));
|
||||
BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id);
|
||||
|
||||
|
@ -278,7 +160,7 @@ void build_switch_block_mux_module(ModuleManager& module_manager,
|
|||
std::vector<CircuitPortId> mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true);
|
||||
VTR_ASSERT(1 == mux_model_output_ports.size());
|
||||
/* Use the port name convention in the circuit library */
|
||||
ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_output_ports[0]));
|
||||
ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id));
|
||||
BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id);
|
||||
ModulePortId sb_output_port_id = find_switch_block_module_chan_port(module_manager, sb_module, rr_gsb, chan_side, cur_rr_node, OUT_PORT);
|
||||
|
@ -550,93 +432,6 @@ void build_switch_block_module(ModuleManager& module_manager,
|
|||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate an input port for routing multiplexer inside the connection block
|
||||
* which is the middle output of a routing track
|
||||
********************************************************************/
|
||||
static
|
||||
ModulePortId find_connection_block_module_chan_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
t_rr_node* chan_rr_node) {
|
||||
ModulePortId input_port_id;
|
||||
/* Generate the input port object */
|
||||
switch (chan_rr_node->type) {
|
||||
case CHANX:
|
||||
case CHANY: {
|
||||
/* Create port description for the routing track middle output */
|
||||
vtr::Point<size_t> port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
int chan_node_track_id = rr_gsb.get_cb_chan_node_index(cb_type, chan_rr_node);
|
||||
/* Create a port description for the middle output */
|
||||
std::string input_port_name = generate_routing_track_port_name(cb_type,
|
||||
port_coord, chan_node_track_id,
|
||||
IN_PORT);
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
input_port_id = module_manager.find_module_port(cb_module, input_port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, input_port_id));
|
||||
break;
|
||||
}
|
||||
default: /* OPIN, SOURCE, IPIN, SINK are invalid*/
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return input_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a port for a routing track of a swtich block
|
||||
********************************************************************/
|
||||
static
|
||||
ModulePortId find_connection_block_module_ipin_port(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const std::vector<std::vector<t_grid_tile>>& grids,
|
||||
t_rr_node* src_rr_node) {
|
||||
|
||||
/* Ensure the src_rr_node is an input pin of a CLB */
|
||||
VTR_ASSERT(IPIN == src_rr_node->type);
|
||||
/* Create port description for input pin of a CLB */
|
||||
vtr::Point<size_t> port_coord(src_rr_node->xlow, src_rr_node->ylow);
|
||||
/* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */
|
||||
enum e_side cb_ipin_side = NUM_SIDES;
|
||||
int cb_ipin_index = -1;
|
||||
rr_gsb.get_node_side_and_index(src_rr_node, OUT_PORT, &cb_ipin_side, &cb_ipin_index);
|
||||
/* We need to be sure that drive_rr_node is part of the CB */
|
||||
VTR_ASSERT((-1 != cb_ipin_index)&&(NUM_SIDES != cb_ipin_side));
|
||||
std::string port_name = generate_grid_side_port_name(grids,
|
||||
port_coord,
|
||||
rr_gsb.get_ipin_node_grid_side(cb_ipin_side, cb_ipin_index),
|
||||
rr_gsb.get_ipin_node(cb_ipin_side, cb_ipin_index)->ptc_num);
|
||||
|
||||
/* Must find a valid port id in the Switch Block module */
|
||||
ModulePortId ipin_port_id = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, ipin_port_id));
|
||||
return ipin_port_id;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate a list of routing track middle output ports
|
||||
* for routing multiplexer inside the connection block
|
||||
********************************************************************/
|
||||
static
|
||||
std::vector<ModulePortId> find_connection_block_module_input_ports(const ModuleManager& module_manager,
|
||||
const ModuleId& cb_module,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const std::vector<t_rr_node*>& input_rr_nodes) {
|
||||
std::vector<ModulePortId> input_ports;
|
||||
|
||||
for (auto input_rr_node : input_rr_nodes) {
|
||||
input_ports.push_back(find_connection_block_module_chan_port(module_manager, cb_module, rr_gsb, cb_type, input_rr_node));
|
||||
}
|
||||
|
||||
return input_ports;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Print a short interconneciton in connection
|
||||
********************************************************************/
|
||||
|
@ -651,7 +446,7 @@ void build_connection_block_module_short_interc(ModuleManager& module_manager,
|
|||
/* Ensure we have only one 1 driver node */
|
||||
VTR_ASSERT_SAFE(1 == src_rr_node->fan_in);
|
||||
|
||||
/* Find the driver node */
|
||||
/* Find the driver node */
|
||||
t_rr_node* drive_rr_node = src_rr_node->drive_rr_nodes[0];
|
||||
|
||||
/* We have OPINs since we may have direct connections:
|
||||
|
@ -726,6 +521,12 @@ void build_connection_block_mux_module(ModuleManager& module_manager,
|
|||
size_t mux_instance_id = module_manager.num_instance(cb_module, mux_module);
|
||||
module_manager.add_child_module(cb_module, mux_module);
|
||||
|
||||
/* Give an instance name: this name should be consistent with the block name given in SDC manager,
|
||||
* If you want to bind the SDC generation to modules
|
||||
*/
|
||||
std::string mux_instance_name = generate_cb_mux_instance_name(CONNECTION_BLOCK_MUX_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, ipin_index), ipin_index, std::string(""));
|
||||
module_manager.set_child_instance_name(cb_module, mux_module, mux_instance_id, mux_instance_name);
|
||||
|
||||
/* TODO: Generate input ports that are wired to the input bus of the routing multiplexer */
|
||||
std::vector<ModulePortId> cb_input_port_ids = find_connection_block_module_input_ports(module_manager, cb_module, rr_gsb, cb_type, drive_rr_nodes);
|
||||
|
||||
|
@ -733,7 +534,7 @@ void build_connection_block_mux_module(ModuleManager& module_manager,
|
|||
std::vector<CircuitPortId> mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_INPUT, true);
|
||||
VTR_ASSERT(1 == mux_model_input_ports.size());
|
||||
/* Find the module port id of the input port */
|
||||
ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_input_ports[0]));
|
||||
ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id));
|
||||
BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id);
|
||||
|
||||
|
@ -752,7 +553,7 @@ void build_connection_block_mux_module(ModuleManager& module_manager,
|
|||
std::vector<CircuitPortId> mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, SPICE_MODEL_PORT_OUTPUT, true);
|
||||
VTR_ASSERT(1 == mux_model_output_ports.size());
|
||||
/* Use the port name convention in the circuit library */
|
||||
ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_lib_name(mux_model_output_ports[0]));
|
||||
ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id));
|
||||
BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id);
|
||||
ModulePortId cb_output_port_id = find_connection_block_module_ipin_port(module_manager, cb_module, rr_gsb, grids, cur_rr_node);
|
||||
|
@ -780,7 +581,7 @@ void build_connection_block_mux_module(ModuleManager& module_manager,
|
|||
/* Give an instance name: this name should be consistent with the block name given in bitstream manager,
|
||||
* If you want to bind the bitstream generation to modules
|
||||
*/
|
||||
std::string mem_instance_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, ipin_index), cur_rr_node->ptc_num, std::string(""));
|
||||
std::string mem_instance_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_gsb.get_ipin_node_grid_side(cb_ipin_side, ipin_index), ipin_index, std::string(""));
|
||||
module_manager.set_child_instance_name(cb_module, mem_module, mem_instance_id, mem_instance_name);
|
||||
|
||||
/* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */
|
||||
|
@ -1044,8 +845,7 @@ void build_flatten_connection_block_modules(ModuleManager& module_manager,
|
|||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
if ( (TRUE != is_cb_exist(cb_type, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
build_connection_block_module(module_manager,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "vpr_types.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include "rr_blocks_utils.h"
|
||||
#include "fpga_x2p_reserved_words.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
|
@ -311,9 +312,7 @@ std::vector<std::vector<size_t>> add_top_module_connection_block_instances(Modul
|
|||
*/
|
||||
const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy);
|
||||
vtr::Point<size_t> cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
const DeviceCoordinator cb_coordinator = rr_gsb.get_cb_coordinator(cb_type);
|
||||
if ( (TRUE != is_cb_exist(cb_type, cb_coordinator.get_x(), cb_coordinator.get_y()))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if ( false == rr_gsb.is_cb_exist(cb_type) ) {
|
||||
continue;
|
||||
}
|
||||
/* If we use compact routing hierarchy, we should instanciate the unique module of SB */
|
||||
|
@ -522,8 +521,12 @@ void add_top_module_nets_connect_grids_and_cb(ModuleManager& module_manager,
|
|||
DeviceCoordinator module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
|
||||
/* Skip those Connection blocks that do not exist */
|
||||
if ( (TRUE != is_cb_exist(cb_type, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip if the cb does not contain any configuration bits! */
|
||||
if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -684,16 +687,14 @@ void add_top_module_nets_connect_sb_and_cb(ModuleManager& module_manager,
|
|||
* FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then use is_cb_exist()
|
||||
*/
|
||||
if ( TOP == side_manager.get_side() || LEFT == side_manager.get_side() ) {
|
||||
if ( (TRUE != is_cb_exist(cb_type, module_gsb_cb_coordinate.get_x(), module_gsb_cb_coordinate.get_y()))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if ( false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side() ) {
|
||||
const RRGSB& adjancent_gsb = L_device_rr_gsb.get_gsb(module_gsb_cb_coordinate);
|
||||
if ( (TRUE != is_cb_exist(cb_type, module_gsb_cb_coordinate.get_x(), module_gsb_cb_coordinate.get_y()))
|
||||
|| (true != adjancent_gsb.is_cb_exist(cb_type))) {
|
||||
const RRGSB& adjacent_gsb = L_device_rr_gsb.get_gsb(module_gsb_cb_coordinate);
|
||||
if ( false == adjacent_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,17 +92,17 @@ void add_module_nets_clb2clb_direct_connection(ModuleManager& module_manager,
|
|||
/* Find inputs and outputs of the direct circuit module */
|
||||
std::vector<CircuitPortId> direct_input_ports = circuit_lib.model_ports_by_type(direct.circuit_model, SPICE_MODEL_PORT_INPUT, true);
|
||||
VTR_ASSERT(1 == direct_input_ports.size());
|
||||
ModulePortId direct_input_port_id = module_manager.find_module_port(direct_module, circuit_lib.port_lib_name(direct_input_ports[0]));
|
||||
ModulePortId direct_input_port_id = module_manager.find_module_port(direct_module, circuit_lib.port_prefix(direct_input_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(direct_module, direct_input_port_id));
|
||||
VTR_ASSERT(1 == module_manager.module_port(direct_module, direct_input_port_id).get_width());
|
||||
|
||||
std::vector<CircuitPortId> direct_output_ports = circuit_lib.model_ports_by_type(direct.circuit_model, SPICE_MODEL_PORT_OUTPUT, true);
|
||||
VTR_ASSERT(1 == direct_output_ports.size());
|
||||
ModulePortId direct_output_port_id = module_manager.find_module_port(direct_module, circuit_lib.port_lib_name(direct_output_ports[0]));
|
||||
ModulePortId direct_output_port_id = module_manager.find_module_port(direct_module, circuit_lib.port_prefix(direct_output_ports[0]));
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(direct_module, direct_output_port_id));
|
||||
VTR_ASSERT(1 == module_manager.module_port(direct_module, direct_output_port_id).get_width());
|
||||
|
||||
for (size_t pin_id : src_clb_port.pins()) {
|
||||
for (size_t pin_id = 0; pin_id < src_clb_port.pins().size(); ++pin_id) {
|
||||
/* Generate the pin name of source port/pin in the grid */
|
||||
size_t src_pin_height = find_grid_pin_height(grids, src_clb_coord, src_clb_port.pins()[pin_id]);
|
||||
e_side src_pin_grid_side = find_grid_pin_side(device_size, grids, src_clb_coord, src_pin_height, src_clb_port.pins()[pin_id]);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*******************************************************************/
|
||||
#include "vtr_assert.h"
|
||||
|
||||
#include "rr_blocks_utils.h"
|
||||
#include "fpga_x2p_utils.h"
|
||||
#include "fpga_x2p_naming.h"
|
||||
|
||||
|
@ -31,8 +32,12 @@ void organize_top_module_tile_cb_modules(ModuleManager& module_manager,
|
|||
const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* If the CB does not exist, we can skip addition */
|
||||
if ( (TRUE != is_cb_exist(cb_type, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)))
|
||||
|| (true != rr_gsb.is_cb_exist(cb_type))) {
|
||||
if ( false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip if the cb does not contain any configuration bits! */
|
||||
if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,51 +52,6 @@ void build_wire_module(ModuleManager& module_manager,
|
|||
add_circuit_model_to_module_manager(module_manager, circuit_lib, wire_model);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Build module of a routing track wire segment
|
||||
* Routing track wire, which is 1-input and dual output
|
||||
* This type of wires are used in the global routing architecture.
|
||||
* One of the output is wired to another Switch block multiplexer,
|
||||
* while the mid-output is wired to a Connection block multiplexer.
|
||||
*
|
||||
* | CLB |
|
||||
* +------------+
|
||||
* ^
|
||||
* |
|
||||
* +------------------------------+
|
||||
* | Connection block multiplexer |
|
||||
* +------------------------------+
|
||||
* ^
|
||||
* | mid-output +--------------
|
||||
* +--------------------+ |
|
||||
* input --->| Routing track wire |--------->| Switch Block
|
||||
* +--------------------+ output |
|
||||
* +--------------
|
||||
*
|
||||
*******************************************************************/
|
||||
static
|
||||
void build_routing_wire_module(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& wire_model,
|
||||
const std::string& wire_subckt_name) {
|
||||
/* Find the input port, output port*/
|
||||
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(wire_model, SPICE_MODEL_PORT_INPUT, true);
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(wire_model, SPICE_MODEL_PORT_OUTPUT, true);
|
||||
|
||||
/* Make sure the port size is what we want */
|
||||
VTR_ASSERT (1 == input_ports.size());
|
||||
VTR_ASSERT (1 == output_ports.size());
|
||||
VTR_ASSERT (1 == circuit_lib.port_size(input_ports[0]));
|
||||
VTR_ASSERT (1 == circuit_lib.port_size(output_ports[0]));
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId wire_module = add_circuit_model_to_module_manager(module_manager, circuit_lib, wire_model, wire_subckt_name);
|
||||
|
||||
/* Add a mid-output port to the module */
|
||||
BasicPort module_mid_output_port(generate_segment_wire_mid_output_name(circuit_lib.port_lib_name(output_ports[0])), circuit_lib.port_size(output_ports[0]));
|
||||
module_manager.add_port(wire_module, module_mid_output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function will only create wire modules with a number of
|
||||
* ports that are defined by users.
|
||||
|
@ -104,8 +59,7 @@ void build_routing_wire_module(ModuleManager& module_manager,
|
|||
* by Verilog/SPICE writers
|
||||
*******************************************************************/
|
||||
void build_wire_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::vector<t_segment_inf> routing_segments) {
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
|
@ -122,23 +76,6 @@ void build_wire_modules(ModuleManager& module_manager,
|
|||
build_wire_module(module_manager, circuit_lib, wire_model);
|
||||
}
|
||||
|
||||
for (const auto& seg : routing_segments) {
|
||||
VTR_ASSERT( CircuitModelId::INVALID() != seg.circuit_model);
|
||||
VTR_ASSERT( SPICE_MODEL_CHAN_WIRE == circuit_lib.model_type(seg.circuit_model));
|
||||
/* Bypass user-defined circuit models */
|
||||
if ( (!circuit_lib.model_spice_netlist(seg.circuit_model).empty())
|
||||
&& (!circuit_lib.model_verilog_netlist(seg.circuit_model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Give a unique name for subckt of wire_model of segment,
|
||||
* circuit_model name is unique, and segment id is unique as well
|
||||
*/
|
||||
std::string segment_wire_subckt_name = generate_segment_wire_subckt_name(circuit_lib.model_name(seg.circuit_model), &seg - &routing_segments[0]);
|
||||
|
||||
/* Print a Verilog module */
|
||||
build_routing_wire_module(module_manager, circuit_lib, seg.circuit_model, segment_wire_subckt_name);
|
||||
}
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "module_manager.h"
|
||||
|
||||
void build_wire_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::vector<t_segment_inf> routing_segments);
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* This function includes the writer for generating exchangeable
|
||||
* information, in order to interface different simulators
|
||||
********************************************************************/
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#define MINI_CASE_SENSITIVE
|
||||
#include "ini.h"
|
||||
|
@ -18,17 +18,39 @@
|
|||
#include "verilog_global.h"
|
||||
#include "simulation_info_writer.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Local Variable
|
||||
********************************************************************/
|
||||
constexpr char* DEFAULT_SIMULATION_INI_FILE_NAME = "simulation_deck_info.ini";
|
||||
|
||||
/*********************************************************************
|
||||
* Top-level function to write an ini file which contains exchangeable
|
||||
* information, in order to interface different Verilog simulators
|
||||
********************************************************************/
|
||||
void print_verilog_simulation_info(const int &num_operating_clock_cycles,
|
||||
const std::string &verilog_dir_formatted,
|
||||
const std::string &chomped_circuit_name,
|
||||
const std::string &src_dir_path,
|
||||
const size_t &num_program_clock_cycles,
|
||||
const float &prog_clock_freq,
|
||||
const float &op_clock_freq) {
|
||||
void print_verilog_simulation_info(const std::string& simulation_ini_filename,
|
||||
const std::string& parent_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& src_dir,
|
||||
const size_t& num_program_clock_cycles,
|
||||
const int& num_operating_clock_cycles,
|
||||
const float& prog_clock_freq,
|
||||
const float& op_clock_freq) {
|
||||
|
||||
/* Start time count */
|
||||
clock_t t_start = clock();
|
||||
|
||||
/* Use default name if user does not provide one */
|
||||
std::string ini_fname;
|
||||
if (true == simulation_ini_filename.empty()) {
|
||||
ini_fname = parent_dir + std::string(DEFAULT_SIMULATION_INI_FILE_NAME);
|
||||
} else {
|
||||
ini_fname = simulation_ini_filename;
|
||||
}
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"Writing exchangeable file containing simulation information: %s...",
|
||||
ini_fname.c_str());
|
||||
|
||||
mINI::INIStructure ini;
|
||||
// std::map<char, int> units_map;
|
||||
// units_map['s']=1; // units_map['ms']=1E-3; // units_map['us']=1E-6;
|
||||
|
@ -40,16 +62,23 @@ void print_verilog_simulation_info(const int &num_operating_clock_cycles,
|
|||
1. / prog_clock_freq,
|
||||
num_operating_clock_cycles,
|
||||
1. / op_clock_freq);
|
||||
|
||||
ini["SIMULATION_DECK"]["PROJECTNAME "] = "ModelSimProject";
|
||||
ini["SIMULATION_DECK"]["BENCHMARK "] = chomped_circuit_name;
|
||||
ini["SIMULATION_DECK"]["TOP_TB"] = chomped_circuit_name + std::string("_top_formal_verification_random_tb");
|
||||
ini["SIMULATION_DECK"]["BENCHMARK "] = circuit_name;
|
||||
ini["SIMULATION_DECK"]["TOP_TB"] = circuit_name + std::string("_top_formal_verification_random_tb");
|
||||
ini["SIMULATION_DECK"]["SIMTIME "] = std::to_string(simulation_time_period);
|
||||
ini["SIMULATION_DECK"]["UNIT "] = "ms";
|
||||
ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir_path);
|
||||
ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir);
|
||||
ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(defines_verilog_file_name);
|
||||
ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(chomped_circuit_name + "_include_netlists.v");
|
||||
ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + "_include_netlists.v");
|
||||
|
||||
mINI::INIFile file("SimulationDeckInfo.ini");
|
||||
mINI::INIFile file(ini_fname);
|
||||
file.generate(ini, true);
|
||||
|
||||
/* End time count */
|
||||
clock_t t_end = clock();
|
||||
|
||||
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||
vpr_printf(TIO_MESSAGE_INFO,
|
||||
"took %g seconds\n",
|
||||
run_time_sec);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
void print_verilog_simulation_info(const int &num_operating_clock_cycles,
|
||||
const std::string &verilog_dir_formatted,
|
||||
const std::string &chomped_circuit_name,
|
||||
const std::string &src_dir_path,
|
||||
const size_t &num_program_clock_cycles,
|
||||
const float &prog_clock_freq,
|
||||
const float &op_clock_freq);
|
||||
void print_verilog_simulation_info(const std::string& simulation_ini_filename,
|
||||
const std::string& parent_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& src_dir,
|
||||
const size_t& num_program_clock_cycles,
|
||||
const int& num_operating_clock_cycles,
|
||||
const float& prog_clock_freq,
|
||||
const float& op_clock_freq);
|
||||
#endif
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#include "verilog_sdc.h"
|
||||
#include "verilog_formality_autodeck.h"
|
||||
#include "verilog_sdc_pb_types.h"
|
||||
#include "verilog_include_netlists.h"
|
||||
#include "verilog_auxiliary_netlists.h"
|
||||
#include "simulation_info_writer.h"
|
||||
|
||||
#include "verilog_api.h"
|
||||
|
@ -152,14 +152,10 @@ void vpr_fpga_verilog(ModuleManager& module_manager,
|
|||
char* fm_dir_path = NULL;
|
||||
char* top_netlist_file = NULL;
|
||||
char* top_netlist_path = NULL;
|
||||
char* top_testbench_file_name = NULL;
|
||||
char* top_testbench_file_path = NULL;
|
||||
char* blif_testbench_file_name = NULL;
|
||||
char* blif_testbench_file_path = NULL;
|
||||
char* bitstream_file_name = NULL;
|
||||
char* bitstream_file_path = NULL;
|
||||
char* autocheck_top_testbench_file_name = NULL;
|
||||
char* autocheck_top_testbench_file_path = NULL;
|
||||
|
||||
char* chomped_parent_dir = NULL;
|
||||
char* chomped_circuit_name = NULL;
|
||||
|
@ -268,11 +264,16 @@ void vpr_fpga_verilog(ModuleManager& module_manager,
|
|||
init_pb_types_num_iopads();
|
||||
/* init_grids_num_mode_bits(); */
|
||||
|
||||
dump_verilog_defines_preproc(src_dir_path,
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts);
|
||||
/* Print Verilog files containing preprocessing flags */
|
||||
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts);
|
||||
|
||||
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts);
|
||||
/*
|
||||
dump_verilog_simulation_preproc(src_dir_path,
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts);
|
||||
*/
|
||||
|
||||
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric
|
||||
* Note that this function MUST be called before Verilog generation of
|
||||
|
@ -390,19 +391,21 @@ void vpr_fpga_verilog(ModuleManager& module_manager,
|
|||
my_free(bitstream_file_path);
|
||||
}
|
||||
|
||||
/* dump verilog testbench only for top-level: ONLY valid when bitstream is generated! */
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_top_testbench) {
|
||||
top_testbench_file_name = my_strcat(chomped_circuit_name, top_testbench_verilog_file_postfix);
|
||||
top_testbench_file_path = my_strcat(src_dir_path, top_testbench_file_name);
|
||||
dump_verilog_top_testbench(sram_verilog_orgz_info, chomped_circuit_name, top_testbench_file_path,
|
||||
src_dir_path, *(Arch.spice));
|
||||
/* Free */
|
||||
my_free(top_testbench_file_name);
|
||||
my_free(top_testbench_file_path);
|
||||
}
|
||||
|
||||
/* Collect global ports from the circuit library
|
||||
* TODO: move outside this function
|
||||
*/
|
||||
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(Arch.spice->circuit_lib);
|
||||
|
||||
/* dump verilog testbench only for top-level: ONLY valid when bitstream is generated! */
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_top_testbench) {
|
||||
std::string top_testbench_file_path = std::string(src_dir_path)
|
||||
+ std::string(chomped_circuit_name)
|
||||
+ std::string(top_testbench_verilog_file_postfix);
|
||||
/* TODO: this is an old function, to be shadowed */
|
||||
dump_verilog_top_testbench(sram_verilog_orgz_info, chomped_circuit_name, top_testbench_file_path.c_str(),
|
||||
src_dir_path, *(Arch.spice));
|
||||
}
|
||||
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_formal_verification_top_netlist) {
|
||||
std::string formal_verification_top_netlist_file_path = std::string(src_dir_path)
|
||||
+ std::string(chomped_circuit_name)
|
||||
|
@ -431,21 +434,43 @@ void vpr_fpga_verilog(ModuleManager& module_manager,
|
|||
print_verilog_random_top_testbench(std::string(chomped_circuit_name), random_top_testbench_file_path,
|
||||
std::string(src_dir_path), L_logical_blocks,
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts, Arch.spice->spice_params);
|
||||
print_verilog_simulation_info(Arch.spice->spice_params.meas_params.sim_num_clock_cycle,
|
||||
std::string(msim_dir_path),
|
||||
}
|
||||
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_simulation_ini) {
|
||||
/* Print exchangeable files which contains simulation settings */
|
||||
std::string simulation_ini_file_name;
|
||||
if (NULL != vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.simulation_ini_path) {
|
||||
simulation_ini_file_name = std::string(vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.simulation_ini_path);
|
||||
}
|
||||
print_verilog_simulation_info(simulation_ini_file_name,
|
||||
std::string(format_dir_path(chomped_parent_dir)),
|
||||
std::string(chomped_circuit_name),
|
||||
std::string(src_dir_path),
|
||||
bitstream_manager.bits().size(),
|
||||
Arch.spice->spice_params.meas_params.sim_num_clock_cycle,
|
||||
Arch.spice->spice_params.stimulate_params.prog_clock_freq,
|
||||
Arch.spice->spice_params.stimulate_params.op_clock_freq);
|
||||
}
|
||||
|
||||
if (TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_autocheck_top_testbench) {
|
||||
autocheck_top_testbench_file_name = my_strcat(chomped_circuit_name, autocheck_top_testbench_verilog_file_postfix);
|
||||
autocheck_top_testbench_file_path = my_strcat(src_dir_path, autocheck_top_testbench_file_name);
|
||||
std::string autocheck_top_testbench_file_path = std::string(src_dir_path)
|
||||
+ std::string(chomped_circuit_name)
|
||||
+ std::string(autocheck_top_testbench_verilog_file_postfix);
|
||||
/* TODO: this is an old function, to be shadowed */
|
||||
/*
|
||||
dump_verilog_autocheck_top_testbench(sram_verilog_orgz_info, chomped_circuit_name,
|
||||
autocheck_top_testbench_file_path, src_dir_path,
|
||||
autocheck_top_testbench_file_path.c_str(), src_dir_path,
|
||||
vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts, *(Arch.spice));
|
||||
*/
|
||||
/* TODO: new function: to be tested */
|
||||
print_verilog_top_testbench(module_manager, bitstream_manager, fabric_bitstream,
|
||||
sram_verilog_orgz_info->type,
|
||||
Arch.spice->circuit_lib, global_ports,
|
||||
L_logical_blocks, device_size, L_grids, L_blocks,
|
||||
std::string(chomped_circuit_name),
|
||||
autocheck_top_testbench_file_path,
|
||||
std::string(src_dir_path),
|
||||
Arch.spice->spice_params);
|
||||
}
|
||||
|
||||
/* Output Modelsim Autodeck scripts */
|
||||
|
@ -489,9 +514,15 @@ void vpr_fpga_verilog(ModuleManager& module_manager,
|
|||
sram_verilog_orgz_info->type);
|
||||
}
|
||||
|
||||
write_include_netlists(src_dir_path,
|
||||
chomped_circuit_name,
|
||||
*(Arch.spice) );
|
||||
/* Print a Verilog file including all the netlists that have been generated */
|
||||
std::string ref_verilog_benchmark_file_name;
|
||||
if (NULL != vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.reference_verilog_benchmark_file) {
|
||||
ref_verilog_benchmark_file_name = std::string(vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.reference_verilog_benchmark_file);
|
||||
}
|
||||
print_include_netlists(std::string(src_dir_path),
|
||||
std::string(chomped_circuit_name),
|
||||
ref_verilog_benchmark_file_name,
|
||||
Arch.spice->circuit_lib);
|
||||
|
||||
vpr_printf(TIO_MESSAGE_INFO, "Outputted %lu Verilog modules in total.\n", module_manager.num_modules());
|
||||
|
||||
|
|
|
@ -348,7 +348,7 @@ void dump_verilog_top_auto_testbench_check(FILE* fp){
|
|||
|
||||
void dump_verilog_autocheck_top_testbench(t_sram_orgz_info* cur_sram_orgz_info,
|
||||
char* circuit_name,
|
||||
char* top_netlist_name,
|
||||
const char* top_netlist_name,
|
||||
char* verilog_dir_path,
|
||||
t_syn_verilog_opts fpga_verilog_opts,
|
||||
t_spice verilog) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue