iT邦幫忙

0

[gem5][minor] TimeBuffer 以及 Latch

  • 分享至 

  • xImage
  •  

系列文章 : [gem5] 從零開始的 gem5 學習筆記

關於 TimeBuffer 以及 Latch,參考連結 的文章已經寫的很詳盡了,
這邊簡單做些筆記。

TimeBuffer

TimeBuffer 是一個 Buffer,裡面總共會有 past + future + 1 個元素。

  • past 代表了,你想要記錄過去幾筆資料。
  • future 代表了,你想要記錄未來幾筆資料。
  • past + future + 1 的 +1,代表的正是當下的資料
  • base 是一個索引直, 指向 “當下” 的資料
    因為一開始時間都沒有推進,所以預設為 0

在建構子裡面可以看到,這邊用了 C++ 的 placement new。也就是,當我們 new 一個 class 的時候,不是malloc 一樣在 heap section 裡面拿記憶體,而是在我們的 data 指標指向的記憶體裡面去 new class。

雖然 data 的型態是 char *, 但這裡實際上會用 placement new 的方式把這一個空間變成 class T 的陣列。

然後 index[i] 則是指向的 i 個 class T 元素。

{ gem5/src/cpu/timebuf.hh }

template <class T>
class TimeBuffer
{
  protected:
    int past;
    int future;
    unsigned size;
    int _id;

    char *data;
    std::vector<char *> index;
    unsigned base;
    TimeBuffer(int p, int f)
        : past(p), future(f), size(past + future + 1),
          data(new char[size * sizeof(T)]), index(size), base(0)
    {
        assert(past >= 0 && future >= 0);
        char *ptr = data;
        for (unsigned i = 0; i < size; i++) {
            index[i] = ptr;
            std::memset(ptr, 0, sizeof(T));
            new (ptr) T;
            ptr += sizeof(T);
        }

        _id = -1;
    }
  protected:
    //Calculate the index into this->index for element at position idx
    //relative to now
    inline int calculateVectorIndex(int idx) const
    {
        //Need more complex math here to calculate index.
        valid(idx);

        int vector_index = idx + base;
        if (vector_index >= (int)size) {
            vector_index -= size;
        } else if (vector_index < 0) {
            vector_index += size;
        }

        return vector_index;
    }


TimeBuff/advance

advance 的時候,會將 base + 1
例如說原本 base0,那經過 advance 之後, base = 1
這代表, index 為 0 的資料,成為了過去的資料,而 index 為 1 的資料,從原本未來的資料變成現在的資料

最後會把最舊的資料用 placement new 的方式給重新初始化。

{ src/cpu/timebuf.hh }

    advance()
    {
        if (++base >= size)
            base = 0;

        int ptr = base + future;
        if (ptr >= (int)size)
            ptr -= size;
        (reinterpret_cast<T *>(index[ptr]))->~T();
        std::memset(index[ptr], 0, sizeof(T));
        new (index[ptr]) T;
    }


TimeBuff/wire

wire 的 index 對 TimeBuffer 而言,是相對於 base 而言的
所以可以看到當 wire 要取用 TimeBuffer 的資料的時候,會需要先用 calculateVectorIndex ,從 wire 的 index 值去計算出在 TimeBuffer 內部真正的索引值。

{ gem5/src/cpu/timebuf.hh }

        T &operator*() const { return *buffer->access(index); }
        T *operator->() const { return buffer->access(index); }
  public:
    T *access(int idx)
    {
        int vector_index = calculateVectorIndex(idx);

        return reinterpret_cast<T *>(index[vector_index]);
    }


Latch

而 Latch 是怎麼去使用 TimeBuffer 的呢 ? 這邊用 Latch<ForwardLineData> f1ToF2 為例子。

{ gem5/src/cpu/minor/pipeline.cc }

    f1ToF2(cpu.name() + ".f1ToF2", "lines",
        params.fetch1ToFetch2ForwardDelay),

{ gem5/src/cpu/minor/BaseMinorCPU.py }

    fetch1ToFetch2ForwardDelay = Param.Cycles(
        1, "Forward cycle delay from Fetch1 to Fetch2 (1 means next cycle)"
    )

{ gem5/src/cpu/minor/buffers.hh }

  public:
    /** forward/backwardDelay specify the delay from input to output in each
     *  direction.  These arguments *must* be >= 1 */
    Latch(const std::string &name,
        const std::string &data_name,
        Cycles delay_ = Cycles(1),
        bool report_backwards = false) :
        delay(delay_),
        buffer(name, data_name, delay_, 0, (report_backwards ? -delay_ : 0),
            (report_backwards ? 0 : -delay_))
    { }
  public:
    MinorBuffer(const std::string &name,
        const std::string &data_name,
        int num_past, int num_future,
        int report_left = -1, int report_right = -1) :
        Named(name), TimeBuffer<ElemType>(num_past, num_future),
        reportLeft(report_left), reportRight(report_right),
            dataName(data_name)
    { }

BaseMinorCPU.py,我們可以知道預設的 num_past 參數值為 1。

於是 TimeBuffer 裡面的 char *data 其實只會有兩個 class T 的大小。( 這邊的 class Tclass ForwardLineData )

  • past == 1
  • future == 0
  • total size == past + future + 1


這裡可以看到,f1ToF2.input() 會交給 fetch1 stage
而 f1ToF2.output() 會交給 fetch2 stage

{ gem5/src/cpu/minor/pipeline.cc }

    fetch2(cpu.name() + ".fetch2", cpu, params,
        f1ToF2.output(), eToF1.output(), f2ToF1.input(), f2ToD.input(),
        decode.inputBuffer),
    fetch1(cpu.name() + ".fetch1", cpu, params,
        eToF1.output(), f1ToF2.input(), f2ToF1.output(), fetch2.inputBuffer),


當我們呼叫 f1ToF2.input() 的時候,
會去呼叫 TimeBuffer.getWire(0)。

代表 f1ToF2.input() 永遠指向,TimeBuffer 內相對於 base ,偏差為 0 的位子。
簡而言之就是永遠指向 base,永遠指向當下的資料

當我們呼叫 f1ToF2.output() 的時候,
會去呼叫 TimeBuffer.getWire(-delay)。而這邊的 delay 用的是預設值 1,
會去呼叫 TimeBuffer.getWire(-1)。

代表 f1ToF2.output() 永遠指向,TimeBuffer 內相對於 base ,偏差為 -1 的位子。
簡而言之就是永遠指向過去的第一筆資料

{ gem5/src/cpu/minor/buffers.hh }

    class Input
    {
      public:
        typename Buffer::wire inputWire;

      public:
        Input(typename Buffer::wire input_wire) :
            inputWire(input_wire)
        { }
    };

    class Output
    {
      public:
        typename Buffer::wire outputWire;

      public:
        Output(typename Buffer::wire output_wire) :
            outputWire(output_wire)
        { }
    };

{ gem5/src/cpu/timebuf.hh }

    wire getWire(int idx)
    {
        valid(idx);

        return wire(this, idx);
    }

所以每當我們在 fetch1 向 Latch 丟任何一筆資料,都需要經過一拍之後
fetch2 才有辦法拿到 fetch1 給的資料。



參考連結


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言