在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

流水線中Half-Buffer與Skid-Buffer的使用

網絡交換FPGA ? 來源:網絡交換FPGA ? 2024-11-05 17:16 ? 次閱讀

1.問題描述

在介紹skid buffer之前,我們先來假設這樣一種情況,在一個多級流水模型之中,比如最為經典的順序五級流水的處理器模型中,各級之間通過僅通過valid-ready的握手信號進行數據傳遞,(需要注意的是,這里的輸入側和輸出側的握手信號是不建議直連的,這樣不符合流水設計思想的同時,還會加中時序壓力)當其中某級發生阻塞的時候,比如lsu的執行訪存指令,但是cache未命中,需要從更下級的儲存器去請求數據的時候,此時需要通過握手信號來需要阻塞流水線,理所應當的,我們拉低lsu的input_ready信號來阻塞來自上級流水的輸入(比如EXU),可是問題是此時上上級(比如IDU)并未被阻塞,還在向上級(EXU)傳輸數據,同樣的情況發生在所有的上游模塊。這篇文章便是用來解決上述問題。

a2b84bf6-90b3-11ef-a511-92fbcf53809c.png

2.Half-Buffer

2.1 Half-Buffer是什么?

引發上述問題的原因是未能及時阻塞之前的流水線,再深究其原因,是因為其輸入側和輸出側的握手允許在相同時鐘周期完成,所以阻塞的信息沒有同步到上級。為了解決以上問題,我們現在為流水線每級做如下限定:

1.輸入側和輸出側不能同時完成握手操作。

2.在沒有有效數據之前之前只能進行輸入握手,在內部有有效數據后,只能做輸出握手,在完成握手后才能重新開始輸入。

而這種方法叫做Half-Buffer,他內部只有一個buffer來緩存數據,所以他不支持輸入和輸出側同時完成握手。他的缺點是顯而易見的,每次啟動或停止的時候需要兩個時鐘周期的同時,還讓最大帶寬減半。但是,對于內部需要多個時鐘周期來計算結果的模塊而言,其影響并沒有那么大。

2.2 Half-Buffer源碼分析

這里我們選取fpgacpu網站上的源碼進行講解,網址會帖在文末。

首先是接口部分,需要注意的是,此處的CIRCULAR_BUFFER部分非0 時候,是允許內部有效數據在未完成輸出側握手的情況下接受新數據對原有數據進行覆蓋的。因為這種模式我們使用不多,這里現不做介紹。

`default_nettype none
 
module Pipeline_Half_Buffer
#(
    parameter WORD_WIDTH            = 0,
    parameter CIRCULAR_BUFFER       = 0     // non-zero to enable
)
(
    input  wire                     clock,
    input  wire                     clear,
 
    input  wire                     input_valid,
    output reg                      input_ready,
    input  wire [WORD_WIDTH-1:0]    input_data,
 
    output reg                      output_valid,
    input  wire                     output_ready,
    output wire [WORD_WIDTH-1:0]    output_data
);
 
    localparam WORD_ZERO = {WORD_WIDTH{1'b0}};

這部分是half_buffer部分,可以看到其內部只有一個buffer用來儲存數據:

    reg half_buffer_load = 1'b0;
 
    Register
    #(
        .WORD_WIDTH     (WORD_WIDTH),
        .RESET_VALUE    (WORD_ZERO)
    )
    half_buffer
    (
        .clock          (clock),
        .clock_enable   (half_buffer_load),
        .clear          (clear),
        .data_in        (input_data),
        .data_out       (output_data)
    );

空滿信號的產生模塊:

    reg  set_to_empty = 1'b0;
    reg  set_to_full  = 1'b0;
    wire buffer_full;
 
    Register
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    empty_full
    (
        .clock          (clock),
        .clock_enable   (set_to_full),
        .clear          (set_to_empty),
        .data_in        (1'b1),
        .data_out       (buffer_full)
    );

然后是最為重要的邏輯模塊,我們可以發現,在非循環模式下,input_ready和output_valid是互斥的,這也就完成了我們之前所說的每次只能完成一邊的握手。
在完成輸入握手之后將full信號拉高,并將數據寫入buffer,在完成輸出握手之后,將empty信號拉高。同時我們看到,在初始情況下,內部為empty,所以必須先完成empty->full->empty這個流程,這與我們預期相符。

 always @(*) begin
        input_ready      = (buffer_full   == 1'b0) || (CIRCULAR_BUFFER != 0);
        output_valid     = (buffer_full   == 1'b1);
        set_to_full      = (input_valid   == 1'b1) && (input_ready  == 1'b1);
        set_to_empty     = (output_valid  == 1'b1) && (output_ready == 1'b1) && (set_to_full == 1'b0);
        set_to_empty     = (set_to_empty  == 1'b1) || (clear == 1'b1);
        half_buffer_load = (set_to_full   == 1'b1);
    end
 
endmodule

3.Skid Buffer

3.1 Skid Buffer是什么?

那么有沒有其他方法能夠解決問題的同時,避免到Half-Buffer帶來的損耗呢?如果輸入輸出同時允許握手帶來的后果是可能在阻塞的情況下沖刷掉內部的有效數據,那么如果我們讓內部不止一個Buffer是不可以解決這個問題呢?

Skid Buffer就是這么來的,它其實是是一個最小的FIFO,深度為2,一個用于輸出,一個用來緩存,同時在緩存的這個周期,就能將下一級的阻塞信號傳遞到上級,這樣便可以在允許兩次同時握手,消除Half-Buffer帶來的兩個周期和最大帶寬的損耗的同時,擁有更好的布局布線空間。

3.2 Skid Buffer源碼分析

這里我們同樣選取fpgacpu網站上的源碼進行講解(ps:這個真的是最近發現的最寶藏的網站,之后如果有時間,可以會出一個專門介紹和解析這個網站源碼的一個專欄)

首先是接口部分,需要注意的是,此處的CIRCULAR_BUFFER部分非0 時候,是指可以在內部數據已經滿的情況下,進行覆蓋,同理,我們對該模式不做解析。

`default_nettype none
 
module Pipeline_Skid_Buffer
#(
    parameter WORD_WIDTH                = 0,
    parameter CIRCULAR_BUFFER           = 0     // non-zero to enable
)
(
    input   wire                        clock,
    input   wire                        clear,
 
    input   wire                        input_valid,
    output  wire                        input_ready,
    input   wire    [WORD_WIDTH-1:0]    input_data,
 
    output  wire                        output_valid,
    input   wire                        output_ready,
    output  wire    [WORD_WIDTH-1:0]    output_data
);
 
    localparam WORD_ZERO = {WORD_WIDTH{1'b0}};

然后是數據部分,我們可以清楚地看到,此處使用了兩個Buffer,data_buffer_out為緩存buffer,output_Data為輸出的數據,通過2mux1來決定輸出來源于緩存還是input_data。他這個地方還有個聰明之處在于他將數據通路和狀態解耦,這樣大大的便捷了整體的設計,是一個值得學習的地方。

 reg                     data_buffer_wren = 1'b0; // EMPTY at start, so don't load.
    wire [WORD_WIDTH-1:0]   data_buffer_out;
 
    Register
    #(
        .WORD_WIDTH     (WORD_WIDTH),
        .RESET_VALUE    (WORD_ZERO)
    )
    data_buffer_reg
    (
        .clock          (clock),
        .clock_enable   (data_buffer_wren),
        .clear          (clear),
        .data_in        (input_data),
        .data_out       (data_buffer_out)
    );
 
    reg                     data_out_wren       = 1'b1; // EMPTY at start, so accept data.
    reg                     use_buffered_data   = 1'b0;
    reg [WORD_WIDTH-1:0]    selected_data       = WORD_ZERO;
 
    always @(*) begin
        selected_data = (use_buffered_data == 1'b1) ? data_buffer_out : input_data;
    end
    Register
    #(
        .WORD_WIDTH     (WORD_WIDTH),
        .RESET_VALUE    (WORD_ZERO)
    )
    data_out_reg
    (
        .clock          (clock),
        .clock_enable   (data_out_wren),
        .clear          (clear),
        .data_in        (selected_data),
        .data_out       (output_data)
    );

接下來是最為重要的控制部分,首先我們先來將系統劃分為以下幾個狀態:

a2deb5a2-90b3-11ef-a511-92fbcf53809c.png

Empty:輸出和緩存區都沒有數據。

Busy :在輸出寄存器有一個有效值待處理,緩存區為空。

Full : 輸出寄存器和緩存區都有有效數據待處理 。

需要注意的是,在Empty下,只支持輸入側的握手,在Full模式下,只支持輸出側的握手,這樣可以有效防止數據的覆蓋和重復讀取。

我們來看一下每個狀態之間的轉換條件:

load:緩存區和輸出寄存器為空,數據直接載入輸出寄存器。(輸入握手,輸出沒握手)

fill:輸出寄存器為空,將數據載入緩存區。(輸入握手,輸出沒握手)

flow:輸出寄存器的值被下級接收的同時,將輸入的數據載入到輸出寄存器。(輸入輸出同時握手)

flush:輸出寄存器的值被下級接受,將緩存區的有效數據載入輸出寄存器(輸入沒握手,輸出握手)

unload:輸出寄存器的被下級接受,現在輸出和緩存區都為空。(輸入沒握手,輸出握手)。

在得到所有的轉化條件之后,我們還需要去決定輸入的ready和輸出valid信號。我們只需要在當前非滿時拉高ready信號,在當前非空的時候拉高valid信號即可。

    Register
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b1) // EMPTY at start, so accept data
    )
    input_ready_reg
    (
        .clock          (clock),
        .clock_enable   (1'b1),
        .clear          (clear),
        .data_in        ((state_next != FULL) || (CIRCULAR_BUFFER != 0)),
        .data_out       (input_ready)
    );
 
    Register
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    output_valid_reg
    (
        .clock          (clock),
        .clock_enable   (1'b1),
        .clear          (clear),
        .data_in        (state_next != EMPTY),
        .data_out       (output_valid)
    );
 

然后,在輸入握手時插入數據,在輸出握手時移除數據:

    reg insert = 1'b0;
    reg remove = 1'b0;
 
    always @(*) begin
        insert = (input_valid  == 1'b1) && (input_ready  == 1'b1);
        remove = (output_valid == 1'b1) && (output_ready == 1'b1);
    end

最后便是狀態的轉化和數據通路的選擇部分,在此不做贅述。

    reg load    = 1'b0; // Empty datapath inserts data into output register.
    reg flow    = 1'b0; // New inserted data into output register as the old data is removed.
    reg fill    = 1'b0; // New inserted data into buffer register. Data not removed from output register.
    reg flush   = 1'b0; // Move data from buffer register into output register. Remove old data. No new data inserted.
    reg unload  = 1'b0; // Remove data from output register, leaving the datapath empty.
    reg dump    = 1'b0; // New inserted data into buffer register. Move data from buffer register into output register. Discard old output data. (CBM)
    reg pass    = 1'b0; // New inserted data into buffer register. Move data from buffer register into output register. Remove old output data.  (CBM)
    always @(*) begin
        load    = (state == EMPTY) && (insert == 1'b1) && (remove == 1'b0);
        flow    = (state == BUSY)  && (insert == 1'b1) && (remove == 1'b1);
        fill    = (state == BUSY)  && (insert == 1'b1) && (remove == 1'b0);
        unload  = (state == BUSY)  && (insert == 1'b0) && (remove == 1'b1);
        flush   = (state == FULL)  && (insert == 1'b0) && (remove == 1'b1);
        dump    = (state == FULL)  && (insert == 1'b1) && (remove == 1'b0) && (CIRCULAR_BUFFER != 0);
        pass    = (state == FULL)  && (insert == 1'b1) && (remove == 1'b1) && (CIRCULAR_BUFFER != 0);
    end
    always @(*) begin
        data_out_wren     = (load  == 1'b1) || (flow == 1'b1) || (flush == 1'b1) || (dump == 1'b1) || (pass == 1'b1);
        data_buffer_wren  = (fill  == 1'b1)                                      || (dump == 1'b1) || (pass == 1'b1);
        use_buffered_data = (flush == 1'b1)                                      || (dump == 1'b1) || (pass == 1'b1);
    end
endmodule

4.剛玉中的流水代碼分析

在開源代碼剛玉中大量運用了流水線,我們以其為例子進行分析。我們以其axi_register_rd中對于ar port的流水處理進行分析。

剛玉采用了三種可選方式,bypass,Half-Buffer以及Skid-Buffer。我們針對其后兩種進行分析。需要說明的是,其中s_axi為輸入側,m_axi為輸出側。ps:剛玉的作者Alex的代碼水平真的十分高,他經常用一些互斥條件的組合來代替狀態機的書寫,所以對我來說想要理解往往需要花費一定的時間。

4.1 剛玉中的Half-Buffer

// enable ready input next cycle if output buffer will be empty
wire s_axi_arready_early = !m_axi_arvalid_next;
 
always @* begin
    // transfer sink ready state to source
    m_axi_arvalid_next = m_axi_arvalid_reg;
 
    store_axi_ar_input_to_output = 1'b0;
    if (s_axi_arready_reg) begin
        m_axi_arvalid_next = s_axi_arvalid;
        store_axi_ar_input_to_output = 1'b1;
    end else if (m_axi_arready) begin
        m_axi_arvalid_next = 1'b0;
    end
end

我們可以看到,只有在輸出側在下一拍為低的時候,才拉高輸入側的ready信號,保證每一拍只有一側的握手是可以完成的。

然后在輸入側ready的情況下,將上一級的有效信號傳遞到輸出寄存器,這里比較有意思的是,他沒有等到輸入握手成功再傳遞,而是直接傳遞,這是因為輸入側的ready和輸出側的valid是互斥的,即使沒有握手就傳遞,也不會出現兩邊同時握手的情況。

如果輸入側的ready無效,但是輸入側的ready有效時,將下一拍的輸出側的有效信號拉低,我當初看到這里很疑惑,后來一想其實很簡單,因為輸入側的ready無效就意味著當前拍的輸出側valid肯定是拉高的,這句話其實可以理解成完成輸出側握手后,將已經處理過的有效信號拉低的操作。

4.2 剛玉中的Skid-Buffer

wire s_axi_arready_early = m_axi_arready | (~temp_m_axi_arvalid_reg & (~m_axi_arvalid_reg | ~s_axi_arvalid));
 
always @* begin
    // transfer sink ready state to source
    m_axi_arvalid_next = m_axi_arvalid_reg;
    temp_m_axi_arvalid_next = temp_m_axi_arvalid_reg;
 
    store_axi_ar_input_to_output = 1'b0;
    store_axi_ar_input_to_temp = 1'b0;
    store_axi_ar_temp_to_output = 1'b0;
    if (s_axi_arready_reg) begin
        // input is ready
        if (m_axi_arready | ~m_axi_arvalid_reg) begin
            // output is ready or currently not valid, transfer data to output
            m_axi_arvalid_next = s_axi_arvalid;
            store_axi_ar_input_to_output = 1'b1;
        end else begin
            // output is not ready, store input in temp
            temp_m_axi_arvalid_next = s_axi_arvalid;
            store_axi_ar_input_to_temp = 1'b1;
        end
    end else if (m_axi_arready) begin
        // input is not ready, but output is ready
        m_axi_arvalid_next = temp_m_axi_arvalid_reg;
        temp_m_axi_arvalid_next = 1'b0;
        store_axi_ar_temp_to_output = 1'b1;
    end
end

首先還是先來分析輸入側的ready信號,可以看到,他拉高的條件有兩個,首先是輸入側的ready為高,這是為什么?我們來簡單分析一下,當輸出側的ready為高的時候,他的輸出寄存器主要有效,那么一定會被讀取,所以當前狀態永遠不會是full,所以可以拉高。

第二個條件:

(~temp_m_axi_arvalid_reg & (~m_axi_arvalid_reg | ~s_axi_arvalid))

我們來解析一下,首先他要求緩存寄存器為空的同時,輸入側和輸出寄存器不能同時有待處理的請求,這個也很好理解,我們這個系統最大的待處理請求只能是兩個,如果不滿足以上條件,那么系統中可能會出現待處理請求,緩存區的請求有被覆蓋的風險。

    if (s_axi_arready_reg) begin
        // input is ready
        if (m_axi_arready | ~m_axi_arvalid_reg) begin
            // output is ready or currently not valid, transfer data to output
            m_axi_arvalid_next = s_axi_arvalid;
            store_axi_ar_input_to_output = 1'b1;
        end else begin
            // output is not ready, store input in temp
            temp_m_axi_arvalid_next = s_axi_arvalid;
            store_axi_ar_input_to_temp = 1'b1;
        end

然后就是接下來的部分,我們看到,在輸入側ready的情況下,如果輸出側ready有效或者沒有待處理的請求時,可以將新的請求從輸入加載到輸出寄存器。又是很奇怪是不是?這里真的感嘆一句Alex的水平之高,好了,我們來認真分析一下,如果輸出側ready有效,那意味著當前狀態不為full,那么任何被傳遞的請求都是可以被下級處理的,同理,如果下級已經沒有待處理的請求,那么自然可以加載新的有效請求。然后,如果下級不能處理新的請求的時候,也就是對應我們之前的BUSY狀態下,可以完成輸入側握手,不能完成輸出側握手的時候,我們就需要把輸入側的請求存入緩存區。

    end else if (m_axi_arready) begin
        // input is not ready, but output is ready
        m_axi_arvalid_next = temp_m_axi_arvalid_reg;
        temp_m_axi_arvalid_next = 1'b0;
        store_axi_ar_temp_to_output = 1'b1;
    end

最后,便是輸出側可以完成握手,但是輸入側不能完成的時候,對應之前的flush狀態,輸出寄存器被下級讀取之后,我們把緩存區的數據載入到輸出寄存器即可。

5.結語

文章主要分析了流水線中的Half-Buffer與Skid-Buffer的使用,之后如果有機會,將繼續分享更多DE技巧。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19286

    瀏覽量

    229843
  • 流水線
    +關注

    關注

    0

    文章

    120

    瀏覽量

    25739
  • 模型
    +關注

    關注

    1

    文章

    3243

    瀏覽量

    48840

原文標題:Half-Buffer與Skid-Buffer介紹及其在流水線中的應用

文章出處:【微信號:gh_cb8502189068,微信公眾號:網絡交換FPGA】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    FPGA流水線設計

    處理速度)。第二 什么時候用流水線設計使用流水線一般是時序比較緊張,對電路工作頻率較高的時候。典型情況如下:1)功能模塊之間的流水線,用乒乓 buffer 來交互數據。代價是增加了 m
    發表于 10-26 14:38

    現代RISC流水線技術

    流水線技術是提高系統吞吐率的一項強大的實現技術,并且不需要大量重復設置硬件。20世界60年代早期的一些高端機器第一次采用了流水線技術。第一個采用指令流水線的機器是IBM7030(又稱
    發表于 03-01 17:52

    周期精確的流水線仿真模型

    使用軟件仿真硬件流水線是很耗時又復雜的工作,仿真過程由于流水線的沖突而導致運行速度緩慢。本文通過對嵌入式處理器的流水線, 指令集, 設備控制器等內部結構的分析和
    發表于 12-31 11:30 ?9次下載

    什么是流水線技術

    什么是流水線技術 流水線技術
    發表于 02-04 10:21 ?3927次閱讀

    流水線的相關培訓教程[1]

    流水線的相關培訓教程[1]  學習目標     理解流水線相關的分類及定義;
    發表于 04-13 15:56 ?1052次閱讀

    流水線的相關培訓教程[3]

    流水線的相關培訓教程[3] (1) 寫后讀相關(RAW:Read After Write) (命名規則) :j 的執行要用到 i 的計算結果,當它們在流水線重疊執行時,j 可
    發表于 04-13 16:02 ?845次閱讀

    流水線的相關培訓教程[4]

    流水線的相關培訓教程[4] 下面討論如何利用編譯器技術來減少這種必須的暫停,然后論述如何在流水線實現數據相關檢測和定向。
    發表于 04-13 16:09 ?4805次閱讀

    電鍍流水線的PLC控制

    電鍍流水線的PLC控制電鍍流水線的PLC控制電鍍流水線的PLC控制
    發表于 02-17 17:13 ?36次下載

    采用單通道通訊協議設計高速異步流水線控制器STFB電路的設計

    單元GasP電路,在文中提出的準延時無關QDI異步流水線控制單元WCHB(weak condition half buffer)、PCHB(precharged fullbuffer),以及在文中提出的基于單通道通訊協議的QDI
    的頭像 發表于 08-30 08:04 ?2749次閱讀
    采用單通道通訊協議設計高速異步<b class='flag-5'>流水線</b>控制器STFB電路的設計

    各種流水線特點及常見流水線設計方式

    按照流水線的輸送方式大體可以分為:皮帶流水裝配線、板鏈線、倍速鏈、插件線、網帶線、懸掛線及滾筒流水線這七類流水線
    的頭像 發表于 07-05 11:12 ?7354次閱讀
    各種<b class='flag-5'>流水線</b>特點及常見<b class='flag-5'>流水線</b>設計方式

    滾筒輸流水線故障排除方法

    在工程建造,滾筒流水線演著重要的角色。在一些工程建造過程,經常看到滾筒流水線的身影。在工業不斷發展下的今天,滾筒流水線日益增長,走向多元
    發表于 07-08 09:32 ?1909次閱讀

    如何選擇合適的LED生產流水線輸送方式

    LED生產流水線輸送形式分為平面直線傳輸流水線、各種角度平面轉彎傳輸流水線、斜面上傳流水線、斜面下傳流水線這四種輸送方式,企業也是可以根據L
    發表于 08-06 11:53 ?1028次閱讀

    嵌入式_流水線

    ,每個子過程由專門的功能部件來實現。? 把多個處理過程在時間上錯開,依次通過各功能段,這樣,每個子過程就可以與其他的子過程并行進行。流水線的每個子過程及其功能部件稱為流水線的級或段,段與段相互連接形成
    發表于 10-20 20:51 ?6次下載
    嵌入式_<b class='flag-5'>流水線</b>

    CPU流水線的問題

    1989 年推出的 i486 處理器引入了五級流水線。這時,在 CPU 不再僅運行一條指令,每一級流水線在同一時刻都運行著不同的指令。這個設計使得 i486 比同頻率的 386 處理器性能提升了不止一倍。
    的頭像 發表于 09-22 10:04 ?2008次閱讀

    什么是流水線 Jenkins的流水線詳解

    jenkins 有 2 種流水線分為聲明式流水線與腳本化流水線,腳本化流水線是 jenkins 舊版本使用的流水線腳本,新版本 Jenkin
    發表于 05-17 16:57 ?1086次閱讀
    主站蜘蛛池模板: 亚洲天堂资源| 三级在线免费| 美女中出视频| 忘忧草爱网| 中文字幕在线一区二区在线| 四虎影院永久地址| 99久久综合| 色涩网站在线观看| 四虎电影院| 4tube高清性欧美| 俺也来国产精品欧美在线观看| 日日夜夜爽| 免费观看做网站爱| 萌白酱香蕉白丝护士服喷浆 | 综合五月婷婷| 久在草影院| 97伊人网| 在线a亚洲老鸭窝天堂新地址| 亚洲综合狠狠| 国产福利在线观看你懂的| 亚洲人成网站色7799在线播放| 天堂影院在线| 午夜视频在线观看www中文| 日韩一卡2卡三卡4卡无卡网站| 欧美高清成人videosex| 在线观看色视频网站| 69xx女xo69| 美女视频永久黄网站免费观看国产| 欧美456| 亚洲美女视频在线观看| 94在线| 免费播放欧美毛片欧美aaaaa| 狠狠狠狠狠狠狠狠| 视频免费播放| 农村三级毛片| 国产第一页在线观看| 色多多免费观看| 久久香蕉精品视频| 色噜噜狠狠成人中文小说| 六月婷婷色| 国产精品天天看天天爽|