├── third_party ├── Makefile.am └── gtest │ └── Makefile.am ├── autogen.sh ├── README.md ├── examples ├── las.dot ├── srpt.dot ├── strict_priorities.dot ├── wfq.dot ├── tbf.dot ├── ets.dot └── hpfq.dot ├── tests ├── main.cc ├── Makefile.am └── pifo_tests.cc ├── Makefile.am ├── convenience_typedefs.h ├── .travis.yml ├── .gitignore ├── assert_exception.h ├── priority_queue.h ├── optional.h ├── configure.ac ├── calendar_queue.h ├── pifo_machine.cc ├── pifo.h ├── pifo_pipeline.h └── pifo_pipeline_stage.h /third_party/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = gtest 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec autoreconf -fi 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | C++ reference implementation of a pipeline of Push-In First-Out Queues 2 | 3 | Build using: 4 | 5 | 1. ./autogen.sh 6 | 2. ./configure 7 | 3. make 8 | 4. make check 9 | -------------------------------------------------------------------------------- /examples/las.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | pkt.prio = pkt.attained_service 6 | " 7 | fillcolor=white]; 8 | } 9 | -------------------------------------------------------------------------------- /examples/srpt.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | pkt.prio = pkt.rem_flow_size 6 | " 7 | fillcolor=white]; 8 | } 9 | -------------------------------------------------------------------------------- /examples/strict_priorities.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | pkt.prio = pkt.tos 6 | " 7 | fillcolor=white]; 8 | } 9 | -------------------------------------------------------------------------------- /tests/main.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | ::testing::InitGoogleTest(&argc, argv); 11 | return RUN_ALL_TESTS(); 12 | } 13 | -------------------------------------------------------------------------------- /third_party/gtest/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(srcdir)/.. 2 | 3 | check_LTLIBRARIES = libgtest.la 4 | 5 | common_source = \ 6 | gtest-all.cpp 7 | 8 | common_include = \ 9 | gtest.h 10 | 11 | libgtest_la_SOURCES = $(common_source) $(common_include) 12 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) 2 | bin_PROGRAMS = pifo_machine 3 | common_source = 4 | pifo_machine_SOURCES = $(common_source) pifo_machine.cc pifo.h priority_queue.h element_gate.h calendar_queue.h optional.h assert_exception.h pifo_pipeline.h pifo_pipeline_stage.h convenience_typedefs.h 5 | 6 | SUBDIRS = third_party . tests 7 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) -isystem $(srcdir)/../third_party/ -pthread 2 | LDADD = -lm $(srcdir)/../third_party/gtest/libgtest.la 3 | 4 | # Define unit tests 5 | gtest_main_source = main.cc 6 | TESTS = pifo_tests 7 | check_PROGRAMS = $(TESTS) 8 | 9 | # OMNeT++ tests 10 | pifo_tests_SOURCES = $(gtest_main_source) pifo_tests.cc 11 | -------------------------------------------------------------------------------- /examples/wfq.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | if pkt.flow_id in finish_times: 6 | p.start = max(virtual_time, finish_times[pkt.flow_id]); 7 | else: 8 | p.start = virtual_time 9 | finish_times[pkt.flow_id] = p.start + p.len / p.weight; 10 | pkt.prio = p.start 11 | " 12 | fillcolor=white]; 13 | } 14 | -------------------------------------------------------------------------------- /convenience_typedefs.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVENIENCE_TYPEDEFS_H_ 2 | #define CONVENIENCE_TYPEDEFS_H_ 3 | 4 | /// Use Banzai's FieldContainer to represent a packet in the PIFO pipeline 5 | typedef FieldContainer PIFOPacket; 6 | 7 | /// Set priority_t to a 32-bit uint for now 8 | /// Better than creating a template for it. 9 | typedef uint32_t priority_t; 10 | 11 | #endif // CONVENIENCE_TYPEDEFS_H_ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | before_install: 4 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq g++-4.9 7 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 99 8 | 9 | script: "git clone git@github.com:anirudhSK/banzai.git; cd banzai; ./autogen.sh; ./configure CXX='g++-4.9'; sudo make install; ./autogen.sh && ./configure && make && make check" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.cache/ 6 | compile 7 | config.guess 8 | config.h 9 | config.h.in 10 | config.log 11 | config.status 12 | config.sub 13 | configure 14 | depcomp 15 | install-sh 16 | libtool 17 | ltmain.sh 18 | missing 19 | stamp-h1 20 | .libs/ 21 | *.lo 22 | *.o 23 | pifo_machine 24 | 25 | # Test-related 26 | test-driver 27 | tests/pifo_tests 28 | tests/pifo_tests.log 29 | tests/pifo_tests.trs 30 | tests/test-suite.log 31 | third_party/gtest/libgtest.la 32 | -------------------------------------------------------------------------------- /examples/tbf.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | tokens[pkt.flow_id] = min(tokens[pkt.flow_id] + r * (pkt.arrival_time - last_arrival[pkt.flow_id]), B) 6 | if (pkt.length <= tokens[pkt.flow_id]): 7 | pkt.send_time = pkt.arrival_time 8 | else : 9 | pkt.send_time = pkt.arrival_time + (pkt.length - pkt.tokens) / r 10 | pkt.tokens = pkt.tokens - pkt.length 11 | last_arrival[pkt.flow_id] = pkt.arrival_time 12 | " 13 | fillcolor=green]; 14 | } 15 | -------------------------------------------------------------------------------- /tests/pifo_tests.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "gtest/gtest.h" 4 | 5 | template 6 | bool check_for_correctness(std::priority_queue & q) { 7 | auto last_element = q.top(); 8 | while(! q.empty()) { 9 | // Every top element must be <= the previous one 10 | EXPECT_LE(q.top(), last_element); 11 | q.pop(); 12 | } 13 | assert(q.empty()); 14 | return true; 15 | } 16 | 17 | TEST(PifoTests, Pifo) { 18 | // Push packets into priority queue 19 | std::priority_queue q; 20 | for(const auto & n : {1,8,5,6,3,4,0,9,3,2}) 21 | q.push(n); 22 | 23 | check_for_correctness(q); 24 | 25 | for(const auto & n : {1,1,50,60,-3,4,-1,9,-30,2}) 26 | q.push(n); 27 | 28 | check_for_correctness(q); 29 | } 30 | -------------------------------------------------------------------------------- /examples/ets.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | if pkt.class_id in finish_times: 6 | p.start = max(virtual_time, finish_times[pkt.class_id]); 7 | else: 8 | p.start = virtual_time 9 | finish_times[pkt.class_id] = p.start + p.len / p.weight; 10 | pkt.prio = p.start 11 | " 12 | fillcolor=white]; 13 | 14 | left [label = 15 | "pifo_pfabric: 16 | pkt.prio = p.rem_flow_size 17 | " 18 | fillcolor = white]; 19 | 20 | right [label = 21 | "pifo_tbf: 22 | tokens[pkt.flow_id] = min(tokens[pkt.flow_id] + r * (pkt.arrival_time - last_arrival[pkt.flow_id]), B) 23 | if (pkt.length <= tokens[pkt.flow_id]): 24 | pkt.send_time = pkt.arrival_time 25 | else : 26 | pkt.send_time = pkt.arrival_time + (pkt.length - pkt.tokens) / r 27 | pkt.tokens = pkt.tokens - pkt.length 28 | last_arrival[pkt.flow_id] = pkt.arrival_time 29 | " 30 | fillcolor = green]; 31 | 32 | root -> left; 33 | right -> root; 34 | } 35 | -------------------------------------------------------------------------------- /examples/hpfq.dot: -------------------------------------------------------------------------------- 1 | digraph sched_graph { 2 | node [shape = box style="rounded,filled"]; 3 | root [label = 4 | "pifo_root: 5 | if pkt.class_id in finish_times: 6 | p.start = max(virtual_time, finish_times[pkt.class_id]); 7 | else: 8 | p.start = virtual_time 9 | finish_times[pkt.class_id] = p.start + p.len / p.weight; 10 | pkt.prio = p.start 11 | " 12 | fillcolor=white]; 13 | 14 | left [label = 15 | "pifo_left: 16 | if pkt.flow_id in finish_times: 17 | p.start = max(virtual_time, finish_times[pkt.flow_id]); 18 | else: 19 | p.start = virtual_time 20 | finish_times[pkt.flow_id] = p.start + p.len / p.weight; 21 | pkt.prio = p.start 22 | " 23 | fillcolor = white]; 24 | 25 | right [label = 26 | "pifo_right: 27 | if pkt.flow_id in finish_times: 28 | p.start = max(virtual_time, finish_times[pkt.flow_id]); 29 | else: 30 | p.start = virtual_time 31 | finish_times[pkt.flow_id] = p.start + p.len / p.weight; 32 | pkt.prio = p.start 33 | " 34 | fillcolor = white]; 35 | 36 | root -> left; 37 | root -> right; 38 | } 39 | -------------------------------------------------------------------------------- /assert_exception.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSERT_EXCEPTION_H_ 2 | #define ASSERT_EXCEPTION_H_ 3 | 4 | #include 5 | #include 6 | 7 | class AssertException : public std::exception { 8 | public: 9 | AssertException(const std::string & t_error_message) 10 | : std::exception(), 11 | assert_message_(t_error_message) {} 12 | 13 | const char * what(void) const noexcept override { return assert_message_.c_str(); } 14 | private: 15 | std::string assert_message_; 16 | }; 17 | 18 | # define assert_exception(expr) if (not (expr)) { throw AssertException(\ 19 | " assert failed with expr " + std::string(__STRING(expr)) + "\n" + \ 20 | " in function " + std::string(__PRETTY_FUNCTION__) + "\n" + \ 21 | " in file " + std::string(__FILE__) + "\n" + \ 22 | " at line number " + std::to_string(__LINE__)); } 23 | 24 | 25 | 26 | #endif // ASSERT_EXCEPTION_H_ 27 | -------------------------------------------------------------------------------- /priority_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef PRIORITY_QUEUE_H_ 2 | #define PRIORITY_QUEUE_H_ 3 | 4 | #include "pifo.h" 5 | #include "optional.h" 6 | 7 | /// The priority queue abstraction to determine 8 | /// the order of transmission of packets 9 | template 10 | class PriorityQueue { 11 | public: 12 | /// Enqueue method 13 | void enq(const ElementType & element, const PriorityType & prio, 14 | const uint32_t & tick __attribute__((unused))) { 15 | pifo_.push(element, prio); 16 | } 17 | 18 | /// Dequeue method 19 | Optional deq(const uint32_t & tick __attribute__((unused))) { 20 | return pifo_.pop(); 21 | } 22 | 23 | /// Print method / stream insertion operator 24 | friend std::ostream & operator<<(std::ostream & out, 25 | const PriorityQueue & priority_queue) { 26 | out << priority_queue.pifo_; 27 | return out; 28 | } 29 | 30 | private: 31 | /// Underlying PIFO 32 | PIFO pifo_ = {}; 33 | }; 34 | 35 | #endif // PRIORITY_QUEUE_H_ 36 | -------------------------------------------------------------------------------- /optional.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTIONAL_H_ 2 | #define OPTIONAL_H_ 3 | 4 | #include 5 | #include "assert_exception.h" 6 | 7 | /// Simplistic implementation of Optional types 8 | /// Boost's optional class is far too complicated 9 | /// for my liking and includes too many implicit 10 | /// type casts. 11 | template 12 | class Optional { 13 | public: 14 | /// Default constructor for Optional 15 | Optional() : initialized_(false), value_(T()) {} 16 | 17 | /// Constructor from type T 18 | Optional(const T & t_value) : initialized_(true), value_(t_value) {} 19 | 20 | /// Getter for value 21 | T get() const { assert_exception(initialized_); return value_; } 22 | 23 | /// Setter for value 24 | void set(T t_value) { value_ = t_value; initialized_ = true; } 25 | 26 | /// Is it initalized 27 | bool initialized() const { return initialized_; } 28 | 29 | /// Stream insertion operator 30 | friend std::ostream & operator<<(std::ostream & out, const Optional & optional) { 31 | out << "Optional initialized?: " << optional.initialized_ << " value " << optional.value_; 32 | return out; 33 | } 34 | 35 | private: 36 | bool initialized_ = false; 37 | T value_ = {}; 38 | }; 39 | 40 | #endif // OPTIONAL_H_ 41 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.61]) 2 | AC_INIT([pifo_reference], [0.1], [anirudh@csail.mit.edu]) 3 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 4 | AC_CONFIG_SRCDIR([pifo_machine.cc]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | LT_INIT 7 | 8 | # Checks for programs. 9 | AC_PROG_CXX 10 | 11 | # Add picky CXXFLAGS 12 | CPPFLAGS="-std=c++14 -pthread" 13 | PICKY_CXXFLAGS="-pedantic -Wconversion -Wsign-conversion -Wall -Wextra -Weffc++ -Werror -fno-default-inline" 14 | AC_SUBST([PICKY_CXXFLAGS]) 15 | 16 | # Checks for header files. 17 | AC_LANG_PUSH(C++) 18 | 19 | AC_CHECK_HEADERS([algorithm array cassert cmath queue map \ 20 | cstdio string sys/stat.h sys/types.h ctime tuple unistd.h unordered_map \ 21 | utility vector], [], [AC_MSG_ERROR([Missing header file])]) 22 | 23 | # Check for pisa headers 24 | AC_CHECK_HEADERS([pipeline.h stage.h field_container.h atom.h packet.h packet_latches.h], 25 | [], [AC_MSG_ERROR([Missing header files for pisa target machine])]) 26 | 27 | 28 | 29 | # Checks for typedefs, structures, and compiler characteristics. 30 | AC_TYPE_SIZE_T 31 | AC_TYPE_UINT64_T 32 | AC_LANG_POP(C++) 33 | 34 | AC_CONFIG_FILES([Makefile 35 | third_party/Makefile 36 | third_party/gtest/Makefile 37 | tests/Makefile 38 | ]) 39 | AC_OUTPUT 40 | -------------------------------------------------------------------------------- /calendar_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef CALENDAR_QUEUE_H_ 2 | #define CALENDAR_QUEUE_H_ 3 | 4 | #include 5 | 6 | #include "pifo.h" 7 | 8 | /// Calendar queue abstraction to determine 9 | /// absolute time of transmission of packets 10 | template 11 | class CalendarQueue { 12 | public: 13 | /// Enqueue method 14 | void enq(const ElementType & element, const PriorityType & prio, 15 | const uint32_t & tick) { 16 | // Don't push in a packet that was due in the past. 17 | assert_exception(prio >= tick); 18 | pifo_.push(element, prio); 19 | } 20 | 21 | /// Dequeue method 22 | Optional deq(const uint32_t & tick) { 23 | // Get top of pifo 24 | auto top_prio = pifo_.top_prio(); 25 | 26 | if (top_prio.initialized() and top_prio.get() <= tick) { 27 | // If top element's tick is less than current time, 28 | // assert that the top element and the current time match up 29 | assert_exception(top_prio.get() == tick); 30 | return pifo_.pop(); 31 | } else { 32 | // Otherwise, return nothing 33 | std::cout << "Returning nothing \n"; 34 | return Optional(); 35 | } 36 | } 37 | 38 | /// Print method / stream insertion operator 39 | friend std::ostream & operator<<(std::ostream & out, 40 | const CalendarQueue & calendar_queue) { 41 | out << calendar_queue.pifo_; 42 | return out; 43 | } 44 | 45 | private: 46 | /// Underlying PIFO 47 | PIFO pifo_ = {}; 48 | }; 49 | 50 | #endif // CALENDAR_QUEUE_H_ 51 | -------------------------------------------------------------------------------- /pifo_machine.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pifo_pipeline_stage.h" 5 | #include "pifo_pipeline.h" 6 | 7 | int main() { 8 | try { 9 | // Random number generation 10 | std::random_device rd; 11 | std::mt19937 gen(rd()); 12 | std::uniform_int_distribution ele_dis(1, 5); 13 | 14 | // Traffic generation 15 | PIFOPacket test_packet; 16 | 17 | // Single PIFO pipeline stage consisting of 18 | // 1 priority and queue 19 | // (every PIFO pipeline stage has 1 calendar queue) 20 | PIFOPipeline strict_prio_pipeline({PIFOPipelineStage(1, 21 | "fid", 22 | {{1, {Operation::TRANSMIT, {}}}, 23 | {2, {Operation::TRANSMIT, {}}}, 24 | {3, {Operation::TRANSMIT, {}}}, 25 | {4, {Operation::TRANSMIT, {}}}, 26 | {5, {Operation::TRANSMIT, {}}}, 27 | }, 28 | [] (const auto & x) { return x("fid"); })}); 29 | 30 | PIFOPipeline strict_wfq_pipeline({PIFOPipelineStage(1, 31 | "fid", 32 | {{1, {Operation::TRANSMIT, {}}}, 33 | {2, {Operation::TRANSMIT, {}}}, 34 | {3, {Operation::TRANSMIT, {}}}, 35 | {4, {Operation::TRANSMIT, {}}}, 36 | {5, {Operation::TRANSMIT, {}}}, 37 | }, 38 | [] (const auto & x) { 39 | static std::map last_fin_time = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}}; 40 | auto ret = last_fin_time.at(static_cast(x("fid"))); 41 | last_fin_time.at(static_cast(x("fid"))) += 1; 42 | return ret; })}); 43 | 44 | 45 | // Execute simulation 46 | for (uint32_t i = 0; i < 10000; i++) { 47 | test_packet("fid") = ele_dis(gen); 48 | test_packet("ptr") = 666; 49 | strict_wfq_pipeline.enq(0, QueueType::PRIORITY_QUEUE, 0, test_packet, i); 50 | } 51 | 52 | std::cout << "Finished enqs\n"; 53 | 54 | for (uint32_t i = 10000; i < 20000; i++) { 55 | auto result = strict_wfq_pipeline.deq(0, QueueType::PRIORITY_QUEUE, 0, i); 56 | std::cout << "Deq result is \"" << result << "\"" << std::endl; 57 | } 58 | 59 | assert_exception(strict_wfq_pipeline.deq(0, QueueType::PRIORITY_QUEUE, 0, 20000).initialized() == false); 60 | } catch (const std::exception & e) { 61 | std::cerr << "Caught exception in main " << std::endl << e.what() << std::endl; 62 | return EXIT_FAILURE; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pifo.h: -------------------------------------------------------------------------------- 1 | #ifndef PIFO_H_ 2 | #define PIFO_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "optional.h" 9 | 10 | /// Helper class for an element that can be "pushed" 11 | /// into a PIFO i.e. a class that pairs an ElementType element 12 | /// with a PriorityType priority for that element in the PIFO 13 | template 14 | struct PushableElement { 15 | public: 16 | /// Constructor for PushableElement 17 | PushableElement(const ElementType & t_element, const PriorityType & t_prio) 18 | : element_(t_element), 19 | prio_(t_prio) {} 20 | 21 | /// Print method, overload stream insertion operator 22 | friend std::ostream & operator<<(std::ostream & out, const PushableElement & push_element) { 23 | out << "(ele: " << push_element.element_ << ", prio: " << push_element.prio_ << ")"; 24 | return out; 25 | } 26 | 27 | /// Override the less-than operator for std::priority_queue to work 28 | /// N.B. higher priorities have lower absolute values, which is why the comparison is inverted. 29 | /// Put differently, we have a min-heap, not a max heap 30 | bool operator<(const PushableElement & other) const { return prio_ >= other.prio_; } 31 | 32 | /// Element itself 33 | ElementType element_ = ElementType(); 34 | 35 | /// Element's priority in PIFO 36 | PriorityType prio_ = PriorityType(); 37 | }; 38 | 39 | template 40 | class PIFO { 41 | static_assert(std::is_integral::value, "PriorityType must be an integral type for PIFO"); 42 | public: 43 | /// Default constructor 44 | PIFO() : queue_(std::priority_queue>()) {} 45 | 46 | /// Push element of ElementType into PIFO using prio of PriorityType as its priority 47 | void push(const ElementType & element, const PriorityType & prio) { 48 | PushableElement element_with_prio(element, prio); 49 | queue_.push(element_with_prio); 50 | } 51 | 52 | /// Pop and return element from the top of the PIFO 53 | Optional pop(void) { 54 | if (not queue_.empty()) { 55 | auto top_element = queue_.top(); 56 | queue_.pop(); 57 | return Optional(top_element.element_); 58 | } else { 59 | return Optional(); 60 | } 61 | } 62 | 63 | /// Get the top-most priority from the PIFO, but don't pop it 64 | Optional top_prio(void) const { 65 | if (not queue_.empty()) { 66 | return Optional(queue_.top().prio_); 67 | } else { 68 | return Optional(); 69 | } 70 | } 71 | 72 | /// print queue contents 73 | friend std::ostream & operator<<(std::ostream & out, const PIFO & pifo) { 74 | // Copy priority_queue and then iterate over it 75 | // The copy is required because there is no way to iterate over a std::priority_queue 76 | // without dequeueing its elements in the process 77 | auto shadow_copy = pifo.queue_; 78 | out << "HEAD "; 79 | while (not shadow_copy.empty()) { 80 | out << shadow_copy.top(); 81 | shadow_copy.pop(); 82 | } 83 | out << " TAIL"; 84 | out << std::endl; 85 | return out; 86 | } 87 | 88 | private: 89 | /// Underlying priority_queue of PushableElement 90 | std::priority_queue> queue_; 91 | }; 92 | 93 | #endif // PIFO_H_ 94 | -------------------------------------------------------------------------------- /pifo_pipeline.h: -------------------------------------------------------------------------------- 1 | #ifndef PIFO_PIPELINE_H_ 2 | #define PIFO_PIPELINE_H_ 3 | 4 | #include "pifo_pipeline_stage.h" 5 | 6 | /// A pipeline of PIFOStages 7 | /// Used (for instance) for hierarchical scheduling 8 | class PIFOPipeline { 9 | public: 10 | /// Constructor for the PIFOPipeline from an std::initializer_list 11 | PIFOPipeline(const std::initializer_list & t_pipeline_stages) 12 | : PIFOPipeline(std::vector(t_pipeline_stages)) {} 13 | 14 | /// Constructor for the PIFOPipeline from vector 15 | PIFOPipeline(const std::vector & t_stages) 16 | : stages_(t_stages) {} 17 | 18 | /// Enqueue into the pipeline at every tick 19 | /// Returns nothing because the packet is just assumed to be pushed in 20 | /// If we need to enqueue into multiple prio / cal qs, we need to call enq 21 | /// multiple times. 22 | void enq(const uint32_t & stage_id, 23 | const QueueType & q_type, 24 | const uint32_t & queue_id, 25 | const PIFOPacket & packet, 26 | const uint32_t & tick) { 27 | stages_.at(stage_id).enq(q_type, queue_id, packet, tick); 28 | } 29 | 30 | /// Dequeues from the pipeline at every tick 31 | /// Dequeue recursively until we either find a packet 32 | /// or we push an output from a calendar queue into the next stage 33 | Optional deq(const uint32_t & stage_id, 34 | const QueueType & q_type, 35 | const uint32_t & queue_id, 36 | const uint32_t & tick) { 37 | // Start off with a dequeue operation to the specified stage_id 38 | NextHop next_hop = {Operation::DEQ, {{stage_id, q_type, queue_id}}}; 39 | 40 | // Keep dequeuing until the next operation is either 41 | // an Operation::ENQ (push from prio q.) or 42 | // an Operation::TRANSMIT (reached a packet, transmit it) 43 | Optional ret; 44 | while (next_hop.op == Operation::DEQ) { 45 | // Make sure there is only one pifo argument if the op is a DEQ 46 | assert_exception(next_hop.pifo_arguments.size() == 1); 47 | 48 | // Get single pifo argument 49 | const auto pifo_args = next_hop.pifo_arguments.front(); 50 | ret = stages_.at(pifo_args.stage_id).deq(pifo_args.q_type, pifo_args.queue_id, tick); 51 | // Check that ret is initialized. 52 | if (ret.initialized()) { 53 | next_hop = stages_.at(pifo_args.stage_id).find_next_hop(ret.get()); 54 | } else { 55 | return ret; 56 | } 57 | } 58 | 59 | // Handle loop termination appropriately 60 | assert_exception(ret.initialized()); 61 | if (next_hop.op == Operation::ENQ) { 62 | // This only happens if a calendar queue pushes into the next stage 63 | for (uint32_t i = 0; i < next_hop.pifo_arguments.size(); i++) { 64 | const auto pifo_args = next_hop.pifo_arguments.at(i); 65 | stages_.at(pifo_args.stage_id).enq(pifo_args.q_type, 66 | pifo_args.queue_id, 67 | ret.get(), 68 | tick); 69 | } 70 | return Optional(); 71 | } else { 72 | // This is when we have finally reached a packet, which needs to 73 | // be pulled out of the data buffer and transmitted on the link. 74 | assert_exception(next_hop.op == Operation::TRANSMIT); 75 | return ret; 76 | } 77 | } 78 | 79 | /// Overload stream insertion operator 80 | friend std::ostream & operator<<(std::ostream & out, const PIFOPipeline & t_pipeline) { 81 | out << "Contents of PIFOPipeline " << std::endl; 82 | for (uint32_t i = 0; i < t_pipeline.stages_.size(); i++) { 83 | out << "Stage " << i << " " << t_pipeline.stages_.at(i); 84 | } 85 | return out; 86 | } 87 | 88 | private: 89 | /// Bank of pipeline stages 90 | std::vector stages_ = {}; 91 | }; 92 | 93 | #endif // PIFO_PIPELINE_H_ 94 | -------------------------------------------------------------------------------- /pifo_pipeline_stage.h: -------------------------------------------------------------------------------- 1 | #ifndef PIFO_PIPELINE_STAGE_H_ 2 | #define PIFO_PIPELINE_STAGE_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // Banzai headers 10 | #include "field_container.h" 11 | 12 | #include "convenience_typedefs.h" 13 | #include "priority_queue.h" 14 | #include "calendar_queue.h" 15 | 16 | /// enum to distinguish between priority and calendar queues 17 | /// This lets us keep each in its own distinct namespace without 18 | /// having to create a polymorphic queue class 19 | enum class QueueType { 20 | PRIORITY_QUEUE, 21 | CALENDAR_QUEUE 22 | }; 23 | 24 | /// Opcode class, specify whether we are doing 25 | /// an enqueue / dequeue / transmit. 26 | enum class Operation { 27 | ENQ, 28 | DEQ, 29 | TRANSMIT 30 | }; 31 | 32 | /// Arguments to enqueue or dequeue 33 | /// from a particular stage 34 | /// in the PIFO pipeline 35 | struct PIFOArguments { 36 | /// Which stage to enqueue or dequeue from 37 | uint32_t stage_id; 38 | 39 | /// Queue type (calendar / prio. q) to enqueue or dequeue from 40 | QueueType q_type; 41 | 42 | /// Queue id to enqueue or dequeue from 43 | uint32_t queue_id; 44 | }; 45 | 46 | /// Next hop information, what operation, which stage, 47 | /// which queue type and which queue id should we be 48 | /// sending this PIFOPacket to? 49 | struct NextHop { 50 | /// Operation: ENQ/DEQ/TRANSMIT 51 | Operation op; 52 | 53 | /// Vector of PIFOArguments 54 | /// We use a vector because for an Operation::ENQ, 55 | /// we might need to insert it into multiple stages 56 | std::vector pifo_arguments; 57 | }; 58 | 59 | /// Simple look-up table to look-up a packet's next hop. 60 | /// Takes as input a packet field name as a string 61 | /// and a std::map that determines the next-hop based on that 62 | /// field name. TODO: We assume all fields are ints 63 | class NextHopLookupTable { 64 | public: 65 | NextHopLookupTable(const std::string & lut_field_name, const std::initializer_list> & lut_init) 66 | : look_up_field_name_(lut_field_name), 67 | look_up_table_(lut_init) {} 68 | 69 | /// Lookup a PIFOPacket in a LookUpTable using a specific field name 70 | auto lookup(const PIFOPacket & packet) const { 71 | if (look_up_table_.find(packet(look_up_field_name_)) == look_up_table_.end()) { 72 | throw std::logic_error("Field named " + look_up_field_name_ + 73 | " does not have an entry with value " + std::to_string(packet(look_up_field_name_)) + 74 | " in NextHopLookupTable"); 75 | } 76 | return look_up_table_.at(packet(look_up_field_name_)); 77 | } 78 | 79 | private: 80 | /// Field name to use for lookup 81 | const std::string look_up_field_name_ = ""; 82 | 83 | /// Lookup table itself 84 | const std::map look_up_table_ = {}; 85 | }; 86 | 87 | /// PIFOPipelineStage models a stage of PIFOs 88 | /// ---each of which can be a priority queue or a calendar queue. 89 | /// On any tick, there can be at most one enqueue and one dequeue 90 | /// to the PIFOPipelineStage using the enq and deq methods. 91 | /// These enq and deq methods can be external or from adjacent stages. 92 | /// A lookup table within each stage tells each packet where to go next. 93 | /// The compiler fills in the lut based on the graphviz dot file 94 | /// describing the scheduling hierarchy. 95 | class PIFOPipelineStage { 96 | public: 97 | typedef PushableElement PushablePIFOPacket; 98 | 99 | /// Constructor for PIFOPipelineStage with a number of prio. and cal. qs 100 | PIFOPipelineStage(const uint32_t & num_prio_queues, 101 | const std::string & lut_field_name, 102 | const std::initializer_list> & lut_initializer, 103 | const std::function & t_prio_computer) 104 | : priority_queue_bank_(num_prio_queues), 105 | calendar_queue_(), 106 | next_hop_lut_(lut_field_name, lut_initializer), 107 | prio_computer_(t_prio_computer) {} 108 | 109 | /// Enqueue 110 | /// These happen externally from the ingress pipeline 111 | /// or from a push from a calendar queue/ 112 | void enq(const QueueType & q_type, const uint32_t & queue_id, 113 | const PIFOPacket & packet, const uint32_t & tick) { 114 | num_enq_ops++; 115 | assert_exception(num_enq_ops == 1); 116 | const auto prio = prio_computer_(packet); 117 | if (q_type == QueueType::PRIORITY_QUEUE) { 118 | priority_queue_bank_.at(queue_id).enq(packet, 119 | prio, tick); 120 | } else { 121 | calendar_queue_.enq(packet, prio, tick); 122 | } 123 | num_enq_ops = 0; 124 | } 125 | 126 | /// Dequeues 127 | /// Happen implicitly starting from the root PIFO 128 | Optional deq(const QueueType & q_type, const uint32_t & queue_id, 129 | const uint32_t & tick) { 130 | num_deq_ops++; 131 | assert_exception(num_deq_ops == 1); 132 | if (q_type == QueueType::PRIORITY_QUEUE) { 133 | num_deq_ops = 0; 134 | return priority_queue_bank_.at(queue_id).deq(tick); 135 | } else { 136 | num_deq_ops = 0; 137 | return calendar_queue_.deq(tick); 138 | } 139 | } 140 | 141 | /// Overload stream insertion operator 142 | friend std::ostream & operator<<(std::ostream & out, const PIFOPipelineStage & pipe_stage) { 143 | out << "Contents of PIFOPipelineStage " << std::endl; 144 | out << "Priority Queues: " << std::endl; 145 | for (uint32_t i = 0; i < pipe_stage.priority_queue_bank_.size(); i++) { 146 | out << "Index " << i << " " << pipe_stage.priority_queue_bank_.at(i) << std::endl; 147 | } 148 | 149 | out << "Calendar Queue: " << std::endl; 150 | out << pipe_stage.calendar_queue_ << std::endl; 151 | 152 | out << "End of contents of PIFOPipelineStage " << std::endl; 153 | 154 | return out; 155 | } 156 | 157 | /// Find "next hop" after a dequeue 158 | auto find_next_hop(const PIFOPacket & packet) const { 159 | return next_hop_lut_.lookup(packet); 160 | } 161 | 162 | private: 163 | /// Bank of priority queues 164 | std::vector> priority_queue_bank_; 165 | 166 | /// Single calendar queue 167 | CalendarQueue calendar_queue_; 168 | 169 | /// look-up table to find the next hop 170 | const NextHopLookupTable next_hop_lut_; 171 | 172 | /// Function object to compute incoming packet's priority 173 | /// Identity function by default 174 | const std::function prio_computer_; 175 | 176 | /// Count the number of enq ops on this stage every clock tick 177 | uint32_t num_enq_ops = 0; 178 | 179 | /// Count the number of deq ops on this stage every clock tick 180 | uint32_t num_deq_ops = 0; 181 | }; 182 | 183 | #endif // PIFO_PIPELINE_STAGE_H_ 184 | --------------------------------------------------------------------------------