SystemC FIFO 之模組化經驗談

Share on:

本文內容採用創用 CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款授權.

SystemC 中的 FIFO 在寫中大型的電路的 approximate timing model 相當好用,幾乎可以只用 FIFO 就建構出整個 model。此外,用 FIFO 建構出來的 SystemC module 可以 1-to-1 的轉換成 Verilog module(這篇)。然而,SystemC 裡面有 sc_fifo_in, sc_fifo_out, sc_fifo 三者,這篇文章分享了作者的經驗,如何使用這三者,寫出來的 module 會比較好模組化。

在最簡單的情形下,可以只用 sc_fifo 就建構出一個 sc_module(為了簡化程式碼,沒有寫出 constructor)。

 1struct Top: public class sc_module {
 2   sc_fifo<int> data_in_;
 3   sc_fifo<int> data_middle_;
 4   sc_fifo<int> data_out_;
 5   void ScThread1() {
 6      int count;
 7      while (count = data_in_.read())
 8         for (int i = 0; i < count; ++i)
 9            data_middle_.write(i);
10   }
11   void ScThread2() {
12      int num;
13      while (num = data_middle_.read())
14         data_out_.write(num+1);
15   }
16};
其對應到的 SystemC 圖如下:

「圖!!!!!!!!!!」

然而這個 Top 有兩個 SystemC threads,如果我們將他們拆分在成兩個 module 的話 (Module1, Module2),就出現了一個問題:FIFO 要放在哪裡其實有三個選項:

  1. 放在 Module1
  2. 放在 Module2
  3. 放在 Top

「圖!!!!!!!!!!」

從 SystemC 的設計上來看,應該是 Top,而這也就是 sc_fifo_in, sc_fifo_out 存在的原因。他們可以視為指向 sc_fifo 的 pointer,但是 sc_fifo_in 只能允使用讀取資料的功能,sc_fifo_out 只能使用寫入資料的功能。

其對應之程式碼如下:

 1struct Module1: public class sc_module {
 2   sc_fifo_in<int> data_in_;
 3   sc_fifo_out<int> data_out_;
 4   void ScThread() {
 5      int count;
 6      while (count = data_in_.read())
 7         for (int i = 0; i < count; ++i)
 8            data_out_.write(i);
 9   }
10};
11struct Module1: public class sc_module {
12   sc_fifo_in<int> data_in_;
13   sc_fifo_out<int> data_out_;
14   void ScThread() {
15      int num;
16      while (num = data_in_.read())
17         data_out_.write(num+1);
18   }
19};
20struct Top: public class sc_module {
21   sc_fifo<int> data_in_;
22   sc_fifo<int> data_middle_;
23   sc_fifo<int> data_out_;
24   Module1 u_m1_;
25   Module2 u_m2_;
26   // These lines in constructor
27   u_m1_.data_in_ (data_in_    );
28   u_m1_.data_out_(data_middle_);
29   u_m2_.data_in_ (data_middle_);
30   u_m2_.data_out_(data_out_   );
31};

Unittest 中,不需要測試所有 Output Port 時…

然而,在一些錯誤嘗試下,作者覺得放在 Module1 是一個不錯的選擇。更精準的說,作者覺得只使用 sc_fifo_insc_fifo 的話可以少寫一點 code。一個很大的原因是 SystemC 中,如果沒有把所有的 FIFO port 接起來,是無法跑模擬的。

這導致了一個問題:當我們要單獨測試每個 module 的正確性時,如果說 Module1 是一個更複雜的 module,有多個 output port,但是我們只想測試其中一個 port 的話,就必須寫額外的很多 FIFO,把所有的 port 都接上才能使用,造成不少麻煩。因此,我才會認為 module 需要提供「可以選擇是否使用 output FIFO」的選擇權。這只需要一點小小的改動,把所有 sc_fifo_out 改成 sc_fifo,並加上「是否使用 output FIFO」的 flag:

 1struct Module1: public class sc_module {
 2   sc_fifo_in<int> data_in_;
 3   bool data_out_enabled_ = false;
 4   sc_fifo<int> data_out_;
 5   void ScThread() {
 6      int count;
 7      while (count = data_in_.read())
 8         for (int i = 0; i < count; ++i)
 9            if (data_out_enabled)
10               data_out_.write(i);
11   }
12};

用 Accessor 自動開啟 Output Port

一般來說,當有人呼叫 FIFO 的時候,就是這個 FIFO 有被用到的時機。因此,合理的作法可以透過 accessor 來自動決定要不要 enable output FIFO,節省使用者手動修改 enable flag 的需要。

 1struct Module1: public class sc_module {
 2private:
 3   sc_fifo_in<int> data_in_;
 4   bool data_out_enabled_ = false;
 5   sc_fifo<int> data_out_;
 6public:
 7   sc_fifo<int>& get_data_out() {
 8      data_out_enabled_ = true;
 9      return data_out_;
10   }
11};