FPGA基礎(chǔ)知識極簡教程(4)從FIFO設(shè)計講起之異步FIFO篇


寫在前面

一開始是想既然是極簡教程,就應(yīng)該只給出FIFO的概念巍虫,沒想到還是給出了同步以及異步FIFO的設(shè)計,要不然總感覺內(nèi)容不完整毁葱,也好垫言,自己設(shè)計的FIFO模塊不用去擔(dān)心因IP核跨平臺不通用的缺陷!那我們開始吧倾剿。

  • 個人微信公眾號: FPGA LAB
  • 個人博客首頁
  • 注:學(xué)習(xí)交流使用筷频!

正文

同步FIFO回顧

上一篇博客講了同步FIFO的概念以及同步FIFO的設(shè)計問題,并給出了同步FIFO的Verilog代碼以及VHDL代碼前痘,并經(jīng)過了行為仿真測試凛捏,鏈接如下:

FPGA基礎(chǔ)知識極簡教程(3)從FIFO設(shè)計講起之同步FIFO篇

$clog2()系統(tǒng)函數(shù)使用

這里簡單提一下,同步FIFO的代碼中用到了一個系統(tǒng)函數(shù)$clog2()芹缔,這個系統(tǒng)函數(shù)的使用方法很簡單:

parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 8;

reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;
reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;

例如我定義了FIFO緩沖區(qū)的深度為DATA_DEPTH = 8坯癣,那么其地址(指針)位寬是多少呢?
這時候就可以使用系統(tǒng)函數(shù)$clog2()了最欠,位寬可以表示為:

$clog2(DATA_DEPTH)                  // = 3;

指針就可以定義為:

reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;
reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;

綜合屬性控制資源使用

還有一點需要提的是示罗,我們都知道在FPGA中FIFO的實現(xiàn)可以使用分布式資源或者BLOCK RAM惩猫,那么如何掌控呢?
當(dāng)使用FIFO緩沖空間較小時蚜点,我們選擇使用Distributed RAM轧房;當(dāng)使用FIFO緩沖空間較大時,我們選擇使用BLOCK RAM資源绍绘;這是一般的選擇原則奶镶。
我們可以通過在設(shè)計代碼中加入約束條件來控制,之前有寫過

Vivado 隨筆(1) 綜合屬性之 ram_style & rom_style?

就上述同步FIFO而言陪拘,我們可以在緩沖區(qū)定義時候添加如下約束:

(*ram_style = "distributed"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

或者:

(*ram_style = "block"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

為了驗證是否有用厂镇,我們在Vivado中進(jìn)行驗證如下:

當(dāng)設(shè)計中使用BLOCK RAM約束:

(*ram_style = "block"*)reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

綜合后的電路圖如下,可見FIFO緩存區(qū)使用的資源為BLOCK RAM左刽;


綜合后的電路圖

同時給出資源利用率報告:

資源利用情況

可見存在BLOCK RAM 捺信,由于我僅僅綜合了一個同步FIFO,因此這個Block RAM一定是FIFO緩沖區(qū)消耗的欠痴。

當(dāng)使用Distributed RAM約束時:

(*ram_style = "distributed"*)reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

綜合后電路圖FIFO緩沖區(qū)部分:

綜合后的電路圖

資源利用率情況残黑;


資源利用情況

可見 ,并未使用BLOCK RAM斋否,而是使用了LUT RAM梨水,也即分布式RAM。
綜上茵臭,驗證了這條約束的有效性疫诽。

異步FIFO設(shè)計

FIFO用途回顧

再設(shè)計異步FIFO電路之前,有必要說明一下FIFO的用途旦委,上篇博文提到:

  • 跨時鐘域

FPGA或者ASIC設(shè)計內(nèi)部電路多位數(shù)據(jù)在不同的時鐘域交互奇徒,為了數(shù)據(jù)安全、正確缨硝、穩(wěn)定交互摩钙,我們需要設(shè)計異步FIFO進(jìn)行跨時鐘域交互。正如之前博客所寫:漫談時序設(shè)計(1)跨時鐘域是設(shè)計出來的查辩,而非約束出來的胖笛!
我們在時序分析時候,通常都將跨時鐘域路徑進(jìn)行偽路徑約束宜岛,因此我們必須在設(shè)計時候解決跨時鐘域數(shù)據(jù)傳輸問題长踊,異步FIFO在此起到關(guān)鍵作用。

  • 在將數(shù)據(jù)發(fā)送到芯片外之前將其緩沖(例如萍倡,發(fā)送到DRAM或SRAM)
  • 緩沖數(shù)據(jù)以供軟件在以后查看
  • 存儲數(shù)據(jù)以備后用

這三條大概講的都是一個意思身弊,總結(jié)起來就是FIFO可以起到數(shù)據(jù)緩沖或緩存的作用,例如突然數(shù)據(jù),我們就需要先將其緩存起來阱佛,之后再從FIFO中讀出出來進(jìn)行處理帖汞,這樣也可以保證數(shù)據(jù)不會丟失。

引用互聯(lián)網(wǎng)上其他說法就是:數(shù)據(jù)寫入過快凑术,并且間隔時間長涨冀,也就是突發(fā)寫入。那么通過設(shè)置一定深度的FIFO麦萤,可以起到數(shù)據(jù)暫存的功能,且使得后續(xù)處理流程平滑扁眯。

異步FIFO原理回顧

無論是同步FIFO還是異步FIFO壮莹,其大致原理都是一致的,先入先出自然不必多說姻檀,關(guān)于空滿的判斷都是通過讀寫指針之間的關(guān)系來判斷命满;還有就是異步FIFO的指針需要進(jìn)行一定的處理,例如格雷碼處理绣版,這樣可以減小讀指針同步到寫指針時鐘域胶台,或者寫指針同步到讀指針時鐘域時出現(xiàn)亞穩(wěn)態(tài)的概率,這是因為格雷碼每次只有一位變化杂抽,這樣一位數(shù)據(jù)在進(jìn)行跨時鐘域傳輸?shù)臅r候亞穩(wěn)態(tài)出現(xiàn)的概率會大大減小诈唬。同步之后便進(jìn)行對比,以此來判斷FIFO的空滿缩麸。
那異步FIFO如何判斷空滿呢铸磅?
回答這個問題之前,我想先統(tǒng)一的說明FIFO(同步或者異步)是如何判斷空滿的杭朱?


同步FIFO空滿判斷示意框圖

起始阅仔,讀寫指針都是0,F(xiàn)IFO一定為空弧械;之后對FIFO進(jìn)行一系列的讀寫操作八酒,導(dǎo)致讀寫指針關(guān)系發(fā)生了變化,可以分為下面兩種情況:

  1. 讀比寫要快刃唐,或者說讀指針追寫指針羞迷,如果追上了,也即二者再次相等画饥,則FIFO讀空闭树;
  2. 寫比讀快,或者說寫指針彎道超越追讀指針荒澡,當(dāng)寫指針再次繞到讀指針背后并與讀指針重合报辱,也即二者相等時,F(xiàn)IFO寫滿!

在同步FIFO中碍现,我們使用計數(shù)的方式進(jìn)行判斷空滿幅疼,運用的也是這個原理,寫一個數(shù)據(jù)時昼接,計數(shù)值加1爽篷,讀出一個數(shù)據(jù)時,計數(shù)值減1慢睡,如下圖:

FIFO存取數(shù)據(jù)原理圖

我最喜歡用這幅圖來分析FIFO逐工,下面一行一行的分析:

  1. 第一行:寫入1個數(shù)據(jù),計數(shù)值為1漂辐;
  2. 第二行:寫入5個數(shù)據(jù)泪喊,計數(shù)值為6;
  3. 第三行:讀出3個數(shù)據(jù)髓涯,計數(shù)值為3袒啼;
  4. 第四行:寫入3個數(shù)據(jù),計數(shù)值為6纬纪;
  5. 第五行:寫入2個數(shù)據(jù)蚓再,計數(shù)值為8,等于FIFO深度包各,則表示寫滿摘仅;
  6. 第六行:讀出6個數(shù)據(jù),計數(shù)值為2问畅,表示還剩下兩個數(shù)據(jù)緩存在FIFO中实檀。

如果再接著讀2個 數(shù)據(jù),則計數(shù)值為0按声,F(xiàn)IFO就被讀空了膳犹。

好了,我們分析完了同步FIFO是如何判斷空滿的签则,下面重點放在異步FIFO的原理上须床。

我曾寫過一篇CDC問題的博客,談到了異步FIFO的設(shè)計:
談?wù)効鐣r鐘域傳輸問題(CDC)

這篇博客中說渐裂,同步FIFO可以使用計數(shù)方式來判斷空滿豺旬,但是異步FIFO不能,因為寫指針和讀指針根本不在同一個時鐘域柒凉,計數(shù)器無法處理這樣的計數(shù)族阅。
那么怎么處理呢?
博客里采用的方法是對讀寫指針的位寬多添1位膝捞,這樣可以在讀寫指針相等時坦刀,表示FIFO空,而在寫指針和讀指針最高位不同,而其他位相等時鲤遥,也即寫指針大于讀指針一個FIFO深度的數(shù)值沐寺,表示FIFO滿,這不就是意味著寫指針繞了一圈盖奈,又追上了讀指針了嗎混坞?
恰是如此,用來解決不用計數(shù)而具體判斷FIFO空滿的問題钢坦。

這只是解決了判斷空滿的一個問題究孕,也就是確定指針的關(guān)系!
那下一個問題就是如何判斷爹凹?
由于讀寫指針不在同一個時鐘域厨诸,二者需要同步到同一個時鐘域后進(jìn)行判斷大小。


異步FIFO原理示意圖

具體的操作就是在各自的時鐘域內(nèi)進(jìn)行讀寫操作逛万,同時:

  1. 判斷是否寫滿時,需要將讀指針轉(zhuǎn)換成格雷碼形式批钠,再同步到寫時鐘域宇植,與寫指針比較,判斷是否寫滿埋心!
    細(xì)心的人恐怕能否發(fā)現(xiàn)指郁,這里存在的一個小插曲,當(dāng)讀指針轉(zhuǎn)換成格雷碼以及同步到寫時鐘域的過程中拷呆,讀寫指針可能還都在遞增闲坎,這樣的話,等同步后的讀指針與寫指針相等時(不包括最高位)茬斧,實際的讀指針可能已經(jīng)變了腰懂,這樣的話其實還有幾個空間沒有寫滿!但這樣設(shè)計就有問題嗎项秉?沒有問題绣溜!這叫保守設(shè)計,可以增加FIFO的安全性娄蔼。

下面是判斷是否寫滿的示意圖:


判斷是否寫滿的示意圖

上面是寫滿判斷的情況怖喻,下面給出讀空判斷的可能情形分析:

  1. 當(dāng)判斷是否讀空時,需要把寫指針同步到讀時鐘域岁诉,具體過程是先將寫指針轉(zhuǎn)換為格雷碼锚沸,再同步到讀時鐘域,之后和讀指針比較涕癣,如果二者相等哗蜈,則空標(biāo)志置位!
    還是和第一種情況有同樣的插曲,當(dāng)寫指針轉(zhuǎn)換成格雷碼以及同步到讀時鐘域的過程中恬叹,寫指針和讀指針都可能還在遞增候生,這樣當(dāng)二者判斷相等的時候,則寫指針可能還多寫了幾個空間绽昼,實際上并沒有讀空唯鸭。
    那問題來了,這樣操作就有問題了嗎硅确?同樣沒有問題目溉,這樣也保證來了FIFO的安全,防止被讀空菱农。

下面給出手繪示意圖:

判斷是否讀空的示意圖

到此缭付,這一種設(shè)計方式的異步FIFO算是講完了,下面就是設(shè)計的問題了循未。

異步FIFO設(shè)計

如果你認(rèn)真分析了上述異步FIFO的實現(xiàn)方式陷猫,那么你會分分鐘寫出實現(xiàn)代碼,我的版本如下:

`timescale 1ns / 1ps
////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Module Name: asy_fifo
// https://blog.csdn.net/Reborn_Lee
////////////////////////////////////////////////////


module asy_fifo#(
    parameter DATA_WIDTH = 8,
    parameter DATA_DEPTH = 32
    )(
    input wr_clk,
    input wr_rst,
    input wr_en,
    input [DATA_WIDTH - 1 : 0] wr_data,
    output reg full,

    input rd_clk,
    input rd_rst,
    input rd_en,
    output reg [DATA_WIDTH - 1 : 0] rd_data,
    output reg empty

    );


    // define FIFO buffer 
    reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];

    //define the write and read pointer and 
    //pay attention to the size of pointer which should be greater one to normal

    reg [$clog2(DATA_DEPTH) : 0] wr_pointer = 0, rd_pointer = 0; 

    //write data to fifo buffer and wr_pointer control
    always@(posedge wr_clk) begin
        if(wr_rst) begin
            wr_pointer <= 0;
        end
        else if(wr_en) begin
            wr_pointer <= wr_pointer + 1;
            fifo_buffer[wr_pointer] <= wr_data;
        end

    end

    //read data from fifo buffer and rd_pointer control
    always@(posedge rd_clk) begin
        if(rd_rst) begin
            rd_pointer <= 0;
        end
        else if(rd_en) begin
            rd_pointer <= rd_pointer + 1;
            rd_data <= fifo_buffer[rd_pointer];
        end

    end

    //wr_pointer and rd_pointer translate into gray code

    wire [$clog2(DATA_DEPTH) : 0] wr_ptr_g, rd_ptr_g; 

    assign wr_ptr_g = wr_pointer ^ (wr_pointer >>> 1);
    assign rd_ptr_g = rd_pointer ^ (rd_pointer >>> 1);



    //wr_pointer after gray coding synchronize into read clock region
    reg [$clog2(DATA_DEPTH) : 0] wr_ptr_gr, wr_ptr_grr, rd_ptr_gr, rd_ptr_grr; 

    always@(rd_clk) begin
        if(rd_rst) begin
            wr_ptr_gr <= 0;
            wr_ptr_grr <= 0;
        end
        else begin
            wr_ptr_gr <= wr_ptr_g;
            wr_ptr_grr <= wr_ptr_gr;
        end
    end


    //rd_pointer after gray coding synchronize into  write clock region
    always@(wr_clk) begin
        if(wr_rst) begin
            rd_ptr_gr <= 0;
            rd_ptr_grr <= 0;
        end
        else begin
            rd_ptr_gr <= rd_ptr_g;
            rd_ptr_grr <= rd_ptr_gr;
        end
    end

    // judge full or empty

    always@(posedge rd_clk) begin
        if(rd_rst) empty <= 0;
        else if(wr_ptr_grr == rd_ptr_g) begin
            empty <= 1;
        end
        else empty <= 0;
    end

    always@(posedge wr_clk) begin
        if(wr_rst) full <= 0;
        else if( (rd_ptr_grr[$clog2(DATA_DEPTH) - 1 : 0] == wr_ptr_g[$clog2(DATA_DEPTH) - 1 : 0])
            && ( rd_ptr_grr[$clog2(DATA_DEPTH)] != wr_ptr_g[$clog2(DATA_DEPTH)] ) ) begin
            full <= 1;
        end
        else full <= 0;
    end





endmodule

注意事項

  • 讀寫指針寬度要是$clog2(DATA_DEPTH) + 1的妖,定義的時候應(yīng)該定義為:
reg [$clog2(DATA_DEPTH) : 0] wr_pointer = 0, rd_pointer = 0; 
  • 其次绣檬,判斷空的時候要拿轉(zhuǎn)換為格雷碼并且同步到讀時鐘域之后的寫指針與讀指針比較,比較代碼如下:
always@(posedge rd_clk) begin
        if(rd_rst) empty <= 0;
        else if(wr_ptr_grr == rd_ptr_g) begin
            empty <= 1;
        end
        else empty <= 0;
    end

一定要二者相等的下一個讀周期empty信號為1嫂粟;

  • 對于滿full信號娇未,一定要用轉(zhuǎn)換為格雷碼且同步到寫時鐘域之后的讀指針與轉(zhuǎn)換為格雷碼之后的寫時鐘比較,比較的條件是最高位不同星虹,但是其他位相同零抬。
always@(posedge wr_clk) begin
        if(wr_rst) full <= 0;
        else if( (rd_ptr_grr[$clog2(DATA_DEPTH) - 1 : 0] == wr_ptr_g[$clog2(DATA_DEPTH) - 1 : 0])
            && ( rd_ptr_grr[$clog2(DATA_DEPTH)] != wr_ptr_g[$clog2(DATA_DEPTH)] ) ) begin
            full <= 1;
        end
        else full <= 0;
    end
  • 最后提出的是轉(zhuǎn)換為格雷碼的方式是組合邏輯的方式,即:
//wr_pointer and rd_pointer translate into gray code

    wire [$clog2(DATA_DEPTH) : 0] wr_ptr_g, rd_ptr_g; 

    assign wr_ptr_g = wr_pointer ^ (wr_pointer >>> 1);
    assign rd_ptr_g = rd_pointer ^ (rd_pointer >>> 1);

當(dāng)然你用時序邏輯也可以哦宽涌。

異步FIFO仿真

我們對上述設(shè)計進(jìn)行行為仿真平夜,先給出我的測試文件:


`timescale 1ns/1ps
module asy_fifo_tb;
    parameter DATA_WIDTH = 8;
    parameter DATA_DEPTH = 16;

    reg wr_clk;
    reg wr_rst;
    reg wr_en;
    reg [DATA_WIDTH - 1 : 0] wr_data;
    wire full;

    reg rd_clk;
    reg rd_rst;
    reg rd_en;
    wire [DATA_WIDTH - 1 : 0] rd_data;
    wire empty;

    initial begin
        wr_clk = 0;
        forever begin
            #5 wr_clk = ~wr_clk;
        end
    end

    initial begin
        rd_clk = 0;
        forever begin
            #10 rd_clk = ~rd_clk;
        end
    end

    initial begin
        wr_rst = 1;
        rd_rst = 1;
        wr_en = 0;
        rd_en = 0;
        #30 
        wr_rst = 0;
        rd_rst = 0;

        //write data into fifo buffer
        @(negedge wr_clk) 
        wr_data = $random;
        wr_en = 1;

        repeat(7) begin
            @(negedge wr_clk) 
            wr_data = $random; // write into fifo 8 datas in all;
        end

        // read parts
        @(negedge wr_clk) 
        wr_en = 0;

        @(negedge rd_clk) 
        rd_en = 1;

        repeat(7) begin
            @(negedge rd_clk);  // read empty 
        end 
        @(negedge rd_clk)
        rd_en = 0;

        //write full
        # 150

        @(negedge wr_clk)
        wr_en = 1;
        wr_data = $random;

        repeat(15) begin
        @(negedge wr_clk)
            wr_data = $random;
        end

        @(negedge wr_clk)
        wr_en = 0;


        #50 $finish;





    end




    asy_fifo #(
            .DATA_WIDTH(DATA_WIDTH),
            .DATA_DEPTH(DATA_DEPTH)
        ) inst_asy_fifo (
            .wr_clk  (wr_clk),
            .wr_rst  (wr_rst),
            .wr_en   (wr_en),
            .wr_data (wr_data),
            .full    (full),
            .rd_clk  (rd_clk),
            .rd_rst  (rd_rst),
            .rd_en   (rd_en),
            .rd_data (rd_data),
            .empty   (empty)
        );


endmodule

仿真波形為:


讀寫FIFO仿真波形

寫FIFO仿真波形

仿真通過,且功能符合預(yù)期卸亮。

由于本博客寫的時候有點長褥芒,幾乎一天了,所以就到這里吧嫡良!不得不說的是锰扶,異步FIFO的實現(xiàn)方式肯定不只有這一種,還有很多其他實現(xiàn)方式寝受,各位可以自行嘗試坷牛。
后面如果有更多有關(guān)FIFO的有趣知識或者心得體會,我會繼續(xù)補充很澄!


參考資料


交個朋友

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颜及,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹂楣,更是在濱河造成了極大的恐慌俏站,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痊土,死亡現(xiàn)場離奇詭異肄扎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赁酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門犯祠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酌呆,你說我怎么就攤上這事衡载。” “怎么了隙袁?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵痰娱,是天一觀的道長。 經(jīng)常有香客問我菩收,道長梨睁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任坛梁,我火速辦了婚禮而姐,結(jié)果婚禮上腊凶,老公的妹妹穿的比我還像新娘划咐。我一直安慰自己,他們只是感情好钧萍,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布褐缠。 她就那樣靜靜地躺著,像睡著了一般风瘦。 火紅的嫁衣襯著肌膚如雪队魏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天万搔,我揣著相機(jī)與錄音胡桨,去河邊找鬼。 笑死瞬雹,一個胖子當(dāng)著我的面吹牛昧谊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酗捌,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呢诬,長吁一口氣:“原來是場噩夢啊……” “哼涌哲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尚镰,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤阀圾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狗唉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體初烘,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年敞曹,在試婚紗的時候發(fā)現(xiàn)自己被綠了账月。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡澳迫,死狀恐怖局齿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橄登,我是刑警寧澤抓歼,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拢锹,受9級特大地震影響谣妻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卒稳,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一蹋半、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧充坑,春花似錦减江、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侵蒙。三九已至蓝牲,卻和暖如春赌莺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甜紫。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工降宅, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囚霸。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓腰根,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邮辽。 傳聞我的和親對象是個殘疾皇子唠雕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355