From 671188dfa4578c7b07183e58c0b6e2a6dcee8535 Mon Sep 17 00:00:00 2001
From: tangxifan <tangxifan@gmail.com>
Date: Fri, 18 Feb 2022 23:05:03 -0800
Subject: [PATCH] [FPGA-Verilog] Now support big/little-endian in bus group

---
 libopenfpga/libbusgroup/src/bus_group.cpp            | 12 ++++++++++++
 libopenfpga/libbusgroup/src/bus_group.h              |  9 +++++++++
 .../libbusgroup/src/bus_group_xml_constants.h        |  1 +
 libopenfpga/libbusgroup/src/read_xml_bus_group.cpp   |  3 +++
 libopenfpga/libbusgroup/src/write_xml_bus_group.cpp  |  1 +
 .../fpga_verilog/verilog_preconfig_top_module.cpp    |  5 ++++-
 openfpga/src/fpga_verilog/verilog_writer_utils.cpp   | 10 ++++++++--
 openfpga/src/fpga_verilog/verilog_writer_utils.h     |  3 ++-
 8 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/libopenfpga/libbusgroup/src/bus_group.cpp b/libopenfpga/libbusgroup/src/bus_group.cpp
index 7aed69215..8bc5688ed 100644
--- a/libopenfpga/libbusgroup/src/bus_group.cpp
+++ b/libopenfpga/libbusgroup/src/bus_group.cpp
@@ -33,6 +33,11 @@ openfpga::BasicPort BusGroup::bus_port(const BusGroupId& bus_id) const {
   return bus_ports_[bus_id]; 
 }
 
+bool BusGroup::is_big_endian(const BusGroupId& bus_id) const {
+  VTR_ASSERT(valid_bus_id(bus_id));
+  return bus_big_endians_[bus_id]; 
+}
+
 std::vector<BusPinId> BusGroup::bus_pins(const BusGroupId& bus_id) const {
   VTR_ASSERT(valid_bus_id(bus_id));
   return bus_pin_ids_[bus_id]; 
@@ -89,6 +94,7 @@ bool BusGroup::empty() const {
 void BusGroup::reserve_buses(const size_t& num_buses) {
   bus_ids_.reserve(num_buses);
   bus_ports_.reserve(num_buses);
+  bus_big_endians_.reserve(num_buses);
   bus_pin_ids_.reserve(num_buses);
 }
 
@@ -105,6 +111,7 @@ BusGroupId BusGroup::create_bus(const openfpga::BasicPort& bus_port) {
   
   bus_ids_.push_back(bus_id);
   bus_ports_.push_back(bus_port);
+  bus_big_endians_.push_back(true);
   bus_pin_ids_.emplace_back();
 
   /* Register to fast look-up */
@@ -119,6 +126,11 @@ BusGroupId BusGroup::create_bus(const openfpga::BasicPort& bus_port) {
   return bus_id;
 }
 
+void BusGroup::set_bus_big_endian(const BusGroupId& bus_id, const bool& big_endian) {
+  VTR_ASSERT(valid_bus_id(bus_id));
+  bus_big_endians_[bus_id] = big_endian;
+}
+
 BusPinId BusGroup::create_pin(const BusGroupId& bus_id, const int& index) {
   /* Create a new id */
   BusPinId pin_id = BusPinId(pin_ids_.size());
diff --git a/libopenfpga/libbusgroup/src/bus_group.h b/libopenfpga/libbusgroup/src/bus_group.h
index da63fb210..85d80c579 100644
--- a/libopenfpga/libbusgroup/src/bus_group.h
+++ b/libopenfpga/libbusgroup/src/bus_group.h
@@ -50,6 +50,9 @@ class BusGroup {
     /** Get port information of a bus with a given id */
     BasicPort bus_port(const BusGroupId& bus_id) const;
 
+    /* Check if a bus follows big endian */
+    bool is_big_endian(const BusGroupId& bus_id) const;
+
     /* Get the pins under a specific bus */
     std::vector<BusPinId> bus_pins(const BusGroupId& bus_id) const;
 
@@ -81,6 +84,9 @@ class BusGroup {
     /* Add a bus to storage */
     BusGroupId create_bus(const openfpga::BasicPort& bus_port);
 
+    /* Set endianness for a bus; If not set, by default it assumes big-endian */
+    void set_bus_big_endian(const BusGroupId& bus_id, const bool& big_endian);
+
     /* Add a pin to a bus, with a given index in the bus, e.g., A[1] in A[0:2] */
     BusPinId create_pin(const BusGroupId& bus_id, const int& index);
 
@@ -101,6 +107,9 @@ class BusGroup {
     /* Port information of each bus */
     vtr::vector<BusGroupId, BasicPort> bus_ports_;
 
+    /* Endianness of each bus: big endian by default */
+    vtr::vector<BusGroupId, bool> bus_big_endians_;
+
     /* Indices of each pin under each bus */
     vtr::vector<BusGroupId, std::vector<BusPinId>> bus_pin_ids_;
 
diff --git a/libopenfpga/libbusgroup/src/bus_group_xml_constants.h b/libopenfpga/libbusgroup/src/bus_group_xml_constants.h
index 9f27ae978..62b552f06 100644
--- a/libopenfpga/libbusgroup/src/bus_group_xml_constants.h
+++ b/libopenfpga/libbusgroup/src/bus_group_xml_constants.h
@@ -6,6 +6,7 @@
 constexpr char* XML_BUS_GROUP_NODE_NAME = "bus_group";
 constexpr char* XML_BUS_NODE_NAME = "bus";
 constexpr char* XML_BUS_PORT_ATTRIBUTE_NAME = "name";
+constexpr char* XML_BUS_BIG_ENDIAN_ATTRIBUTE_NAME = "big_endian";
 constexpr char* XML_PIN_NODE_NAME = "pin";
 constexpr char* XML_PIN_INDEX_ATTRIBUTE_NAME = "id";
 constexpr char* XML_PIN_NAME_ATTRIBUTE_NAME = "name";
diff --git a/libopenfpga/libbusgroup/src/read_xml_bus_group.cpp b/libopenfpga/libbusgroup/src/read_xml_bus_group.cpp
index 020aa224b..768ff455a 100644
--- a/libopenfpga/libbusgroup/src/read_xml_bus_group.cpp
+++ b/libopenfpga/libbusgroup/src/read_xml_bus_group.cpp
@@ -76,6 +76,9 @@ void read_xml_bus(pugi::xml_node& xml_bus,
                    "Bus port is invalid, check LSB and MSB!\n");
   }
 
+  /* Find big endian */
+  bus_group.set_bus_big_endian(bus_id, get_attribute(xml_bus, XML_BUS_BIG_ENDIAN_ATTRIBUTE_NAME, loc_data, pugiutil::OPTIONAL).as_bool(true));
+
   for (pugi::xml_node xml_pin : xml_bus.children()) {
     /* Error out if the XML child has an invalid name! */
     if (xml_pin.name() != std::string(XML_PIN_NODE_NAME)) {
diff --git a/libopenfpga/libbusgroup/src/write_xml_bus_group.cpp b/libopenfpga/libbusgroup/src/write_xml_bus_group.cpp
index 9d9fdb005..be26b44f5 100644
--- a/libopenfpga/libbusgroup/src/write_xml_bus_group.cpp
+++ b/libopenfpga/libbusgroup/src/write_xml_bus_group.cpp
@@ -46,6 +46,7 @@ int write_xml_bus(std::fstream& fp,
   }
 
   write_xml_attribute(fp, XML_BUS_PORT_ATTRIBUTE_NAME, generate_xml_port_name(bus_group.bus_port(bus_id)).c_str());
+  write_xml_attribute(fp, XML_BUS_BIG_ENDIAN_ATTRIBUTE_NAME, bus_group.is_big_endian(bus_id));
   fp << ">" << "\n";
 
   /* Output all the pins under this bus */
diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp
index 26ce11c2f..fba957963 100644
--- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp
+++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp
@@ -56,6 +56,7 @@ void print_verilog_preconfig_top_module_ports(std::fstream &fp,
   /* Ports to be added, this is to avoid any bus port */
   std::vector<BasicPort> port_list;
   std::vector<AtomBlockType> port_types;
+  std::vector<bool> port_big_endian;
 
   /* Print all the I/Os of the circuit implementation to be tested*/
   for (const AtomBlockId &atom_blk : atom_ctx.nlist.blocks()) {
@@ -96,6 +97,7 @@ void print_verilog_preconfig_top_module_ports(std::fstream &fp,
       if (port_list.end() == std::find(port_list.begin(), port_list.end(), bus_group.bus_port(bus_id))) {
         port_list.push_back(bus_group.bus_port(bus_id));
         port_types.push_back(atom_ctx.nlist.block_type(atom_blk));
+        port_big_endian.push_back(bus_group.is_big_endian(bus_id));
       }
       continue;
     }
@@ -104,6 +106,7 @@ void print_verilog_preconfig_top_module_ports(std::fstream &fp,
     BasicPort module_port(std::string(block_name), 1);
     port_list.push_back(module_port);
     port_types.push_back(atom_ctx.nlist.block_type(atom_blk));
+    port_big_endian.push_back(true);
   }
 
   /* After collecting all the ports, now print the port mapping */
@@ -115,7 +118,7 @@ void print_verilog_preconfig_top_module_ports(std::fstream &fp,
       fp << "," << std::endl;
     }
 
-    fp << generate_verilog_port(port_type2type_map[port_type], module_port);
+    fp << generate_verilog_port(port_type2type_map[port_type], module_port, true, port_big_endian[iport]);
 
     /* Update port counter */
     port_counter++;
diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp
index ea34f35b7..d537b89f3 100644
--- a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp
+++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp
@@ -466,13 +466,19 @@ void print_verilog_module_end(std::fstream& fp,
  ***********************************************/
 std::string generate_verilog_port(const enum e_dump_verilog_port_type& verilog_port_type,
                                   const BasicPort& port_info,
-                                  const bool& must_print_port_size) {  
+                                  const bool& must_print_port_size,
+								  const bool& big_endian) {
   std::string verilog_line;
 
   /* Ensure the port type is valid */
   VTR_ASSERT(verilog_port_type < NUM_VERILOG_PORT_TYPES);
 
-  std::string size_str = "[" + std::to_string(port_info.get_lsb()) + ":" + std::to_string(port_info.get_msb()) + "]";
+  std::string size_str;
+  if (big_endian) {
+    size_str = "[" + std::to_string(port_info.get_lsb()) + ":" + std::to_string(port_info.get_msb()) + "]";
+  } else {
+    size_str = "[" + std::to_string(port_info.get_msb()) + ":" + std::to_string(port_info.get_lsb()) + "]";
+  }
 
   /* Only connection require a format of <port_name>[<lsb>:<msb>]
    * others require a format of <port_type> [<lsb>:<msb>] <port_name> 
diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.h b/openfpga/src/fpga_verilog/verilog_writer_utils.h
index af344e146..180df8e45 100644
--- a/openfpga/src/fpga_verilog/verilog_writer_utils.h
+++ b/openfpga/src/fpga_verilog/verilog_writer_utils.h
@@ -87,7 +87,8 @@ void print_verilog_module_end(std::fstream& fp,
 
 std::string generate_verilog_port(const enum e_dump_verilog_port_type& dump_port_type,
                                   const BasicPort& port_info,
-                                  const bool& must_print_port_size = true);
+                                  const bool& must_print_port_size = true,
+                                  const bool& big_endian = true);
 
 bool two_verilog_ports_mergeable(const BasicPort& portA,
                                  const BasicPort& portB);