Merge pull request #732 from lnis-uofu/pcf_refactor
Refactor ``libpcf`` to integrate ``libpinconstrain``, ``librepackdc``; Rewrite ``libpinconstrain``
This commit is contained in:
commit
300e1bcaba
|
@ -24,6 +24,12 @@ OpenFPGA widely uses XML format for interchangable files
|
|||
|
||||
io_mapping_file
|
||||
|
||||
io_info_file
|
||||
|
||||
bitstream_distribution_file
|
||||
|
||||
bus_group_file
|
||||
|
||||
pcf_file
|
||||
|
||||
pin_table_file
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
.. _file_format_io_info_file:
|
||||
|
||||
I/O Information File (.xml)
|
||||
-----------------------
|
||||
---------------------------
|
||||
|
||||
.. note:: This file is in a different usage than the I/O mapping file (see details in :ref:`file_format_io_mapping_file`)
|
||||
|
||||
The I/O information file aims to show
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
.. _file_format_pcf_file:
|
||||
|
||||
Pin Constraints File (.pcf)
|
||||
---------------------------
|
||||
|
||||
.. note:: This file is in a different usage than the Pin Constraints File in XML format (see details in :ref:`file_format_pin_constraints_file`)
|
||||
|
||||
The PCF file is the file which **users** should craft to assign their I/O constraints
|
||||
|
||||
An example of the file is shown as follows.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
set_io a pad_fpga_io[0]
|
||||
set_io b[0] pad_fpga_io[4]
|
||||
set_io c[1] pad_fpga_io[6]
|
||||
|
||||
.. option:: set_io <net> <pin>
|
||||
|
||||
Assign a net (defined as an input or output in users' HDL design) to a specific pin of an FPGA device (typically a packaged chip).
|
||||
|
||||
.. note:: The net should be single-bit and match the port declaration of the top-module in users' HDL design
|
||||
|
||||
.. note:: FPGA devices have different pin names, depending their naming rules. Please contact your vendor about details.
|
|
@ -3,7 +3,8 @@
|
|||
Pin Constraints File (.xml)
|
||||
---------------------------
|
||||
|
||||
The *Pin Constraints File* (PCF) aims to create pin binding between an implementation and an FPGA fabric
|
||||
The *Pin Constraints File* (PCF) aims to create pin binding between an implementation and an FPGA fabric.
|
||||
It is a common file format used by FPGA vendors, for example, `QuickLogic<https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#placement-file-format-place>`_.
|
||||
|
||||
An example of design constraints is shown as follows.
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
.. _file_format_pin_table_file:
|
||||
|
||||
Pin Table File (.csv)
|
||||
---------------------
|
||||
|
||||
.. note:: This file is typically a spreadsheet provided by FPGA vendors. Please contact your vendor for the exact file.
|
||||
|
||||
.. note:: OpenFPGA will not include or guarantee the correctness of the file!!!
|
||||
|
||||
The pin table file is the file which describes the pin mapping between a chip and an FPGA inside the chip.
|
||||
|
||||
An example of the file is shown as follows.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
orientation,row,col,pin_num_in_cell,port_name,mapped_pin,GPIO_type,Associated Clock,Clock Edge
|
||||
TOP,,,,gfpga_pad_IO_A2F[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[31],pad_fpga_io[3],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[31],pad_fpga_io[3],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[40],pad_fpga_io[5],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[40],pad_fpga_io[5],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_A2F[64],pad_fpga_io[6],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_F2A[64],pad_fpga_io[6],,,
|
||||
LEFT,,,,gfpga_pad_IO_F2A[127],pad_fpga_io[7],,,
|
||||
LEFT,,,,gfpga_pad_IO_A2F[127],pad_fpga_io[7],,,
|
||||
|
||||
An pin table may serve in various purposes. However, for OpenFPGA, the following attributes are required
|
||||
|
||||
.. option:: orientation
|
||||
|
||||
Specify on which side the pin locates
|
||||
|
||||
.. option:: port_name
|
||||
|
||||
Specify the port name of the FPGA fabric
|
||||
|
||||
.. option:: mapped_pin
|
||||
|
||||
Specify the pin name of the FPGA chip
|
||||
|
||||
.. warning:: Currently, the direction of the port is inferred by the ``port_name``. A postfix of ``A2F`` indicates an input port, while a postfix of ``F2A`` indicates an output port.
|
|
@ -218,6 +218,8 @@ write_fabric_hierarchy
|
|||
|
||||
.. note:: This file is designed for hierarchical PnR flow, which requires the tree of Multiple-Instanced-Blocks (MIBs).
|
||||
|
||||
.. _openfpga_setup_commands_write_fabric_io_info:
|
||||
|
||||
write_fabric_io_info
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -236,3 +238,36 @@ write_fabric_io_info
|
|||
Show verbose log
|
||||
|
||||
.. note:: This file is designed for pin constraint file conversion.
|
||||
|
||||
pcf2place
|
||||
~~~~~~~~~
|
||||
|
||||
Convert a Pin Constraint File (.pcf, see details in :ref:`file_format_pcf_file`) to a `placement file <https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#placement-file-format-place>`_)
|
||||
|
||||
.. option:: --pcf <string>
|
||||
|
||||
Specify the path to the users' pin constraint file
|
||||
|
||||
.. option:: --blif <string>
|
||||
|
||||
Specify the path to the users' post-synthesis netlist
|
||||
|
||||
.. option:: --fpga_io_map <string>
|
||||
|
||||
Specify the path to the FPGA I/O location. Achieved by the command :ref:`openfpga_setup_commands_write_fabric_io_info`
|
||||
|
||||
.. option:: --pin_table <string>
|
||||
|
||||
Specify the path to the pin table file, which describes the pin mapping between chip I/Os and FPGA I/Os. See details in :ref:`file_format_pin_table_file`
|
||||
|
||||
.. option:: --fpga_fix_pins <string>
|
||||
|
||||
Specify the path to the placement file which will be outputted by running this command
|
||||
|
||||
.. option:: --no_time_stamp
|
||||
|
||||
Do not print time stamp in bitstream files
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
Show verbose log
|
||||
|
|
|
@ -4,8 +4,6 @@ add_subdirectory(libopenfpgashell)
|
|||
add_subdirectory(libarchopenfpga)
|
||||
add_subdirectory(libopenfpgautil)
|
||||
add_subdirectory(libfabrickey)
|
||||
add_subdirectory(librepackdc)
|
||||
add_subdirectory(libfpgabitstream)
|
||||
add_subdirectory(libpcf)
|
||||
add_subdirectory(libbusgroup)
|
||||
add_subdirectory(libpinconstrain)
|
||||
|
|
|
@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.9)
|
|||
project("libpcf")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
|
@ -20,8 +20,10 @@ set_target_properties(libpcf PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
|||
#Specify link-time dependancies
|
||||
target_link_libraries(libpcf
|
||||
libopenfpgautil
|
||||
libarchfpga
|
||||
libarchopenfpga
|
||||
libvtrutil
|
||||
libblifparse
|
||||
libpugixml
|
||||
libpugiutil)
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.model and2
|
||||
.inputs a b
|
||||
.outputs c
|
||||
|
||||
.names a b c
|
||||
11 1
|
||||
|
||||
.end
|
|
@ -0,0 +1,40 @@
|
|||
<!--
|
||||
- FPGA Fabric I/O Information
|
||||
- Generated by OpenFPGA
|
||||
- Date: Tue Jul 26 13:54:26 2022
|
||||
-->
|
||||
|
||||
<io_coordinates>
|
||||
<io pad="gfpga_pad_GPIO_PAD[24]" x="0" y="1" z="0"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[25]" x="0" y="1" z="1"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[26]" x="0" y="1" z="2"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[27]" x="0" y="1" z="3"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[28]" x="0" y="1" z="4"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[29]" x="0" y="1" z="5"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[30]" x="0" y="1" z="6"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[31]" x="0" y="1" z="7"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[16]" x="1" y="0" z="0"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[17]" x="1" y="0" z="1"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[18]" x="1" y="0" z="2"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[19]" x="1" y="0" z="3"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[20]" x="1" y="0" z="4"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[21]" x="1" y="0" z="5"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[22]" x="1" y="0" z="6"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[23]" x="1" y="0" z="7"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[0]" x="1" y="2" z="0"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[1]" x="1" y="2" z="1"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[2]" x="1" y="2" z="2"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[3]" x="1" y="2" z="3"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[4]" x="1" y="2" z="4"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[5]" x="1" y="2" z="5"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[6]" x="1" y="2" z="6"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[7]" x="1" y="2" z="7"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[8]" x="2" y="1" z="0"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[9]" x="2" y="1" z="1"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[10]" x="2" y="1" z="2"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[11]" x="2" y="1" z="3"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[12]" x="2" y="1" z="4"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[13]" x="2" y="1" z="5"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[14]" x="2" y="1" z="6"/>
|
||||
<io pad="gfpga_pad_GPIO_PAD[15]" x="2" y="1" z="7"/>
|
||||
</io_coordinates>
|
|
@ -0,0 +1,18 @@
|
|||
<io_coordinates>
|
||||
<io pad="gfpga_pad_IO_A2F[0]" x="1" y="2" z="0"/>
|
||||
<io pad="gfpga_pad_IO_F2A[0]" x="1" y="2" z="1"/>
|
||||
<io pad="gfpga_pad_IO_A2F[1]" x="1" y="2" z="2"/>
|
||||
<io pad="gfpga_pad_IO_F2A[1]" x="1" y="2" z="3"/>
|
||||
<io pad="gfpga_pad_IO_A2F[2]" x="1" y="2" z="4"/>
|
||||
<io pad="gfpga_pad_IO_F2A[2]" x="1" y="2" z="5"/>
|
||||
<io pad="gfpga_pad_IO_A2F[3]" x="1" y="2" z="6"/>
|
||||
<io pad="gfpga_pad_IO_F2A[3]" x="1" y="2" z="7"/>
|
||||
<io pad="gfpga_pad_IO_A2F[4]" x="2" y="1" z="0"/>
|
||||
<io pad="gfpga_pad_IO_F2A[4]" x="2" y="1" z="1"/>
|
||||
<io pad="gfpga_pad_IO_A2F[5]" x="2" y="1" z="2"/>
|
||||
<io pad="gfpga_pad_IO_F2A[5]" x="2" y="1" z="3"/>
|
||||
<io pad="gfpga_pad_IO_A2F[6]" x="2" y="1" z="4"/>
|
||||
<io pad="gfpga_pad_IO_F2A[6]" x="2" y="1" z="5"/>
|
||||
<io pad="gfpga_pad_IO_A2F[7]" x="2" y="1" z="6"/>
|
||||
<io pad="gfpga_pad_IO_F2A[7]" x="2" y="1" z="7"/>
|
||||
</io_coordinates>
|
|
@ -2,6 +2,7 @@
|
|||
* Memember functions for data structure IoLocationMap
|
||||
******************************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include <algorithm>
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
@ -21,24 +22,54 @@ size_t IoLocationMap::io_index(const size_t& x,
|
|||
const size_t& y,
|
||||
const size_t& z,
|
||||
const std::string& io_port_name) const {
|
||||
if (x >= io_indices_.size()) {
|
||||
std::array<size_t, 3> coord = {x, y, z};
|
||||
auto result = io_indices_.find(coord);
|
||||
if (result == io_indices_.end()) {
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
if (y >= io_indices_[x].size()) {
|
||||
return size_t(-1);
|
||||
for (const BasicPort& candidate : result->second) {
|
||||
if (candidate.get_name() == io_port_name) {
|
||||
/* First found, first return. This may create bugs when FPGA architecture are more flexible
|
||||
* FIXME: However, we should not have multiple I/O in the same name, mapped to the same coordindate
|
||||
* For example, PAD[0] -> (1, 0, 1) and PAD[1] -> (1, 0, 1)
|
||||
*/
|
||||
return candidate.get_lsb();
|
||||
}
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
if (z >= io_indices_[x][y].size()) {
|
||||
return size_t(-1);
|
||||
size_t IoLocationMap::io_x(const BasicPort& io_port) const {
|
||||
for (auto pair : io_indices_) {
|
||||
for (const BasicPort& candidate : pair.second) {
|
||||
if (candidate == io_port) {
|
||||
return pair.first[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
auto result = io_indices_[x][y][z].find(io_port_name);
|
||||
if (result == io_indices_[x][y][z].end()) {
|
||||
return size_t(-1);
|
||||
size_t IoLocationMap::io_y(const BasicPort& io_port) const {
|
||||
for (auto pair : io_indices_) {
|
||||
for (const BasicPort& candidate : pair.second) {
|
||||
if (candidate == io_port) {
|
||||
return pair.first[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
return result->second;
|
||||
size_t IoLocationMap::io_z(const BasicPort& io_port) const {
|
||||
for (auto pair : io_indices_) {
|
||||
for (const BasicPort& candidate : pair.second) {
|
||||
if (candidate == io_port) {
|
||||
return pair.first[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
void IoLocationMap::set_io_index(const size_t& x,
|
||||
|
@ -46,19 +77,18 @@ void IoLocationMap::set_io_index(const size_t& x,
|
|||
const size_t& z,
|
||||
const std::string& io_port_name,
|
||||
const size_t& io_index) {
|
||||
if (x >= io_indices_.size()) {
|
||||
io_indices_.resize(x + 1);
|
||||
std::array<size_t, 3> coord = {x, y, z};
|
||||
BasicPort port_to_add(io_port_name, io_index, io_index);
|
||||
auto result = io_indices_.find(coord);
|
||||
if (result != io_indices_.end()) {
|
||||
if (io_indices_.at(coord).end() != std::find(io_indices_.at(coord).begin(), io_indices_.at(coord).end(), port_to_add)) {
|
||||
VTR_LOG_WARN("Attempt to add duplicated io '%s[%lu]' to coordinate (%lu, %lu, %lu)! Skip to save memory\n",
|
||||
io_port_name.c_str(), io_index,
|
||||
x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
if (y >= io_indices_[x].size()) {
|
||||
io_indices_[x].resize(y + 1);
|
||||
}
|
||||
|
||||
if (z >= io_indices_[x][y].size()) {
|
||||
io_indices_[x][y].resize(z + 1);
|
||||
}
|
||||
|
||||
io_indices_[x][y][z][io_port_name] = io_index;
|
||||
io_indices_[coord].push_back(port_to_add);
|
||||
}
|
||||
|
||||
int IoLocationMap::write_to_xml_file(const std::string& fname,
|
||||
|
@ -106,19 +136,15 @@ int IoLocationMap::write_to_xml_file(const std::string& fname,
|
|||
size_t io_cnt = 0;
|
||||
|
||||
/* Walk through the fabric I/O location map data structure */
|
||||
for (size_t x = 0; x < io_indices_.size(); ++x) {
|
||||
for (size_t y = 0; y < io_indices_[x].size(); ++y) {
|
||||
for (size_t z = 0; z < io_indices_[x][y].size(); ++z) {
|
||||
for (const auto& name_id_pair : io_indices_[x][y][z]) {
|
||||
fp << "\t" << "<io pad=\"" << name_id_pair.first << "[" << name_id_pair.second << "]\"";
|
||||
fp << " " << "x=\"" << x << "\"";
|
||||
fp << " " << "y=\"" << y << "\"";
|
||||
fp << " " << "z=\"" << z << "\"";
|
||||
fp << "/>";
|
||||
fp << "\n";
|
||||
io_cnt++;
|
||||
}
|
||||
}
|
||||
for (auto pair : io_indices_) {
|
||||
for (const BasicPort& port : pair.second) {
|
||||
fp << "\t" << "<io pad=\"" << port.get_name().c_str() << "[" << port.get_lsb() << "]\"";
|
||||
fp << " " << "x=\"" << pair.first[0] << "\"";
|
||||
fp << " " << "y=\"" << pair.first[1] << "\"";
|
||||
fp << " " << "z=\"" << pair.first[2] << "\"";
|
||||
fp << "/>";
|
||||
fp << "\n";
|
||||
io_cnt++;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "openfpga_port.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
@ -34,6 +35,9 @@ class IoLocationMap {
|
|||
const size_t& y,
|
||||
const size_t& z,
|
||||
const std::string& io_port_name) const;
|
||||
size_t io_x(const BasicPort& io_port) const;
|
||||
size_t io_y(const BasicPort& io_port) const;
|
||||
size_t io_z(const BasicPort& io_port) const;
|
||||
public: /* Public mutators */
|
||||
void set_io_index(const size_t& x,
|
||||
const size_t& y,
|
||||
|
@ -45,8 +49,10 @@ class IoLocationMap {
|
|||
const bool& include_time_stamp,
|
||||
const bool& verbose) const;
|
||||
private: /* Internal Data */
|
||||
/* I/O index fast lookup by [x][y][z] location */
|
||||
std::vector<std::vector<std::vector<std::map<std::string, size_t>>>> io_indices_;
|
||||
/* I/O index fast lookup by [x][y][z] location
|
||||
* Note that multiple I/Os may be assigned to the same coordinate!
|
||||
*/
|
||||
std::map<std::array<size_t, 3>, std::vector<BasicPort>> io_indices_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
|
@ -0,0 +1,126 @@
|
|||
/******************************************************************************
|
||||
* Memember functions for data structure IoNetPlace
|
||||
******************************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "io_net_place.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**************************************************
|
||||
* Public Accessors
|
||||
*************************************************/
|
||||
size_t IoNetPlace::io_x(const std::string& net) const {
|
||||
auto result = io_coords_.find(net);
|
||||
if (result == io_coords_.end()) {
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
return result->second[0];
|
||||
}
|
||||
|
||||
size_t IoNetPlace::io_y(const std::string& net) const {
|
||||
auto result = io_coords_.find(net);
|
||||
if (result == io_coords_.end()) {
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
return result->second[1];
|
||||
}
|
||||
|
||||
size_t IoNetPlace::io_z(const std::string& net) const {
|
||||
auto result = io_coords_.find(net);
|
||||
if (result == io_coords_.end()) {
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
return result->second[2];
|
||||
}
|
||||
|
||||
void IoNetPlace::set_net_coord(const std::string& net,
|
||||
const size_t& x,
|
||||
const size_t& y,
|
||||
const size_t& z) {
|
||||
/* Warn when there is an attempt to overwrite */
|
||||
auto result = io_coords_.find(net);
|
||||
if (result != io_coords_.end()) {
|
||||
VTR_LOG_WARN("Overwrite net '%s' coordinate from (%lu, %lu, %lu) to (%lu, %lu, %lu)!\n",
|
||||
net.c_str(),
|
||||
result->second[0], result->second[1], result->second[2],
|
||||
x, y, z);
|
||||
}
|
||||
io_coords_[net][0] = x;
|
||||
io_coords_[net][1] = y;
|
||||
io_coords_[net][2] = z;
|
||||
}
|
||||
|
||||
int IoNetPlace::write_to_place_file(const std::string& fname,
|
||||
const bool& include_time_stamp,
|
||||
const bool& verbose) const {
|
||||
std::string timer_message = std::string("Write I/O coordinates to a place file '") + fname + std::string("'");
|
||||
|
||||
std::string dir_path = format_dir_path(find_path_dir_name(fname));
|
||||
|
||||
/* Create directories */
|
||||
create_directory(dir_path);
|
||||
|
||||
/* Start time count */
|
||||
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||
|
||||
/* Use default name if user does not provide one */
|
||||
VTR_ASSERT(true != fname.empty());
|
||||
|
||||
/* Create a file handler*/
|
||||
std::fstream fp;
|
||||
/* Open a file */
|
||||
fp.open(fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate the file stream */
|
||||
check_file_stream(fname.c_str(), fp);
|
||||
|
||||
int err_code = 0;
|
||||
|
||||
/* Write XML head */
|
||||
fp << "# FPGA Fixed I/O placement file" << std::endl;
|
||||
fp << "# Generated by OpenFPGA" << std::endl;
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
|
||||
if (include_time_stamp) {
|
||||
fp << "# Date: " << std::ctime(&end_time) ;
|
||||
}
|
||||
|
||||
fp << "#Block Name\tx\ty\tz" << std::endl;
|
||||
fp << "#----------\t-\t-\t-" << std::endl;
|
||||
|
||||
size_t io_cnt = 0;
|
||||
|
||||
/* Walk through the fabric I/O location map data structure */
|
||||
for (auto pair : io_coords_) {
|
||||
fp << pair.first.c_str() << "\t";
|
||||
fp << pair.second[0] << "\t";
|
||||
fp << pair.second[1] << "\t";
|
||||
fp << pair.second[2] << "\n";
|
||||
io_cnt++;
|
||||
}
|
||||
|
||||
/* close a file */
|
||||
fp.close();
|
||||
|
||||
VTR_LOGV(verbose,
|
||||
"Outputted %lu I/Os to place file: %s\n",
|
||||
io_cnt,
|
||||
fname.c_str());
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef IO_NET_PLACE_H
|
||||
#define IO_NET_PLACE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* I/O net place is a data structure to store the coordinate of each nets
|
||||
* defined in users' HDL designs on FPGA fabric.
|
||||
*
|
||||
* For example:
|
||||
* netA netB
|
||||
* | |
|
||||
* v v
|
||||
* ioA[0] ioA[1] ioB[0] ioB[1] ioA[2]
|
||||
* +-----------------+ +--------+--------+ +--------+
|
||||
* | | | | | | | |
|
||||
* | I/O | I/O | | I/O | I/O | | I/O |
|
||||
* | [0][y] | [0][y] | | [1][y] | [1][y] | | [2][y] | ...
|
||||
* | [0] | [1] | | [0] | [1] | | [0] |
|
||||
* +-----------------+ +--------+--------+ +--------+
|
||||
*
|
||||
*******************************************************************/
|
||||
class IoNetPlace {
|
||||
public: /* Public aggregators */
|
||||
size_t io_x(const std::string& net) const;
|
||||
size_t io_y(const std::string& net) const;
|
||||
size_t io_z(const std::string& net) const;
|
||||
public: /* Writers */
|
||||
int write_to_place_file(const std::string& fname,
|
||||
const bool& include_time_stamp,
|
||||
const bool& verbose) const;
|
||||
public: /* Public mutators */
|
||||
void set_net_coord(const std::string& net,
|
||||
const size_t& x,
|
||||
const size_t& y,
|
||||
const size_t& z);
|
||||
private: /* Internal Data */
|
||||
/* I/O coordinate fast lookup by net name */
|
||||
std::map<std::string, std::array<size_t, 3>> io_coords_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
#include "io_pin_table.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/************************************************************************
|
||||
* Member functions for class IoPinTable
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Constructors
|
||||
***********************************************************************/
|
||||
IoPinTable::IoPinTable() {
|
||||
return;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Accessors : aggregates
|
||||
***********************************************************************/
|
||||
IoPinTable::io_pin_table_range IoPinTable::pins() const {
|
||||
return vtr::make_range(pin_ids_.begin(), pin_ids_.end());
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Accessors : Basic data query
|
||||
***********************************************************************/
|
||||
BasicPort IoPinTable::internal_pin(const IoPinTableId& pin_id) const {
|
||||
/* validate the pin_id */
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
return internal_pins_[pin_id];
|
||||
}
|
||||
|
||||
BasicPort IoPinTable::external_pin(const IoPinTableId& pin_id) const {
|
||||
/* validate the pin_id */
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
return external_pins_[pin_id];
|
||||
}
|
||||
|
||||
e_side IoPinTable::pin_side(const IoPinTableId& pin_id) const {
|
||||
/* validate the pin_id */
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
return pin_sides_[pin_id];
|
||||
}
|
||||
|
||||
IoPinTable::e_io_direction IoPinTable::pin_direction(const IoPinTableId& pin_id) const {
|
||||
/* validate the pin_id */
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
return pin_directions_[pin_id];
|
||||
}
|
||||
|
||||
std::vector<IoPinTableId> IoPinTable::find_internal_pin(const BasicPort& ext_pin,
|
||||
const e_io_direction& pin_direction) const {
|
||||
std::vector<IoPinTableId> int_pin_ids;
|
||||
for (auto pin_id : pin_ids_) {
|
||||
if ((external_pins_[pin_id] == ext_pin) && (pin_directions_[pin_id] == pin_direction)) {
|
||||
int_pin_ids.push_back(pin_id);
|
||||
}
|
||||
}
|
||||
return int_pin_ids;
|
||||
}
|
||||
|
||||
bool IoPinTable::empty() const {
|
||||
return 0 == pin_ids_.size();
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Mutators
|
||||
***********************************************************************/
|
||||
void IoPinTable::reserve_pins(const size_t& num_pins) {
|
||||
pin_ids_.reserve(num_pins);
|
||||
internal_pins_.reserve(num_pins);
|
||||
external_pins_.reserve(num_pins);
|
||||
pin_sides_.reserve(num_pins);
|
||||
pin_directions_.reserve(num_pins);
|
||||
}
|
||||
|
||||
IoPinTableId IoPinTable::create_pin() {
|
||||
/* Create a new id */
|
||||
IoPinTableId pin_id = IoPinTableId(pin_ids_.size());
|
||||
|
||||
pin_ids_.push_back(pin_id);
|
||||
internal_pins_.emplace_back();
|
||||
external_pins_.emplace_back();
|
||||
pin_sides_.emplace_back(NUM_SIDES);
|
||||
pin_directions_.emplace_back(NUM_IO_DIRECTIONS);
|
||||
|
||||
return pin_id;
|
||||
}
|
||||
|
||||
void IoPinTable::set_internal_pin(const IoPinTableId& pin_id, const BasicPort& pin) {
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
internal_pins_[pin_id] = pin;
|
||||
}
|
||||
|
||||
void IoPinTable::set_external_pin(const IoPinTableId& pin_id, const BasicPort& pin) {
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
external_pins_[pin_id] = pin;
|
||||
}
|
||||
|
||||
void IoPinTable::set_pin_side(const IoPinTableId& pin_id, const e_side& side) {
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
pin_sides_[pin_id] = side;
|
||||
}
|
||||
|
||||
void IoPinTable::set_pin_direction(const IoPinTableId& pin_id, const e_io_direction& direction) {
|
||||
VTR_ASSERT(valid_pin_id(pin_id));
|
||||
pin_directions_[pin_id] = direction;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Internal invalidators/validators
|
||||
***********************************************************************/
|
||||
/* Validators */
|
||||
bool IoPinTable::valid_pin_id(const IoPinTableId& pin_id) const {
|
||||
return ( size_t(pin_id) < pin_ids_.size() ) && ( pin_id == pin_ids_[pin_id] );
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef IO_PIN_TABLE_H
|
||||
#define IO_PIN_TABLE_H
|
||||
|
||||
/********************************************************************
|
||||
* This file include the declaration of pin constraints
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_vector.h"
|
||||
#include "vtr_geometry.h"
|
||||
|
||||
/* Headers from libarchfpga library */
|
||||
#include "physical_types.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_port.h"
|
||||
|
||||
#include "io_pin_table_fwd.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* A data structure to describe the I/O pin table for FPGA fabrics
|
||||
* This data structure may include a number of I/O pins
|
||||
* each of which contains the following information
|
||||
* - side: the side which this I/O locates on FPGA perimeter
|
||||
* - external_pin_name: the name of the external I/O pin (typically on a packaged chip), which is exposed to end-users
|
||||
* - internal_pin_name: the name of the internal I/O pin (typically inside the chip but on an FPGA fabric), which is defined in FPGA netlists
|
||||
* - direction: the direction of the internal pin, can be input, output or inout
|
||||
*
|
||||
* The following figure illustrates the relationship between external and internal pins.
|
||||
* FPGA Chip
|
||||
* +----------------------------------------
|
||||
* | FPGA fabric
|
||||
* | +----------------------
|
||||
* | +----- + |
|
||||
* CHIP_IO_TOP --->|--->| I/O |--->| FPGA_IN[0]
|
||||
* (External pin) | | Ctrl | | (internal pin as input)
|
||||
* | | |<---| FPGA_OUT[1]
|
||||
* | +------+ | (internal pin as output)
|
||||
*
|
||||
* Typical usage:
|
||||
* --------------
|
||||
* // Create an object
|
||||
* IoPinTable io_pin_table;
|
||||
* // Add a pin
|
||||
* openfpga::BasicPort ext_pin_info("CHIP_IO_TOP", 1);
|
||||
* openfpga::BasicPort int_pin_info("FPGA_IN", 1, 1);
|
||||
* IoPinTableId pin_id = io_pin_table.create_io_pin(int_pin_info, ext_pin_info, TOP, INPUT);
|
||||
*
|
||||
*******************************************************************/
|
||||
class IoPinTable {
|
||||
public: /* Types */
|
||||
typedef vtr::vector<IoPinTableId, IoPinTableId>::const_iterator io_pin_table_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<io_pin_table_iterator> io_pin_table_range;
|
||||
/* Logic value */
|
||||
enum e_io_direction {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
NUM_IO_DIRECTIONS
|
||||
};
|
||||
public: /* Constructors */
|
||||
IoPinTable();
|
||||
public: /* Accessors: aggregates */
|
||||
/* Walk through the internal pins. We do not walk through external pins because they are not unique in the table.
|
||||
* An external pin may be accessible by two internal pins
|
||||
*/
|
||||
io_pin_table_range pins() const;
|
||||
public: /* Public Accessors: Basic data query */
|
||||
/* Get the basic information for a pin */
|
||||
BasicPort internal_pin(const IoPinTableId& pin_id) const;
|
||||
BasicPort external_pin(const IoPinTableId& pin_id) const;
|
||||
e_side pin_side(const IoPinTableId& pin_id) const;
|
||||
e_io_direction pin_direction(const IoPinTableId& pin_id) const;
|
||||
/* Given an external pin, find all the internal pin that is mapped */
|
||||
std::vector<IoPinTableId> find_internal_pin(const BasicPort& ext_pin,
|
||||
const e_io_direction& pin_direction) const;
|
||||
/* Check if there are any pins */
|
||||
bool empty() const;
|
||||
public: /* Public Mutators */
|
||||
/* Reserve to be memory efficent */
|
||||
void reserve_pins(const size_t& num_pins);
|
||||
/* Add a pin to storage */
|
||||
IoPinTableId create_pin();
|
||||
/* Set pin attributes */
|
||||
void set_internal_pin(const IoPinTableId& pin_id, const BasicPort& pin);
|
||||
void set_external_pin(const IoPinTableId& pin_id, const BasicPort& pin);
|
||||
void set_pin_side(const IoPinTableId& pin_id, const e_side& side);
|
||||
void set_pin_direction(const IoPinTableId& pin_id, const e_io_direction& direction);
|
||||
|
||||
public: /* Public invalidators/validators */
|
||||
/* Show if the pin id is a valid for data queries */
|
||||
bool valid_pin_id(const IoPinTableId& pin_id) const;
|
||||
private: /* Internal data */
|
||||
/* Unique ids for each design constraint */
|
||||
vtr::vector<IoPinTableId, IoPinTableId> pin_ids_;
|
||||
|
||||
/* Pin information*/
|
||||
vtr::vector<IoPinTableId, BasicPort> internal_pins_;
|
||||
vtr::vector<IoPinTableId, BasicPort> external_pins_;
|
||||
vtr::vector<IoPinTableId, e_side> pin_sides_;
|
||||
vtr::vector<IoPinTableId, e_io_direction> pin_directions_;
|
||||
};
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/************************************************************************
|
||||
* A header file for IoPinTable class, including critical data declaration
|
||||
* Please include this file only for using any IoPinTable data structure
|
||||
* Refer to IoPinTable.h for more details
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Create strong id for IoPinTable to avoid illegal type casting
|
||||
***********************************************************************/
|
||||
#ifndef IO_PIN_TABLE_FWD_H
|
||||
#define IO_PIN_TABLE_FWD_H
|
||||
|
||||
#include "vtr_strong_id.h"
|
||||
|
||||
struct io_pin_table_id_tag;
|
||||
|
||||
typedef vtr::StrongId<io_pin_table_id_tag> IoPinTableId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class IoPinTable;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,110 @@
|
|||
/******************************************************************************
|
||||
* Inspired from https://github.com/genbtc/VerilogPCFparser
|
||||
******************************************************************************/
|
||||
#include <sstream>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "pcf2place.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Generate a .place file with the a few inputs
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are serious errors
|
||||
*******************************************************************/
|
||||
int pcf2place(const PcfData& pcf_data,
|
||||
const std::vector<std::string>& input_nets,
|
||||
const std::vector<std::string>& output_nets,
|
||||
const IoPinTable& io_pin_table,
|
||||
const IoLocationMap& io_location_map,
|
||||
IoNetPlace& io_net_place) {
|
||||
vtr::ScopedStartFinishTimer timer("Convert PCF data to VPR I/O place data");
|
||||
|
||||
int num_err = 0;
|
||||
|
||||
/* TODO: Validate pcf data, blif_head and io_pin_table
|
||||
* - there are no duplicated pin assignment in pcf
|
||||
* - the pin direction in io_pin_table matches the pin type defined in blif
|
||||
*/
|
||||
if (!pcf_data.validate()) {
|
||||
VTR_LOG_ERROR("PCF contains invalid I/O assignment!\n");
|
||||
return 1;
|
||||
} else {
|
||||
VTR_LOG_ERROR("PCF basic check passed\n");
|
||||
}
|
||||
|
||||
/* Build the I/O place */
|
||||
for (const PcfIoConstraintId& io_id : pcf_data.io_constraints()) {
|
||||
/* Find the net name */
|
||||
std::string net = pcf_data.io_net(io_id);
|
||||
/* Find the external pin name */
|
||||
BasicPort ext_pin = pcf_data.io_pin(io_id);
|
||||
/* Find the pin direction from blif reader */
|
||||
IoPinTable::e_io_direction pin_direction = IoPinTable::NUM_IO_DIRECTIONS;
|
||||
if (input_nets.end() != std::find(input_nets.begin(), input_nets.end(), net)) {
|
||||
pin_direction = IoPinTable::INPUT;
|
||||
} else if (output_nets.end() != std::find(output_nets.begin(), output_nets.end(), net)) {
|
||||
pin_direction = IoPinTable::OUTPUT;
|
||||
} else {
|
||||
/* Cannot find the pin, error out! */
|
||||
VTR_LOG_ERROR("Net '%s' from .pcf is neither defined as input nor output in .blif!\n",
|
||||
net.c_str());
|
||||
num_err++;
|
||||
continue;
|
||||
}
|
||||
/* Find the internal pin name from pin table, currently we only support 1-to-1 mapping */
|
||||
auto int_pin_ids = io_pin_table.find_internal_pin(ext_pin, pin_direction);
|
||||
if (0 == int_pin_ids.size()) {
|
||||
VTR_LOG_ERROR("Cannot find any internal pin that net '%s' is mapped through an external pin '%s[%lu]'!\n",
|
||||
net.c_str(),
|
||||
ext_pin.get_name().c_str(), ext_pin.get_lsb());
|
||||
num_err++;
|
||||
continue;
|
||||
} else if (1 < int_pin_ids.size()) {
|
||||
VTR_LOG_ERROR("Found multiple internal pins that net '%s' is mapped through an external pin '%s[%lu]'! Please double check your pin table!\n",
|
||||
net.c_str(),
|
||||
ext_pin.get_name().c_str(), ext_pin.get_lsb());
|
||||
for (auto int_pin_id : int_pin_ids) {
|
||||
VTR_LOG("%s[%ld]\n", io_pin_table.internal_pin(int_pin_id).get_name().c_str(), io_pin_table.internal_pin(int_pin_id).get_lsb());
|
||||
}
|
||||
num_err++;
|
||||
continue;
|
||||
}
|
||||
VTR_ASSERT(1 == int_pin_ids.size());
|
||||
BasicPort int_pin = io_pin_table.internal_pin(int_pin_ids[0]);
|
||||
/* Find the coordinate from io location map */
|
||||
size_t x = io_location_map.io_x(int_pin);
|
||||
size_t y = io_location_map.io_y(int_pin);
|
||||
size_t z = io_location_map.io_z(int_pin);
|
||||
/* Sanity check */
|
||||
if (size_t(-1) == x || size_t(-1) == y || size_t(-1) == z) {
|
||||
VTR_LOG_ERROR("Invalid coordinate (%ld, %ld, %ld) found for net '%s' mapped to an external pin '%s[%lu]' through an internal pin '%s[%lu]'!\n",
|
||||
x, y, z,
|
||||
net.c_str(),
|
||||
ext_pin.get_name().c_str(), ext_pin.get_lsb(),
|
||||
int_pin.get_name().c_str(), int_pin.get_lsb());
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add a fixed prefix to net namei, this is hard coded by VPR */
|
||||
if (IoPinTable::OUTPUT == pin_direction) {
|
||||
net = "out:" + net;
|
||||
}
|
||||
/* Add the information to I/O place data */
|
||||
io_net_place.set_net_coord(net, x, y, z);
|
||||
}
|
||||
|
||||
return num_err;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef PCF2PLACE_H
|
||||
#define PCF2PLACE_H
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pcf_data.h"
|
||||
#include "io_pin_table.h"
|
||||
#include "io_location_map.h"
|
||||
#include "io_net_place.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/* Generate a .place file with the following input files
|
||||
* - A Pin Constraint File (.pcf)
|
||||
* - Input and output lists from a netlist
|
||||
* - A chip I/O pin table file (.csv)
|
||||
* - An FPGA I/O location file (.xml)
|
||||
*/
|
||||
int pcf2place(const PcfData& pcf_data,
|
||||
const std::vector<std::string>& input_nets,
|
||||
const std::vector<std::string>& output_nets,
|
||||
const IoPinTable& io_pin_table,
|
||||
const IoLocationMap& io_location_map,
|
||||
IoNetPlace& io_net_place);
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,128 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
#include "openfpga_port_parser.h"
|
||||
#include "pcf_data.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/************************************************************************
|
||||
* Member functions for class PcfData
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Constructors
|
||||
***********************************************************************/
|
||||
PcfData::PcfData() {
|
||||
return;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Accessors : aggregates
|
||||
***********************************************************************/
|
||||
PcfData::pcf_io_constraint_range PcfData::io_constraints() const {
|
||||
return vtr::make_range(io_constraint_ids_.begin(), io_constraint_ids_.end());
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Accessors : Basic data query
|
||||
***********************************************************************/
|
||||
openfpga::BasicPort PcfData::io_pin(const PcfIoConstraintId& io_id) const {
|
||||
/* validate the io_id */
|
||||
VTR_ASSERT(valid_io_constraint_id(io_id));
|
||||
return io_constraint_pins_[io_id];
|
||||
}
|
||||
|
||||
std::string PcfData::io_net(const PcfIoConstraintId& io_id) const {
|
||||
/* validate the io_id */
|
||||
VTR_ASSERT(valid_io_constraint_id(io_id));
|
||||
return io_constraint_nets_[io_id];
|
||||
}
|
||||
|
||||
bool PcfData::empty() const {
|
||||
return 0 == io_constraint_ids_.size();
|
||||
}
|
||||
|
||||
bool PcfData::validate() const {
|
||||
size_t num_err = 0;
|
||||
/* In principle, we do not expect duplicated assignment: 1 net -> 2 pins */
|
||||
std::map<std::string, BasicPort> net2pin;
|
||||
for (const PcfIoConstraintId& io_id : io_constraints()) {
|
||||
std::string curr_net = io_constraint_nets_[io_id];
|
||||
BasicPort curr_pin = io_constraint_pins_[io_id];
|
||||
auto result = net2pin.find(curr_net);
|
||||
if (result != net2pin.end()) {
|
||||
/* Found one nets assigned to two pins, throw warning */
|
||||
VTR_LOG_WARN("Net '%s' is assigned to two pins '%s[%lu]' and '%s[%lu]'!\n",
|
||||
curr_net.c_str(),
|
||||
curr_pin.get_name().c_str(), curr_pin.get_lsb(),
|
||||
result->second.get_name().c_str(), result->second.get_lsb());
|
||||
}
|
||||
net2pin[curr_net] = curr_pin;
|
||||
}
|
||||
/* We should not have duplicated pins in assignment: 1 pin -> 2 nets */
|
||||
std::map<BasicPort, std::string> pin2net;
|
||||
for (const PcfIoConstraintId& io_id : io_constraints()) {
|
||||
std::string curr_net = io_constraint_nets_[io_id];
|
||||
BasicPort curr_pin = io_constraint_pins_[io_id];
|
||||
auto result = pin2net.find(curr_pin);
|
||||
if (result != pin2net.end()) {
|
||||
/* Found one pin assigned to two nets, this is definitely an error */
|
||||
VTR_LOG_ERROR("Pin '%s[%lu]' is assigned to two nets '%s' and '%s'!\n",
|
||||
curr_pin.get_name().c_str(), curr_pin.get_lsb(),
|
||||
result->second.c_str(), curr_net.c_str());
|
||||
}
|
||||
pin2net[curr_pin] = curr_net;
|
||||
}
|
||||
if (num_err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Mutators
|
||||
***********************************************************************/
|
||||
void PcfData::reserve_io_constraints(const size_t& num_io_constraints) {
|
||||
io_constraint_ids_.reserve(num_io_constraints);
|
||||
io_constraint_pins_.reserve(num_io_constraints);
|
||||
io_constraint_nets_.reserve(num_io_constraints);
|
||||
}
|
||||
|
||||
PcfIoConstraintId PcfData::create_io_constraint() {
|
||||
/* Create a new id */
|
||||
PcfIoConstraintId io_id = PcfIoConstraintId(io_constraint_ids_.size());
|
||||
|
||||
io_constraint_ids_.push_back(io_id);
|
||||
io_constraint_pins_.emplace_back();
|
||||
io_constraint_nets_.emplace_back();
|
||||
|
||||
return io_id;
|
||||
}
|
||||
|
||||
void PcfData::set_io_net(const PcfIoConstraintId& io_id,
|
||||
const std::string& net) {
|
||||
VTR_ASSERT(valid_io_constraint_id(io_id));
|
||||
io_constraint_nets_[io_id] = net;
|
||||
}
|
||||
|
||||
void PcfData::set_io_pin(const PcfIoConstraintId& io_id,
|
||||
const std::string& pin) {
|
||||
VTR_ASSERT(valid_io_constraint_id(io_id));
|
||||
PortParser port_parser(pin);
|
||||
io_constraint_pins_[io_id] = port_parser.port();
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Internal invalidators/validators
|
||||
***********************************************************************/
|
||||
/* Validators */
|
||||
bool PcfData::valid_io_constraint_id(const PcfIoConstraintId& io_id) const {
|
||||
return ( size_t(io_id) < io_constraint_ids_.size() ) && ( io_id == io_constraint_ids_[io_id] );
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef PCF_DATA_H
|
||||
#define PCF_DATA_H
|
||||
|
||||
/********************************************************************
|
||||
* This file include the declaration of pcf data
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_vector.h"
|
||||
#include "vtr_geometry.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_port.h"
|
||||
|
||||
#include "pcf_data_fwd.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* A data structure to constain PCF data
|
||||
* This data structure may include a number of design constraints
|
||||
* - I/O constraint, for instance, force a net to be mapped to specific pin
|
||||
*
|
||||
* Typical usage:
|
||||
* --------------
|
||||
* // Create an object
|
||||
* PcfData pcf_data;
|
||||
* // Add a constraint
|
||||
* PcfIoConstraintId io_id = pcf_data.create_io_constraint();
|
||||
* pcf_data.set_io_net(io_id, net_name);
|
||||
* pcf_data.set_io_pin(io_id, pin_name);
|
||||
*
|
||||
*******************************************************************/
|
||||
class PcfData {
|
||||
public: /* Types */
|
||||
typedef vtr::vector<PcfIoConstraintId, PcfIoConstraintId>::const_iterator pcf_io_constraint_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<pcf_io_constraint_iterator> pcf_io_constraint_range;
|
||||
public: /* Constructors */
|
||||
PcfData();
|
||||
public: /* Accessors: aggregates */
|
||||
pcf_io_constraint_range io_constraints() const;
|
||||
public: /* Public Accessors: Basic data query */
|
||||
/* Get the pin to be constrained */
|
||||
openfpga::BasicPort io_pin(const PcfIoConstraintId& io_id) const;
|
||||
|
||||
/* Get the net to be constrained */
|
||||
std::string io_net(const PcfIoConstraintId& io_id) const;
|
||||
|
||||
/* Check if there are any io constraints */
|
||||
bool empty() const;
|
||||
|
||||
/* Check if the data is valid: each pin can only be mapped to one net */
|
||||
bool validate() const;
|
||||
|
||||
public: /* Public Mutators */
|
||||
/* Reserve a number of design constraints to be memory efficent */
|
||||
void reserve_io_constraints(const size_t& num_io_constraints);
|
||||
|
||||
/* Add a pin constraint to storage */
|
||||
PcfIoConstraintId create_io_constraint();
|
||||
|
||||
/* Set the net for an io constraint */
|
||||
void set_io_net(const PcfIoConstraintId& io_id,
|
||||
const std::string& net);
|
||||
|
||||
/* Set the net for an io constraint */
|
||||
void set_io_pin(const PcfIoConstraintId& io_id,
|
||||
const std::string& pin);
|
||||
|
||||
public: /* Public invalidators/validators */
|
||||
/* Show if the constraint id is a valid for data queries */
|
||||
bool valid_io_constraint_id(const PcfIoConstraintId& io_id) const;
|
||||
|
||||
private: /* Internal data */
|
||||
/* Unique ids for each design constraint */
|
||||
vtr::vector<PcfIoConstraintId, PcfIoConstraintId> io_constraint_ids_;
|
||||
|
||||
/* Pins to constraint */
|
||||
vtr::vector<PcfIoConstraintId, openfpga::BasicPort> io_constraint_pins_;
|
||||
|
||||
/* Nets to constraint */
|
||||
vtr::vector<PcfIoConstraintId, std::string> io_constraint_nets_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/************************************************************************
|
||||
* A header file for PinConstraints class, including critical data declaration
|
||||
* Please include this file only for using any PinConstraints data structure
|
||||
* Refer to pin_constraints.h for more details
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Create strong id for PinConstraints to avoid illegal type casting
|
||||
***********************************************************************/
|
||||
#ifndef PCF_DATA_FWD_H
|
||||
#define PCF_DATA_FWD_H
|
||||
|
||||
#include "vtr_strong_id.h"
|
||||
|
||||
struct pcf_io_constraint_id_tag;
|
||||
|
||||
typedef vtr::StrongId<pcf_io_constraint_id_tag> PcfIoConstraintId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class PcfData;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
#include "blif_head_reader.h"
|
||||
|
||||
namespace blifparse {
|
||||
|
||||
void BlifHeadReader::start_parse() {
|
||||
//Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::finish_parse() {
|
||||
//Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::begin_model(std::string model_name) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::inputs(std::vector<std::string> input_conns) {
|
||||
input_pins_ = input_conns;
|
||||
}
|
||||
|
||||
void BlifHeadReader::outputs(std::vector<std::string> output_conns) {
|
||||
output_pins_ = output_conns;
|
||||
}
|
||||
|
||||
void BlifHeadReader::names(std::vector<std::string> nets, std::vector<std::vector<LogicValue>> so_cover) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::latch(std::string input, std::string output, LatchType type, std::string control, LogicValue init) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::subckt(std::string model, std::vector<std::string> ports, std::vector<std::string> nets) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::blackbox() {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::end_model() {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::conn(std::string src, std::string dst) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::cname(std::string cell_name) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::attr(std::string name, std::string value) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::param(std::string name, std::string value) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::filename(std::string fname) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
void BlifHeadReader::lineno(int line_num) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef BLIF_HEAD_READER
|
||||
#define BLIF_HEAD_READER
|
||||
#include <cstdio>
|
||||
#include "blifparse.hpp"
|
||||
#include "vtr_log.h"
|
||||
|
||||
namespace blifparse {
|
||||
|
||||
//An example callback which pretty-prints to stdout
|
||||
//the BLIF which is being parsed
|
||||
class BlifHeadReader : public Callback {
|
||||
public:
|
||||
void start_parse() override;
|
||||
void filename(std::string fname) override;
|
||||
void lineno(int line_num) override;
|
||||
void begin_model(std::string model_name) override;
|
||||
void inputs(std::vector<std::string> inputs) override;
|
||||
void outputs(std::vector<std::string> outputs) override;
|
||||
|
||||
void names(std::vector<std::string> nets, std::vector<std::vector<LogicValue>> so_cover) override;
|
||||
|
||||
void latch(std::string input, std::string output, LatchType type, std::string control, LogicValue init) override;
|
||||
|
||||
void subckt(std::string model, std::vector<std::string> ports, std::vector<std::string> nets) override;
|
||||
|
||||
void blackbox() override;
|
||||
|
||||
void end_model() override;
|
||||
|
||||
//BLIF Extensions
|
||||
void conn(std::string src, std::string dst) override;
|
||||
void cname(std::string cell_name) override;
|
||||
void attr(std::string name, std::string value) override;
|
||||
void param(std::string name, std::string value) override;
|
||||
|
||||
void finish_parse() override;
|
||||
|
||||
void parse_error(const int curr_lineno, const std::string& near_text, const std::string& msg) override {
|
||||
VTR_LOG_ERROR("Error when parsing .blif at line %d near '%s': %s\n", curr_lineno, near_text.c_str(), msg.c_str());
|
||||
had_error_ = true;
|
||||
}
|
||||
|
||||
bool had_error() { return had_error_; }
|
||||
std::vector<std::string> input_pins() { return input_pins_; }
|
||||
std::vector<std::string> output_pins() { return output_pins_; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> input_pins_;
|
||||
std::vector<std::string> output_pins_;
|
||||
bool had_error_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,78 @@
|
|||
/******************************************************************************
|
||||
* Inspired from https://github.com/genbtc/VerilogPCFparser
|
||||
******************************************************************************/
|
||||
#include <sstream>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "pcf_reader.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**************************************************
|
||||
* Constants
|
||||
*************************************************/
|
||||
constexpr char COMMENT = '#';
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a repack pin constraint object to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are serious errors when parsing data
|
||||
* Return 2 if fail when opening files
|
||||
*******************************************************************/
|
||||
int read_pcf(const char* fname,
|
||||
PcfData& pcf_data) {
|
||||
vtr::ScopedStartFinishTimer timer("Read " + std::string(fname));
|
||||
|
||||
/* Create a file handler */
|
||||
std::ifstream fp(fname);
|
||||
if (!fp.is_open()) {
|
||||
VTR_LOG_ERROR("Fail to open pcf file '%s'!", fname);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int num_err = 0;
|
||||
|
||||
/* Get line by line */
|
||||
std::string line;
|
||||
while (std::getline(fp, line)) {
|
||||
std::stringstream ss(line);
|
||||
while (ss) {
|
||||
std::string word;
|
||||
/* TODO: Use command parser */
|
||||
if (ss >> word) {
|
||||
if (word.find("set_io") == 0) {
|
||||
std::string net_name;
|
||||
std::string pin_name;
|
||||
ss >> net_name >> pin_name;
|
||||
/* Decode data */
|
||||
PcfIoConstraintId io_id = pcf_data.create_io_constraint();
|
||||
pcf_data.set_io_net(io_id, net_name);
|
||||
pcf_data.set_io_pin(io_id, pin_name);
|
||||
} else if (word[0] == COMMENT) { // if it's a comment
|
||||
break; //or ignore the full line comment and move on
|
||||
} else {
|
||||
/* Reach unknown command, error out */
|
||||
VTR_LOG_ERROR("Unknown command '%s'!\n", word.c_str());
|
||||
num_err++;
|
||||
break; //and move onto next line. without this, it will accept more following values on this line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (num_err) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef PCF_READER_H
|
||||
#define PCF_READER_H
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include "pcf_data.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/* Parse a .pcf file through a stream, return an object which contains all the data */
|
||||
int read_pcf(const char* fname,
|
||||
PcfData& pcf_data);
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
/******************************************************************************
|
||||
* Inspired from https://github.com/genbtc/VerilogPCFparser
|
||||
******************************************************************************/
|
||||
#include <sstream>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
/* Headers from arch openfpga library */
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
#include "pcf_writer.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**************************************************
|
||||
* Constants
|
||||
*************************************************/
|
||||
constexpr char* CMD_SET_IO = "set_io";
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a repack pin constraint object to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are serious errors when parsing data
|
||||
* Return 2 if fail when opening files
|
||||
*******************************************************************/
|
||||
int write_pcf(const char* fname,
|
||||
const PcfData& pcf_data) {
|
||||
vtr::ScopedStartFinishTimer timer("Write " + std::string(fname));
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Write from data */
|
||||
for (const PcfIoConstraintId& io_id : pcf_data.io_constraints()) {
|
||||
fp << CMD_SET_IO << " ";
|
||||
fp << pcf_data.io_net(io_id).c_str() << " ";
|
||||
fp << generate_xml_port_name(pcf_data.io_pin(io_id)).c_str() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef PCF_WRITER_H
|
||||
#define PCF_WRITER_H
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include "pcf_data.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/* Write a .pcf file from an object */
|
||||
int write_pcf(const char* fname,
|
||||
const PcfData& pcf_data);
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of pin constraints to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from libopenfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
|
||||
#include "rapidcsv.h"
|
||||
|
||||
#include "read_csv_io_pin_table.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <pin_constraints> to an object of PinConstraints
|
||||
*******************************************************************/
|
||||
IoPinTable read_csv_io_pin_table(const char* fname) {
|
||||
vtr::ScopedStartFinishTimer timer("Read I/O Pin Table");
|
||||
|
||||
IoPinTable io_pin_table;
|
||||
|
||||
rapidcsv::Document doc(fname, rapidcsv::LabelParams(-1, -1),
|
||||
rapidcsv::SeparatorParams(','));
|
||||
|
||||
/* TODO: Move this to constants */
|
||||
std::map<std::string, e_side> side_str_map { {"TOP", TOP}, {"RIGHT", RIGHT}, {"LEFT", LEFT}, {"BOTTOM", BOTTOM} };
|
||||
|
||||
int num_rows = doc.GetRowCount();
|
||||
io_pin_table.reserve_pins(num_rows);
|
||||
|
||||
for (int irow = 1; irow < num_rows; irow++) {
|
||||
std::vector<std::string> row_vec = doc.GetRow<std::string>(irow);
|
||||
IoPinTableId pin_id = io_pin_table.create_pin();
|
||||
/* Fill pin-level information */
|
||||
PortParser internal_pin_parser(row_vec.at(4));
|
||||
io_pin_table.set_internal_pin(pin_id, internal_pin_parser.port());
|
||||
|
||||
PortParser external_pin_parser(row_vec.at(5));
|
||||
io_pin_table.set_external_pin(pin_id, external_pin_parser.port());
|
||||
|
||||
std::string pin_side_str = row_vec.at(0);
|
||||
if (side_str_map.end() == side_str_map.find(pin_side_str)) {
|
||||
VTR_LOG("Invalid side defintion (='%s')! Expect [TOP|RIGHT|LEFT|BOTTOM]\n", pin_side_str.c_str());
|
||||
exit(1);
|
||||
} else {
|
||||
io_pin_table.set_pin_side(pin_id, side_str_map[pin_side_str]);
|
||||
}
|
||||
|
||||
/*This is not general purpose: we should have an explicit attribute in the csv file to decalare direction */
|
||||
if (internal_pin_parser.port().get_name().find("A2F") != std::string::npos) {
|
||||
io_pin_table.set_pin_direction(pin_id, IoPinTable::INPUT);
|
||||
} else if (internal_pin_parser.port().get_name().find("F2A") != std::string::npos) {
|
||||
io_pin_table.set_pin_direction(pin_id, IoPinTable::OUTPUT);
|
||||
} else {
|
||||
VTR_LOG("Invalid direction defintion! Expect [A2F|F2A] in the pin name\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return io_pin_table;
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef READ_CSV_IO_PIN_TABLE_H
|
||||
#define READ_CSV_IO_PIN_TABLE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include "io_pin_table.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
IoPinTable read_csv_io_pin_table(const char* fname);
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,93 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of I/O location to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from pugi XML library */
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from libopenfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
|
||||
/* Headers from libarchfpga */
|
||||
#include "arch_error.h"
|
||||
#include "read_xml_util.h"
|
||||
|
||||
#include "read_xml_io_location_map.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <set_io> to an object of PinConstraint
|
||||
*******************************************************************/
|
||||
static
|
||||
void read_xml_one_io_location(pugi::xml_node& xml_io,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
IoLocationMap& io_location_map) {
|
||||
openfpga::PortParser port_parser(get_attribute(xml_io, "pad", loc_data).as_string());
|
||||
|
||||
int x_coord = get_attribute(xml_io, "x", loc_data).as_int();
|
||||
int y_coord = get_attribute(xml_io, "y", loc_data).as_int();
|
||||
int z_coord = get_attribute(xml_io, "z", loc_data).as_int();
|
||||
|
||||
/* Sanity checks */
|
||||
if (x_coord < 0 || y_coord < 0 || z_coord < 0) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_io),
|
||||
"Invalid coordinate (x, y, z) = (%d, %d, %d)! Expect zero or a positive integer!\n",
|
||||
x_coord, y_coord, z_coord);
|
||||
}
|
||||
if (port_parser.port().get_width() != 1) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_io),
|
||||
"I/O (%s) does not have a port size of 1!\n",
|
||||
get_attribute(xml_io, "pad", loc_data).as_string());
|
||||
}
|
||||
io_location_map.set_io_index(size_t(x_coord), size_t(y_coord), size_t(z_coord), port_parser.port().get_name(), port_parser.port().get_lsb());
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <io_coordinates> to an object of PinConstraints
|
||||
*******************************************************************/
|
||||
IoLocationMap read_xml_io_location_map(const char* fname) {
|
||||
|
||||
vtr::ScopedStartFinishTimer timer("Read I/O Location Map");
|
||||
|
||||
IoLocationMap io_location_map;
|
||||
|
||||
/* Parse the file */
|
||||
pugi::xml_document doc;
|
||||
pugiutil::loc_data loc_data;
|
||||
|
||||
try {
|
||||
loc_data = pugiutil::load_xml(doc, fname);
|
||||
|
||||
pugi::xml_node xml_root = get_single_child(doc, "io_coordinates", loc_data);
|
||||
|
||||
size_t num_children = std::distance(xml_root.children().begin(), xml_root.children().end());
|
||||
/* TODO: Reserve memory space for efficiency */
|
||||
|
||||
for (pugi::xml_node xml_io : xml_root.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_io.name() != std::string("io")) {
|
||||
bad_tag(xml_io, loc_data, xml_root, {"io"});
|
||||
}
|
||||
read_xml_one_io_location(xml_io, loc_data, io_location_map);
|
||||
}
|
||||
} catch (pugiutil::XmlError& e) {
|
||||
archfpga_throw(fname, e.line(),
|
||||
"%s", e.what());
|
||||
}
|
||||
|
||||
return io_location_map;
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
#ifndef OPENFPGA_CONSTRAIN_PIN_LOCATION_COMMAND_H
|
||||
#define OPENFPGA_CONSTRAIN_PIN_LOCATION_COMMAND_H
|
||||
#ifndef READ_XML_IO_LOCATION_MAP_H
|
||||
#define READ_XML_IO_LOCATION_MAP_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "shell.h"
|
||||
#include "openfpga_context.h"
|
||||
#include "io_location_map.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void add_openfpga_constrain_pin_location_command(openfpga::Shell<OpenfpgaContext>& shell);
|
||||
IoLocationMap read_xml_io_location_map(const char* fname);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of pin constraints to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from libopenfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
/* Headers from arch openfpga library */
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
#include "write_csv_io_pin_table.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Write I/O pin table to a csv file
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious errors
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
int write_csv_io_pin_table(const char* fname,
|
||||
const IoPinTable& io_pin_table) {
|
||||
vtr::ScopedStartFinishTimer timer("Write I/O Pin Table");
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* TODO: Move this to constants header file */
|
||||
std::array<std::string, IoPinTable::NUM_IO_DIRECTIONS> IO_DIRECTION_STRING = {"input", "output"};
|
||||
|
||||
/* Print head row */
|
||||
std::vector<std::string> head_row_str({"orientation", "port_name", "mapped_pin", "direction"});
|
||||
for (size_t icol = 0; icol < head_row_str.size(); icol++) {
|
||||
fp << head_row_str[icol];
|
||||
if (icol < head_row_str.size() - 1) {
|
||||
fp << ",";
|
||||
}
|
||||
}
|
||||
fp << "\n";
|
||||
|
||||
/* Print data */
|
||||
for (const IoPinTableId& pin_id : io_pin_table.pins()) {
|
||||
std::vector<std::string> data_row_str;
|
||||
data_row_str.push_back(SIDE_STRING[io_pin_table.pin_side(pin_id)]);
|
||||
data_row_str.push_back(generate_xml_port_name(io_pin_table.internal_pin(pin_id)));
|
||||
data_row_str.push_back(generate_xml_port_name(io_pin_table.external_pin(pin_id)));
|
||||
data_row_str.push_back(IO_DIRECTION_STRING[io_pin_table.pin_direction(pin_id)]);
|
||||
for (size_t icol = 0; icol < head_row_str.size(); icol++) {
|
||||
fp << data_row_str[icol];
|
||||
if (icol < data_row_str.size() - 1) {
|
||||
fp << ",";
|
||||
}
|
||||
}
|
||||
fp << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef WRITE_CSV_IO_PIN_TABLE_H
|
||||
#define WRITE_CSV_IO_PIN_TABLE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include "io_pin_table.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
int write_csv_io_pin_table(const char* fname,
|
||||
const IoPinTable& io_pin_table);
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,42 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "blif_head_reader.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT(2 == argc);
|
||||
|
||||
/* Parse the blif */
|
||||
blifparse::BlifHeadReader callback;
|
||||
blifparse::blif_parse_filename(argv[1], callback);
|
||||
VTR_LOG("Read the blif from a file: %s.\n",
|
||||
argv[1]);
|
||||
|
||||
if (callback.had_error()) {
|
||||
VTR_LOG("Read the blif ends with errors\n",
|
||||
argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Output */
|
||||
VTR_LOG("Input pins: \n");
|
||||
for (const std::string& pin : callback.input_pins()) {
|
||||
VTR_LOG("%s\n", pin.c_str());
|
||||
}
|
||||
VTR_LOG("Output pins: \n");
|
||||
for (const std::string& pin : callback.output_pins()) {
|
||||
VTR_LOG("%s\n", pin.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "read_csv_io_pin_table.h"
|
||||
#include "write_csv_io_pin_table.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
/* Parse the fabric key from an XML file */
|
||||
openfpga::IoPinTable io_pin_table = openfpga::read_csv_io_pin_table(argv[1]);
|
||||
VTR_LOG("Read the I/O pin table from a csv file: %s.\n",
|
||||
argv[1]);
|
||||
|
||||
/* Output to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
write_csv_io_pin_table(argv[2], io_pin_table);
|
||||
VTR_LOG("Echo the I/O pin table to a csv file: %s.\n",
|
||||
argv[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "pcf_reader.h"
|
||||
#include "pcf_writer.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
/* Parse the fabric key from an XML file */
|
||||
openfpga::PcfData pcf_data;
|
||||
openfpga::read_pcf(argv[1], pcf_data);
|
||||
VTR_LOG("Read the design constraints from a pcf file: %s.\n",
|
||||
argv[1]);
|
||||
|
||||
/* Output to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
write_pcf(argv[2], pcf_data);
|
||||
VTR_LOG("Echo the design constraints to a pcf file: %s.\n",
|
||||
argv[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "pcf_reader.h"
|
||||
#include "blif_head_reader.h"
|
||||
#include "read_csv_io_pin_table.h"
|
||||
#include "read_xml_io_location_map.h"
|
||||
#include "io_net_place.h"
|
||||
#include "pcf2place.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have the following arguments:
|
||||
* 1. Input - Users Design Constraints (.pcf)
|
||||
* 2. Input - Netlist (.blif)
|
||||
* 3. Input - Fabic I/O location map (.xml)
|
||||
* 4. Input - Chip pin table (.csv)
|
||||
* 5. Output - I/O placement (.place)
|
||||
*/
|
||||
VTR_ASSERT(6 == argc);
|
||||
|
||||
/* Parse the input files */
|
||||
openfpga::PcfData pcf_data;
|
||||
openfpga::read_pcf(argv[1], pcf_data);
|
||||
VTR_LOG("Read the design constraints from a pcf file: %s.\n",
|
||||
argv[1]);
|
||||
|
||||
blifparse::BlifHeadReader callback;
|
||||
blifparse::blif_parse_filename(argv[2], callback);
|
||||
VTR_LOG("Read the blif from a file: %s.\n",
|
||||
argv[2]);
|
||||
if (callback.had_error()) {
|
||||
VTR_LOG("Read the blif ends with errors\n",
|
||||
argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
openfpga::IoLocationMap io_location_map = openfpga::read_xml_io_location_map(argv[3]);
|
||||
VTR_LOG("Read the I/O location map from an XML file: %s.\n",
|
||||
argv[3]);
|
||||
|
||||
openfpga::IoPinTable io_pin_table = openfpga::read_csv_io_pin_table(argv[4]);
|
||||
VTR_LOG("Read the I/O pin table from a csv file: %s.\n",
|
||||
argv[4]);
|
||||
|
||||
/* Convert */
|
||||
openfpga::IoNetPlace io_net_place;
|
||||
int status = pcf2place(pcf_data, callback.input_pins(), callback.output_pins(), io_pin_table, io_location_map, io_net_place);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Output */
|
||||
status = io_net_place.write_to_place_file(argv[5], true, true);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "read_xml_io_location_map.h"
|
||||
//#include "write_xml_io_location_map.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
/* Parse the fabric key from an XML file */
|
||||
openfpga::IoLocationMap io_location_map = openfpga::read_xml_io_location_map(argv[1]);
|
||||
VTR_LOG("Read the I/O location map from an XML file: %s.\n",
|
||||
argv[1]);
|
||||
|
||||
/* Output to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
io_location_map.write_to_xml_file(std::string(argv[2]), true, true);
|
||||
VTR_LOG("Echo the I/O location map to an XML file: %s.\n",
|
||||
argv[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project("libpinconstrain")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
file(GLOB_RECURSE BLIF_READ_HEADERS ../../vpr/src/base/*.h)
|
||||
files_to_dirs(BLIF_READ_HEADERS BLIF_READ_INCLUDE_DIRS)
|
||||
|
||||
file(GLOB_RECURSE VTR_UTIL_HEADERS ../../libs/libvtrutil/src/*.h)
|
||||
files_to_dirs(VTR_UTIL_HEADERS VTR_UTIL_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||
|
||||
#Create the library
|
||||
add_library(libpinconstrain STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(libpinconstrain PUBLIC ${LIB_INCLUDE_DIRS} ${BLIF_READ_INCLUDE_DIRS} ${VTR_UTIL_INCLUDE_DIRS})
|
||||
set_target_properties(libpinconstrain PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Specify link-time dependancies
|
||||
target_link_libraries(libpinconstrain
|
||||
libvpr
|
||||
libvtrutil
|
||||
libpugixml)
|
||||
|
||||
#Create the test executable
|
||||
foreach(testsourcefile ${EXEC_SOURCES})
|
||||
# Use a simple string replace, to cut off .cpp.
|
||||
get_filename_component(testname ${testsourcefile} NAME_WE)
|
||||
add_executable(${testname} ${testsourcefile})
|
||||
target_include_directories(${testname} PUBLIC ${LIB_INCLUDE_DIRS} ${BLIF_READ_INCLUDE_DIRS} ${VTR_UTIL_INCLUDE_DIRS})
|
||||
# Make sure the library is linked to each test executable
|
||||
target_link_libraries(${testname} libpinconstrain)
|
||||
endforeach(testsourcefile ${EXEC_SOURCES})
|
|
@ -1,3 +0,0 @@
|
|||
set_io a pad_fpga_io[0]
|
||||
set_io b pad_fpga_io[4]
|
||||
set_io c pad_fpga_io[6]
|
|
@ -1,17 +0,0 @@
|
|||
orientation,row,col,pin_num_in_cell,port_name,mapped_pin,GPIO_type,Associated Clock,Clock Edge
|
||||
TOP,,,,gfpga_pad_IO_A2F[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[31],pad_fpga_io[3],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[31],pad_fpga_io[3],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[40],pad_fpga_io[5],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[40],pad_fpga_io[5],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_A2F[64],pad_fpga_io[6],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_F2A[64],pad_fpga_io[6],,,
|
||||
LEFT,,,,gfpga_pad_IO_F2A[127],pad_fpga_io[7],,,
|
||||
LEFT,,,,gfpga_pad_IO_A2F[127],pad_fpga_io[7],,,
|
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
XML file specification is primarily to define the mapping of the interface cell ports defined
|
||||
in vpr_arch xml, to the EFPGA IO interface port names. This mapping is required by OpenFPGA
|
||||
alongwith architecture definition file i.e. vpr_arch xml file. OpenFPGA will process this
|
||||
file and use this information for IO placement and then later on use this to map it with the
|
||||
user-defined pin-mapping file.
|
||||
-->
|
||||
<DEVICE name= "k4_N4_tileable_40nm" family="k4n4" width="6" height="6" z="8">
|
||||
<IO>
|
||||
<TOP_IO y="5">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[0:31]" startx="1" endx="4"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[0:31]" startx="1" endx="4"/>
|
||||
</TOP_IO>
|
||||
<RIGHT_IO x="5">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[32:63]" starty="4" endy="1"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[32:63]" starty="4" endy="1"/>
|
||||
</RIGHT_IO>
|
||||
<BOTTOM_IO y="0">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[64:95]" startx="4" endx="1"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[64:95]" startx="4" endx="1"/>
|
||||
</BOTTOM_IO>
|
||||
<LEFT_IO x="0">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[96:127]" starty="1" endy="4"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[96:127]" starty="1" endy="4"/>
|
||||
</LEFT_IO>
|
||||
</IO>
|
||||
</DEVICE>
|
|
@ -1,71 +0,0 @@
|
|||
#include "vtr_path.h"
|
||||
#include "read_blif.h"
|
||||
#include "blifparse.hpp"
|
||||
#include "blif_reader.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
// blif parser callback
|
||||
using namespace blifparse;
|
||||
class BlifParserCallback : public Callback {
|
||||
public:
|
||||
void start_parse() override {}
|
||||
|
||||
void filename(std::string /*fname*/) override {}
|
||||
void lineno(int /*line_num*/) override {}
|
||||
|
||||
void begin_model(std::string /*model_name*/) override {}
|
||||
void inputs(std::vector<std::string> input_ports) override {
|
||||
for (auto input_port : input_ports) {
|
||||
inputs_.push_back(input_port);
|
||||
}
|
||||
}
|
||||
void outputs(std::vector<std::string> output_ports) override {
|
||||
for (auto output_port : output_ports) {
|
||||
outputs_.push_back(output_port);
|
||||
}
|
||||
}
|
||||
|
||||
void names(std::vector<std::string> /*nets*/, std::vector<std:: vector<LogicValue>> /*so_cover*/) override {}
|
||||
void latch(std::string /*input*/, std::string /*output*/, LatchType /* type*/, std::string /*control*/, LogicValue /*init*/) override {}
|
||||
void subckt(std::string /*model*/, std::vector<std::string> /*ports*/, std:: vector<std::string> /*nets*/) override {}
|
||||
void blackbox() override {}
|
||||
|
||||
void end_model() override {}
|
||||
|
||||
void finish_parse() override {}
|
||||
|
||||
void parse_error(const int curr_lineno, const std::string& near_text, const std::string& msg) override {
|
||||
VTR_LOG_ERROR("Custom Error at line %d near '%s': %s\n", curr_lineno, near_text.c_str(), msg.c_str());
|
||||
had_error_ = true;
|
||||
}
|
||||
|
||||
bool had_error() { return had_error_ == true; }
|
||||
std::vector<std::string> get_inputs() { return inputs_;}
|
||||
std::vector<std::string> get_outputs() { return outputs_;}
|
||||
private:
|
||||
bool had_error_ = false;
|
||||
std::vector<std::string> inputs_;
|
||||
std::vector<std::string> outputs_;
|
||||
};
|
||||
|
||||
// read port info from blif file
|
||||
bool BlifReader::read_blif(const std::string &blif_file_name)
|
||||
{
|
||||
e_circuit_format circuit_format;
|
||||
auto name_ext = vtr::split_ext(blif_file_name);
|
||||
if (name_ext[1] == ".blif") {
|
||||
circuit_format = e_circuit_format::BLIF;
|
||||
} else if (name_ext[1] == ".eblif") {
|
||||
circuit_format = e_circuit_format::EBLIF;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
BlifParserCallback callback;
|
||||
blif_parse_filename(blif_file_name, callback);
|
||||
if (callback.had_error()) {
|
||||
return false;
|
||||
}
|
||||
inputs = callback.get_inputs();
|
||||
outputs = callback.get_outputs();
|
||||
return true;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#ifndef BLIF_READER_H
|
||||
#define BLIF_READER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
Supported PCF commands:
|
||||
|
||||
* set_io <net> <pad> - constrain a given <net> to a given physical <pad> in eFPGA pinout.
|
||||
* set_clk <pin> <net> - constrain a given global clock <pin> to a given <net>
|
||||
|
||||
Every tile where <net> is present will be constrained to use a given global clock.
|
||||
*/
|
||||
using namespace std;
|
||||
class BlifReader
|
||||
{
|
||||
vector<string> inputs;
|
||||
vector<string> outputs;
|
||||
|
||||
public:
|
||||
BlifReader() {}
|
||||
BlifReader(const std::string &f)
|
||||
{
|
||||
read_blif(f);
|
||||
}
|
||||
bool read_blif(const std::string &f);
|
||||
const vector<string>& get_inputs()const { return inputs;}
|
||||
const vector<string>& get_outputs()const { return outputs;}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,53 +0,0 @@
|
|||
#include "csv_reader.h"
|
||||
#include <algorithm>
|
||||
#include "vtr_log.h"
|
||||
|
||||
|
||||
bool CvsReader::read_cvs(const std::string &f)
|
||||
{
|
||||
std::ifstream infile(f);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
VTR_LOG_ERROR("ERROR: cound not open the file %s.\n", f.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
// cout << line << " - " << endl;
|
||||
if (!line.size())
|
||||
continue;
|
||||
entries.push_back(vector<string>());
|
||||
entries.back().push_back(string(""));
|
||||
for (auto c : line)
|
||||
{
|
||||
if (isspace(c))
|
||||
continue;
|
||||
if (c == ',')
|
||||
{
|
||||
entries.back().push_back(string(""));
|
||||
}
|
||||
else
|
||||
entries.back().back().push_back(c);
|
||||
}
|
||||
}
|
||||
std::vector<string> first_v = entries[0];
|
||||
auto result1 = std::find(first_v.begin(), first_v.end(), "port_name");
|
||||
vector<string>::iterator result2 = find(first_v.begin(), first_v.end(), "mapped_pin");
|
||||
int port_name_index = distance(first_v.begin(), result1);
|
||||
int mapped_pin_index = distance(first_v.begin(), result2);
|
||||
|
||||
for (auto &v : entries)
|
||||
{
|
||||
string port_name = v[port_name_index];
|
||||
string mapped_pin = v[mapped_pin_index];
|
||||
port_map.insert(std::pair<std::string, string>(mapped_pin, port_name));
|
||||
//for (auto &s : v)
|
||||
//{
|
||||
// cout << s << " - ";
|
||||
//}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef CVS_READER_H
|
||||
#define CVS_READER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <ctype.h>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CvsReader
|
||||
{
|
||||
vector<vector<string>> entries;
|
||||
map<string, string> port_map;
|
||||
public:
|
||||
CvsReader() {}
|
||||
bool read_cvs(const std::string &f);
|
||||
const vector<vector<string>>& get_entries()const { return entries;}
|
||||
const map<string, string>& get_port_map()const { return port_map;}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,27 +0,0 @@
|
|||
#include "pcf_reader.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
bool PcfReader::read_pcf(const std::string &f)
|
||||
{
|
||||
std::ifstream infile(f);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
VTR_LOG_ERROR("ERROR: cound not open the file %s.\n", f.c_str());
|
||||
return false;
|
||||
}
|
||||
std::string line;
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
std::istringstream iss(line);
|
||||
std::string a, b, c;
|
||||
if (!(iss >> a >> b >> c))
|
||||
{
|
||||
break;
|
||||
} // error
|
||||
commands.push_back(std::vector<std::string>());
|
||||
commands.back().push_back(a);
|
||||
commands.back().push_back(b);
|
||||
commands.back().push_back(c);
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef PCF_READER_H
|
||||
#define PCF_READER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
Supported PCF commands:
|
||||
|
||||
* set_io <net> <pad> - constrain a given <net> to a given physical <pad> in eFPGA pinout.
|
||||
* set_clk <pin> <net> - constrain a given global clock <pin> to a given <net>
|
||||
|
||||
Every tile where <net> is present will be constrained to use a given global clock.
|
||||
*/
|
||||
class PcfReader
|
||||
{
|
||||
std::vector<std::vector<std::string>> commands;
|
||||
|
||||
public:
|
||||
PcfReader() {}
|
||||
PcfReader(const std::string &f)
|
||||
{
|
||||
read_pcf(f);
|
||||
}
|
||||
bool read_pcf(const std::string &f);
|
||||
const std::vector<std::vector<std::string>>& get_commands()const { return commands;}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,243 +0,0 @@
|
|||
#include "xml_reader.h"
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
//======================================================================
|
||||
std::vector<std::string> XmlReader::vec_to_scalar(std::string str)
|
||||
{
|
||||
auto open_bracket_pos = str.find("[");
|
||||
auto close_bracket_pos = str.find("]");
|
||||
auto colon_pos = str.find(":");
|
||||
std::vector<std::string> scalar_ports;
|
||||
|
||||
//Parse checks
|
||||
if (open_bracket_pos == std::string::npos && close_bracket_pos != std::string::npos) {
|
||||
//Close brace only
|
||||
std::string msg = "near '" + str + "', missing '['";
|
||||
std::cerr << " ERROR: " << msg << std::endl;
|
||||
}
|
||||
|
||||
if (open_bracket_pos != std::string::npos && close_bracket_pos == std::string::npos) {
|
||||
//Open brace only
|
||||
std::string msg = "near '" + str + "', missing ']'";
|
||||
std::cerr << " ERROR: " << msg << std::endl;
|
||||
}
|
||||
|
||||
if (open_bracket_pos != std::string::npos && close_bracket_pos != std::string::npos) {
|
||||
//Have open and close braces, close must be after open
|
||||
if (open_bracket_pos > close_bracket_pos) {
|
||||
std::string msg = "near '" + str + "', '[' after ']'";
|
||||
std::cerr << " ERROR: " << msg << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (colon_pos != std::string::npos) {
|
||||
//Have a colon, it must be between open/close braces
|
||||
if (colon_pos > close_bracket_pos || colon_pos < open_bracket_pos) {
|
||||
std::string msg = "near '" + str + "', found ':' but not between '[' and ']'";
|
||||
std::cerr << " ERROR: " << msg << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = str.substr(0, open_bracket_pos);
|
||||
std::string first_idx_str;
|
||||
std::string second_idx_str;
|
||||
|
||||
if (colon_pos == std::string::npos && open_bracket_pos == std::string::npos && close_bracket_pos == std::string::npos) {
|
||||
} else if (colon_pos == std::string::npos) {
|
||||
first_idx_str = str.substr(open_bracket_pos + 1, close_bracket_pos);
|
||||
second_idx_str = first_idx_str;
|
||||
} else {
|
||||
first_idx_str = str.substr(open_bracket_pos + 1, colon_pos);
|
||||
second_idx_str = str.substr(colon_pos + 1, close_bracket_pos);
|
||||
}
|
||||
|
||||
int first_idx = std::stoi(first_idx_str);
|
||||
int second_idx = std::stoi(second_idx_str);
|
||||
|
||||
if (first_idx < second_idx)
|
||||
{
|
||||
for(int i=first_idx; i < second_idx+1; i++)
|
||||
{
|
||||
std::string curr_port_name = name + '[' + std::to_string(i) + ']';
|
||||
scalar_ports.push_back(curr_port_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=first_idx; i >= second_idx ; i--)
|
||||
{
|
||||
std::string curr_port_name = name + '[' + std::to_string(i) + ']';
|
||||
scalar_ports.push_back(curr_port_name);
|
||||
}
|
||||
}
|
||||
return scalar_ports;
|
||||
}
|
||||
//======================================================================
|
||||
bool XmlReader::parse_io_cell (const pugi::xml_node xml_orient_io, const int row_or_col, const int io_per_cell, std::map<std::string, PinMappingData> *port_map)
|
||||
{
|
||||
pugi::xpath_node_set cells = xml_orient_io.select_nodes("CELL");
|
||||
for (pugi::xpath_node_set::const_iterator it = cells.begin(); it != cells.end(); ++it)
|
||||
{
|
||||
pugi::xpath_node node = *it;
|
||||
int startx, starty, endx, endy, i, j, x, y;
|
||||
std::string port_name = node.node().attribute("port_name").as_string();
|
||||
std::string mapped_name = node.node().attribute("mapped_name").as_string();
|
||||
std::vector<std::string> scalar_mapped_pins = vec_to_scalar(mapped_name);
|
||||
i = 0;
|
||||
|
||||
if (node.node().attribute("startx") && node.node().attribute("endx"))
|
||||
{
|
||||
startx = node.node().attribute("startx").as_int();
|
||||
endx = node.node().attribute("endx").as_int();
|
||||
y = row_or_col;
|
||||
if (startx < endx)
|
||||
{
|
||||
for (x=startx; x < endx+1; x++)
|
||||
{
|
||||
for (j=0; j < io_per_cell; j++)
|
||||
{
|
||||
std::string mapped_pin = scalar_mapped_pins[i];
|
||||
PinMappingData pinMapData = PinMappingData(port_name, mapped_pin, x, y, j);
|
||||
port_map->insert(std::pair<std::string, PinMappingData>(mapped_pin, pinMapData));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x=startx; x >= endx; x--)
|
||||
{
|
||||
for (j=0; j < io_per_cell; j++)
|
||||
{
|
||||
std::string mapped_pin = scalar_mapped_pins[i];
|
||||
PinMappingData pinMapData = PinMappingData(port_name, mapped_pin, x, y, j);
|
||||
port_map->insert(std::pair<std::string, PinMappingData>(mapped_pin, pinMapData));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.node().attribute("starty") && node.node().attribute("endy").value())
|
||||
{
|
||||
starty = node.node().attribute("starty").as_int();
|
||||
endy = node.node().attribute("endy").as_int();
|
||||
x = row_or_col;
|
||||
if (starty < endy)
|
||||
{
|
||||
for (y=starty; y < endy+1; y++)
|
||||
{
|
||||
for (j=0; j < io_per_cell; j++)
|
||||
{
|
||||
std::string mapped_pin = scalar_mapped_pins[i];
|
||||
PinMappingData pinMapData = PinMappingData(port_name, mapped_pin, x, y, j);
|
||||
port_map->insert(std::pair<std::string, PinMappingData>(mapped_pin, pinMapData));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (y=starty; y >= endy; y--)
|
||||
{
|
||||
for (j=0; j < io_per_cell; j++)
|
||||
{
|
||||
std::string mapped_pin = scalar_mapped_pins[i];
|
||||
PinMappingData pinMapData = PinMappingData(port_name, mapped_pin, x, y, j);
|
||||
port_map->insert(std::pair<std::string, PinMappingData>(mapped_pin, pinMapData));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
bool XmlReader::parse_io (const pugi::xml_node xml_io, const int width, const int height, const int io_per_cell, std::map<std::string, PinMappingData> *port_map)
|
||||
{
|
||||
pugi::xml_node xml_top_io = xml_io.child("TOP_IO");
|
||||
if (!xml_top_io)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int io_row_top = height - 1;
|
||||
if (xml_top_io.attribute("y"))
|
||||
io_row_top = xml_top_io.attribute("y").as_int();
|
||||
|
||||
pugi::xml_node xml_bottom_io = xml_io.child("BOTTOM_IO");
|
||||
if (!xml_bottom_io)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int io_row_bottom = 0;
|
||||
if (xml_bottom_io.attribute("y"))
|
||||
io_row_bottom = xml_bottom_io.attribute("y").as_int();
|
||||
|
||||
pugi::xml_node xml_left_io = xml_io.child("LEFT_IO");
|
||||
if (!xml_left_io)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int io_col_left = 0;
|
||||
if (xml_left_io.attribute("x"))
|
||||
io_col_left = xml_left_io.attribute("x").as_int();
|
||||
pugi::xml_node xml_right_io = xml_io.child("RIGHT_IO");
|
||||
if (!xml_right_io)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int io_col_right = width - 1;
|
||||
if (xml_right_io.attribute("x"))
|
||||
io_col_right = xml_right_io.attribute("x").as_int();
|
||||
|
||||
|
||||
if (!parse_io_cell (xml_top_io, io_row_top, io_per_cell, port_map))
|
||||
return false;
|
||||
if (!parse_io_cell (xml_bottom_io, io_row_bottom, io_per_cell, port_map))
|
||||
return false;
|
||||
if (!parse_io_cell (xml_right_io, io_col_right, io_per_cell, port_map))
|
||||
return false;
|
||||
if (!parse_io_cell (xml_left_io, io_col_left, io_per_cell, port_map))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//======================================================================
|
||||
|
||||
bool XmlReader::read_xml(const std::string &f)
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
std::ifstream infile(f);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
std::cerr << "ERROR: cound not open the file " << f << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
pugi::xml_parse_result result = doc.load_file(f.c_str());
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
pugi::xml_node device = doc.child("DEVICE");
|
||||
if (!device)
|
||||
return false;
|
||||
|
||||
int width = device.attribute("width").as_int();
|
||||
int height = device.attribute("height").as_int();
|
||||
int z = device.attribute("z").as_int();
|
||||
if (z <= 0)
|
||||
return false;
|
||||
|
||||
pugi::xml_node xml_io = device.child("IO");
|
||||
if (!xml_io)
|
||||
return false;
|
||||
|
||||
if (!parse_io (xml_io, width, height, z, &port_map_))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#ifndef XML_READER_H
|
||||
#define XML_READER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
class PinMappingData
|
||||
{
|
||||
public:
|
||||
PinMappingData(std::string p_name, std::string map_pin, int x, int y, int z):port_name_(p_name), mapped_pin_(map_pin), x_(x), y_(y), z_(z){}
|
||||
std::string get_port_name () { return port_name_; }
|
||||
std::string get_mapped_pin () { return mapped_pin_; }
|
||||
int get_x () { return x_; }
|
||||
int get_y () { return y_; }
|
||||
int get_z () { return z_; }
|
||||
private:
|
||||
std::string port_name_;
|
||||
std::string mapped_pin_;
|
||||
int x_;
|
||||
int y_;
|
||||
int z_;
|
||||
|
||||
};
|
||||
|
||||
class XmlReader
|
||||
{
|
||||
std::map<std::string, PinMappingData> port_map_;
|
||||
public:
|
||||
XmlReader() {}
|
||||
bool read_xml(const std::string &f);
|
||||
const std::map<std::string, PinMappingData>& get_port_map()const { return port_map_;}
|
||||
std::vector<std::string> vec_to_scalar(std::string str);
|
||||
bool parse_io_cell (const pugi::xml_node xml_orient_io, const int row_or_col, const int io_per_cell, std::map<std::string, PinMappingData> *port_map);
|
||||
bool parse_io (const pugi::xml_node xml_io, const int width, const int height, const int io_per_cell, std::map<std::string, PinMappingData> *port_map);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,23 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include "csv_reader.h"
|
||||
#include "pcf_reader.h"
|
||||
#include "xml_reader.h"
|
||||
#include "cmd_line.h"
|
||||
// #include "pin_location.h"
|
||||
#include "pin_constrain_loc.h"
|
||||
|
||||
// Convert a PCF file into a VPR io.place file.
|
||||
// This requires : XML file where we can get (x, y, z) of internal port
|
||||
// CSV file where we have the maching list (external, internal) ports
|
||||
// PCF file: constraint file. a design pin can be assigned to an external port
|
||||
// BLIF file: user design. We need to check the input and output. Special handling for outputs
|
||||
// The output is a file constraint in VPR format.
|
||||
// Usage options: --xml PINMAP_XML --pcf PCF --blif BLIF --output OUTPUT --xml PINMAP_XML --csv CSV_FILE
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
cmd_line cmd(argc, argv);
|
||||
|
||||
return pin_constrain_location_cmd_line(cmd);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#include "pin_location.h"
|
||||
#include "cmd_line.h"
|
||||
#include "pin_constrain_loc.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// wraper function to do the real job, it is used by openfpga shell as well
|
||||
// this gurantees same behavior
|
||||
int pin_constrain_location (vector<string> args) {
|
||||
if (args.size() != PIN_C_ARGUMENT_NUMBER ) {
|
||||
return 1;
|
||||
}
|
||||
const char* pin_c_args [PIN_C_ARGUMENT_NUMBER];
|
||||
for (int i = 0; i < PIN_C_ARGUMENT_NUMBER; i++) {
|
||||
pin_c_args[i] = const_cast<char*> (args[i].c_str());
|
||||
}
|
||||
cmd_line pin_c_cmd (PIN_C_ARGUMENT_NUMBER, pin_c_args);
|
||||
return pin_constrain_location_cmd_line(pin_c_cmd);
|
||||
}
|
||||
|
||||
// base function for both openfpga wrapper and pin_c executable
|
||||
int pin_constrain_location_cmd_line (cmd_line& cmd) {
|
||||
pin_location pl (cmd);
|
||||
//pl.get_cmd().print_options();
|
||||
if (!pl.reader_and_writer()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#include "pin_location.h"
|
||||
#include "cmd_line.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// wraper function to do the real job, it is used by openfpga shell as well
|
||||
// this gurantees same behavior
|
||||
int pin_constrain_location (std::vector<std::string> args);
|
||||
|
||||
// base function for both openfpga wrapper and pin_c executable
|
||||
int pin_constrain_location_cmd_line (cmd_line& cmd);
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
|
||||
#include "csv_reader.h"
|
||||
#include "pcf_reader.h"
|
||||
#include "xml_reader.h"
|
||||
#include "blif_reader.h"
|
||||
#include "cmd_line.h"
|
||||
#include "pin_location.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "vtr_log.h"
|
||||
|
||||
const string USAGE_MSG = "usage options: --xml PINMAP_XML --pcf PCF --blif BLIF --csv CSV_FILE --output OUTPUT";
|
||||
const cmd_line & pin_location::get_cmd() const
|
||||
{
|
||||
return cl_;
|
||||
}
|
||||
|
||||
bool pin_location::reader_and_writer()
|
||||
{
|
||||
cmd_line cmd = cl_;
|
||||
string xml_name = cmd.get_param("--xml");
|
||||
string csv_name = cmd.get_param("--csv");
|
||||
string pcf_name = cmd.get_param("--pcf");
|
||||
string blif_name = cmd.get_param("--blif");
|
||||
string output_name = cmd.get_param("--output");
|
||||
if ((xml_name == "") || (csv_name == "") || (pcf_name == "") || (blif_name == "")|| (output_name == "") )
|
||||
{
|
||||
VTR_LOG_ERROR("%s\n %s\n", error_messages[MISSING_IN_OUT_FILES].c_str(), USAGE_MSG.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlReader rd_xml;
|
||||
if (!rd_xml.read_xml(xml_name))
|
||||
{
|
||||
VTR_LOG_ERROR("%s.\n", error_messages[PIN_LOC_XML_PARSE_ERROR].c_str());
|
||||
return false;
|
||||
}
|
||||
std::map<std::string, PinMappingData> xml_port_map = rd_xml.get_port_map();
|
||||
|
||||
CvsReader rd_csv;
|
||||
if (!rd_csv.read_cvs(csv_name))
|
||||
{
|
||||
VTR_LOG_ERROR("%s.\n", error_messages[PIN_MAP_CSV_PARSE_ERROR].c_str());
|
||||
return false;
|
||||
}
|
||||
map<string, string> csv_port_map = rd_csv.get_port_map();
|
||||
|
||||
PcfReader rd_pcf;
|
||||
if (!rd_pcf.read_pcf(pcf_name))
|
||||
{
|
||||
VTR_LOG_ERROR("%s.\n", error_messages[PIN_CONSTRAINT_PARSE_ERROR].c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// read port info from blif file
|
||||
BlifReader rd_blif;
|
||||
if (!rd_blif.read_blif(blif_name)) {
|
||||
VTR_LOG_ERROR("%s.\n", error_messages[INPUT_DESIGN_PARSE_ERROR].c_str());
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> inputs = rd_blif.get_inputs();
|
||||
std::vector<std::string> outputs = rd_blif.get_outputs();
|
||||
|
||||
std::ofstream out_file;
|
||||
out_file.open(output_name);
|
||||
out_file << "#Block Name x y z\n";
|
||||
out_file << "#------------ -- -- -\n";
|
||||
|
||||
vector<vector<string>> pcf_pin_cstr = rd_pcf.get_commands();
|
||||
std::set<std::string> constrained_ports, constrained_pins;
|
||||
for (auto pin_cstr_v : pcf_pin_cstr)
|
||||
{
|
||||
if ((pin_cstr_v[0] != "set_io") && (pin_cstr_v[0] != "set_clk"))
|
||||
continue;
|
||||
|
||||
string pin_name = pin_cstr_v[1];
|
||||
string cstr_name = pin_cstr_v[2];
|
||||
auto found_in = std::find(inputs.begin(), inputs.end(), pin_name);
|
||||
bool valid = false, is_out = false;
|
||||
|
||||
if (found_in != inputs.end())
|
||||
valid = true;
|
||||
else
|
||||
{
|
||||
auto found_out = std::find(outputs.begin(), outputs.end(), pin_name);
|
||||
if (found_out != outputs.end())
|
||||
{
|
||||
valid = true;
|
||||
is_out = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VTR_LOG_ERROR("%s: <%s>.\n", error_messages[CONSTRAINED_PORT_NOT_FOUND].c_str(), pin_name.c_str());
|
||||
out_file.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (constrained_ports.find(pin_name) == constrained_ports.end()) {
|
||||
constrained_ports.insert(pin_name);
|
||||
} else {
|
||||
VTR_LOG_ERROR("%s: <%s>.\n", error_messages[RE_CONSTRAINED_PORT].c_str(), pin_name.c_str());
|
||||
out_file.close();
|
||||
return false;
|
||||
}
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
std::string content_to_write;
|
||||
|
||||
// Get the the intternal port from the CSV file
|
||||
auto element_cstr = csv_port_map.find(cstr_name);
|
||||
if (element_cstr != csv_port_map.end())
|
||||
{
|
||||
|
||||
if (constrained_pins.find(cstr_name) == constrained_pins.end()) {
|
||||
constrained_pins.insert(cstr_name);
|
||||
} else {
|
||||
VTR_LOG_ERROR("%s: <%s>.\n", error_messages[OVERLAP_PIN_IN_CONSTRAINT].c_str(), cstr_name.c_str());
|
||||
out_file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the (x, y, z) from the XML reader
|
||||
PinMappingData pinMapData = xml_port_map.at(element_cstr->second);
|
||||
if (is_out)
|
||||
{
|
||||
content_to_write += "out:";
|
||||
}
|
||||
content_to_write += pin_name + " " + std::to_string(pinMapData.get_x()) + " " + std::to_string(pinMapData.get_y()) + " " + std::to_string(pinMapData.get_z()) + "\n";
|
||||
} else {
|
||||
VTR_LOG_ERROR("%s: <%s>.\n", error_messages[CONSTRAINED_PIN_NOT_FOUND].c_str(), cstr_name.c_str());
|
||||
out_file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
out_file << content_to_write;
|
||||
}
|
||||
out_file.close();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#ifndef PIN_LOCATION_H
|
||||
#define PIN_LOCATION_H
|
||||
#include "cmd_line.h"
|
||||
|
||||
// number of arguments for "pin_c", inlcuding the "pin_c" command itself
|
||||
#define PIN_C_ARGUMENT_NUMBER 11
|
||||
class pin_location {
|
||||
private:
|
||||
// error messages to be printed out with std::cerr
|
||||
enum {
|
||||
MISSING_IN_OUT_FILES = 0,
|
||||
PIN_LOC_XML_PARSE_ERROR,
|
||||
PIN_MAP_CSV_PARSE_ERROR,
|
||||
PIN_CONSTRAINT_PARSE_ERROR,
|
||||
INPUT_DESIGN_PARSE_ERROR,
|
||||
CONSTRAINED_PORT_NOT_FOUND,
|
||||
CONSTRAINED_PIN_NOT_FOUND,
|
||||
RE_CONSTRAINED_PORT,
|
||||
OVERLAP_PIN_IN_CONSTRAINT,
|
||||
MAX_MESSAGE_ID
|
||||
};
|
||||
std::string error_messages[MAX_MESSAGE_ID] = {
|
||||
"Missing input or output file arguments", // MISSING_IN_OUT_FILES
|
||||
"Pin location file parse error", // PIN_LOC_XML_PARSE_ERROR
|
||||
"Pin map file parse error", // PIN_MAP_CSV_PARSE_ERROR
|
||||
"Pin constraint file parse error", // PIN_CONSTRAINT_PARSE_ERROR
|
||||
"Input design parse error", // INPUT_DESIGN_PARSE_ERROR
|
||||
"Constrained port not found in design", // CONSTRAINED_PORT_NOT_FOUND
|
||||
"Constrained pin not found in device", // CONSTRAINED_PIN_NOT_FOUND
|
||||
"Re-constrained port", // RE_CONSTRAINED_PORT
|
||||
"Overlap pin found in constraint" // OVERLAP_PIN_IN_CONSTRAINT
|
||||
};
|
||||
|
||||
cmd_line cl_;
|
||||
|
||||
public:
|
||||
pin_location(cmd_line& cl): cl_(cl){
|
||||
|
||||
}
|
||||
const cmd_line& get_cmd()const;
|
||||
bool reader_and_writer();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,77 +0,0 @@
|
|||
#include "cmd_line.h"
|
||||
|
||||
cmd_line::cmd_line(int argc, const char *argv[])
|
||||
{
|
||||
bool needVal = false;
|
||||
string key;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
string s(argv[i]);
|
||||
if (s.size() < 2)
|
||||
{
|
||||
cout << "Warning: Not a valid flag \"" << s << "\" discarding" << endl;
|
||||
continue;
|
||||
}
|
||||
if ('-' == s[0])
|
||||
{
|
||||
if ('-' == s[1])
|
||||
{ // param key
|
||||
if (needVal)
|
||||
cout << "Warning: Key " << key << " did not get a value" << endl;
|
||||
needVal = true;
|
||||
key = s;
|
||||
}
|
||||
else
|
||||
{ // flag
|
||||
flags.insert(s);
|
||||
if (needVal)
|
||||
cout << "Warning: Key " << key << " did not get a value" << endl;
|
||||
needVal = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // param value
|
||||
if (needVal)
|
||||
{
|
||||
params[key] = s;
|
||||
needVal = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Warning: No key for value " << s << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmd_line::is_flag_set(string &fl)
|
||||
{
|
||||
return (flags.find(fl) != end(flags));
|
||||
}
|
||||
|
||||
string cmd_line::get_param(const string &key)
|
||||
{
|
||||
if (params.find(key) != end(params))
|
||||
return params[key];
|
||||
return "";
|
||||
}
|
||||
|
||||
void cmd_line::set_flag(string &fl)
|
||||
{
|
||||
flags.insert(fl);
|
||||
}
|
||||
|
||||
void cmd_line::set_param_value(string &key, string &val)
|
||||
{
|
||||
params[key] = val;
|
||||
}
|
||||
|
||||
void cmd_line::print_options() const
|
||||
{
|
||||
cout << "Flags :\n";
|
||||
for (auto &f : flags)
|
||||
cout << "\t" << f << endl;
|
||||
cout << "Params :\n";
|
||||
for (auto &p : params)
|
||||
cout << "\t" << p.first << "\t" << p.second << endl;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef CMD_LINE
|
||||
#define CMD_LINE
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
using namespace std;
|
||||
class cmd_line
|
||||
{
|
||||
unordered_map<string, string> params;
|
||||
unordered_set<string> flags;
|
||||
|
||||
public:
|
||||
cmd_line(int argc, const char *argv[]);
|
||||
const unordered_set<string> &get_flag_set() const { return flags; }
|
||||
const unordered_map<string, string> get_param_map() const { return params; }
|
||||
bool is_flag_set(string &fl);
|
||||
string get_param(const string &key);
|
||||
void set_flag(string &fl);
|
||||
void set_param_value(string &key, string &val);
|
||||
void print_options() const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,30 +0,0 @@
|
|||
|
||||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "cmd_line.h"
|
||||
#include "pin_location.h"
|
||||
#include "pin_constrain_loc.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
VTR_ASSERT(argc == PIN_C_ARGUMENT_NUMBER);
|
||||
string command_line;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0) {
|
||||
command_line += " ";
|
||||
}
|
||||
command_line += argv[i];
|
||||
}
|
||||
VTR_LOG("Created command line <%s> for test.\n", command_line.c_str());
|
||||
cmd_line pin_c_cmd (argc, argv);
|
||||
VTR_LOG("Testing reader and writer.\n");
|
||||
int status = pin_constrain_location_cmd_line(pin_c_cmd);
|
||||
VTR_LOG("Test result: %s.\n", status == 0 ? "PASS" : "FAIL");
|
||||
return status;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
set_io a pad_fpga_io[0]
|
||||
set_io b pad_fpga_io[4]
|
||||
set_io c pad_fpga_io[6]
|
|
@ -1,35 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project("librepackdc")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||
|
||||
#Create the library
|
||||
add_library(librepackdc STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(librepackdc PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(librepackdc PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Specify link-time dependancies
|
||||
target_link_libraries(librepackdc
|
||||
libopenfpgautil
|
||||
libarchopenfpga
|
||||
libvtrutil
|
||||
libpugixml
|
||||
libpugiutil)
|
||||
|
||||
#Create the test executable
|
||||
foreach(testsourcefile ${EXEC_SOURCES})
|
||||
# Use a simple string replace, to cut off .cpp.
|
||||
get_filename_component(testname ${testsourcefile} NAME_WE)
|
||||
add_executable(${testname} ${testsourcefile})
|
||||
# Make sure the library is linked to each test executable
|
||||
target_link_libraries(${testname} librepackdc)
|
||||
endforeach(testsourcefile ${EXEC_SOURCES})
|
|
@ -23,14 +23,12 @@ target_link_libraries(libopenfpga
|
|||
libopenfpgashell
|
||||
libopenfpgautil
|
||||
libfabrickey
|
||||
librepackdc
|
||||
libfpgabitstream
|
||||
libini
|
||||
libpcf
|
||||
libvtrutil
|
||||
libbusgroup
|
||||
libvpr
|
||||
libpinconstrain)
|
||||
libvpr)
|
||||
|
||||
#Create the test executable
|
||||
add_executable(openfpga ${EXEC_SOURCE})
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream database
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_time.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgashell library */
|
||||
#include "command_exit_codes.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_reserved_words.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
#include "openfpga_constrain_pin_location.h"
|
||||
|
||||
/* Include global variables of VPR */
|
||||
#include "globals.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
extern int pin_constrain_location (std::vector<std::string> args);
|
||||
namespace openfpga {
|
||||
/********************************************************************
|
||||
* Top-level function to read an OpenFPGA architecture file
|
||||
* we use the APIs from the libarchopenfpga library
|
||||
*
|
||||
* The command will accept an option '--file' which is the architecture
|
||||
* file provided by users
|
||||
*******************************************************************/
|
||||
int constrain_pin_location(OpenfpgaContext& openfpga_context, const Command& cmd, const CommandContext& cmd_context) {
|
||||
|
||||
/* initialize arguments with sanity check*/
|
||||
std::vector<std::string> pin_constrain_args;
|
||||
pin_constrain_args.push_back("pin_c");
|
||||
|
||||
/* todo: create a factory to produce this in the future*/
|
||||
std::string pcf_option_name = "pcf";
|
||||
CommandOptionId opt_pcf_file = cmd.option(pcf_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_pcf_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_pcf_file).empty());
|
||||
std::string pcf_file_name = cmd_context.option_value(cmd, opt_pcf_file);
|
||||
pin_constrain_args.push_back(std::string("--") + pcf_option_name);
|
||||
pin_constrain_args.push_back(pcf_file_name.c_str());
|
||||
|
||||
std::string blif_option_name = "blif";
|
||||
CommandOptionId opt_blif_file = cmd.option(blif_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_blif_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_blif_file).empty());
|
||||
std::string blif_file_name = cmd_context.option_value(cmd, opt_blif_file);
|
||||
pin_constrain_args.push_back(std::string("--") + blif_option_name);
|
||||
pin_constrain_args.push_back(blif_file_name);
|
||||
|
||||
#if 0
|
||||
// --net is not supported by pin_c yet
|
||||
std::string net_option_name = "net";
|
||||
CommandOptionId opt_net_file = cmd.option(net_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_net_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_net_file).empty());
|
||||
std::string net_file_name = cmd_context.option_value(cmd, opt_net_file);
|
||||
pin_constrain_args.push_back(std::string("--") + net_option_name);
|
||||
pin_constrain_args.push_back(net_file_name);
|
||||
#endif
|
||||
|
||||
std::string pinmap_xml_option_name = "pinmap_xml";
|
||||
CommandOptionId opt_pinmap_xml_file = cmd.option(pinmap_xml_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_pinmap_xml_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_pinmap_xml_file).empty());
|
||||
std::string pinmap_xml_file_name = cmd_context.option_value(cmd, opt_pinmap_xml_file);
|
||||
pin_constrain_args.push_back(std::string("--xml"));
|
||||
pin_constrain_args.push_back(pinmap_xml_file_name);
|
||||
|
||||
std::string csv_file_option_name = "csv_file";
|
||||
CommandOptionId opt_csv_file = cmd.option(csv_file_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_csv_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_csv_file).empty());
|
||||
std::string csv_file_name = cmd_context.option_value(cmd, opt_csv_file);
|
||||
pin_constrain_args.push_back(std::string("--csv"));
|
||||
pin_constrain_args.push_back(csv_file_name);
|
||||
|
||||
std::string output_option_name = "output";
|
||||
CommandOptionId opt_output_file = cmd.option(output_option_name);
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_output_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_output_file).empty());
|
||||
std::string output_file_name = cmd_context.option_value(cmd, opt_output_file);
|
||||
pin_constrain_args.push_back(std::string("--") + output_option_name);
|
||||
pin_constrain_args.push_back(output_file_name);
|
||||
|
||||
if (pin_constrain_location(pin_constrain_args)) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -1,49 +0,0 @@
|
|||
/********************************************************************
|
||||
* Add commands to the OpenFPGA shell interface,
|
||||
* in purpose of setting up OpenFPGA core engine, including:
|
||||
* - read_openfpga_arch : read OpenFPGA architecture file
|
||||
*******************************************************************/
|
||||
#include "openfpga_constrain_pin_location_command.h"
|
||||
#include "openfpga_constrain_pin_location.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* - Add a command to Shell environment: constrain_pin_location
|
||||
* - Add associated options
|
||||
* - Add command dependency
|
||||
*******************************************************************/
|
||||
void add_openfpga_constrain_pin_location_command(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||
Command shell_cmd("constrain_pin_location");
|
||||
|
||||
/* Options are created by following the python script crafted by QL, since that has been used for a while:
|
||||
python3 create_ioplace.py --pcf “pcf_file” --blif “blif_file” --net “net_file” --pinmap_xml “pinmap_xml_file” --csv_file “csv_file” --output “outputplace_file”
|
||||
*/
|
||||
/* Add an option '--pcf'*/
|
||||
CommandOptionId opt_pcf_file = shell_cmd.add_option("pcf", true, "file path to the user pin constraint");
|
||||
shell_cmd.set_option_require_value(opt_pcf_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--blif'*/
|
||||
CommandOptionId opt_blif_file = shell_cmd.add_option("blif", true, "file path to the synthesized blif");
|
||||
shell_cmd.set_option_require_value(opt_blif_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--pinmap_xml'*/
|
||||
CommandOptionId opt_pinmap_file = shell_cmd.add_option("pinmap_xml", true, "file path to the pin location XML");
|
||||
shell_cmd.set_option_require_value(opt_pinmap_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--csv_file'*/
|
||||
CommandOptionId opt_csv_file = shell_cmd.add_option("csv_file", true, "file path to the pin map csv");
|
||||
shell_cmd.set_option_require_value(opt_csv_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--output'*/
|
||||
CommandOptionId opt_output_file = shell_cmd.add_option("output", true, "file path to the output");
|
||||
shell_cmd.set_option_require_value(opt_output_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add command 'constrain_pin_location' to the Shell */
|
||||
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "constrain pin location by generating a place file");
|
||||
shell.set_command_execute_function(shell_cmd_id, constrain_pin_location);
|
||||
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,90 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream database
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_time.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgashell library */
|
||||
#include "command_exit_codes.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Headers from pcf library */
|
||||
#include "pcf_reader.h"
|
||||
#include "blif_head_reader.h"
|
||||
#include "read_csv_io_pin_table.h"
|
||||
#include "read_xml_io_location_map.h"
|
||||
#include "io_net_place.h"
|
||||
#include "pcf2place.h"
|
||||
|
||||
#include "openfpga_pcf2place.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to convert a .pcf file to a .place file which
|
||||
* which VPR can force I/O placement
|
||||
*******************************************************************/
|
||||
int pcf2place_wrapper(const OpenfpgaContext& openfpga_context,
|
||||
const Command& cmd, const CommandContext& cmd_context) {
|
||||
|
||||
/* todo: create a factory to produce this in the future*/
|
||||
CommandOptionId opt_pcf = cmd.option("pcf");
|
||||
CommandOptionId opt_blif = cmd.option("blif");
|
||||
CommandOptionId opt_fpga_io_map = cmd.option("fpga_io_map");
|
||||
CommandOptionId opt_pin_table = cmd.option("pin_table");
|
||||
CommandOptionId opt_fpga_fix_pins = cmd.option("fpga_fix_pins");
|
||||
CommandOptionId opt_no_time_stamp = cmd.option("no_time_stamp");
|
||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||
|
||||
std::string pcf_fname = cmd_context.option_value(cmd, opt_pcf);
|
||||
std::string blif_fname = cmd_context.option_value(cmd, opt_blif);
|
||||
std::string fpga_io_map_fname = cmd_context.option_value(cmd, opt_fpga_io_map);
|
||||
std::string pin_table_fname = cmd_context.option_value(cmd, opt_pin_table);
|
||||
std::string fpga_fix_pins_fname = cmd_context.option_value(cmd, opt_fpga_fix_pins);
|
||||
|
||||
/* Parse the input files */
|
||||
openfpga::PcfData pcf_data;
|
||||
openfpga::read_pcf(pcf_fname.c_str(), pcf_data);
|
||||
VTR_LOG("Read the design constraints from a pcf file: %s.\n",
|
||||
pcf_fname.c_str());
|
||||
|
||||
blifparse::BlifHeadReader callback;
|
||||
blifparse::blif_parse_filename(blif_fname.c_str(), callback);
|
||||
VTR_LOG("Read the blif from a file: %s.\n",
|
||||
blif_fname.c_str());
|
||||
if (callback.had_error()) {
|
||||
VTR_LOG_ERROR("Read the blif ends with errors\n");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
IoLocationMap io_location_map = read_xml_io_location_map(fpga_io_map_fname.c_str());
|
||||
VTR_LOG("Read the I/O location map from an XML file: %s.\n",
|
||||
fpga_io_map_fname.c_str());
|
||||
|
||||
IoPinTable io_pin_table = read_csv_io_pin_table(pin_table_fname.c_str());
|
||||
VTR_LOG("Read the I/O pin table from a csv file: %s.\n",
|
||||
pin_table_fname.c_str());
|
||||
|
||||
/* Convert */
|
||||
IoNetPlace io_net_place;
|
||||
int status = pcf2place(pcf_data, callback.input_pins(), callback.output_pins(), io_pin_table, io_location_map, io_net_place);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Output */
|
||||
status = io_net_place.write_to_place_file(fpga_fix_pins_fname.c_str(),
|
||||
cmd_context.option_enable(cmd, opt_no_time_stamp),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef OPENFPGA_BITSTREAM_H
|
||||
#define OPENFPGA_BITSTREAM_H
|
||||
#ifndef OPENFPGA_PCF2PLACE_H
|
||||
#define OPENFPGA_PCF2PLACE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
|
@ -14,7 +14,10 @@
|
|||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
int constrain_pin_location(OpenfpgaContext& openfpga_context, const Command& cmd, const CommandContext& cmd_context);
|
||||
|
||||
int pcf2place_wrapper(const OpenfpgaContext& openfpga_context,
|
||||
const Command& cmd, const CommandContext& cmd_context);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -10,6 +10,7 @@
|
|||
#include "check_netlist_naming_conflict.h"
|
||||
#include "openfpga_build_fabric.h"
|
||||
#include "openfpga_write_gsb.h"
|
||||
#include "openfpga_pcf2place.h"
|
||||
#include "openfpga_setup_command.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
|
@ -421,6 +422,50 @@ ShellCommandId add_openfpga_write_fabric_io_info_command(openfpga::Shell<Openfpg
|
|||
return shell_cmd_id;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* - Add a command to Shell environment: pcf2place
|
||||
* - Add associated options
|
||||
* - Add command dependency
|
||||
*******************************************************************/
|
||||
static
|
||||
ShellCommandId add_openfpga_pcf2place_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||
const ShellCommandClassId& cmd_class_id) {
|
||||
Command shell_cmd("pcf2place");
|
||||
|
||||
/* Add an option '--pcf'*/
|
||||
CommandOptionId opt_pcf_file = shell_cmd.add_option("pcf", true, "file path to the user pin constraint");
|
||||
shell_cmd.set_option_require_value(opt_pcf_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--blif'*/
|
||||
CommandOptionId opt_blif_file = shell_cmd.add_option("blif", true, "file path to the synthesized netlist (.blif)");
|
||||
shell_cmd.set_option_require_value(opt_blif_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--fpga_io_map'*/
|
||||
CommandOptionId opt_fpga_io_map_file = shell_cmd.add_option("fpga_io_map", true, "file path to FPGA I/O location map (.xml)");
|
||||
shell_cmd.set_option_require_value(opt_fpga_io_map_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--pin_table'*/
|
||||
CommandOptionId opt_pin_table_file = shell_cmd.add_option("pin_table", true, "file path to the pin table (.csv)");
|
||||
shell_cmd.set_option_require_value(opt_pin_table_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--fpga_fix_pins'*/
|
||||
CommandOptionId opt_fpga_fix_pins_file = shell_cmd.add_option("fpga_fix_pins", true, "file path to the output fix-pin placement file (.place)");
|
||||
shell_cmd.set_option_require_value(opt_fpga_fix_pins_file, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--no_time_stamp' */
|
||||
shell_cmd.add_option("no_time_stamp", false, "Do not print time stamp in output files");
|
||||
|
||||
/* Add an option '--verbose' */
|
||||
shell_cmd.add_option("verbose", false, "Enable verbose output");
|
||||
|
||||
/* Add command to the Shell */
|
||||
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Convert user Pin Constraint File (.pcf) to an placement file");
|
||||
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||
shell.set_command_execute_function(shell_cmd_id, pcf2place_wrapper);
|
||||
|
||||
return shell_cmd_id;
|
||||
}
|
||||
|
||||
void add_openfpga_setup_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||
/* Get the unique id of 'vpr' command which is to be used in creating the dependency graph */
|
||||
const ShellCommandId& vpr_cmd_id = shell.command(std::string("vpr"));
|
||||
|
@ -428,6 +473,12 @@ void add_openfpga_setup_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
|||
/* Add a new class of commands */
|
||||
ShellCommandClassId openfpga_setup_cmd_class = shell.add_command_class("OpenFPGA setup");
|
||||
|
||||
/********************************
|
||||
* Command 'pcf2place'
|
||||
*/
|
||||
ShellCommandId pcf2place_cmd_id = add_openfpga_pcf2place_command(shell,
|
||||
openfpga_setup_cmd_class);
|
||||
|
||||
/********************************
|
||||
* Command 'read_openfpga_arch'
|
||||
*/
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "openfpga_bitstream_command.h"
|
||||
#include "openfpga_spice_command.h"
|
||||
#include "openfpga_sdc_command.h"
|
||||
#include "openfpga_constrain_pin_location_command.h"
|
||||
#include "basic_command.h"
|
||||
|
||||
#include "openfpga_title.h"
|
||||
|
@ -81,9 +80,6 @@ int main(int argc, char** argv) {
|
|||
/* Add openfpga sdc commands */
|
||||
openfpga::add_openfpga_sdc_commands(shell);
|
||||
|
||||
/* Add constrain pin location command */
|
||||
openfpga::add_openfpga_constrain_pin_location_command(shell);
|
||||
|
||||
/* Add basic commands: exit, help, etc.
|
||||
* Note:
|
||||
* This MUST be the last command group to be added!
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Run constrain_pin_location
|
||||
constrain_pin_location --pcf ${AND2_PIN_CONSTRAIN_FILE} --blif ${VPR_TESTBENCH_BLIF} --pinmap_xml ${AND2_PIN_MAP_XML_FILE} --csv_file ${AND2_PIN_MAP_CSV_FILE} --output ${OPENFPGA_VPR_FIX_PINS_FILE}
|
||||
# Convert .pcf to a .place file that VPR can accept
|
||||
pcf2place --pcf ${OPENFPGA_PCF} --blif ${VPR_TESTBENCH_BLIF} --pin_table ${OPENFPGA_PIN_TABLE} --fpga_io_map ${OPENFPGA_IO_MAP_FILE} --fpga_fix_pins ${OPENFPGA_VPR_FIX_PINS_FILE}
|
||||
|
||||
# Run VPR for the 'and' design
|
||||
#--write_rr_graph example_rr_graph.xml
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<io_coordinates>
|
||||
<io pad="gfpga_pad_IO_A2F[0]" x="1" y="0" z="0"/>
|
||||
<io pad="gfpga_pad_IO_F2A[0]" x="1" y="0" z="1"/>
|
||||
<io pad="gfpga_pad_IO_A2F[1]" x="1" y="0" z="2"/>
|
||||
<io pad="gfpga_pad_IO_F2A[1]" x="1" y="0" z="3"/>
|
||||
<io pad="gfpga_pad_IO_A2F[2]" x="1" y="0" z="4"/>
|
||||
<io pad="gfpga_pad_IO_F2A[2]" x="1" y="0" z="5"/>
|
||||
<io pad="gfpga_pad_IO_A2F[3]" x="1" y="0" z="6"/>
|
||||
<io pad="gfpga_pad_IO_F2A[3]" x="1" y="0" z="7"/>
|
||||
<io pad="gfpga_pad_IO_A2F[4]" x="2" y="0" z="0"/>
|
||||
<io pad="gfpga_pad_IO_F2A[4]" x="2" y="0" z="1"/>
|
||||
<io pad="gfpga_pad_IO_A2F[5]" x="2" y="0" z="2"/>
|
||||
<io pad="gfpga_pad_IO_F2A[5]" x="2" y="0" z="3"/>
|
||||
<io pad="gfpga_pad_IO_A2F[6]" x="2" y="0" z="4"/>
|
||||
<io pad="gfpga_pad_IO_F2A[6]" x="2" y="0" z="5"/>
|
||||
<io pad="gfpga_pad_IO_A2F[7]" x="2" y="0" z="6"/>
|
||||
<io pad="gfpga_pad_IO_F2A[7]" x="2" y="0" z="7"/>
|
||||
</io_coordinates>
|
|
@ -1,17 +1,17 @@
|
|||
orientation,row,col,pin_num_in_cell,port_name,mapped_pin,GPIO_type,Associated Clock,Clock Edge
|
||||
TOP,,,,gfpga_pad_IO_A2F[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[0],pad_fpga_io[0],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[4],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[8],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[31],pad_fpga_io[3],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[31],pad_fpga_io[3],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[32],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[40],pad_fpga_io[5],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[40],pad_fpga_io[5],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_A2F[64],pad_fpga_io[6],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_F2A[64],pad_fpga_io[6],,,
|
||||
LEFT,,,,gfpga_pad_IO_F2A[127],pad_fpga_io[7],,,
|
||||
LEFT,,,,gfpga_pad_IO_A2F[127],pad_fpga_io[7],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[2],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[2],pad_fpga_io[1],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[1],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[1],pad_fpga_io[2],,,
|
||||
TOP,,,,gfpga_pad_IO_A2F[3],pad_fpga_io[3],,,
|
||||
TOP,,,,gfpga_pad_IO_F2A[3],pad_fpga_io[3],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[5],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[5],pad_fpga_io[4],,,
|
||||
RIGHT,,,,gfpga_pad_IO_A2F[4],pad_fpga_io[5],,,
|
||||
RIGHT,,,,gfpga_pad_IO_F2A[4],pad_fpga_io[5],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_A2F[6],pad_fpga_io[6],,,
|
||||
BOTTOM,,,,gfpga_pad_IO_F2A[6],pad_fpga_io[6],,,
|
||||
LEFT,,,,gfpga_pad_IO_F2A[7],pad_fpga_io[7],,,
|
||||
LEFT,,,,gfpga_pad_IO_A2F[7],pad_fpga_io[7],,,
|
||||
|
|
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
XML file specification is primarily to define the mapping of the interface cell ports defined
|
||||
in vpr_arch xml, to the EFPGA IO interface port names. This mapping is required by OpenFPGA
|
||||
alongwith architecture definition file i.e. vpr_arch xml file. OpenFPGA will process this
|
||||
file and use this information for IO placement and then later on use this to map it with the
|
||||
user-defined pin-mapping file.
|
||||
-->
|
||||
<DEVICE name= "k4_N4_tileable_40nm" family="k4n4" width="6" height="6" z="8">
|
||||
<IO>
|
||||
<TOP_IO y="5">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[0:31]" startx="1" endx="4"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[0:31]" startx="1" endx="4"/>
|
||||
</TOP_IO>
|
||||
<RIGHT_IO x="5">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[32:63]" starty="4" endy="1"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[32:63]" starty="4" endy="1"/>
|
||||
</RIGHT_IO>
|
||||
<BOTTOM_IO y="0">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[64:95]" startx="4" endx="1"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[64:95]" startx="4" endx="1"/>
|
||||
</BOTTOM_IO>
|
||||
<LEFT_IO x="0">
|
||||
<CELL port_name="output" mapped_name="gfpga_pad_IO_F2A[96:127]" starty="1" endy="4"/>
|
||||
<CELL port_name="input" mapped_name="gfpga_pad_IO_A2F[96:127]" starty="1" endy="4"/>
|
||||
</LEFT_IO>
|
||||
</IO>
|
||||
</DEVICE>
|
|
@ -21,10 +21,10 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_
|
|||
openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml
|
||||
openfpga_vpr_device_layout=4x4
|
||||
openfpga_vpr_route_chan_width=20
|
||||
and2_pin_constrain_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/and2.pcf
|
||||
and2_pin_map_xml_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/pinmap_k4_N4_tileable_40nm.xml
|
||||
and2_pin_map_csv_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/pinmap_k4_N4_tileable_40nm.csv
|
||||
openfpga_vpr_fix_pins_file=./and2_constrain_pin.place
|
||||
openfpga_pcf=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/and2.pcf
|
||||
openfpga_io_map_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/fpga_io_location.xml
|
||||
openfpga_pin_table=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_tests/constrain_pin_location/config/pinmap_k4_N4_tileable_40nm.csv
|
||||
openfpga_vpr_fix_pins_file=and2_fix_pins.place
|
||||
|
||||
[ARCHITECTURES]
|
||||
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml
|
||||
|
|
Loading…
Reference in New Issue