Merge remote-tracking branch 'lnis_origin/master' into ganesh_dev
This commit is contained in:
commit
e31589f9b6
|
@ -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.
|
|
@ -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.
|
90
.travis.yml
90
.travis.yml
|
@ -15,7 +15,7 @@ cache:
|
|||
|
||||
# Supported Operating systems
|
||||
dist: bionic
|
||||
compiler: g++-8
|
||||
#compiler: g++-8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
|
@ -33,8 +33,6 @@ addons:
|
|||
- doxygen
|
||||
- flex
|
||||
- fontconfig
|
||||
- g++-8
|
||||
- gcc-8
|
||||
- gdb
|
||||
- git
|
||||
- gperf
|
||||
|
@ -56,6 +54,19 @@ addons:
|
|||
- zip
|
||||
- qt5-default
|
||||
- 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
|
||||
# osx_image: xcode10.2 # we target latest MacOS Mojave
|
||||
# sudo: true
|
||||
|
@ -76,6 +87,10 @@ addons:
|
|||
# - libxml++
|
||||
# - qt5
|
||||
|
||||
# Use gcc-8 as default compiler
|
||||
env:
|
||||
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
|
||||
|
||||
before_script:
|
||||
- source .travis/common.sh
|
||||
- source .travis/install.sh
|
||||
|
@ -96,7 +111,74 @@ jobs:
|
|||
name: "FPGA-Verilog regression tests"
|
||||
script:
|
||||
- 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:
|
||||
# - .travis/after_failure.sh
|
||||
|
|
|
@ -11,40 +11,45 @@ cd ${TRAVIS_BUILD_DIR}
|
|||
echo -e "Basic regression tests";
|
||||
|
||||
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 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/full_testbench/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 basic_tests/preconfig_testbench/configuration_chain --debug --show_thread_logs
|
||||
|
||||
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 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/full_testbench/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 basic_tests/preconfig_testbench/configuration_frame --debug --show_thread_logs
|
||||
|
||||
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 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/full_testbench/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 basic_tests/preconfig_testbench/memory_bank --debug --show_thread_logs
|
||||
|
||||
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 preconfig_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 basic_tests/preconfig_testbench/flatten_memory --debug --show_thread_logs
|
||||
|
||||
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";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py 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
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_testbench --debug --show_thread_logs
|
||||
|
||||
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";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py sdc_time_unit --debug --show_thread_logs
|
||||
|
||||
echo -e "Testing FPGA-SPICE with netlist generation";
|
||||
python3 openfpga_flow/scripts/run_fpga_task.py fpga_spice/generate_spice --debug --show_thread_logs
|
||||
echo -e "Testing K4 series FPGA";
|
||||
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 K4N4 with hard adders";
|
||||
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"
|
||||
|
|
|
@ -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
|
||||
else
|
||||
# For linux, we use g++-8 and gcc-8 as default compilers
|
||||
export CC=gcc-8
|
||||
export CXX=g++-8
|
||||
eval "${MATRIX_EVAL}"
|
||||
export "CC=$CC"
|
||||
export "CXX=$CXX"
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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/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/*
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# 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)
|
||||
|
||||
## Introduction
|
||||
|
|
|
@ -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 |
|
@ -52,7 +52,7 @@ Layout
|
|||
|
||||
.. 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:
|
||||
|
|
|
@ -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
|
||||
|
||||
.. option:: <pb_type name="<string>" physical_pb_type_name="<string>" circuit_model_name="<string>"
|
||||
mode_bits="<int>" physical_pb_type_index_factor="<float>" physical_pb_type_index_offset="<int>">
|
||||
.. option:: <pb_type name="<string>" physical_pb_type_name="<string>"
|
||||
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
|
||||
|
||||
.. 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.
|
||||
|
||||
- ``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>">
|
||||
|
||||
- ``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.
|
||||
|
||||
.. 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``
|
||||
|
||||
|
@ -136,7 +142,38 @@ 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_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:: 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.
|
||||
|
||||
.. 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::
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
||||
.. 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.
|
||||
|
||||
|
|
|
@ -67,26 +67,13 @@ In principle, the file consist a number of XML node ``<bit>``, each bit contains
|
|||
|
||||
- ``value``: The configuration bit value.
|
||||
|
||||
- ``hierarchy`` represents the location of this block in 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.
|
||||
- ``path`` represents the location of this block in FPGA fabric, i.e., the full path in the hierarchy of FPGA fabric.
|
||||
|
||||
A quick example:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<bit id="0" value="1">
|
||||
<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 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]"/>
|
||||
</bit>
|
||||
|
||||
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
|
||||
|
||||
<bit id="0" value="1">
|
||||
<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>
|
||||
<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]"/>
|
||||
<bl address="000000"/>
|
||||
<wl address="000000"/>
|
||||
</bit>
|
||||
|
@ -121,13 +101,6 @@ Other information may depend on the type of configuration procotol.
|
|||
|
||||
.. code-block:: xml
|
||||
|
||||
<bit id="0" value="1">
|
||||
<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>
|
||||
<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]"/>
|
||||
<frame address="0000000000000000"/>
|
||||
</bit>
|
||||
|
|
|
@ -9,7 +9,7 @@ OpenFPGA aims to be an open-source framework that enables rapid prototyping of c
|
|||
:scale: 50%
|
||||
: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.
|
||||
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.
|
||||
|
|
|
@ -7,6 +7,6 @@ Design Flows
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
blif_to_verification
|
||||
verilog2verification
|
||||
|
||||
verilog_to_gds2
|
||||
verilog2gds2
|
||||
|
|
|
@ -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
|
||||
- generate Verilog netlists for a FPGA fabric
|
|
@ -197,6 +197,43 @@ size_t check_circuit_model_port_required(const CircuitLibrary& circuit_lib,
|
|||
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
|
||||
* 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;
|
||||
|
||||
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)]);
|
||||
exit(1);
|
||||
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);
|
||||
|
||||
/* 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_CHAN_WIRE);
|
||||
num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_WIRE);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
#include "openfpga_port_parser.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];
|
||||
}
|
||||
|
||||
/* Access the path + file of user-defined circuit netlist of a circuit model */
|
||||
std::string CircuitLibrary::model_circuit_netlist(const CircuitModelId& model_id) const {
|
||||
/* Access the path + file of user-defined spice netlist of a circuit model */
|
||||
std::string CircuitLibrary::model_spice_netlist(const CircuitModelId& model_id) const {
|
||||
/* validate the 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 */
|
||||
|
@ -1131,7 +1132,7 @@ CircuitModelId CircuitLibrary::add_model(const enum e_circuit_model_type& type)
|
|||
model_names_.emplace_back();
|
||||
model_prefix_.emplace_back();
|
||||
model_verilog_netlists_.emplace_back();
|
||||
model_circuit_netlists_.emplace_back();
|
||||
model_spice_netlists_.emplace_back();
|
||||
model_is_default_.push_back(false);
|
||||
sub_models_.emplace_back();
|
||||
|
||||
|
@ -1225,11 +1226,11 @@ void CircuitLibrary::set_model_verilog_netlist(const CircuitModelId& model_id, c
|
|||
return;
|
||||
}
|
||||
|
||||
/* Set the circuit_netlist of a Circuit Model */
|
||||
void CircuitLibrary::set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist) {
|
||||
/* Set the spice_netlist of a Circuit Model */
|
||||
void CircuitLibrary::set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist) {
|
||||
/* validate the model_id */
|
||||
VTR_ASSERT(valid_model_id(model_id));
|
||||
model_circuit_netlists_[model_id] = circuit_netlist;
|
||||
model_spice_netlists_[model_id] = spice_netlist;
|
||||
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*/
|
||||
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()) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
***********************************************************************/
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
* 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
|
||||
* 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
|
||||
* 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.
|
||||
|
@ -190,7 +190,7 @@ class CircuitLibrary {
|
|||
std::string model_name(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_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 dump_structural_verilog(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_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_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);
|
||||
/* Verilog generator options */
|
||||
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 */
|
||||
void build_model_links();
|
||||
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 */
|
||||
void add_edge(const CircuitModelId& model_id,
|
||||
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_prefix_;
|
||||
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_;
|
||||
|
||||
/* Submodules that a circuit model contains */
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/************************************************************************
|
||||
* Member functions for class PbTypeAnnotation
|
||||
***********************************************************************/
|
||||
#include <algorithm>
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "pb_type_annotation.h"
|
||||
|
@ -85,24 +86,15 @@ std::vector<std::string> PbTypeAnnotation::port_names() const {
|
|||
return keys;
|
||||
}
|
||||
|
||||
BasicPort 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<BasicPort, std::array<int, 2>> PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const {
|
||||
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()) {
|
||||
/* Return an empty port */
|
||||
return BasicPort();
|
||||
return std::map<BasicPort, std::array<int, 2>>();
|
||||
}
|
||||
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> keys;
|
||||
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,
|
||||
const BasicPort& physical_pb_port) {
|
||||
/* 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);
|
||||
/* 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());
|
||||
}
|
||||
std::map<std::string, std::map<BasicPort, std::array<int, 2>>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name);
|
||||
|
||||
operating_pb_type_ports_[operating_pb_port_name] = physical_pb_port;
|
||||
}
|
||||
|
||||
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 not exist, initialize and set a default value */
|
||||
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_port_name.c_str(), physical_pin_rotate_offset);
|
||||
operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0};
|
||||
/* We can return early */
|
||||
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,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*******************************************************************/
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
#include "openfpga_port.h"
|
||||
|
||||
|
@ -48,8 +49,7 @@ class PbTypeAnnotation {
|
|||
float physical_pb_type_index_factor() const;
|
||||
int physical_pb_type_index_offset() const;
|
||||
std::vector<std::string> port_names() const;
|
||||
BasicPort physical_pb_type_port(const std::string& port_name) const;
|
||||
int physical_pin_rotate_offset(const std::string& port_name) const;
|
||||
std::map<BasicPort, std::array<int, 2>> physical_pb_type_port(const std::string& port_name) const;
|
||||
std::vector<std::string> interconnect_names() const;
|
||||
std::string interconnect_circuit_model_name(const std::string& interc_name) const;
|
||||
public: /* Public mutators */
|
||||
|
@ -67,7 +67,11 @@ class PbTypeAnnotation {
|
|||
void set_physical_pb_type_index_offset(const int& value);
|
||||
void add_pb_type_port_pair(const std::string& operating_pb_port_name,
|
||||
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,
|
||||
const BasicPort& physical_pb_port,
|
||||
const int& physical_pin_rotate_offset);
|
||||
void add_interconnect_circuit_model_pair(const std::string& interc_name,
|
||||
const std::string& circuit_model_name);
|
||||
|
@ -133,10 +137,13 @@ class PbTypeAnnotation {
|
|||
*/
|
||||
int physical_pb_type_index_offset_;
|
||||
|
||||
/* Link from the pins under an operating pb_type to physical pb_type */
|
||||
std::map<std::string, BasicPort> operating_pb_type_ports_;
|
||||
|
||||
/* The offset aims to align the pin indices for port of pb_type
|
||||
/* Link from the pins under an operating pb_type to pairs of
|
||||
* its physical pb_type and its pin initial & rotating offset
|
||||
*
|
||||
* 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
|
||||
* mode contains multiple pb_type (num_pb>1) that are linked to
|
||||
* the same physical pb_type.
|
||||
|
@ -144,11 +151,21 @@ class PbTypeAnnotation {
|
|||
* the pin index of pb_type (whose index is large than 1)
|
||||
* will be shifted by the given offset.
|
||||
*
|
||||
* For example, an offset of 1 is used to map
|
||||
* operating pb_type adder[0].pin[0] with a full path clb.fle[arith].adder[0]
|
||||
* to physical pb_type adder[0].pin[1] with a full path clb.fle[physical].adder[0]
|
||||
* 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]
|
||||
*
|
||||
* 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 */
|
||||
std::map<std::string, std::string> interconnect_circuit_model_names_;
|
||||
|
|
|
@ -459,7 +459,7 @@ void read_xml_output_mask(pugi::xml_node& xml_port,
|
|||
if (circuit_lib.port_size(port) != mask_vector.size()) {
|
||||
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",
|
||||
output_mask_attr, circuit_lib.port_size(port));
|
||||
output_mask_attr.c_str(), circuit_lib.port_size(port));
|
||||
}
|
||||
} else {
|
||||
/* 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));
|
||||
|
||||
/* 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*/
|
||||
circuit_lib.set_model_verilog_netlist(model, get_attribute(xml_model, "verilog_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(""));
|
||||
|
|
|
@ -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);
|
||||
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 */
|
||||
openfpga_arch.circuit_lib.build_model_links();
|
||||
|
||||
|
|
|
@ -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& physical_mode_port_attr = get_attribute(xml_port, "physical_mode_port", loc_data).as_string();
|
||||
|
||||
/* Parse the mode port using openfpga port parser */
|
||||
openfpga::PortParser port_parser(physical_mode_port_attr);
|
||||
pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port());
|
||||
/* Split the physical mode port attributes with space */
|
||||
openfpga::StringToken port_tokenizer(physical_mode_port_attr);
|
||||
const std::vector<std::string> physical_mode_ports = port_tokenizer.split();
|
||||
|
||||
/* We have an optional attribute: physical_mode_pin_rotate_offset */
|
||||
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));
|
||||
/* Parse the mode port using openfpga port parser */
|
||||
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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
|
|
@ -409,8 +409,8 @@ void write_xml_circuit_model(std::fstream& fp,
|
|||
if (true == circuit_lib.dump_structural_verilog(model)) {
|
||||
write_xml_attribute(fp, "dump_structural_verilog", "true");
|
||||
}
|
||||
if (!circuit_lib.model_circuit_netlist(model).empty()) {
|
||||
write_xml_attribute(fp, "circuit_netlist", circuit_lib.model_circuit_netlist(model).c_str());
|
||||
if (!circuit_lib.model_spice_netlist(model).empty()) {
|
||||
write_xml_attribute(fp, "spice_netlist", circuit_lib.model_spice_netlist(model).c_str());
|
||||
}
|
||||
if (!circuit_lib.model_verilog_netlist(model).empty()) {
|
||||
write_xml_attribute(fp, "verilog_netlist", circuit_lib.model_verilog_netlist(model).c_str());
|
||||
|
|
|
@ -116,8 +116,33 @@ void write_xml_pb_port_annotation(std::fstream& fp,
|
|||
fp << "\t\t\t" << "<port";
|
||||
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -55,4 +55,21 @@ std::vector<ConfigBlockId> find_bitstream_manager_top_blocks(const BitstreamMana
|
|||
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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager,
|
||||
const ConfigBitId& bit_id);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -67,7 +67,7 @@ bool parse_command(const std::vector<std::string>& argv,
|
|||
/* Validate that the command name matches argv[0] */
|
||||
if (argv[0] != cmd.name()) {
|
||||
VTR_LOG("Unexpected command name '%s'!\n",
|
||||
argv[0]);
|
||||
argv[0].c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
/********************************************************************
|
||||
|
|
|
@ -124,6 +124,16 @@ bool BasicPort::operator== (const BasicPort& portA) const {
|
|||
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
|
||||
***********************************************************************/
|
||||
|
|
|
@ -21,6 +21,7 @@ class BasicPort {
|
|||
BasicPort(const BasicPort& basic_port); /* Copy constructor */
|
||||
public: /* Overloaded operators */
|
||||
bool operator== (const BasicPort& portA) const;
|
||||
bool operator< (const BasicPort& portA) const;
|
||||
public: /* Accessors */
|
||||
size_t get_width() const; /* get the port width */
|
||||
size_t get_msb() const; /* get the LSB */
|
||||
|
|
|
@ -140,7 +140,7 @@ float string_to_unit(const std::string& scale) {
|
|||
/* Invalid unit report error */
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid unit %s!\nAcceptable units are [a|f|p|n|u|k|M|B|T] or empty\n",
|
||||
scale);
|
||||
scale.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -153,13 +153,13 @@ float string_to_time_unit(const std::string& scale) {
|
|||
&& (2 != scale.length()) ) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Time unit (='%s') must contain only one or two characters!\n",
|
||||
scale);
|
||||
scale.c_str());
|
||||
}
|
||||
/* The last character must be 's' */
|
||||
if ('s' != scale.back()) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Time unit (='%s') must end with 's'!\n",
|
||||
scale);
|
||||
scale.c_str());
|
||||
}
|
||||
|
||||
float unit = 1.;
|
||||
|
|
|
@ -306,35 +306,50 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin,
|
|||
t_pb_graph_pin* physical_pb_graph_pin,
|
||||
const VprDeviceAnnotation& vpr_device_annotation) {
|
||||
/* 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)) {
|
||||
return false;
|
||||
}
|
||||
/* Check the pin number of physical pb_graph_pin matches the pin number of
|
||||
* operating pb_graph_pin plus a rotation offset
|
||||
* operating port physical port
|
||||
* LSB port_range.lsb() pin_number pin_number MSB
|
||||
* | | |
|
||||
* Operating port | | +------ |
|
||||
* | |<----offset--->|
|
||||
* Physical port | + + +
|
||||
*
|
||||
* Note:
|
||||
* - accumulated offset is NOT the pin rotate offset specified by users
|
||||
* It is an aggregation of the offset during pin pairing
|
||||
* Each time, we manage to pair two pins, the accumulated offset will be incremented
|
||||
* by the pin rotate offset value
|
||||
* 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);
|
||||
const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port);
|
||||
if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number
|
||||
+ (int)physical_port_range.get_lsb()
|
||||
+ acc_offset) {
|
||||
return false;
|
||||
for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) {
|
||||
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
|
||||
* 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
|
||||
* LSB port_range.lsb() pin_number pin_number MSB
|
||||
* | | init_offset |
|
||||
* Operating port | | +------ + |
|
||||
* | |<----acc_offset--->|
|
||||
* Physical port | + + +
|
||||
*
|
||||
* Note:
|
||||
* - accumulated offset is NOT the pin rotate offset specified by users
|
||||
* It is an aggregation of the offset during pin pairing
|
||||
* Each time, we manage to pair two pins, the accumulated offset will be incremented
|
||||
* by the pin rotate offset value
|
||||
* 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, candidate_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
|
||||
+ (int)physical_port_range.get_lsb()
|
||||
+ init_offset
|
||||
+ acc_offset) {
|
||||
/* Not the one we want, try the next candidate */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Reach here, it means all the requirements have been met */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Reach here, it means all the requirements have been met */
|
||||
return true;
|
||||
/* If we reach here, we failed */
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -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! */
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -211,32 +211,39 @@ 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
|
||||
*/
|
||||
for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) {
|
||||
/* Try to find the port in the pb_type_annotation */
|
||||
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()) {
|
||||
/* Not found, we reset the port information to be consistent as the operating pb_port */
|
||||
std::map<BasicPort, std::array<int, 2>> expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name));
|
||||
|
||||
/* If not defined in the annotation, set the default pair:
|
||||
* 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_width(operating_pb_port->num_pins);
|
||||
expected_physical_pb_ports[expected_physical_pb_port] = {0, 0};
|
||||
}
|
||||
|
||||
/* 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());
|
||||
/* Not found, mapping fails */
|
||||
if (nullptr == physical_pb_port) {
|
||||
return false;
|
||||
for (const auto& expected_physical_pb_port : expected_physical_pb_ports) {
|
||||
/* 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.first.get_name());
|
||||
/* Not found, mapping fails */
|
||||
if (nullptr == physical_pb_port) {
|
||||
return false;
|
||||
}
|
||||
/* 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.first)) {
|
||||
return false;
|
||||
}
|
||||
/* Now, port mapping should succeed, we update the vpr_device_annotation
|
||||
* - port binding
|
||||
* - port range
|
||||
* - port pin rotate offset
|
||||
*/
|
||||
vpr_device_annotation.add_physical_pb_port(operating_pb_port, 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_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]);
|
||||
}
|
||||
/* 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)) {
|
||||
return false;
|
||||
}
|
||||
/* Now, port mapping should succeed, we update the vpr_device_annotation
|
||||
* - port binding
|
||||
* - port range
|
||||
* - port pin rotate offset
|
||||
*/
|
||||
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_pin_rotate_offset(operating_pb_port, pb_type_annotation.physical_pin_rotate_offset(std::string(operating_pb_port->name)));
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
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_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 */
|
||||
|
|
|
@ -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 */
|
||||
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",
|
||||
pb_port->name, cur_pb_type->name);
|
||||
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 */
|
||||
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",
|
||||
cur_pb_type->name);
|
||||
generate_pb_type_hierarchy_path(cur_pb_type).c_str());
|
||||
num_err++;
|
||||
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)) {
|
||||
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",
|
||||
port->name, cur_pb_type->name);
|
||||
port->name,
|
||||
generate_pb_type_hierarchy_path(cur_pb_type).c_str());
|
||||
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",
|
||||
interc->name,
|
||||
physical_mode->name,
|
||||
cur_pb_type->name);
|
||||
generate_pb_type_hierarchy_path(cur_pb_type).c_str());
|
||||
num_err++;
|
||||
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",
|
||||
interc->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_MODEL_TYPE_STRING[circuit_lib.model_type(interc_circuit_model)],
|
||||
CIRCUIT_MODEL_TYPE_STRING[required_circuit_model_type]);
|
||||
|
|
|
@ -48,23 +48,28 @@ t_pb_type* VprDeviceAnnotation::physical_pb_type(t_pb_type* pb_type) const {
|
|||
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 */
|
||||
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()) {
|
||||
return nullptr;
|
||||
return std::vector<t_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 */
|
||||
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()) {
|
||||
/* 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(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 {
|
||||
|
@ -185,27 +190,51 @@ int VprDeviceAnnotation::physical_pb_type_index_offset(t_pb_type* pb_type) const
|
|||
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 */
|
||||
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()) {
|
||||
/* Default value is 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 */
|
||||
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()) {
|
||||
/* Default value is 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 {
|
||||
/* Ensure that the pb_type is in the list */
|
||||
std::map<const t_pb_graph_pin*, t_pb_graph_pin*>::const_iterator it = physical_pb_graph_pins_.find(pb_graph_pin);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
void VprDeviceAnnotation::add_physical_pb_port(t_port* operating_pb_port, t_port* physical_pb_port) {
|
||||
/* Warn any override attempt */
|
||||
std::map<t_port*, t_port*>::const_iterator it = physical_pb_ports_.find(operating_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(t_port* operating_pb_port,
|
||||
t_port* physical_pb_port) {
|
||||
physical_pb_ports_[operating_pb_port].push_back(physical_pb_port);
|
||||
}
|
||||
|
||||
void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range) {
|
||||
void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port,
|
||||
t_port* physical_pb_port,
|
||||
const BasicPort& port_range) {
|
||||
/* 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 */
|
||||
std::map<t_port*, BasicPort>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port);
|
||||
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",
|
||||
operating_pb_port->name, port_range.get_lsb(), port_range.get_msb());
|
||||
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())
|
||||
&& (0 < physical_pb_port_ranges_[operating_pb_port].count(physical_pb_port)) ) {
|
||||
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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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 */
|
||||
std::map<t_port*, int>::const_iterator it = physical_pb_pin_rotate_offsets_.find(pb_port);
|
||||
if (it != physical_pb_pin_rotate_offsets_.end()) {
|
||||
VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port pin rotate offset '%d'!\n",
|
||||
pb_port->name, offset);
|
||||
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())
|
||||
&& (0 < physical_pb_pin_initial_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_[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 */
|
||||
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,
|
||||
|
@ -432,17 +477,17 @@ void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operat
|
|||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
+ physical_pb_port_range(operating_pb_graph_pin->port).get_lsb()
|
||||
+ physical_pb_pin_offsets_[operating_pb_graph_pin->port]) {
|
||||
physical_pb_pin_offsets_[operating_pb_graph_pin->port] = 0;
|
||||
+ 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_graph_pin->port]) {
|
||||
physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,9 @@ class VprDeviceAnnotation {
|
|||
bool is_physical_pb_type(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_port* physical_pb_port(t_port* pb_port) const;
|
||||
BasicPort physical_pb_port_range(t_port* pb_port) const;
|
||||
std::vector<t_port*> physical_pb_port(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 interconnect_circuit_model(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;
|
||||
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
|
||||
* accumulated offset is NOT the pin rotate offset specified by users
|
||||
|
@ -69,7 +74,8 @@ class VprDeviceAnnotation {
|
|||
* by the pin rotate offset value
|
||||
* 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;
|
||||
CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const;
|
||||
CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const;
|
||||
|
@ -78,8 +84,11 @@ class VprDeviceAnnotation {
|
|||
public: /* Public mutators */
|
||||
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_port(t_port* operating_pb_port, t_port* physical_pb_port);
|
||||
void add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range);
|
||||
void add_physical_pb_port(t_port* operating_pb_port,
|
||||
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_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);
|
||||
|
@ -90,7 +99,12 @@ class VprDeviceAnnotation {
|
|||
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_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_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);
|
||||
|
@ -139,17 +153,18 @@ class VprDeviceAnnotation {
|
|||
* Note:
|
||||
* - 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*, int> physical_pb_pin_rotate_offsets_;
|
||||
std::map<t_port*, std::vector<t_port*>> physical_pb_ports_;
|
||||
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 */
|
||||
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
|
||||
* Note:
|
||||
* - 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
|
||||
* Note:
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
|
|
|
@ -38,7 +38,7 @@ int fpga_bitstream(OpenfpgaContext& openfpga_ctx,
|
|||
CommandOptionId opt_read_file = cmd.option("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 {
|
||||
openfpga_ctx.mutable_bitstream_manager() = build_device_bitstream(g_vpr_ctx,
|
||||
openfpga_ctx,
|
||||
|
|
|
@ -133,6 +133,7 @@ void update_cluster_pin_with_post_routing_results(const DeviceContext& device_ct
|
|||
if (routing_net_id == cluster_net_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add to net modification */
|
||||
vpr_clustering_annotation.rename_net(blk_id, j, routing_net_id);
|
||||
|
||||
|
|
|
@ -40,6 +40,10 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx,
|
|||
status = fpga_fabric_spice(openfpga_ctx.module_graph(),
|
||||
openfpga_ctx.mutable_spice_netlists(),
|
||||
openfpga_ctx.arch(),
|
||||
openfpga_ctx.mux_lib(),
|
||||
g_vpr_ctx.device(),
|
||||
openfpga_ctx.vpr_device_annotation(),
|
||||
openfpga_ctx.device_rr_gsb(),
|
||||
options);
|
||||
|
||||
return status;
|
||||
|
|
|
@ -187,7 +187,7 @@ void build_user_defined_modules(ModuleManager& module_manager,
|
|||
for (const auto& model : circuit_lib.models()) {
|
||||
/* We only care about user-defined models */
|
||||
if ( (true == circuit_lib.model_verilog_netlist(model).empty())
|
||||
&& (true == circuit_lib.model_circuit_netlist(model).empty()) ) {
|
||||
&& (true == circuit_lib.model_spice_netlist(model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
|
@ -255,7 +255,7 @@ void rename_primitive_module_port_names(ModuleManager& module_manager,
|
|||
for (const CircuitModelId& model : circuit_lib.models()) {
|
||||
/* We only care about user-defined models */
|
||||
if ( (true == circuit_lib.model_verilog_netlist(model).empty())
|
||||
&& (true == circuit_lib.model_circuit_netlist(model).empty()) ) {
|
||||
&& (true == circuit_lib.model_spice_netlist(model).empty()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
|
|
|
@ -62,7 +62,7 @@ void build_wire_modules(ModuleManager& module_manager,
|
|||
/* Print Verilog models for regular wires*/
|
||||
for (const auto& wire_model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) {
|
||||
/* 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()) ) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -77,30 +77,26 @@ int write_fabric_config_bit_to_xml_file(std::fstream& fp,
|
|||
}
|
||||
|
||||
write_tab_to_file(fp, 1);
|
||||
fp << "<bit id=\"" << size_t(fabric_bit) << "\" ";
|
||||
fp << "value=\"";
|
||||
fp << "<bit id=\"" << size_t(fabric_bit) << "\"";
|
||||
fp << " value=\"";
|
||||
fp << bitstream_manager.bit_value(fabric_bitstream.config_bit(fabric_bit));
|
||||
fp << "\">\n";
|
||||
fp << "\"";
|
||||
|
||||
/* Output hierarchy of this parent*/
|
||||
const ConfigBitId& config_bit = fabric_bitstream.config_bit(fabric_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);
|
||||
write_tab_to_file(fp, 2);
|
||||
fp << "<hierarchy>\n";
|
||||
size_t hierarchy_counter = 0;
|
||||
std::string hie_path;
|
||||
for (const ConfigBlockId& temp_block : block_hierarchy) {
|
||||
write_tab_to_file(fp, 3);
|
||||
fp << "<instance level=\"" << hierarchy_counter << "\"";
|
||||
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) << "\"";
|
||||
fp << "/>\n";
|
||||
hierarchy_counter++;
|
||||
hie_path += bitstream_manager.block_name(temp_block);
|
||||
hie_path += std::string(".");
|
||||
}
|
||||
write_tab_to_file(fp, 2);
|
||||
fp << "</hierarchy>\n";
|
||||
hie_path += generate_configurable_memory_data_out_name();
|
||||
hie_path += std::string("[");
|
||||
hie_path += std::to_string(find_bitstream_manager_config_bit_index_in_parent_block(bitstream_manager, config_bit));
|
||||
hie_path += std::string("]");
|
||||
|
||||
fp << " path=\"" << hie_path << "\">\n";
|
||||
|
||||
switch (config_type) {
|
||||
case CONFIG_MEM_STANDALONE:
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#include "spice_constants.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 */
|
||||
#include "spice_api.h"
|
||||
|
@ -40,6 +44,10 @@ namespace openfpga {
|
|||
int fpga_fabric_spice(const ModuleManager& module_manager,
|
||||
NetlistManager& netlist_manager,
|
||||
const Arch& openfpga_arch,
|
||||
const MuxLibrary& mux_lib,
|
||||
const DeviceContext &device_ctx,
|
||||
const VprDeviceAnnotation &device_annotation,
|
||||
const DeviceRRGSB &device_rr_gsb,
|
||||
const FabricSpiceOption& options) {
|
||||
|
||||
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,
|
||||
module_manager,
|
||||
openfpga_arch,
|
||||
mux_lib,
|
||||
submodule_dir_path);
|
||||
|
||||
if (CMD_EXEC_SUCCESS != 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 */
|
||||
VTR_LOGV(options.verbose_output(),
|
||||
"Written %lu SPICE modules in total\n",
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include "netlist_manager.h"
|
||||
#include "module_manager.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"
|
||||
|
||||
/********************************************************************
|
||||
|
@ -22,6 +26,10 @@ namespace openfpga {
|
|||
int fpga_fabric_spice(const ModuleManager& module_manager,
|
||||
NetlistManager& netlist_manager,
|
||||
const Arch& openfpga_arch,
|
||||
const MuxLibrary& mux_lib,
|
||||
const DeviceContext &device_ctx,
|
||||
const VprDeviceAnnotation &device_annotation,
|
||||
const DeviceRRGSB &device_rr_gsb,
|
||||
const FabricSpiceOption& options);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -2,12 +2,27 @@
|
|||
#define SPICE_CONSTANTS_H
|
||||
|
||||
/* 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* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper";
|
||||
|
||||
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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,9 +18,9 @@
|
|||
/* 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_supply_wrappers(NetlistManager& netlist_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const std::string& submodule_dir);
|
||||
|
||||
int print_spice_essential_gates(NetlistManager& netlist_manager,
|
||||
const ModuleManager& module_manager,
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
||||
********************************************************************/
|
||||
|
||||
|
@ -10,7 +10,11 @@
|
|||
/* Headers from openfpgashell library */
|
||||
#include "command_exit_codes.h"
|
||||
|
||||
#include "spice_transistor_wrapper.h"
|
||||
#include "spice_essential_gates.h"
|
||||
#include "spice_mux.h"
|
||||
#include "spice_lut.h"
|
||||
#include "spice_memory.h"
|
||||
|
||||
#include "spice_constants.h"
|
||||
#include "spice_submodule.h"
|
||||
|
@ -21,23 +25,46 @@ namespace openfpga {
|
|||
/*********************************************************************
|
||||
* Top-level function to generate primitive modules:
|
||||
* 1. Transistor wrapper
|
||||
* 2. TODO: Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor
|
||||
* 3. TODO: Routing multiplexers
|
||||
* 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor
|
||||
* 3. Routing multiplexers
|
||||
* 4. TODO: Local encoders for routing multiplexers
|
||||
* 5. TODO: Wires
|
||||
* 6. TODO: Configuration memory blocks
|
||||
* 5. Wires
|
||||
* 6. Configuration memory blocks
|
||||
********************************************************************/
|
||||
int print_spice_submodule(NetlistManager& netlist_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const Arch& openfpga_arch,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::string& submodule_dir) {
|
||||
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Transistor wrapper */
|
||||
status = print_spice_transistor_wrapper(netlist_manager,
|
||||
openfpga_arch.tech_lib,
|
||||
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,
|
||||
module_manager,
|
||||
openfpga_arch.circuit_lib,
|
||||
|
@ -45,6 +72,50 @@ int print_spice_submodule(NetlistManager& netlist_manager,
|
|||
openfpga_arch.circuit_tech_binding,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "netlist_manager.h"
|
||||
#include "module_manager.h"
|
||||
#include "openfpga_arch.h"
|
||||
#include "mux_library.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
|
@ -18,6 +19,7 @@ namespace openfpga {
|
|||
int print_spice_submodule(NetlistManager& netlist_manager,
|
||||
const ModuleManager& module_manager,
|
||||
const Arch& openfpga_arch,
|
||||
const MuxLibrary& mux_lib,
|
||||
const std::string& submodule_dir);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -18,6 +18,7 @@
|
|||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "spice_constants.h"
|
||||
#include "spice_writer_utils.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
|
@ -70,10 +71,21 @@ void print_spice_comment(std::fstream& fp,
|
|||
|
||||
/************************************************
|
||||
* 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());
|
||||
|
||||
std::string ret = port.get_name();
|
||||
|
||||
if ((true == omit_pin_zero)
|
||||
&& (0 == port.get_lsb())) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret += "[";
|
||||
ret += std::to_string(port.get_lsb());
|
||||
ret += "]";
|
||||
|
@ -87,7 +99,9 @@ std::string generate_spice_port(const BasicPort& port) {
|
|||
* module <module_name> (<ports without directions>);
|
||||
***********************************************/
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
pin_cnt++;
|
||||
|
||||
/* Currently we limit 10 ports per line to keep a clean netlist */
|
||||
new_line = false;
|
||||
if (10 == pin_cnt) {
|
||||
if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
|
||||
pin_cnt = 0;
|
||||
fp << std::endl;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -149,4 +187,182 @@ void print_spice_subckt_end(std::fstream& fp,
|
|||
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 */
|
||||
|
|
|
@ -39,14 +39,37 @@ void print_spice_include_netlist(std::fstream& fp,
|
|||
void print_spice_comment(std::fstream& fp,
|
||||
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,
|
||||
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,
|
||||
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 */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "verilog_preconfig_top_module.h"
|
||||
#include "verilog_formal_random_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 */
|
||||
#include "verilog_api.h"
|
||||
|
@ -33,7 +33,7 @@
|
|||
namespace openfpga
|
||||
{
|
||||
|
||||
/********************************************************************
|
||||
/********************************************************************
|
||||
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
||||
* This function will generate
|
||||
* - primitive modules required by the full fabric
|
||||
|
@ -52,97 +52,93 @@ namespace openfpga
|
|||
* The only exception now is the user-defined modules.
|
||||
* We should think clearly about how to handle them for both Verilog and SPICE generators!
|
||||
********************************************************************/
|
||||
void fpga_fabric_verilog(ModuleManager &module_manager,
|
||||
NetlistManager &netlist_manager,
|
||||
const CircuitLibrary &circuit_lib,
|
||||
const MuxLibrary &mux_lib,
|
||||
const DecoderLibrary &decoder_lib,
|
||||
const DeviceContext &device_ctx,
|
||||
const VprDeviceAnnotation &device_annotation,
|
||||
const DeviceRRGSB &device_rr_gsb,
|
||||
const FabricVerilogOption &options)
|
||||
{
|
||||
void fpga_fabric_verilog(ModuleManager &module_manager,
|
||||
NetlistManager &netlist_manager,
|
||||
const CircuitLibrary &circuit_lib,
|
||||
const MuxLibrary &mux_lib,
|
||||
const DecoderLibrary &decoder_lib,
|
||||
const DeviceContext &device_ctx,
|
||||
const VprDeviceAnnotation &device_annotation,
|
||||
const DeviceRRGSB &device_rr_gsb,
|
||||
const FabricVerilogOption &options) {
|
||||
|
||||
vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n");
|
||||
vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n");
|
||||
|
||||
std::string src_dir_path = format_dir_path(options.output_directory());
|
||||
std::string src_dir_path = format_dir_path(options.output_directory());
|
||||
|
||||
/* Create directories */
|
||||
create_directory(src_dir_path);
|
||||
/* Create directories */
|
||||
create_directory(src_dir_path);
|
||||
|
||||
/* Sub directory under SRC directory to contain all the primitive block netlists */
|
||||
std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME);
|
||||
create_directory(submodule_dir_path);
|
||||
/* Sub directory under SRC directory to contain all the primitive block netlists */
|
||||
std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME);
|
||||
create_directory(submodule_dir_path);
|
||||
|
||||
/* Sub directory under SRC directory to contain all the logic block netlists */
|
||||
std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME);
|
||||
create_directory(lb_dir_path);
|
||||
/* Sub directory under SRC directory to contain all the logic block netlists */
|
||||
std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME);
|
||||
create_directory(lb_dir_path);
|
||||
|
||||
/* Sub directory under SRC directory to contain all the routing block netlists */
|
||||
std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME);
|
||||
create_directory(rr_dir_path);
|
||||
/* Sub directory under SRC directory to contain all the routing block netlists */
|
||||
std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME);
|
||||
create_directory(rr_dir_path);
|
||||
|
||||
/* Print Verilog files containing preprocessing flags */
|
||||
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
|
||||
options);
|
||||
/* Print Verilog files containing preprocessing flags */
|
||||
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
|
||||
options);
|
||||
|
||||
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric
|
||||
* Note that this function MUST be called before Verilog generation of
|
||||
* core logic (i.e., logic blocks and routing resources) !!!
|
||||
* This is because that this function will add the primitive Verilog modules to
|
||||
* the module manager.
|
||||
* Without the modules in the module manager, core logic generation is not possible!!!
|
||||
*/
|
||||
print_verilog_submodule(module_manager, netlist_manager,
|
||||
mux_lib, decoder_lib, circuit_lib,
|
||||
submodule_dir_path,
|
||||
options);
|
||||
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric
|
||||
* Note that this function MUST be called before Verilog generation of
|
||||
* core logic (i.e., logic blocks and routing resources) !!!
|
||||
* This is because that this function will add the primitive Verilog modules to
|
||||
* the module manager.
|
||||
* Without the modules in the module manager, core logic generation is not possible!!!
|
||||
*/
|
||||
print_verilog_submodule(module_manager, netlist_manager,
|
||||
mux_lib, decoder_lib, circuit_lib,
|
||||
submodule_dir_path,
|
||||
options);
|
||||
|
||||
/* Generate routing blocks */
|
||||
if (true == options.compress_routing())
|
||||
{
|
||||
print_verilog_unique_routing_modules(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
else
|
||||
{
|
||||
VTR_ASSERT(false == options.compress_routing());
|
||||
print_verilog_flatten_routing_modules(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate grids */
|
||||
print_verilog_grids(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_ctx, device_annotation,
|
||||
lb_dir_path,
|
||||
options.explicit_port_mapping(),
|
||||
options.verbose_output());
|
||||
|
||||
/* Generate FPGA fabric */
|
||||
print_verilog_top_module(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
src_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
|
||||
/* Generate an netlist including all the fabric-related netlists */
|
||||
print_fabric_include_netlist(const_cast<const NetlistManager &>(netlist_manager),
|
||||
src_dir_path,
|
||||
circuit_lib);
|
||||
|
||||
/* Given a brief stats on how many Verilog modules have been written to files */
|
||||
VTR_LOGV(options.verbose_output(),
|
||||
"Written %lu Verilog modules in total\n",
|
||||
module_manager.num_modules());
|
||||
/* Generate routing blocks */
|
||||
if (true == options.compress_routing()) {
|
||||
print_verilog_unique_routing_modules(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
} else {
|
||||
VTR_ASSERT(false == options.compress_routing());
|
||||
print_verilog_flatten_routing_modules(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
/* Generate grids */
|
||||
print_verilog_grids(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
device_ctx, device_annotation,
|
||||
lb_dir_path,
|
||||
options.explicit_port_mapping(),
|
||||
options.verbose_output());
|
||||
|
||||
/* Generate FPGA fabric */
|
||||
print_verilog_top_module(netlist_manager,
|
||||
const_cast<const ModuleManager &>(module_manager),
|
||||
src_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
|
||||
/* Generate an netlist including all the fabric-related netlists */
|
||||
print_verilog_fabric_include_netlist(const_cast<const NetlistManager &>(netlist_manager),
|
||||
src_dir_path,
|
||||
circuit_lib);
|
||||
|
||||
/* Given a brief stats on how many Verilog modules have been written to files */
|
||||
VTR_LOGV(options.verbose_output(),
|
||||
"Written %lu Verilog modules in total\n",
|
||||
module_manager.num_modules());
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
||||
* This function will generate
|
||||
* - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark
|
||||
|
@ -151,100 +147,95 @@ namespace openfpga
|
|||
* This testbench is created for quick verification and formal verification purpose.
|
||||
* - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
|
||||
********************************************************************/
|
||||
void fpga_verilog_testbench(const ModuleManager &module_manager,
|
||||
const BitstreamManager &bitstream_manager,
|
||||
const FabricBitstream &fabric_bitstream,
|
||||
const AtomContext &atom_ctx,
|
||||
const PlacementContext &place_ctx,
|
||||
const IoLocationMap &io_location_map,
|
||||
const VprNetlistAnnotation &netlist_annotation,
|
||||
const CircuitLibrary &circuit_lib,
|
||||
const SimulationSetting &simulation_setting,
|
||||
const e_config_protocol_type &config_protocol_type,
|
||||
const VerilogTestbenchOption &options)
|
||||
{
|
||||
void fpga_verilog_testbench(const ModuleManager &module_manager,
|
||||
const BitstreamManager &bitstream_manager,
|
||||
const FabricBitstream &fabric_bitstream,
|
||||
const AtomContext &atom_ctx,
|
||||
const PlacementContext &place_ctx,
|
||||
const IoLocationMap &io_location_map,
|
||||
const VprNetlistAnnotation &netlist_annotation,
|
||||
const CircuitLibrary &circuit_lib,
|
||||
const SimulationSetting &simulation_setting,
|
||||
const e_config_protocol_type &config_protocol_type,
|
||||
const VerilogTestbenchOption &options) {
|
||||
|
||||
vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n");
|
||||
vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n");
|
||||
|
||||
std::string src_dir_path = format_dir_path(options.output_directory());
|
||||
std::string src_dir_path = format_dir_path(options.output_directory());
|
||||
|
||||
std::string netlist_name = atom_ctx.nlist.netlist_name();
|
||||
std::string netlist_name = atom_ctx.nlist.netlist_name();
|
||||
|
||||
/* Create directories */
|
||||
create_directory(src_dir_path);
|
||||
/* Create directories */
|
||||
create_directory(src_dir_path);
|
||||
|
||||
/* TODO: check if this works here. This function was in fabric generator */
|
||||
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
||||
options);
|
||||
/* TODO: check if this works here. This function was in fabric generator */
|
||||
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
||||
options);
|
||||
|
||||
/* Collect global ports from the circuit library:
|
||||
* TODO: should we place this in the OpenFPGA context?
|
||||
*/
|
||||
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(circuit_lib);
|
||||
/* Collect global ports from the circuit library:
|
||||
* TODO: should we place this in the OpenFPGA context?
|
||||
*/
|
||||
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 */
|
||||
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);
|
||||
print_verilog_preconfig_top_module(module_manager, bitstream_manager,
|
||||
circuit_lib, global_ports,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
netlist_annotation,
|
||||
netlist_name,
|
||||
formal_verification_top_netlist_file_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
if (true == options.print_preconfig_top_testbench())
|
||||
{
|
||||
/* 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);
|
||||
print_verilog_random_top_testbench(netlist_name,
|
||||
random_top_testbench_file_path,
|
||||
atom_ctx,
|
||||
netlist_annotation,
|
||||
simulation_setting,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate full testbench for verification, including configuration phase and operating phase */
|
||||
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);
|
||||
print_verilog_top_testbench(module_manager,
|
||||
bitstream_manager, fabric_bitstream,
|
||||
config_protocol_type,
|
||||
circuit_lib, global_ports,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
netlist_annotation,
|
||||
netlist_name,
|
||||
top_testbench_file_path,
|
||||
simulation_setting,
|
||||
options.fast_configuration(),
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate exchangeable files which contains simulation settings */
|
||||
if (true == options.print_simulation_ini())
|
||||
{
|
||||
std::string simulation_ini_file_name = options.simulation_ini_path();
|
||||
VTR_ASSERT(true != options.simulation_ini_path().empty());
|
||||
print_verilog_simulation_info(simulation_ini_file_name,
|
||||
netlist_name,
|
||||
src_dir_path,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
module_manager,
|
||||
config_protocol_type,
|
||||
bitstream_manager.num_bits(),
|
||||
simulation_setting.num_clock_cycles(),
|
||||
simulation_setting.programming_clock_frequency(),
|
||||
simulation_setting.operating_clock_frequency());
|
||||
}
|
||||
|
||||
/* Generate a Verilog file including all the netlists that have been generated */
|
||||
print_include_netlists(src_dir_path,
|
||||
netlist_name,
|
||||
options.reference_benchmark_file_path());
|
||||
/* 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()) {
|
||||
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,
|
||||
circuit_lib, global_ports,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
netlist_annotation,
|
||||
netlist_name,
|
||||
formal_verification_top_netlist_file_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
if (true == options.print_preconfig_top_testbench()) {
|
||||
/* 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);
|
||||
print_verilog_random_top_testbench(netlist_name,
|
||||
random_top_testbench_file_path,
|
||||
atom_ctx,
|
||||
netlist_annotation,
|
||||
simulation_setting,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate full testbench for verification, including configuration phase and operating phase */
|
||||
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);
|
||||
print_verilog_top_testbench(module_manager,
|
||||
bitstream_manager, fabric_bitstream,
|
||||
config_protocol_type,
|
||||
circuit_lib, global_ports,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
netlist_annotation,
|
||||
netlist_name,
|
||||
top_testbench_file_path,
|
||||
simulation_setting,
|
||||
options.fast_configuration(),
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate exchangeable files which contains simulation settings */
|
||||
if (true == options.print_simulation_ini()) {
|
||||
std::string simulation_ini_file_name = options.simulation_ini_path();
|
||||
VTR_ASSERT(true != options.simulation_ini_path().empty());
|
||||
print_verilog_simulation_info(simulation_ini_file_name,
|
||||
netlist_name,
|
||||
src_dir_path,
|
||||
atom_ctx, place_ctx, io_location_map,
|
||||
module_manager,
|
||||
config_protocol_type,
|
||||
bitstream_manager.num_bits(),
|
||||
simulation_setting.num_clock_cycles(),
|
||||
simulation_setting.programming_clock_frequency(),
|
||||
simulation_setting.operating_clock_frequency());
|
||||
}
|
||||
|
||||
/* Generate a Verilog file including all the netlists that have been generated */
|
||||
print_verilog_testbench_include_netlists(src_dir_path,
|
||||
netlist_name,
|
||||
options.reference_benchmark_file_path());
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -30,10 +30,10 @@ namespace openfpga {
|
|||
* This does NOT include any testbenches!
|
||||
* Some netlists are open to compile under specific preprocessing flags
|
||||
*******************************************************************/
|
||||
void print_fabric_include_netlist(const NetlistManager& netlist_manager,
|
||||
const std::string& src_dir,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME);
|
||||
void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager,
|
||||
const std::string& src_dir,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME);
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
|
@ -94,10 +94,10 @@ void print_fabric_include_netlist(const NetlistManager& netlist_manager,
|
|||
* that have been generated and user-defined.
|
||||
* Some netlists are open to compile under specific preprocessing flags
|
||||
*******************************************************************/
|
||||
void print_include_netlists(const std::string& src_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& reference_benchmark_file) {
|
||||
std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX);
|
||||
void print_verilog_testbench_include_netlists(const std::string& src_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& reference_benchmark_file) {
|
||||
std::string verilog_fname = src_dir + circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX);
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
|
@ -116,7 +116,7 @@ void print_include_netlists(const std::string& src_dir,
|
|||
|
||||
/* Include FPGA top module */
|
||||
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;
|
||||
|
||||
/* Include reference benchmark netlist only when auto-check flag is enabled */
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_fabric_include_netlist(const NetlistManager& netlist_manager,
|
||||
const std::string& src_dir,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager,
|
||||
const std::string& src_dir,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
void print_include_netlists(const std::string& src_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& reference_benchmark_file);
|
||||
void print_verilog_testbench_include_netlists(const std::string& src_dir,
|
||||
const std::string& circuit_name,
|
||||
const std::string& reference_benchmark_file);
|
||||
|
||||
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
||||
const FabricVerilogOption& fabric_verilog_opts);
|
||||
|
|
|
@ -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
|
||||
// End of Icarus variables and flag
|
||||
|
||||
constexpr char* FABRIC_INCLUDE_NETLIST_FILE_NAME = "fabric_netlists.v";
|
||||
constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
|
||||
constexpr char* FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME = "fabric_netlists.v";
|
||||
constexpr char* TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
|
||||
constexpr char* VERILOG_TOP_POSTFIX = "_top.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 */
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "simulation_utils.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "simulation_info_writer.h"
|
||||
#include "verilog_simulation_info_writer.h"
|
||||
|
||||
/* begin 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"]["VERILOG_PATH "] = std::string(src_dir);
|
||||
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]);
|
||||
|
||||
/* Information required by UVM */
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef SIMULATION_INFO_WRITER_H
|
||||
#define SIMULATION_INFO_WRITER_H
|
||||
#ifndef VERILOG_SIMULATION_INFO_WRITER_H
|
||||
#define VERILOG_SIMULATION_INFO_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
|
@ -20,6 +20,73 @@
|
|||
/* begin 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
|
||||
* 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,
|
||||
const t_pb_graph_pin* source_pb_pin,
|
||||
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,
|
||||
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())
|
||||
&& (OUT_PORT == sink_pb_pin->port->type)) {
|
||||
sink_pb_pins.push_back(sink_pb_pin);
|
||||
/* 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);
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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
|
||||
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* packing_source_pb_pin,
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
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
|
||||
* net_terminal[net][0] is the list of source 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 */
|
||||
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* 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);
|
||||
|
@ -258,22 +407,25 @@ void add_lb_router_nets(LbRouter& lb_router,
|
|||
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,
|
||||
* they are all connected to a common source node
|
||||
*/
|
||||
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));
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size());
|
||||
|
|
|
@ -251,6 +251,29 @@ std::vector<std::string> find_circuit_library_unique_verilog_netlists(const Circ
|
|||
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
|
||||
* satisfy the needs of configuration protocol
|
||||
|
|
|
@ -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_spice_netlists(const CircuitLibrary& circuit_lib);
|
||||
|
||||
bool check_configurable_memory_circuit_model(const e_config_protocol_type& config_protocol_type,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& config_mem_circuit_model);
|
||||
|
|
|
@ -324,4 +324,42 @@ std::vector<t_port*> find_pb_type_ports_match_circuit_model_port_type(t_pb_type*
|
|||
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 */
|
||||
|
|
|
@ -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 VprDeviceAnnotation& vpr_device_annotation);
|
||||
|
||||
std::string generate_pb_type_hierarchy_path(t_pb_type* cur_pb_type);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
# Run VPR for the 'and' design
|
||||
#--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_arch -f ${OPENFPGA_ARCH_FILE}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Run VPR for the 'and' design
|
||||
#--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_arch -f ${OPENFPGA_ARCH_FILE}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue