MatchLib
nvhls_connections_buffered_ports.h
1 /*
2  * Copyright (c) 2016-2024, NVIDIA CORPORATION. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License")
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 //========================================================================
17 // nvhls_connections_buffered_ports.h
18 //========================================================================
19 
20 #ifndef NVHLS_CONNECTIONS_BUFFERED_PORTS_H_
21 #define NVHLS_CONNECTIONS_BUFFERED_PORTS_H_
22 
23 #include <iomanip>
24 #include <systemc.h>
25 #include <nvhls_assert.h>
26 #include <nvhls_marshaller.h>
27 #include <nvhls_module.h>
28 #include <fifo.h>
29 #include <ccs_p2p.h>
30 
31 namespace Connections {
32 
33 template <typename Message, int BufferSize = 1, connections_port_t port_marshall_type = AUTO_PORT>
34 class InBuffered : public InBlocking<Message, port_marshall_type> {
36 
37  public:
38  InBuffered() : InBlocking<Message, port_marshall_type>(), fifo() {}
39 
40  explicit InBuffered(const char* name) : InBlocking<Message, port_marshall_type>(name), fifo() {}
41 
42  void Reset() {
43  InBlocking<Message,port_marshall_type>::Reset();
44  fifo.reset();
45  }
46 
47  // Empty
48  bool Empty() { return fifo.isEmpty(); }
49 
50  Message Pop() { return fifo.pop(); }
51 
52  void IncrHead() { fifo.incrHead(); }
53 
54  Message Peek() { return fifo.peek(); }
55 
56  void TransferNB() {
57 
58  if (!fifo.isFull()) {
59  Message dat;
60  if (this->PopNB(dat)) {
61  fifo.push(dat);
62  }
63  }
64  }
65 };
66 
67 template <typename Message, int BufferSize = 1, connections_port_t port_marshall_type = AUTO_PORT>
68 class OutBuffered : public OutBlocking<Message, port_marshall_type> {
70  typedef NVUINTW(nvhls::index_width<BufferSize+1>::val) AddressPlusOne;
71  public:
72  OutBuffered() : OutBlocking<Message, port_marshall_type>(), fifo() {}
73 
74  explicit OutBuffered(const char* name) : OutBlocking<Message, port_marshall_type>(name), fifo() {}
75 
76  void Reset() {
77  OutBlocking<Message,port_marshall_type>::Reset();
78  fifo.reset();
79  }
80 
81  // Full
82  bool Full() { return fifo.isFull(); }
83  // Empty
84  bool Empty() { return fifo.isEmpty(); }
85 
86  AddressPlusOne NumAvailable() { return fifo.NumAvailable(); }
87 
88  void Push(const Message& dat) { fifo.push(dat); }
89 
90  void TransferNB() {
91  if (!fifo.isEmpty()) {
92  Message dat = fifo.peek();
93  if (this->PushNB(dat)) {
94  fifo.pop();
95  }
96  }
97  }
98 };
99 
100 
101 //------------------------------------------------------------------------
102 // Helper class for Bypass and Pipeline
103 //------------------------------------------------------------------------
104 
105 template <typename Message, connections_port_t port_marshall_type = AUTO_PORT>
107 
108 template <typename Message>
109 class StateSignal<Message, SYN_PORT> {
110  public:
111  // Interface
112  typedef Wrapped<Message> WMessage;
113  static const unsigned int width = WMessage::width;
114  typedef sc_lv<WMessage::width> MsgBits;
115  sc_signal<MsgBits> dat;
116 
117  StateSignal() : dat(sc_gen_unique_name("dat")) {}
118 
119  StateSignal(sc_module_name name) : dat(CONNECTIONS_CONCAT(name, "_dat")) { }
120 
121  void reset_state() {
122  dat.write(0);
123  }
124 };
125 
126 template <typename Message>
127 class StateSignal<Message, MARSHALL_PORT> {
128  public:
129  // Interface
130  typedef Wrapped<Message> WMessage;
131  static const unsigned int width = WMessage::width;
132  typedef sc_lv<WMessage::width> MsgBits;
133  sc_signal<MsgBits> dat;
134 
135  StateSignal() : dat(sc_gen_unique_name("dat")) {}
136 
137  StateSignal(sc_module_name name) : dat(CONNECTIONS_CONCAT(name, "_dat")) { }
138 
139  void reset_state() {
140  dat.write(0);
141  }
142 };
143 
144 template <typename Message>
145 class StateSignal<Message, DIRECT_PORT> {
146  public:
147  // Interface
148  sc_signal<Message> dat;
149 
150  StateSignal() : dat(sc_gen_unique_name("dat")) {}
151 
152  StateSignal(sc_module_name name) : dat(CONNECTIONS_CONCAT(name, "_dat")) { }
153 
154  void reset_state() {
155  Message dc;
156  dat.write(dc);
157  }
158 };
159 
160 // Because of ports not existing in TLM_PORT and the code depending on it,
161 // we remap to DIRECT_PORT here.
162 template <typename Message>
163 class StateSignal<Message, TLM_PORT> : public StateSignal<Message, DIRECT_PORT>
164 {
165  public:
167  StateSignal(sc_module_name name) : StateSignal<Message, DIRECT_PORT>(name) {}
168 };
169 
170 //------------------------------------------------------------------------
171 // Bypass
172 //------------------------------------------------------------------------
173 
174 template <typename Message, connections_port_t port_marshall_type>
175 class Bypass : public sc_module {
176  SC_HAS_PROCESS(Bypass);
177 
178  public:
179  static const int kDebugLevel = 3;
180  // Interface
181  sc_in_clk clk;
182  sc_in<bool> rst;
183  In<Message, port_marshall_type> enq;
184  Out<Message, port_marshall_type> deq;
185 
186  Bypass()
187  : sc_module(sc_module_name(sc_gen_unique_name("byp"))),
188  clk("clk"),
189  rst("rst") {
190  Init();
191  }
192 
193  Bypass(sc_module_name name) : sc_module(name), clk("clk"), rst("rst") {
194  Init();
195  }
196 
197  protected:
198  typedef bool Bit;
199 
200  // Internal wires
201  sc_signal<Bit> wen;
202 
203  // Internal state
204  sc_signal<Bit> full;
206 
207  // Helper functions
208  void Init() {
209 #ifdef CONNECTIONS_SIM_ONLY
210  enq.disable_spawn();
211  deq.disable_spawn();
212 #endif
213 
214  SC_METHOD(EnqRdy);
215  sensitive << full;
216 
217  SC_METHOD(DeqVld);
218  sensitive << enq.vld << full;
219 
220  SC_METHOD(WriteEn);
221  sensitive << enq.vld << deq.rdy << full;
222 
223  SC_METHOD(BypassMux);
224  sensitive << enq.dat << state.dat << full;
225 
226  SC_THREAD(Seq);
227  sensitive << clk.pos();
229  }
230 
231  // Combinational logic
232 
233  // Write enable
234  void WriteEn() {
235  wen.write(enq.vld.read() && !deq.rdy.read() && !full.read());
236  }
237 
238  // Enqueue ready
239  void EnqRdy() { enq.rdy.write(!full.read()); }
240 
241  // Dequeue valid
242  void DeqVld() {
243  deq.vld.write(full.read() || (!full.read() && enq.vld.read()));
244  }
245 
246  // Bypass mux
247  void BypassMux() {
248  if (full.read()) {
249  deq.dat.write(state.dat.read());
250  } else {
251  deq.dat.write(enq.dat.read());
252  }
253  }
254 
255  // Sequential logic
256  void Seq() {
257  // Reset state
258  full.write(0);
259  state.dat.write(0);
260 
261  wait();
262 
263  while (1) {
264  // Full update
265  if (deq.rdy.read()) {
266  full.write(false);
267  } else if (enq.vld.read() && !deq.rdy.read() && !full.read()) {
268  full.write(true);
269  }
270 
271  // State Update
272  if (wen.read()) {
273  state.dat.write(enq.dat.read());
274  }
275  wait();
276  }
277  }
278 
279 #ifndef __SYNTHESIS__
280  public:
281  void line_trace() {
282  if (rst.read()) {
283  unsigned int width = (Message().length() / 4);
284  // Enqueue port
285  if (enq.vld.read() && enq.rdy.read()) {
286  CDCOUT(std::hex << std::setw(width) << enq.dat.read(), kDebugLevel);
287  } else {
288  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
289  }
290 
291  CDCOUT(" ( " << full.read() << " ) ", kDebugLevel);
292 
293  // Dequeue port
294  if (deq.vld.read() && deq.rdy.read()) {
295  CDCOUT(std::hex << std::setw(width) << deq.dat.read(), kDebugLevel);
296  } else {
297  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
298  }
299  CDCOUT(" | ", kDebugLevel);
300  }
301  }
302 #endif
303 };
304 
305 // Because of ports not existing in TLM_PORT and the code depending on it,
306 // we remap to DIRECT_PORT here.
307 template <typename Message>
308 class Bypass<Message, TLM_PORT> : public Bypass<Message, DIRECT_PORT>
309 {
310  public:
312  Bypass(sc_module_name name) : Bypass<Message, DIRECT_PORT>(name) {}
313 };
314 
315 //------------------------------------------------------------------------
316 // Pipeline
317 //------------------------------------------------------------------------
318 
319 template <typename Message, connections_port_t port_marshall_type>
320 class Pipeline : public sc_module {
321  SC_HAS_PROCESS(Pipeline);
322 
323  public:
324  static const int kDebugLevel = 3;
325  // Interface
326  sc_in_clk clk;
327  sc_in<bool> rst;
328  In<Message, port_marshall_type> enq;
329  Out<Message, port_marshall_type> deq;
330 
331  Pipeline()
332  : sc_module(sc_module_name(sc_gen_unique_name("byp"))),
333  clk("clk"),
334  rst("rst") {
335  Init();
336  }
337 
338  Pipeline(sc_module_name name) : sc_module(name), clk("clk"), rst("rst") {
339  Init();
340  }
341 
342  protected:
343  typedef bool Bit;
344 
345  // Internal wires
346  sc_signal<Bit> wen;
347 
348  // Internal state
349  sc_signal<Bit> full;
351 
352  // Helper functions
353  void Init() {
354 #ifdef CONNECTIONS_SIM_ONLY
355  enq.disable_spawn();
356  deq.disable_spawn();
357 #endif
358 
359  SC_METHOD(EnqRdy);
360  sensitive << full << deq.rdy;
361 
362  SC_METHOD(DeqVld);
363  sensitive << full;
364 
365  SC_METHOD(WriteEn);
366  sensitive << enq.vld << deq.rdy << full;
367 
368  SC_METHOD(DeqMsg);
369  sensitive << state.dat;
370 
371  SC_THREAD(Seq);
372  sensitive << clk.pos();
374  }
375 
376  // Combinational logic
377 
378  // Internal state write enable: incoming dat is valid and (internal state is
379  // not set or outgoing channel is ready.
380  void WriteEn() {
381  wen.write(enq.vld.read() && (!full.read() || (full && deq.rdy.read())));
382  }
383 
384  // Enqueue ready if either internal state is not set or outgoing channel is
385  // ready.
386  void EnqRdy() { enq.rdy.write(!full.read() || (full && deq.rdy.read())); }
387 
388  // Dequeue valid if the internal state is set.
389  void DeqVld() { deq.vld.write(full.read()); }
390 
391  // Dequeue Msg is from the internal state.
392  void DeqMsg() { deq.dat.write(state.dat.read()); }
393 
394  // Sequential logic
395  void Seq() {
396  // Reset state
397  full.write(0);
398  state.reset_state();
399 
400  wait();
401 
402  while (1) {
403  // Full update
404  if (full.read() && deq.rdy.read() && !enq.vld.read()) {
405  full.write(false);
406  } else if (enq.vld.read() &&
407  (!full.read() || (full.read() && deq.rdy.read()))) {
408  full.write(true);
409  }
410 
411  // State Update
412  if (wen.read()) {
413  state.dat.write(enq.dat.read());
414  }
415  wait();
416  }
417  }
418 
419 #ifndef __SYNTHESIS__
420  public:
421  void line_trace() {
422  if (rst.read()) {
423  unsigned int width = (Message().length() / 4);
424  // Enqueue port
425  if (enq.vld.read() && enq.rdy.read()) {
426  CDCOUT(std::hex << std::setw(width) << enq.dat.read(), kDebugLevel);
427  } else {
428  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
429  }
430 
431  CDCOUT(" ( " << full.read() << " ) ", kDebugLevel);
432 
433  // Dequeue port
434  if (deq.vld.read() && deq.rdy.read()) {
435  CDCOUT(std::hex << std::setw(width) << deq.dat.read(), kDebugLevel);
436  } else {
437  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
438  }
439  CDCOUT(" | ", kDebugLevel);
440  }
441  }
442 #endif
443 };
444 
445 // Because of ports not existing in TLM_PORT and the code depending on it,
446 // we remap to DIRECT_PORT here.
447 template <typename Message>
448 class Pipeline<Message, TLM_PORT> : public Pipeline<Message, DIRECT_PORT>
449 {
450  public:
452  Pipeline(sc_module_name name) : Pipeline<Message, DIRECT_PORT>(name) {}
453 };
454 
455 
456 //
457 // NEW FEATURE: Buffered Bypass Channel.
458 // This is a BypassBuffered channel that can have depth > 1.
459 // W.r.t the Bypass it takes one more template parameter.
460 // TODO: It may also work with depth = 1, but it hasn't been tested.
461 
462 //------------------------------------------------------------------------
463 // BypassBuffered
464 //------------------------------------------------------------------------
465 
466 template <typename Message, unsigned int NumEntries, connections_port_t port_marshall_type = AUTO_PORT>
467 class BypassBuffered : public sc_module {
468  SC_HAS_PROCESS(BypassBuffered);
469 
470  public:
471  static const int kDebugLevel = 3;
472  // Interface
473  sc_in_clk clk;
474  sc_in<bool> rst;
475  In<Message, port_marshall_type> enq;
476  Out<Message, port_marshall_type> deq;
477 
479  : sc_module(sc_module_name(sc_gen_unique_name("byp"))),
480  clk("clk"),
481  rst("rst") {
482  Init();
483  }
484 
485  BypassBuffered(sc_module_name name) : sc_module(name), clk("clk"), rst("rst") {
486  Init();
487  }
488 
489  protected:
490  typedef bool Bit;
491  static const int AddrWidth = nvhls::nbits<NumEntries - 1>::val;
492  typedef NVUINTW(AddrWidth) BuffIdx;
493 
494  // Internal wires
495  sc_signal<Bit> full_next;
496  sc_signal<BuffIdx> head_next;
497  sc_signal<BuffIdx> tail_next;
498 
499  // Internal state
500  sc_signal<Bit> full;
501  sc_signal<BuffIdx> head;
502  sc_signal<BuffIdx> tail;
503  StateSignal<Message, port_marshall_type> buffer[NumEntries];
504 
505  // Helper functions
506  void Init() {
507 #ifdef CONNECTIONS_SIM_ONLY
508  enq.disable_spawn();
509  deq.disable_spawn();
510 #endif
511 
512  SC_METHOD(EnqRdy);
513  sensitive << full;
514 
515  SC_METHOD(DeqVld);
516  sensitive << full << head << tail << enq.vld;
517 
518  SC_METHOD(DeqMsg);
519 #ifndef __SYNTHESIS__
520  sensitive << deq.rdy << full << head << tail << enq.vld << enq.dat;
521 #else
522  sensitive << full << head << tail << enq.dat;
523 #endif
524 
525  SC_METHOD(HeadNext);
526  sensitive << enq.vld << full << head << tail << deq.rdy;
527 
528  SC_METHOD(TailNext);
529  sensitive << deq.rdy << full << head << tail;
530 
531  SC_METHOD(FullNext);
532  sensitive << enq.vld << deq.rdy << full << head << tail;
533 
534  SC_THREAD(Seq);
535  sensitive << clk.pos();
537 
538  // Needed so that DeqMsg always has a good tail value
539  tail.write(0);
540  }
541 
542  // Combinational logic
543 
544  // Enqueue ready
545  void EnqRdy() { enq.rdy.write(!full.read()); }
546 
547  // Dequeue valid
548  void DeqVld() {
549  bool empty = (!full.read() && (head.read() == tail.read()));
550  deq.vld.write(!empty || enq.vld.read());
551  }
552 
553  // Dequeue messsage
554  void DeqMsg() {
555  bool empty = (!full.read() && (head.read() == tail.read()));
556 #ifndef __SYNTHESIS__
557  bool do_deq = !empty || enq.vld.read();
558  if (do_deq) {
559 #endif
560  if (!empty) {
561  deq.dat.write(buffer[tail.read()].dat.read());
562  } else {
563  deq.dat.write(enq.dat.read());
564  }
565 #ifndef __SYNTHESIS__
566  } else {
567  deq.dat.write(0);
568  }
569 #endif
570  }
571 
572  // Head next calculations
573  void HeadNext() {
574  bool empty = (!full.read() && (head.read() == tail.read()));
575  bool do_enq = (enq.vld.read() && !full.read() &&
576  !(empty && deq.rdy.read()));
577  BuffIdx head_inc;
578  if ((head.read() + 1) == NumEntries)
579  head_inc = 0;
580  else
581  head_inc = head.read() + 1;
582 
583  if (do_enq)
584  head_next.write(head_inc);
585  else
586  head_next.write(head.read());
587  }
588 
589  // Tail next calculations
590  void TailNext() {
591  bool empty = (!full.read() && (head.read() == tail.read()));
592  bool do_deq = (deq.rdy.read() && !empty);
593  BuffIdx tail_inc;
594  if ((tail.read() + 1) == NumEntries)
595  tail_inc = 0;
596  else
597  tail_inc = tail.read() + 1;
598 
599  if (do_deq)
600  tail_next.write(tail_inc);
601  else
602  tail_next.write(tail.read());
603  }
604 
605  // Full next calculations
606  void FullNext() {
607  bool empty = (!full.read() && (head.read() == tail.read()));
608  bool do_enq = (enq.vld.read() && !full.read() &&
609  !(empty && deq.rdy.read()));
610  bool do_deq = (deq.rdy.read() && !empty);
611 
612  BuffIdx head_inc;
613  if ((head.read() + 1) == NumEntries)
614  head_inc = 0;
615  else
616  head_inc = head.read() + 1;
617 
618  if (do_enq && !do_deq && (head_inc == tail.read()))
619  full_next.write(1);
620  else if (do_deq && full.read())
621  full_next.write(0);
622  else
623  full_next.write(full.read());
624  }
625 
626  // Sequential logic
627  void Seq() {
628  // Reset state
629  full.write(0);
630  head.write(0);
631  tail.write(0);
632 #pragma hls_unroll yes
633  for (unsigned int i = 0; i < NumEntries; ++i)
634  buffer[i].dat.reset_state();
635 
636  wait();
637 
638  while (1) {
639 
640  // Head update
641  head.write(head_next);
642 
643  // Tail update
644  tail.write(tail_next);
645 
646  // Full update
647  full.write(full_next);
648 
649  // Enqueue message
650  bool empty = (!full.read() && (head.read() == tail.read()));
651 
652  if (enq.vld.read() && !full.read() &&
653  !(empty && deq.rdy.read())) {
654  buffer[head.read()].dat.write(enq.dat.read());
655  }
656 
657  wait();
658  }
659  }
660 
661 #ifndef __SYNTHESIS__
662  public:
663  void line_trace() {
664  if (rst.read()) {
665  unsigned int width = (Message().length() / 4);
666  // Enqueue port
667  if (enq.vld.read() && enq.rdy.read()) {
668  CDCOUT(std::hex << std::setw(width) << enq.dat.read(), kDebugLevel);
669  } else {
670  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
671  }
672 
673  CDCOUT(" ( " << full.read() << " ) ", kDebugLevel);
674 
675  // Dequeue port
676  if (deq.vld.read() && deq.rdy.read()) {
677  CDCOUT(std::hex << std::setw(width) << deq.dat.read(), kDebugLevel);
678  } else {
679  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
680  }
681  CDCOUT(" | ", kDebugLevel);
682  }
683  }
684 #endif
685 };
686 
687 // Because of ports not existing in TLM_PORT and the code depending on it,
688 // we remap to DIRECT_PORT here.
689 template <typename Message, unsigned int NumEntries>
690 class BypassBuffered<Message, NumEntries, TLM_PORT> : public BypassBuffered<Message, NumEntries, DIRECT_PORT>
691 {
692  public:
695 };
696 
697 
698 //------------------------------------------------------------------------
699 // Buffer
700 //------------------------------------------------------------------------
701 
702 template <typename Message, unsigned int NumEntries, connections_port_t port_marshall_type>
703 class Buffer : public sc_module {
704  SC_HAS_PROCESS(Buffer);
705 
706  public:
707  static const int kDebugLevel = 3;
708  // Interface
709  sc_in_clk clk;
710  sc_in<bool> rst;
711  In<Message, port_marshall_type> enq;
712  Out<Message, port_marshall_type> deq;
713 
714  Buffer()
715  : sc_module(sc_module_name(sc_gen_unique_name("buffer"))),
716  clk("clk"),
717  rst("rst") {
718  Init();
719  }
720 
721  Buffer(sc_module_name name) : sc_module(name), clk("clk"), rst("rst") {
722  Init();
723  }
724 
725  protected:
726  typedef bool Bit;
727  static const int AddrWidth = nvhls::index_width<NumEntries>::val;
728  typedef NVUINTW(AddrWidth) BuffIdx;
729 
730  // Internal wires
731  sc_signal<Bit> full_next;
732  sc_signal<BuffIdx> head_next;
733  sc_signal<BuffIdx> tail_next;
734 
735  // Internal state
736  sc_signal<Bit> full;
737  sc_signal<BuffIdx> head;
738  sc_signal<BuffIdx> tail;
739  StateSignal<Message, port_marshall_type> buffer[NumEntries];
740 
741  // Helper functions
742  void Init() {
743 #ifdef CONNECTIONS_SIM_ONLY
744  enq.disable_spawn();
745  deq.disable_spawn();
746 #endif
747 
748  SC_METHOD(EnqRdy);
749  sensitive << full;
750 
751  SC_METHOD(DeqVld);
752  sensitive << full << head << tail;
753 
754  SC_METHOD(DeqMsg);
755 #ifndef __SYNTHESIS__
756  sensitive << deq.rdy << full << head << tail;
757 #else
758  sensitive << tail;
759 #endif
760 
761  SC_METHOD(HeadNext);
762  sensitive << enq.vld << full << head;
763 
764  SC_METHOD(TailNext);
765  sensitive << deq.rdy << full << head << tail;
766 
767  SC_METHOD(FullNext);
768  sensitive << enq.vld << deq.rdy << full << head << tail;
769 
770  SC_THREAD(Seq);
771  sensitive << clk.pos();
773 
774  // Needed so that DeqMsg always has a good tail value
775  tail.write(0);
776  }
777 
778  // Combinational logic
779 
780  // Enqueue ready
781  void EnqRdy() { enq.rdy.write(!full.read()); }
782 
783  // Dequeue valid
784  void DeqVld() {
785  bool empty = (!full.read() && (head.read() == tail.read()));
786  deq.vld.write(!empty);
787  }
788 
789  // Dequeue messsage
790  void DeqMsg() {
791 #ifndef __SYNTHESIS__
792  bool empty = (!full.read() && (head.read() == tail.read()));
793  bool do_deq = !empty;
794  if (do_deq) {
795 #endif
796  deq.dat.write(buffer[tail.read()].dat.read());
797 #ifndef __SYNTHESIS__
798  } else {
799  deq.dat.write(0);
800  }
801 #endif
802  }
803 
804  // Head next calculations
805  void HeadNext() {
806  bool do_enq = (enq.vld.read() && !full.read());
807  BuffIdx head_inc;
808  if ((head.read() + 1) == NumEntries)
809  head_inc = 0;
810  else
811  head_inc = head.read() + 1;
812 
813  if (do_enq)
814  head_next.write(head_inc);
815  else
816  head_next.write(head.read());
817  }
818 
819  // Tail next calculations
820  void TailNext() {
821  bool empty = (!full.read() && (head.read() == tail.read()));
822  bool do_deq = (deq.rdy.read() && !empty);
823  BuffIdx tail_inc;
824  if ((tail.read() + 1) == NumEntries)
825  tail_inc = 0;
826  else
827  tail_inc = tail.read() + 1;
828 
829  if (do_deq)
830  tail_next.write(tail_inc);
831  else
832  tail_next.write(tail.read());
833  }
834 
835  // Full next calculations
836  void FullNext() {
837  bool empty = (!full.read() && (head.read() == tail.read()));
838  bool do_enq = (enq.vld.read() && !full.read());
839  bool do_deq = (deq.rdy.read() && !empty);
840 
841  BuffIdx head_inc;
842  if ((head.read() + 1) == NumEntries)
843  head_inc = 0;
844  else
845  head_inc = head.read() + 1;
846 
847  if (do_enq && !do_deq && (head_inc == tail.read()))
848  full_next.write(1);
849  else if (do_deq && full.read())
850  full_next.write(0);
851  else
852  full_next.write(full.read());
853  }
854 
855  // Sequential logic
856  void Seq() {
857  // Reset state
858  full.write(0);
859  head.write(0);
860  tail.write(0);
861 #pragma hls_unroll yes
862  for (unsigned int i = 0; i < NumEntries; ++i)
863  buffer[i].reset_state();
864 
865  wait();
866 
867  while (1) {
868  // Head update
869  head.write(head_next);
870 
871  // Tail update
872  tail.write(tail_next);
873 
874  // Full update
875  full.write(full_next);
876 
877  // Enqueue message
878  if (enq.vld.read() && !full.read()) {
879  buffer[head.read()].dat.write(enq.dat.read());
880  }
881 
882  wait();
883  }
884  }
885 
886 #ifndef __SYNTHESIS__
887  public:
888  void line_trace() {
889  if (rst.read()) {
890  unsigned int width = (Message().length() / 4);
891  // Enqueue port
892  if (enq.vld.read() && enq.rdy.read()) {
893  CDCOUT(std::hex << std::setw(width) << enq.dat.read(), kDebugLevel);
894  } else {
895  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
896  }
897 
898  CDCOUT(" ( " << full.read() << " ) ", kDebugLevel);
899 
900  // Dequeue port
901  if (deq.vld.read() && deq.rdy.read()) {
902  CDCOUT(std::hex << std::setw(width) << deq.dat.read(), kDebugLevel);
903  } else {
904  CDCOUT(std::setw(width + 1) << " ", kDebugLevel);
905  }
906  CDCOUT(" | ", kDebugLevel);
907  }
908  }
909 #endif
910 };
911 
912 // Because of ports not existing in TLM_PORT and the code depending on it,
913 // we remap to DIRECT_PORT here.
914 template <typename Message, unsigned int NumEntries>
915 class Buffer<Message, NumEntries, TLM_PORT> : public Buffer<Message, NumEntries, DIRECT_PORT>
916 {
917  public:
919  Buffer(sc_module_name name) : Buffer<Message, NumEntries, DIRECT_PORT>(name) {}
920 };
921 
923 // Sink and Source
925 
926 template <typename MessageType, connections_port_t port_marshall_type = AUTO_PORT>
927 class Sink : public sc_module {
928  SC_HAS_PROCESS(Sink);
929  public:
930  // External interface
931  sc_in_clk clk;
932  sc_in<bool> rst;
933  In<MessageType, port_marshall_type> in;
934 
935  // Constructor
936  Sink(sc_module_name nm)
937  : sc_module(nm),
938  clk("clk"),
939  rst("rst"),
940  in("in") {
941  SC_THREAD(DoSink);
942  sensitive << clk.pos();
944  }
945  static const unsigned int width = 0;
946  template <unsigned int Size>
947  void Marshall(Marshaller<Size>& m) {
948  }
949 
950  protected:
951  // Behavior
952  void DoSink() {
953  in.Reset();
954  wait();
955  while (1) {
956  in.Pop();
957  }
958  }
959 
960  // Binding
961  template <typename C>
962  void operator()(C& rhs) {
963  in.Bind(rhs);
964  }
965 };
966 
967 template <typename MessageType, connections_port_t port_marshall_type = AUTO_PORT>
968 class DummySink : public sc_module {
969  SC_HAS_PROCESS(DummySink);
970  public:
971  // External interface
972  sc_in_clk clk;
973  sc_in<bool> rst;
974  In<MessageType, port_marshall_type> in;
975 
976  // Constructor
977  DummySink(sc_module_name nm)
978  : sc_module(nm),
979  clk("clk"),
980  rst("rst"),
981  in("in") {
982  SC_THREAD(DoSink);
983  sensitive << clk.pos();
985  }
986  static const unsigned int width = 0;
987  template <unsigned int Size>
988  void Marshall(Marshaller<Size>& m) {
989  }
990 
991  protected:
992  // Behavior
993  void DoSink() {
994  in.Reset();
995  wait();
996  while (1) {
997  wait();
998  }
999  }
1000 
1001  // Binding
1002  template <typename C>
1003  void operator()(C& rhs) {
1004  in.Bind(rhs);
1005  }
1006 };
1007 
1008 template <typename MessageType, connections_port_t port_marshall_type = AUTO_PORT>
1009 class DummySource : public sc_module {
1010  SC_HAS_PROCESS(DummySource);
1011  public:
1012  // External interface
1013  sc_in_clk clk;
1014  sc_in<bool> rst;
1015  Out<MessageType, port_marshall_type> out;
1016 
1017  // Constructor
1018  DummySource(sc_module_name nm)
1019  : sc_module(nm),
1020  clk("clk"),
1021  rst("rst"),
1022  out("out") {
1023  SC_THREAD(DoSource);
1024  sensitive << clk.pos();
1026  }
1027  static const unsigned int width = 0;
1028  template <unsigned int Size>
1029  void Marshall(Marshaller<Size>& m) {
1030  }
1031 
1032  protected:
1033  // Behavior
1034  void DoSource() {
1035  out.Reset();
1036  wait();
1037  while (1) {
1038  wait();
1039  }
1040  }
1041 
1042  // Binding
1043  template <typename C>
1044  void operator()(C& rhs) {
1045  out.Bind(rhs);
1046  }
1047 };
1048 
1049 //
1050 // NEW FEATURE: ChannelBinder class
1051 // This class binds together a channel to two ports.
1052 // It works with any type of port and channel.
1053 // TODO: add constructors that take clk and rst from the sender
1054 // module. They take as argument the sender module instead of its output
1055 // port.
1056 //
1057 
1058 template <typename Message, unsigned int NumEntries = 1>
1060 
1061  public:
1062 
1063  Combinational<Message> enq;
1064  Combinational<Message> deq;
1065 
1066  // Combinational binding
1067  ChannelBinder(InBlocking<Message>& in,
1068  OutBlocking<Message>& out,
1069  Combinational<Message>& chan)
1070  : enq(sc_gen_unique_name("bind_enq")),
1071  deq(sc_gen_unique_name("bind_deq")) {
1072 
1073  out(chan);
1074  in(chan);
1075  }
1076 
1077  // Combinational binding w/ extra clk and rst arguments
1078  ChannelBinder(InBlocking<Message>& in,
1079  OutBlocking<Message>& out,
1080  Combinational<Message>& chan,
1081  sc_in_clk& clk, sc_in<bool>& rst)
1082  : enq(sc_gen_unique_name("bind_enq")),
1083  deq(sc_gen_unique_name("bind_deq")) {
1084 
1085  out(chan);
1086  in(chan);
1087  }
1088 
1089  // Bypass (depth 1) binding w/ clk and rst arguments.
1090  ChannelBinder(InBlocking<Message>& in,
1091  OutBlocking<Message>& out,
1092  Bypass<Message>& chan,
1093  sc_in_clk& clk, sc_in<bool>& rst)
1094  : enq(sc_gen_unique_name("bind_enq")),
1095  deq(sc_gen_unique_name("bind_deq")) {
1096 
1097  out(enq);
1098  chan.clk(clk);
1099  chan.rst(rst);
1100  chan.enq(enq);
1101  chan.deq(deq);
1102  in(deq);
1103  }
1104 
1105  // BypassBuffered (any depth) binding w/ clk and rst arguments.
1106  ChannelBinder(InBlocking<Message>& in,
1107  OutBlocking<Message>& out,
1109  sc_in_clk& clk, sc_in<bool>& rst)
1110  : enq(sc_gen_unique_name("bind_enq")),
1111  deq(sc_gen_unique_name("bind_deq")) {
1112 
1113  out(enq);
1114  chan.clk(clk);
1115  chan.rst(rst);
1116  chan.enq(enq);
1117  chan.deq(deq);
1118  in(deq);
1119 
1120  }
1121 
1122  // Pipeline binding w/ clk and rst arguments.
1123  ChannelBinder(InBlocking<Message>& in,
1124  OutBlocking<Message>& out,
1125  Pipeline<Message>& chan,
1126  sc_in_clk& clk, sc_in<bool>& rst)
1127  : enq(sc_gen_unique_name("bind_enq")),
1128  deq(sc_gen_unique_name("bind_deq")) {
1129 
1130  out(enq);
1131  chan.clk(clk);
1132  chan.rst(rst);
1133  chan.enq(enq);
1134  chan.deq(deq);
1135  in(deq);
1136  }
1137 
1138  // Buffer binding w/ clk and rst arguments.
1139  ChannelBinder(InBlocking<Message>& in,
1140  OutBlocking<Message>& out,
1142  sc_in_clk& clk, sc_in<bool>& rst)
1143  : enq(sc_gen_unique_name("bind_enq")),
1144  deq(sc_gen_unique_name("bind_deq")) {
1145 
1146  out(enq);
1147  chan.clk(clk);
1148  chan.rst(rst);
1149  chan.enq(enq);
1150  chan.deq(deq);
1151  in(deq);
1152 
1153  }
1154 };
1155 
1156 
1157 } // namespace Connections
1158 
1159 
1160 #endif // NVHLS_CONNECTIONS_BUFFERED_PORTS_H_
1161 
Configurable FIFO class.
Definition: fifo.h:65
#define CDCOUT(x, y)
Definition: hls_globals.h:73
#define NVHLS_NEG_RESET_SIGNAL_IS(port)
ENABLE_SYNC_RESET define: Select synchronous or asynchronous reset.
Definition: nvhls_module.h:39
Compute index width of a constant.
Definition: nvhls_int.h:285
Compute number of bits to represent a constant.
Definition: nvhls_int.h:118