diff --git a/.travis.yml b/.travis.yml index d4f2d90ad..431f5a02c 100644 --- a/.travis.yml +++ b/.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= diff --git a/.travis/script.sh b/.travis/script.sh index 09187ef2a..dc0f1f18a 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -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" diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f9964539..a6d51a7c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/libs/external/libini/CMakeLists.txt b/libs/external/libini/CMakeLists.txt index 2730c6325..c09ff863b 100644 --- a/libs/external/libini/CMakeLists.txt +++ b/libs/external/libini/CMakeLists.txt @@ -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) \ No newline at end of file +set_target_properties(libini PROPERTIES PREFIX "" LINKER_LANGUAGE CXX) diff --git a/openfpga_flow/VerilogNetlists/dpram.v b/openfpga_flow/VerilogNetlists/dpram.v new file mode 100644 index 000000000..45bdbecc0 --- /dev/null +++ b/openfpga_flow/VerilogNetlists/dpram.v @@ -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 diff --git a/openfpga_flow/VerilogNetlists/ff.v b/openfpga_flow/VerilogNetlists/ff.v index bd556f936..f0097019e 100644 --- a/openfpga_flow/VerilogNetlists/ff.v +++ b/openfpga_flow/VerilogNetlists/ff.v @@ -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; diff --git a/openfpga_flow/VerilogNetlists/mux2.v b/openfpga_flow/VerilogNetlists/mux2.v new file mode 100644 index 000000000..b539b7a69 --- /dev/null +++ b/openfpga_flow/VerilogNetlists/mux2.v @@ -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 diff --git a/openfpga_flow/arch/template/k4_N4_sram_chain_FC_behavioral_verilog_template.xml b/openfpga_flow/arch/template/k4_N4_sram_chain_FC_behavioral_verilog_template.xml index 29badf323..0c932d704 100644 --- a/openfpga_flow/arch/template/k4_N4_sram_chain_FC_behavioral_verilog_template.xml +++ b/openfpga_flow/arch/template/k4_N4_sram_chain_FC_behavioral_verilog_template.xml @@ -321,12 +321,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml index fa32d4e41..9af75ba69 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_1IO_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml index 10cd18ddd..b053a80cd 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_DPRAM_template.xml @@ -146,7 +146,7 @@ - + @@ -163,8 +163,8 @@ - - + + @@ -344,14 +344,14 @@ - + - - + + @@ -386,17 +386,17 @@ - + - + - + @@ -421,16 +421,16 @@ - + - - - + + + - + @@ -613,31 +613,31 @@ - + - - - + + + - + - - - + + + - + - - - - + + + + - - + + @@ -647,8 +647,8 @@ - - + + @@ -656,8 +656,8 @@ - - + + @@ -674,13 +674,13 @@ - - + memory_dp.d_in - memory_dp.clk memory_dp.wen memory_dp.waddr + memory_dp.clk memory_dp.wen memory_dp.waddr memory_dp.d_out memory_dp.ren memory_dp.raddr - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml index 4d26f30ad..f22464011 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_behavioral_verilog_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml index 48e1503c7..fd4ef33fb 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_local_encoder_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml index ff9d23ac1..7b20e6ff9 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_non_lut_intermediate_buffer_template.xml @@ -377,12 +377,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_stdcell_mux2_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_stdcell_mux2_template.xml new file mode 100644 index 000000000..410edebcf --- /dev/null +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_stdcell_mux2_template.xml @@ -0,0 +1,1030 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + 10e-12 10e-12 + + + 10e-12 10e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 255e-12 + 255e-12 + 255e-12 + 255e-12 + 255e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 202e-12 + 202e-12 + 202e-12 + 202e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clb.cin clb.cin_trick clb.regin clb.clk + clb.I0[9:0] clb.I1[9:0] clb.O[9:0] + clb.cout clb.regout clb.I2[9:0] clb.I3[9:0] clb.O[19:10] + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml index fa32d4e41..9af75ba69 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml index ce1615d0b..4cea2b20c 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tileable_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml index c362da80c..078a876da 100644 --- a/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml +++ b/openfpga_flow/arch/template/k6_N10_sram_chain_HC_tree_mux_template.xml @@ -379,12 +379,12 @@ - + - + diff --git a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.act b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.act index 9bc344191..b91e3bfa6 100644 --- a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.act +++ b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.act @@ -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 diff --git a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.blif b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.blif index 7ba78bd10..69f1a70c6 100644 --- a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.blif +++ b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.blif @@ -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_ diff --git a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.v b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.v index b1e082d06..5a7719ec8 100644 --- a/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.v +++ b/openfpga_flow/benchmarks/mcnc_big20/apex2/apex2.v @@ -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_; diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act new file mode 100644 index 000000000..d8c59ec70 --- /dev/null +++ b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.act @@ -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 diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif new file mode 100644 index 000000000..a76ccdef5 --- /dev/null +++ b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.blif @@ -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 diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v new file mode 100644 index 000000000..c963eb22f --- /dev/null +++ b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder.v @@ -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 diff --git a/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v new file mode 100644 index 000000000..4276260c2 --- /dev/null +++ b/openfpga_flow/benchmarks/pipelined_8bit_adder/pipelined_8bit_adder_formal_random_top_tb.v @@ -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 diff --git a/openfpga_flow/scripts/run_simulation_task.py b/openfpga_flow/scripts/run_simulation_task.py new file mode 100644 index 000000000..ad1e5f863 --- /dev/null +++ b/openfpga_flow/scripts/run_simulation_task.py @@ -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) diff --git a/openfpga_flow/tasks/blif_vpr_flow/config/task.conf b/openfpga_flow/tasks/blif_vpr_flow/config/task.conf deleted file mode 100644 index 152fcd6be..000000000 --- a/openfpga_flow/tasks/blif_vpr_flow/config/task.conf +++ /dev/null @@ -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= - diff --git a/openfpga_flow/tasks/heterogeneous_dpram/config/task.conf b/openfpga_flow/tasks/heterogeneous_dpram/config/task.conf new file mode 100644 index 000000000..b567b74e0 --- /dev/null +++ b/openfpga_flow/tasks/heterogeneous_dpram/config/task.conf @@ -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= diff --git a/openfpga_flow/tasks/mcnc_big20/config/task.conf b/openfpga_flow/tasks/mcnc_big20/config/task.conf index 50d2402b8..05961d06b 100644 --- a/openfpga_flow/tasks/mcnc_big20/config/task.conf +++ b/openfpga_flow/tasks/mcnc_big20/config/task.conf @@ -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= diff --git a/openfpga_flow/tasks/multi_mode/config/task.conf b/openfpga_flow/tasks/multi_mode/config/task.conf index 5157bb235..18f471cf7 100644 --- a/openfpga_flow/tasks/multi_mode/config/task.conf +++ b/openfpga_flow/tasks/multi_mode/config/task.conf @@ -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= + diff --git a/openfpga_flow/tasks/single_mode/config/task.conf b/openfpga_flow/tasks/single_mode/config/task.conf index 4d721002b..e3e2d3e8c 100644 --- a/openfpga_flow/tasks/single_mode/config/task.conf +++ b/openfpga_flow/tasks/single_mode/config/task.conf @@ -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= diff --git a/vpr7_x2p/libarchfpga/SRC/check_circuit_library.cpp b/vpr7_x2p/libarchfpga/SRC/check_circuit_library.cpp index 4ceab180e..bf70df367 100644 --- a/vpr7_x2p/libarchfpga/SRC/check_circuit_library.cpp +++ b/vpr7_x2p/libarchfpga/SRC/check_circuit_library.cpp @@ -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; } diff --git a/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.cpp b/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.cpp index cb7641b0a..22145b38f 100644 --- a/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.cpp +++ b/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.cpp @@ -226,7 +226,7 @@ std::vector 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 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 find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib) { + std::vector 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::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; +} diff --git a/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.h b/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.h index c0c8251c7..056777c61 100644 --- a/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.h +++ b/vpr7_x2p/libarchfpga/SRC/circuit_library_utils.h @@ -29,4 +29,6 @@ size_t find_circuit_num_config_bits(const CircuitLibrary& circuit_lib, std::vector find_circuit_library_global_ports(const CircuitLibrary& circuit_lib); +std::vector find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib); + #endif diff --git a/vpr7_x2p/vpr/CMakeLists.txt b/vpr7_x2p/vpr/CMakeLists.txt index 0fa11717e..2e70b82fa 100644 --- a/vpr7_x2p/vpr/CMakeLists.txt +++ b/vpr7_x2p/vpr/CMakeLists.txt @@ -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 diff --git a/vpr7_x2p/vpr/SRC/base/OptionTokens.c b/vpr7_x2p/vpr/SRC/base/OptionTokens.c index 78b956ce3..b8ef5c44a 100644 --- a/vpr7_x2p/vpr/SRC/base/OptionTokens.c +++ b/vpr7_x2p/vpr/SRC/base/OptionTokens.c @@ -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 diff --git a/vpr7_x2p/vpr/SRC/base/OptionTokens.h b/vpr7_x2p/vpr/SRC/base/OptionTokens.h index 46a44ac05..5b20ef1cd 100644 --- a/vpr7_x2p/vpr/SRC/base/OptionTokens.h +++ b/vpr7_x2p/vpr/SRC/base/OptionTokens.h @@ -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, diff --git a/vpr7_x2p/vpr/SRC/base/ReadOptions.c b/vpr7_x2p/vpr/SRC/base/ReadOptions.c index 108e9d355..e51dcf530 100644 --- a/vpr7_x2p/vpr/SRC/base/ReadOptions.c +++ b/vpr7_x2p/vpr/SRC/base/ReadOptions.c @@ -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; diff --git a/vpr7_x2p/vpr/SRC/base/ReadOptions.h b/vpr7_x2p/vpr/SRC/base/ReadOptions.h index a637f5792..a242ba0b1 100644 --- a/vpr7_x2p/vpr/SRC/base/ReadOptions.h +++ b/vpr7_x2p/vpr/SRC/base/ReadOptions.h @@ -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; }; diff --git a/vpr7_x2p/vpr/SRC/base/SetupVPR.c b/vpr7_x2p/vpr/SRC/base/SetupVPR.c index 5b0e90004..f1443c4f7 100644 --- a/vpr7_x2p/vpr/SRC/base/SetupVPR.c +++ b/vpr7_x2p/vpr/SRC/base/SetupVPR.c @@ -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; diff --git a/vpr7_x2p/vpr/SRC/base/vpr_api.c b/vpr7_x2p/vpr/SRC/base/vpr_api.c index 7d1730883..3b2471a72 100644 --- a/vpr7_x2p/vpr/SRC/base/vpr_api.c +++ b/vpr7_x2p/vpr/SRC/base/vpr_api.c @@ -211,6 +211,8 @@ void vpr_print_usage(void) { vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_verilog_report_timing_rpt_path \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 \n"); /* Xifan Tang: Bitstream generator */ vpr_printf(TIO_MESSAGE_INFO, "Bitstream Generator Options:\n"); vpr_printf(TIO_MESSAGE_INFO, "\t--fpga_bitstream_generator\n"); diff --git a/vpr7_x2p/vpr/SRC/base/vpr_types.h b/vpr7_x2p/vpr/SRC/base/vpr_types.h index 7b87d664a..0c3491d8d 100755 --- a/vpr7_x2p/vpr/SRC/base/vpr_types.h +++ b/vpr7_x2p/vpr/SRC/base/vpr_types.h @@ -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 */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp new file mode 100644 index 000000000..9eac8c52a --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp @@ -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 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& 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& grid_coordinate, + const std::vector>& L_grids, + const std::vector& 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 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& device_size, + const std::vector>& L_grids, + const std::vector& 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(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 io_sides{TOP, RIGHT, BOTTOM, LEFT}; + std::map>> io_coordinates; + + /* TOP side*/ + for (size_t ix = 1; ix < device_size.x() - 1; ++ix) { + io_coordinates[TOP].push_back(vtr::Point(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(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(ix, 0)); + } + + /* LEFT side */ + for (size_t iy = 1; iy < device_size.y() - 1; ++iy) { + io_coordinates[LEFT].push_back(vtr::Point(0, iy)); + } + + /* Add instances of I/O grids to top_module */ + for (const e_side& io_side : io_sides) { + for (const vtr::Point& 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); + } + } +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.h new file mode 100644 index 000000000..a198cc129 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.h @@ -0,0 +1,16 @@ +#ifndef ANALYSIS_SDC_GRID_WRITER_H +#define ANALYSIS_SDC_GRID_WRITER_H + +#include +#include +#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& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp new file mode 100644 index 000000000..0509c08de --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp @@ -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 + +#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>& 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 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 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 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 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 mux_instance_to_net_map; + + /* Disable all the output port (grid input pins), which are not used by benchmark */ + std::vector 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 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 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>& 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>& 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>& 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 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 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 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 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 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 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 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>& 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); + } + } +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.h new file mode 100644 index 000000000..4b8fcc033 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.h @@ -0,0 +1,22 @@ +#ifndef ANALYSIS_SDC_ROUTING_WRITER_H +#define ANALYSIS_SDC_ROUTING_WRITER_H + +#include +#include +#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>& 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>& grids, + const ModuleManager& module_manager, + const DeviceRRGSB& L_device_rr_gsb, + const bool& compact_routing_hierarchy); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp new file mode 100644 index 000000000..d4297d8dc --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp @@ -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 +#include +#include + +#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& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const std::vector& 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 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 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& 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& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::vector& 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); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.h new file mode 100644 index 000000000..252c76c71 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.h @@ -0,0 +1,22 @@ +#ifndef ANALYSIS_SDC_WRITER_H +#define ANALYSIS_SDC_WRITER_H + +#include +#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& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const bool& compact_routing_hierarchy); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp new file mode 100644 index 000000000..3c2885fd3 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp @@ -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 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::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 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 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::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; + } +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h new file mode 100644 index 000000000..7b5ff9348 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h @@ -0,0 +1,40 @@ +#ifndef ANALYSIS_SDC_WRITER_UTILS_H +#define ANALYSIS_SDC_WRITER_UTILS_H + +#include +#include +#include +#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 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 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 mux_instance_to_net_map); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.cpp new file mode 100644 index 000000000..8032daca4 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.cpp @@ -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 +#include + +#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); +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.h new file mode 100644 index 000000000..d27b292ef --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_grid_writer.h @@ -0,0 +1,11 @@ +#ifndef PNR_SDC_GRID_WRITER_H +#define PNR_SDC_GRID_WRITER_H + +#include +#include +#include "vpr_types.h" + +void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, + const ModuleManager& module_manager); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.cpp new file mode 100644 index 000000000..1efc9279c --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.cpp @@ -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 +#include + +#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>& grids, + const std::vector& 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 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 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 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>& grids, + const std::vector& switches, + const RRGSB& rr_gsb) { + + /* Create the file name for Verilog netlist */ + vtr::Point 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>& grids, + const std::vector& 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>& grids, + const std::vector& 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>& grids, + const std::vector& 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 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 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 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>& grids, + const std::vector& switches) { + /* Create the netlist */ + vtr::Point 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 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>& grids, + const std::vector& 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>& grids, + const std::vector& 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>& grids, + const std::vector& 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); +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.h new file mode 100644 index 000000000..c28bfbe26 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_routing_writer.h @@ -0,0 +1,34 @@ +#ifndef PNR_SDC_ROUTING_WRITER_H +#define PNR_SDC_ROUTING_WRITER_H + +#include +#include +#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>& grids, + const std::vector& 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>& grids, + const std::vector& 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>& grids, + const std::vector& switches); + +void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir, + const ModuleManager& module_manager, + const std::vector>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp new file mode 100644 index 000000000..771e4ba22 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.cpp @@ -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 +#include +#include + +#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& 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 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 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>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& 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); + } +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h new file mode 100644 index 000000000..6b618b2df --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/pnr_sdc_writer.h @@ -0,0 +1,24 @@ +#ifndef PNR_SDC_WRITER_H +#define PNR_SDC_WRITER_H + +#include +#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>& grids, + const std::vector& switches, + const DeviceRRGSB& L_device_rr_gsb, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const bool& compact_routing_hierarchy); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp new file mode 100644 index 000000000..83683db1f --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.cpp @@ -0,0 +1,59 @@ +/******************************************************************** + * Useful APIs for SDC generator + *******************************************************************/ +#include +#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>& grids, + const std::vector& rr_switches, + const DeviceRRGSB& L_device_rr_gsb, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& 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); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h new file mode 100644 index 000000000..402d65bed --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_api.h @@ -0,0 +1,25 @@ +#ifndef SDC_API_H +#define SDC_API_H + +#include +#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>& grids, + const std::vector& rr_switches, + const DeviceRRGSB& L_device_rr_gsb, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const bool& compact_routing_hierarchy); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.cpp new file mode 100644 index 000000000..6df5ede5f --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.cpp @@ -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 __ */ + 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; + } + } +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.h new file mode 100644 index 000000000..9ce57d6d8 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_memory_utils.h @@ -0,0 +1,13 @@ +#ifndef SDC_MEMORY_UTILS_H +#define SDC_MEMORY_UTILS_H + +#include +#include +#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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.cpp new file mode 100644 index 000000000..088266337 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.cpp @@ -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; +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.h new file mode 100644 index 000000000..ee6192830 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_option.h @@ -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 + +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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h new file mode 100644 index 000000000..9a9f3af29 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_naming.h @@ -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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp new file mode 100644 index 000000000..0613cac7a --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.cpp @@ -0,0 +1,172 @@ +/******************************************************************** + * This file include most utilized functions to be used in SDC writers + *******************************************************************/ +#include +#include +#include + +#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 [:] + * others require a format of [:] + */ + /* When LSB == MSB, we can use a simplified format []*/ + 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; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h new file mode 100644 index 000000000..9483f0379 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/sdc_writer_utils.h @@ -0,0 +1,42 @@ +#ifndef SDC_WRITER_UTILS_H +#define SDC_WRITER_UTILS_H + +#include +#include +#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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c index 4665bc0bb..edff64348 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_api.c @@ -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 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) diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_backannotate_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_backannotate_utils.c index b8e8e8112..5b7abd0bb 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_backannotate_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_backannotate_utils.c @@ -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]; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp index a90a301e2..72bc59994 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.cpp @@ -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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h index ece002039..83e84af3b 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_naming.h @@ -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& coordina std::string generate_connection_block_module_name(const t_rr_type& cb_type, const vtr::Point& 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); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_reserved_words.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_reserved_words.h index face6ad8f..c334feef1 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_reserved_words.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_reserved_words.h @@ -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"; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_unique_routing.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_unique_routing.c index 4385cd5f7..e8ac5a4e4 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_unique_routing.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_unique_routing.c @@ -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, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.c index 9f73bc651..e61a4defb 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.c @@ -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 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 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 my_itobin_vec(const size_t& in_int, const size_t& bin_len) { - std::vector 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)); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.cpp new file mode 100644 index 000000000..1f49526f0 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.cpp @@ -0,0 +1,135 @@ +/******************************************************************** + * Most utilized functions in FPGA X2P framework + *******************************************************************/ +#include +#include + +#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: / + * This function will return + ********************************************************************/ +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: / + * This function will return + ********************************************************************/ +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 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 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 my_itobin_vec(const size_t& in_int, const size_t& bin_len) { + std::vector 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; +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.h index 114d2d604..4ad93fc2b 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_utils.h @@ -3,16 +3,28 @@ #include #include +#include #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 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 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 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 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, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp index bdd636071..3b6b0a6f0 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.cpp @@ -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 */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h index 1efcd0d24..ac7096fd3 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager.h @@ -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 */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp index 7fe3bf2df..f8c79a238 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.cpp @@ -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 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 logic_module_sram_port_ids; @@ -565,11 +594,11 @@ void add_module_nets_between_logic_and_memory_sram_bus(ModuleManager& module_man std::vector 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 logic_module_sramb_port_ids; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h index 5156e7af2..a44abbfc4 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/module_manager_utils.h @@ -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); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp index 9cd735aeb..4066c3a63 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.cpp @@ -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 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; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h index 711517a0c..59e8639ad 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_utils.h @@ -51,4 +51,7 @@ size_t find_switch_block_num_shared_conf_bits(t_sram_orgz_info* cur_sram_orgz_in const std::vector& rr_switches, const RRGSB& rr_gsb); +bool connection_block_contain_only_routing_tracks(const RRGSB& rr_gsb, + const t_rr_type& cb_type); + #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/bitstream/build_routing_bitstream.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/bitstream/build_routing_bitstream.cpp index c650d7704..48c4a0504 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/bitstream/build_routing_bitstream.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/bitstream/build_routing_bitstream.cpp @@ -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, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_device_module.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_device_module.cpp index 49b904257..24c0e8c7d 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_device_module.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_device_module.cpp @@ -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 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(); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp index a474ab26c..9bb471710 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.cpp @@ -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& 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 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)); + } + } +} + + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h index 34200f68b..703d8a285 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_essential_modules.h @@ -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& 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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp index 57eca34e7..f6a940d85 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp @@ -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 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); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_lut_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_lut_modules.cpp index 75795bd50..3122cf178 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_lut_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_lut_modules.cpp @@ -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 */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_memory_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_memory_modules.cpp index 02dca4351..a4a33bed2 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_memory_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_memory_modules.cpp @@ -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 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 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& 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 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 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); } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp index 241b5f631..fc710a20d 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_module_graph_utils.cpp @@ -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; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp index 529f1e760..3d550d9ee 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_mux_modules.cpp @@ -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 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 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 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 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 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); } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp new file mode 100644 index 000000000..a81eb567f --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.cpp @@ -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 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>& 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 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 find_switch_block_module_input_ports(const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + const std::vector& input_rr_nodes) { + std::vector 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 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>& 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 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 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& input_rr_nodes) { + std::vector 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; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h new file mode 100644 index 000000000..db684ea0a --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_module_utils.h @@ -0,0 +1,48 @@ +#ifndef BUILD_ROUTING_MODULE_UTILS_H +#define BUILD_ROUTING_MODULE_UTILS_H + +#include +#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>& grids, + const e_side& input_side, + t_rr_node* input_rr_node); + +std::vector find_switch_block_module_input_ports(const ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGSB& rr_gsb, + const std::vector>& grids, + const std::vector& 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>& grids, + t_rr_node* src_rr_node); + +std::vector 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& input_rr_nodes); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp index 94de58fea..fcd0f93d2 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_routing_modules.cpp @@ -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 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>& 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 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 find_switch_block_module_input_ports(const ModuleManager& module_manager, - const ModuleId& sb_module, - const RRGSB& rr_gsb, - const std::vector>& grids, - const std::vector& input_rr_nodes) { - std::vector 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 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 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 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 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>& 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 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 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& input_rr_nodes) { - std::vector 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 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 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 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, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module.cpp index a8a5d66e9..a8e3543e5 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module.cpp @@ -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> add_top_module_connection_block_instances(Modul */ const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); vtr::Point 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; } } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_directs.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_directs.cpp index 536ab15c8..6179e4d4c 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_directs.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_directs.cpp @@ -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 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 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]); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_memory.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_memory.cpp index e795ab7ad..4042febd7 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_memory.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_top_module_memory.cpp @@ -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; } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.cpp index f338110c8..fd7527b6d 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.cpp @@ -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 input_ports = circuit_lib.model_ports_by_type(wire_model, SPICE_MODEL_PORT_INPUT, true); - std::vector 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 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(); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.h b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.h index a68eb22b0..193071526 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_wire_modules.h @@ -14,7 +14,6 @@ #include "module_manager.h" void build_wire_modules(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::vector routing_segments); + const CircuitLibrary& circuit_lib); #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.cpp index 67850403d..e2b347f84 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.cpp @@ -2,8 +2,8 @@ * This function includes the writer for generating exchangeable * information, in order to interface different simulators ********************************************************************/ -#include -#include +#include +#include #include #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 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); } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.h index feedb8e94..9644e2a63 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/simulation_info_writer.h @@ -3,11 +3,12 @@ #include -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 diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c index 998a8f2db..9047cb473 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_api.c @@ -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 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()); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.c index e3c234c08..2cb7bf089 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.c @@ -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) { diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.cpp deleted file mode 100644 index 6f60b4d21..000000000 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************** - * This file includes functions that are used to create - * an auto-check top-level testbench for a FPGA fabric - *******************************************************************/ -#include -#include - -#include "vtr_assert.h" - -#include "fpga_x2p_utils.h" - -#include "verilog_writer_utils.h" - -#include "verilog_autocheck_top_testbench.h" - -/******************************************************************** - * The top-level function to generate a testbench, in order to verify: - * 1. Configuration phase of the FPGA fabric, where the bitstream is - * loaded to the configuration protocol of the FPGA fabric - * 2. Operating phase of the FPGA fabric, where input stimuli are - * fed to the I/Os of the FPGA fabric - *******************************************************************/ -void print_verilog_autocheck_top_testbench(const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const CircuitLibrary& circuit_lib, - const std::vector& global_ports, - const std::vector& L_logical_blocks, - const vtr::Point& device_size, - const std::vector>& L_grids, - const std::vector& L_blocks, - const std::string& circuit_name, - const std::string& verilog_fname, - const std::string& verilog_dir, - const t_syn_verilog_opts& fpga_verilog_opts, - const t_spice_params& simulation_parameters) { - vpr_printf(TIO_MESSAGE_INFO, - "Writing Autocheck Testbench for FPGA Top-level Verilog netlist for %s...", - circuit_name.c_str()); - - /* Start time count */ - clock_t t_start = clock(); - - /* Create the file stream */ - std::fstream fp; - fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); - - /* Validate the file stream */ - check_file_handler(fp); - - /* Generate a brief description on the Verilog file*/ - std::string title = std::string("FPGA Verilog Testbench for Top-level netlist of Design: ") + circuit_name; - print_verilog_file_header(fp, title); - - /* Print preprocessing flags and external netlists */ - print_verilog_include_defines_preproc_file(fp, verilog_dir); - - /* Start of testbench */ - //dump_verilog_top_auto_testbench_ports(fp, cur_sram_orgz_info, circuit_name, fpga_verilog_opts); - - /* Call defined top-level module */ - //dump_verilog_top_testbench_call_top_module(cur_sram_orgz_info, fp, - // circuit_name, is_explicit_mapping); - - /* Call defined benchmark */ - //dump_verilog_top_auto_testbench_call_benchmark(fp, circuit_name); - - /* Add stimuli for reset, set, clock and iopad signals */ - //dump_verilog_top_testbench_stimuli(cur_sram_orgz_info, fp, verilog); - - /* Add output autocheck */ - //dump_verilog_top_auto_testbench_check(fp); - - /* Add Icarus requirement */ - //dump_verilog_timeout_and_vcd(fp, circuit_name , verilog, cur_sram_orgz_info); - - /* Testbench ends*/ - //fprintf(fp, "endmodule\n"); - - /* Close the file stream */ - 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); -} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.h index a34deb5da..4b9f09d62 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_autocheck_top_testbench.h @@ -1,33 +1,12 @@ #ifndef VERILOG_AUTOCHECK_TOP_TESTBENCH_H #define VERILOG_AUTOCHECK_TOP_TESTBENCH_H -#include -#include -#include "module_manager.h" -#include "bitstream_manager.h" -#include "circuit_library.h" 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); -/* -void print_verilog_autocheck_top_testbench(const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const CircuitLibrary& circuit_lib, - const std::vector& global_ports, - const std::vector& L_logical_blocks, - const vtr::Point& device_size, - const std::vector>& L_grids, - const std::vector& L_blocks, - const std::string& circuit_name, - const std::string& verilog_fname, - const std::string& verilog_dir, - const t_syn_verilog_opts& fpga_verilog_opts, - const t_spice_params& simulation_parameters); -*/ - #endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.cpp new file mode 100644 index 000000000..0aa47d8a6 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.cpp @@ -0,0 +1,195 @@ +/******************************************************************** + * This file includes functions that are used to generate Verilog files + * or code blocks, with a focus on + * `include user-defined or auto-generated netlists in Verilog format + *******************************************************************/ +#include + +#include "vtr_assert.h" + +#include "circuit_library_utils.h" + +#include "fpga_x2p_utils.h" +#include "fpga_x2p_naming.h" + +#include "verilog_global.h" +#include "verilog_writer_utils.h" +#include "verilog_auxiliary_netlists.h" + +/******************************************************************** + * Local constant variables + *******************************************************************/ +constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; + +/******************************************************************** + * Print a file that includes all the netlists that have been generated + * and user-defined. + * Some netlists are open to compile under specific preprocessing flags + *******************************************************************/ +void print_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file, + const CircuitLibrary& circuit_lib) { + std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_handler(fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Netlist Summary")); + + /* Print preprocessing flags */ + print_verilog_comment(fp, std::string("------ Include defines: preproc flags -----")); + print_verilog_include_netlist(fp, std::string(src_dir + std::string(defines_verilog_file_name))); + fp << std::endl; + + print_verilog_comment(fp, std::string("------ Include simulation defines -----")); + print_verilog_include_netlist(fp, src_dir + std::string(defines_verilog_simulation_file_name)); + fp << std::endl; + + /* Include all the user-defined netlists */ + for (const std::string& user_defined_netlist : find_circuit_library_unique_verilog_netlists(circuit_lib)) { + print_verilog_include_netlist(fp, user_defined_netlist); + } + + /* Include all the primitive modules */ + print_verilog_include_netlist(fp, src_dir + std::string(default_submodule_dir_name) + std::string(submodule_verilog_file_name)); + fp << std::endl; + + /* Include all the CLB, heterogeneous block modules */ + print_verilog_include_netlist(fp, src_dir + std::string(default_lb_dir_name) + std::string(logic_block_verilog_file_name)); + fp << std::endl; + + /* Include all the routing architecture modules */ + print_verilog_include_netlist(fp, src_dir + std::string(default_rr_dir_name) + std::string(routing_verilog_file_name)); + fp << std::endl; + + /* Include FPGA top module */ + print_verilog_include_netlist(fp, src_dir + generate_fpga_top_netlist_name(std::string(verilog_netlist_file_postfix))); + fp << std::endl; + + /* Include reference benchmark netlist only when auto-check flag is enabled */ + print_verilog_preprocessing_flag(fp, std::string(autochecked_simulation_flag)); + fp << "\t"; + print_verilog_include_netlist(fp, std::string(reference_benchmark_file)); + print_verilog_endif(fp); + fp << std::endl; + + /* Include formal verification netlists only when formal verification flag is enable */ + print_verilog_preprocessing_flag(fp, std::string(verilog_formal_verification_preproc_flag)); + fp << "\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(formal_verification_verilog_file_postfix)); + + /* Include formal verification testbench only when formal simulation flag is enabled */ + fp << "\t"; + print_verilog_preprocessing_flag(fp, std::string(formal_simulation_flag)); + fp << "\t\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(random_top_testbench_verilog_file_postfix)); + fp << "\t"; + print_verilog_endif(fp); + + print_verilog_endif(fp); + fp << std::endl; + + /* Include top-level testbench only when auto-check flag is enabled */ + print_verilog_preprocessing_flag(fp, std::string(autochecked_simulation_flag)); + fp << "\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(autocheck_top_testbench_verilog_file_postfix)); + print_verilog_endif(fp); + fp << std::endl; + + /* Close the file stream */ + fp.close(); +} + +/******************************************************************** + * Print a Verilog file containing preprocessing flags + * which are used enable/disable some features in FPGA Verilog modules + *******************************************************************/ +void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, + const t_syn_verilog_opts& fpga_verilog_opts) { + + std::string verilog_fname = src_dir + std::string(defines_verilog_file_name); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_handler(fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules")); + + /* To enable timing */ + if (TRUE == fpga_verilog_opts.include_timing) { + print_verilog_define_flag(fp, std::string(verilog_timing_preproc_flag), 1); + fp << std::endl; + } + + /* To enable timing */ + if (TRUE == fpga_verilog_opts.include_signal_init) { + print_verilog_define_flag(fp, std::string(verilog_signal_init_preproc_flag), 1); + fp << std::endl; + } + + /* To enable formal verfication flag */ + if (TRUE == fpga_verilog_opts.print_formal_verification_top_netlist) { + print_verilog_define_flag(fp, std::string(verilog_formal_verification_preproc_flag), 1); + fp << std::endl; + } + + /* To enable functional verfication with Icarus */ + if (TRUE == fpga_verilog_opts.include_icarus_simulator) { + print_verilog_define_flag(fp, std::string(icarus_simulator_flag), 1); + fp << std::endl; + } + + /* Close the file stream */ + fp.close(); +} + +/******************************************************************** + * Print a Verilog file containing simulation-related preprocessing flags + *******************************************************************/ +void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, + const t_syn_verilog_opts& fpga_verilog_opts) { + + std::string verilog_fname = src_dir + std::string(defines_verilog_simulation_file_name); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_handler(fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features")); + + /* To enable manualy checked simulation */ + if (TRUE == fpga_verilog_opts.print_top_testbench) { + print_verilog_define_flag(fp, std::string(initial_simulation_flag), 1); + fp << std::endl; + } + + /* To enable auto-checked simulation */ + if (TRUE == fpga_verilog_opts.print_autocheck_top_testbench) { + print_verilog_define_flag(fp, std::string(autochecked_simulation_flag), 1); + fp << std::endl; + } + + /* To enable pre-configured FPGA simulation */ + if (TRUE == fpga_verilog_opts.print_formal_verification_top_netlist) { + print_verilog_define_flag(fp, std::string(formal_simulation_flag), 1); + fp << std::endl; + } + + + /* Close the file stream */ + fp.close(); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.h new file mode 100644 index 000000000..0a56690cd --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_auxiliary_netlists.h @@ -0,0 +1,19 @@ +#ifndef VERILOG_AUXILIARY_NETLISTS_H +#define VERILOG_AUXILIARY_NETLISTS_H + +#include +#include "circuit_library.h" +#include "vpr_types.h" + +void print_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file, + const CircuitLibrary& circuit_lib); + +void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, + const t_syn_verilog_opts& fpga_verilog_opts); + +void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, + const t_syn_verilog_opts& fpga_verilog_opts); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_essential_gates.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_essential_gates.cpp index 13cd62ada..5f116f4a0 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_essential_gates.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_essential_gates.cpp @@ -40,7 +40,7 @@ void print_verilog_power_gated_invbuf_body(std::fstream& fp, print_verilog_comment(fp, std::string("----- Verilog codes of a power-gated inverter -----")); /* Create a sensitive list */ - fp << "\treg " << circuit_lib.port_lib_name(output_port) << "_reg;" << std::endl; + fp << "\treg " << circuit_lib.port_prefix(output_port) << "_reg;" << std::endl; fp << "\talways @(" << std::endl; /* Power-gate port first*/ @@ -49,9 +49,9 @@ void print_verilog_power_gated_invbuf_body(std::fstream& fp, if (0 < &power_gate_port - &power_gate_ports[0]) { fp << ","; } - fp << circuit_lib.port_lib_name(power_gate_port); + fp << circuit_lib.port_prefix(power_gate_port); } - fp << circuit_lib.port_lib_name(input_port) << ") begin" << std::endl; + fp << circuit_lib.port_prefix(input_port) << ") begin" << std::endl; /* Dump the case of power-gated */ fp << "\t\tif ("; @@ -71,14 +71,14 @@ void print_verilog_power_gated_invbuf_body(std::fstream& fp, fp << "~"; } - fp << circuit_lib.port_lib_name(power_gate_port) << "[" << power_gate_pin << "])"; + fp << circuit_lib.port_prefix(power_gate_port) << "[" << power_gate_pin << "])"; port_cnt++; /* Update port counter*/ } } fp << ") begin" << std::endl; - fp << "\t\t\tassign " << circuit_lib.port_lib_name(output_port) << "_reg = "; + fp << "\t\t\tassign " << circuit_lib.port_prefix(output_port) << "_reg = "; /* Branch on the type of inverter/buffer: * 1. If this is an inverter or an tapered(multi-stage) buffer with odd number of stages, @@ -93,12 +93,12 @@ void print_verilog_power_gated_invbuf_body(std::fstream& fp, fp << "~"; } - fp << circuit_lib.port_lib_name(input_port) << ";" << std::endl; + fp << circuit_lib.port_prefix(input_port) << ";" << std::endl; fp << "\t\tend else begin" << std::endl; - fp << "\t\t\tassign " << circuit_lib.port_lib_name(output_port) << "_reg = 1'bz;" << std::endl; + fp << "\t\t\tassign " << circuit_lib.port_prefix(output_port) << "_reg = 1'bz;" << std::endl; fp << "\t\tend" << std::endl; fp << "\tend" << std::endl; - fp << "\tassign " << circuit_lib.port_lib_name(output_port) << " = " << circuit_lib.port_lib_name(output_port) << "_reg;" << std::endl; + fp << "\tassign " << circuit_lib.port_prefix(output_port) << " = " << circuit_lib.port_prefix(output_port) << "_reg;" << std::endl; } /************************************************ @@ -116,7 +116,7 @@ void print_verilog_invbuf_body(std::fstream& fp, print_verilog_comment(fp, std::string("----- Verilog codes of a regular inverter -----")); - fp << "\tassign " << circuit_lib.port_lib_name(output_port) << " = (" << circuit_lib.port_lib_name(input_port) << " === 1'bz)? $random : "; + fp << "\tassign " << circuit_lib.port_prefix(output_port) << " = (" << circuit_lib.port_prefix(input_port) << " === 1'bz)? $random : "; /* Branch on the type of inverter/buffer: * 1. If this is an inverter or an tapered(multi-stage) buffer with odd number of stages, @@ -131,7 +131,7 @@ void print_verilog_invbuf_body(std::fstream& fp, fp << "~"; } - fp << circuit_lib.port_lib_name(input_port) << ";" << std::endl; + fp << circuit_lib.port_prefix(input_port) << ";" << std::endl; } /************************************************ @@ -282,8 +282,8 @@ void print_verilog_passgate_module(ModuleManager& module_manager, /* Dump logics: we propagate input to the output when the gate is '1' * the input is blocked from output when the gate is '0' */ - fp << "\tassign " << circuit_lib.port_lib_name(output_ports[0]) << " = "; - fp << circuit_lib.port_lib_name(input_ports[1]) << " ? " << circuit_lib.port_lib_name(input_ports[0]); + fp << "\tassign " << circuit_lib.port_prefix(output_ports[0]) << " = "; + fp << circuit_lib.port_prefix(input_ports[1]) << " ? " << circuit_lib.port_prefix(input_ports[0]); fp << " : 1'bz;" << std::endl; /* Print timing info */ @@ -329,7 +329,7 @@ void print_verilog_and_or_gate_body(std::fstream& fp, for (const auto& output_port : output_ports) { for (const auto& output_pin : circuit_lib.pins(output_port)) { - BasicPort output_port_info(circuit_lib.port_lib_name(output_port), output_pin, output_pin); + BasicPort output_port_info(circuit_lib.port_prefix(output_port), output_pin, output_pin); fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, output_port_info); fp << " = "; @@ -341,7 +341,7 @@ void print_verilog_and_or_gate_body(std::fstream& fp, fp << " " << gate_verilog_operator << " "; } - BasicPort input_port_info(circuit_lib.port_lib_name(input_port), input_pin, input_pin); + BasicPort input_port_info(circuit_lib.port_prefix(input_port), input_pin, input_pin); fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port_info); /* Increment the counter for port */ @@ -413,10 +413,10 @@ void print_verilog_mux2_gate_body(std::fstream& fp, * the third input is the select port */ fp << "\tassign "; - BasicPort out_port_info(circuit_lib.port_lib_name(output_ports[0]), 0, 0); - BasicPort sel_port_info(circuit_lib.port_lib_name(input_ports[2]), 0, 0); - BasicPort in0_port_info(circuit_lib.port_lib_name(input_ports[0]), 0, 0); - BasicPort in1_port_info(circuit_lib.port_lib_name(input_ports[1]), 0, 0); + BasicPort out_port_info(circuit_lib.port_prefix(output_ports[0]), 0, 0); + BasicPort sel_port_info(circuit_lib.port_prefix(input_ports[2]), 0, 0); + BasicPort in0_port_info(circuit_lib.port_prefix(input_ports[0]), 0, 0); + BasicPort in1_port_info(circuit_lib.port_prefix(input_ports[1]), 0, 0); fp << generate_verilog_port(VERILOG_PORT_CONKT, out_port_info); fp << " = "; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formal_random_top_testbench.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formal_random_top_testbench.cpp index 4277487ea..b52d41d43 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formal_random_top_testbench.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formal_random_top_testbench.cpp @@ -24,6 +24,7 @@ /* Include FPGA Verilog headers*/ #include "verilog_global.h" #include "verilog_writer_utils.h" +#include "verilog_testbench_utils.h" #include "verilog_formal_random_top_testbench.h" /******************************************************************** @@ -40,22 +41,6 @@ constexpr char* ERROR_COUNTER = "nb_error"; constexpr char* FORMAL_TB_SIM_START_PORT_NAME = "sim_start"; constexpr int MAGIC_NUMBER_FOR_SIMULATION_TIME = 200; -/******************************************************************** - * Generate the clock port name to be used in this testbench - * - * Restrictions: - * Assume this is a single clock benchmark - *******************************************************************/ -static -BasicPort generate_verilog_top_clock_port(const std::vector& clock_port_names) { - if (0 == clock_port_names.size()) { - return BasicPort(std::string(DEFAULT_CLOCK_NAME), 1); - } - - VTR_ASSERT(1 == clock_port_names.size()); - return BasicPort(clock_port_names[0], 1); -} - /******************************************************************** * Print the module ports for the Verilog testbench * using random vectors @@ -76,94 +61,21 @@ void print_verilog_top_random_testbench_ports(std::fstream& fp, /* Print the declaration for the module */ fp << "module " << circuit_name << FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX << ";" << std::endl; - /* Instantiate register for inputs stimulis */ - print_verilog_comment(fp, std::string("----- Shared inputs -------")); - for (const t_logical_block& lb : L_logical_blocks) { - /* We care only those logic blocks which are input I/Os */ - if (VPACK_INPAD != lb.type) { - continue; - } - - /* Each logical block assumes a single-width port */ - BasicPort input_port(std::string(lb.name), 1); - fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" << std::endl; - } - - /* Add an empty line as splitter */ - fp << std::endl; - /* Create a clock port if the benchmark does not have one! * The clock is used for counting and synchronizing input stimulus */ - if (0 == clock_port_names.size()) { - BasicPort clock_port = generate_verilog_top_clock_port(clock_port_names); - print_verilog_comment(fp, std::string("----- Default clock port is added here since benchmark does not contain one -------")); - fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, clock_port) << ";" << std::endl; - } + BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME)); + print_verilog_comment(fp, std::string("----- Default clock port is added here since benchmark does not contain one -------")); + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, clock_port) << ";" << std::endl; /* Add an empty line as splitter */ fp << std::endl; - /* Instantiate wires for FPGA fabric outputs */ - print_verilog_comment(fp, std::string("----- FPGA fabric outputs -------")); - - for (const t_logical_block& lb : L_logical_blocks) { - /* We care only those logic blocks which are input I/Os */ - if (VPACK_OUTPAD != lb.type) { - continue; - } - - /* Each logical block assumes a single-width port */ - BasicPort output_port(std::string(std::string(lb.name) + std::string(FPGA_PORT_POSTFIX)), 1); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl; - } - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */ - print_verilog_preprocessing_flag(fp, std::string(autochecked_simulation_flag)); - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Instantiate wire for benchmark output */ - print_verilog_comment(fp, std::string("----- Benchmark outputs -------")); - for (const t_logical_block& lb : L_logical_blocks) { - /* We care only those logic blocks which are input I/Os */ - if (VPACK_OUTPAD != lb.type) { - continue; - } - - /* Each logical block assumes a single-width port */ - BasicPort output_port(std::string(std::string(lb.name) + std::string(BENCHMARK_PORT_POSTFIX)), 1); - fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl; - } - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Instantiate register for output comparison */ - print_verilog_comment(fp, std::string("----- Output vectors checking flags -------")); - for (const t_logical_block& lb : L_logical_blocks) { - /* We care only those logic blocks which are input I/Os */ - if (VPACK_OUTPAD != lb.type) { - continue; - } - - /* Each logical block assumes a single-width port */ - BasicPort output_port(std::string(std::string(lb.name) + std::string(CHECKFLAG_PORT_POSTFIX)), 1); - fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, output_port) << ";" << std::endl; - } - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Condition ends for the benchmark instanciation */ - print_verilog_endif(fp); - - /* Add an empty line as splitter */ - fp << std::endl; + print_verilog_testbench_shared_ports(fp, L_logical_blocks, + std::string(BENCHMARK_PORT_POSTFIX), + std::string(FPGA_PORT_POSTFIX), + std::string(CHECKFLAG_PORT_POSTFIX), + std::string(autochecked_simulation_flag)); /* Instantiate an integer to count the number of error * and determine if the simulation succeed or failed @@ -175,46 +87,6 @@ void print_verilog_top_random_testbench_ports(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * Instanciate the FPGA fabric module - *******************************************************************/ -static -void print_verilog_random_testbench_instance(std::fstream& fp, - const std::string& module_name, - const std::string& instance_name, - const std::string& output_port_postfix, - const std::vector& L_logical_blocks) { - - /* Validate the file stream */ - check_file_handler(fp); - - fp << "\t" << module_name << " " << instance_name << "(" << std::endl; - - size_t port_counter = 0; - for (const t_logical_block& lb : L_logical_blocks) { - /* Bypass non-I/O logical blocks ! */ - if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { - continue; - } - /* The first port does not need a comma */ - if(0 < port_counter){ - fp << "," << std::endl; - } - /* Input port follows the logical block name while output port requires a special postfix */ - if (VPACK_INPAD == lb.type){ - fp << "\t\t" << std::string(lb.name); - } else { - VTR_ASSERT_SAFE(VPACK_OUTPAD == lb.type); - fp << "\t\t" << std::string(lb.name) << output_port_postfix; - } - /* Update the counter */ - port_counter++; - } - fp << "\t);" << std::endl; -} - - - /******************************************************************** * Instanciate the input benchmark module *******************************************************************/ @@ -230,9 +102,15 @@ void print_verilog_top_random_testbench_benchmark_instance(std::fstream& fp, print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------")); - print_verilog_random_testbench_instance(fp, reference_verilog_top_name, - std::string(BENCHMARK_INSTANCE_NAME), - std::string(BENCHMARK_PORT_POSTFIX), L_logical_blocks); + /* Do NOT use explicit port mapping here: + * VPR added a prefix of "out_" to the output ports of input benchmark + */ + print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name, + std::string(BENCHMARK_INSTANCE_NAME), + std::string(), + std::string(), + std::string(BENCHMARK_PORT_POSTFIX), L_logical_blocks, + false); print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------")); @@ -246,137 +124,6 @@ void print_verilog_top_random_testbench_benchmark_instance(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * Print Verilog codes to set up a timeout for the simulation - * and dump the waveform to VCD files - * - * Note that: these codes are tuned for Icarus simulator!!! - *******************************************************************/ -static -void print_verilog_timeout_and_vcd(std::fstream& fp, - const std::string& circuit_name, - const int& simulation_time) { - /* Validate the file stream */ - check_file_handler(fp); - - /* The following verilog codes are tuned for Icarus */ - print_verilog_preprocessing_flag(fp, std::string(icarus_simulator_flag)); - - print_verilog_comment(fp, std::string("----- Begin Icarus requirement -------")); - - fp << "\tinitial begin" << std::endl; - fp << "\t\t$dumpfile(\"" << circuit_name << "_formal.vcd\");" << std::endl; - fp << "\t\t$dumpvars(1, " << circuit_name << FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX << ");" << std::endl; - fp << "\tend" << std::endl; - - /* Condition ends for the Icarus requirement */ - print_verilog_endif(fp); - - print_verilog_comment(fp, std::string("----- END Icarus requirement -------")); - - /* Add an empty line as splitter */ - fp << std::endl; - - BasicPort sim_start_port(std::string(FORMAL_TB_SIM_START_PORT_NAME), 1); - - fp << "initial begin" << std::endl; - fp << "\t" << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << " <= 1'b1;" << std::endl; - fp << "\t$timeformat(-9, 2, \"ns\", 20);" << std::endl; - fp << "\t$display(\"Simulation start\");" << std::endl; - print_verilog_comment(fp, std::string("----- Can be changed by the user for his/her need -------")); - fp << "\t#" << simulation_time << std::endl; - fp << "\tif(" << ERROR_COUNTER << " == 0) begin" << std::endl; - fp << "\t\t$display(\"Simulation Succeed\");" << std::endl; - fp << "\tend else begin" << std::endl; - fp << "\t\t$display(\"Simulation Failed with " << std::string("%d") << " error(s)\", " << ERROR_COUNTER << ");" << std::endl; - fp << "\tend" << std::endl; - fp << "\t$finish;" << std::endl; - fp << "end" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; -} - -/******************************************************************** - * Print Verilog codes to check the equivalence of output vectors - * - * Restriction: this function only supports single clock benchmarks! - *******************************************************************/ -static -void print_verilog_top_random_testbench_check(std::fstream& fp, - const std::vector& L_logical_blocks, - const std::vector& clock_port_names) { - - /* Validate the file stream */ - check_file_handler(fp); - - /* Add output autocheck conditionally: only when a preprocessing flag is enable */ - print_verilog_preprocessing_flag(fp, std::string(autochecked_simulation_flag)); - - print_verilog_comment(fp, std::string("----- Begin checking output vectors -------")); - - BasicPort clock_port = generate_verilog_top_clock_port(clock_port_names); - - print_verilog_comment(fp, std::string("----- Skip the first falling edge of clock, it is for initialization -------")); - - BasicPort sim_start_port(std::string(FORMAL_TB_SIM_START_PORT_NAME), 1); - - fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, sim_start_port) << ";" << std::endl; - fp << std::endl; - - fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl; - fp << "\t\tif (1'b1 == " << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << ") begin" << std::endl; - fp << "\t\t"; - print_verilog_register_connection(fp, sim_start_port, sim_start_port, true); - fp << "\t\tend else begin" << std::endl; - - for (const t_logical_block& lb : L_logical_blocks) { - /* Bypass non-I/O logical blocks ! */ - if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { - continue; - } - - if (VPACK_OUTPAD == lb.type){ - fp << "\t\t\tif(!(" << std::string(lb.name) << std::string(FPGA_PORT_POSTFIX); - fp << " === " << std::string(lb.name) << std::string(BENCHMARK_PORT_POSTFIX); - fp << ") && !(" << std::string(lb.name) << std::string(BENCHMARK_PORT_POSTFIX); - fp << " === 1'bx)) begin" << std::endl; - fp << "\t\t\t\t" << std::string(lb.name) << std::string(CHECKFLAG_PORT_POSTFIX) << " <= 1'b1;" << std::endl; - fp << "\t\t\tend else begin" << std::endl; - fp << "\t\t\t\t" << std::string(lb.name) << std::string(CHECKFLAG_PORT_POSTFIX) << "<= 1'b0;" << std::endl; - fp << "\t\t\tend" << std::endl; - } - } - fp << "\t\tend" << std::endl; - fp << "\tend" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; - - for (const t_logical_block& lb : L_logical_blocks) { - /* Bypass non-I/O logical blocks ! */ - if (VPACK_OUTPAD != lb.type) { - continue; - } - - fp << "\talways@(posedge " << std::string(lb.name) << std::string(CHECKFLAG_PORT_POSTFIX) << ") begin" << std::endl; - fp << "\t\tif(" << std::string(lb.name) << std::string(CHECKFLAG_PORT_POSTFIX) << ") begin" << std::endl; - fp << "\t\t\t" << ERROR_COUNTER << " = " << ERROR_COUNTER << " + 1;" << std::endl; - fp << "\t\t\t$display(\"Mismatch on " << std::string(lb.name) << std::string(FPGA_PORT_POSTFIX) << " at time = " << std::string("%t") << "\", $realtime);" << std::endl; - fp << "\t\tend" << std::endl; - fp << "\tend" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; - } - - /* Condition ends */ - print_verilog_endif(fp); - - /* Add an empty line as splitter */ - fp << std::endl; -} - /******************************************************************** * Instanciate the FPGA fabric module *******************************************************************/ @@ -389,10 +136,13 @@ void print_verilog_random_testbench_fpga_instance(std::fstream& fp, print_verilog_comment(fp, std::string("----- FPGA fabric instanciation -------")); - - print_verilog_random_testbench_instance(fp, std::string(circuit_name + std::string(formal_verification_top_postfix)), - std::string(FPGA_INSTANCE_NAME), - std::string(FPGA_PORT_POSTFIX), L_logical_blocks); + /* Always use explicit port mapping */ + print_verilog_testbench_benchmark_instance(fp, std::string(circuit_name + std::string(formal_verification_top_postfix)), + std::string(FPGA_INSTANCE_NAME), + std::string(formal_verification_top_module_port_postfix), + std::string(formal_verification_top_module_port_postfix), + std::string(FPGA_PORT_POSTFIX), L_logical_blocks, + true); print_verilog_comment(fp, std::string("----- End FPGA Fabric Instanication -------")); @@ -400,105 +150,6 @@ void print_verilog_random_testbench_fpga_instance(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * Generate random stimulus for the input ports - *******************************************************************/ -static -void print_verilog_top_random_stimuli(std::fstream& fp, - const t_spice_params& simulation_parameters, - const std::vector& L_logical_blocks, - const std::vector& clock_port_names) { - /* Validate the file stream */ - check_file_handler(fp); - - print_verilog_comment(fp, std::string("----- Initialization -------")); - - fp << "\tinitial begin" << std::endl; - /* Create clock stimuli */ - BasicPort clock_port = generate_verilog_top_clock_port(clock_port_names); - fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << " <= 1'b0;" << std::endl; - fp << "\t\twhile(1) begin" << std::endl; - fp << "\t\t\t#" << std::setprecision(2) << ((0.5/simulation_parameters.stimulate_params.op_clock_freq)/verilog_sim_timescale) << std::endl; - fp << "\t\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); - fp << " <= !"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); - fp << ";" << std::endl; - fp << "\t\tend" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; - - for (const t_logical_block& lb : L_logical_blocks) { - /* Bypass non-I/O logical blocks ! */ - if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { - continue; - } - - /* Clock ports will be initialized later */ - if ( (VPACK_INPAD == lb.type) && (FALSE == lb.is_clock) ) { - fp << "\t\t" << std::string(lb.name) << " <= 1'b0;" << std::endl; - } - } - - /* Add an empty line as splitter */ - fp << std::endl; - - /* Set 0 to registers for checking flags */ - for (const t_logical_block& lb : L_logical_blocks) { - /* We care only those logic blocks which are input I/Os */ - if (VPACK_OUTPAD != lb.type) { - continue; - } - - /* Each logical block assumes a single-width port */ - BasicPort output_port(std::string(std::string(lb.name) + std::string(CHECKFLAG_PORT_POSTFIX)), 1); - fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, output_port) << " <= 1'b0;" << std::endl; - } - - fp << "\tend" << std::endl; - /* Finish initialization */ - - /* Add an empty line as splitter */ - fp << std::endl; - - // Not ready yet to determine if input is reset -/* - fprintf(fp, "//----- Reset Stimulis\n"); - fprintf(fp, " initial begin\n"); - fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001); - fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); - fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001); - fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); - fprintf(fp, " while(1) begin\n"); - fprintf(fp, " #%.3f\n", (rand() % 15) + 0.5); - fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); - fprintf(fp, " #%.3f\n", (rand() % 10000) + 200); - fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); - fprintf(fp, " end\n"); - fprintf(fp, " end\n\n"); -*/ - - print_verilog_comment(fp, std::string("----- Input Stimulus -------")); - fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl; - - for (const t_logical_block& lb : L_logical_blocks) { - /* Bypass non-I/O logical blocks ! */ - if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { - continue; - } - - /* Clock ports will be initialized later */ - if ( (VPACK_INPAD == lb.type) && (FALSE == lb.is_clock) ) { - fp << "\t\t" << std::string(lb.name) << " <= $random;" << std::endl; - } - } - - fp << "\tend" << std::endl; - - /* Add an empty line as splitter */ - fp << std::endl; -} - /********************************************************************* * Top-level function in this file: * Create a Verilog testbench using random input vectors @@ -553,8 +204,6 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, print_verilog_include_netlist(fp, std::string(verilog_dir + std::string(defines_verilog_simulation_file_name))); - print_verilog_include_netlist(fp, std::string(fpga_verilog_opts.reference_verilog_benchmark_file)); - /* Preparation: find all the clock ports */ std::vector clock_port_names = find_benchmark_clock_port_name(L_logical_blocks); @@ -567,10 +216,23 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, /* Call defined benchmark */ print_verilog_top_random_testbench_benchmark_instance(fp, circuit_name, L_logical_blocks); - /* Add stimuli for reset, set, clock and iopad signals */ - print_verilog_top_random_stimuli(fp, simulation_parameters, L_logical_blocks, clock_port_names); + /* Find clock port to be used */ + BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME)); - print_verilog_top_random_testbench_check(fp, L_logical_blocks, clock_port_names); + /* Add stimuli for reset, set, clock and iopad signals */ + print_verilog_testbench_clock_stimuli(fp, simulation_parameters, + clock_port); + print_verilog_testbench_random_stimuli(fp, L_logical_blocks, + std::string(CHECKFLAG_PORT_POSTFIX), clock_port); + + print_verilog_testbench_check(fp, + std::string(autochecked_simulation_flag), + std::string(FORMAL_TB_SIM_START_PORT_NAME), + std::string(BENCHMARK_PORT_POSTFIX), + std::string(FPGA_PORT_POSTFIX), + std::string(CHECKFLAG_PORT_POSTFIX), + std::string(ERROR_COUNTER), + L_logical_blocks, clock_port_names, std::string(DEFAULT_CLOCK_NAME)); int simulation_time = find_operating_phase_simulation_time(MAGIC_NUMBER_FOR_SIMULATION_TIME, simulation_parameters.meas_params.sim_num_clock_cycle, @@ -578,7 +240,13 @@ void print_verilog_random_top_testbench(const std::string& circuit_name, verilog_sim_timescale); /* Add Icarus requirement */ - print_verilog_timeout_and_vcd(fp, circuit_name, simulation_time); + print_verilog_timeout_and_vcd(fp, + std::string(icarus_simulator_flag), + std::string(circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX)), + std::string(circuit_name + std::string("_formal.vcd")), + std::string(FORMAL_TB_SIM_START_PORT_NAME), + std::string(ERROR_COUNTER), + simulation_time); /* Testbench ends*/ print_verilog_module_end(fp, std::string(circuit_name) + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX)); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formality_autodeck.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formality_autodeck.c index 33f587a75..0052ab079 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formality_autodeck.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_formality_autodeck.c @@ -177,8 +177,8 @@ void write_formality_script (t_syn_verilog_opts fpga_verilog_opts, char* formality_script_file_name = NULL; char* benchmark_path = NULL; char* original_output_name = NULL; - char WriteBuffer[200]; - char INI_lbl[20]; + char WriteBuffer[4096]; + char INI_lbl[4096]; /* int output_length; */ /* int pos; */ FILE* fp = NULL; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.c deleted file mode 100644 index 19f6bbc50..000000000 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.c +++ /dev/null @@ -1,131 +0,0 @@ -// Formality runsim -// Need to declare formality_script_name_postfix = "formality_script.tcl"; -#include -#include -#include -#include -#include -#include -#include -#include - -/* Include vpr structs*/ -#include "util.h" -#include "physical_types.h" -#include "vpr_types.h" -#include "globals.h" -#include "rr_graph.h" -#include "vpr_utils.h" -#include "path_delay.h" -#include "stats.h" - -/* Include FPGA-SPICE utils */ -#include "linkedlist.h" -#include "fpga_x2p_utils.h" -#include "fpga_x2p_globals.h" -#include "fpga_x2p_naming.h" - -/* Include verilog utils */ -#include "verilog_global.h" -#include "verilog_utils.h" - -#include "verilog_include_netlists.h" - -static -void include_netlists_include_user_defined_verilog_netlists(FILE* fp, - t_spice spice) { - int i; - - /* A valid file handler*/ - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR, - "(File:%s, [LINE%d])Invalid File Handler!\n", - __FILE__, __LINE__); - exit(1); - } - - /* Include user-defined sub-circuit netlist */ - for (i = 0; i < spice.num_include_netlist; i++) { - if (0 == spice.include_netlists[i].included) { - assert(NULL != spice.include_netlists[i].path); - fprintf(fp, "`include \"%s\"\n", spice.include_netlists[i].path); - spice.include_netlists[i].included = 1; - } else { - assert(1 == spice.include_netlists[i].included); - } - } - - return; -} - -void write_include_netlists (char* src_dir_formatted, - char* chomped_circuit_name, - t_spice spice){ - - char* include_netlists_file_name = NULL; -/* int output_length; */ -/* int pos; */ - FILE* fp = NULL; - - include_netlists_file_name = my_strcat(src_dir_formatted, my_strcat(chomped_circuit_name, "_include_netlists.v")); - fp = fopen(include_netlists_file_name, "w"); - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR, - "(FILE:%s,LINE[%d])Failure in create formality script %s", - __FILE__, __LINE__, include_netlists_file_name); - exit(1); - } - - /* Print the title */ - dump_verilog_file_header(fp, "Netlists Summary"); - - /* Print preprocessing flags */ - verilog_include_defines_preproc_file(fp, src_dir_formatted); - verilog_include_simulation_defines_file(fp, src_dir_formatted); - - /* - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - chomped_circuit_name, - verilog_top_postfix); - */ - fprintf(fp, "`include \"%s%s\"\n", src_dir_formatted, - generate_fpga_top_netlist_name(std::string(verilog_netlist_file_postfix)).c_str()); - fprintf(fp, "`ifdef %s\n", verilog_formal_verification_preproc_flag); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - chomped_circuit_name, - formal_verification_verilog_file_postfix); - fprintf(fp, " `ifdef %s\n", formal_simulation_flag); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - chomped_circuit_name, - random_top_testbench_verilog_file_postfix); - fprintf(fp, " `endif\n"); - fprintf(fp, "`elsif %s\n", initial_simulation_flag); - /* TODO: bring these testbench onboard when it is ready - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - chomped_circuit_name, - top_testbench_verilog_file_postfix); - fprintf(fp, "`elsif %s\n", autochecked_simulation_flag); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - chomped_circuit_name, - autocheck_top_testbench_verilog_file_postfix); - */ - fprintf(fp, "`endif\n"); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - default_rr_dir_name, - routing_verilog_file_name); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - default_lb_dir_name, - logic_block_verilog_file_name); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - default_submodule_dir_name, - submodule_verilog_file_name); - fprintf(fp, "`include \"%s%s%s\"\n", src_dir_formatted, - default_submodule_dir_name, - config_peripheral_verilog_file_name); - init_include_user_defined_verilog_netlists(spice); - include_netlists_include_user_defined_verilog_netlists(fp, spice); - - fclose(fp); - - return; -} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.h deleted file mode 100644 index 85659c56a..000000000 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_include_netlists.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef VERILOG_INCLUDE_NETLISTS_H -#define VERILOG_INCLUDE_NETLISTS_H - -void write_include_netlists (char* src_dir_formatted, - char* chomped_circuit_name, - t_spice spice); - -#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp index 2a495e91a..0b89a87e2 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_module_writer.cpp @@ -95,9 +95,9 @@ BasicPort generate_verilog_port_for_module_net(const ModuleManager& module_manag * to write up local wire declaration in Verilog format *******************************************************************/ static -std::vector find_verilog_module_local_wires(const ModuleManager& module_manager, - const ModuleId& module_id) { - std::vector local_wires; +std::map> find_verilog_module_local_wires(const ModuleManager& module_manager, + const ModuleId& module_id) { + std::map> local_wires; /* Local wires come from the child modules */ for (ModuleNetId module_net : module_manager.module_nets(module_id)) { @@ -119,20 +119,28 @@ std::vector find_verilog_module_local_wires(const ModuleManager& modu } /* Find the name for this local wire */ BasicPort local_wire_candidate = generate_verilog_port_for_module_net(module_manager, module_id, module_net); - /* Try to find a port in the list that can absorb the current local wire */ + /* Cache the net name, try to find it in the cache. + * If you can find one, it means this port may be mergeable, try to do merging. If merge fail, add to the local wire list + * If you cannot find one, it means that this port is not mergeable, add to the local wire list immediately. + */ + std::map>::iterator it = local_wires.find(local_wire_candidate.get_name()); bool merged = false; - for (BasicPort& local_wire : local_wires) { - /* check if the candidate can be combined to an existing local wire */ - if (true == two_verilog_ports_mergeable(local_wire, local_wire_candidate)) { - /* Merge the ports */ - local_wire = merge_two_verilog_ports(local_wire, local_wire_candidate); - merged = true; - break; - } + if (it != local_wires.end()) { + /* Try to merge to one the port in the list that can absorb the current local wire */ + for (BasicPort& local_wire : local_wires[local_wire_candidate.get_name()]) { + /* check if the candidate can be combined to an existing local wire */ + if (true == two_verilog_ports_mergeable(local_wire, local_wire_candidate)) { + /* Merge the ports */ + local_wire = merge_two_verilog_ports(local_wire, local_wire_candidate); + merged = true; + break; + } + } } - /* If not merged, push the port to the list */ + + /* If not merged/not found in the cache, push the port to the list */ if (false == merged) { - local_wires.push_back(local_wire_candidate); + local_wires[local_wire_candidate.get_name()].push_back(local_wire_candidate); } } @@ -161,7 +169,7 @@ std::vector find_verilog_module_local_wires(const ModuleManager& modu instance_port.set_width(*std::min_element(undriven_pins.begin(), undriven_pins.end()), *std::max_element(undriven_pins.begin(), undriven_pins.end())); - local_wires.push_back(instance_port); + local_wires[instance_port.get_name()].push_back(instance_port); } } } @@ -169,6 +177,50 @@ std::vector find_verilog_module_local_wires(const ModuleManager& modu return local_wires; } +/******************************************************************** + * Print a Verilog wire connection + * We search all the sinks of the net, + * if we find a module output, we try to find the next module output + * among the sinks of the net + * For each module output (except the first one), we print a wire connection + *******************************************************************/ +static +void print_verilog_module_output_short_connection(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const ModuleNetId& module_net) { + /* Ensure a valid file stream */ + check_file_handler(fp); + + bool first_port = true; + BasicPort src_port; + + /* We have found a module input, now check all the sink modules of the net */ + for (ModuleNetSinkId net_sink : module_manager.module_net_sinks(module_id, module_net)) { + ModuleId sink_module = module_manager.net_sink_modules(module_id, module_net)[net_sink]; + if (module_id != sink_module) { + continue; + } + + /* Find the sink port and pin information */ + ModulePortId sink_port_id = module_manager.net_sink_ports(module_id, module_net)[net_sink]; + size_t sink_pin = module_manager.net_sink_pins(module_id, module_net)[net_sink]; + BasicPort sink_port(module_manager.module_port(module_id, sink_port_id).get_name(), sink_pin, sink_pin); + + /* For the first module output, this is the source port, we do nothing and go to the next */ + if (true == first_port) { + src_port = sink_port; + /* Flip the flag */ + first_port = false; + continue; + } + + /* We need to print a wire connection here */ + print_verilog_wire_connection(fp, sink_port, src_port, false); + } +} + + /******************************************************************** * Print a Verilog wire connection * We search all the sources of the net, @@ -242,6 +294,36 @@ void print_verilog_module_local_short_connections(std::fstream& fp, } } +/******************************************************************** + * Print output short connections inside a Verilog module + * The output short connection is defined as the direct connection + * between two output ports of the module + * This type of connection is not covered when printing Verilog instances + * Therefore, they are covered in this function + * + * module + * +-----------------------------+ + * | + * src------>+--------------->|--->outputA + * | | + * | | + * +--------------->|--->outputB + * +-----------------------------+ + *******************************************************************/ +static +void print_verilog_module_output_short_connections(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id) { + /* Local wires come from the child modules */ + for (ModuleNetId module_net : module_manager.module_nets(module_id)) { + /* We only care the nets that indicate short connections */ + if (false == module_net_include_output_short_connection(module_manager, module_id, module_net)) { + continue; + } + print_verilog_module_output_short_connection(fp, module_manager, module_id, module_net); + } +} + /******************************************************************** * Write a Verilog instance to a file * This function will name the input and output connections to @@ -362,9 +444,11 @@ void write_verilog_module_to_file(std::fstream& fp, fp << std::endl; /* Print internal wires */ - std::vector local_wires = find_verilog_module_local_wires(module_manager, module_id); - for (BasicPort local_wire : local_wires) { - fp << generate_verilog_port(VERILOG_PORT_WIRE, local_wire) << ";" << std::endl; + std::map> local_wires = find_verilog_module_local_wires(module_manager, module_id); + for (std::pair> port_group : local_wires) { + for (const BasicPort& local_wire : port_group.second) { + fp << generate_verilog_port(VERILOG_PORT_WIRE, local_wire) << ";" << std::endl; + } } /* Print an empty line as splitter */ @@ -372,6 +456,8 @@ void write_verilog_module_to_file(std::fstream& fp, /* Print local connection (from module inputs to output! */ print_verilog_module_local_short_connections(fp, module_manager, module_id); + + print_verilog_module_output_short_connections(fp, module_manager, module_id); /* Print an empty line as splitter */ fp << std::endl; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp index 7fd71767d..a52018ff3 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_mux.cpp @@ -538,18 +538,18 @@ void generate_verilog_rram_mux_branch_module(ModuleManager& module_manager, VTR_ASSERT(true == module_manager.valid_module_id(module_id)); /* Find 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); /* Find 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); /* Find 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); - 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); /* dump module definition + ports */ print_verilog_module_declaration(fp, module_manager, module_id); @@ -582,6 +582,13 @@ void generate_verilog_mux_branch_module(ModuleManager& module_manager, /* Multiplexers built with different technology is in different organization */ switch (circuit_lib.design_tech_type(mux_model)) { case SPICE_MODEL_DESIGN_CMOS: + /* Skip module writing if the branch subckt is a standard cell! */ + if (true == circuit_lib.valid_model_id(circuit_lib.model(module_name))) { + /* This model must be a MUX2 gate */ + VTR_ASSERT(SPICE_MODEL_GATE == circuit_lib.model_type(circuit_lib.model(module_name))); + VTR_ASSERT(SPICE_MODEL_GATE_MUX2 == circuit_lib.gate_type(circuit_lib.model(module_name))); + break; + } if (true == circuit_lib.dump_structural_verilog(mux_model)) { /* Structural verilog can be easily generated by module writer */ ModuleId mux_module = module_manager.find_module(module_name); @@ -633,7 +640,7 @@ void generate_verilog_cmos_mux_module_input_buffers(ModuleManager& module_manage VTR_ASSERT(1 == mux_input_ports.size()); /* Get the input port from MUX module */ - ModulePortId module_input_port_id = module_manager.find_module_port(module_id, circuit_lib.port_lib_name(mux_input_ports[0])); + ModulePortId module_input_port_id = module_manager.find_module_port(module_id, 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(module_id, module_input_port_id); @@ -721,7 +728,7 @@ void generate_verilog_cmos_mux_module_output_buffers(ModuleManager& module_manag /* 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(module_id, circuit_lib.port_lib_name(output_port)); + ModulePortId module_output_port_id = module_manager.find_module_port(module_id, 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(module_id, module_output_port_id); @@ -918,7 +925,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu for (const auto& mem : mems) { /* Generate the port info of each mem node: */ - BasicPort branch_node_blb_port(circuit_lib.port_lib_name(mux_blb_ports[0]), size_t(mem), size_t(mem)); + BasicPort branch_node_blb_port(circuit_lib.port_prefix(mux_blb_ports[0]), size_t(mem), size_t(mem)); branch_node_blb_ports.push_back(branch_node_blb_port); } /* Every stage, we have an additonal BLB and WL in controlling purpose @@ -930,7 +937,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu * * output_node_level is always larger than the mem_level by 1 */ - branch_node_blb_ports.push_back(BasicPort(circuit_lib.port_lib_name(mux_blb_ports[0]), + branch_node_blb_ports.push_back(BasicPort(circuit_lib.port_prefix(mux_blb_ports[0]), mux_graph.num_memory_bits() + output_node_level - 1, mux_graph.num_memory_bits() + output_node_level - 1) ); @@ -949,7 +956,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu } /* Link nodes to BLB ports for the branch module */ - ModulePortId module_blb_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_lib_name(mux_blb_ports[0])); + ModulePortId module_blb_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_prefix(mux_blb_ports[0])); VTR_ASSERT(ModulePortId::INVALID() != module_blb_port_id); /* Get the port from module */ BasicPort module_blb_port = module_manager.module_port(branch_module_id, module_blb_port_id); @@ -959,7 +966,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu for (const auto& mem : mems) { /* Generate the port info of each mem node: */ - BasicPort branch_node_blb_port(circuit_lib.port_lib_name(mux_wl_ports[0]), size_t(mem), size_t(mem)); + BasicPort branch_node_blb_port(circuit_lib.port_prefix(mux_wl_ports[0]), size_t(mem), size_t(mem)); branch_node_wl_ports.push_back(branch_node_blb_port); } /* Every stage, we have an additonal BLB and WL in controlling purpose @@ -971,7 +978,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu * * output_node_level is always larger than the mem_level by 1 */ - branch_node_wl_ports.push_back(BasicPort(circuit_lib.port_lib_name(mux_wl_ports[0]), + branch_node_wl_ports.push_back(BasicPort(circuit_lib.port_prefix(mux_wl_ports[0]), mux_graph.num_memory_bits() + output_node_level - 1, mux_graph.num_memory_bits() + output_node_level - 1) ); @@ -990,7 +997,7 @@ void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& modu } /* Link nodes to BLB ports for the branch module */ - ModulePortId module_wl_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_lib_name(mux_wl_ports[0])); + ModulePortId module_wl_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_prefix(mux_wl_ports[0])); VTR_ASSERT(ModulePortId::INVALID() != module_wl_port_id); /* Get the port from module */ BasicPort module_wl_port = module_manager.module_port(branch_module_id, module_wl_port_id); @@ -1107,13 +1114,13 @@ void generate_verilog_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++; @@ -1122,7 +1129,7 @@ void generate_verilog_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)); } @@ -1134,7 +1141,7 @@ void generate_verilog_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); } @@ -1143,7 +1150,7 @@ void generate_verilog_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); } @@ -1185,7 +1192,10 @@ void generate_verilog_mux_module(ModuleManager& module_manager, ModuleId mux_module = module_manager.find_module(module_name); VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); write_verilog_module_to_file(fp, module_manager, mux_module, - use_explicit_port_map || circuit_lib.dump_explicit_port_map(mux_model)); + ( use_explicit_port_map + || circuit_lib.dump_explicit_port_map(mux_model) + || circuit_lib.dump_explicit_port_map(circuit_lib.pass_gate_logic_model(mux_model)) ) + ); /* Add an empty line as a splitter */ fp << std::endl; break; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_preconfig_top_module.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_preconfig_top_module.cpp index 14ca60752..4e955cc7f 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_preconfig_top_module.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_preconfig_top_module.cpp @@ -17,6 +17,7 @@ #include "verilog_global.h" #include "verilog_writer_utils.h" +#include "verilog_testbench_utils.h" #include "verilog_preconfig_top_module.h" /******************************************************************** @@ -89,31 +90,6 @@ void print_verilog_preconfig_top_module_internal_wires(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * Print an instance of the FPGA top-level module - *******************************************************************/ -static -void print_verilog_preconfig_top_instance(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module) { - /* Validate the file stream */ - check_file_handler(fp); - - /* Include defined top-level module */ - print_verilog_comment(fp, std::string("----- FPGA top-level module to be capsulated -----")); - - /* Create an empty port-to-port name mapping, because we use default names */ - std::map port2port_name_map; - - /* Use explicit port mapping for a clean instanciation */ - print_verilog_module_instance(fp, module_manager, top_module, - std::string(formal_verification_top_module_uut_name), - port2port_name_map, true); - - /* Add an empty line as a splitter */ - fp << std::endl; -} - /******************************************************************** * Connect global ports of FPGA top module to constants except: * 1. operating clock, which should be wired to the clock port of @@ -144,7 +120,7 @@ void print_verilog_preconfig_top_module_connect_global_ports(std::fstream& fp, CircuitPortId linked_circuit_port_id = CircuitPortId::INVALID(); /* Find the circuit port with the same name */ for (const CircuitPortId& circuit_port_id : global_ports) { - if (0 != module_global_port.get_name().compare(circuit_lib.port_lib_name(circuit_port_id))) { + if (0 != module_global_port.get_name().compare(circuit_lib.port_prefix(circuit_port_id))) { continue; } linked_circuit_port_id = circuit_port_id; @@ -180,90 +156,6 @@ void print_verilog_preconfig_top_module_connect_global_ports(std::fstream& fp, fp << std::endl; } -/******************************************************************** - * This function adds stimuli to I/Os of FPGA fabric - * 1. For mapped I/Os, this function will wire them to the input ports - * of the pre-configured FPGA top module - * 2. For unmapped I/Os, this function will assign a constant value - * by default - *******************************************************************/ -static -void print_verilog_preconfig_top_module_connect_ios(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module, - const std::vector& L_logical_blocks, - const vtr::Point& device_size, - const std::vector>& L_grids, - const std::vector& L_blocks) { - /* Validate the file stream */ - check_file_handler(fp); - - /* 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 io_used(module_io_port.get_width(), false); - - /* See if this I/O should be wired to a benchmark input/output */ - /* Add signals from blif benchmark and short-wire them to FPGA I/O PADs - * This brings convenience to checking functionality - */ - print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----")); - 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; - } - - /* 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); - - /* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1 */ - BasicPort benchmark_io_port(std::string(std::string(io_lb.name)+ std::string(formal_verification_top_module_port_postfix)), 1); - - print_verilog_comment(fp, std::string("----- Blif Benchmark inout " + std::string(io_lb.name) + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); - if (VPACK_INPAD == io_lb.type) { - print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); - } else { - VTR_ASSERT(VPACK_OUTPAD == io_lb.type); - print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false); - } - - /* Mark this I/O has been used/wired */ - io_used[io_index] = true; - } - - /* Add an empty line as a splitter */ - fp << std::endl; - - /* Wire the unused iopads to a constant */ - print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----")); - 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); - - std::vector default_values(module_unused_io_port.get_width(), verilog_default_signal_init_value); - print_verilog_wire_constant_values(fp, module_unused_io_port, default_values); - } - - /* Add an empty line as a splitter */ - fp << std::endl; -} - /******************************************************************** * Impose the bitstream on the configuration memories * This function uses 'assign' syntax to impost the bitstream at mem port @@ -514,7 +406,8 @@ void print_verilog_preconfig_top_module(const ModuleManager& module_manager, print_verilog_preconfig_top_module_internal_wires(fp, module_manager, top_module); /* Instanciate FPGA top-level module */ - print_verilog_preconfig_top_instance(fp, module_manager, top_module); + print_verilog_testbench_fpga_instance(fp, module_manager, top_module, + std::string(formal_verification_top_module_uut_name)); /* Find clock ports in benchmark */ std::vector benchmark_clock_port_names = find_benchmark_clock_port_name(L_logical_blocks); @@ -525,9 +418,12 @@ void print_verilog_preconfig_top_module(const ModuleManager& module_manager, benchmark_clock_port_names); /* Connect I/Os to benchmark I/Os or constant driver */ - print_verilog_preconfig_top_module_connect_ios(fp, module_manager, top_module, - L_logical_blocks, device_size, L_grids, - L_blocks); + print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module, + L_logical_blocks, device_size, L_grids, + L_blocks, + std::string(formal_verification_top_module_port_postfix), + std::string(formal_verification_top_module_port_postfix), + (size_t)verilog_default_signal_init_value); /* Assign FPGA internal SRAM/Memory ports to bitstream values */ print_verilog_preconfig_top_module_load_bitstream(fp, module_manager, top_module, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c index 2c995e864..12afb3a2e 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c @@ -4064,7 +4064,7 @@ void print_verilog_flatten_connection_block_modules(ModuleManager& module_manage * We will skip those modules */ const RRGSB& rr_gsb = L_device_rr_gsb.get_gsb(ix, iy); - if ( (TRUE != is_cb_exist(CHANX, rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))) + 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))) { continue; } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c index a4199308d..a792ed4ae 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_submodules.c @@ -3085,8 +3085,7 @@ void dump_verilog_submodule_memories(t_sram_orgz_info* cur_sram_orgz_info, ********************************************************************/ static void add_user_defined_verilog_modules(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const std::vector& routing_segments) { + const CircuitLibrary& circuit_lib) { /* Iterate over Verilog modules */ for (const auto& model : circuit_lib.models()) { /* We only care about user-defined models */ @@ -3106,37 +3105,6 @@ void add_user_defined_verilog_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()) { - 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]); - - /* Try to find the module in the module manager, - * If not found, create a Verilog Module based on the circuit model, - * and add to module manager */ - if (ModuleId::INVALID() != module_manager.find_module(segment_wire_subckt_name)) { - continue; - } - 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 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); - } } /* Print a template for a user-defined circuit model @@ -3186,7 +3154,6 @@ void print_one_verilog_template_module(const ModuleManager& module_manager, static void print_verilog_submodule_templates(const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const std::vector& routing_segments, const std::string& verilog_dir, const std::string& submodule_dir) { std::string verilog_fname(submodule_dir + user_defined_template_verilog_file_name); @@ -3220,22 +3187,6 @@ void print_verilog_submodule_templates(const ModuleManager& module_manager, print_one_verilog_template_module(module_manager, fp, circuit_lib.model_name(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()) { - 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 template for the circuit model */ - print_one_verilog_template_module(module_manager, fp, segment_wire_subckt_name); - } - /* close file stream */ fp.close(); @@ -3255,18 +3206,12 @@ void dump_verilog_submodules(ModuleManager& module_manager, t_det_routing_arch* routing_arch, t_syn_verilog_opts fpga_verilog_opts) { - /* Create a vector of segments. TODO: should come from DeviceContext */ - std::vector L_segment_vec; - for (int i = 0; i < Arch.num_segments; ++i) { - L_segment_vec.push_back(Arch.Segments[i]); - } - /* TODO: Register all the user-defined modules in the module manager * This should be done prior to other steps in this function, * because they will be instanciated by other primitive modules */ vpr_printf(TIO_MESSAGE_INFO, "Registering user-defined modules...\n"); - add_user_defined_verilog_modules(module_manager, Arch.spice->circuit_lib, L_segment_vec); + add_user_defined_verilog_modules(module_manager, Arch.spice->circuit_lib); print_verilog_submodule_essentials(module_manager, std::string(verilog_dir), @@ -3299,7 +3244,7 @@ void dump_verilog_submodules(ModuleManager& module_manager, fpga_verilog_opts.dump_explicit_verilog); /* 3. Hardwires */ - print_verilog_submodule_wires(module_manager, Arch.spice->circuit_lib, L_segment_vec, std::string(verilog_dir), std::string(submodule_dir)); + print_verilog_submodule_wires(module_manager, Arch.spice->circuit_lib, std::string(verilog_dir), std::string(submodule_dir)); /* 4. Memories */ vpr_printf(TIO_MESSAGE_INFO, "Generating modules of memories...\n"); @@ -3311,7 +3256,7 @@ void dump_verilog_submodules(ModuleManager& module_manager, /* 5. Dump template for all the modules */ if (TRUE == fpga_verilog_opts.print_user_defined_template) { - print_verilog_submodule_templates(module_manager, Arch.spice->circuit_lib, L_segment_vec, std::string(verilog_dir), std::string(submodule_dir)); + print_verilog_submodule_templates(module_manager, Arch.spice->circuit_lib, std::string(verilog_dir), std::string(submodule_dir)); } /* Create a header file to include all the subckts */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_tcl_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_tcl_utils.c index c3df4ed95..7fa1b51e6 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_tcl_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_tcl_utils.c @@ -354,8 +354,11 @@ t_cb* get_chan_rr_node_ending_cb(t_rr_node* src_rr_node, */ assert (src_rr_node->xlow == src_rr_node->xhigh); next_cb_x = src_rr_node->xlow; - assert (end_rr_node->ylow == end_rr_node->yhigh); - next_cb_y = end_rr_node->ylow; + /* Heterogeneous blocks may have ylow != yhigh */ + if (IPIN != end_rr_node->type) { + assert (end_rr_node->ylow == end_rr_node->yhigh); + } + next_cb_y = end_rr_node->yhigh; /* Side will be either on RIGHT or LEFT */ ipin_side[0] = LEFT; ipin_side[1] = RIGHT; @@ -372,6 +375,7 @@ t_cb* get_chan_rr_node_ending_cb(t_rr_node* src_rr_node, } /* Double check if src_rr_node is in the IN_PORT list */ + /* TODO: this part should be refactored! node_exist = 0; for (iside = 0; iside < num_chan_sides; iside++) { if (OPEN != get_rr_node_index_in_cb_info( src_rr_node, @@ -381,8 +385,10 @@ t_cb* get_chan_rr_node_ending_cb(t_rr_node* src_rr_node, } } assert (0 < node_exist); + */ /* Double check if end_rr_node is in the OUT_PORT list */ + /* TODO: this part should be refactored! node_exist = 0; for (iside = 0; iside < num_ipin_sides; iside++) { if (OPEN != get_rr_node_index_in_cb_info( end_rr_node, @@ -392,6 +398,7 @@ t_cb* get_chan_rr_node_ending_cb(t_rr_node* src_rr_node, } } assert (0 < node_exist); + */ return next_cb; } diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.cpp new file mode 100644 index 000000000..ee2a80ea6 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.cpp @@ -0,0 +1,564 @@ +/******************************************************************** + * This file includes most utilized functions that are used to create + * Verilog testbenches + * + * Note: please try to avoid using global variables in this file + * so that we can make it free to use anywhere + *******************************************************************/ +#include + +#include "vtr_assert.h" +#include "device_port.h" + +#include "fpga_x2p_utils.h" +#include "fpga_x2p_benchmark_utils.h" + +#include "verilog_writer_utils.h" +#include "verilog_testbench_utils.h" + +/******************************************************************** + * Print an instance of the FPGA top-level module + *******************************************************************/ +void print_verilog_testbench_fpga_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const std::string& top_instance_name) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Include defined top-level module */ + print_verilog_comment(fp, std::string("----- FPGA top-level module to be capsulated -----")); + + /* Create an empty port-to-port name mapping, because we use default names */ + std::map port2port_name_map; + + /* Use explicit port mapping for a clean instanciation */ + print_verilog_module_instance(fp, module_manager, top_module, + top_instance_name, + port2port_name_map, true); + + /* Add an empty line as a splitter */ + fp << std::endl; +} + +/******************************************************************** + * Instanciate the input benchmark module + *******************************************************************/ +void print_verilog_testbench_benchmark_instance(std::fstream& fp, + const std::string& module_name, + const std::string& instance_name, + const std::string& module_input_port_postfix, + const std::string& module_output_port_postfix, + const std::string& output_port_postfix, + const std::vector& L_logical_blocks, + const bool& use_explicit_port_map) { + /* Validate the file stream */ + check_file_handler(fp); + + fp << "\t" << module_name << " " << instance_name << "(" << std::endl; + + size_t port_counter = 0; + for (const t_logical_block& lb : L_logical_blocks) { + /* Bypass non-I/O logical blocks ! */ + if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { + continue; + } + /* The first port does not need a comma */ + if(0 < port_counter){ + fp << "," << std::endl; + } + /* Input port follows the logical block name while output port requires a special postfix */ + if (VPACK_INPAD == lb.type){ + fp << "\t\t"; + if (true == use_explicit_port_map) { + fp << "." << std::string(lb.name) << module_input_port_postfix << "("; + } + fp << std::string(lb.name); + if (true == use_explicit_port_map) { + fp << ")"; + } + } else { + VTR_ASSERT_SAFE(VPACK_OUTPAD == lb.type); + fp << "\t\t"; + if (true == use_explicit_port_map) { + fp << "." << std::string(lb.name) << module_output_port_postfix << "("; + } + fp << std::string(lb.name) << output_port_postfix; + if (true == use_explicit_port_map) { + fp << ")"; + } + } + /* Update the counter */ + port_counter++; + } + fp << "\t);" << std::endl; +} + +/******************************************************************** + * This function adds stimuli to I/Os of FPGA fabric + * 1. For mapped I/Os, this function will wire them to the input ports + * of the pre-configured FPGA top module + * 2. For unmapped I/Os, this function will assign a constant value + * by default + *******************************************************************/ +void print_verilog_testbench_connect_fpga_ios(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const std::string& io_input_port_name_postfix, + const std::string& io_output_port_name_postfix, + const size_t& unused_io_value) { + /* Validate the file stream */ + check_file_handler(fp); + + /* 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 io_used(module_io_port.get_width(), false); + + /* See if this I/O should be wired to a benchmark input/output */ + /* Add signals from blif benchmark and short-wire them to FPGA I/O PADs + * This brings convenience to checking functionality + */ + print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----")); + 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; + } + + /* 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); + + /* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1 + * In addition, the input and output ports may have different postfix in naming + * due to verification context! Here, we give full customization on naming + */ + BasicPort benchmark_io_port; + if (VPACK_INPAD == io_lb.type) { + benchmark_io_port.set_name(std::string(std::string(io_lb.name) + io_input_port_name_postfix)); + benchmark_io_port.set_width(1); + print_verilog_comment(fp, std::string("----- Blif Benchmark input " + std::string(io_lb.name) + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); + } else { + VTR_ASSERT(VPACK_OUTPAD == io_lb.type); + benchmark_io_port.set_name(std::string(std::string(io_lb.name) + io_output_port_name_postfix)); + benchmark_io_port.set_width(1); + print_verilog_comment(fp, std::string("----- Blif Benchmark output " + std::string(io_lb.name) + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false); + } + + /* Mark this I/O has been used/wired */ + io_used[io_index] = true; + } + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Wire the unused iopads to a constant */ + print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----")); + 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); + + std::vector default_values(module_unused_io_port.get_width(), unused_io_value); + print_verilog_wire_constant_values(fp, module_unused_io_port, default_values); + } + + /* Add an empty line as a splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print Verilog codes to set up a timeout for the simulation + * and dump the waveform to VCD files + * + * Note that: these codes are tuned for Icarus simulator!!! + *******************************************************************/ +void print_verilog_timeout_and_vcd(std::fstream& fp, + const std::string& icarus_preprocessing_flag, + const std::string& module_name, + const std::string& vcd_fname, + const std::string& simulation_start_counter_name, + const std::string& error_counter_name, + const int& simulation_time) { + /* Validate the file stream */ + check_file_handler(fp); + + /* The following verilog codes are tuned for Icarus */ + print_verilog_preprocessing_flag(fp, icarus_preprocessing_flag); + + print_verilog_comment(fp, std::string("----- Begin Icarus requirement -------")); + + fp << "\tinitial begin" << std::endl; + fp << "\t\t$dumpfile(\"" << vcd_fname << "\");" << std::endl; + fp << "\t\t$dumpvars(1, " << module_name << ");" << std::endl; + fp << "\tend" << std::endl; + + /* Condition ends for the Icarus requirement */ + print_verilog_endif(fp); + + print_verilog_comment(fp, std::string("----- END Icarus requirement -------")); + + /* Add an empty line as splitter */ + fp << std::endl; + + BasicPort sim_start_port(simulation_start_counter_name, 1); + + fp << "initial begin" << std::endl; + fp << "\t" << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << " <= 1'b1;" << std::endl; + fp << "\t$timeformat(-9, 2, \"ns\", 20);" << std::endl; + fp << "\t$display(\"Simulation start\");" << std::endl; + print_verilog_comment(fp, std::string("----- Can be changed by the user for his/her need -------")); + fp << "\t#" << simulation_time << std::endl; + fp << "\tif(" << error_counter_name << " == 0) begin" << std::endl; + fp << "\t\t$display(\"Simulation Succeed\");" << std::endl; + fp << "\tend else begin" << std::endl; + fp << "\t\t$display(\"Simulation Failed with " << std::string("%d") << " error(s)\", " << error_counter_name << ");" << std::endl; + fp << "\tend" << std::endl; + fp << "\t$finish;" << std::endl; + fp << "end" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Generate the clock port name to be used in this testbench + * + * Restrictions: + * Assume this is a single clock benchmark + *******************************************************************/ +BasicPort generate_verilog_testbench_clock_port(const std::vector& clock_port_names, + const std::string& default_clock_name) { + if (0 == clock_port_names.size()) { + return BasicPort(default_clock_name, 1); + } + + VTR_ASSERT(1 == clock_port_names.size()); + return BasicPort(clock_port_names[0], 1); +} + +/******************************************************************** + * Print Verilog codes to check the equivalence of output vectors + * + * Restriction: this function only supports single clock benchmarks! + *******************************************************************/ +void print_verilog_testbench_check(std::fstream& fp, + const std::string& autochecked_preprocessing_flag, + const std::string& simulation_start_counter_name, + const std::string& benchmark_port_postfix, + const std::string& fpga_port_postfix, + const std::string& check_flag_port_postfix, + const std::string& error_counter_name, + const std::vector& L_logical_blocks, + const std::vector& clock_port_names, + const std::string& default_clock_name) { + + /* Validate the file stream */ + check_file_handler(fp); + + /* Add output autocheck conditionally: only when a preprocessing flag is enable */ + print_verilog_preprocessing_flag(fp, autochecked_preprocessing_flag); + + print_verilog_comment(fp, std::string("----- Begin checking output vectors -------")); + + BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, default_clock_name); + + print_verilog_comment(fp, std::string("----- Skip the first falling edge of clock, it is for initialization -------")); + + BasicPort sim_start_port(simulation_start_counter_name, 1); + + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, sim_start_port) << ";" << std::endl; + fp << std::endl; + + fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl; + fp << "\t\tif (1'b1 == " << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << ") begin" << std::endl; + fp << "\t\t"; + print_verilog_register_connection(fp, sim_start_port, sim_start_port, true); + fp << "\t\tend else begin" << std::endl; + + for (const t_logical_block& lb : L_logical_blocks) { + /* Bypass non-I/O logical blocks ! */ + if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { + continue; + } + + if (VPACK_OUTPAD == lb.type){ + fp << "\t\t\tif(!(" << std::string(lb.name) << fpga_port_postfix; + fp << " === " << std::string(lb.name) << benchmark_port_postfix; + fp << ") && !(" << std::string(lb.name) << benchmark_port_postfix; + fp << " === 1'bx)) begin" << std::endl; + fp << "\t\t\t\t" << std::string(lb.name) << check_flag_port_postfix << " <= 1'b1;" << std::endl; + fp << "\t\t\tend else begin" << std::endl; + fp << "\t\t\t\t" << std::string(lb.name) << check_flag_port_postfix << "<= 1'b0;" << std::endl; + fp << "\t\t\tend" << std::endl; + } + } + fp << "\t\tend" << std::endl; + fp << "\tend" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; + + for (const t_logical_block& lb : L_logical_blocks) { + /* Bypass non-I/O logical blocks ! */ + if (VPACK_OUTPAD != lb.type) { + continue; + } + + fp << "\talways@(posedge " << std::string(lb.name) << check_flag_port_postfix << ") begin" << std::endl; + fp << "\t\tif(" << std::string(lb.name) << check_flag_port_postfix << ") begin" << std::endl; + fp << "\t\t\t" << error_counter_name << " = " << error_counter_name << " + 1;" << std::endl; + fp << "\t\t\t$display(\"Mismatch on " << std::string(lb.name) << fpga_port_postfix << " at time = " << std::string("%t") << "\", $realtime);" << std::endl; + fp << "\t\tend" << std::endl; + fp << "\tend" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; + } + + /* Condition ends */ + print_verilog_endif(fp); + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Generate random stimulus for the clock port + * This function is designed to drive the clock port of a benchmark module + * If there is no clock port found, we will give a default clock name + * In such case, this clock will not be wired to the benchmark module + * but be only used as a synchronizer in verification + *******************************************************************/ +void print_verilog_testbench_clock_stimuli(std::fstream& fp, + const t_spice_params& simulation_parameters, + const BasicPort& clock_port) { + /* Validate the file stream */ + check_file_handler(fp); + + print_verilog_comment(fp, std::string("----- Clock Initialization -------")); + + fp << "\tinitial begin" << std::endl; + /* Create clock stimuli */ + fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << " <= 1'b0;" << std::endl; + fp << "\t\twhile(1) begin" << std::endl; + fp << "\t\t\t#" << std::setprecision(10) << ((0.5/simulation_parameters.stimulate_params.op_clock_freq)/verilog_sim_timescale) << std::endl; + fp << "\t\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); + fp << " <= !"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, clock_port); + fp << ";" << std::endl; + fp << "\t\tend" << std::endl; + + fp << "\tend" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Generate random stimulus for the input ports (non-clock signals) + * For clock signals, please use print_verilog_testbench_clock_stimuli + *******************************************************************/ +void print_verilog_testbench_random_stimuli(std::fstream& fp, + const std::vector& L_logical_blocks, + const std::string& check_flag_port_postfix, + const BasicPort& clock_port) { + /* Validate the file stream */ + check_file_handler(fp); + + print_verilog_comment(fp, std::string("----- Input Initialization -------")); + + fp << "\tinitial begin" << std::endl; + + for (const t_logical_block& lb : L_logical_blocks) { + /* Bypass non-I/O logical blocks ! */ + if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { + continue; + } + + /* Clock ports will be initialized later */ + if ( (VPACK_INPAD == lb.type) && (FALSE == lb.is_clock) ) { + fp << "\t\t" << std::string(lb.name) << " <= 1'b0;" << std::endl; + } + } + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Set 0 to registers for checking flags */ + for (const t_logical_block& lb : L_logical_blocks) { + /* We care only those logic blocks which are input I/Os */ + if (VPACK_OUTPAD != lb.type) { + continue; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(std::string(lb.name) + check_flag_port_postfix), 1); + fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, output_port) << " <= 1'b0;" << std::endl; + } + + fp << "\tend" << std::endl; + /* Finish initialization */ + + /* Add an empty line as splitter */ + fp << std::endl; + + // Not ready yet to determine if input is reset +/* + fprintf(fp, "//----- Reset Stimulis\n"); + fprintf(fp, " initial begin\n"); + fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001); + fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); + fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001); + fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); + fprintf(fp, " while(1) begin\n"); + fprintf(fp, " #%.3f\n", (rand() % 15) + 0.5); + fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); + fprintf(fp, " #%.3f\n", (rand() % 10000) + 200); + fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name); + fprintf(fp, " end\n"); + fprintf(fp, " end\n\n"); +*/ + + print_verilog_comment(fp, std::string("----- Input Stimulus -------")); + fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl; + + for (const t_logical_block& lb : L_logical_blocks) { + /* Bypass non-I/O logical blocks ! */ + if ( (VPACK_INPAD != lb.type) && (VPACK_OUTPAD != lb.type) ) { + continue; + } + + /* Clock ports will be initialized later */ + if ( (VPACK_INPAD == lb.type) && (FALSE == lb.is_clock) ) { + fp << "\t\t" << std::string(lb.name) << " <= $random;" << std::endl; + } + } + + fp << "\tend" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print Verilog declaration of shared ports appear in testbenches + * which are + * 1. the shared input ports (registers) to drive both + * FPGA fabric and benchmark instance + * 2. the output ports (wires) for both FPGA fabric and benchmark instance + * 3. the checking flag ports to evaluate if outputs matches under the + * same input vectors + *******************************************************************/ +void print_verilog_testbench_shared_ports(std::fstream& fp, + const std::vector& L_logical_blocks, + const std::string& benchmark_output_port_postfix, + const std::string& fpga_output_port_postfix, + const std::string& check_flag_port_postfix, + const std::string& autocheck_preprocessing_flag) { + /* Validate the file stream */ + check_file_handler(fp); + + + /* Instantiate register for inputs stimulis */ + print_verilog_comment(fp, std::string("----- Shared inputs -------")); + for (const t_logical_block& lb : L_logical_blocks) { + /* We care only those logic blocks which are input I/Os */ + if (VPACK_INPAD != lb.type) { + continue; + } + + /* Skip clocks because they are handled in another function */ + if (TRUE == lb.is_clock) { + continue; + } + + /* Each logical block assumes a single-width port */ + BasicPort input_port(std::string(lb.name), 1); + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" << std::endl; + } + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Instantiate wires for FPGA fabric outputs */ + print_verilog_comment(fp, std::string("----- FPGA fabric outputs -------")); + + for (const t_logical_block& lb : L_logical_blocks) { + /* We care only those logic blocks which are input I/Os */ + if (VPACK_OUTPAD != lb.type) { + continue; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(std::string(lb.name) + fpga_output_port_postfix), 1); + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl; + } + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */ + print_verilog_preprocessing_flag(fp, std::string(autocheck_preprocessing_flag)); + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Instantiate wire for benchmark output */ + print_verilog_comment(fp, std::string("----- Benchmark outputs -------")); + for (const t_logical_block& lb : L_logical_blocks) { + /* We care only those logic blocks which are input I/Os */ + if (VPACK_OUTPAD != lb.type) { + continue; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(std::string(lb.name) + benchmark_output_port_postfix), 1); + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl; + } + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Instantiate register for output comparison */ + print_verilog_comment(fp, std::string("----- Output vectors checking flags -------")); + for (const t_logical_block& lb : L_logical_blocks) { + /* We care only those logic blocks which are input I/Os */ + if (VPACK_OUTPAD != lb.type) { + continue; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(std::string(lb.name) + check_flag_port_postfix), 1); + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, output_port) << ";" << std::endl; + } + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Condition ends for the benchmark instanciation */ + print_verilog_endif(fp); + + /* Add an empty line as splitter */ + fp << std::endl; +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.h new file mode 100644 index 000000000..0bbb1bc53 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_testbench_utils.h @@ -0,0 +1,74 @@ +#ifndef VERILOG_TESTBENCH_UTILS_H +#define VERILOG_TESTBENCH_UTILS_H + +/* Include header files which are used in the function declaration */ +#include +#include +#include +#include "module_manager.h" +#include "vpr_types.h" + +void print_verilog_testbench_fpga_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const std::string& top_instance_name); + +void print_verilog_testbench_benchmark_instance(std::fstream& fp, + const std::string& module_name, + const std::string& instance_name, + const std::string& module_input_port_postfix, + const std::string& module_output_port_postfix, + const std::string& output_port_postfix, + const std::vector& L_logical_blocks, + const bool& use_explicit_port_map); + +void print_verilog_testbench_connect_fpga_ios(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const std::string& io_input_port_name_postfix, + const std::string& io_output_port_name_postfix, + const size_t& unused_io_value); + +void print_verilog_timeout_and_vcd(std::fstream& fp, + const std::string& icarus_preprocessing_flag, + const std::string& module_name, + const std::string& vcd_fname, + const std::string& simulation_start_counter_name, + const std::string& error_counter_name, + const int& simulation_time); + +BasicPort generate_verilog_testbench_clock_port(const std::vector& clock_port_names, + const std::string& default_clock_name); + +void print_verilog_testbench_check(std::fstream& fp, + const std::string& autochecked_preprocessing_flag, + const std::string& simulation_start_counter_name, + const std::string& benchmark_port_postfix, + const std::string& fpga_port_postfix, + const std::string& check_flag_port_postfix, + const std::string& error_counter_name, + const std::vector& L_logical_blocks, + const std::vector& clock_port_names, + const std::string& default_clock_name); + +void print_verilog_testbench_clock_stimuli(std::fstream& fp, + const t_spice_params& simulation_parameters, + const BasicPort& clock_port); + +void print_verilog_testbench_random_stimuli(std::fstream& fp, + const std::vector& L_logical_blocks, + const std::string& check_flag_port_postfix, + const BasicPort& clock_port); + +void print_verilog_testbench_shared_ports(std::fstream& fp, + const std::vector& L_logical_blocks, + const std::string& benchmark_output_port_postfix, + const std::string& fpga_output_port_postfix, + const std::string& check_flag_port_postfix, + const std::string& autocheck_preprocessing_flag); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.c index 6671ec6e4..b1c628c99 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.c @@ -1329,7 +1329,7 @@ void dump_verilog_input_blif_testbench_stimuli(FILE* fp, */ void dump_verilog_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_spice verilog) { FILE* fp = NULL; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.cpp new file mode 100644 index 000000000..9f801c44a --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.cpp @@ -0,0 +1,881 @@ +/******************************************************************** + * This file includes functions that are used to create + * an auto-check top-level testbench for a FPGA fabric + *******************************************************************/ +#include +#include +#include +#include + +#include "vtr_assert.h" +#include "device_port.h" +#include "util.h" + +#include "bitstream_manager_utils.h" + +#include "fpga_x2p_naming.h" +#include "fpga_x2p_utils.h" +#include "simulation_utils.h" +#include "fpga_x2p_benchmark_utils.h" + +#include "verilog_global.h" +#include "verilog_writer_utils.h" +#include "verilog_testbench_utils.h" +#include "verilog_top_testbench.h" + +/******************************************************************** + * Local variables used only in this file + *******************************************************************/ +constexpr char* TOP_TESTBENCH_REFERENCE_INSTANCE_NAME = "REF_DUT"; +constexpr char* TOP_TESTBENCH_FPGA_INSTANCE_NAME = "FPGA_DUT"; +constexpr char* TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX = "_benchmark"; +constexpr char* TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX = "_fpga"; + +constexpr char* TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX = "_flag"; + +constexpr char* TOP_TESTBENCH_CC_PROG_TASK_NAME = "prog_cycle_task"; + +constexpr char* TOP_TESTBENCH_SIM_START_PORT_NAME = "sim_start"; + +constexpr int TOP_TESTBENCH_MAGIC_NUMBER_FOR_SIMULATION_TIME = 200; +constexpr char* TOP_TESTBENCH_ERROR_COUNTER = "nb_error"; + +/******************************************************************** + * Print local wires for configuration chain protocols + *******************************************************************/ +static +void print_verilog_top_testbench_config_chain_port(std::fstream& fp) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Print the head of configuraion-chains here */ + print_verilog_comment(fp, std::string("---- Configuration-chain head -----")); + BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, config_chain_head_port) << ";" << std::endl; + + /* Print the tail of configuration-chains here */ + print_verilog_comment(fp, std::string("---- Configuration-chain tail -----")); + BasicPort config_chain_tail_port(generate_configuration_chain_tail_name(), 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, config_chain_tail_port) << ";" << std::endl; +} + +/******************************************************************** + * Print local wires for different types of configuration protocols + *******************************************************************/ +static +void print_verilog_top_testbench_config_protocol_port(std::fstream& fp, + const e_sram_orgz& sram_orgz_type) { + switch(sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* TODO */ + break; + case SPICE_SRAM_SCAN_CHAIN: + print_verilog_top_testbench_config_chain_port(fp); + break; + case SPICE_SRAM_MEMORY_BANK: + /* TODO */ + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d]) Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/******************************************************************** + * Wire the global ports of FPGA fabric to local wires + *******************************************************************/ +static +void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports) { + /* Validate the file stream */ + check_file_handler(fp); + + print_verilog_comment(fp, std::string("----- Begin connecting global ports of FPGA fabric to stimuli -----")); + + /* Connect global clock ports to operating or programming clock signal */ + for (const CircuitPortId& model_global_port : global_ports) { + if (SPICE_MODEL_PORT_CLOCK != circuit_lib.port_type(model_global_port)) { + continue; + } + /* Reach here, it means we have a global clock to deal with: + * 1. if the port is identified as a programming clock, + * connect it to the local wire of programming clock + * 2. if the port is identified as an operating clock + * connect it to the local wire of operating clock + */ + /* Find the module port */ + ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port)); + + BasicPort stimuli_clock_port; + if (true == circuit_lib.port_is_prog(model_global_port)) { + stimuli_clock_port.set_name(std::string(top_tb_prog_clock_port_name)); + stimuli_clock_port.set_width(1); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port)); + stimuli_clock_port.set_name(std::string(top_tb_op_clock_port_name)); + stimuli_clock_port.set_width(1); + } + /* Wire the port to the input stimuli: + * The wiring will be inverted if the default value of the global port is 1 + * Otherwise, the wiring will not be inverted! + */ + print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port), + stimuli_clock_port, + 1 == circuit_lib.port_default_value(model_global_port)); + } + + /* Connect global configuration done ports to configuration done signal */ + for (const CircuitPortId& model_global_port : global_ports) { + /* Bypass clock signals, they have been processed */ + if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) { + continue; + } + if (false == circuit_lib.port_is_config_enable(model_global_port)) { + continue; + } + /* Reach here, it means we have a configuration done port to deal with */ + /* Find the module port */ + ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port)); + + BasicPort stimuli_config_done_port(std::string(top_tb_config_done_port_name), 1); + /* Wire the port to the input stimuli: + * The wiring will be inverted if the default value of the global port is 1 + * Otherwise, the wiring will not be inverted! + */ + print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port), + stimuli_config_done_port, + 1 == circuit_lib.port_default_value(model_global_port)); + } + + /* Connect global reset ports to operating or programming reset signal */ + for (const CircuitPortId& model_global_port : global_ports) { + /* Bypass clock signals, they have been processed */ + if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) { + continue; + } + /* Bypass config_done signals, they have been processed */ + if (true == circuit_lib.port_is_config_enable(model_global_port)) { + continue; + } + + if (false == circuit_lib.port_is_reset(model_global_port)) { + continue; + } + /* Reach here, it means we have a reset port to deal with */ + /* Find the module port */ + ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port)); + + BasicPort stimuli_reset_port; + if (true == circuit_lib.port_is_prog(model_global_port)) { + stimuli_reset_port.set_name(std::string(top_tb_prog_reset_port_name)); + stimuli_reset_port.set_width(1); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port)); + stimuli_reset_port.set_name(std::string(top_tb_reset_port_name)); + stimuli_reset_port.set_width(1); + } + /* Wire the port to the input stimuli: + * The wiring will be inverted if the default value of the global port is 1 + * Otherwise, the wiring will not be inverted! + */ + print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port), + stimuli_reset_port, + 1 == circuit_lib.port_default_value(model_global_port)); + } + + /* Connect global set ports to operating or programming set signal */ + for (const CircuitPortId& model_global_port : global_ports) { + /* Bypass clock signals, they have been processed */ + if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) { + continue; + } + /* Bypass config_done signals, they have been processed */ + if (true == circuit_lib.port_is_config_enable(model_global_port)) { + continue; + } + + /* Bypass reset signals, they have been processed */ + if (true == circuit_lib.port_is_reset(model_global_port)) { + continue; + } + + if (false == circuit_lib.port_is_set(model_global_port)) { + continue; + } + /* Reach here, it means we have a set port to deal with */ + /* Find the module port */ + ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port)); + + BasicPort stimuli_set_port; + if (true == circuit_lib.port_is_prog(model_global_port)) { + stimuli_set_port.set_name(std::string(top_tb_prog_set_port_name)); + stimuli_set_port.set_width(1); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port)); + stimuli_set_port.set_name(std::string(top_tb_set_port_name)); + stimuli_set_port.set_width(1); + } + /* Wire the port to the input stimuli: + * The wiring will be inverted if the default value of the global port is 1 + * Otherwise, the wiring will not be inverted! + */ + print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port), + stimuli_set_port, + 1 == circuit_lib.port_default_value(model_global_port)); + } + + /* For the rest of global ports, wire them to constant signals */ + for (const CircuitPortId& model_global_port : global_ports) { + /* Bypass clock signals, they have been processed */ + if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) { + continue; + } + /* Bypass config_done signals, they have been processed */ + if (true == circuit_lib.port_is_config_enable(model_global_port)) { + continue; + } + + /* Bypass reset signals, they have been processed */ + if (true == circuit_lib.port_is_reset(model_global_port)) { + continue; + } + + /* Bypass set signals, they have been processed */ + if (true == circuit_lib.port_is_set(model_global_port)) { + continue; + } + + /* Reach here, it means we have a port to deal with */ + /* Find the module port and wire it to constant values */ + ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port)); + VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port)); + + BasicPort module_port = module_manager.module_port(top_module, module_global_port); + std::vector default_values(module_port.get_width(), circuit_lib.port_default_value(model_global_port)); + print_verilog_wire_constant_values(fp, module_port, default_values); + } + + print_verilog_comment(fp, std::string("----- End connecting global ports of FPGA fabric to stimuli -----")); +} + +/******************************************************************** + * This function prints the top testbench module declaration + * and internal wires/port declaration + * Ports can be classified in two categories: + * 1. General-purpose ports, which are datapath I/Os, clock signals + * for the FPGA fabric and input benchmark + * 2. Fabric-featured ports, which are required by configuration + * protocols. + * Due the difference in configuration protocols, the internal + * wires and ports will be different: + * (a) configuration-chain: we will have two ports, + * a head and a tail for the configuration chain, + * in addition to the regular ports. + * (b) memory-decoders: we will have a few ports to drive + * address lines for decoders and a bit input port to feed + * configuration bits + *******************************************************************/ +static +void print_verilog_top_testbench_ports(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const std::vector& L_logical_blocks, + const std::vector& clock_port_names, + const e_sram_orgz& sram_orgz_type, + const std::string& circuit_name){ + /* Validate the file stream */ + check_file_handler(fp); + + /* Print module definition */ + fp << "module " << circuit_name << std::string(modelsim_autocheck_testbench_module_postfix); + fp << ";" << std::endl; + + /* Print regular local wires: + * 1. global ports, i.e., reset, set and clock signals + * 2. datapath I/O signals + */ + /* Global ports of top-level module */ + print_verilog_comment(fp, std::string("----- Local wires for global ports of FPGA fabric -----")); + for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) { + fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl; + } + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Datapath I/Os of top-level module */ + print_verilog_comment(fp, std::string("----- Local wires for I/Os of FPGA fabric -----")); + for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)) { + fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl; + } + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Add local wires/registers that drive stimulus + * We create these general purpose ports here, + * and then wire them to the ports of FPGA fabric depending on their usage + */ + /* Configuration done port */ + BasicPort config_done_port(std::string(top_tb_config_done_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, config_done_port) << ";" << std::endl; + + /* Programming clock */ + BasicPort prog_clock_port(std::string(top_tb_prog_clock_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, prog_clock_port) << ";" << std::endl; + BasicPort prog_clock_register_port(std::string(std::string(top_tb_prog_clock_port_name) + std::string(top_tb_clock_reg_postfix)), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, prog_clock_register_port) << ";" << std::endl; + + /* Operating clock */ + BasicPort op_clock_port(std::string(top_tb_op_clock_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, op_clock_port) << ";" << std::endl; + BasicPort op_clock_register_port(std::string(std::string(top_tb_op_clock_port_name) + std::string(top_tb_clock_reg_postfix)), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, op_clock_register_port) << ";" << std::endl; + + /* Programming set and reset */ + BasicPort prog_reset_port(std::string(top_tb_prog_reset_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, prog_reset_port) << ";" << std::endl; + BasicPort prog_set_port(std::string(top_tb_prog_set_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, prog_set_port) << ";" << std::endl; + + /* Global set and reset */ + BasicPort reset_port(std::string(top_tb_reset_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, reset_port) << ";" << std::endl; + BasicPort set_port(std::string(top_tb_set_port_name), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, set_port) << ";" << std::endl; + + /* Configuration ports depend on the organization of SRAMs */ + print_verilog_top_testbench_config_protocol_port(fp, sram_orgz_type); + + /* Create a clock port if the benchmark have one but not in the default name! + * We will wire the clock directly to the operating clock directly + */ + for (const std::string clock_port_name : clock_port_names) { + if (0 == clock_port_name.compare(op_clock_port.get_name())) { + continue; + } + /* Ensure the clock port name is not a duplication of global ports of the FPGA module */ + bool print_clock_port = true; + for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) { + if (0 == clock_port_name.compare(module_port.get_name())) { + print_clock_port = false; + } + } + if (false == print_clock_port) { + continue; + } + + /* Print the clock and wire it to op_clock */ + print_verilog_comment(fp, std::string("----- Create a clock for benchmark and wire it to op_clock -------")); + BasicPort clock_port(clock_port_name, 1); + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, clock_port) << ";" << std::endl; + print_verilog_wire_connection(fp, clock_port, op_clock_port, false); + } + + print_verilog_testbench_shared_ports(fp, L_logical_blocks, + std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), + std::string(autochecked_simulation_flag)); + + /* Instantiate an integer to count the number of error and + * determine if the simulation succeed or failed + */ + print_verilog_comment(fp, std::string("----- Error counter -----")); + fp << "\tinteger " << TOP_TESTBENCH_ERROR_COUNTER << "= 0;" << std::endl; +} + +/******************************************************************** + * Instanciate the input benchmark module + *******************************************************************/ +static +void print_verilog_top_testbench_benchmark_instance(std::fstream& fp, + const std::string& reference_verilog_top_name, + const std::vector& L_logical_blocks) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */ + print_verilog_preprocessing_flag(fp, std::string(autochecked_simulation_flag)); + + print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------")); + + /* Do NOT use explicit port mapping here: + * VPR added a prefix of "out_" to the output ports of input benchmark + */ + print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name, + std::string(TOP_TESTBENCH_REFERENCE_INSTANCE_NAME), + std::string(), + std::string(), + std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), L_logical_blocks, + false); + + print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------")); + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Condition ends for the benchmark instanciation */ + print_verilog_endif(fp); + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print tasks (processes) in Verilog format, + * which is very useful in generating stimuli for each clock cycle + * This function is tuned for configuration-chain manipulation: + * During each programming cycle, we feed the input of scan chain with a memory bit + *******************************************************************/ +static +void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fstream& fp) { + + /* Validate the file stream */ + check_file_handler(fp); + + BasicPort prog_clock_port(std::string(top_tb_prog_clock_port_name), 1); + BasicPort cc_head_port(generate_configuration_chain_head_name(), 1); + BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), 1); + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Feed the scan-chain input at each falling edge of programming clock + * It aims at avoid racing the programming clock (scan-chain data changes at the rising edge). + */ + print_verilog_comment(fp, std::string("----- Task: input values during a programming clock cycle -----")); + fp << "task " << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME) << ";" << std::endl; + fp << generate_verilog_port(VERILOG_PORT_INPUT, cc_head_value) << ";" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + fp << "\t\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_port); + fp << " = "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_value); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + fp << "endtask" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print tasks, which is very useful in generating stimuli for each clock cycle + *******************************************************************/ +static +void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp, + const e_sram_orgz& sram_orgz_type) { + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + break; + case SPICE_SRAM_SCAN_CHAIN: + print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp); + break; + case SPICE_SRAM_MEMORY_BANK: + /* TODO: + dump_verilog_top_testbench_stimuli_serial_version_tasks_memory_bank(cur_sram_orgz_info, fp); + */ + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s,[LINE%d])Invalid type of SRAM organization!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/******************************************************************** + * Print generatic input stimuli for the top testbench + * include: + * 1. configuration done signal + * 2. programming clock + * 3. operating clock + * 4. programming reset signal + * 5. programming set signal + * 6. reset signal + * 7. set signal + *******************************************************************/ +static +void print_verilog_top_testbench_generic_stimulus(std::fstream& fp, + const size_t& num_config_clock_cycles, + const float& prog_clock_period, + const float& op_clock_period, + const float& timescale) { + /* Validate the file stream */ + check_file_handler(fp); + + print_verilog_comment(fp, std::string("----- Number of clock cycles in configuration phase: " + std::to_string(num_config_clock_cycles) + " -----")); + + BasicPort config_done_port(std::string(top_tb_config_done_port_name), 1); + + BasicPort op_clock_port(std::string(top_tb_op_clock_port_name), 1); + BasicPort op_clock_register_port(std::string(std::string(top_tb_op_clock_port_name) + std::string(top_tb_clock_reg_postfix)), 1); + + BasicPort prog_clock_port(std::string(top_tb_prog_clock_port_name), 1); + BasicPort prog_clock_register_port(std::string(std::string(top_tb_prog_clock_port_name) + std::string(top_tb_clock_reg_postfix)), 1); + + BasicPort prog_reset_port(std::string(top_tb_prog_reset_port_name), 1); + BasicPort prog_set_port(std::string(top_tb_prog_set_port_name), 1); + + BasicPort reset_port(std::string(top_tb_reset_port_name), 1); + BasicPort set_port(std::string(top_tb_set_port_name), 1); + + /* Generate stimuli waveform for configuration done signals */ + print_verilog_comment(fp, "----- Begin configuration done signal generation -----"); + print_verilog_pulse_stimuli(fp, config_done_port, + 0, /* Initial value */ + num_config_clock_cycles * prog_clock_period / timescale, 0); + print_verilog_comment(fp, "----- End configuration done signal generation -----"); + fp << std::endl; + + /* Generate stimuli waveform for programming clock signals */ + print_verilog_comment(fp, "----- Begin raw programming clock signal generation -----"); + print_verilog_clock_stimuli(fp, prog_clock_register_port, + 0, /* Initial value */ + 0.5 * prog_clock_period / timescale, + std::string()); + print_verilog_comment(fp, "----- End raw programming clock signal generation -----"); + fp << std::endl; + + /* Programming clock should be only enabled during programming phase. + * When configuration is done (config_done is enabled), programming clock should be always zero. + */ + print_verilog_comment(fp, std::string("----- Actual programming clock is triggered only when " + config_done_port.get_name() + " and " + prog_reset_port.get_name() + " are disabled -----")); + fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port); + fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_register_port); + fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port) << ")"; + fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_reset_port) << ")"; + fp << ";" << std::endl; + + fp << std::endl; + + /* Generate stimuli waveform for operating clock signals */ + print_verilog_comment(fp, "----- Begin raw operating clock signal generation -----"); + print_verilog_clock_stimuli(fp, op_clock_register_port, + 0, /* Initial value */ + 0.5 * op_clock_period / timescale, + std::string("~" + reset_port.get_name())); + print_verilog_comment(fp, "----- End raw operating clock signal generation -----"); + + /* Operation clock should be enabled after programming phase finishes. + * Before configuration is done (config_done is enabled), operation clock should be always zero. + */ + print_verilog_comment(fp, std::string("----- Actual operating clock is triggered only when " + config_done_port.get_name() + " is enabled -----")); + fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_port); + fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_register_port); + fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << ";" << std::endl; + + fp << std::endl; + + /* Reset signal for configuration circuit: + * only enable during the first clock cycle in programming phase + */ + print_verilog_comment(fp, "----- Begin programming reset signal generation -----"); + print_verilog_pulse_stimuli(fp, prog_reset_port, + 1, /* Initial value */ + prog_clock_period / timescale, 0); + print_verilog_comment(fp, "----- End programming reset signal generation -----"); + + fp << std::endl; + + /* Programming set signal for configuration circuit : always disabled */ + print_verilog_comment(fp, "----- Begin programming set signal generation: always disabled -----"); + print_verilog_pulse_stimuli(fp, prog_set_port, + 0, /* Initial value */ + prog_clock_period / timescale, 0); + print_verilog_comment(fp, "----- End programming set signal generation: always disabled -----"); + + fp << std::endl; + + /* Operating reset signals: only enabled during the first clock cycle in operation phase */ + std::vector reset_pulse_widths; + reset_pulse_widths.push_back(op_clock_period / timescale); + reset_pulse_widths.push_back(2 * op_clock_period / timescale); + + std::vector reset_flip_values; + reset_flip_values.push_back(1); + reset_flip_values.push_back(0); + + print_verilog_comment(fp, "----- Begin operating reset signal generation -----"); + print_verilog_comment(fp, "----- Reset signal is enabled until the first clock cycle in operation phase -----"); + print_verilog_pulse_stimuli(fp, reset_port, + 1, + reset_pulse_widths, + reset_flip_values, + config_done_port.get_name()); + print_verilog_comment(fp, "----- End operating reset signal generation -----"); + + /* Operating set signal for configuration circuit : always disabled */ + print_verilog_comment(fp, "----- Begin operating set signal generation: always disabled -----"); + print_verilog_pulse_stimuli(fp, set_port, + 0, /* Initial value */ + op_clock_period / timescale, 0); + print_verilog_comment(fp, "----- End operating set signal generation: always disabled -----"); + + fp << std::endl; +} + +/******************************************************************** + * Print stimulus for a FPGA fabric with a configuration chain protocol + * where configuration bits are programming in serial (one by one) + * Task list: + * 1. For clock signal, we should create voltage waveforms for two types of clock signals: + * a. operation clock + * b. programming clock + * 2. For Set/Reset, we reset the chip after programming phase ends + * and before operation phase starts + * 3. For input/output clb nets (mapped to I/O grids), + * we should create voltage waveforms only after programming phase + *******************************************************************/ +static +void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp, + const BitstreamManager& bitstream_manager, + const std::vector& fabric_bitstream) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Initial value should be the first configuration bits + * In the rest of programming cycles, + * configuration bits are fed at the falling edge of programming clock. + * We do not care the value of scan_chain head during the first programming cycle + * It is reset anyway + */ + BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1); + std::vector initial_values(config_chain_head_port.get_width(), 0); + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + print_verilog_comment(fp, "----- Configuration chain default input -----"); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values); + fp << ";"; + + fp << std::endl; + + /* Attention: the configuration chain protcol requires the last configuration bit is fed first + * We will visit the fabric bitstream in a reverse way + */ + std::vector cc_bitstream = fabric_bitstream; + std::reverse(cc_bitstream.begin(), cc_bitstream.end()); + for (const ConfigBitId& bit_id : cc_bitstream) { + fp << "\t\t" << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME); + fp << "(1'b" << (size_t)bitstream_manager.bit_value(bit_id) << ");" << std::endl; + } + + /* Raise the flag of configuration done when bitstream loading is complete */ + BasicPort prog_clock_port(std::string(top_tb_prog_clock_port_name), 1); + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + + BasicPort config_done_port(std::string(top_tb_config_done_port_name), 1); + fp << "\t\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << " <= "; + std::vector config_done_enable_values(config_done_port.get_width(), 1); + fp << generate_verilog_constant_values(config_done_enable_values); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + +/******************************************************************** + * Generate the stimuli for the top-level testbench + * The simulation consists of two phases: configuration phase and operation phase + * Configuration bits are loaded serially. + * This is actually what we do for a physical FPGA + *******************************************************************/ +static +void print_verilog_top_testbench_bitstream(std::fstream& fp, + const e_sram_orgz& sram_orgz_type, + const BitstreamManager& bitstream_manager, + const std::vector& fabric_bitstream) { + /* Branch on the type of configuration protocol */ + switch (sram_orgz_type) { + case SPICE_SRAM_STANDALONE: + /* TODO */ + break; + case SPICE_SRAM_SCAN_CHAIN: + print_verilog_top_testbench_configuration_chain_bitstream(fp, bitstream_manager, fabric_bitstream); + break; + case SPICE_SRAM_MEMORY_BANK: + /* TODO */ + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d]) Invalid SRAM organization type!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/******************************************************************** + * The top-level function to generate a testbench, in order to verify: + * 1. Configuration phase of the FPGA fabric, where the bitstream is + * loaded to the configuration protocol of the FPGA fabric + * 2. Operating phase of the FPGA fabric, where input stimuli are + * fed to the I/Os of the FPGA fabric + * +----------+ + * | FPGA | +------------+ + * +----->| Fabric |------>| | + * | | | | | + * | +----------+ | | + * | | Output | + * random_input_vectors -----+ | Vector |---->Functional correct? + * | | Comparator | + * | +-----------+ | | + * | | Input | | | + * +----->| Benchmark |----->| | + * +-----------+ +------------+ + * + *******************************************************************/ +void print_verilog_top_testbench(const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const std::vector& fabric_bitstream, + const e_sram_orgz& sram_orgz_type, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const std::string& circuit_name, + const std::string& verilog_fname, + const std::string& verilog_dir, + const t_spice_params& simulation_parameters) { + vpr_printf(TIO_MESSAGE_INFO, + "Writing Autocheck Testbench for FPGA Top-level Verilog netlist for %s...", + circuit_name.c_str()); + + /* Start time count */ + clock_t t_start = clock(); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_handler(fp); + + /* Generate a brief description on the Verilog file*/ + std::string title = std::string("FPGA Verilog Testbench for Top-level netlist of Design: ") + circuit_name; + print_verilog_file_header(fp, title); + + /* Print preprocessing flags and external netlists */ + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* 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)); + + /* Preparation: find all the clock ports */ + std::vector clock_port_names = find_benchmark_clock_port_name(L_logical_blocks); + + /* Start of testbench */ + print_verilog_top_testbench_ports(fp, module_manager, top_module, + L_logical_blocks, clock_port_names, + sram_orgz_type, circuit_name); + + /* Find the clock period */ + float prog_clock_period = (1./simulation_parameters.stimulate_params.prog_clock_freq); + float op_clock_period = (1./simulation_parameters.stimulate_params.op_clock_freq); + /* Estimate the number of configuration clock cycles + * by traversing the linked-list and count the number of SRAM=1 or BL=1&WL=1 in it. + * We plus 1 additional config clock cycle here because we need to reset everything during the first clock cycle + */ + size_t num_config_clock_cycles = 1 + fabric_bitstream.size(); + + /* Generate stimuli for general control signals */ + print_verilog_top_testbench_generic_stimulus(fp, + num_config_clock_cycles, + prog_clock_period, + op_clock_period, + verilog_sim_timescale); + + /* Generate stimuli for global ports or connect them to existed signals */ + print_verilog_top_testbench_global_ports_stimuli(fp, + module_manager, top_module, + circuit_lib, global_ports); + + /* Instanciate FPGA top-level module */ + print_verilog_testbench_fpga_instance(fp, module_manager, top_module, + std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME)); + + /* Connect I/Os to benchmark I/Os or constant driver */ + print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module, + L_logical_blocks, device_size, L_grids, + L_blocks, + std::string(), + std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), + (size_t)verilog_default_signal_init_value); + + /* Instanciate input benchmark */ + print_verilog_top_testbench_benchmark_instance(fp, + circuit_name, + L_logical_blocks); + + /* Print tasks used for loading bitstreams */ + print_verilog_top_testbench_load_bitstream_task(fp, sram_orgz_type); + + /* load bitstream to FPGA fabric in a configuration phase */ + print_verilog_top_testbench_bitstream(fp, sram_orgz_type, + bitstream_manager, fabric_bitstream); + + /* Add stimuli for reset, set, clock and iopad signals */ + print_verilog_testbench_random_stimuli(fp, L_logical_blocks, + std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), + BasicPort(std::string(top_tb_op_clock_port_name), 1)); + + /* Add output autocheck */ + print_verilog_testbench_check(fp, + std::string(autochecked_simulation_flag), + std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), + std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), + std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), + std::string(TOP_TESTBENCH_ERROR_COUNTER), + L_logical_blocks, clock_port_names, std::string(top_tb_op_clock_port_name)); + + /* Find simulation time */ + float simulation_time = find_simulation_time_period(verilog_sim_timescale, + num_config_clock_cycles, + 1./simulation_parameters.stimulate_params.prog_clock_freq, + simulation_parameters.meas_params.sim_num_clock_cycle, + 1./simulation_parameters.stimulate_params.op_clock_freq); + + + /* Add Icarus requirement */ + print_verilog_timeout_and_vcd(fp, + std::string(icarus_simulator_flag), + std::string(circuit_name + std::string(modelsim_autocheck_testbench_module_postfix)), + std::string(circuit_name + std::string("_formal.vcd")), + std::string(TOP_TESTBENCH_SIM_START_PORT_NAME), + std::string(TOP_TESTBENCH_ERROR_COUNTER), + (int)simulation_time); + + + /* Testbench ends*/ + print_verilog_module_end(fp, std::string(circuit_name) + std::string(modelsim_autocheck_testbench_module_postfix)); + + /* Close the file stream */ + 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); +} diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.h index caf107479..ab4a7f955 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_top_testbench.h @@ -1,3 +1,26 @@ +#ifndef VERILOG_TOP_TESTBENCH +#define VERILOG_TOP_TESTBENCH + +#include +#include +#include "module_manager.h" +#include "bitstream_manager.h" +#include "circuit_library.h" + +void print_verilog_top_testbench(const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const std::vector& fabric_bitstream, + const e_sram_orgz& sram_orgz_type, + const CircuitLibrary& circuit_lib, + const std::vector& global_ports, + const std::vector& L_logical_blocks, + const vtr::Point& device_size, + const std::vector>& L_grids, + const std::vector& L_blocks, + const std::string& circuit_name, + const std::string& verilog_fname, + const std::string& verilog_dir, + const t_spice_params& simulation_parameters); void dump_verilog_top_testbench_global_ports(FILE* fp, t_llist* head, enum e_dump_verilog_port_type dump_port_type); @@ -15,7 +38,7 @@ void dump_verilog_top_testbench_stimuli(t_sram_orgz_info* cur_sram_orgz_info, void dump_verilog_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_spice verilog); @@ -23,3 +46,5 @@ void dump_verilog_input_blif_testbench(char* circuit_name, char* top_netlist_name, char* verilog_dir_path, t_spice verilog); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c index 0bdad0ee8..7fe8eedbe 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c @@ -164,145 +164,6 @@ void dump_verilog_file_header(FILE* fp, return; } -/* Dump preproc */ -void dump_verilog_preproc(FILE* fp, - t_syn_verilog_opts fpga_verilog_opts, - enum e_verilog_tb_type verilog_tb_type) { - - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s, LINE[%d]) FileHandle is NULL!\n",__FILE__,__LINE__); - exit(1); - } - - /* To enable timing */ - if (TRUE == fpga_verilog_opts.include_timing) { - fprintf(fp, "`define %s 1\n", verilog_timing_preproc_flag); - fprintf(fp, "\n"); - } - - /* To enable timing */ - if (TRUE == fpga_verilog_opts.include_signal_init) { - fprintf(fp, "`define %s 1\n", verilog_signal_init_preproc_flag); - fprintf(fp, "\n"); - } - - /* To enable formal verfication flag */ - if (TRUE == fpga_verilog_opts.print_formal_verification_top_netlist) { - fprintf(fp, "`define %s 1\n", - verilog_formal_verification_preproc_flag); // the flag to enable formal verification during compilation - fprintf(fp, "\n"); - } - - /* To enable functional verfication with Icarus */ - if (TRUE == fpga_verilog_opts.include_icarus_simulator) { - fprintf(fp, "`define %s 1\n", - icarus_simulator_flag); // the flag to enable formal verification during compilation - fprintf(fp, "\n"); - } - - return; -} - -void dump_simulation_preproc(FILE* fp, - t_syn_verilog_opts fpga_verilog_opts, - enum e_verilog_tb_type verilog_tb_type) { - - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s, LINE[%d]) FileHandle is NULL!\n",__FILE__,__LINE__); - exit(1); - } - - /* To enable manualy checked simulation */ - if (TRUE == fpga_verilog_opts.print_top_testbench) { - fprintf(fp, "`define %s 1\n", initial_simulation_flag); - fprintf(fp, "\n"); - } - - /* To enable auto-checked simulation */ - if (TRUE == fpga_verilog_opts.print_autocheck_top_testbench) { - fprintf(fp, "`define %s 1\n", autochecked_simulation_flag); - fprintf(fp, "\n"); - } - - /* To enable pre-configured FPGA simulation */ - if (TRUE == fpga_verilog_opts.print_formal_verification_top_netlist) { - fprintf(fp, "`define %s 1\n", formal_simulation_flag); - fprintf(fp, "\n"); - } - - return; -} - -void dump_verilog_simulation_preproc(char* subckt_dir, - t_syn_verilog_opts fpga_verilog_opts) { - /* Create a file handler */ - FILE* fp = NULL; - char* file_description = NULL; - char* fname = NULL; - - fname = my_strcat(subckt_dir, - defines_verilog_simulation_file_name); - - /* Create a file*/ - fp = fopen(fname, "w"); - - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR, - "(FILE:%s,LINE[%d])Failure in create Verilog netlist %s", - __FILE__, __LINE__, fname); - exit(1); - } - - /* Generate the descriptions*/ - file_description = "Simulation Flags"; - dump_verilog_file_header(fp, file_description); - - /* Dump the defines preproc flags*/ - dump_simulation_preproc(fp, fpga_verilog_opts, VERILOG_TB_TOP); - - fclose(fp); - - /* Free */ - my_free(fname); - - return; -} - -void dump_verilog_defines_preproc(char* subckt_dir, - t_syn_verilog_opts fpga_verilog_opts) { - /* Create a file handler */ - FILE* fp = NULL; - char* file_description = NULL; - char* fname = NULL; - - fname = my_strcat(subckt_dir, - defines_verilog_file_name); - - /* Create a file*/ - fp = fopen(fname, "w"); - - if (NULL == fp) { - vpr_printf(TIO_MESSAGE_ERROR, - "(FILE:%s,LINE[%d])Failure in create Verilog netlist %s", - __FILE__, __LINE__, fname); - exit(1); - } - - /* Generate the descriptions*/ - file_description = "Preproc Flags"; - dump_verilog_file_header(fp, file_description); - - /* Dump the defines preproc flags*/ - dump_verilog_preproc(fp, fpga_verilog_opts, VERILOG_TB_TOP); - - fclose(fp); - - /* Free */ - my_free(fname); - - return; -} - void verilog_include_defines_preproc_file(FILE* fp, char* verilog_dir) { char* temp_include_file_path = NULL; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.h index 1fac4fff5..30a4d8863 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.h @@ -15,19 +15,6 @@ void dump_verilog_preproc(FILE* fp, t_syn_verilog_opts fpga_verilog_opts, enum e_verilog_tb_type verilog_tb_type); -void dump_simulation_preproc(FILE* fp, - t_syn_verilog_opts fpga_verilog_opts, - enum e_verilog_tb_type verilog_tb_type); - -void dump_verilog_simulation_preproc(char* subckt_dir, - t_syn_verilog_opts fpga_verilog_opts); - -void dump_verilog_defines_preproc(char* subckt_dir, - t_syn_verilog_opts fpga_verilog_opts); - -void verilog_include_simulation_defines_file(FILE* fp, - char* formatted_verilog_dir); - void verilog_include_defines_preproc_file(FILE* fp, char* formatted_verilog_dir); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.cpp index b8b9443bc..1efe6d6cb 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.cpp @@ -90,92 +90,10 @@ void print_verilog_wire_module(ModuleManager& module_manager, } /******************************************************************** - * Print a Verilog 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 | - * +-------------- - * + * Top-level function to print wire modules *******************************************************************/ -static -void print_verilog_routing_wire_module(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - std::fstream& fp, - const CircuitModelId& wire_model, - const std::string& wire_subckt_name) { - /* Ensure a valid file handler*/ - check_file_handler(fp); - - /* Find the input port, output port*/ - std::vector input_ports = circuit_lib.model_ports_by_type(wire_model, SPICE_MODEL_PORT_INPUT, true); - std::vector 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 = module_manager.find_module(wire_subckt_name); - VTR_ASSERT(true == module_manager.valid_module_id(wire_module)); - - /* 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])); - ModulePortId module_mid_output_port_id = module_manager.find_module_port(wire_module, module_mid_output_port.get_name()); - VTR_ASSERT(ModulePortId::INVALID() != module_mid_output_port_id); - - /* dump module definition + ports */ - print_verilog_module_declaration(fp, module_manager, wire_module); - /* Finish dumping ports */ - - /* Print the internal logic of Verilog module */ - /* Find the input port of the module */ - ModulePortId module_input_port_id = module_manager.find_module_port(wire_module, circuit_lib.port_lib_name(input_ports[0])); - VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id); - BasicPort module_input_port = module_manager.module_port(wire_module, module_input_port_id); - - /* Find the output port of the module */ - ModulePortId module_output_port_id = module_manager.find_module_port(wire_module, circuit_lib.port_lib_name(output_ports[0])); - VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id); - BasicPort module_output_port = module_manager.module_port(wire_module, module_output_port_id); - - /* Print wire declaration for the inputs and outputs */ - fp << generate_verilog_port(VERILOG_PORT_WIRE, module_input_port) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_WIRE, module_output_port) << ";" << std::endl; - fp << generate_verilog_port(VERILOG_PORT_WIRE, module_mid_output_port) << ";" << std::endl; - - /* Direct shortcut */ - print_verilog_wire_connection(fp, module_output_port, module_input_port, false); - print_verilog_wire_connection(fp, module_mid_output_port, module_input_port, false); - - /* Print timing info */ - print_verilog_submodule_timing(fp, circuit_lib, wire_model); - - /* Put an end to the Verilog module */ - print_verilog_module_end(fp, circuit_lib.model_name(wire_model)); - - /* Add an empty line as a splitter */ - fp << std::endl; -} - void print_verilog_submodule_wires(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - std::vector routing_segments, const std::string& verilog_dir, const std::string& submodule_dir) { std::string verilog_fname(submodule_dir + wires_verilog_file_name); @@ -205,26 +123,6 @@ void print_verilog_submodule_wires(ModuleManager& module_manager, print_verilog_wire_module(module_manager, circuit_lib, fp, model); } print_verilog_comment(fp, std::string("----- END Verilog modules for regular wires -----")); - - /* Create wire models for routing segments*/ - print_verilog_comment(fp, std::string("----- BEGIN Verilog modules for routing track 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)); - /* Bypass user-defined circuit models */ - if (!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 */ - print_verilog_routing_wire_module(module_manager, circuit_lib, fp, seg.circuit_model, segment_wire_subckt_name); - } - print_verilog_comment(fp, std::string("----- END Verilog modules for routing track wires -----")); /* Close the file stream */ fp.close(); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.h index 1d4fc7c71..e45cf483a 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_wire.h @@ -16,7 +16,6 @@ void print_verilog_submodule_wires(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - std::vector routing_segments, const std::string& verilog_dir, const std::string& submodule_dir); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp index 69f774592..41766444b 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.cpp @@ -6,6 +6,8 @@ #include #include #include +#include + #include "vtr_assert.h" /* Device-level header files */ @@ -40,7 +42,7 @@ void print_verilog_file_header(std::fstream& fp, fp << "//-------------------------------------------" << std::endl; fp << "//----- Time scale -----" << std::endl; fp << "`timescale 1ns / 1ps" << std::endl; - fp << "\n"; + fp << std::endl; } /******************************************************************** @@ -50,9 +52,18 @@ void print_verilog_include_netlist(std::fstream& fp, const std::string& netlist_name) { check_file_handler(fp); - fp << "//------ Include external netlist: " << netlist_name << " -----" << std::endl; fp << "`include \"" << netlist_name << "\"" << std::endl; - fp << "//------ End include external netlist: " << netlist_name << " -----" << std::endl; +} + +/******************************************************************** + * Print Verilog codes to define a preprocessing flag + *******************************************************************/ +void print_verilog_define_flag(std::fstream& fp, + const std::string& flag_name, + const int& flag_value) { + check_file_handler(fp); + + fp << "`define " << flag_name << " " << flag_value << std::endl; } /************************************************ @@ -1210,3 +1221,149 @@ void print_verilog_formal_verification_mux_sram_ports_wiring(std::fstream& fp, print_verilog_wire_connection(fp, mux_sram_output, formal_verification_port, false); } +/******************************************************************** + * Print stimuli for a pulse generation + * + * |<--- pulse width --->| + * +------ flip_value + * | + * initial_value ----------------------+ + * + *******************************************************************/ +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const size_t& flip_value) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t"; + std::vector initial_values(port.get_width(), initial_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + /* if flip_value is the same as initial value, we do not need to flip the signal ! */ + if (flip_value != initial_value) { + fp << "\t" << "#" << std::setprecision(10) << pulse_width; + std::vector port_flip_values(port.get_width(), flip_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, port_flip_values); + fp << ";" << std::endl; + } + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print stimuli for a pulse generation + * This function supports multiple signal switching under different pulse width + * + * |<-- wait condition -->| + * |<--- pulse width --->| + * +------ flip_values + * | + * initial_value ------- ... --------------------------------+ + * + *******************************************************************/ +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const std::vector& pulse_widths, + const std::vector& flip_values, + const std::string& wait_condition) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t"; + std::vector initial_values(port.get_width(), initial_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + /* Set a wait condition if specified */ + if (false == wait_condition.empty()) { + fp << "\twait(" << wait_condition << ")" << std::endl; + } + + /* Number of flip conditions and values should match */ + VTR_ASSERT(flip_values.size() == pulse_widths.size()); + for (size_t ipulse = 0; ipulse < pulse_widths.size(); ++ipulse) { + fp << "\t" << "#" << std::setprecision(10) << pulse_widths[ipulse]; + std::vector port_flip_value(port.get_width(), flip_values[ipulse]); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, port_flip_value); + fp << ";" << std::endl; + } + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print stimuli for a clock signal + * This function can support if the clock signal should wait for a period + * of time and then start + * pulse width + * |<----->| + * +-------+ +-------+ + * | | | | + * initial_value --- ... ---+ +-------+ +------ ... + * |<--wait_condition-->| + * + *******************************************************************/ +void print_verilog_clock_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const std::string& wait_condition) { + /* Validate the file stream */ + check_file_handler(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + + std::vector initial_values(port.get_width(), initial_value); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + fp << "always"; + + /* Set a wait condition if specified */ + if (true == wait_condition.empty()) { + fp << std::endl; + } else { + fp << " wait(" << wait_condition << ")" << std::endl; + } + + fp << "\tbegin" << std::endl; + fp << "\t\t" << "#" << std::setprecision(10) << pulse_width; + + fp << "\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, port); + fp << " = "; + fp << "~"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, port); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.h index d09e950de..977ae037b 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_writer_utils.h @@ -6,6 +6,8 @@ #ifndef VERILOG_WRITER_UTILS_H #define VERILOG_WRITER_UTILS_H +#include +#include #include #include "verilog_global.h" #include "device_port.h" @@ -24,6 +26,10 @@ void print_verilog_file_header(std::fstream& fp, void print_verilog_include_netlist(std::fstream& fp, const std::string& netlist_name); +void print_verilog_define_flag(std::fstream& fp, + const std::string& flag_name, + const int& flag_value); + void print_verilog_include_defines_preproc_file(std::fstream& fp, const std::string& verilog_dir); @@ -143,4 +149,23 @@ void print_verilog_formal_verification_mux_sram_ports_wiring(std::fstream& fp, const size_t& num_conf_bits, const BasicPort& fm_config_bus); +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const size_t& flip_value); + +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const std::vector& pulse_widths, + const std::vector& flip_values, + const std::string& wait_condition); + +void print_verilog_clock_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const std::string& wait_condition); + #endif diff --git a/vpr7_x2p/vpr/SRC/route/rr_graph.c b/vpr7_x2p/vpr/SRC/route/rr_graph.c index 1c9c01e18..c637473a4 100755 --- a/vpr7_x2p/vpr/SRC/route/rr_graph.c +++ b/vpr7_x2p/vpr/SRC/route/rr_graph.c @@ -1920,7 +1920,7 @@ static void check_all_tracks_reach_pins(t_type_ptr type, for (itrack = 0; itrack < nodes_per_chan; itrack++) { if (num_conns_to_track[itrack] <= 0) { - vpr_printf(TIO_MESSAGE_ERROR, "check_all_tracks_reach_pins: Track %d does not connect to any CLB %ss.\n", + vpr_printf(TIO_MESSAGE_WARNING, "check_all_tracks_reach_pins: Track %d does not connect to any CLB %ss.\n", itrack, (ipin_or_opin == DRIVER ? "OPIN" : "IPIN")); } }