Merge remote-tracking branch 'lnis_origin/master' into ganesh_dev

This commit is contained in:
ganeshgore 2020-09-23 14:03:25 -06:00
commit e31589f9b6
249 changed files with 17360 additions and 1690 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Enviornment (please complete the following information):**
- OS: [e.g. CentOs, Ubuntu]
- Compiler [e.g. gcc, clang]
- Version [e.g. Github commit id]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -15,7 +15,7 @@ cache:
# Supported Operating systems # Supported Operating systems
dist: bionic dist: bionic
compiler: g++-8 #compiler: g++-8
addons: addons:
apt: apt:
sources: sources:
@ -33,8 +33,6 @@ addons:
- doxygen - doxygen
- flex - flex
- fontconfig - fontconfig
- g++-8
- gcc-8
- gdb - gdb
- git - git
- gperf - gperf
@ -56,6 +54,19 @@ addons:
- zip - zip
- qt5-default - qt5-default
- clang-format-7 - clang-format-7
# Add all the supported compilers
- g++-5
- gcc-5
- g++-6
- gcc-6
- g++-7
- gcc-7
- g++-8
- gcc-8
- g++-9
- gcc-9
- clang-6.0
- clang-8
#- os: osx #- os: osx
# osx_image: xcode10.2 # we target latest MacOS Mojave # osx_image: xcode10.2 # we target latest MacOS Mojave
# sudo: true # sudo: true
@ -76,6 +87,10 @@ addons:
# - libxml++ # - libxml++
# - qt5 # - qt5
# Use gcc-8 as default compiler
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
before_script: before_script:
- source .travis/common.sh - source .travis/common.sh
- source .travis/install.sh - source .travis/install.sh
@ -96,7 +111,74 @@ jobs:
name: "FPGA-Verilog regression tests" name: "FPGA-Verilog regression tests"
script: script:
- source .travis/build.sh - source .travis/build.sh
- source .travis/verilog_reg_test.sh - source .travis/fpga_verilog_reg_test.sh
- stage: Test
name: "FPGA-Bitstream regression tests"
script:
- source .travis/build.sh
- source .travis/fpga_bitstream_reg_test.sh
- stage: Test
name: "FPGA-SDC regression tests"
script:
- source .travis/build.sh
- source .travis/fpga_sdc_reg_test.sh
- stage: Test
name: "FPGA-SPICE regression tests"
script:
- source .travis/build.sh
- source .travis/fpga_spice_reg_test.sh
- stage: Test
name: "Build Compatibility: GCC-5 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: GCC-6 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=gcc-6 && CXX=g++-6"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: GCC-7 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: GCC-8 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: GCC-9 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=gcc-9 && CXX=g++-9"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: Clang-6 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0"
script:
- source .travis/build.sh
- stage: Test
name: "Build Compatibility: Clang-8 (Ubuntu Bionic 18.04)"
env:
- MATRIX_EVAL="CC=clang-8 && CXX=clang++-8"
script:
- source .travis/build.sh
#after_failure: #after_failure:
# - .travis/after_failure.sh # - .travis/after_failure.sh

View File

@ -11,40 +11,45 @@ cd ${TRAVIS_BUILD_DIR}
echo -e "Basic regression tests"; echo -e "Basic regression tests";
echo -e "Testing configuration chain of a K4N4 FPGA"; echo -e "Testing configuration chain of a K4N4 FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/configuration_chain --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_configuration_chain --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_chain --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/configuration_chain --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/configuration_chain --debug --show_thread_logs
echo -e "Testing fram-based configuration protocol of a K4N4 FPGA"; echo -e "Testing fram-based configuration protocol of a K4N4 FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/configuration_frame --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_frame --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_configuration_frame --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_frame --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/configuration_frame --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/configuration_frame --debug --show_thread_logs
echo -e "Testing memory bank configuration protocol of a K4N4 FPGA"; echo -e "Testing memory bank configuration protocol of a K4N4 FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/memory_bank --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/memory_bank --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_memory_bank --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_memory_bank --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/memory_bank --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/memory_bank --debug --show_thread_logs
echo -e "Testing standalone (flatten memory) configuration protocol of a K4N4 FPGA"; echo -e "Testing standalone (flatten memory) configuration protocol of a K4N4 FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/flatten_memory --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/flatten_memory --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/flatten_memory --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/flatten_memory --debug --show_thread_logs
echo -e "Testing fabric Verilog generation only"; echo -e "Testing fabric Verilog generation only";
python3 openfpga_flow/scripts/run_fpga_task.py generate_fabric --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_fabric --debug --show_thread_logs
echo -e "Testing Verilog testbench generation only"; echo -e "Testing Verilog testbench generation only";
python3 openfpga_flow/scripts/run_fpga_task.py generate_testbench --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_testbench --debug --show_thread_logs
echo -e "Testing bitstream generation only";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream --debug --show_thread_logs
echo -e "Testing user-defined simulation settings: clock frequency and number of cycles"; echo -e "Testing user-defined simulation settings: clock frequency and number of cycles";
python3 openfpga_flow/scripts/run_fpga_task.py fixed_simulation_settings --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/fixed_simulation_settings --debug --show_thread_logs
echo -e "Testing SDC generation with time units"; echo -e "Testing K4 series FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py sdc_time_unit --debug --show_thread_logs echo -e "Testing K4N4 with facturable LUTs";
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_frac_lut --debug --show_thread_logs
echo -e "Testing FPGA-SPICE with netlist generation"; echo -e "Testing K4N4 with hard adders";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_spice/generate_spice --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_adder --debug --show_thread_logs
echo -e "Testing K4N4 without local routing architecture";
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_no_local_routing --debug --show_thread_logs
echo -e "Testing K4N4 with block RAM";
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_bram --debug --show_thread_logs
echo -e "Testing K4N4 with multiple lengths of routing segments";
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_L124 --debug --show_thread_logs
echo -e "Testing K4N4 with 32-bit fracturable multiplier";
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_frac_mult --debug --show_thread_logs
end_section "OpenFPGA.TaskTun" end_section "OpenFPGA.TaskTun"

View File

@ -39,8 +39,9 @@ if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / -allowUntrusted sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / -allowUntrusted
else else
# For linux, we use g++-8 and gcc-8 as default compilers # For linux, we use g++-8 and gcc-8 as default compilers
export CC=gcc-8 eval "${MATRIX_EVAL}"
export CXX=g++-8 export "CC=$CC"
export "CXX=$CXX"
fi fi

View File

@ -0,0 +1,26 @@
#!/bin/bash
set -e
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
cd ${TRAVIS_BUILD_DIR}
###############################################
# OpenFPGA Shell with VPR8
##############################################
echo -e "FPGA-Bitstream regression tests";
echo -e "Testing bitstream generation for an auto-sized device";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_auto --debug --show_thread_logs
echo -e "Testing bitstream generation for an 48x48 FPGA device";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_48x48 --debug --show_thread_logs
echo -e "Testing bitstream generation for an 96x96 FPGA device";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_96x96 --debug --show_thread_logs
echo -e "Testing loading architecture bitstream from an external file";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/load_external_architecture_bitstream --debug --show_thread_logs
end_section "OpenFPGA.TaskTun"

16
.travis/fpga_sdc_reg_test.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
cd ${TRAVIS_BUILD_DIR}
###############################################
# OpenFPGA Shell with VPR8
##############################################
echo -e "FPGA-SDC regression tests";
echo -e "Testing SDC generation with time units";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_sdc/sdc_time_unit --debug --show_thread_logs
end_section "OpenFPGA.TaskTun"

16
.travis/fpga_spice_reg_test.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
cd ${TRAVIS_BUILD_DIR}
###############################################
# OpenFPGA Shell with VPR8
##############################################
echo -e "FPGA-SPICE regression tests";
echo -e "Testing FPGA-SPICE with netlist generation";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_spice/generate_spice --debug --show_thread_logs
end_section "OpenFPGA.TaskTun"

109
.travis/fpga_verilog_reg_test.sh Executable file
View File

@ -0,0 +1,109 @@
#!/bin/bash
set -e
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
cd ${TRAVIS_BUILD_DIR}
###############################################
# OpenFPGA Shell with VPR8
##############################################
echo -e "FPGA-Verilog Feature Tests";
echo -e "Testing Verilog generation for LUTs: a single mode LUT6 FPGA using micro benchmarks";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/single_mode --debug --show_thread_logs
echo -e "Testing Verilog generation for LUTs: simple fracturable LUT4 ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut4 --debug --show_thread_logs
echo -e "Testing Verilog generation for LUTs: simple fracturable LUT6 ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut6 --debug --show_thread_logs
echo -e "Testing Verilog generation for LUTs: LUT6 with intermediate buffers";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/intermediate_buffer --debug --show_thread_logs
echo -e "Testing Verilog generation with VPR's untileable routing architecture ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/untileable --debug --show_thread_logs
echo -e "Testing Verilog generation with hard adder chain in CLBs ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/hard_adder --debug --show_thread_logs
echo -e "Testing Verilog generation with 16k block RAMs ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/bram/dpram16k --debug --show_thread_logs
echo -e "Testing Verilog generation with 16k block RAMs spanning two columns ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/bram/wide_dpram16k --debug --show_thread_logs
echo -e "Testing Verilog generation with different I/O capacities on each side of an FPGA ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/io/multi_io_capacity --debug --show_thread_logs
echo -e "Testing Verilog generation with I/Os only on left and right sides of an FPGA ";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/io/reduced_io --debug --show_thread_logs
echo -e "Testing Verilog generation with adder chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/adder_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with shift register chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/register_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with scan chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/scan_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers implemented by tree structure";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/tree_structure --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers implemented by standard cell MUX2";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/stdcell_mux2 --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers implemented by local encoders";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/local_encoder --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers without buffers";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/debuf_mux --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers with input buffers only";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/inbuf_only_mux --debug --show_thread_logs
echo -e "Testing Verilog generation with routing multiplexers with output buffers only";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/outbuf_only_mux --debug --show_thread_logs
echo -e "Testing Verilog generation with behavioral description";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/behavioral_verilog --debug --show_thread_logs
echo -e "Testing implicit Verilog generation";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/implicit_verilog --debug --show_thread_logs
echo -e "Testing Verilog generation with flatten routing modules";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/flatten_routing --debug --show_thread_logs
echo -e "Testing Verilog generation with duplicated grid output pins";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/duplicated_grid_pin --debug --show_thread_logs
echo -e "Testing Verilog generation with spy output pads";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/spypad --debug --show_thread_logs
echo -e "Testing Secured FPGA fabrics";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/generate_vanilla_key --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/generate_random_key --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/load_external_key --debug --show_thread_logs
echo -e "Testing Power-gating designs";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/power_gated_design/power_gated_inverter --show_thread_logs --debug
echo -e "Testing Depopulated crossbar in local routing";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar --debug --show_thread_logs
echo -e "Testing Fully connected output crossbar in local routing";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fully_connected_output_crossbar --debug --show_thread_logs
echo -e "Testing through channels in tileable routing";
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_narrow_tile --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_wide_tile --debug --show_thread_logs
# Verify MCNC big20 benchmark suite with ModelSim
# Please make sure you have ModelSim installed in the environment
# Otherwise, it will fail
#python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mcnc_big20 --debug --show_thread_logs --maxthreads 20
#python3 openfpga_flow/scripts/run_modelsim.py mcnc_big20 --run_sim
end_section "OpenFPGA.TaskTun"

View File

@ -1,90 +0,0 @@
#!/bin/bash
set -e
start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}"
cd ${TRAVIS_BUILD_DIR}
###############################################
# OpenFPGA Shell with VPR8
##############################################
echo -e "OpenFPGA Feature Testing for Verilog-to-Verification";
echo -e "Testing Verilog generation for LUTs: a single mode LUT6 FPGA using micro benchmarks";
python3 openfpga_flow/scripts/run_fpga_task.py lut_design/single_mode --debug --show_thread_logs
echo -e "Testing Verilog generation for LUTs: simple fracturable LUT6 ";
python3 openfpga_flow/scripts/run_fpga_task.py lut_design/frac_lut --debug --show_thread_logs
echo -e "Testing Verilog generation for LUTs: LUT6 with intermediate buffers";
python3 openfpga_flow/scripts/run_fpga_task.py lut_design/intermediate_buffer --debug --show_thread_logs
echo -e "Testing Verilog generation with VPR's untileable routing architecture ";
python3 openfpga_flow/scripts/run_fpga_task.py untileable --debug --show_thread_logs
echo -e "Testing Verilog generation with hard adder chain in CLBs ";
python3 openfpga_flow/scripts/run_fpga_task.py hard_adder --debug --show_thread_logs
echo -e "Testing Verilog generation with 16k block RAMs ";
python3 openfpga_flow/scripts/run_fpga_task.py bram/dpram16k --debug --show_thread_logs
echo -e "Testing Verilog generation with 16k block RAMs spanning two columns ";
python3 openfpga_flow/scripts/run_fpga_task.py bram/wide_dpram16k --debug --show_thread_logs
echo -e "Testing Verilog generation with different I/O capacities on each side of an FPGA ";
python3 openfpga_flow/scripts/run_fpga_task.py io/multi_io_capacity --debug --show_thread_logs
echo -e "Testing Verilog generation with I/Os only on left and right sides of an FPGA ";
python3 openfpga_flow/scripts/run_fpga_task.py io/reduced_io --debug --show_thread_logs
echo -e "Testing Verilog generation with adder chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/adder_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with shift register chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/register_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with scan chain across an FPGA";
python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/scan_chain --debug --show_thread_logs
echo -e "Testing Verilog generation with routing mutliplexers implemented by tree structure";
python3 openfpga_flow/scripts/run_fpga_task.py mux_design/tree_structure --debug --show_thread_logs
echo -e "Testing Verilog generation with routing mutliplexers implemented by standard cell MUX2";
python3 openfpga_flow/scripts/run_fpga_task.py mux_design/stdcell_mux2 --debug --show_thread_logs
echo -e "Testing Verilog generation with routing mutliplexers implemented by local encoders";
python3 openfpga_flow/scripts/run_fpga_task.py mux_design/local_encoder --debug --show_thread_logs
echo -e "Testing Verilog generation with behavioral description";
python3 openfpga_flow/scripts/run_fpga_task.py behavioral_verilog --debug --show_thread_logs
echo -e "Testing implicit Verilog generation";
python3 openfpga_flow/scripts/run_fpga_task.py implicit_verilog --debug --show_thread_logs
echo -e "Testing Verilog generation with flatten routing modules";
python3 openfpga_flow/scripts/run_fpga_task.py flatten_routing --debug --show_thread_logs
echo -e "Testing Verilog generation with duplicated grid output pins";
python3 openfpga_flow/scripts/run_fpga_task.py duplicated_grid_pin --debug --show_thread_logs
echo -e "Testing Verilog generation with spy output pads";
python3 openfpga_flow/scripts/run_fpga_task.py spypad --debug --show_thread_logs
echo -e "Testing Secured FPGA fabrics";
python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/generate_vanilla_key --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/generate_random_key --debug --show_thread_logs
python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/load_external_key --debug --show_thread_logs
echo -e "Testing Power-gating designs";
python3 openfpga_flow/scripts/run_fpga_task.py power_gated_design/power_gated_inverter --show_thread_logs --debug
echo -e "Testing Depopulated crossbar in local routing";
python3 openfpga_flow/scripts/run_fpga_task.py depopulate_crossbar --debug --show_thread_logs
# Verify MCNC big20 benchmark suite with ModelSim
# Please make sure you have ModelSim installed in the environment
# Otherwise, it will fail
#python3 openfpga_flow/scripts/run_fpga_task.py mcnc_big20 --debug --show_thread_logs --maxthreads 20
#python3 openfpga_flow/scripts/run_modelsim.py mcnc_big20 --run_sim
end_section "OpenFPGA.TaskTun"

View File

@ -17,7 +17,7 @@ RUN mkdir -p /release /dev
RUN cd release && git clone --single-branch --branch master https://github.com/LNIS-Projects/OpenFPGA.git OpenFPGA RUN cd release && git clone --single-branch --branch master https://github.com/LNIS-Projects/OpenFPGA.git OpenFPGA
RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_NO_GRAPHICS=on && make RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_NO_GRAPHICS=on && make
RUN rm -rf /var/lib/apt/lists/* RUN rm -rf /var/lib/apt/lists/*

View File

@ -1,5 +1,5 @@
# Getting Started with OpenFPGA <img src="./docs/source/figures/OpenFPGA_logo.png" width="200" align="right"> # Getting Started with OpenFPGA <img src="./docs/source/figures/OpenFPGA_logo.png" width="200" align="right">
[![Build Status](https://travis-ci.org/LNIS-Projects/OpenFPGA.svg?branch=master)](https://travis-ci.org/LNIS-Projects/OpenFPGA) [![Build Status](https://travis-ci.com/LNIS-Projects/OpenFPGA.svg?branch=master)](https://travis-ci.com/LNIS-Projects/OpenFPGA)
[![Documentation Status](https://readthedocs.org/projects/openfpga/badge/?version=master)](https://openfpga.readthedocs.io/en/master/?badge=master) [![Documentation Status](https://readthedocs.org/projects/openfpga/badge/?version=master)](https://openfpga.readthedocs.io/en/master/?badge=master)
## Introduction ## Introduction

View File

@ -1,9 +0,0 @@
[TODO]: # (This document might be incomplete. It is important to keep it updated with the right names)
### Benchmarks README ###
#########
fpga_flow is a folder which contains benchmarks testing the performances of the tool on a known variety of benchmarks. In order to launch them, a script called run_fpga_spice_testbench_study.sh is called. This script is based on different other scripts which can be found in the script folder.
The different benchmarks can be found in the fpga_spice_bench.txt found in the benchmarks folder. By commenting them, the script will not read them and not run through them. This can be useful if you want to focus on one benchmark in particular for example.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 273 KiB

View File

@ -52,7 +52,7 @@ Layout
.. warning:: Do NOT enable ``through_channel`` if you are not using the tileable routing resource graph generator! .. warning:: Do NOT enable ``through_channel`` if you are not using the tileable routing resource graph generator!
.. warning:: Currently ``through_channel`` supports only a fixed routing channel width! .. warning:: You cannot use ``spread`` pin location for the ``height > 1`` or ``width >1`` tiles when using the tileable routing resource graph!!! Otherwise, it will cause undriven pins in your device!!!
A quick example to show tileable routing is enabled and through channels are disabled: A quick example to show tileable routing is enabled and through channels are disabled:

View File

@ -103,13 +103,16 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de
.. note:: OpenFPGA will infer the physical mode for a single-mode ``pb_type`` defined in VPR architecture .. note:: OpenFPGA will infer the physical mode for a single-mode ``pb_type`` defined in VPR architecture
.. option:: <pb_type name="<string>" physical_pb_type_name="<string>" circuit_model_name="<string>" .. option:: <pb_type name="<string>" physical_pb_type_name="<string>"
mode_bits="<int>" physical_pb_type_index_factor="<float>" physical_pb_type_index_offset="<int>"> circuit_model_name="<string>" mode_bits="<int>"
physical_pb_type_index_factor="<float>" physical_pb_type_index_offset="<int>">
Specify the physical implementation for a primitive ``pb_type`` in VPR architecture Specify the physical implementation for a primitive ``pb_type`` in VPR architecture
.. note:: This should be applied to primitive ``pb_type``, i.e., ``pb_type`` have no children. .. note:: This should be applied to primitive ``pb_type``, i.e., ``pb_type`` have no children.
.. note:: This definition should be placed directly under the XML node ``<pb_type_annotation>`` without any intermediate XML nodes!
- ``name="<string>"`` specifiy the full name of a ``pb_type`` in the hierarchy of VPR architecture. - ``name="<string>"`` specifiy the full name of a ``pb_type`` in the hierarchy of VPR architecture.
- ``physical_pb_type_name=<string>`` creates the link on ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid name of primitive ``pb_type`` in physical mode. - ``physical_pb_type_name=<string>`` creates the link on ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid name of primitive ``pb_type`` in physical mode.
@ -124,11 +127,14 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de
.. option:: <interconnect name="<string>" circuit_model_name="<string>"> .. option:: <interconnect name="<string>" circuit_model_name="<string>">
- ``name="<string>"`` specifiy the name of a ``interconnect`` in VPR architecture. Different from ``pb_type``, hierarchical name is not required here. - ``name="<string>"`` specify the name of a ``interconnect`` in VPR architecture. Different from ``pb_type``, hierarchical name is not required here.
- ``circuit_model_name="<string>"`` For the interconnection type direct, the type of the linked circuit model should be wire. For multiplexers, the type of linked circuit model should be ``mux``. For complete, the type of the linked circuit model can be either ``mux`` or ``wire``, depending on the case. - ``circuit_model_name="<string>"`` For the interconnection type direct, the type of the linked circuit model should be wire. For multiplexers, the type of linked circuit model should be ``mux``. For complete, the type of the linked circuit model can be either ``mux`` or ``wire``, depending on the case.
.. option:: <port name="<string>" physical_mode_port="<string>" physical_mode_pin_rotate_offset="<int>"/> .. note:: A ``<pb_type name="<string>">`` parent XML node is required for the interconnect-to-circuit bindings whose interconnects are defined under the ``pb_type`` in VPR architecture description.
.. option:: <port name="<string>" physical_mode_port="<string>"
physical_mode_pin_initial_offset="<int>" physical_mode_pin_rotate_offset="<int>"/>
Link a port of an operating ``pb_type`` to a port of a physical ``pb_type`` Link a port of an operating ``pb_type`` to a port of a physical ``pb_type``
@ -136,8 +142,39 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de
- ``physical_mode_pin="<string>" creates the link of ``port`` of ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid ``port`` name of leaf ``pb_type`` in physical mode and the port size should also match. - ``physical_mode_pin="<string>" creates the link of ``port`` of ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid ``port`` name of leaf ``pb_type`` in physical mode and the port size should also match.
.. note:: Users can define multiple ports. For example: ``physical_mode_pin="a[0:1] b[2:2]"``. When multiple ports are used, the ``physical_mode_pin_initial_offset`` and ``physical_mode_pin_rotate_offset`` should also be adapt. For example: ``physical_mode_pin_rotate_offset="1 0"``)
- ``physical_mode_pin_initial_offset="<int>"`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when part of port of operating mode is mapped to a port in physical ``pb_type``. When ``physical_mode_pin_initial_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset.
.. note:: A quick example to understand the initial offset
For example, an initial offset of -32 is used to map
- operating pb_type ``bram[0].dout[32]`` with a full path ``memory[dual_port].bram[0]``
- operating pb_type ``bram[0].dout[33]`` with a full path ``memory[dual_port].bram[0]``
to
- physical pb_type ``bram[0].dout_a[0]`` with a full path ``memory[physical].bram[0]``
- physical pb_type ``bram[0].dout_a[1]`` with a full path ``memory[physical].bram[0]``
.. note:: If not defined, the default value of ``physical_mode_pin_initial_offset`` is set to ``0``.
- ``physical_mode_pin_rotate_offset="<int>"`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. - ``physical_mode_pin_rotate_offset="<int>"`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset.
.. note:: A quick example to understand the rotate offset
For example, a rotating offset of 9 is used to map
- operating pb_type ``mult_9x9[0].a[0:8]`` with a full path ``mult[frac].mult_9x9[0]``
- operating pb_type ``mult_9x9[1].a[0:8]`` with a full path ``mult[frac].mult_9x9[1]``
to
- physical pb_type ``mult_36x36.a[0:8]`` with a full path ``mult[physical].mult_36x36[0]``
- physical pb_type ``mult_36x36.a[9:17]`` with a full path ``mult[physical].mult_36x36[0]``
.. note:: If not defined, the default value of ``physical_mode_pin_rotate_offset`` is set to ``0``.
.. note:: .. note::
It is highly recommended that only one physical mode is defined for a multi-mode configurable block. Try not to use nested physical mode definition. This will ease the debugging and lead to clean XML description. It is highly recommended that only one physical mode is defined for a multi-mode configurable block. Try not to use nested physical mode definition. This will ease the debugging and lead to clean XML description.

View File

@ -78,9 +78,9 @@ Here, we focus these common syntax and we will detail special syntax in :ref:`ci
.. warning:: ``prefix`` may be deprecated soon .. warning:: ``prefix`` may be deprecated soon
.. note:: Multiplexers cannot be user-defined. .. warning:: Multiplexers cannot be user-defined.
.. note:: For a circuit model type, only one circuit model can be set as default. .. warning:: For a circuit model type, only one circuit model is allowed to be set as default. If there is only one circuit model defined in a type, it will be considered as the default automatically.
.. note:: If ``<spice_netlist>`` or ``<verilog_netlist>`` are not specified, FPGA-Verilog/SPICE auto-generates the Verilog/SPICE netlists for multiplexers, wires, and LUTs. .. note:: If ``<spice_netlist>`` or ``<verilog_netlist>`` are not specified, FPGA-Verilog/SPICE auto-generates the Verilog/SPICE netlists for multiplexers, wires, and LUTs.

View File

@ -67,26 +67,13 @@ In principle, the file consist a number of XML node ``<bit>``, each bit contains
- ``value``: The configuration bit value. - ``value``: The configuration bit value.
- ``hierarchy`` represents the location of this block in FPGA fabric. - ``path`` represents the location of this block in FPGA fabric, i.e., the full path in the hierarchy of FPGA fabric.
The hierachy includes the full hierarchy of this block
- ``instance`` denotes the instance name which you can find in the fabric netlists
- ``level`` denotes the depth of the block in the hierarchy
- ``width`` denotes the number of configuration bits under the instance. Typically, only leaf instance has this attribute.
A quick example: A quick example:
.. code-block:: xml .. code-block:: xml
<bit id="0" value="1"> <bit id="0" value="1" path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5.mem_out[0]"/>
<hierarchy>
<instance level="0" name="fpga_top"/>
<instance level="1" name="grid_clb_1__2_"/>
<instance level="2" name="logical_tile_clb_mode_clb__0"/>
<instance level="3" width="10" name="mem_fle_9_in_5"/>
</hierarchy>
</bit> </bit>
Other information may depend on the type of configuration procotol. Other information may depend on the type of configuration procotol.
@ -101,14 +88,7 @@ Other information may depend on the type of configuration procotol.
.. code-block:: xml .. code-block:: xml
<bit id="0" value="1"> <bit id="0" value="1" path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5.mem_out[0]"/>
<hierarchy>
<instance level="0" name="fpga_top"/>
<instance level="1" name="grid_io_bottom_1__0_"/>
<instance level="2" name="logical_tile_io_mode_io__0"/>
<instance level="3" name="logical_tile_io_mode_physical__iopad_0"/>
<instance level="4" width="1" name="iopad_sram_blwl_mem"/>
</hierarchy>
<bl address="000000"/> <bl address="000000"/>
<wl address="000000"/> <wl address="000000"/>
</bit> </bit>
@ -121,13 +101,6 @@ Other information may depend on the type of configuration procotol.
.. code-block:: xml .. code-block:: xml
<bit id="0" value="1"> <bit id="0" value="1" path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5.mem_out[0]"/>
<hierarchy>
<instance level="0" name="fpga_top"/>
<instance level="1" name="grid_io_bottom_1__0_"/>
<instance level="2" name="logical_tile_io_mode_io__0"/>
<instance level="3" name="logical_tile_io_mode_physical__iopad_0"/>
<instance level="4" width="1" name="iopad_config_latch_mem"/>
</hierarchy>
<frame address="0000000000000000"/> <frame address="0000000000000000"/>
</bit> </bit>

View File

@ -9,7 +9,7 @@ OpenFPGA aims to be an open-source framework that enables rapid prototyping of c
:scale: 50% :scale: 50%
:alt: OpenFPGA: a fast prototyping framework for customizable FPGAs :alt: OpenFPGA: a fast prototyping framework for customizable FPGAs
Comparison on engineering time and effort to prototype an FPGA using OpenFPGA and conventional approaches Comparison on engineering time and effort to prototype an FPGA using OpenFPGA and conventional approaches [All the layout figures are permitted to publish under proper licenses]
Using OpenFPGA, the development cycle in both hardware and software can be significantly accelerated. OpenFPGA can automatically generate Verilog netlists describing a full FPGA fabric based on an XML-based description file. Thanks to modern semi-custom design tools, production-ready layout generation can be achieved within 24 hours. To help sign-off, OpenFPGA can auto-generate Verilog testbenches to validate the correctness of FPGA fabric using modern verification tools. Using OpenFPGA, the development cycle in both hardware and software can be significantly accelerated. OpenFPGA can automatically generate Verilog netlists describing a full FPGA fabric based on an XML-based description file. Thanks to modern semi-custom design tools, production-ready layout generation can be achieved within 24 hours. To help sign-off, OpenFPGA can auto-generate Verilog testbenches to validate the correctness of FPGA fabric using modern verification tools.
OpenFPGA also provides native bitstream generation support based the same XML-based description file used in Verilog generation. This avoid the recurring engineering in developing CAD tools for different FPGAs. Once the FPGA architecture is finalized, the CAD tool is ready to use. OpenFPGA also provides native bitstream generation support based the same XML-based description file used in Verilog generation. This avoid the recurring engineering in developing CAD tools for different FPGAs. Once the FPGA architecture is finalized, the CAD tool is ready to use.

View File

@ -7,6 +7,6 @@ Design Flows
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
blif_to_verification verilog2verification
verilog_to_gds2 verilog2gds2

View File

@ -1,7 +1,7 @@
.. _from_blif_to_verification: .. _from_verilog_to_verification:
From BLIF to Verification From Verilog to Verification
------------------------- ----------------------------
This tutorial will show an example how to This tutorial will show an example how to
- generate Verilog netlists for a FPGA fabric - generate Verilog netlists for a FPGA fabric

View File

@ -197,6 +197,43 @@ size_t check_circuit_model_port_required(const CircuitLibrary& circuit_lib,
return num_err; return num_err;
} }
/************************************************************************
* A generic function to search each default circuit model by types
* that have been defined by users.
* If a type of circuit model is defined, we expect there is a default model
* to be specified
***********************************************************************/
static
size_t check_default_circuit_model_by_types(const CircuitLibrary& circuit_lib) {
size_t num_err = 0;
for (size_t itype = 0; itype < NUM_CIRCUIT_MODEL_TYPES; ++itype) {
std::vector<CircuitModelId> curr_models = circuit_lib.models_by_type(e_circuit_model_type(itype));
if (0 == curr_models.size()) {
continue;
}
/* Go through the models and try to find a default one */
size_t found_default_counter = 0;
for (const auto& curr_model : curr_models) {
if (true == circuit_lib.model_is_default(curr_model)) {
found_default_counter++;
}
}
if (0 == found_default_counter) {
VTR_LOG_ERROR("Miss a default circuit model for the type %s! Try to define it in your architecture file!\n",
CIRCUIT_MODEL_TYPE_STRING[itype]);
num_err++;
}
if (1 < found_default_counter) {
VTR_LOG_ERROR("Found >1 default circuit models for the type %s! Expect only one!\n",
CIRCUIT_MODEL_TYPE_STRING[itype]);
num_err++;
}
}
return num_err;
}
/************************************************************************ /************************************************************************
* A generic function to find the default circuit model with a given type * A generic function to find the default circuit model with a given type
* If not found, we give an error * If not found, we give an error
@ -207,9 +244,9 @@ size_t check_required_default_circuit_model(const CircuitLibrary& circuit_lib,
size_t num_err = 0; size_t num_err = 0;
if (CircuitModelId::INVALID() == circuit_lib.default_model(circuit_model_type)) { if (CircuitModelId::INVALID() == circuit_lib.default_model(circuit_model_type)) {
VTR_LOG_ERROR("A default circuit model for the type %s! Try to define it in your architecture file!\n", VTR_LOG_ERROR("Miss a default circuit model for the type %s! Try to define it in your architecture file!\n",
CIRCUIT_MODEL_TYPE_STRING[size_t(circuit_model_type)]); CIRCUIT_MODEL_TYPE_STRING[size_t(circuit_model_type)]);
exit(1); num_err++;
} }
return num_err; return num_err;
@ -626,7 +663,10 @@ bool check_circuit_library(const CircuitLibrary& circuit_lib) {
num_err += check_circuit_model_port_required(circuit_lib, CIRCUIT_MODEL_LUT, lut_port_types_required); num_err += check_circuit_model_port_required(circuit_lib, CIRCUIT_MODEL_LUT, lut_port_types_required);
/* 10. We must have default circuit models for these types: MUX, channel wires and wires */ /* 10. For each type of circuit models that are define, we must have 1 default model
* We must have default circuit models for these types: MUX, channel wires and wires
*/
num_err += check_default_circuit_model_by_types(circuit_lib);
num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_MUX); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_MUX);
num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_CHAN_WIRE); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_CHAN_WIRE);
num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_WIRE); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_WIRE);

View File

@ -2,6 +2,7 @@
#include <algorithm> #include <algorithm>
#include "vtr_assert.h" #include "vtr_assert.h"
#include "vtr_log.h"
#include "openfpga_port_parser.h" #include "openfpga_port_parser.h"
#include "circuit_library.h" #include "circuit_library.h"
@ -78,11 +79,11 @@ std::string CircuitLibrary::model_verilog_netlist(const CircuitModelId& model_id
return model_verilog_netlists_[model_id]; return model_verilog_netlists_[model_id];
} }
/* Access the path + file of user-defined circuit netlist of a circuit model */ /* Access the path + file of user-defined spice netlist of a circuit model */
std::string CircuitLibrary::model_circuit_netlist(const CircuitModelId& model_id) const { std::string CircuitLibrary::model_spice_netlist(const CircuitModelId& model_id) const {
/* validate the model_id */ /* validate the model_id */
VTR_ASSERT(valid_model_id(model_id)); VTR_ASSERT(valid_model_id(model_id));
return model_circuit_netlists_[model_id]; return model_spice_netlists_[model_id];
} }
/* Access the is_default flag (check if this is the default circuit model in the type) of a circuit model */ /* Access the is_default flag (check if this is the default circuit model in the type) of a circuit model */
@ -1131,7 +1132,7 @@ CircuitModelId CircuitLibrary::add_model(const enum e_circuit_model_type& type)
model_names_.emplace_back(); model_names_.emplace_back();
model_prefix_.emplace_back(); model_prefix_.emplace_back();
model_verilog_netlists_.emplace_back(); model_verilog_netlists_.emplace_back();
model_circuit_netlists_.emplace_back(); model_spice_netlists_.emplace_back();
model_is_default_.push_back(false); model_is_default_.push_back(false);
sub_models_.emplace_back(); sub_models_.emplace_back();
@ -1225,11 +1226,11 @@ void CircuitLibrary::set_model_verilog_netlist(const CircuitModelId& model_id, c
return; return;
} }
/* Set the circuit_netlist of a Circuit Model */ /* Set the spice_netlist of a Circuit Model */
void CircuitLibrary::set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist) { void CircuitLibrary::set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist) {
/* validate the model_id */ /* validate the model_id */
VTR_ASSERT(valid_model_id(model_id)); VTR_ASSERT(valid_model_id(model_id));
model_circuit_netlists_[model_id] = circuit_netlist; model_spice_netlists_[model_id] = spice_netlist;
return; return;
} }
@ -1962,7 +1963,7 @@ void CircuitLibrary::link_buffer_model(const CircuitModelId& model_id) {
/* Get the circuit model id by name, skip those with empty names*/ /* Get the circuit model id by name, skip those with empty names*/
for (size_t buffer_id = 0; buffer_id < buffer_model_names_[model_id].size(); ++buffer_id) { for (size_t buffer_id = 0; buffer_id < buffer_model_names_[model_id].size(); ++buffer_id) {
if (true == buffer_model_names_[model_id][buffer_id].empty()) { if (true == buffer_model_names_[model_id][buffer_id].empty()) {
return; continue;
} }
buffer_model_ids_[model_id][buffer_id] = model(buffer_model_names_[model_id][buffer_id]); buffer_model_ids_[model_id][buffer_id] = model(buffer_model_names_[model_id][buffer_id]);
} }
@ -2108,6 +2109,23 @@ void CircuitLibrary::build_timing_graphs() {
return; return;
} }
/* Automatically identify the default models for each type*/
void CircuitLibrary::auto_detect_default_models() {
/* Go through the model fast look-up */
for (const auto& curr_type_models : model_lookup_) {
if ( (1 == curr_type_models.size())
&& (false == model_is_default(curr_type_models[0]))) {
/* This is the only model in this type,
* it is safe to set it to be default
* Give a warning for users
*/
set_model_is_default(curr_type_models[0], true);
VTR_LOG_WARN("Automatically set circuit model '%s' to be default in its type.\n",
model_name(curr_type_models[0]).c_str());
}
}
}
/************************************************************************ /************************************************************************
* Internal mutators: build timing graphs * Internal mutators: build timing graphs
***********************************************************************/ ***********************************************************************/

View File

@ -48,7 +48,7 @@
* It should be the same as user-defined Verilog modules, if it is not auto-generated * It should be the same as user-defined Verilog modules, if it is not auto-generated
* 4. model_prefix_: the prefix of a circuit model when it is instanciated * 4. model_prefix_: the prefix of a circuit model when it is instanciated
* 5. verilog_netlist_: specified path and file name of Verilog netlist if a circuit model is not auto-generated * 5. verilog_netlist_: specified path and file name of Verilog netlist if a circuit model is not auto-generated
* 6. circuit_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated * 6. spice_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated
* 7. is_default_: indicate if the circuit model is the default one among all those in the same type * 7. is_default_: indicate if the circuit model is the default one among all those in the same type
* 8. sub_models_: the sub circuit models included by a circuit model. It is a collection of unique circuit model ids * 8. sub_models_: the sub circuit models included by a circuit model. It is a collection of unique circuit model ids
* found in the CircuitModelId of pass-gate/buffers/port-related circuit models. * found in the CircuitModelId of pass-gate/buffers/port-related circuit models.
@ -190,7 +190,7 @@ class CircuitLibrary {
std::string model_name(const CircuitModelId& model_id) const; std::string model_name(const CircuitModelId& model_id) const;
std::string model_prefix(const CircuitModelId& model_id) const; std::string model_prefix(const CircuitModelId& model_id) const;
std::string model_verilog_netlist(const CircuitModelId& model_id) const; std::string model_verilog_netlist(const CircuitModelId& model_id) const;
std::string model_circuit_netlist(const CircuitModelId& model_id) const; std::string model_spice_netlist(const CircuitModelId& model_id) const;
bool model_is_default(const CircuitModelId& model_id) const; bool model_is_default(const CircuitModelId& model_id) const;
bool dump_structural_verilog(const CircuitModelId& model_id) const; bool dump_structural_verilog(const CircuitModelId& model_id) const;
bool dump_explicit_port_map(const CircuitModelId& model_id) const; bool dump_explicit_port_map(const CircuitModelId& model_id) const;
@ -314,7 +314,7 @@ class CircuitLibrary {
void set_model_name(const CircuitModelId& model_id, const std::string& name); void set_model_name(const CircuitModelId& model_id, const std::string& name);
void set_model_prefix(const CircuitModelId& model_id, const std::string& prefix); void set_model_prefix(const CircuitModelId& model_id, const std::string& prefix);
void set_model_verilog_netlist(const CircuitModelId& model_id, const std::string& verilog_netlist); void set_model_verilog_netlist(const CircuitModelId& model_id, const std::string& verilog_netlist);
void set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist); void set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist);
void set_model_is_default(const CircuitModelId& model_id, const bool& is_default); void set_model_is_default(const CircuitModelId& model_id, const bool& is_default);
/* Verilog generator options */ /* Verilog generator options */
void set_model_dump_structural_verilog(const CircuitModelId& model_id, const bool& dump_structural_verilog); void set_model_dump_structural_verilog(const CircuitModelId& model_id, const bool& dump_structural_verilog);
@ -461,6 +461,10 @@ class CircuitLibrary {
public: /* Public Mutators: builders */ public: /* Public Mutators: builders */
void build_model_links(); void build_model_links();
void build_timing_graphs(); void build_timing_graphs();
/* Automatically identify the default models for each type,
* suggest to do this after circuit library is built
*/
void auto_detect_default_models();
public: /* Internal mutators: build timing graphs */ public: /* Internal mutators: build timing graphs */
void add_edge(const CircuitModelId& model_id, void add_edge(const CircuitModelId& model_id,
const CircuitPortId& from_port, const size_t& from_pin, const CircuitPortId& from_port, const size_t& from_pin,
@ -495,7 +499,7 @@ class CircuitLibrary {
vtr::vector<CircuitModelId, std::string> model_names_; vtr::vector<CircuitModelId, std::string> model_names_;
vtr::vector<CircuitModelId, std::string> model_prefix_; vtr::vector<CircuitModelId, std::string> model_prefix_;
vtr::vector<CircuitModelId, std::string> model_verilog_netlists_; vtr::vector<CircuitModelId, std::string> model_verilog_netlists_;
vtr::vector<CircuitModelId, std::string> model_circuit_netlists_; vtr::vector<CircuitModelId, std::string> model_spice_netlists_;
vtr::vector<CircuitModelId, bool> model_is_default_; vtr::vector<CircuitModelId, bool> model_is_default_;
/* Submodules that a circuit model contains */ /* Submodules that a circuit model contains */

View File

@ -1,6 +1,7 @@
/************************************************************************ /************************************************************************
* Member functions for class PbTypeAnnotation * Member functions for class PbTypeAnnotation
***********************************************************************/ ***********************************************************************/
#include <algorithm>
#include "vtr_log.h" #include "vtr_log.h"
#include "vtr_assert.h" #include "vtr_assert.h"
#include "pb_type_annotation.h" #include "pb_type_annotation.h"
@ -85,24 +86,15 @@ std::vector<std::string> PbTypeAnnotation::port_names() const {
return keys; return keys;
} }
BasicPort PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { std::map<BasicPort, std::array<int, 2>> PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const {
std::map<std::string, BasicPort>::const_iterator it = operating_pb_type_ports_.find(port_name); std::map<std::string, std::map<BasicPort, std::array<int, 2>>>::const_iterator it = operating_pb_type_ports_.find(port_name);
if (it == operating_pb_type_ports_.end()) { if (it == operating_pb_type_ports_.end()) {
/* Return an empty port */ /* Return an empty port */
return BasicPort(); return std::map<BasicPort, std::array<int, 2>>();
} }
return operating_pb_type_ports_.at(port_name); return operating_pb_type_ports_.at(port_name);
} }
int PbTypeAnnotation::physical_pin_rotate_offset(const std::string& port_name) const {
std::map<std::string, int>::const_iterator it = physical_pin_rotate_offsets_.find(port_name);
if (it == physical_pin_rotate_offsets_.end()) {
/* Return a zero offset which is default */
return 0;
}
return physical_pin_rotate_offsets_.at(port_name);
}
std::vector<std::string> PbTypeAnnotation::interconnect_names() const { std::vector<std::string> PbTypeAnnotation::interconnect_names() const {
std::vector<std::string> keys; std::vector<std::string> keys;
for (auto const& element : interconnect_circuit_model_names_) { for (auto const& element : interconnect_circuit_model_names_) {
@ -177,27 +169,65 @@ void PbTypeAnnotation::set_physical_pb_type_index_offset(const int& value) {
void PbTypeAnnotation::add_pb_type_port_pair(const std::string& operating_pb_port_name, void PbTypeAnnotation::add_pb_type_port_pair(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port) { const BasicPort& physical_pb_port) {
/* Give a warning if the operating_pb_port_name already exist */ /* Give a warning if the operating_pb_port_name already exist */
std::map<std::string, BasicPort>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); std::map<std::string, std::map<BasicPort, std::array<int, 2>>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name);
/* Give a warning if the interconnection name already exist */
if (it != operating_pb_type_ports_.end()) {
VTR_LOG_WARN("Redefine operating pb type port '%s' with physical pb type port '%s'\n",
operating_pb_port_name.c_str(), physical_pb_port.get_name().c_str());
}
operating_pb_type_ports_[operating_pb_port_name] = physical_pb_port; /* If not exist, initialize and set a default value */
}
void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name,
const int& physical_pin_rotate_offset) {
std::map<std::string, BasicPort>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name);
/* Give a warning if the interconnection name already exist */
if (it == operating_pb_type_ports_.end()) { if (it == operating_pb_type_ports_.end()) {
VTR_LOG_ERROR("Operating pb type port '%s' does not exist! Ignore physical pin rotate offset '%d'\n", operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0};
operating_pb_port_name.c_str(), physical_pin_rotate_offset); /* We can return early */
return; return;
} }
physical_pin_rotate_offsets_[operating_pb_port_name] = physical_pin_rotate_offset; /* If the physical port is not in the list, we create one and set a default value */
if (0 == operating_pb_type_ports_[operating_pb_port_name].count(physical_pb_port)) {
operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0};
}
}
void PbTypeAnnotation::set_physical_pin_initial_offset(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port,
const int& physical_pin_initial_offset) {
std::map<std::string, std::map<BasicPort, std::array<int, 2>>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name);
if (it == operating_pb_type_ports_.end()) {
VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n",
operating_pb_port_name.c_str());
exit(1);
}
if (operating_pb_type_ports_[operating_pb_port_name].end() == operating_pb_type_ports_[operating_pb_port_name].find(physical_pb_port)) {
VTR_LOG_ERROR("The physical pb_type port '%s[%lu:%lu]' definition for operating pb_type port '%s' is not valid!\n",
physical_pb_port.get_name().c_str(),
physical_pb_port.get_lsb(),
physical_pb_port.get_msb(),
operating_pb_port_name.c_str());
exit(1);
}
operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][0] = physical_pin_initial_offset;
}
void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port,
const int& physical_pin_rotate_offset) {
std::map<std::string, std::map<BasicPort, std::array<int, 2>>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name);
if (it == operating_pb_type_ports_.end()) {
VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n",
operating_pb_port_name.c_str());
exit(1);
}
if (operating_pb_type_ports_[operating_pb_port_name].end() == operating_pb_type_ports_[operating_pb_port_name].find(physical_pb_port)) {
VTR_LOG_ERROR("The physical pb_type port '%s[%lu:%lu]' definition for operating pb_type port '%s' is not valid!\n",
physical_pb_port.get_name().c_str(),
physical_pb_port.get_lsb(),
physical_pb_port.get_msb(),
operating_pb_port_name.c_str());
exit(1);
}
operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][1] = physical_pin_rotate_offset;
} }
void PbTypeAnnotation::add_interconnect_circuit_model_pair(const std::string& interc_name, void PbTypeAnnotation::add_interconnect_circuit_model_pair(const std::string& interc_name,

View File

@ -6,6 +6,7 @@
*******************************************************************/ *******************************************************************/
#include <vector> #include <vector>
#include <map> #include <map>
#include <array>
#include "openfpga_port.h" #include "openfpga_port.h"
@ -48,8 +49,7 @@ class PbTypeAnnotation {
float physical_pb_type_index_factor() const; float physical_pb_type_index_factor() const;
int physical_pb_type_index_offset() const; int physical_pb_type_index_offset() const;
std::vector<std::string> port_names() const; std::vector<std::string> port_names() const;
BasicPort physical_pb_type_port(const std::string& port_name) const; std::map<BasicPort, std::array<int, 2>> physical_pb_type_port(const std::string& port_name) const;
int physical_pin_rotate_offset(const std::string& port_name) const;
std::vector<std::string> interconnect_names() const; std::vector<std::string> interconnect_names() const;
std::string interconnect_circuit_model_name(const std::string& interc_name) const; std::string interconnect_circuit_model_name(const std::string& interc_name) const;
public: /* Public mutators */ public: /* Public mutators */
@ -67,7 +67,11 @@ class PbTypeAnnotation {
void set_physical_pb_type_index_offset(const int& value); void set_physical_pb_type_index_offset(const int& value);
void add_pb_type_port_pair(const std::string& operating_pb_port_name, void add_pb_type_port_pair(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port); const BasicPort& physical_pb_port);
void set_physical_pin_initial_offset(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port,
const int& physical_pin_initial_offset);
void set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, void set_physical_pin_rotate_offset(const std::string& operating_pb_port_name,
const BasicPort& physical_pb_port,
const int& physical_pin_rotate_offset); const int& physical_pin_rotate_offset);
void add_interconnect_circuit_model_pair(const std::string& interc_name, void add_interconnect_circuit_model_pair(const std::string& interc_name,
const std::string& circuit_model_name); const std::string& circuit_model_name);
@ -133,10 +137,13 @@ class PbTypeAnnotation {
*/ */
int physical_pb_type_index_offset_; int physical_pb_type_index_offset_;
/* Link from the pins under an operating pb_type to physical pb_type */ /* Link from the pins under an operating pb_type to pairs of
std::map<std::string, BasicPort> operating_pb_type_ports_; * its physical pb_type and its pin initial & rotating offset
*
/* The offset aims to align the pin indices for port of pb_type * Note that initial offset is the first element of the std::array
* Note that rotating offset is the second element of the std::array
*
* The offsets aim to align the pin indices for port of pb_type
* between operating and physical modes, especially when an operating * between operating and physical modes, especially when an operating
* mode contains multiple pb_type (num_pb>1) that are linked to * mode contains multiple pb_type (num_pb>1) that are linked to
* the same physical pb_type. * the same physical pb_type.
@ -144,11 +151,21 @@ class PbTypeAnnotation {
* the pin index of pb_type (whose index is large than 1) * the pin index of pb_type (whose index is large than 1)
* will be shifted by the given offset. * will be shifted by the given offset.
* *
* For example, an offset of 1 is used to map * For example, an initial offset of -32 is used to map
* operating pb_type adder[0].pin[0] with a full path clb.fle[arith].adder[0] * operating pb_type bram[0].dout[32] with a full path memory[dual_port].bram[0]
* to physical pb_type adder[0].pin[1] with a full path clb.fle[physical].adder[0] * operating pb_type bram[0].dout[33] with a full path memory[dual_port].bram[0]
* to
* physical pb_type bram[0].dout_a[0] with a full path memory[physical].bram[0]
* physical pb_type bram[0].dout_a[1] with a full path memory[physical].bram[0]
*
* For example, a rotating offset of 9 is used to map
* operating pb_type mult_9x9[0].a[0:8] with a full path mult[frac].mult_9x9[0]
* operating pb_type mult_9x9[1].a[0:8] with a full path mult[frac].mult_9x9[1]
* to
* physical pb_type mult_36x36.a[0:8] with a full path mult[physical].mult_36x36[0]
* physical pb_type mult_36x36.a[9:17] with a full path mult[physical].mult_36x36[0]
*/ */
std::map<std::string, int> physical_pin_rotate_offsets_; std::map<std::string, std::map<BasicPort, std::array<int, 2>>> operating_pb_type_ports_;
/* Link between the interconnects under this pb_type and circuit model names */ /* Link between the interconnects under this pb_type and circuit model names */
std::map<std::string, std::string> interconnect_circuit_model_names_; std::map<std::string, std::string> interconnect_circuit_model_names_;

View File

@ -459,7 +459,7 @@ void read_xml_output_mask(pugi::xml_node& xml_port,
if (circuit_lib.port_size(port) != mask_vector.size()) { if (circuit_lib.port_size(port) != mask_vector.size()) {
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port), archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port),
"Invalid lut_output_mask attribute '%s'! It must match the port size (=%lu)\n", "Invalid lut_output_mask attribute '%s'! It must match the port size (=%lu)\n",
output_mask_attr, circuit_lib.port_size(port)); output_mask_attr.c_str(), circuit_lib.port_size(port));
} }
} else { } else {
/* By default, we give a mask vector covering each pin of the port */ /* By default, we give a mask vector covering each pin of the port */
@ -674,7 +674,7 @@ void read_xml_circuit_model(pugi::xml_node& xml_model,
circuit_lib.set_model_prefix(model, std::string(prefix_attr)); circuit_lib.set_model_prefix(model, std::string(prefix_attr));
/* Find a SPICE netlist which is an optional attribute*/ /* Find a SPICE netlist which is an optional attribute*/
circuit_lib.set_model_circuit_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); circuit_lib.set_model_spice_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(""));
/* Find a Verilog netlist which is an optional attribute*/ /* Find a Verilog netlist which is an optional attribute*/
circuit_lib.set_model_verilog_netlist(model, get_attribute(xml_model, "verilog_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); circuit_lib.set_model_verilog_netlist(model, get_attribute(xml_model, "verilog_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(""));

View File

@ -52,6 +52,9 @@ openfpga::Arch read_xml_openfpga_arch(const char* arch_file_name) {
auto xml_circuit_models = get_single_child(xml_openfpga_arch, "circuit_library", loc_data); auto xml_circuit_models = get_single_child(xml_openfpga_arch, "circuit_library", loc_data);
openfpga_arch.circuit_lib = read_xml_circuit_library(xml_circuit_models, loc_data); openfpga_arch.circuit_lib = read_xml_circuit_library(xml_circuit_models, loc_data);
/* Automatically identify the default models for circuit library */
openfpga_arch.circuit_lib.auto_detect_default_models();
/* Build the internal links for the circuit library */ /* Build the internal links for the circuit library */
openfpga_arch.circuit_lib.build_model_links(); openfpga_arch.circuit_lib.build_model_links();

View File

@ -55,12 +55,65 @@ void read_xml_pb_port_annotation(pugi::xml_node& xml_port,
const std::string& name_attr = get_attribute(xml_port, "name", loc_data).as_string(); const std::string& name_attr = get_attribute(xml_port, "name", loc_data).as_string();
const std::string& physical_mode_port_attr = get_attribute(xml_port, "physical_mode_port", loc_data).as_string(); const std::string& physical_mode_port_attr = get_attribute(xml_port, "physical_mode_port", loc_data).as_string();
/* Parse the mode port using openfpga port parser */ /* Split the physical mode port attributes with space */
openfpga::PortParser port_parser(physical_mode_port_attr); openfpga::StringToken port_tokenizer(physical_mode_port_attr);
pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port()); const std::vector<std::string> physical_mode_ports = port_tokenizer.split();
/* We have an optional attribute: physical_mode_pin_rotate_offset */ /* Parse the mode port using openfpga port parser */
pb_type_annotation.set_physical_pin_rotate_offset(name_attr, get_attribute(xml_port, "physical_mode_pin_rotate_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_int(0)); for (const auto& physical_mode_port : physical_mode_ports) {
openfpga::PortParser port_parser(physical_mode_port);
pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port());
}
/* We have an optional attribute: physical_mode_pin_initial_offset
* Split based on the number of physical pb_type ports that have been defined
*/
const std::string& physical_pin_initial_offset_attr = get_attribute(xml_port, "physical_mode_pin_initial_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string();
if (false == physical_pin_initial_offset_attr.empty()) {
/* Split the physical mode port attributes with space */
openfpga::StringToken offset_tokenizer(physical_pin_initial_offset_attr);
const std::vector<std::string> initial_offsets = offset_tokenizer.split();
/* Error out if the offset does not match the port definition */
if (physical_mode_ports.size() != initial_offsets.size()) {
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port),
"Defined %lu physical mode ports but only %lu physical pin initial offset are defined! Expect size matching.\n",
physical_mode_ports.size(), initial_offsets.size());
}
for (size_t iport = 0; iport < physical_mode_ports.size(); ++iport) {
openfpga::PortParser port_parser(physical_mode_ports[iport]);
pb_type_annotation.set_physical_pin_initial_offset(name_attr,
port_parser.port(),
std::stoi(initial_offsets[iport]));
}
}
/* We have an optional attribute: physical_mode_pin_rotate_offset
* Split based on the number of physical pb_type ports that have been defined
*/
const std::string& physical_pin_rotate_offset_attr = get_attribute(xml_port, "physical_mode_pin_rotate_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string();
if (false == physical_pin_rotate_offset_attr.empty()) {
/* Split the physical mode port attributes with space */
openfpga::StringToken offset_tokenizer(physical_pin_rotate_offset_attr);
const std::vector<std::string> rotate_offsets = offset_tokenizer.split();
/* Error out if the offset does not match the port definition */
if (physical_mode_ports.size() != rotate_offsets.size()) {
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port),
"Defined %lu physical mode ports but only %lu physical pin rotate offset are defined! Expect size matching.\n",
physical_mode_ports.size(), rotate_offsets.size());
}
for (size_t iport = 0; iport < physical_mode_ports.size(); ++iport) {
openfpga::PortParser port_parser(physical_mode_ports[iport]);
pb_type_annotation.set_physical_pin_rotate_offset(name_attr,
port_parser.port(),
std::stoi(rotate_offsets[iport]));
}
}
} }
/******************************************************************** /********************************************************************

View File

@ -409,8 +409,8 @@ void write_xml_circuit_model(std::fstream& fp,
if (true == circuit_lib.dump_structural_verilog(model)) { if (true == circuit_lib.dump_structural_verilog(model)) {
write_xml_attribute(fp, "dump_structural_verilog", "true"); write_xml_attribute(fp, "dump_structural_verilog", "true");
} }
if (!circuit_lib.model_circuit_netlist(model).empty()) { if (!circuit_lib.model_spice_netlist(model).empty()) {
write_xml_attribute(fp, "circuit_netlist", circuit_lib.model_circuit_netlist(model).c_str()); write_xml_attribute(fp, "spice_netlist", circuit_lib.model_spice_netlist(model).c_str());
} }
if (!circuit_lib.model_verilog_netlist(model).empty()) { if (!circuit_lib.model_verilog_netlist(model).empty()) {
write_xml_attribute(fp, "verilog_netlist", circuit_lib.model_verilog_netlist(model).c_str()); write_xml_attribute(fp, "verilog_netlist", circuit_lib.model_verilog_netlist(model).c_str());

View File

@ -116,8 +116,33 @@ void write_xml_pb_port_annotation(std::fstream& fp,
fp << "\t\t\t" << "<port"; fp << "\t\t\t" << "<port";
write_xml_attribute(fp, "name", port_name.c_str()); write_xml_attribute(fp, "name", port_name.c_str());
write_xml_attribute(fp, "physical_mode_port", generate_physical_pb_port_name(pb_type_annotation.physical_pb_type_port(port_name)).c_str());
write_xml_attribute(fp, "physical_mode_pin_rotate_offset", pb_type_annotation.physical_pin_rotate_offset(port_name)); std::string physical_mode_port_attr;
for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) {
if (false == physical_mode_port_attr.empty()) {
physical_mode_port_attr += " ";
}
physical_mode_port_attr += generate_physical_pb_port_name(physical_pb_port_pair.first);
}
write_xml_attribute(fp, "physical_mode_port", physical_mode_port_attr.c_str());
std::string physical_mode_pin_initial_offset_attr;
for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) {
if (false == physical_mode_pin_initial_offset_attr.empty()) {
physical_mode_pin_initial_offset_attr += " ";
}
physical_mode_pin_initial_offset_attr += std::to_string(physical_pb_port_pair.second[0]);
}
write_xml_attribute(fp, "physical_mode_pin_initial_offset", physical_mode_pin_initial_offset_attr.c_str());
std::string physical_mode_pin_rotate_offset_attr;
for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) {
if (false == physical_mode_pin_rotate_offset_attr.empty()) {
physical_mode_pin_rotate_offset_attr += " ";
}
physical_mode_pin_rotate_offset_attr += std::to_string(physical_pb_port_pair.second[1]);
}
write_xml_attribute(fp, "physical_mode_pin_rotate_offset", physical_mode_pin_rotate_offset_attr.c_str());
fp << "/>" << "\n"; fp << "/>" << "\n";
} }

View File

@ -55,4 +55,21 @@ std::vector<ConfigBlockId> find_bitstream_manager_top_blocks(const BitstreamMana
return top_blocks; return top_blocks;
} }
/********************************************************************
* Find the index of a configuration bit in the children bits of its parent block
*******************************************************************/
size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager,
const ConfigBitId& bit_id) {
size_t curr_index = 0;
for (const ConfigBitId& cand_bit : bitstream_manager.block_bits(bitstream_manager.bit_parent_block(bit_id))) {
if (cand_bit == bit_id) {
break;
}
curr_index++;
}
return curr_index;
}
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -19,6 +19,9 @@ std::vector<ConfigBlockId> find_bitstream_manager_block_hierarchy(const Bitstrea
std::vector<ConfigBlockId> find_bitstream_manager_top_blocks(const BitstreamManager& bitstream_manager); std::vector<ConfigBlockId> find_bitstream_manager_top_blocks(const BitstreamManager& bitstream_manager);
size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager,
const ConfigBitId& bit_id);
} /* end namespace openfpga */ } /* end namespace openfpga */
#endif #endif

View File

@ -67,7 +67,7 @@ bool parse_command(const std::vector<std::string>& argv,
/* Validate that the command name matches argv[0] */ /* Validate that the command name matches argv[0] */
if (argv[0] != cmd.name()) { if (argv[0] != cmd.name()) {
VTR_LOG("Unexpected command name '%s'!\n", VTR_LOG("Unexpected command name '%s'!\n",
argv[0]); argv[0].c_str());
return false; return false;
} }

View File

@ -4,6 +4,7 @@
/******************************************************************** /********************************************************************
* Include header files that are required by function declaration * Include header files that are required by function declaration
*******************************************************************/ *******************************************************************/
#include <stddef.h>
#include <vector> #include <vector>
/******************************************************************** /********************************************************************

View File

@ -124,6 +124,16 @@ bool BasicPort::operator== (const BasicPort& portA) const {
return false; return false;
} }
bool BasicPort::operator< (const BasicPort& portA) const {
if ( (0 == this->get_name().compare(portA.get_name()))
&& (this->get_lsb() < portA.get_lsb())
&& (this->get_msb() < portA.get_msb()) ) {
return true;
}
return false;
}
/************************************************************************ /************************************************************************
* Mutators * Mutators
***********************************************************************/ ***********************************************************************/

View File

@ -21,6 +21,7 @@ class BasicPort {
BasicPort(const BasicPort& basic_port); /* Copy constructor */ BasicPort(const BasicPort& basic_port); /* Copy constructor */
public: /* Overloaded operators */ public: /* Overloaded operators */
bool operator== (const BasicPort& portA) const; bool operator== (const BasicPort& portA) const;
bool operator< (const BasicPort& portA) const;
public: /* Accessors */ public: /* Accessors */
size_t get_width() const; /* get the port width */ size_t get_width() const; /* get the port width */
size_t get_msb() const; /* get the LSB */ size_t get_msb() const; /* get the LSB */

View File

@ -140,7 +140,7 @@ float string_to_unit(const std::string& scale) {
/* Invalid unit report error */ /* Invalid unit report error */
VTR_LOGF_ERROR(__FILE__, __LINE__, VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid unit %s!\nAcceptable units are [a|f|p|n|u|k|M|B|T] or empty\n", "Invalid unit %s!\nAcceptable units are [a|f|p|n|u|k|M|B|T] or empty\n",
scale); scale.c_str());
exit(1); exit(1);
} }
@ -153,13 +153,13 @@ float string_to_time_unit(const std::string& scale) {
&& (2 != scale.length()) ) { && (2 != scale.length()) ) {
VTR_LOGF_ERROR(__FILE__, __LINE__, VTR_LOGF_ERROR(__FILE__, __LINE__,
"Time unit (='%s') must contain only one or two characters!\n", "Time unit (='%s') must contain only one or two characters!\n",
scale); scale.c_str());
} }
/* The last character must be 's' */ /* The last character must be 's' */
if ('s' != scale.back()) { if ('s' != scale.back()) {
VTR_LOGF_ERROR(__FILE__, __LINE__, VTR_LOGF_ERROR(__FILE__, __LINE__,
"Time unit (='%s') must end with 's'!\n", "Time unit (='%s') must end with 's'!\n",
scale); scale.c_str());
} }
float unit = 1.; float unit = 1.;

View File

@ -306,16 +306,24 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin,
t_pb_graph_pin* physical_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin,
const VprDeviceAnnotation& vpr_device_annotation) { const VprDeviceAnnotation& vpr_device_annotation) {
/* If the parent ports of the two pins are not paired, fail */ /* If the parent ports of the two pins are not paired, fail */
if (physical_pb_graph_pin->port != vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) {
return false; if (physical_pb_graph_pin->port != candidate_port) {
/* Not the one we want, try the next candidate */
continue;
} }
/* Check the pin number of physical pb_graph_pin matches the pin number of /* Check the pin number of physical pb_graph_pin matches the pin number of
* operating pb_graph_pin plus a rotation offset * operating pb_graph_pin plus a rotation offset with an initial offset,
* which is to align the lsb between operating and physical ports
*
* For example:
* We can align the operating_port[32] to physical_port[0] with an initial offset
* which is -32
*
* operating port physical port * operating port physical port
* LSB port_range.lsb() pin_number pin_number MSB * LSB port_range.lsb() pin_number pin_number MSB
* | | | * | | init_offset |
* Operating port | | +------ | * Operating port | | +------ + |
* | |<----offset--->| * | |<----acc_offset--->|
* Physical port | + + + * Physical port | + + +
* *
* Note: * Note:
@ -325,18 +333,25 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin,
* by the pin rotate offset value * by the pin rotate offset value
* The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port
*/ */
int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port); int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port, candidate_port);
const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port); int init_offset = vpr_device_annotation.physical_pb_pin_initial_offset(operating_pb_graph_pin->port, candidate_port);
const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port, candidate_port);
if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number
+ (int)physical_port_range.get_lsb() + (int)physical_port_range.get_lsb()
+ init_offset
+ acc_offset) { + acc_offset) {
return false; /* Not the one we want, try the next candidate */
continue;
} }
/* Reach here, it means all the requirements have been met */ /* Reach here, it means all the requirements have been met */
return true; return true;
} }
/* If we reach here, we failed */
return false;
}
/******************************************************************** /********************************************************************
* A function to print message to log file/screen when physical pb_graph_pin * A function to print message to log file/screen when physical pb_graph_pin
* binding succeed * binding succeed
@ -424,7 +439,7 @@ void annotate_physical_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin,
/* If we reach here, it means that pin pairing fails, error out! */ /* If we reach here, it means that pin pairing fails, error out! */
VTR_LOG_ERROR("Fail to match a physical pin for '%s' from pb_graph_node '%s'!\n", VTR_LOG_ERROR("Fail to match a physical pin for '%s' from pb_graph_node '%s'!\n",
operating_pb_graph_pin->port->name, operating_pb_graph_pin->to_string().c_str(),
physical_pb_graph_node->hierarchical_type_name().c_str()); physical_pb_graph_node->hierarchical_type_name().c_str());
} }

View File

@ -211,22 +211,27 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type,
* if not found, we assume that the physical port is the same as the operating pb_port * if not found, we assume that the physical port is the same as the operating pb_port
*/ */
for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) { for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) {
/* Try to find the port in the pb_type_annotation */ std::map<BasicPort, std::array<int, 2>> expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name));
BasicPort expected_physical_pb_port = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name));
if (true == expected_physical_pb_port.get_name().empty()) { /* If not defined in the annotation, set the default pair:
/* Not found, we reset the port information to be consistent as the operating pb_port */ * rotate_offset is 0 by default!
*/
if (true == expected_physical_pb_ports.empty()) {
BasicPort expected_physical_pb_port;
expected_physical_pb_port.set_name(std::string(operating_pb_port->name)); expected_physical_pb_port.set_name(std::string(operating_pb_port->name));
expected_physical_pb_port.set_width(operating_pb_port->num_pins); expected_physical_pb_port.set_width(operating_pb_port->num_pins);
expected_physical_pb_ports[expected_physical_pb_port] = {0, 0};
} }
for (const auto& expected_physical_pb_port : expected_physical_pb_ports) {
/* Try to find the expected port in the physical pb_type */ /* Try to find the expected port in the physical pb_type */
t_port* physical_pb_port = find_pb_type_port(physical_pb_type, expected_physical_pb_port.get_name()); t_port* physical_pb_port = find_pb_type_port(physical_pb_type, expected_physical_pb_port.first.get_name());
/* Not found, mapping fails */ /* Not found, mapping fails */
if (nullptr == physical_pb_port) { if (nullptr == physical_pb_port) {
return false; return false;
} }
/* If the port range does not match, mapping fails */ /* If the port range does not match, mapping fails */
if (false == BasicPort(physical_pb_port->name, physical_pb_port->num_pins).contained(expected_physical_pb_port)) { if (false == BasicPort(physical_pb_port->name, physical_pb_port->num_pins).contained(expected_physical_pb_port.first)) {
return false; return false;
} }
/* Now, port mapping should succeed, we update the vpr_device_annotation /* Now, port mapping should succeed, we update the vpr_device_annotation
@ -235,8 +240,10 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type,
* - port pin rotate offset * - port pin rotate offset
*/ */
vpr_device_annotation.add_physical_pb_port(operating_pb_port, physical_pb_port); vpr_device_annotation.add_physical_pb_port(operating_pb_port, physical_pb_port);
vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, expected_physical_pb_port); vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, physical_pb_port, expected_physical_pb_port.first);
vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, pb_type_annotation.physical_pin_rotate_offset(std::string(operating_pb_port->name))); vpr_device_annotation.add_physical_pb_pin_initial_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[0]);
vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[1]);
}
} }
/* Now, pb_type mapping should succeed, we update the vpr_device_annotation /* Now, pb_type mapping should succeed, we update the vpr_device_annotation
@ -365,7 +372,7 @@ bool self_pair_physical_pb_types(t_pb_type* physical_pb_type,
for (t_port* physical_pb_port : pb_type_ports(physical_pb_type)) { for (t_port* physical_pb_port : pb_type_ports(physical_pb_type)) {
BasicPort physical_port_range(physical_pb_port->name, physical_pb_port->num_pins); BasicPort physical_port_range(physical_pb_port->name, physical_pb_port->num_pins);
vpr_device_annotation.add_physical_pb_port(physical_pb_port, physical_pb_port); vpr_device_annotation.add_physical_pb_port(physical_pb_port, physical_pb_port);
vpr_device_annotation.add_physical_pb_port_range(physical_pb_port, physical_port_range); vpr_device_annotation.add_physical_pb_port_range(physical_pb_port, physical_pb_port, physical_port_range);
} }
/* Now, pb_type mapping should succeed, we update the vpr_device_annotation */ /* Now, pb_type mapping should succeed, we update the vpr_device_annotation */

View File

@ -114,7 +114,7 @@ void check_vpr_physical_primitive_pb_type_annotation(t_pb_type* cur_pb_type,
/* Now we need to check each port of the pb_type */ /* Now we need to check each port of the pb_type */
for (t_port* pb_port : pb_type_ports(cur_pb_type)) { for (t_port* pb_port : pb_type_ports(cur_pb_type)) {
if (nullptr == vpr_device_annotation.physical_pb_port(pb_port)) { if (0 == vpr_device_annotation.physical_pb_port(pb_port).size()) {
VTR_LOG_ERROR("Find a port '%s' of pb_type '%s' which has not been mapped to any physical port!\n", VTR_LOG_ERROR("Find a port '%s' of pb_type '%s' which has not been mapped to any physical port!\n",
pb_port->name, cur_pb_type->name); pb_port->name, cur_pb_type->name);
VTR_LOG_ERROR("Please specify in the OpenFPGA architecture\n"); VTR_LOG_ERROR("Please specify in the OpenFPGA architecture\n");
@ -194,7 +194,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type,
/* Every physical pb_type should be linked to a valid circuit model */ /* Every physical pb_type should be linked to a valid circuit model */
if (CircuitModelId::INVALID() == vpr_device_annotation.pb_type_circuit_model(cur_pb_type)) { if (CircuitModelId::INVALID() == vpr_device_annotation.pb_type_circuit_model(cur_pb_type)) {
VTR_LOG_ERROR("Found a physical pb_type '%s' missing circuit model binding!\n", VTR_LOG_ERROR("Found a physical pb_type '%s' missing circuit model binding!\n",
cur_pb_type->name); generate_pb_type_hierarchy_path(cur_pb_type).c_str());
num_err++; num_err++;
return; /* Invalid id already, further check is not applicable */ return; /* Invalid id already, further check is not applicable */
} }
@ -202,7 +202,8 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type,
for (t_port* port : pb_type_ports(cur_pb_type)) { for (t_port* port : pb_type_ports(cur_pb_type)) {
if (CircuitPortId::INVALID() == vpr_device_annotation.pb_circuit_port(port)) { if (CircuitPortId::INVALID() == vpr_device_annotation.pb_circuit_port(port)) {
VTR_LOG_ERROR("Found a port '%s' of physical pb_type '%s' missing circuit port binding!\n", VTR_LOG_ERROR("Found a port '%s' of physical pb_type '%s' missing circuit port binding!\n",
port->name, cur_pb_type->name); port->name,
generate_pb_type_hierarchy_path(cur_pb_type).c_str());
num_err++; num_err++;
} }
} }
@ -217,7 +218,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type,
VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' missing circuit model binding!\n", VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' missing circuit model binding!\n",
interc->name, interc->name,
physical_mode->name, physical_mode->name,
cur_pb_type->name); generate_pb_type_hierarchy_path(cur_pb_type).c_str());
num_err++; num_err++;
continue; continue;
} }
@ -226,7 +227,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type,
VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' linked to a circuit model '%s' with a wrong type!\nExpect: '%s' Linked: '%s'\n", VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' linked to a circuit model '%s' with a wrong type!\nExpect: '%s' Linked: '%s'\n",
interc->name, interc->name,
physical_mode->name, physical_mode->name,
cur_pb_type->name, generate_pb_type_hierarchy_path(cur_pb_type).c_str(),
circuit_lib.model_name(interc_circuit_model).c_str(), circuit_lib.model_name(interc_circuit_model).c_str(),
CIRCUIT_MODEL_TYPE_STRING[circuit_lib.model_type(interc_circuit_model)], CIRCUIT_MODEL_TYPE_STRING[circuit_lib.model_type(interc_circuit_model)],
CIRCUIT_MODEL_TYPE_STRING[required_circuit_model_type]); CIRCUIT_MODEL_TYPE_STRING[required_circuit_model_type]);

View File

@ -48,23 +48,28 @@ t_pb_type* VprDeviceAnnotation::physical_pb_type(t_pb_type* pb_type) const {
return physical_pb_types_.at(pb_type); return physical_pb_types_.at(pb_type);
} }
t_port* VprDeviceAnnotation::physical_pb_port(t_port* pb_port) const { std::vector<t_port*> VprDeviceAnnotation::physical_pb_port(t_port* pb_port) const {
/* Ensure that the pb_type is in the list */ /* Ensure that the pb_type is in the list */
std::map<t_port*, t_port*>::const_iterator it = physical_pb_ports_.find(pb_port); std::map<t_port*, std::vector<t_port*>>::const_iterator it = physical_pb_ports_.find(pb_port);
if (it == physical_pb_ports_.end()) { if (it == physical_pb_ports_.end()) {
return nullptr; return std::vector<t_port*>();
} }
return physical_pb_ports_.at(pb_port); return physical_pb_ports_.at(pb_port);
} }
BasicPort VprDeviceAnnotation::physical_pb_port_range(t_port* pb_port) const { BasicPort VprDeviceAnnotation::physical_pb_port_range(t_port* operating_pb_port,
t_port* physical_pb_port) const {
/* Ensure that the pb_type is in the list */ /* Ensure that the pb_type is in the list */
std::map<t_port*, BasicPort>::const_iterator it = physical_pb_port_ranges_.find(pb_port); std::map<t_port*, std::map<t_port*, BasicPort>>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port);
if (it == physical_pb_port_ranges_.end()) { if (it == physical_pb_port_ranges_.end()) {
/* Return an invalid port. As such the port width will be 0, which is an invalid value */ /* Return an invalid port. As such the port width will be 0, which is an invalid value */
return BasicPort(); return BasicPort();
} }
return physical_pb_port_ranges_.at(pb_port); if (0 == physical_pb_port_ranges_.at(operating_pb_port).count(physical_pb_port)) {
/* Return an invalid port. As such the port width will be 0, which is an invalid value */
return BasicPort();
}
return physical_pb_port_ranges_.at(operating_pb_port).at(physical_pb_port);
} }
CircuitModelId VprDeviceAnnotation::pb_type_circuit_model(t_pb_type* physical_pb_type) const { CircuitModelId VprDeviceAnnotation::pb_type_circuit_model(t_pb_type* physical_pb_type) const {
@ -185,26 +190,50 @@ int VprDeviceAnnotation::physical_pb_type_index_offset(t_pb_type* pb_type) const
return physical_pb_type_index_offsets_.at(pb_type); return physical_pb_type_index_offsets_.at(pb_type);
} }
int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* pb_port) const { int VprDeviceAnnotation::physical_pb_pin_initial_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const {
/* Ensure that the pb_type is in the list */ /* Ensure that the pb_type is in the list */
std::map<t_port*, int>::const_iterator it = physical_pb_pin_rotate_offsets_.find(pb_port); std::map<t_port*, std::map<t_port*, int>>::const_iterator it = physical_pb_pin_initial_offsets_.find(operating_pb_port);
if (it == physical_pb_pin_initial_offsets_.end()) {
/* Default value is 0 */
return 0;
}
if (0 == physical_pb_pin_initial_offsets_.at(operating_pb_port).count(physical_pb_port)) {
/* Default value is 0 */
return 0;
}
return physical_pb_pin_initial_offsets_.at(operating_pb_port).at(physical_pb_port);
}
int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const {
/* Ensure that the pb_type is in the list */
std::map<t_port*, std::map<t_port*, int>>::const_iterator it = physical_pb_pin_rotate_offsets_.find(operating_pb_port);
if (it == physical_pb_pin_rotate_offsets_.end()) { if (it == physical_pb_pin_rotate_offsets_.end()) {
/* Default value is 0 */ /* Default value is 0 */
return 0; return 0;
} }
return physical_pb_pin_rotate_offsets_.at(pb_port); if (0 == physical_pb_pin_rotate_offsets_.at(operating_pb_port).count(physical_pb_port)) {
/* Default value is 0 */
return 0;
}
return physical_pb_pin_rotate_offsets_.at(operating_pb_port).at(physical_pb_port);
} }
int VprDeviceAnnotation::physical_pb_pin_offset(t_port* pb_port) const { int VprDeviceAnnotation::physical_pb_pin_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const {
/* Ensure that the pb_type is in the list */ /* Ensure that the pb_type is in the list */
std::map<t_port*, int>::const_iterator it = physical_pb_pin_offsets_.find(pb_port); std::map<t_port*, std::map<t_port*, int>>::const_iterator it = physical_pb_pin_offsets_.find(operating_pb_port);
if (it == physical_pb_pin_offsets_.end()) { if (it == physical_pb_pin_offsets_.end()) {
/* Default value is 0 */ /* Default value is 0 */
return 0; return 0;
} }
return physical_pb_pin_offsets_.at(pb_port); if (0 == physical_pb_pin_offsets_.at(operating_pb_port).count(physical_pb_port)) {
/* Default value is 0 */
return 0;
}
return physical_pb_pin_offsets_.at(operating_pb_port).at(physical_pb_port);
} }
t_pb_graph_pin* VprDeviceAnnotation::physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const { t_pb_graph_pin* VprDeviceAnnotation::physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const {
/* Ensure that the pb_type is in the list */ /* Ensure that the pb_type is in the list */
@ -274,29 +303,28 @@ void VprDeviceAnnotation::add_physical_pb_type(t_pb_type* operating_pb_type, t_p
physical_pb_types_[operating_pb_type] = physical_pb_type; physical_pb_types_[operating_pb_type] = physical_pb_type;
} }
void VprDeviceAnnotation::add_physical_pb_port(t_port* operating_pb_port, t_port* physical_pb_port) { void VprDeviceAnnotation::add_physical_pb_port(t_port* operating_pb_port,
/* Warn any override attempt */ t_port* physical_pb_port) {
std::map<t_port*, t_port*>::const_iterator it = physical_pb_ports_.find(operating_pb_port); physical_pb_ports_[operating_pb_port].push_back(physical_pb_port);
if (it != physical_pb_ports_.end()) {
VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s'!\n",
operating_pb_port->name, physical_pb_port->name);
} }
physical_pb_ports_[operating_pb_port] = physical_pb_port; void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port,
} t_port* physical_pb_port,
const BasicPort& port_range) {
void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range) {
/* The port range must satify the port width*/ /* The port range must satify the port width*/
VTR_ASSERT((size_t)operating_pb_port->num_pins == port_range.get_width()); VTR_ASSERT((size_t)operating_pb_port->num_pins >= port_range.get_width());
/* Warn any override attempt */ /* Warn any override attempt */
std::map<t_port*, BasicPort>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port); std::map<t_port*, std::map<t_port*, BasicPort>>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port);
if (it != physical_pb_port_ranges_.end()) { if ( (it != physical_pb_port_ranges_.end())
VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port range '[%ld:%ld]'!\n", && (0 < physical_pb_port_ranges_[operating_pb_port].count(physical_pb_port)) ) {
operating_pb_port->name, port_range.get_lsb(), port_range.get_msb()); VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port range '%s[%ld:%ld]'!\n",
operating_pb_port->name,
physical_pb_port->name,
port_range.get_lsb(), port_range.get_msb());
} }
physical_pb_port_ranges_[operating_pb_port] = port_range; physical_pb_port_ranges_[operating_pb_port][physical_pb_port] = port_range;
} }
void VprDeviceAnnotation::add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model) { void VprDeviceAnnotation::add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model) {
@ -396,17 +424,34 @@ void VprDeviceAnnotation::add_physical_pb_type_index_offset(t_pb_type* pb_type,
physical_pb_type_index_offsets_[pb_type] = offset; physical_pb_type_index_offsets_[pb_type] = offset;
} }
void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* pb_port, const int& offset) { void VprDeviceAnnotation::add_physical_pb_pin_initial_offset(t_port* operating_pb_port,
t_port* physical_pb_port,
const int& offset) {
/* Warn any override attempt */ /* Warn any override attempt */
std::map<t_port*, int>::const_iterator it = physical_pb_pin_rotate_offsets_.find(pb_port); std::map<t_port*, std::map<t_port*, int>>::const_iterator it = physical_pb_pin_initial_offsets_.find(operating_pb_port);
if (it != physical_pb_pin_rotate_offsets_.end()) { if ( (it != physical_pb_pin_initial_offsets_.end())
VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port pin rotate offset '%d'!\n", && (0 < physical_pb_pin_initial_offsets_[operating_pb_port].count(physical_pb_port)) ) {
pb_port->name, offset); VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s' pin rotate offset '%d'!\n",
operating_pb_port->name, offset);
} }
physical_pb_pin_rotate_offsets_[pb_port] = offset; physical_pb_pin_initial_offsets_[operating_pb_port][physical_pb_port] = offset;
}
void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* operating_pb_port,
t_port* physical_pb_port,
const int& offset) {
/* Warn any override attempt */
std::map<t_port*, std::map<t_port*, int>>::const_iterator it = physical_pb_pin_rotate_offsets_.find(operating_pb_port);
if ( (it != physical_pb_pin_rotate_offsets_.end())
&& (0 < physical_pb_pin_rotate_offsets_[operating_pb_port].count(physical_pb_port)) ) {
VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s' pin rotate offset '%d'!\n",
operating_pb_port->name, offset);
}
physical_pb_pin_rotate_offsets_[operating_pb_port][physical_pb_port] = offset;
/* We initialize the accumulated offset to 0 */ /* We initialize the accumulated offset to 0 */
physical_pb_pin_offsets_[pb_port] = 0; physical_pb_pin_offsets_[operating_pb_port][physical_pb_port] = 0;
} }
void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin, void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin,
@ -432,17 +477,17 @@ void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operat
* Physical port | + + + * Physical port | + + +
* *
*/ */
if (0 == physical_pb_pin_rotate_offset(operating_pb_graph_pin->port)) { if (0 == physical_pb_pin_rotate_offset(operating_pb_graph_pin->port, physical_pb_graph_pin->port)) {
return; return;
} }
physical_pb_pin_offsets_[operating_pb_graph_pin->port] += physical_pb_pin_rotate_offset(operating_pb_graph_pin->port); physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port] += physical_pb_pin_rotate_offset(operating_pb_graph_pin->port, physical_pb_graph_pin->port);
if ((size_t)physical_pb_port(operating_pb_graph_pin->port)->num_pins - 1 if ((size_t)physical_pb_graph_pin->port->num_pins - 1
< operating_pb_graph_pin->pin_number < operating_pb_graph_pin->pin_number
+ physical_pb_port_range(operating_pb_graph_pin->port).get_lsb() + physical_pb_port_range(operating_pb_graph_pin->port, physical_pb_graph_pin->port).get_lsb()
+ physical_pb_pin_offsets_[operating_pb_graph_pin->port]) { + physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port]) {
physical_pb_pin_offsets_[operating_pb_graph_pin->port] = 0; physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port] = 0;
} }
} }

View File

@ -45,8 +45,9 @@ class VprDeviceAnnotation {
bool is_physical_pb_type(t_pb_type* pb_type) const; bool is_physical_pb_type(t_pb_type* pb_type) const;
t_mode* physical_mode(t_pb_type* pb_type) const; t_mode* physical_mode(t_pb_type* pb_type) const;
t_pb_type* physical_pb_type(t_pb_type* pb_type) const; t_pb_type* physical_pb_type(t_pb_type* pb_type) const;
t_port* physical_pb_port(t_port* pb_port) const; std::vector<t_port*> physical_pb_port(t_port* pb_port) const;
BasicPort physical_pb_port_range(t_port* pb_port) const; BasicPort physical_pb_port_range(t_port* operating_pb_port,
t_port* physical_pb_port) const;
CircuitModelId pb_type_circuit_model(t_pb_type* physical_pb_type) const; CircuitModelId pb_type_circuit_model(t_pb_type* physical_pb_type) const;
CircuitModelId interconnect_circuit_model(t_interconnect* pb_interconnect) const; CircuitModelId interconnect_circuit_model(t_interconnect* pb_interconnect) const;
e_interconnect interconnect_physical_type(t_interconnect* pb_interconnect) const; e_interconnect interconnect_physical_type(t_interconnect* pb_interconnect) const;
@ -60,7 +61,11 @@ class VprDeviceAnnotation {
float physical_pb_type_index_factor(t_pb_type* pb_type) const; float physical_pb_type_index_factor(t_pb_type* pb_type) const;
int physical_pb_type_index_offset(t_pb_type* pb_type) const; int physical_pb_type_index_offset(t_pb_type* pb_type) const;
int physical_pb_pin_rotate_offset(t_port* pb_port) const; int physical_pb_pin_initial_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const;
int physical_pb_pin_rotate_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const;
/**This function returns an accumulated offset. Note that the /**This function returns an accumulated offset. Note that the
* accumulated offset is NOT the pin rotate offset specified by users * accumulated offset is NOT the pin rotate offset specified by users
@ -69,7 +74,8 @@ class VprDeviceAnnotation {
* by the pin rotate offset value * by the pin rotate offset value
* The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port
*/ */
int physical_pb_pin_offset(t_port* pb_port) const; int physical_pb_pin_offset(t_port* operating_pb_port,
t_port* physical_pb_port) const;
t_pb_graph_pin* physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const; t_pb_graph_pin* physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const;
CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const; CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const;
CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const; CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const;
@ -78,8 +84,11 @@ class VprDeviceAnnotation {
public: /* Public mutators */ public: /* Public mutators */
void add_pb_type_physical_mode(t_pb_type* pb_type, t_mode* physical_mode); void add_pb_type_physical_mode(t_pb_type* pb_type, t_mode* physical_mode);
void add_physical_pb_type(t_pb_type* operating_pb_type, t_pb_type* physical_pb_type); void add_physical_pb_type(t_pb_type* operating_pb_type, t_pb_type* physical_pb_type);
void add_physical_pb_port(t_port* operating_pb_port, t_port* physical_pb_port); void add_physical_pb_port(t_port* operating_pb_port,
void add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range); t_port* physical_pb_port);
void add_physical_pb_port_range(t_port* operating_pb_port,
t_port* physical_pb_port,
const BasicPort& port_range);
void add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model); void add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model);
void add_interconnect_circuit_model(t_interconnect* pb_interconnect, const CircuitModelId& circuit_model); void add_interconnect_circuit_model(t_interconnect* pb_interconnect, const CircuitModelId& circuit_model);
void add_interconnect_physical_type(t_interconnect* pb_interconnect, const e_interconnect& physical_type); void add_interconnect_physical_type(t_interconnect* pb_interconnect, const e_interconnect& physical_type);
@ -90,7 +99,12 @@ class VprDeviceAnnotation {
t_pb_graph_node* physical_pb_graph_node); t_pb_graph_node* physical_pb_graph_node);
void add_physical_pb_type_index_factor(t_pb_type* pb_type, const float& factor); void add_physical_pb_type_index_factor(t_pb_type* pb_type, const float& factor);
void add_physical_pb_type_index_offset(t_pb_type* pb_type, const int& offset); void add_physical_pb_type_index_offset(t_pb_type* pb_type, const int& offset);
void add_physical_pb_pin_rotate_offset(t_port* pb_port, const int& offset); void add_physical_pb_pin_initial_offset(t_port* operating_pb_port,
t_port* physical_pb_port,
const int& offset);
void add_physical_pb_pin_rotate_offset(t_port* operating_pb_port,
t_port* physical_pb_port,
const int& offset);
void add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin); void add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin);
void add_rr_switch_circuit_model(const RRSwitchId& rr_switch, const CircuitModelId& circuit_model); void add_rr_switch_circuit_model(const RRSwitchId& rr_switch, const CircuitModelId& circuit_model);
void add_rr_segment_circuit_model(const RRSegmentId& rr_segment, const CircuitModelId& circuit_model); void add_rr_segment_circuit_model(const RRSegmentId& rr_segment, const CircuitModelId& circuit_model);
@ -139,17 +153,18 @@ class VprDeviceAnnotation {
* Note: * Note:
* - the parent of physical pb_port MUST be a physical pb_type * - the parent of physical pb_port MUST be a physical pb_type
*/ */
std::map<t_port*, t_port*> physical_pb_ports_; std::map<t_port*, std::vector<t_port*>> physical_pb_ports_;
std::map<t_port*, int> physical_pb_pin_rotate_offsets_; std::map<t_port*, std::map<t_port*, int>> physical_pb_pin_initial_offsets_;
std::map<t_port*, std::map<t_port*, int>> physical_pb_pin_rotate_offsets_;
/* Accumulated offsets for a physical pb_type port, just for internal usage */ /* Accumulated offsets for a physical pb_type port, just for internal usage */
std::map<t_port*, int> physical_pb_pin_offsets_; std::map<t_port*, std::map<t_port*, int>> physical_pb_pin_offsets_;
/* Pair a pb_port to its LSB and MSB of a physical pb_port /* Pair a pb_port to its LSB and MSB of a physical pb_port
* Note: * Note:
* - the LSB and MSB MUST be in range of the physical pb_port * - the LSB and MSB MUST be in range of the physical pb_port
*/ */
std::map<t_port*, BasicPort> physical_pb_port_ranges_; std::map<t_port*, std::map<t_port*, BasicPort>> physical_pb_port_ranges_;
/* Pair a pb_port to a circuit port in circuit model /* Pair a pb_port to a circuit port in circuit model
* Note: * Note:

View File

@ -4,6 +4,7 @@
/******************************************************************** /********************************************************************
* Include header files required by the data structure definition * Include header files required by the data structure definition
*******************************************************************/ *******************************************************************/
#include <stddef.h>
#include <vector> #include <vector>
/* Begin namespace openfpga */ /* Begin namespace openfpga */

View File

@ -38,7 +38,7 @@ int fpga_bitstream(OpenfpgaContext& openfpga_ctx,
CommandOptionId opt_read_file = cmd.option("read_file"); CommandOptionId opt_read_file = cmd.option("read_file");
if (true == cmd_context.option_enable(cmd, opt_read_file)) { if (true == cmd_context.option_enable(cmd, opt_read_file)) {
openfpga_ctx.mutable_bitstream_manager() = read_xml_architecture_bitstream(cmd_context.option_value(cmd, opt_write_file).c_str()); openfpga_ctx.mutable_bitstream_manager() = read_xml_architecture_bitstream(cmd_context.option_value(cmd, opt_read_file).c_str());
} else { } else {
openfpga_ctx.mutable_bitstream_manager() = build_device_bitstream(g_vpr_ctx, openfpga_ctx.mutable_bitstream_manager() = build_device_bitstream(g_vpr_ctx,
openfpga_ctx, openfpga_ctx,

View File

@ -133,6 +133,7 @@ void update_cluster_pin_with_post_routing_results(const DeviceContext& device_ct
if (routing_net_id == cluster_net_id) { if (routing_net_id == cluster_net_id) {
continue; continue;
} }
/* Add to net modification */ /* Add to net modification */
vpr_clustering_annotation.rename_net(blk_id, j, routing_net_id); vpr_clustering_annotation.rename_net(blk_id, j, routing_net_id);

View File

@ -40,6 +40,10 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx,
status = fpga_fabric_spice(openfpga_ctx.module_graph(), status = fpga_fabric_spice(openfpga_ctx.module_graph(),
openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.mutable_spice_netlists(),
openfpga_ctx.arch(), openfpga_ctx.arch(),
openfpga_ctx.mux_lib(),
g_vpr_ctx.device(),
openfpga_ctx.vpr_device_annotation(),
openfpga_ctx.device_rr_gsb(),
options); options);
return status; return status;

View File

@ -187,7 +187,7 @@ void build_user_defined_modules(ModuleManager& module_manager,
for (const auto& model : circuit_lib.models()) { for (const auto& model : circuit_lib.models()) {
/* We only care about user-defined models */ /* We only care about user-defined models */
if ( (true == circuit_lib.model_verilog_netlist(model).empty()) if ( (true == circuit_lib.model_verilog_netlist(model).empty())
&& (true == circuit_lib.model_circuit_netlist(model).empty()) ) { && (true == circuit_lib.model_spice_netlist(model).empty()) ) {
continue; continue;
} }
/* Skip Routing channel wire models because they need a different name. Do it later */ /* Skip Routing channel wire models because they need a different name. Do it later */
@ -255,7 +255,7 @@ void rename_primitive_module_port_names(ModuleManager& module_manager,
for (const CircuitModelId& model : circuit_lib.models()) { for (const CircuitModelId& model : circuit_lib.models()) {
/* We only care about user-defined models */ /* We only care about user-defined models */
if ( (true == circuit_lib.model_verilog_netlist(model).empty()) if ( (true == circuit_lib.model_verilog_netlist(model).empty())
&& (true == circuit_lib.model_circuit_netlist(model).empty()) ) { && (true == circuit_lib.model_spice_netlist(model).empty()) ) {
continue; continue;
} }
/* Skip Routing channel wire models because they need a different name. Do it later */ /* Skip Routing channel wire models because they need a different name. Do it later */

View File

@ -62,7 +62,7 @@ void build_wire_modules(ModuleManager& module_manager,
/* Print Verilog models for regular wires*/ /* Print Verilog models for regular wires*/
for (const auto& wire_model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) { for (const auto& wire_model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) {
/* Bypass user-defined circuit models */ /* Bypass user-defined circuit models */
if ( (!circuit_lib.model_circuit_netlist(wire_model).empty()) if ( (!circuit_lib.model_spice_netlist(wire_model).empty())
&& (!circuit_lib.model_verilog_netlist(wire_model).empty()) ) { && (!circuit_lib.model_verilog_netlist(wire_model).empty()) ) {
continue; continue;
} }

View File

@ -80,27 +80,23 @@ int write_fabric_config_bit_to_xml_file(std::fstream& fp,
fp << "<bit id=\"" << size_t(fabric_bit) << "\""; fp << "<bit id=\"" << size_t(fabric_bit) << "\"";
fp << " value=\""; fp << " value=\"";
fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit)); fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit));
fp << "\">\n"; fp << "\"";
/* Output hierarchy of this parent*/ /* Output hierarchy of this parent*/
const ConfigBitId& config_bit = fabric_bitstream.config_bit(fabric_bit); const ConfigBitId& config_bit = fabric_bitstream.config_bit(fabric_bit);
const ConfigBlockId& config_block = bitstream_manager.bit_parent_block(config_bit); const ConfigBlockId& config_block = bitstream_manager.bit_parent_block(config_bit);
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block); std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block);
write_tab_to_file(fp, 2); std::string hie_path;
fp << "<hierarchy>\n";
size_t hierarchy_counter = 0;
for (const ConfigBlockId& temp_block : block_hierarchy) { for (const ConfigBlockId& temp_block : block_hierarchy) {
write_tab_to_file(fp, 3); hie_path += bitstream_manager.block_name(temp_block);
fp << "<instance level=\"" << hierarchy_counter << "\""; hie_path += std::string(".");
if (0 < bitstream_manager.block_bits(temp_block).size()) {
fp << " width=\"" << bitstream_manager.block_bits(temp_block).size() << "\"";
} }
fp << " name=\"" << bitstream_manager.block_name(temp_block) << "\""; hie_path += generate_configurable_memory_data_out_name();
fp << "/>\n"; hie_path += std::string("[");
hierarchy_counter++; hie_path += std::to_string(find_bitstream_manager_config_bit_index_in_parent_block(bitstream_manager, config_bit));
} hie_path += std::string("]");
write_tab_to_file(fp, 2);
fp << "</hierarchy>\n"; fp << " path=\"" << hie_path << "\">\n";
switch (config_type) { switch (config_type) {
case CONFIG_MEM_STANDALONE: case CONFIG_MEM_STANDALONE:

View File

@ -16,6 +16,10 @@
#include "spice_constants.h" #include "spice_constants.h"
#include "spice_submodule.h" #include "spice_submodule.h"
#include "spice_routing.h"
#include "spice_grid.h"
#include "spice_top_module.h"
#include "spice_auxiliary_netlists.h"
/* Header file for this source file */ /* Header file for this source file */
#include "spice_api.h" #include "spice_api.h"
@ -40,6 +44,10 @@ namespace openfpga {
int fpga_fabric_spice(const ModuleManager& module_manager, int fpga_fabric_spice(const ModuleManager& module_manager,
NetlistManager& netlist_manager, NetlistManager& netlist_manager,
const Arch& openfpga_arch, const Arch& openfpga_arch,
const MuxLibrary& mux_lib,
const DeviceContext &device_ctx,
const VprDeviceAnnotation &device_annotation,
const DeviceRRGSB &device_rr_gsb,
const FabricSpiceOption& options) { const FabricSpiceOption& options) {
vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n"); vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n");
@ -73,12 +81,44 @@ int fpga_fabric_spice(const ModuleManager& module_manager,
status = print_spice_submodule(netlist_manager, status = print_spice_submodule(netlist_manager,
module_manager, module_manager,
openfpga_arch, openfpga_arch,
mux_lib,
submodule_dir_path); submodule_dir_path);
if (CMD_EXEC_SUCCESS != status) { if (CMD_EXEC_SUCCESS != status) {
return status; return status;
} }
/* Generate routing blocks */
if (true == options.compress_routing()) {
print_spice_unique_routing_modules(netlist_manager,
module_manager,
device_rr_gsb,
rr_dir_path);
} else {
VTR_ASSERT(false == options.compress_routing());
print_spice_flatten_routing_modules(netlist_manager,
module_manager,
device_rr_gsb,
rr_dir_path);
}
/* Generate grids */
print_spice_grids(netlist_manager,
module_manager,
device_ctx, device_annotation,
lb_dir_path,
options.verbose_output());
/* Generate FPGA fabric */
print_spice_top_module(netlist_manager,
module_manager,
src_dir_path);
/* Generate an netlist including all the fabric-related netlists */
print_spice_fabric_include_netlist(const_cast<const NetlistManager &>(netlist_manager),
src_dir_path,
openfpga_arch.circuit_lib);
/* Given a brief stats on how many Spice modules have been written to files */ /* Given a brief stats on how many Spice modules have been written to files */
VTR_LOGV(options.verbose_output(), VTR_LOGV(options.verbose_output(),
"Written %lu SPICE modules in total\n", "Written %lu SPICE modules in total\n",

View File

@ -10,6 +10,10 @@
#include "netlist_manager.h" #include "netlist_manager.h"
#include "module_manager.h" #include "module_manager.h"
#include "openfpga_arch.h" #include "openfpga_arch.h"
#include "mux_library.h"
#include "vpr_context.h"
#include "vpr_device_annotation.h"
#include "device_rr_gsb.h"
#include "fabric_spice_options.h" #include "fabric_spice_options.h"
/******************************************************************** /********************************************************************
@ -22,6 +26,10 @@ namespace openfpga {
int fpga_fabric_spice(const ModuleManager& module_manager, int fpga_fabric_spice(const ModuleManager& module_manager,
NetlistManager& netlist_manager, NetlistManager& netlist_manager,
const Arch& openfpga_arch, const Arch& openfpga_arch,
const MuxLibrary& mux_lib,
const DeviceContext &device_ctx,
const VprDeviceAnnotation &device_annotation,
const DeviceRRGSB &device_rr_gsb,
const FabricSpiceOption& options); const FabricSpiceOption& options);
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -0,0 +1,86 @@
/********************************************************************
* This file includes functions that are used to generate SPICE files
* or code blocks, with a focus on
* `include user-defined or auto-generated netlists in SPICE format
*******************************************************************/
#include <fstream>
/* Headers from vtrutil library */
#include "vtr_assert.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "openfpga_naming.h"
#include "circuit_library_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_auxiliary_netlists.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Local constant variables
*******************************************************************/
/********************************************************************
* Print a file that includes all the fabric netlists
* that have been generated and user-defined.
* This does NOT include any testbenches!
* Some netlists are open to compile under specific preprocessing flags
*******************************************************************/
void print_spice_fabric_include_netlist(const NetlistManager& netlist_manager,
const std::string& src_dir,
const CircuitLibrary& circuit_lib) {
std::string spice_fname = src_dir + std::string(FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME);
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
/* Validate the file stream */
check_file_stream(spice_fname.c_str(), fp);
/* Print the title */
print_spice_file_header(fp, std::string("Fabric Netlist Summary"));
/* Include all the user-defined netlists */
print_spice_comment(fp, std::string("Include user-defined netlists"));
for (const std::string& user_defined_netlist : find_circuit_library_unique_spice_netlists(circuit_lib)) {
print_spice_include_netlist(fp, user_defined_netlist);
}
/* Include all the primitive modules */
print_spice_comment(fp, std::string("Include primitive module netlists"));
for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::SUBMODULE_NETLIST)) {
print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id));
}
fp << std::endl;
/* Include all the CLB, heterogeneous block modules */
print_spice_comment(fp, std::string("Include logic block netlists"));
for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::LOGIC_BLOCK_NETLIST)) {
print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id));
}
fp << std::endl;
/* Include all the routing architecture modules */
print_spice_comment(fp, std::string("Include routing module netlists"));
for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::ROUTING_MODULE_NETLIST)) {
print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id));
}
fp << std::endl;
/* Include FPGA top module */
print_spice_comment(fp, std::string("Include fabric top-level netlists"));
for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::TOP_MODULE_NETLIST)) {
print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id));
}
fp << std::endl;
/* Close the file stream */
fp.close();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,24 @@
#ifndef SPICE_AUXILIARY_NETLISTS_H
#define SPICE_AUXILIARY_NETLISTS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include "circuit_library.h"
#include "netlist_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_spice_fabric_include_netlist(const NetlistManager& netlist_manager,
const std::string& src_dir,
const CircuitLibrary& circuit_lib);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,897 @@
/************************************************
* This file includes functions on
* outputting SPICE netlists for inverters and buffers
***********************************************/
#include <fstream>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "circuit_library_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_buffer.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Generate the SPICE modeling for a power-gated inverter
*
* This function is created to be shared by inverter and buffer SPICE netlist writer
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* an inverter. Any preprocessing or subckt definition should not be included!
*******************************************************************/
static
int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& output_port_name,
const CircuitLibrary& circuit_lib,
const CircuitPortId& enb_port,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Write power-gating transistor pairs using the technology model
* Note that for a mulit-bit power gating port, we should cascade the transistors
*/
bool first_enb_pin = true;
size_t last_enb_pin;
for (const auto& power_gate_pin : circuit_lib.pins(enb_port)) {
BasicPort enb_pin(circuit_lib.port_prefix(enb_port), power_gate_pin, power_gate_pin);
fp << "Xpmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " ";
/* For the first pin, we should connect it to local VDD*/
if (true == first_enb_pin) {
fp << output_port_name << "_pmos_pg_" << power_gate_pin << " ";
fp << generate_spice_port(enb_pin) << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
first_enb_pin = false;
} else {
VTR_ASSERT_SAFE(false == first_enb_pin);
fp << output_port_name << "_pmos_pg_" << last_enb_pin << " ";
fp << generate_spice_port(enb_pin) << " ";
fp << output_port_name << "_pmos_pg_" << power_gate_pin << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
}
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
/* Cache the last pin*/
last_enb_pin = power_gate_pin;
}
/* Write transistor pairs using the technology model */
fp << "Xpmos_" << trans_name_postfix << " ";
fp << output_port_name << " ";
fp << input_port_name << " ";
fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE modeling for the NMOS part of a power-gated inverter
*
* This function is created to be shared by inverter and buffer SPICE netlist writer
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* an inverter. Any preprocessing or subckt definition should not be included!
*******************************************************************/
static
int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& output_port_name,
const CircuitLibrary& circuit_lib,
const CircuitPortId& en_port,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
bool first_en_pin = true;
size_t last_en_pin;
for (const auto& power_gate_pin : circuit_lib.pins(en_port)) {
BasicPort en_pin(circuit_lib.port_prefix(en_port), power_gate_pin, power_gate_pin);
fp << "Xnmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " ";
/* For the first pin, we should connect it to local VDD*/
if (true == first_en_pin) {
fp << output_port_name << "_nmos_pg_" << power_gate_pin << " ";
fp << generate_spice_port(en_pin) << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
first_en_pin = false;
} else {
VTR_ASSERT_SAFE(false == first_en_pin);
fp << output_port_name << "_nmos_pg_" << last_en_pin << " ";
fp << circuit_lib.port_prefix(en_port) << " ";
fp << output_port_name << "_nmos_pg_" << power_gate_pin << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
}
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
/* Cache the last pin*/
last_en_pin = power_gate_pin;
}
fp << "Xnmos_" << trans_name_postfix << " ";
fp << output_port_name << " ";
fp << input_port_name << " ";
fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE subckt for a power gated inverter
* The Enable signal controlled the power gating
*
* Note:
* - This function supports multi-bit power gating
*
* Schematic
* LVDD
* |
* -
* ENb[0] -o||
* -
* |
* -
* ENb[1] -o||
* -
* |
*
* ... <More control signals if available in the port>
*
* |
* -
* +-o||
* | -
* | |
* in-->+ +--> OUT
* | |
* | -
* +--||
* -
*
* ... <More control signals if available in the port>
*
* |
* -
* EN[1] -||
* -
* |
* -
* EN[0] -||
* -
* |
* LGND
*
*******************************************************************/
static
int print_spice_powergated_inverter_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 1 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
/* If the circuit model is power-gated, we need to find at least one global config_enable signals */
VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model));
CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model);
CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model);
VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port));
VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port));
int status = CMD_EXEC_SUCCESS;
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = circuit_lib.buffer_size(circuit_model)
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
status = print_spice_powergated_inverter_pmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
circuit_lib,
enb_port,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = circuit_lib.buffer_size(circuit_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
status = print_spice_powergated_inverter_nmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
circuit_lib,
en_port,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE modeling for the PMOS part of a regular inverter
*
* This function is created to be shared by inverter and buffer SPICE netlist writer
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* an inverter. Any preprocessing or subckt definition should not be included!
*******************************************************************/
static
int print_spice_regular_inverter_pmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Write transistor pairs using the technology model */
fp << "Xpmos_" << trans_name_postfix << " ";
fp << output_port_name << " ";
fp << input_port_name << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE modeling for the NMOS part of a regular inverter
*
* This function is created to be shared by inverter and buffer SPICE netlist writer
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* an inverter. Any preprocessing or subckt definition should not be included!
*******************************************************************/
static
int print_spice_regular_inverter_nmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
fp << "Xnmos_" << trans_name_postfix << " ";
fp << output_port_name << " ";
fp << input_port_name << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE subckt for a regular inverter
*
* Note:
* - This function does NOT support power-gating
* It should be managed in a separated function
*
* Schematic
* LVDD
* |
* -
* +-o||
* | -
* | |
* in-->+ +--> OUT
* | |
* | -
* +--||
* -
* |
* LGND
*
*******************************************************************/
static
int print_spice_regular_inverter_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 1 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = circuit_lib.buffer_size(circuit_model)
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
status = print_spice_regular_inverter_pmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = circuit_lib.buffer_size(circuit_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
status = print_spice_regular_inverter_nmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
/********************************************************************
* Generate the SPICE subckt for an inverter
* Branch on the different circuit topologies
*******************************************************************/
int print_spice_inverter_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
int status = CMD_EXEC_SUCCESS;
if (true == circuit_lib.is_power_gated(circuit_model)) {
status = print_spice_powergated_inverter_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
} else {
VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model));
status = print_spice_regular_inverter_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
}
return status;
}
/********************************************************************
* Generate the SPICE subckt for a power-gated buffer
* which contains at least 2 stages
*
* Schematic of a multi-stage buffer
*
* LVDD LVDD
* | |
* - -
* ENb[0] -o|| ENb[0] -o||
* - -
* | |
* - -
* ENb[1] -o|| ENb[1] -o||
* - -
* | |
*
* ... <More control signals if available in the port>
*
* | |
* - -
* +-o|| +-o||
* | - | -
* | | | |
* in-->+ +-- ... ---+---->+---> out
* | | | |
* | - | -
* +--|| +--||
* - -
* | |
*
* ... <More control signals if available in the port>
*
* | |
* - -
* EN[0] -|| EN[0] -||
* - -
* | |
* - -
* EN[1] -|| EN[1] -||
* - -
* | |
* | |
* LGND LGND
*
*******************************************************************/
static
int print_spice_powergated_buffer_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 1 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
/* If the circuit model is power-gated, we need to find at least one global config_enable signals */
VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model));
CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model);
CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model);
VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port));
VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port));
int status = CMD_EXEC_SUCCESS;
/* Buffers must have >= 2 stages */
VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model));
/* Build the array denoting width of inverters per stage */
std::vector<float> buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1);
for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) {
buffer_widths[level] = circuit_lib.buffer_size(circuit_model)
* std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level);
}
for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) {
std::string input_port_name = circuit_lib.port_prefix(input_ports[0]);
std::string output_port_name = circuit_lib.port_prefix(output_ports[0]);
/* Special for first stage: output port should be an intermediate node
* Special for rest of stages: input port should be the output of previous stage
*/
if (0 == level) {
output_port_name += std::string("_level") + std::to_string(level);
} else {
VTR_ASSERT(0 < level);
input_port_name += std::string("_level") + std::to_string(level - 1);
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = buffer_widths[level]
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin);
status = print_spice_powergated_inverter_pmos_modeling(fp,
name_postfix,
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
circuit_lib,
enb_port,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = buffer_widths[level]
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin);
status = print_spice_powergated_inverter_nmos_modeling(fp,
name_postfix,
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
circuit_lib,
en_port,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE subckt for a regular buffer
* which contains at least 2 stages
*
* Note:
* - This function does NOT support power-gating
* It should be managed in a separated function
*
* Schematic of a multi-stage buffer
*
* LVDD LVDD
* | |
* - -
* +-o|| +-o||
* | - | -
* | | | |
* in-->+ +-- ... ---+---->+---> out
* | | | |
* | - | -
* +--|| +--||
* - -
* | |
* LGND LGND
*
*******************************************************************/
static
int print_spice_regular_buffer_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 1 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Buffers must have >= 2 stages */
VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model));
/* Build the array denoting width of inverters per stage */
std::vector<float> buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1);
for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) {
buffer_widths[level] = circuit_lib.buffer_size(circuit_model)
* std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level);
}
for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) {
std::string input_port_name = circuit_lib.port_prefix(input_ports[0]);
std::string output_port_name = circuit_lib.port_prefix(output_ports[0]);
/* Special for first stage: output port should be an intermediate node
* Special for rest of stages: input port should be the output of previous stage
*/
if (0 == level) {
output_port_name += std::string("_level") + std::to_string(level);
} else {
VTR_ASSERT(0 < level);
input_port_name += std::string("_level") + std::to_string(level - 1);
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = buffer_widths[level]
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin);
status = print_spice_regular_inverter_pmos_modeling(fp,
name_postfix,
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = buffer_widths[level]
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin);
status = print_spice_regular_inverter_nmos_modeling(fp,
name_postfix,
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
/********************************************************************
* Generate the SPICE subckt for an buffer
* which consists of multiple stage of inverters
*******************************************************************/
int print_spice_buffer_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
int status = CMD_EXEC_SUCCESS;
if (true == circuit_lib.is_power_gated(circuit_model)) {
status = print_spice_powergated_buffer_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
} else {
VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model));
status = print_spice_regular_buffer_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
}
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,39 @@
#ifndef SPICE_BUFFER_H
#define SPICE_BUFFER_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#include "module_manager.h"
#include "circuit_library.h"
#include "technology_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_inverter_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model);
int print_spice_buffer_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model);
} /* end namespace openfpga */
#endif

View File

@ -2,12 +2,27 @@
#define SPICE_CONSTANTS_H #define SPICE_CONSTANTS_H
/* global parameters for dumping spice netlists */ /* global parameters for dumping spice netlists */
constexpr size_t SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE = 10;
constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp";
constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper";
constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp";
constexpr char* ESSENTIALS_SPICE_FILE_NAME = "inv_buf_passgate.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp";
constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp";
constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp";
constexpr char* MEMORIES_SPICE_FILE_NAME = "memories.sp";
constexpr char* FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME = "fabric_netlists.sp";
constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD";
constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS";
constexpr char* SPICE_MUX_BASIS_POSTFIX = "_basis";
constexpr char* SPICE_MEM_POSTFIX = "_mem";
constexpr char* SB_SPICE_FILE_NAME_PREFIX = "sb_";
constexpr char* LOGICAL_MODULE_SPICE_FILE_NAME_PREFIX = "logical_tile_";
constexpr char* GRID_SPICE_FILE_NAME_PREFIX = "grid_";
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -18,8 +18,8 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
int print_spice_transistor_wrapper(NetlistManager& netlist_manager, int print_spice_supply_wrappers(NetlistManager& netlist_manager,
const TechnologyLibrary& tech_lib, const ModuleManager& module_manager,
const std::string& submodule_dir); const std::string& submodule_dir);
int print_spice_essential_gates(NetlistManager& netlist_manager, int print_spice_essential_gates(NetlistManager& netlist_manager,

View File

@ -0,0 +1,434 @@
/********************************************************************
* This file includes functions to print SPICE subckts for a Grid
* (CLBs, I/Os, heterogeneous blocks etc.)
*******************************************************************/
/* System header files */
#include <vector>
#include <fstream>
/* Headers from vtrutil library */
#include "vtr_geometry.h"
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from readarch library */
#include "physical_types.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "openfpga_side_manager.h"
/* Headers from vpr library */
#include "vpr_utils.h"
#include "openfpga_reserved_words.h"
#include "openfpga_naming.h"
#include "openfpga_physical_tile_utils.h"
#include "pb_type_utils.h"
#include "circuit_library_utils.h"
#include "module_manager_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_grid.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print SPICE subckts of a primitive node in the pb_graph_node graph
* This generic function can support all the different types of primitive nodes
* i.e., Look-Up Tables (LUTs), Flip-flops (FFs) and hard logic blocks such as adders.
*
* The SPICE subckt will consist of two parts:
* 1. Logic module of the primitive node
* This module performs the logic function of the block
* 2. Memory module of the primitive node
* This module stores the configuration bits for the logic module
* if the logic module is a programmable resource, such as LUT
*
* SPICE subckt structure:
*
* Primitive block
* +---------------------------------------+
* | |
* | +---------+ +---------+ |
* in |----->| |--->| |<------|configuration lines
* | | Logic |... | Memory | |
* out|<-----| |--->| | |
* | +---------+ +---------+ |
* | |
* +---------------------------------------+
*
*******************************************************************/
static
void print_spice_primitive_block(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& subckt_dir,
t_pb_graph_node* primitive_pb_graph_node,
const bool& verbose) {
/* Ensure a valid pb_graph_node */
if (nullptr == primitive_pb_graph_node) {
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid primitive_pb_graph_node!\n");
exit(1);
}
/* Give a name to the Verilog netlist */
/* Create the file name for Verilog */
std::string spice_fname(subckt_dir
+ generate_logical_tile_netlist_name(std::string(), primitive_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX))
);
VTR_LOG("Writing SPICE netlist '%s' for primitive pb_type '%s' ...",
spice_fname.c_str(), primitive_pb_graph_node->pb_type->name);
VTR_LOGV(verbose, "\n");
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("SPICE subckts for primitive pb_type: " + std::string(primitive_pb_graph_node->pb_type->name)));
/* Generate the module name for this primitive pb_graph_node*/
std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type);
/* Create a module of the primitive LUT and register it to module manager */
ModuleId primitive_module = module_manager.find_module(primitive_module_name);
/* Ensure that the module has been created and thus unique! */
VTR_ASSERT(true == module_manager.valid_module_id(primitive_module));
VTR_LOGV(verbose,
"Writing SPICE codes of logical tile primitive block '%s'...",
module_manager.module_name(primitive_module).c_str());
/* Write the spice module */
write_spice_subckt_to_file(fp, module_manager, primitive_module);
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST);
VTR_LOGV(verbose, "Done\n");
}
/********************************************************************
* Print SPICE subckts of physical blocks inside a grid (CLB, I/O. etc.)
* This function will traverse the graph of complex logic block (t_pb_graph_node)
* in a recursive way, using a Depth First Search (DFS) algorithm.
* As such, primitive physical blocks (LUTs, FFs, etc.), leaf node of the pb_graph
* will be printed out first, while the top-level will be printed out in the last
*
* Note: this function will print a unique SPICE subckt for each type of
* t_pb_graph_node, i.e., t_pb_type, in the graph, in order to enable highly
* hierarchical Verilog organization as well as simplify the Verilog file sizes.
*
* Note: DFS is the right way. Do NOT use BFS.
* DFS can guarantee that all the sub-modules can be registered properly
* to its parent in module manager
*******************************************************************/
static
void rec_print_spice_logical_tile(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const VprDeviceAnnotation& device_annotation,
const std::string& subckt_dir,
t_pb_graph_node* physical_pb_graph_node,
const bool& verbose) {
/* Check cur_pb_graph_node*/
if (nullptr == physical_pb_graph_node) {
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid physical_pb_graph_node\n");
exit(1);
}
/* Get the pb_type definition related to the node */
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
/* Find the mode that physical implementation of a pb_type */
t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type);
/* For non-leaf node in the pb_type graph:
* Recursively Depth-First Generate all the child pb_type at the level
*/
if (false == is_primitive_pb_type(physical_pb_type)) {
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
/* Go recursive to visit the children */
rec_print_spice_logical_tile(netlist_manager,
module_manager, device_annotation,
subckt_dir,
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]),
verbose);
}
}
/* For leaf node, a primitive SPICE subckt will be generated.
* Note that the primitive may be mapped to a standard cell, we force to use
* explict port mapping. This aims to avoid any port sequence issues!!!
*/
if (true == is_primitive_pb_type(physical_pb_type)) {
print_spice_primitive_block(netlist_manager,
module_manager,
subckt_dir,
physical_pb_graph_node,
verbose);
/* Finish for primitive node, return */
return;
}
/* Give a name to the Verilog netlist */
/* Create the file name for Verilog */
std::string spice_fname(subckt_dir
+ generate_logical_tile_netlist_name(std::string(), physical_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX))
);
VTR_LOG("Writing SPICE netlist '%s' for pb_type '%s' ...",
spice_fname.c_str(), physical_pb_type->name);
VTR_LOGV(verbose, "\n");
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("SPICE subckts for pb_type: " + std::string(physical_pb_type->name)));
/* Generate the name of the SPICE subckt for this pb_type */
std::string pb_module_name = generate_physical_block_module_name(physical_pb_type);
/* Register the SPICE subckt in module manager */
ModuleId pb_module = module_manager.find_module(pb_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
VTR_LOGV(verbose,
"Writing SPICE codes of pb_type '%s'...",
module_manager.module_name(pb_module).c_str());
/* Comment lines */
print_spice_comment(fp, std::string("BEGIN Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name)));
/* Write the spice module */
write_spice_subckt_to_file(fp, module_manager, pb_module);
print_spice_comment(fp, std::string("END Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name)));
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST);
VTR_LOGV(verbose, "Done\n");
}
/*****************************************************************************
* This function will create a Verilog file and print out a Verilog netlist
* for the logical tile (pb_graph/pb_type)
*****************************************************************************/
static
void print_spice_logical_tile_netlist(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const VprDeviceAnnotation& device_annotation,
const std::string& subckt_dir,
t_pb_graph_node* pb_graph_head,
const bool& verbose) {
VTR_LOG("Writing Verilog netlists for logic tile '%s' ...",
pb_graph_head->pb_type->name);
VTR_LOG("\n");
/* Print SPICE subckts for all the pb_types/pb_graph_nodes
* use a Depth-First Search Algorithm to print the sub-modules
* Note: DFS is the right way. Do NOT use BFS.
* DFS can guarantee that all the sub-modules can be registered properly
* to its parent in module manager
*/
/* Print SPICE subckts starting from the top-level pb_type/pb_graph_node, and traverse the graph in a recursive way */
rec_print_spice_logical_tile(netlist_manager,
module_manager,
device_annotation,
subckt_dir,
pb_graph_head,
verbose);
VTR_LOG("Done\n");
VTR_LOG("\n");
}
/*****************************************************************************
* This function will create a Verilog file and print out a Verilog netlist
* for a type of physical block
*
* For IO blocks:
* The param 'border_side' is required, which is specify which side of fabric
* the I/O block locates at.
*****************************************************************************/
static
void print_spice_physical_tile_netlist(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& subckt_dir,
t_physical_tile_type_ptr phy_block_type,
const e_side& border_side) {
/* Check code: if this is an IO block, the border side MUST be valid */
if (true == is_io_type(phy_block_type)) {
VTR_ASSERT(NUM_SIDES != border_side);
}
/* Give a name to the Verilog netlist */
/* Create the file name for Verilog */
std::string spice_fname(subckt_dir
+ generate_grid_block_netlist_name(std::string(GRID_MODULE_NAME_PREFIX) + std::string(phy_block_type->name),
is_io_type(phy_block_type),
border_side,
std::string(SPICE_NETLIST_FILE_POSTFIX))
);
/* Echo status */
if (true == is_io_type(phy_block_type)) {
SideManager side_manager(border_side);
VTR_LOG("Writing SPICE Netlist '%s' for physical tile '%s' at %s side ...",
spice_fname.c_str(), phy_block_type->name,
side_manager.c_str());
} else {
VTR_LOG("Writing SPICE Netlist '%s' for physical_tile '%s'...",
spice_fname.c_str(), phy_block_type->name);
}
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("SPICE subckts for physical tile: " + std::string(phy_block_type->name) + "]"));
/* Create a Verilog Module for the top-level physical block, and add to module manager */
std::string grid_module_name = generate_grid_block_module_name(std::string(GRID_SPICE_FILE_NAME_PREFIX), std::string(phy_block_type->name), is_io_type(phy_block_type), border_side);
ModuleId grid_module = module_manager.find_module(grid_module_name);
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
/* Write the spice module */
print_spice_comment(fp, std::string("BEGIN Grid SPICE subckt: " + module_manager.module_name(grid_module)));
write_spice_subckt_to_file(fp, module_manager, grid_module);
print_spice_comment(fp, std::string("END Grid SPICE subckt: " + module_manager.module_name(grid_module)));
/* Add an empty line as a splitter */
fp << std::endl;
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST);
VTR_LOG("Done\n");
}
/*****************************************************************************
* Create logic block modules in a compact way:
* 1. Only one module for each I/O on each border side (IO_TYPE)
* 2. Only one module for each CLB (FILL_TYPE)
* 3. Only one module for each heterogeneous block
****************************************************************************/
void print_spice_grids(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceContext& device_ctx,
const VprDeviceAnnotation& device_annotation,
const std::string& subckt_dir,
const bool& verbose) {
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
std::vector<std::string> netlist_names;
/* Enumerate the types of logical tiles, and build a module for each
* Write modules for all the pb_types/pb_graph_nodes
* use a Depth-First Search Algorithm to print the sub-modules
* Note: DFS is the right way. Do NOT use BFS.
* DFS can guarantee that all the sub-modules can be registered properly
* to its parent in module manager
*/
VTR_LOG("Writing logical tiles...");
VTR_LOGV(verbose, "\n");
for (const t_logical_block_type& logical_tile : device_ctx.logical_block_types) {
/* Bypass empty pb_graph */
if (nullptr == logical_tile.pb_graph_head) {
continue;
}
print_spice_logical_tile_netlist(netlist_manager,
module_manager,
device_annotation,
subckt_dir,
logical_tile.pb_graph_head,
verbose);
}
VTR_LOG("Writing logical tiles...");
VTR_LOG("Done\n");
VTR_LOG("\n");
/* Enumerate the types of physical tiles
* Use the logical tile module to build the physical tiles
*/
VTR_LOG("Building physical tiles...");
VTR_LOGV(verbose, "\n");
for (const t_physical_tile_type& physical_tile : device_ctx.physical_tile_types) {
/* Bypass empty type or nullptr */
if (true == is_empty_type(&physical_tile)) {
continue;
} else if (true == is_io_type(&physical_tile)) {
/* Special for I/O block:
* We will search the grids and see where the I/O blocks are located:
* - If a I/O block locates on border sides of FPGA fabric:
* i.e., one or more from {TOP, RIGHT, BOTTOM, LEFT},
* we will generate one module for each border side
* - If a I/O block locates in the center of FPGA fabric:
* we will generate one module with NUM_SIDES (same treatment as regular grids)
*/
std::set<e_side> io_type_sides = find_physical_io_tile_located_sides(device_ctx.grid,
&physical_tile);
for (const e_side& io_type_side : io_type_sides) {
print_spice_physical_tile_netlist(netlist_manager,
module_manager,
subckt_dir,
&physical_tile,
io_type_side);
}
continue;
} else {
/* For CLB and heterogenenous blocks */
print_spice_physical_tile_netlist(netlist_manager,
module_manager,
subckt_dir,
&physical_tile,
NUM_SIDES);
}
}
VTR_LOG("Building physical tiles...");
VTR_LOG("Done\n");
VTR_LOG("\n");
/* Output a header file for all the logic blocks */
/*
std::string grid_spice_fname(LOGIC_BLOCK_VERILOG_FILE_NAME);
VTR_LOG("Writing header file for grid SPICE subckts '%s' ...",
grid_spice_fname.c_str());
print_spice_netlist_include_header_file(netlist_names,
subckt_dir.c_str(),
grid_spice_fname.c_str());
VTR_LOG("Done\n");
*/
}
} /* end namespace openfpga */

View File

@ -0,0 +1,30 @@
#ifndef SPICE_GRID_H
#define SPICE_GRID_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include "vpr_context.h"
#include "module_manager.h"
#include "netlist_manager.h"
#include "vpr_device_annotation.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_spice_grids(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceContext& device_ctx,
const VprDeviceAnnotation& device_annotation,
const std::string& subckt_dir,
const bool& verbose);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,346 @@
/************************************************
* This file includes functions on
* outputting SPICE netlists for logic gates:
* - N-input AND gate
* - N-input OR gate
***********************************************/
#include <fstream>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "circuit_library_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_transistor_wrapper.h"
#include "spice_logic_gate.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Generate the SPICE subckt for a N-input AND gate
*
* Schematic
*
* VDD VDD VDD
* | | |
* - - -
* in0 -o|| in1 -o|| ... in[N-1] -o||
* - - -
* | | |
* +----+-----+- ... -------------+
* |
* -
* in0 -||
* -
* |
* -
* in1 -||
* -
* |
* ...
* |
* -
* in[N-1] -||
* -
* |
* GND
*******************************************************************/
int print_spice_and_gate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There are at least 2 input ports and 1 output port,
* each size of which is 1
*/
VTR_ASSERT(2 <= input_ports.size());
for (const auto& input_port : input_ports) {
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
}
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = 1. /* TODO: allow users to define gate strength */
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
/* Output the PMOS network */
for (const auto& input_port : input_ports) {
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
status = print_spice_generic_pmos_modeling(fp,
std::to_string(ibin),
std::string(SPICE_SUBCKT_VDD_PORT_NAME),
circuit_lib.port_prefix(input_port),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = 1. /* TODO: allow users to define gate strength */
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
/* Output the NMOS network */
for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) {
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
/* Depending on the input id, we assign different port names to source/drain */
std::string source_port_name;
std::string drain_port_name;
if (0 == input_id) {
/* First transistor should connect to the output port and an internal node */
source_port_name = circuit_lib.port_prefix(output_ports[0]);
drain_port_name = std::string("internal_node") + std::to_string(input_id);
} else if (input_id == input_ports.size() - 1) {
/* Last transistor should connect to an internal node and GND */
source_port_name = std::string("internal_node") + std::to_string(input_id - 1);
drain_port_name = std::string(SPICE_SUBCKT_GND_PORT_NAME);
} else {
/* Other transistors should connect to two internal nodes */
source_port_name = std::string("internal_node") + std::to_string(input_id - 1);
drain_port_name = std::string("internal_node") + std::to_string(input_id);
}
status = print_spice_generic_nmos_modeling(fp,
std::to_string(ibin),
source_port_name,
circuit_lib.port_prefix(input_ports[input_id]),
drain_port_name,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
/********************************************************************
* Generate the SPICE subckt for a N-input OR gate
*
* Schematic
*
*
* VDD
* |
* -
* in0 -o||
* -
* |
* -
* in1 -o||
* -
* |
* ...
* |
* -
* in[N-1] -o||
* -
* |
* +----+-----+- ... -------------+
* | | |
* - - -
* in0 -|| in1 -|| ... in[N-1] -||
* - - -
* | | |
* GND GND GND
*******************************************************************/
int print_spice_or_gate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There are at least 2 input ports and 1 output port,
* each size of which is 1
*/
VTR_ASSERT(2 <= input_ports.size());
for (const auto& input_port : input_ports) {
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
}
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = 1. /* TODO: allow users to define gate strength */
* tech_lib.model_pn_ratio(tech_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
/* Output the PMOS network */
for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) {
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
/* Depending on the input id, we assign different port names to source/drain */
std::string source_port_name;
std::string drain_port_name;
if (0 == input_id) {
/* First transistor should connect to the output port and an internal node */
source_port_name = circuit_lib.port_prefix(output_ports[0]);
drain_port_name = std::string("internal_node") + std::to_string(input_id);
} else if (input_id == input_ports.size() - 1) {
/* Last transistor should connect to an internal node and GND */
source_port_name = std::string("internal_node") + std::to_string(input_id - 1);
drain_port_name = std::string(SPICE_SUBCKT_VDD_PORT_NAME);
} else {
/* Other transistors should connect to two internal nodes */
source_port_name = std::string("internal_node") + std::to_string(input_id - 1);
drain_port_name = std::string("internal_node") + std::to_string(input_id);
}
status = print_spice_generic_pmos_modeling(fp,
std::to_string(ibin),
source_port_name,
circuit_lib.port_prefix(input_ports[input_id]),
drain_port_name,
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = 1. /* TODO: allow users to define gate strength */
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
/* Output the NMOS network */
for (const auto& input_port : input_ports) {
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
status = print_spice_generic_nmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(output_ports[0]),
circuit_lib.port_prefix(input_port),
std::string(SPICE_SUBCKT_GND_PORT_NAME),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,38 @@
#ifndef SPICE_LOGIC_GATE_H
#define SPICE_LOGIC_GATE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#include "module_manager.h"
#include "circuit_library.h"
#include "technology_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_and_gate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model);
int print_spice_or_gate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,82 @@
/********************************************************************
* This file includes functions to generate SPICE subcircuits for LUTs
********************************************************************/
#include <string>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
#include "mux_graph.h"
#include "module_manager.h"
#include "mux_utils.h"
#include "openfpga_naming.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_lut.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print SPICE modules for the Look-Up Tables (LUTs)
* in the circuit library
********************************************************************/
int print_spice_submodule_luts(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir) {
int status = CMD_EXEC_SUCCESS;
std::string spice_fname = submodule_dir + std::string(LUTS_SPICE_FILE_NAME);
std::fstream fp;
/* Create the file stream */
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
/* Check if the file stream if valid or not */
check_file_stream(spice_fname.c_str(), fp);
/* Create file */
VTR_LOG("Writing SPICE netlist for LUTs '%s'...",
spice_fname.c_str());
print_spice_file_header(fp, "Look-Up Tables");
/* Search for each LUT circuit model */
for (const auto& lut_model : circuit_lib.models()) {
/* Bypass user-defined and non-LUT modules */
if ( (!circuit_lib.model_spice_netlist(lut_model).empty())
|| (CIRCUIT_MODEL_LUT != circuit_lib.model_type(lut_model)) ) {
continue;
}
/* Find the module id */
ModuleId lut_module = module_manager.find_module(circuit_lib.model_name(lut_model));
VTR_ASSERT(true == module_manager.valid_module_id(lut_module));
write_spice_subckt_to_file(fp, module_manager, lut_module);
}
/* Close the file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST);
VTR_LOG("Done\n");
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,28 @@
#ifndef SPICE_LUT_H
#define SPICE_LUT_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include <string>
#include "circuit_library.h"
#include "module_manager.h"
#include "netlist_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_submodule_luts(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,196 @@
/*********************************************************************
* This file includes functions to generate SPICE sub-circuits for
* the memories that are affiliated to multiplexers and other programmable
* circuit models, such as IOPADs, LUTs, etc.
********************************************************************/
#include <string>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
#include "mux_graph.h"
#include "module_manager.h"
#include "circuit_library_utils.h"
#include "mux_utils.h"
#include "openfpga_naming.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_memory.h"
/* begin namespace openfpga */
namespace openfpga {
/*********************************************************************
* Generate Verilog modules for the memories that are used
* by multiplexers
*
* +----------------+
* mem_in --->| Memory Module |---> mem_out
* +----------------+
* | | ... | |
* v v v v SRAM ports of multiplexer
* +---------------------+
* in--->| Multiplexer Module |---> out
* +---------------------+
********************************************************************/
static
void print_spice_mux_memory_module(const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
std::fstream& fp,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph) {
/* Multiplexers built with different technology is in different organization */
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS: {
/* Generate module name */
std::string 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(SPICE_MEM_POSTFIX));
ModuleId mem_module = module_manager.find_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
/* Write the module content in Verilog format */
write_spice_subckt_to_file(fp, module_manager, mem_module);
/* Add an empty line as a splitter */
fp << std::endl;
break;
}
case CIRCUIT_MODEL_DESIGN_RRAM:
/* We do not need a memory submodule for RRAM MUX,
* RRAM are embedded in the datapath
* TODO: generate local encoders for RRAM-based multiplexers here!!!
*/
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid design technology of multiplexer '%s'\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
}
/*********************************************************************
* Generate Verilog modules for
* the memories that are affiliated to multiplexers and other programmable
* circuit models, such as IOPADs, LUTs, etc.
*
* We keep the memory modules separated from the multiplexers and other
* programmable circuit models, for the sake of supporting
* various configuration schemes.
* By following such organiztion, the Verilog modules of the circuit models
* implements the functionality (circuit logic) only, while the memory Verilog
* modules implements the memory circuits as well as configuration protocols.
* For example, the local decoders of multiplexers are implemented in the
* memory modules.
* Take another example, the memory circuit can implement the scan-chain or
* memory-bank organization for the memories.
********************************************************************/
int print_spice_submodule_memories(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const MuxLibrary& mux_lib,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir) {
int status = CMD_EXEC_SUCCESS;
/* Plug in with the mux subckt */
std::string spice_fname(submodule_dir + std::string(MEMORIES_SPICE_FILE_NAME));
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
/* Print out debugging information for if the file is not opened/created properly */
VTR_LOG("Writing SPICE netlist for memories '%s' ...",
spice_fname.c_str());
print_spice_file_header(fp, "Memories used in FPGA");
/* Create the memory circuits for the multiplexer */
for (auto mux : mux_lib.muxes()) {
const MuxGraph& mux_graph = mux_lib.mux_graph(mux);
CircuitModelId mux_model = mux_lib.mux_circuit_model(mux);
/* Bypass the non-MUX circuit models (i.e., LUTs).
* They should be handled in a different way
* Memory circuits of LUT includes both regular and mode-select ports
*/
if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) {
continue;
}
/* Create a Verilog module for the memories used by the multiplexer */
print_spice_mux_memory_module(module_manager, circuit_lib, fp, mux_model, mux_graph);
}
/* Create the memory circuits for non-MUX circuit models.
* In this case, the memory modules are designed to interface
* the mode-select ports
*/
for (const auto& model : circuit_lib.models()) {
/* Bypass MUXes, they have already been considered */
if (CIRCUIT_MODEL_MUX == circuit_lib.model_type(model)) {
continue;
}
/* Bypass those modules without any SRAM ports */
std::vector<CircuitPortId> sram_ports = circuit_lib.model_ports_by_type(model, CIRCUIT_MODEL_PORT_SRAM, true);
if (0 == sram_ports.size()) {
continue;
}
/* Find the name of memory module */
/* Get the total number of SRAMs */
size_t num_mems = 0;
for (const auto& port : sram_ports) {
num_mems += circuit_lib.port_size(port);
}
/* Get the circuit model for the memory circuit used by the multiplexer */
std::vector<CircuitModelId> sram_models;
for (const auto& port : sram_ports) {
CircuitModelId sram_model = circuit_lib.port_tri_state_model(port);
VTR_ASSERT(CircuitModelId::INVALID() != sram_model);
/* Found in the vector of sram_models, do not update and go to the next */
if (sram_models.end() != std::find(sram_models.begin(), sram_models.end(), sram_model)) {
continue;
}
/* sram_model not found in the vector, update the sram_models */
sram_models.push_back(sram_model);
}
/* Should have only 1 SRAM model */
VTR_ASSERT( 1 == sram_models.size() );
/* Create the module name for the memory block */
std::string module_name = generate_memory_module_name(circuit_lib, model, sram_models[0], std::string(SPICE_MEM_POSTFIX));
ModuleId mem_module = module_manager.find_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
/* Write the module content in Verilog format */
write_spice_subckt_to_file(fp, module_manager, mem_module);
/* Add an empty line as a splitter */
fp << std::endl;
}
/* Close the file stream */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST);
VTR_LOG("Done\n");
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,30 @@
#ifndef SPICE_MEMORY_H
#define SPICE_MEMORY_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include "circuit_library.h"
#include "mux_graph.h"
#include "mux_library.h"
#include "module_manager.h"
#include "netlist_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_submodule_memories(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const MuxLibrary& mux_lib,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,189 @@
/***********************************************
* This file includes functions to generate
* SPICE subcircuits for multiplexers.
* including both fundamental submodules
* such as a branch in a multiplexer
* and the full multiplexer
**********************************************/
#include <string>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from readarch library */
#include "physical_types.h"
/* Headers from readarcopenfpga library */
#include "circuit_types.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
#include "mux_graph.h"
#include "module_manager.h"
#include "mux_utils.h"
#include "circuit_library_utils.h"
#include "decoder_library_utils.h"
#include "openfpga_naming.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_mux.h"
/* begin namespace openfpga */
namespace openfpga {
/***********************************************
* Generate SPICE modeling for an branch circuit
* for a multiplexer with the given size
**********************************************/
static
void generate_spice_mux_branch_subckt(const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
std::fstream& fp,
const CircuitModelId& mux_model,
const size_t& mux_size,
const MuxGraph& mux_graph) {
std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), SPICE_MUX_BASIS_POSTFIX);
/* Multiplexers built with different technology is in different organization */
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_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(CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_lib.model(module_name)));
VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(circuit_lib.model(module_name)));
break;
}
/* Structural verilog can be easily generated by module writer */
ModuleId mux_module = module_manager.find_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
write_spice_subckt_to_file(fp, module_manager, mux_module);
/* Add an empty line as a splitter */
fp << std::endl;
break;
}
case CIRCUIT_MODEL_DESIGN_RRAM:
/* TODO: RRAM-based Multiplexer SPICE module generation */
VTR_LOGF_ERROR(__FILE__, __LINE__,
"RRAM multiplexer '%s' is not supported yet\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid design technology of multiplexer '%s'\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
}
/***********************************************
* Generate SPICE modeling for a multiplexer
* with the given graph-level description
**********************************************/
static
void generate_spice_mux_subckt(const ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
std::fstream& fp,
const CircuitModelId& mux_model,
const MuxGraph& mux_graph) {
std::string 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(""));
/* Multiplexers built with different technology is in different organization */
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS: {
/* Use Verilog writer to print the module to file */
ModuleId mux_module = module_manager.find_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
write_spice_subckt_to_file(fp, module_manager, mux_module);
/* Add an empty line as a splitter */
fp << std::endl;
break;
}
case CIRCUIT_MODEL_DESIGN_RRAM:
/* TODO: RRAM-based Multiplexer SPICE module generation */
VTR_LOGF_ERROR(__FILE__, __LINE__,
"RRAM multiplexer '%s' is not supported yet\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid design technology of multiplexer '%s'\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
}
/***********************************************
* Generate SPICE subcircuits for all the unique
* multiplexers in the FPGA device
**********************************************/
int print_spice_submodule_muxes(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const MuxLibrary& mux_lib,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir) {
int status = CMD_EXEC_SUCCESS;
std::string spice_fname(submodule_dir + std::string(MUXES_SPICE_FILE_NAME));
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
/* Print out debugging information for if the file is not opened/created properly */
VTR_LOG("Writing SPICE netlist for Multiplexers '%s' ...",
spice_fname.c_str());
print_spice_file_header(fp, "Multiplexers");
/* Generate basis sub-circuit for unique branches shared by the multiplexers */
for (auto mux : mux_lib.muxes()) {
const MuxGraph& mux_graph = mux_lib.mux_graph(mux);
CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux);
/* Create a mux graph for the branch circuit */
std::vector<MuxGraph> branch_mux_graphs = mux_graph.build_mux_branch_graphs();
/* Create branch circuits, which are N:1 one-level or 2:1 tree-like MUXes */
for (auto branch_mux_graph : branch_mux_graphs) {
generate_spice_mux_branch_subckt(module_manager, circuit_lib, fp, mux_circuit_model,
find_mux_num_datapath_inputs(circuit_lib, mux_circuit_model, mux_graph.num_inputs()),
branch_mux_graph);
}
}
/* Generate unique Verilog modules for the multiplexers */
for (auto mux : mux_lib.muxes()) {
const MuxGraph& mux_graph = mux_lib.mux_graph(mux);
CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux);
/* Create MUX circuits */
generate_spice_mux_subckt(module_manager, circuit_lib, fp, mux_circuit_model, mux_graph);
}
/* Close the file stream */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST);
VTR_LOG("Done\n");
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,31 @@
#ifndef SPICE_MUX_H
#define SPICE_MUX_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include <vector>
#include "circuit_library.h"
#include "mux_graph.h"
#include "mux_library.h"
#include "module_manager.h"
#include "netlist_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_submodule_muxes(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const MuxLibrary& mux_lib,
const CircuitLibrary& circuit_lib,
const std::string& submodule_dir);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,261 @@
/************************************************
* This file includes functions on
* outputting SPICE netlists for transmission-gates
***********************************************/
#include <fstream>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "circuit_library_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_transistor_wrapper.h"
#include "spice_passgate.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Generate the SPICE subckt for a pass-transistor
*
* Schematic
*
* sel
* |
* ===
* | |
* in -- ---out
*
*******************************************************************/
static
int print_spice_pass_transistor_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 2 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT(2 == input_ports.size());
for (const auto& input_port : input_ports) {
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
}
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = circuit_lib.pass_gate_logic_nmos_size(circuit_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
status = print_spice_generic_nmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(input_ports[1]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
/********************************************************************
* Generate the SPICE subckt for a transmission gate
*
* Schematic
*
* selb
* |
* o
* ===
* | |
* in -- ---out
* | |
* ===
* |
* sel
*
*******************************************************************/
static
int print_spice_transmission_gate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There is only 3 input port and 1 output port,
* each size of which is 1
*/
VTR_ASSERT(3 == input_ports.size());
for (const auto& input_port : input_ports) {
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
}
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
float total_pmos_width = circuit_lib.pass_gate_logic_pmos_size(circuit_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS);
int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width);
float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width);
for (int ibin = 0; ibin < num_pmos_bins; ++ibin) {
float curr_bin_width = regular_pmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_pmos_bins - 1)
&& (0. != last_pmos_bin_width)) {
curr_bin_width = last_pmos_bin_width;
}
status = print_spice_generic_pmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(input_ports[2]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
/* Consider use size/bin to compact layout:
* Try to size transistors to the max width for each bin
* The last bin may not reach the max width
*/
float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
float total_nmos_width = circuit_lib.pass_gate_logic_nmos_size(circuit_model)
* tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS);
int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width);
float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width);
for (int ibin = 0; ibin < num_nmos_bins; ++ibin) {
float curr_bin_width = regular_nmos_bin_width;
/* For last bin, we need an irregular width */
if ((ibin == num_nmos_bins - 1)
&& (0. != last_nmos_bin_width)) {
curr_bin_width = last_nmos_bin_width;
}
status = print_spice_generic_nmos_modeling(fp,
std::to_string(ibin),
circuit_lib.port_prefix(input_ports[0]),
circuit_lib.port_prefix(input_ports[1]),
circuit_lib.port_prefix(output_ports[0]),
tech_lib,
tech_model,
curr_bin_width);
if (CMD_EXEC_FATAL_ERROR == status) {
return status;
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
/********************************************************************
* Generate the SPICE subckt for a pass-gate
*
* Note:
* - This function supports both pass-transistor
* and transmission gates
*******************************************************************/
int print_spice_passgate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model) {
int status = CMD_EXEC_SUCCESS;
if (CIRCUIT_MODEL_PASS_GATE_TRANSISTOR == circuit_lib.pass_gate_logic_type(circuit_model)) {
status = print_spice_pass_transistor_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
} else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.pass_gate_logic_type(circuit_model)) {
status = print_spice_transmission_gate_subckt(fp,
module_manager, module_id,
circuit_lib, circuit_model,
tech_lib, tech_model);
}
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,30 @@
#ifndef SPICE_PASSGATE_H
#define SPICE_PASSGATE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#include "module_manager.h"
#include "circuit_library.h"
#include "technology_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_passgate_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,346 @@
/*********************************************************************
* This file includes functions that are used for
* SPICE generation of FPGA routing architecture (global routing)
*********************************************************************/
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_time.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
/* Include FPGA-Verilog header files*/
#include "openfpga_naming.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_routing.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print the sub-circuit of a connection Box (Type: [CHANX|CHANY])
* Actually it is very similiar to switch box but
* the difference is connection boxes connect Grid INPUT Pins to channels
* NOTE: direct connection between CLBs should NOT be included inside this
* module! They should be added in the top-level module as their connection
* is not limited to adjacent CLBs!!!
*
* Location of a X- and Y-direction Connection Block in FPGA fabric
* +------------+ +-------------+
* | |------>| |
* | CLB |<------| Y-direction |
* | | ... | Connection |
* | |------>| Block |
* +------------+ +-------------+
* | ^ ... | | ^ ... |
* v | v v | v
* +-------------------+ +-------------+
* --->| |--->| |
* <---| X-direction |<---| Switch |
* ...| Connection block |... | Block |
* --->| |--->| |
* +-------------------+ +-------------+
*
* Internal structure:
* This is an example of a X-direction connection block
* Note that middle output ports are shorted wire from inputs of routing tracks,
* which are also the inputs of routing multiplexer of the connection block
*
* CLB Input Pins
* (IPINs)
* ^ ^ ^
* | | ... |
* +--------------------------+
* | ^ ^ ^ |
* | | | ... | |
* | +--------------------+ |
* | | routing | |
* | | multiplexers | |
* | +--------------------+ |
* | middle outputs |
* | of routing channel |
* | ^ ^ ^ ^ ^ ^ ^ ^ |
* | | | | | ... | | | | |
* in[0] -->|------------------------->|---> out[0]
* out[1] <--|<-------------------------|<--- in[1]
* | ... |
* in[W-2] -->|------------------------->|---> out[W-2]
* out[W-1] <--|<-------------------------|<--- in[W-1]
* +--------------------------+
*
* W: routing channel width
*
********************************************************************/
static
void print_spice_routing_connection_box_unique_module(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& subckt_dir,
const RRGSB& rr_gsb,
const t_rr_type& cb_type) {
/* Create the netlist */
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
std::string spice_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX)));
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("SPICE modules for Unique Connection Blocks[" + std::to_string(rr_gsb.get_cb_x(cb_type)) + "]["+ std::to_string(rr_gsb.get_cb_y(cb_type)) + "]"));
/* Create a Verilog Module based on the circuit model, and add to module manager */
ModuleId cb_module = module_manager.find_module(generate_connection_block_module_name(cb_type, gsb_coordinate));
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
/* Write the spice module */
write_spice_subckt_to_file(fp, module_manager, cb_module);
/* Add an empty line as a splitter */
fp << std::endl;
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST);
}
/*********************************************************************
* Generate the SPICE module for a Switch Box.
* A Switch Box module consists of following ports:
* 1. Channel Y [x][y] inputs
* 2. Channel X [x+1][y] inputs
* 3. Channel Y [x][y-1] outputs
* 4. Channel X [x][y] outputs
* 5. Grid[x][y+1] Right side outputs pins
* 6. Grid[x+1][y+1] Left side output pins
* 7. Grid[x+1][y+1] Bottom side output pins
* 8. Grid[x+1][y] Top side output pins
* 9. Grid[x+1][y] Left side output pins
* 10. Grid[x][y] Right side output pins
* 11. Grid[x][y] Top side output pins
* 12. Grid[x][y+1] Bottom side output pins
*
* Location of a Switch Box in FPGA fabric:
*
* -------------- --------------
* | | | |
* | Grid | ChanY | Grid |
* | [x][y+1] | [x][y+1] | [x+1][y+1] |
* | | | |
* -------------- --------------
* ----------
* ChanX | Switch | ChanX
* [x][y] | Box | [x+1][y]
* | [x][y] |
* ----------
* -------------- --------------
* | | | |
* | Grid | ChanY | Grid |
* | [x][y] | [x][y] | [x+1][y] |
* | | | |
* -------------- --------------
*
* Switch Block pin location map
*
* Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1]
* right_pins inputs/outputs left_pins
* | ^ |
* | | |
* v v v
* +-----------------------------------------------+
* | |
* Grid[x][y+1] | | Grid[x+1][y+1]
* bottom_pins---->| |<---- bottom_pins
* | |
* ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y]
* inputs/outputs<--->| |<---> inputs/outputs
* | |
* Grid[x][y+1] | | Grid[x+1][y+1]
* top_pins---->| |<---- top_pins
* | |
* +-----------------------------------------------+
* ^ ^ ^
* | | |
* | v |
* Grid[x][y] ChanY[x][y] Grid[x+1][y]
* right_pins inputs/outputs left_pins
*
*
********************************************************************/
static
void print_spice_routing_switch_box_unique_module(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& subckt_dir,
const RRGSB& rr_gsb) {
/* Create the netlist */
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
std::string spice_fname(subckt_dir + generate_routing_block_netlist_name(SB_SPICE_FILE_NAME_PREFIX, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX)));
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("SPICE subcircuits for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]"));
/* Create a Verilog Module based on the circuit model, and add to module manager */
ModuleId sb_module = module_manager.find_module(generate_switch_block_module_name(gsb_coordinate));
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
/* Write the spice module */
write_spice_subckt_to_file(fp, module_manager, sb_module);
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST);
}
/********************************************************************
* Iterate over all the connection blocks in a device
* and build a module for each of them
*******************************************************************/
static
void print_spice_flatten_connection_block_modules(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb,
const std::string& subckt_dir,
const t_rr_type& cb_type) {
/* Build unique X-direction connection block modules */
vtr::Point<size_t> cb_range = device_rr_gsb.get_gsb_range();
for (size_t ix = 0; ix < cb_range.x(); ++ix) {
for (size_t iy = 0; iy < cb_range.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 = device_rr_gsb.get_gsb(ix, iy);
if (true != rr_gsb.is_cb_exist(cb_type)) {
continue;
}
print_spice_routing_connection_box_unique_module(netlist_manager,
module_manager,
subckt_dir,
rr_gsb, cb_type);
}
}
}
/********************************************************************
* A top-level function of this file
* Print all the modules for global routing architecture of a FPGA fabric
* in Verilog format in a flatten way:
* Each connection block and switch block will be generated as a unique module
* Covering:
* 1. Connection blocks
* 2. Switch blocks
*******************************************************************/
void print_spice_flatten_routing_modules(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb,
const std::string& subckt_dir) {
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
std::vector<std::string> netlist_names;
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
/* Build unique switch block modules */
for (size_t ix = 0; ix < sb_range.x(); ++ix) {
for (size_t iy = 0; iy < sb_range.y(); ++iy) {
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
if (true != rr_gsb.is_sb_exist()) {
continue;
}
print_spice_routing_switch_box_unique_module(netlist_manager,
module_manager,
subckt_dir,
rr_gsb);
}
}
print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANX);
print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANY);
/*
VTR_LOG("Writing header file for routing submodules '%s'...",
ROUTING_VERILOG_FILE_NAME);
print_spice_netlist_include_header_file(netlist_names,
subckt_dir.c_str(),
ROUTING_VERILOG_FILE_NAME);
VTR_LOG("Done\n");
VTR_LOG("\n");
*/
}
/********************************************************************
* A top-level function of this file
* Print all the unique modules for global routing architecture of a FPGA fabric
* in Verilog format, including:
* 1. Connection blocks
* 2. Switch blocks
*
* Note: this function SHOULD be called only when
* the option compact_routing_hierarchy is turned on!!!
*******************************************************************/
void print_spice_unique_routing_modules(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb,
const std::string& subckt_dir) {
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
std::vector<std::string> netlist_names;
/* Build unique switch block modules */
for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) {
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(isb);
print_spice_routing_switch_box_unique_module(netlist_manager,
module_manager,
subckt_dir,
unique_mirror);
}
/* Build unique X-direction connection block modules */
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) {
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb);
print_spice_routing_connection_box_unique_module(netlist_manager,
module_manager,
subckt_dir,
unique_mirror, CHANX);
}
/* Build unique X-direction connection block modules */
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) {
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb);
print_spice_routing_connection_box_unique_module(netlist_manager,
module_manager,
subckt_dir,
unique_mirror, CHANY);
}
/*
VTR_LOG("Writing header file for routing submodules '%s'...",
ROUTING_VERILOG_FILE_NAME);
print_spice_netlist_include_header_file(netlist_names,
subckt_dir.c_str(),
ROUTING_VERILOG_FILE_NAME);
VTR_LOG("Done\n");
*/
VTR_LOG("\n");
}
} /* end namespace openfpga */

View File

@ -0,0 +1,32 @@
#ifndef SPICE_ROUTING_H
#define SPICE_ROUTING_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include "mux_library.h"
#include "module_manager.h"
#include "netlist_manager.h"
#include "device_rr_gsb.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_spice_flatten_routing_modules(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb,
const std::string& subckt_dir);
void print_spice_unique_routing_modules(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb,
const std::string& subckt_dir);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,475 @@
/********************************************************************
* This file includes functions to write a SPICE module
* based on its definition in Module Manager
*
* Note that SPICE writer functions are just an outputter for the
* module definition.
* You should NOT modify any content of the module manager
* Please use const keyword to restrict this!
*******************************************************************/
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
/* Headers from openfpgautil library */
#include "openfpga_port.h"
#include "openfpga_digest.h"
#include "openfpga_naming.h"
#include "module_manager_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Generate the name of a local wire for a undriven port inside SPICE
* module
*******************************************************************/
static
std::string generate_spice_undriven_local_wire_name(const ModuleManager& module_manager,
const ModuleId& parent,
const ModuleId& child,
const size_t& instance_id,
const ModulePortId& child_port_id) {
std::string wire_name;
if (!module_manager.instance_name(parent, child, instance_id).empty()) {
wire_name = module_manager.instance_name(parent, child, instance_id);
} else {
wire_name = module_manager.module_name(parent) + std::string("_") + std::to_string(instance_id);
wire_name += std::string("_");
}
wire_name += std::string("_undriven_");
wire_name += module_manager.module_port(child, child_port_id).get_name();
return wire_name;
}
/********************************************************************
* Name a net for a local wire for a SPICE subckt
* 1. If this is a local wire, name it after the <src_module_name>_<instance_id>_<src_port_name>
* 2. If this is not a local wire, name it after the port name of parent module
*
* In addition, it will assign the pin index as well
*
* Restriction: this function requires each net has single driver
* which is definitely always true in circuits.
*******************************************************************/
static
BasicPort generate_spice_port_for_module_net(const ModuleManager& module_manager,
const ModuleId& module_id,
const ModuleNetId& module_net) {
/* Check all the sink modules of the net,
* if we have a source module is the current module, this is not local wire
*/
for (ModuleNetSrcId src_id : module_manager.module_net_sources(module_id, module_net)) {
if (module_id == module_manager.net_source_modules(module_id, module_net)[src_id]) {
/* Here, this is not a local wire, return the port name of the src_port */
ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[src_id];
size_t src_pin_index = module_manager.net_source_pins(module_id, module_net)[src_id];
return BasicPort(module_manager.module_port(module_id, net_src_port).get_name(), src_pin_index, src_pin_index);
}
}
/* Check all the sink modules of the net */
for (ModuleNetSinkId sink_id : module_manager.module_net_sinks(module_id, module_net)) {
if (module_id == module_manager.net_sink_modules(module_id, module_net)[sink_id]) {
/* Here, this is not a local wire, return the port name of the sink_port */
ModulePortId net_sink_port = module_manager.net_sink_ports(module_id, module_net)[sink_id];
size_t sink_pin_index = module_manager.net_sink_pins(module_id, module_net)[sink_id];
return BasicPort(module_manager.module_port(module_id, net_sink_port).get_name(), sink_pin_index, sink_pin_index);
}
}
/* Reach here, this is a local wire */
std::string net_name;
/* Each net must only one 1 source */
VTR_ASSERT(1 == module_manager.net_source_modules(module_id, module_net).size());
/* Get the source module */
ModuleId net_src_module = module_manager.net_source_modules(module_id, module_net)[ModuleNetSrcId(0)];
/* Get the instance id */
size_t net_src_instance = module_manager.net_source_instances(module_id, module_net)[ModuleNetSrcId(0)];
/* Get the port id */
ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[ModuleNetSrcId(0)];
/* Get the pin id */
size_t net_src_pin = module_manager.net_source_pins(module_id, module_net)[ModuleNetSrcId(0)];
/* Load user-defined name if we have it */
if (false == module_manager.net_name(module_id, module_net).empty()) {
net_name = module_manager.net_name(module_id, module_net);
} else {
net_name = module_manager.module_name(net_src_module);
net_name += std::string("_") + std::to_string(net_src_instance) + std::string("_");
net_name += module_manager.module_port(net_src_module, net_src_port).get_name();
}
return BasicPort(net_name, net_src_pin, net_src_pin);
}
/********************************************************************
* Print a SPICE 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_spice_subckt_output_short_connection(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const ModuleNetId& module_net) {
/* Ensure a valid file stream */
VTR_ASSERT(true == valid_file_stream(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 */
VTR_ASSERT(src_port.get_width() == sink_port.get_width());
for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) {
BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]);
BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]);
print_spice_short_connection(fp,
generate_spice_port(src_spice_pin),
generate_spice_port(sink_spice_pin));
}
}
}
/********************************************************************
* Print a SPICE wire connection
* We search all the sources of the net,
* if we find a module input, we try to find a module output
* among the sinks of the net
* If we find such a pair, we print a wire connection
*******************************************************************/
static
void print_spice_subckt_local_short_connection(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const ModuleNetId& module_net) {
/* Ensure a valid file stream */
VTR_ASSERT(true == valid_file_stream(fp));
for (ModuleNetSrcId net_src : module_manager.module_net_sources(module_id, module_net)) {
ModuleId src_module = module_manager.net_source_modules(module_id, module_net)[net_src];
if (module_id != src_module) {
continue;
}
/* Find the source port and pin information */
print_spice_comment(fp, std::string("Net source id " + std::to_string(size_t(net_src))));
ModulePortId src_port_id = module_manager.net_source_ports(module_id, module_net)[net_src];
size_t src_pin = module_manager.net_source_pins(module_id, module_net)[net_src];
BasicPort src_port(module_manager.module_port(module_id, src_port_id).get_name(), src_pin, src_pin);
/* 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 */
print_spice_comment(fp, std::string("Net sink id " + std::to_string(size_t(net_sink))));
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);
/* We need to print a wire connection here */
VTR_ASSERT(src_port.get_width() == sink_port.get_width());
for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) {
BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]);
BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]);
print_spice_short_connection(fp,
generate_spice_port(src_spice_pin),
generate_spice_port(sink_spice_pin));
}
}
}
}
/********************************************************************
* Print short connections inside a SPICE module
* The short connection is defined as the direct connection
* between an input port of the module and an output port of the module
* This type of connection is not covered when printing SPICE instances
* Therefore, they are covered in this function
*
* module
* +-----------------------------+
* | |
* inputA--->|---------------------------->|--->outputB
* | |
* | |
* | |
* +-----------------------------+
*******************************************************************/
static
void print_spice_subckt_local_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_local_short_connection(module_manager, module_id, module_net)) {
continue;
}
print_spice_comment(fp, std::string("Local connection due to Wire " + std::to_string(size_t(module_net))));
print_spice_subckt_local_short_connection(fp, module_manager, module_id, module_net);
}
}
/********************************************************************
* Print output short connections inside a SPICE 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 SPICE instances
* Therefore, they are covered in this function
*
* module
* +-----------------------------+
* |
* src------>+--------------->|--->outputA
* | |
* | |
* +--------------->|--->outputB
* +-----------------------------+
*******************************************************************/
static
void print_spice_subckt_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_spice_subckt_output_short_connection(fp, module_manager, module_id, module_net);
}
}
/********************************************************************
* Write a SPICE instance to a file
* This function will name the input and output connections to
* the inputs/output or local wires available in the parent module
*
* Parent_module
* +-----------------------------+
* | |
* | +--------------+ |
* | | | |
* | | child_module | |
* | | [instance] | |
* | +--------------+ |
* | |
* +-----------------------------+
*
*******************************************************************/
static
void write_spice_instance_to_file(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& parent_module,
const ModuleId& child_module,
const size_t& instance_id) {
/* Ensure a valid file stream */
VTR_ASSERT(true == valid_file_stream(fp));
/* Print instance name:
* if we have an instance name, use it;
* if not, we use a default name <name>_<num_instance_in_parent_module>
*/
std::string instance_head_line = "X ";
if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) {
instance_head_line += generate_instance_name(module_manager.module_name(child_module), instance_id);
} else {
instance_head_line += module_manager.instance_name(parent_module, child_module, instance_id);
}
instance_head_line += " ";
fp << instance_head_line;
/* Port sequence: global, inout, input, output and clock ports, */
bool fit_one_line = true;
bool new_line = false;
size_t pin_cnt = 0;
for (int port_type = ModuleManager::MODULE_GLOBAL_PORT;
port_type < ModuleManager::NUM_MODULE_PORT_TYPES;
++port_type) {
for (const auto& child_port_id : module_manager.module_port_ids_by_type(child_module, static_cast<ModuleManager::e_module_port_type>(port_type))) {
BasicPort child_port = module_manager.module_port(child_module, child_port_id);
/* Create the port name and width to be used by the instance */
std::vector<BasicPort> instance_ports;
for (size_t child_pin : child_port.pins()) {
/* Find the net linked to the pin */
ModuleNetId net = module_manager.module_instance_port_net(parent_module, child_module, instance_id,
child_port_id, child_pin);
BasicPort instance_port;
if (ModuleNetId::INVALID() == net) {
/* We give the same port name as child module, this case happens to global ports */
instance_port.set_name(generate_spice_undriven_local_wire_name(module_manager, parent_module, child_module, instance_id, child_port_id));
instance_port.set_width(child_pin, child_pin);
} else {
/* Find the name for this child port */
instance_port = generate_spice_port_for_module_net(module_manager, parent_module, net);
}
if (true == new_line) {
std::string port_whitespace(instance_head_line.length() - 2, ' ');
fp << "+ " << port_whitespace;
}
if (0 != pin_cnt) {
write_space_to_file(fp, 1);
}
VTR_ASSERT(1 == instance_port.get_width());
/* For single-bit port,
* we can print the port name directly
*/
bool omit_pin_zero = false;
if ((1 == instance_port.pins().size())
&& (0 == instance_port.get_lsb())) {
omit_pin_zero = true;
}
fp << generate_spice_port(instance_port, omit_pin_zero);
/* Increase the counter */
pin_cnt++;
/* Currently we limit 10 ports per line to keep a clean netlist */
new_line = false;
if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
pin_cnt = 0;
fp << std::endl;
new_line = true;
fit_one_line = false;
}
}
}
}
/* Print VDD and VSS ports
* TODO: the supply ports should be derived from module manager
*/
if (true == new_line) {
std::string port_whitespace(instance_head_line.length() - 2, ' ');
fp << "+ " << port_whitespace;
}
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_VDD_PORT_NAME;
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_GND_PORT_NAME;
pin_cnt += 2;
/* Check if we need a new line */
new_line = false;
if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
pin_cnt = 0;
fp << std::endl;
new_line = true;
fit_one_line = false;
}
/* Print module name:
* if port print cannot fit one line, we create a new line for the module for a clean format
*/
if (false == fit_one_line) {
fp << std::endl;
fp << "+";
}
write_space_to_file(fp, 1);
fp << module_manager.module_name(child_module);
/* Print an end to the instance */
fp << std::endl;
}
/********************************************************************
* Write a SPICE sub-circuit to a file
* This is a key function, maybe most frequently called in our SPICE writer
* Note that file stream must be valid
*******************************************************************/
void write_spice_subckt_to_file(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id) {
VTR_ASSERT(true == valid_file_stream(fp));
/* Ensure we have a valid module_id */
VTR_ASSERT(module_manager.valid_module_id(module_id));
/* Print module declaration */
print_spice_subckt_definition(fp, module_manager, module_id);
/* Print an empty line as splitter */
fp << std::endl;
/* Print an empty line as splitter */
fp << std::endl;
/* Print local connection (from module inputs to output! */
print_spice_comment(fp, std::string("BEGIN Local short connections"));
print_spice_subckt_local_short_connections(fp, module_manager, module_id);
print_spice_comment(fp, std::string("END Local short connections"));
print_spice_comment(fp, std::string("BEGIN Local output short connections"));
print_spice_subckt_output_short_connections(fp, module_manager, module_id);
print_spice_comment(fp, std::string("END Local output short connections"));
/* Print an empty line as splitter */
fp << std::endl;
/* Print instances */
for (ModuleId child_module : module_manager.child_modules(module_id)) {
for (size_t instance : module_manager.child_module_instances(module_id, child_module)) {
/* Print an instance */
write_spice_instance_to_file(fp, module_manager, module_id, child_module, instance);
/* Print an empty line as splitter */
fp << std::endl;
}
}
/* Print an end for the module */
print_spice_subckt_end(fp, module_manager.module_name(module_id));
/* Print an empty line as splitter */
fp << std::endl;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,23 @@
#ifndef SPICE_SUBCKT_WRITER_H
#define SPICE_SUBCKT_WRITER_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include "module_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void write_spice_subckt_to_file(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id);
} /* end namespace openfpga */
#endif

View File

@ -1,5 +1,5 @@
/********************************************************************* /*********************************************************************
* This file includes top-level function to generate Spice primitive modules * This file includes top-level function to generate SPICE primitive modules
* and print them to files * and print them to files
********************************************************************/ ********************************************************************/
@ -10,7 +10,11 @@
/* Headers from openfpgashell library */ /* Headers from openfpgashell library */
#include "command_exit_codes.h" #include "command_exit_codes.h"
#include "spice_transistor_wrapper.h"
#include "spice_essential_gates.h" #include "spice_essential_gates.h"
#include "spice_mux.h"
#include "spice_lut.h"
#include "spice_memory.h"
#include "spice_constants.h" #include "spice_constants.h"
#include "spice_submodule.h" #include "spice_submodule.h"
@ -21,23 +25,46 @@ namespace openfpga {
/********************************************************************* /*********************************************************************
* Top-level function to generate primitive modules: * Top-level function to generate primitive modules:
* 1. Transistor wrapper * 1. Transistor wrapper
* 2. TODO: Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor
* 3. TODO: Routing multiplexers * 3. Routing multiplexers
* 4. TODO: Local encoders for routing multiplexers * 4. TODO: Local encoders for routing multiplexers
* 5. TODO: Wires * 5. Wires
* 6. TODO: Configuration memory blocks * 6. Configuration memory blocks
********************************************************************/ ********************************************************************/
int print_spice_submodule(NetlistManager& netlist_manager, int print_spice_submodule(NetlistManager& netlist_manager,
const ModuleManager& module_manager, const ModuleManager& module_manager,
const Arch& openfpga_arch, const Arch& openfpga_arch,
const MuxLibrary& mux_lib,
const std::string& submodule_dir) { const std::string& submodule_dir) {
int status = CMD_EXEC_SUCCESS; int status = CMD_EXEC_SUCCESS;
/* Transistor wrapper */
status = print_spice_transistor_wrapper(netlist_manager, status = print_spice_transistor_wrapper(netlist_manager,
openfpga_arch.tech_lib, openfpga_arch.tech_lib,
submodule_dir); submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* Constant modules: VDD and GND */
status = print_spice_supply_wrappers(netlist_manager,
module_manager,
submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* Logic gates:
* - AND/OR,
* - inverter, buffer
* - transmission-gate/pass-transistor
* - wires
*/
status = print_spice_essential_gates(netlist_manager, status = print_spice_essential_gates(netlist_manager,
module_manager, module_manager,
openfpga_arch.circuit_lib, openfpga_arch.circuit_lib,
@ -45,6 +72,50 @@ int print_spice_submodule(NetlistManager& netlist_manager,
openfpga_arch.circuit_tech_binding, openfpga_arch.circuit_tech_binding,
submodule_dir); submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* TODO: local decoders for routing multiplexers */
/* Routing multiplexers */
status = print_spice_submodule_muxes(netlist_manager,
module_manager,
mux_lib,
openfpga_arch.circuit_lib,
submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* Look-Up Tables */
status = print_spice_submodule_luts(netlist_manager,
module_manager,
openfpga_arch.circuit_lib,
submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* Memories */
status = print_spice_submodule_memories(netlist_manager,
module_manager,
mux_lib,
openfpga_arch.circuit_lib,
submodule_dir);
/* Error out if fatal errors have been reported */
if (CMD_EXEC_SUCCESS != status) {
return CMD_EXEC_FATAL_ERROR;
}
/* TODO: architecture decoders */
return status; return status;
} }

View File

@ -7,6 +7,7 @@
#include "netlist_manager.h" #include "netlist_manager.h"
#include "module_manager.h" #include "module_manager.h"
#include "openfpga_arch.h" #include "openfpga_arch.h"
#include "mux_library.h"
/******************************************************************** /********************************************************************
* Function declaration * Function declaration
@ -18,6 +19,7 @@ namespace openfpga {
int print_spice_submodule(NetlistManager& netlist_manager, int print_spice_submodule(NetlistManager& netlist_manager,
const ModuleManager& module_manager, const ModuleManager& module_manager,
const Arch& openfpga_arch, const Arch& openfpga_arch,
const MuxLibrary& mux_lib,
const std::string& submodule_dir); const std::string& submodule_dir);
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -0,0 +1,76 @@
/********************************************************************
* This file includes functions that are used to print the top-level
* module for the FPGA fabric in SPICE format
*******************************************************************/
#include <fstream>
#include <map>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "openfpga_naming.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_subckt_writer.h"
#include "spice_top_module.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print the top-level module for the FPGA fabric in SPICE format
* This function will
* 1. name the top-level module
* 2. include dependent netlists
* - User defined netlists
* - Auto-generated netlists
* 3. Add the submodules to the top-level graph
* 4. Add module nets to connect datapath ports
* 5. Add module nets/submodules to connect configuration ports
*******************************************************************/
void print_spice_top_module(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& spice_dir) {
/* Create a module as the top-level fabric, and add it to the module manager */
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));
/* Create the file name for SPICE netlist */
std::string spice_fname(spice_dir + generate_fpga_top_netlist_name(std::string(SPICE_NETLIST_FILE_POSTFIX)));
VTR_LOG("Writing SPICE netlist for top-level module of FPGA fabric '%s'...",
spice_fname.c_str());
/* Create the file stream */
std::fstream fp;
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(spice_fname.c_str(), fp);
print_spice_file_header(fp, std::string("Top-level SPICE subckt for FPGA"));
/* Write the module content in Verilog format */
write_spice_subckt_to_file(fp, module_manager, top_module);
/* Add an empty line as a splitter */
fp << std::endl;
/* Close file handler */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::TOP_MODULE_NETLIST);
VTR_LOG("Done\n");
}
} /* end namespace openfpga */

View File

@ -0,0 +1,24 @@
#ifndef SPICE_TOP_MODULE_H
#define SPICE_TOP_MODULE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include "module_manager.h"
#include "netlist_manager.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_spice_top_module(NetlistManager& netlist_manager,
const ModuleManager& module_manager,
const std::string& spice_dir);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,190 @@
/************************************************
* This file includes functions on
* outputting wrapper SPICE netlists for transistor
***********************************************/
#include <fstream>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "circuit_library_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_transistor_wrapper.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print a SPICE model wrapper for a transistor model
*******************************************************************/
static
int print_spice_transistor_model_wrapper(std::fstream& fp,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Transistor model followed a fixed port mapping
* [X|M]<MODEL_CARD_NAME> <DRAIN> <GATE> <SOURCE> <BULK>
* which is a standard in SPICE modeling
* We will output the pmos and nmos transistors wrappers
* which are defined in this model
*/
for (int itype = TECH_LIB_TRANSISTOR_PMOS;
itype < NUM_TECH_LIB_TRANSISTOR_TYPES;
++itype) {
const e_tech_lib_transistor_type& trans_type = static_cast<e_tech_lib_transistor_type>(itype);
fp << ".subckt ";
fp << tech_lib.transistor_model_name(model, trans_type) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " drain gate source bulk";
fp << " L=" << std::setprecision(10) << tech_lib.transistor_model_chan_length(model, trans_type);
fp << " W=" << std::setprecision(10) << tech_lib.transistor_model_min_width(model, trans_type);
fp << "\n";
fp << tech_lib.model_ref(model);
fp << "1";
fp << " drain gate source bulk";
fp << " " << tech_lib.transistor_model_name(model, trans_type);
fp << " L=L W=W";
fp << "\n";
fp << ".ends";
fp << "\n";
}
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE netlist for transistors
*******************************************************************/
int print_spice_transistor_wrapper(NetlistManager& netlist_manager,
const TechnologyLibrary& tech_lib,
const std::string& submodule_dir) {
std::string spice_fname = submodule_dir + std::string(TRANSISTORS_SPICE_FILE_NAME);
std::fstream fp;
/* Create the file stream */
fp.open(spice_fname, std::fstream::out | std::fstream::trunc);
/* Check if the file stream if valid or not */
check_file_stream(spice_fname.c_str(), fp);
/* Create file */
VTR_LOG("Generating SPICE netlist '%s' for transistors...",
spice_fname.c_str());
print_spice_file_header(fp, std::string("Transistor wrappers"));
/* Iterate over the transistor models */
for (const TechnologyModelId& model : tech_lib.models()) {
/* Focus on transistor model */
if (TECH_LIB_MODEL_TRANSISTOR != tech_lib.model_type(model)) {
continue;
}
/* Write a wrapper for the transistor model */
if (CMD_EXEC_SUCCESS != print_spice_transistor_model_wrapper(fp, tech_lib, model)) {
return CMD_EXEC_FATAL_ERROR;
}
}
/* Close file handler*/
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(spice_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST);
VTR_LOG("Done\n");
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE modeling for the PMOS part of a logic gate
*
* This function is created to be shared by pass-transistor and
* transmission-gate SPICE netlist writer
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* a PMOS. Any preprocessing or subckt definition should not be included!
*******************************************************************/
int print_spice_generic_pmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& gate_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Write transistor pairs using the technology model */
fp << "Xpmos_" << trans_name_postfix << " ";
fp << input_port_name << " ";
fp << gate_port_name << " ";
fp << output_port_name << " ";
fp << SPICE_SUBCKT_VDD_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE modeling for the NMOS part of a logic gate
*
* Note:
* - This function does NOT create a file
* but requires a file stream created
* - This function only output SPICE modeling for
* a NMOS. Any preprocessing or subckt definition should not be included!
*******************************************************************/
int print_spice_generic_nmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& gate_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
fp << "Xnmos_" << trans_name_postfix << " ";
fp << input_port_name << " ";
fp << gate_port_name << " ";
fp << output_port_name << " ";
fp << SPICE_SUBCKT_GND_PORT_NAME << " ";
fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX;
fp << " W=" << std::setprecision(10) << trans_width;
fp << "\n";
return CMD_EXEC_SUCCESS;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,43 @@
#ifndef SPICE_TRANSISTOR_WRAPPER_H
#define SPICE_TRANSISTOR_WRAPPER_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#include "netlist_manager.h"
#include "technology_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_transistor_wrapper(NetlistManager& netlist_manager,
const TechnologyLibrary& tech_lib,
const std::string& submodule_dir);
int print_spice_generic_pmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& gate_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width);
int print_spice_generic_nmos_modeling(std::fstream& fp,
const std::string& trans_name_postfix,
const std::string& input_port_name,
const std::string& gate_port_name,
const std::string& output_port_name,
const TechnologyLibrary& tech_lib,
const TechnologyModelId& tech_model,
const float& trans_width);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,408 @@
/************************************************
* This file includes functions on
* outputting SPICE netlists for routing wires:
* - regular wires (1 input and 1 output)
* - routing track wires (1 input and 2 outputs)
***********************************************/
#include <fstream>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgashell library */
#include "command_exit_codes.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "circuit_library_utils.h"
#include "build_module_graph_utils.h"
#include "spice_constants.h"
#include "spice_writer_utils.h"
#include "spice_wire.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print SPICE modeling for pie-type RC network
*
* Schematic
* middle out
* |
* in ---wwww----wwww--- ... --wwww---out
* | | | |
* = = = =
* | | | |
* GND GND GND GND
*******************************************************************/
static
int print_spice_wire_pi_type_rc_modeling(std::fstream& fp,
const std::string& input_port_name,
const std::string& output_port_name,
const std::string& middle_output_port_name,
const float& res_total,
const float& cap_total,
const size_t& num_levels) {
/* Determine the resistance and capacitance of each level*/
float res_per_level = res_total / ((float)(2 * num_levels));
float cap_per_level = cap_total / ((float)(2 * num_levels));
/* All the resistance and capacitance value should be larger than or equal to zero*/
VTR_ASSERT(0. <= res_per_level);
VTR_ASSERT(0. <= cap_per_level);
for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) {
/* Print the first capacitor if this is the first level */
if ((0 == ilvl) && (0. < cap_per_level)) {
print_spice_capacitor(fp, input_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level);
}
/* Output a regular RC pair
*
* midnode
* ^
* |
* ------+-ww-+-ww-+------
* | |
* = =
* | |
* GND GND
*/
std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl);
if (0 == ilvl) {
lvl_input_port_name = input_port_name;
}
std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl);
std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1);
if (ilvl == num_levels - 1) {
lvl_output_port_name = output_port_name;
}
print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level);
print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level);
/* Last level only require 1 unit of cap_per_level */
float cap_curr_level = 2. * cap_per_level;
if (ilvl == num_levels - 1) {
cap_curr_level = cap_per_level;
}
if (0. < cap_curr_level) {
print_spice_capacitor(fp, lvl_output_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_curr_level);
}
}
/* If the middle output is required, create a short connection to
* - when the number of levels is odd
*
* middle_output
* ^
* |
* ---ww-+-ww-+-ww-+-ww---
* | |
* = =
* | |
* GND GND
*
* - when the number of levels is even:
*
* middle_output
* ^
* |
* -+-ww--ww-+-ww--ww-+-
* | | |
* = = =
* | | |
* GND GND GND
*
*/
if (!middle_output_port_name.empty()) {
print_spice_comment(fp, std::string("Connect to the middle output"));
std::string rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1);
if (1 == num_levels % 2) {
rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2);
}
print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name);
}
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Print SPICE modeling for T-type RC network
*
* Schematic
* middle out
* |
* in ---ww-+--ww--+--ww--+--ww--- ... --ww--+--ww--- out
* | | | |
* = = = =
* | | | |
* GND GND GND GND
*******************************************************************/
static
int print_spice_wire_t_type_rc_modeling(std::fstream& fp,
const std::string& input_port_name,
const std::string& output_port_name,
const std::string& middle_output_port_name,
const float& res_total,
const float& cap_total,
const size_t& num_levels) {
/* Determine the resistance and capacitance of each level*/
float res_per_level = res_total / ((float)(2 * num_levels));
float cap_per_level = cap_total / ((float)(num_levels));
/* All the resistance and capacitance value should be larger than or equal to zero*/
VTR_ASSERT(0. <= res_per_level);
VTR_ASSERT(0. <= cap_per_level);
for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) {
/* Output a regular RC pair
*
* midnode
* ^
* |
* --------ww-+-ww--------
* |
* =
* |
* GND
*/
std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl);
if (0 == ilvl) {
lvl_input_port_name = input_port_name;
}
std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl);
std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1);
if (ilvl == num_levels - 1) {
lvl_output_port_name = output_port_name;
}
print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level);
print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level);
if (0. < cap_per_level) {
print_spice_capacitor(fp, lvl_middle_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level);
}
}
/* If the middle output is required, create a short connection to
* - when the number of levels is even
*
* middle_output
* ^
* |
* ---ww-+-ww-+-ww-+-ww---
* | |
* = =
* | |
* GND GND
*
* - when the number of levels is odd:
*
* middle_output
* ^
* |
* -+-ww--ww-+-ww--ww-+-
* | | |
* = = =
* | | |
* GND GND GND
*
*/
if (!middle_output_port_name.empty()) {
print_spice_comment(fp, std::string("Connect to the middle output"));
std::string rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2);
if (0 == num_levels % 2) {
rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1);
}
print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name);
}
return CMD_EXEC_SUCCESS;
}
/********************************************************************
* Generate the SPICE subckt for a regular wire
*
* Schematic
*
* Middle output (only for routing track wires)
* ^
* |
* +--------------------+ +---------------+ +--------------------+
* in ->| Inverter or buffer |--->| RC Network |---->| Inverter or buffer |---> out
* | Optional | | | | Optional |
* +--------------------- +---------------+ +--------------------+
*
*******************************************************************/
int print_spice_wire_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model) {
if (false == valid_file_stream(fp)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Find the input and output ports:
* we do NOT support global ports here,
* it should be handled in another type of inverter subckt (power-gated)
*/
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* Make sure:
* There are 1 input ports and 1 output port,
* each size of which is 1
*/
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
int status = CMD_EXEC_SUCCESS;
/* Print the inverter subckt definition */
print_spice_subckt_definition(fp, module_manager, module_id);
std::string input_port_name = circuit_lib.port_prefix(input_ports[0]);
std::string output_port_name = circuit_lib.port_prefix(output_ports[0]);
std::string middle_output_port_name;
if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) {
middle_output_port_name = std::string("middle") + output_port_name;
}
std::string rc_ntwk_input_port_name = std::string("rc_network_node") + std::to_string(0);
std::string rc_ntwk_output_port_name = std::string("rc_network_node") + std::to_string(circuit_lib.wire_num_level(circuit_model) - 1);
std::string rc_ntwk_middle_output_port_name;
if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) {
rc_ntwk_middle_output_port_name = std::string("middle") + rc_ntwk_output_port_name;
}
ModulePortId wire_module_input_port = module_manager.find_module_port(module_id, input_port_name);
ModulePortId wire_module_output_port = module_manager.find_module_port(module_id, output_port_name);
ModulePortId wire_module_middle_output_port = ModulePortId::INVALID();
if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) {
wire_module_middle_output_port = module_manager.find_module_port(module_id, output_port_name);
}
/* Add input buffer:
* - There is a valid buffer model, instanciate it
* - There is no buffer, set a short connection
*/
if (circuit_lib.input_buffer_model(circuit_model)) {
std::string instance_name = std::string("input_buffer");
std::map<std::string, BasicPort> port2port_name_map;
ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.input_buffer_model(circuit_model)));
VTR_ASSERT(true == module_manager.valid_module_id(buffer_module));
ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT);
ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT);
/* Port size should be 1 ! */
VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width());
VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width());
port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = module_manager.module_port(module_id, wire_module_input_port);
port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = BasicPort(rc_ntwk_input_port_name, 1);
print_spice_subckt_instance(fp,
module_manager,
buffer_module,
instance_name,
port2port_name_map);
} else {
print_spice_short_connection(fp, circuit_lib.port_prefix(input_ports[0]), rc_ntwk_input_port_name);
}
/* Determine which type of model to print*/
switch (circuit_lib.wire_type(circuit_model)) {
case WIRE_MODEL_PI:
status = print_spice_wire_pi_type_rc_modeling(fp,
rc_ntwk_input_port_name,
rc_ntwk_output_port_name,
rc_ntwk_middle_output_port_name,
circuit_lib.wire_r(circuit_model),
circuit_lib.wire_c(circuit_model),
circuit_lib.wire_num_level(circuit_model));
break;
case WIRE_MODEL_T:
status = print_spice_wire_t_type_rc_modeling(fp,
rc_ntwk_input_port_name,
rc_ntwk_output_port_name,
rc_ntwk_middle_output_port_name,
circuit_lib.wire_r(circuit_model),
circuit_lib.wire_c(circuit_model),
circuit_lib.wire_num_level(circuit_model));
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Unsupport wire model type for circuit model '%s.\n",
circuit_lib.model_name(circuit_model).c_str());
return CMD_EXEC_FATAL_ERROR;
}
/* Add output buffer:
* - There is a valid buffer model, instanciate it
* - There is no buffer, set a short connection
*/
if (circuit_lib.output_buffer_model(circuit_model)) {
std::string instance_name = std::string("output_buffer");
std::map<std::string, BasicPort> port2port_name_map;
ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.output_buffer_model(circuit_model)));
VTR_ASSERT(true == module_manager.valid_module_id(buffer_module));
ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT);
ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT);
/* Port size should be 1 ! */
VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width());
VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width());
port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = BasicPort(rc_ntwk_output_port_name, 1);
port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_output_port);
print_spice_subckt_instance(fp,
module_manager,
buffer_module,
instance_name,
port2port_name_map);
if (!rc_ntwk_middle_output_port_name.empty()) {
instance_name = std::string("middle_output_buffer");
port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_middle_output_port);
print_spice_subckt_instance(fp,
module_manager,
buffer_module,
instance_name,
port2port_name_map);
}
} else {
print_spice_short_connection(fp, rc_ntwk_output_port_name, circuit_lib.port_prefix(output_ports[0]));
if (!rc_ntwk_middle_output_port_name.empty()) {
print_spice_short_connection(fp, rc_ntwk_middle_output_port_name, circuit_lib.port_prefix(output_ports[0]));
}
}
print_spice_subckt_end(fp, module_manager.module_name(module_id));
return status;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,27 @@
#ifndef SPICE_WIRE_H
#define SPICE_WIRE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#include "module_manager.h"
#include "circuit_library.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int print_spice_wire_subckt(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model);
} /* end namespace openfpga */
#endif

View File

@ -18,6 +18,7 @@
/* Headers from openfpgautil library */ /* Headers from openfpgautil library */
#include "openfpga_digest.h" #include "openfpga_digest.h"
#include "spice_constants.h"
#include "spice_writer_utils.h" #include "spice_writer_utils.h"
/* begin namespace openfpga */ /* begin namespace openfpga */
@ -70,10 +71,21 @@ void print_spice_comment(std::fstream& fp,
/************************************************ /************************************************
* Generate a string for a port in SPICE format * Generate a string for a port in SPICE format
* If the pin id is zero, e.g., A[0], the option
* 'omit_pin_zero' may be turned on for compact port
* print-out, e.g., A
***********************************************/ ***********************************************/
std::string generate_spice_port(const BasicPort& port) { std::string generate_spice_port(const BasicPort& port,
const bool& omit_pin_zero) {
VTR_ASSERT(1 == port.get_width()); VTR_ASSERT(1 == port.get_width());
std::string ret = port.get_name(); std::string ret = port.get_name();
if ((true == omit_pin_zero)
&& (0 == port.get_lsb())) {
return ret;
}
ret += "["; ret += "[";
ret += std::to_string(port.get_lsb()); ret += std::to_string(port.get_lsb());
ret += "]"; ret += "]";
@ -87,7 +99,9 @@ std::string generate_spice_port(const BasicPort& port) {
* module <module_name> (<ports without directions>); * module <module_name> (<ports without directions>);
***********************************************/ ***********************************************/
void print_spice_subckt_definition(std::fstream& fp, void print_spice_subckt_definition(std::fstream& fp,
const ModuleManager& module_manager, const ModuleId& module_id) { const ModuleManager& module_manager,
const ModuleId& module_id,
const bool& include_supply_ports) {
VTR_ASSERT(true == valid_file_stream(fp)); VTR_ASSERT(true == valid_file_stream(fp));
print_spice_comment(fp, std::string("SPICE module for " + module_manager.module_name(module_id))); print_spice_comment(fp, std::string("SPICE module for " + module_manager.module_name(module_id)));
@ -119,14 +133,22 @@ void print_spice_subckt_definition(std::fstream& fp,
BasicPort port_pin(port.get_name(), pin, pin); BasicPort port_pin(port.get_name(), pin, pin);
fp << generate_spice_port(port_pin); /* For single-bit port,
* we can print the port name directly
*/
bool omit_pin_zero = false;
if ((1 == port.pins().size())
&& (0 == pin)) {
omit_pin_zero = true;
}
fp << generate_spice_port(port_pin, omit_pin_zero);
/* Increase the counter */ /* Increase the counter */
pin_cnt++; pin_cnt++;
/* Currently we limit 10 ports per line to keep a clean netlist */ /* Currently we limit 10 ports per line to keep a clean netlist */
new_line = false; new_line = false;
if (10 == pin_cnt) { if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
pin_cnt = 0; pin_cnt = 0;
fp << std::endl; fp << std::endl;
new_line = true; new_line = true;
@ -134,6 +156,22 @@ void print_spice_subckt_definition(std::fstream& fp,
} }
} }
} }
/* Add supply ports if specified */
if (true == include_supply_ports) {
/* Print VDD and VSS ports
* TODO: the supply ports should be derived from module manager
*/
if (true == new_line) {
std::string port_whitespace(module_head_line.length() - 2, ' ');
fp << "+ " << port_whitespace;
}
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_VDD_PORT_NAME;
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_GND_PORT_NAME;
}
fp << std::endl; fp << std::endl;
} }
@ -149,4 +187,182 @@ void print_spice_subckt_end(std::fstream& fp,
fp << std::endl; fp << std::endl;
} }
/************************************************
* Print a resistor in SPICE syntax
***********************************************/
void print_spice_resistor(std::fstream& fp,
const std::string& input_port,
const std::string& output_port,
const float& resistance) {
VTR_ASSERT(true == valid_file_stream(fp));
/* Set an unique name to the resistor */
fp << "R" << input_port << "_to_" << output_port;
fp << " " << input_port;
fp << " " << output_port;
fp << " " << std::setprecision(10) << resistance;
fp << std::endl;
}
/************************************************
* Print a capacitor in SPICE syntax
***********************************************/
void print_spice_capacitor(std::fstream& fp,
const std::string& input_port,
const std::string& output_port,
const float& capacitance) {
VTR_ASSERT(true == valid_file_stream(fp));
/* Set an unique name to the capacitor */
fp << "C" << input_port << "_to_" << output_port;
fp << " " << input_port;
fp << " " << output_port;
fp << " " << std::setprecision(10) << capacitance;
fp << std::endl;
}
/************************************************
* Print a short-connected wire using zero resistance in SPICE syntax
***********************************************/
void print_spice_short_connection(std::fstream& fp,
const std::string& input_port,
const std::string& output_port) {
print_spice_resistor(fp, input_port, output_port, 0.);
}
/********************************************************************
* Print an instance in SPICE format (a generic version)
* This function will require user to provide an instance name
*
* This function will output the port map by referring to a port-to-port
* mapping:
* <module_port_name> -> <instance_port_name>
* The key of the port-to-port mapping is the port name of the module:
* The value of the port-to-port mapping is the port information of the instance
* With link between module and instance, the function can output a SPICE
* instance easily, by following the define port sequence of the module
*
* Note that, it is not necessary that the port-to-port mapping
* covers all the module ports.
* Any instance/module port which are not specified in the port-to-port
* mapping will be output by the module port name.
*******************************************************************/
void print_spice_subckt_instance(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const std::string& instance_name,
const std::map<std::string, BasicPort>& port2port_name_map) {
VTR_ASSERT(true == valid_file_stream(fp));
/* Check: all the key ports in the port2port_name_map does exist in the child module */
for (const auto& kv : port2port_name_map) {
ModulePortId module_port_id = module_manager.find_module_port(module_id, kv.first);
VTR_ASSERT(ModulePortId::INVALID() != module_port_id);
}
/* Print instance name */
std::string instance_head_line = "X " + instance_name + " ";
fp << instance_head_line;
/* Port sequence: global, inout, input, output and clock ports, */
bool fit_one_line = true;
bool new_line = false;
size_t pin_cnt = 0;
for (int port_type = ModuleManager::MODULE_GLOBAL_PORT;
port_type < ModuleManager::NUM_MODULE_PORT_TYPES;
++port_type) {
for (const auto& port : module_manager.module_ports_by_type(module_id, static_cast<ModuleManager::e_module_port_type>(port_type))) {
/* Deposit a default port name */
BasicPort port_to_print = port;
/* Try to find the instanced port name in the name map */
auto port_search_result = port2port_name_map.find(port.get_name());
if (port_search_result != port2port_name_map.end()) {
/* Found it, we assign the port name */
/* TODO: make sure the port width matches! */
ModulePortId module_port_id = module_manager.find_module_port(module_id, port.get_name());
/* Get the port from module */
BasicPort module_port = module_manager.module_port(module_id, module_port_id);
VTR_ASSERT(module_port.get_width() == port_search_result->second.get_width());
port_to_print = port_search_result->second;
}
/* Print port: only the port name is enough */
for (const auto& pin : port_to_print.pins()) {
if (true == new_line) {
std::string port_whitespace(instance_head_line.length() - 2, ' ');
fp << "+ " << port_whitespace;
}
if (0 != pin_cnt) {
write_space_to_file(fp, 1);
}
BasicPort port_pin(port.get_name(), pin, pin);
/* For single-bit port,
* we can print the port name directly
*/
bool omit_pin_zero = false;
if ((1 == port.pins().size())
&& (0 == pin)) {
omit_pin_zero = true;
}
fp << generate_spice_port(port_pin, omit_pin_zero);
/* Increase the counter */
pin_cnt++;
/* Currently we limit 10 ports per line to keep a clean netlist */
new_line = false;
if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
pin_cnt = 0;
fp << std::endl;
new_line = true;
fit_one_line = false;
}
}
}
}
/* Print VDD and VSS ports
* TODO: the supply ports should be derived from module manager
*/
if (true == new_line) {
std::string port_whitespace(instance_head_line.length() - 2, ' ');
fp << "+ " << port_whitespace;
}
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_VDD_PORT_NAME;
write_space_to_file(fp, 1);
fp << SPICE_SUBCKT_GND_PORT_NAME;
pin_cnt += 2;
/* Check if we need a new line */
new_line = false;
if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
pin_cnt = 0;
fp << std::endl;
new_line = true;
fit_one_line = false;
}
/* Print module name:
* if port print cannot fit one line, we create a new line for the module for a clean format
*/
if (false == fit_one_line) {
fp << std::endl;
fp << "+";
}
write_space_to_file(fp, 1);
fp << module_manager.module_name(module_id);
/* Print an end to the instance */
fp << std::endl;
}
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -39,14 +39,37 @@ void print_spice_include_netlist(std::fstream& fp,
void print_spice_comment(std::fstream& fp, void print_spice_comment(std::fstream& fp,
const std::string& comment); const std::string& comment);
std::string generate_spice_port(const BasicPort& port); std::string generate_spice_port(const BasicPort& port,
const bool& omit_pin_zero = false);
void print_spice_subckt_definition(std::fstream& fp, void print_spice_subckt_definition(std::fstream& fp,
const ModuleManager& module_manager, const ModuleId& module_id); const ModuleManager& module_manager,
const ModuleId& module_id,
const bool& include_supply_ports = true);
void print_spice_subckt_end(std::fstream& fp, void print_spice_subckt_end(std::fstream& fp,
const std::string& module_name); const std::string& module_name);
void print_spice_resistor(std::fstream& fp,
const std::string& input_port,
const std::string& output_port,
const float& resistance);
void print_spice_capacitor(std::fstream& fp,
const std::string& input_port,
const std::string& output_port,
const float& capacitance);
void print_spice_short_connection(std::fstream& fp,
const std::string& input_port,
const std::string& output_port);
void print_spice_subckt_instance(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id,
const std::string& instance_name,
const std::map<std::string, BasicPort>& port2port_name_map);
} /* end namespace openfpga */ } /* end namespace openfpga */
#endif #endif

View File

@ -24,7 +24,7 @@
#include "verilog_preconfig_top_module.h" #include "verilog_preconfig_top_module.h"
#include "verilog_formal_random_top_testbench.h" #include "verilog_formal_random_top_testbench.h"
#include "verilog_top_testbench.h" #include "verilog_top_testbench.h"
#include "simulation_info_writer.h" #include "verilog_simulation_info_writer.h"
/* Header file for this source file */ /* Header file for this source file */
#include "verilog_api.h" #include "verilog_api.h"
@ -60,8 +60,7 @@ namespace openfpga
const DeviceContext &device_ctx, const DeviceContext &device_ctx,
const VprDeviceAnnotation &device_annotation, const VprDeviceAnnotation &device_annotation,
const DeviceRRGSB &device_rr_gsb, const DeviceRRGSB &device_rr_gsb,
const FabricVerilogOption &options) const FabricVerilogOption &options) {
{
vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n");
@ -99,16 +98,13 @@ namespace openfpga
options); options);
/* Generate routing blocks */ /* Generate routing blocks */
if (true == options.compress_routing()) if (true == options.compress_routing()) {
{
print_verilog_unique_routing_modules(netlist_manager, print_verilog_unique_routing_modules(netlist_manager,
const_cast<const ModuleManager &>(module_manager), const_cast<const ModuleManager &>(module_manager),
device_rr_gsb, device_rr_gsb,
rr_dir_path, rr_dir_path,
options.explicit_port_mapping()); options.explicit_port_mapping());
} } else {
else
{
VTR_ASSERT(false == options.compress_routing()); VTR_ASSERT(false == options.compress_routing());
print_verilog_flatten_routing_modules(netlist_manager, print_verilog_flatten_routing_modules(netlist_manager,
const_cast<const ModuleManager &>(module_manager), const_cast<const ModuleManager &>(module_manager),
@ -132,7 +128,7 @@ namespace openfpga
options.explicit_port_mapping()); options.explicit_port_mapping());
/* Generate an netlist including all the fabric-related netlists */ /* Generate an netlist including all the fabric-related netlists */
print_fabric_include_netlist(const_cast<const NetlistManager &>(netlist_manager), print_verilog_fabric_include_netlist(const_cast<const NetlistManager &>(netlist_manager),
src_dir_path, src_dir_path,
circuit_lib); circuit_lib);
@ -161,8 +157,7 @@ namespace openfpga
const CircuitLibrary &circuit_lib, const CircuitLibrary &circuit_lib,
const SimulationSetting &simulation_setting, const SimulationSetting &simulation_setting,
const e_config_protocol_type &config_protocol_type, const e_config_protocol_type &config_protocol_type,
const VerilogTestbenchOption &options) const VerilogTestbenchOption &options) {
{
vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n");
@ -183,8 +178,7 @@ namespace openfpga
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(circuit_lib); std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(circuit_lib);
/* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */
if (true == options.print_formal_verification_top_netlist()) if (true == options.print_formal_verification_top_netlist()) {
{
std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX);
print_verilog_preconfig_top_module(module_manager, bitstream_manager, print_verilog_preconfig_top_module(module_manager, bitstream_manager,
circuit_lib, global_ports, circuit_lib, global_ports,
@ -195,8 +189,7 @@ namespace openfpga
options.explicit_port_mapping()); options.explicit_port_mapping());
} }
if (true == options.print_preconfig_top_testbench()) if (true == options.print_preconfig_top_testbench()) {
{
/* Generate top-level testbench using random vectors */ /* Generate top-level testbench using random vectors */
std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
print_verilog_random_top_testbench(netlist_name, print_verilog_random_top_testbench(netlist_name,
@ -208,8 +201,7 @@ namespace openfpga
} }
/* Generate full testbench for verification, including configuration phase and operating phase */ /* Generate full testbench for verification, including configuration phase and operating phase */
if (true == options.print_top_testbench()) if (true == options.print_top_testbench()) {
{
std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
print_verilog_top_testbench(module_manager, print_verilog_top_testbench(module_manager,
bitstream_manager, fabric_bitstream, bitstream_manager, fabric_bitstream,
@ -225,8 +217,7 @@ namespace openfpga
} }
/* Generate exchangeable files which contains simulation settings */ /* Generate exchangeable files which contains simulation settings */
if (true == options.print_simulation_ini()) if (true == options.print_simulation_ini()) {
{
std::string simulation_ini_file_name = options.simulation_ini_path(); std::string simulation_ini_file_name = options.simulation_ini_path();
VTR_ASSERT(true != options.simulation_ini_path().empty()); VTR_ASSERT(true != options.simulation_ini_path().empty());
print_verilog_simulation_info(simulation_ini_file_name, print_verilog_simulation_info(simulation_ini_file_name,
@ -242,7 +233,7 @@ namespace openfpga
} }
/* Generate a Verilog file including all the netlists that have been generated */ /* Generate a Verilog file including all the netlists that have been generated */
print_include_netlists(src_dir_path, print_verilog_testbench_include_netlists(src_dir_path,
netlist_name, netlist_name,
options.reference_benchmark_file_path()); options.reference_benchmark_file_path());
} }

View File

@ -30,10 +30,10 @@ namespace openfpga {
* This does NOT include any testbenches! * This does NOT include any testbenches!
* Some netlists are open to compile under specific preprocessing flags * Some netlists are open to compile under specific preprocessing flags
*******************************************************************/ *******************************************************************/
void print_fabric_include_netlist(const NetlistManager& netlist_manager, void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager,
const std::string& src_dir, const std::string& src_dir,
const CircuitLibrary& circuit_lib) { const CircuitLibrary& circuit_lib) {
std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME); std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME);
/* Create the file stream */ /* Create the file stream */
std::fstream fp; std::fstream fp;
@ -94,10 +94,10 @@ void print_fabric_include_netlist(const NetlistManager& netlist_manager,
* that have been generated and user-defined. * that have been generated and user-defined.
* Some netlists are open to compile under specific preprocessing flags * Some netlists are open to compile under specific preprocessing flags
*******************************************************************/ *******************************************************************/
void print_include_netlists(const std::string& src_dir, void print_verilog_testbench_include_netlists(const std::string& src_dir,
const std::string& circuit_name, const std::string& circuit_name,
const std::string& reference_benchmark_file) { const std::string& reference_benchmark_file) {
std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX); std::string verilog_fname = src_dir + circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX);
/* Create the file stream */ /* Create the file stream */
std::fstream fp; std::fstream fp;
@ -116,7 +116,7 @@ void print_include_netlists(const std::string& src_dir,
/* Include FPGA top module */ /* Include FPGA top module */
print_verilog_comment(fp, std::string("------ Include fabric top-level netlists -----")); print_verilog_comment(fp, std::string("------ Include fabric top-level netlists -----"));
print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME)); print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME));
fp << std::endl; fp << std::endl;
/* Include reference benchmark netlist only when auto-check flag is enabled */ /* Include reference benchmark netlist only when auto-check flag is enabled */

View File

@ -17,11 +17,11 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
void print_fabric_include_netlist(const NetlistManager& netlist_manager, void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager,
const std::string& src_dir, const std::string& src_dir,
const CircuitLibrary& circuit_lib); const CircuitLibrary& circuit_lib);
void print_include_netlists(const std::string& src_dir, void print_verilog_testbench_include_netlists(const std::string& src_dir,
const std::string& circuit_name, const std::string& circuit_name,
const std::string& reference_benchmark_file); const std::string& reference_benchmark_file);

View File

@ -19,8 +19,8 @@ constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms";
constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches
// End of Icarus variables and flag // End of Icarus variables and flag
constexpr char* FABRIC_INCLUDE_NETLIST_FILE_NAME = "fabric_netlists.v"; constexpr char* FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME = "fabric_netlists.v";
constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; constexpr char* TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; constexpr char* VERILOG_TOP_POSTFIX = "_top.v";
constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v"; constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v";
constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */ constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */

View File

@ -21,7 +21,7 @@
#include "simulation_utils.h" #include "simulation_utils.h"
#include "verilog_constants.h" #include "verilog_constants.h"
#include "simulation_info_writer.h" #include "verilog_simulation_info_writer.h"
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
@ -77,7 +77,7 @@ void print_verilog_simulation_info(const std::string& ini_fname,
ini["SIMULATION_DECK"]["UNIT "] = "ms"; ini["SIMULATION_DECK"]["UNIT "] = "ms";
ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir); ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir);
ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME); ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME);
ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX)); ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX));
ini["SIMULATION_DECK"]["CONFIG_PROTOCOL"] = std::string(CONFIG_PROTOCOL_TYPE_STRING[config_protocol_type]); ini["SIMULATION_DECK"]["CONFIG_PROTOCOL"] = std::string(CONFIG_PROTOCOL_TYPE_STRING[config_protocol_type]);
/* Information required by UVM */ /* Information required by UVM */

View File

@ -1,5 +1,5 @@
#ifndef SIMULATION_INFO_WRITER_H #ifndef VERILOG_SIMULATION_INFO_WRITER_H
#define SIMULATION_INFO_WRITER_H #define VERILOG_SIMULATION_INFO_WRITER_H
/******************************************************************** /********************************************************************
* Include header files that are required by function declaration * Include header files that are required by function declaration

View File

@ -20,6 +20,73 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
/***************************************************************************************
* Try to find sink pb graph pins through walking through the fan-out edges from
* the source pb graph pin
* Only the sink meeting the following requirements can be considered:
* - All the fan-out edges between the source and sink are from direct interconnection
* - sink is an input of a primitive pb_type
*
* Note:
* - If there is a fan-out of the current source pb graph pin is not a direct interconnection
* the direct search should stop.
* - This function is designed for pb graph without local routing
* For example: direct connection between root pb graph node to the LUT pb graph node
*
* root pb_graph_node
* +-----------------------------------------
* | Intermediate pb_graph_node
* | +----------------------------------
* | | primitive pb_graph_node
* | | +-------------------------
* I[0] ---->+------>+------->|I[0] LUT
*
* - This function is designed for passing wires inside pb graph
*
* root pb_graph_node
* +------------------------------+
* | Intermediate pb_graph_node |
* | +-------------+ |
* | | | |
* | | | |
* I[0]----->+------>+--- ... ---->+------->+------>O[0]
*
***************************************************************************************/
static
bool rec_direct_search_sink_pb_graph_pins(const t_pb_graph_pin* source_pb_pin,
std::vector<t_pb_graph_pin*>& sink_pb_pins) {
std::vector<t_pb_graph_pin*> sink_pb_pins_to_search;
for (int iedge = 0; iedge < source_pb_pin->num_output_edges; ++iedge) {
if (DIRECT_INTERC != source_pb_pin->output_edges[iedge]->interconnect->type) {
return false;
}
for (int ipin = 0; ipin < source_pb_pin->output_edges[iedge]->num_output_pins; ++ipin) {
t_pb_graph_pin* cand_sink_pb_pin = source_pb_pin->output_edges[iedge]->output_pins[ipin];
if ( (true == is_primitive_pb_type(cand_sink_pb_pin->parent_node->pb_type))
&& (IN_PORT == cand_sink_pb_pin->port->type)) {
sink_pb_pins.push_back(cand_sink_pb_pin);
} else if ( (true == cand_sink_pb_pin->parent_node->is_root())
&& (OUT_PORT == cand_sink_pb_pin->port->type)) {
sink_pb_pins.push_back(cand_sink_pb_pin);
} else {
sink_pb_pins_to_search.push_back(cand_sink_pb_pin);
}
}
}
for (t_pb_graph_pin* sink_pb_pin : sink_pb_pins_to_search) {
bool direct_search_status = rec_direct_search_sink_pb_graph_pins(sink_pb_pin, sink_pb_pins);
if (false == direct_search_status) {
return false;
}
}
/* Reach here, we succeed. */
return true;
}
/*************************************************************************************** /***************************************************************************************
* Try find all the sink pins which is mapped to a routing trace in the context of pb route * Try find all the sink pins which is mapped to a routing trace in the context of pb route
* This function uses a recursive walk-through over the pb_route * This function uses a recursive walk-through over the pb_route
@ -38,6 +105,8 @@ static
void rec_find_routed_sink_pb_graph_pins(const t_pb* pb, void rec_find_routed_sink_pb_graph_pins(const t_pb* pb,
const t_pb_graph_pin* source_pb_pin, const t_pb_graph_pin* source_pb_pin,
const AtomNetId& atom_net_id, const AtomNetId& atom_net_id,
const VprDeviceAnnotation& device_annotation,
const std::map<const t_pb_graph_pin*, AtomNetId>& pb_pin_mapped_nets,
t_pb_graph_pin** pb_graph_pin_lookup_from_index, t_pb_graph_pin** pb_graph_pin_lookup_from_index,
std::vector<t_pb_graph_pin*>& sink_pb_pins) { std::vector<t_pb_graph_pin*>& sink_pb_pins) {
@ -69,7 +138,49 @@ void rec_find_routed_sink_pb_graph_pins(const t_pb* pb,
if ( (true == sink_pb_pin->parent_node->is_root()) if ( (true == sink_pb_pin->parent_node->is_root())
&& (OUT_PORT == sink_pb_pin->port->type)) { && (OUT_PORT == sink_pb_pin->port->type)) {
/* Be careful!!! There is an inconsistency between pb_route and actual net mapping!
* The sink_pb_pin in the pb_route may not be the one we want
* due to net remapping in the routing stage
* If the net becomes invalid, we search all the fan-out of the source pb_pin
* and find one that is mapped to the net
*/
AtomNetId remapped_net = AtomNetId::INVALID();
auto remapped_result = pb_pin_mapped_nets.find(sink_pb_pin);
if (remapped_result != pb_pin_mapped_nets.end()) {
remapped_net = remapped_result->second;
}
if (atom_net_id == remapped_net) {
sink_pb_pins.push_back(sink_pb_pin); sink_pb_pins.push_back(sink_pb_pin);
} else {
VTR_ASSERT_SAFE(atom_net_id != remapped_net);
bool found_actual_sink_pb_pin = false;
for (int iedge = 0; iedge < source_pb_pin->num_output_edges; ++iedge) {
/* Bypass the interconnect that does not belong to a physical mode */
int parent_mode_index = source_pb_pin->output_edges[iedge]->interconnect->parent_mode_index;
VTR_ASSERT(parent_mode_index < sink_pb_pin->parent_node->pb_type->num_modes);
if (&(sink_pb_pin->parent_node->pb_type->modes[parent_mode_index])
!= device_annotation.physical_mode(sink_pb_pin->parent_node->pb_type)) {
continue;
}
for (int ipin = 0; ipin < source_pb_pin->output_edges[iedge]->num_output_pins; ++ipin) {
const t_pb_graph_pin* cand_sink_pb_pin = source_pb_pin->output_edges[iedge]->output_pins[ipin];
auto cand_remapped_result = pb_pin_mapped_nets.find(cand_sink_pb_pin);
AtomNetId cand_sink_pb_pin_net = AtomNetId::INVALID();
if (cand_remapped_result != pb_pin_mapped_nets.end()) {
cand_sink_pb_pin_net = cand_remapped_result->second;
}
if (atom_net_id == cand_sink_pb_pin_net) {
sink_pb_pins.push_back(const_cast<t_pb_graph_pin*>(cand_sink_pb_pin));
found_actual_sink_pb_pin = true;
break;
}
}
if (true == found_actual_sink_pb_pin) {
break;
}
}
VTR_ASSERT(true == found_actual_sink_pb_pin);
}
continue; continue;
} }
@ -78,7 +189,7 @@ void rec_find_routed_sink_pb_graph_pins(const t_pb* pb,
} }
for (t_pb_graph_pin* sink_pb_pin : sink_pb_pins_to_search) { for (t_pb_graph_pin* sink_pb_pin : sink_pb_pins_to_search) {
rec_find_routed_sink_pb_graph_pins(pb, sink_pb_pin, atom_net_id, pb_graph_pin_lookup_from_index, sink_pb_pins); rec_find_routed_sink_pb_graph_pins(pb, sink_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index, sink_pb_pins);
} }
} }
@ -89,11 +200,32 @@ void rec_find_routed_sink_pb_graph_pins(const t_pb* pb,
static static
std::vector<t_pb_graph_pin*> find_routed_pb_graph_pins_atom_net(const t_pb* pb, std::vector<t_pb_graph_pin*> find_routed_pb_graph_pins_atom_net(const t_pb* pb,
const t_pb_graph_pin* source_pb_pin, const t_pb_graph_pin* source_pb_pin,
const t_pb_graph_pin* packing_source_pb_pin,
const AtomNetId& atom_net_id, const AtomNetId& atom_net_id,
const VprDeviceAnnotation& device_annotation,
const std::map<const t_pb_graph_pin*, AtomNetId>& pb_pin_mapped_nets,
t_pb_graph_pin** pb_graph_pin_lookup_from_index) { t_pb_graph_pin** pb_graph_pin_lookup_from_index) {
std::vector<t_pb_graph_pin*> sink_pb_pins; std::vector<t_pb_graph_pin*> sink_pb_pins;
rec_find_routed_sink_pb_graph_pins(pb, source_pb_pin, atom_net_id, pb_graph_pin_lookup_from_index, sink_pb_pins); /* Try to directly search for sink pb_pins from the source_pb_pin,
* which is the actual source pin to be routed from
* Note that the packing source_pb_pin is the source pin considered by
* VPR packer, but may not be the actual source!!!
*/
if (true == source_pb_pin->parent_node->is_root()) {
bool direct_search_status = rec_direct_search_sink_pb_graph_pins(source_pb_pin, sink_pb_pins);
if (true == direct_search_status) {
VTR_ASSERT(!sink_pb_pins.empty());
/* We have find through direct searching, return now */
return sink_pb_pins;
}
/* Cannot find through direct searching, reset results */
VTR_ASSERT_SAFE(false == direct_search_status);
sink_pb_pins.clear();
}
rec_find_routed_sink_pb_graph_pins(pb, packing_source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index, sink_pb_pins);
return sink_pb_pins; return sink_pb_pins;
} }
@ -227,6 +359,34 @@ void add_lb_router_nets(LbRouter& lb_router,
/* Build the fast look-up between pb_pin_id and pb_graph_pin pointer */ /* Build the fast look-up between pb_pin_id and pb_graph_pin pointer */
t_pb_graph_pin** pb_graph_pin_lookup_from_index = alloc_and_load_pb_graph_pin_lookup_from_index(lb_type); t_pb_graph_pin** pb_graph_pin_lookup_from_index = alloc_and_load_pb_graph_pin_lookup_from_index(lb_type);
/* Build a fast look-up between pb_graph_pin and atom net id which it is mapped to
* Note that, we only care the pb_graph_pin at the root pb_graph_node
* where pb_graph_pin may be remapped to a new net due to routing optimization
*/
std::map<const t_pb_graph_pin*, AtomNetId> pb_pin_mapped_nets;
for (int j = 0; j < lb_type->pb_type->num_pins; j++) {
/* Find the net mapped to this pin in clustering results*/
ClusterNetId cluster_net_id = clustering_ctx.clb_nlist.block_net(block_id, j);
/* Get the actual net id because it may be renamed during routing */
if (true == clustering_annotation.is_net_renamed(block_id, j)) {
cluster_net_id = clustering_annotation.net(block_id, j);
}
/* Bypass unmapped pins */
if (ClusterNetId::INVALID() == cluster_net_id) {
continue;
}
/* Get the source pb_graph pin and find the rr_node in logical block routing resource graph */
const t_pb_graph_pin* pb_pin = get_pb_graph_node_pin_from_block_pin(block_id, j);
VTR_ASSERT(pb_pin->parent_node == pb->pb_graph_node);
AtomNetId atom_net_id = atom_ctx.lookup.atom_net(cluster_net_id);
VTR_ASSERT(AtomNetId::INVALID() != atom_net_id);
pb_pin_mapped_nets[pb_pin] = atom_net_id;
}
/* Cache all the source nodes and sinks node for each net /* Cache all the source nodes and sinks node for each net
* net_terminal[net][0] is the list of source nodes * net_terminal[net][0] is the list of source nodes
* net_terminal[net][1] is the list of sink nodes * net_terminal[net][1] is the list of sink nodes
@ -238,17 +398,6 @@ void add_lb_router_nets(LbRouter& lb_router,
/* Find the source nodes for the nets mapped to inputs of a clustered block */ /* Find the source nodes for the nets mapped to inputs of a clustered block */
for (int j = 0; j < lb_type->pb_type->num_pins; j++) { for (int j = 0; j < lb_type->pb_type->num_pins; j++) {
/* Find the net mapped to this pin in clustering results*/
ClusterNetId cluster_net_id = clustering_ctx.clb_nlist.block_net(block_id, j);
/* Get the actual net id because it may be renamed during routing */
if (true == clustering_annotation.is_net_renamed(block_id, j)) {
cluster_net_id = clustering_annotation.net(block_id, j);
}
/* Bypass unmapped pins */
if (ClusterNetId::INVALID() == cluster_net_id) {
continue;
}
/* Get the source pb_graph pin and find the rr_node in logical block routing resource graph */ /* Get the source pb_graph pin and find the rr_node in logical block routing resource graph */
const t_pb_graph_pin* source_pb_pin = get_pb_graph_node_pin_from_block_pin(block_id, j); const t_pb_graph_pin* source_pb_pin = get_pb_graph_node_pin_from_block_pin(block_id, j);
VTR_ASSERT(source_pb_pin->parent_node == pb->pb_graph_node); VTR_ASSERT(source_pb_pin->parent_node == pb->pb_graph_node);
@ -258,22 +407,25 @@ void add_lb_router_nets(LbRouter& lb_router,
continue; continue;
} }
/* Find the net mapped to this pin in clustering results*/
AtomNetId atom_net_id = pb_pin_mapped_nets[source_pb_pin];
/* Bypass unmapped pins */
if (AtomNetId::INVALID() == atom_net_id) {
continue;
}
/* The outputs of pb_graph_node is INTERMEDIATE node in the routing resource graph, /* The outputs of pb_graph_node is INTERMEDIATE node in the routing resource graph,
* they are all connected to a common source node * they are all connected to a common source node
*/ */
LbRRNodeId source_lb_rr_node = lb_rr_graph.find_node(LB_INTERMEDIATE, source_pb_pin); LbRRNodeId source_lb_rr_node = lb_rr_graph.find_node(LB_INTERMEDIATE, source_pb_pin);
VTR_ASSERT(true == lb_rr_graph.valid_node_id(source_lb_rr_node)); VTR_ASSERT(true == lb_rr_graph.valid_node_id(source_lb_rr_node));
AtomNetId atom_net_id = atom_ctx.lookup.atom_net(cluster_net_id);
VTR_ASSERT(AtomNetId::INVALID() != atom_net_id);
int pb_route_index = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id); int pb_route_index = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id);
t_pb_graph_pin* packing_source_pb_pin = get_pb_graph_node_pin_from_block_pin(block_id, pb_route_index); t_pb_graph_pin* packing_source_pb_pin = get_pb_graph_node_pin_from_block_pin(block_id, pb_route_index);
VTR_ASSERT(nullptr != packing_source_pb_pin); VTR_ASSERT(nullptr != packing_source_pb_pin);
/* Find all the sink pins in the pb_route, we walk through the input pins and find the pin */ /* Find all the sink pins in the pb_route, we walk through the input pins and find the pin */
std::vector<t_pb_graph_pin*> sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, packing_source_pb_pin, atom_net_id, pb_graph_pin_lookup_from_index); std::vector<t_pb_graph_pin*> sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, source_pb_pin, packing_source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index);
std::vector<LbRRNodeId> sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation); std::vector<LbRRNodeId> sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation);
VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size());
@ -338,7 +490,7 @@ void add_lb_router_nets(LbRouter& lb_router,
VTR_ASSERT(AtomNetId::INVALID() != atom_net_id); VTR_ASSERT(AtomNetId::INVALID() != atom_net_id);
/* Find all the sink pins in the pb_route */ /* Find all the sink pins in the pb_route */
std::vector<t_pb_graph_pin*> sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, source_pb_pin, atom_net_id, pb_graph_pin_lookup_from_index); std::vector<t_pb_graph_pin*> sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, physical_source_pb_pin, source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index);
std::vector<LbRRNodeId> sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation); std::vector<LbRRNodeId> sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation);
VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size());

View File

@ -251,6 +251,29 @@ std::vector<std::string> find_circuit_library_unique_verilog_netlists(const Circ
return netlists; return netlists;
} }
/********************************************************************
* A generic function to find all the unique user-defined
* Verilog netlists in a circuit library
* Netlists with same names will be considered as one
*******************************************************************/
std::vector<std::string> find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib) {
std::vector<std::string> netlists;
for (const CircuitModelId& model : circuit_lib.models()) {
/* Skip empty netlist names */
if (true == circuit_lib.model_spice_netlist(model).empty()) {
continue;
}
/* See if the netlist name is already in the list */
std::vector<std::string>::iterator it = std::find(netlists.begin(), netlists.end(), circuit_lib.model_spice_netlist(model));
if (it == netlists.end()) {
netlists.push_back(circuit_lib.model_spice_netlist(model));
}
}
return netlists;
}
/************************************************************************ /************************************************************************
* Advanced check if the circuit model of configurable memory * Advanced check if the circuit model of configurable memory
* satisfy the needs of configuration protocol * satisfy the needs of configuration protocol

View File

@ -39,6 +39,8 @@ std::vector<CircuitPortId> find_circuit_library_global_ports(const CircuitLibrar
std::vector<std::string> find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib); std::vector<std::string> find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib);
std::vector<std::string> find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib);
bool check_configurable_memory_circuit_model(const e_config_protocol_type& config_protocol_type, bool check_configurable_memory_circuit_model(const e_config_protocol_type& config_protocol_type,
const CircuitLibrary& circuit_lib, const CircuitLibrary& circuit_lib,
const CircuitModelId& config_mem_circuit_model); const CircuitModelId& config_mem_circuit_model);

View File

@ -324,4 +324,42 @@ std::vector<t_port*> find_pb_type_ports_match_circuit_model_port_type(t_pb_type*
return ports; return ports;
} }
/*********************************************************************
* Generate the full hierarchy for a pb_type
* The final name will be in the following format:
* <top_pb_type_name>[<mode_name>].<parent_pb_type_name> ... <current_pb_type_name>
*
* TODO: This function should be part of the VPR libarchfpga parser
**********************************************************************/
std::string generate_pb_type_hierarchy_path(t_pb_type* cur_pb_type) {
std::string hie_name(cur_pb_type->name);
t_pb_type* parent_pb_type = cur_pb_type;
/* Backward trace until we meet the top-level pb_type */
while (1) {
/* If there is no parent mode, this is a top-level pb_type, quit the loop here */
t_mode* parent_mode = parent_pb_type->parent_mode;
if (NULL == parent_mode) {
break;
}
/* Add the mode name to the full hierarchy */
hie_name = std::string("[") + std::string(parent_mode->name) + std::string("].") + hie_name;
/* Backtrace to the upper level */
parent_pb_type = parent_mode->parent_pb_type;
/* If there is no parent pb_type, this is a top-level pb_type, quit the loop here */
if (NULL == parent_pb_type) {
break;
}
/* Add the current pb_type name to the hierarchy name */
hie_name = std::string(parent_pb_type->name) + hie_name;
}
return hie_name;
}
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -57,6 +57,8 @@ std::vector<t_port*> find_pb_type_ports_match_circuit_model_port_type(t_pb_type*
const e_circuit_model_port_type& port_type, const e_circuit_model_port_type& port_type,
const VprDeviceAnnotation& vpr_device_annotation); const VprDeviceAnnotation& vpr_device_annotation);
std::string generate_pb_type_hierarchy_path(t_pb_type* cur_pb_type);
} /* end namespace openfpga */ } /* end namespace openfpga */
#endif #endif

View File

@ -0,0 +1,74 @@
# Run VPR for the 'and' design
#--write_rr_graph example_rr_graph.xml
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --device ${OPENFPGA_VPR_DEVICE_LAYOUT}
# Read OpenFPGA architecture definition
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
# Read OpenFPGA simulation settings
read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
# Annotate the OpenFPGA architecture to VPR data base
# to debug use --verbose options
link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
# Check and correct any naming conflicts in the BLIF netlist
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
# Apply fix-up to clustering nets based on routing results
pb_pin_fixup --verbose
# Apply fix-up to Look-Up Table truth tables based on packing results
lut_truth_table_fixup
# Build the module graph
# - Enabled compression on routing architecture modules
# - Enable pin duplication on grid modules
build_fabric --compress_routing #--verbose
# Write the fabric hierarchy of module graph to a file
# This is used by hierarchical PnR flows
write_fabric_hierarchy --file ./fabric_hierarchy.txt
# Repack the netlist to physical pbs
# This must be done before bitstream generator and testbench generation
# Strongly recommend it is done after all the fix-up have been applied
repack #--verbose
# Build the bitstream
# - Output the fabric-independent bitstream to a file
build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml
# Build fabric-dependent bitstream
build_fabric_bitstream --verbose
# Write fabric-dependent bitstream
write_fabric_bitstream --file fabric_bitstream.xml --format xml
# Write the Verilog netlist for FPGA fabric
# - Enable the use of explicit port mapping in Verilog netlist
write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose
# Write the Verilog testbench for FPGA fabric
# - We suggest the use of same output directory as fabric Verilog netlists
# - Must specify the reference benchmark file if you want to output any testbenches
# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA
# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase
# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts
write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --explicit_port_mapping
# Write the SDC files for PnR backend
# - Turn on every options here
write_pnr_sdc --file ./SDC
# Write SDC to disable timing for configure ports
write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc
# Write the SDC to run timing analysis for a mapped FPGA fabric
write_analysis_sdc --file ./SDC_analysis
# Finish and exit OpenFPGA
exit
# Note :
# To run verification at the end of the flow maintain source in ./SRC directory

View File

@ -1,6 +1,6 @@
# Run VPR for the 'and' design # Run VPR for the 'and' design
#--write_rr_graph example_rr_graph.xml #--write_rr_graph example_rr_graph.xml
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --device ${OPENFPGA_VPR_DEVICE_LAYOUT}
# Read OpenFPGA architecture definition # Read OpenFPGA architecture definition
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}

View File

@ -1,6 +1,6 @@
# Run VPR for the 'and' design # Run VPR for the 'and' design
#--write_rr_graph example_rr_graph.xml #--write_rr_graph example_rr_graph.xml
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --absorb_buffer_luts off
# Read OpenFPGA architecture definition # Read OpenFPGA architecture definition
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}

Some files were not shown because too many files have changed in this diff Show More