基于FPGA的PCI总线DMA逻辑

今天清理电脑,好久以前的VHDL逻辑,感觉以后都不会和FPGA有缘了,传git吧。
现在大部分PCI板卡都是基于PCI9054(日本产的一个芯片),对于一些板卡而言,没必要,第一9054芯片占用板卡面积,第二增加连线,第三9054的DMA速度大概在80-100MB/s,记得它好像每传32个字节要停一下.
虽然现在家用电脑很多都是PCIE了,但是一些老的测控设备,特别是军工使用的,还都是使用PCI槽比较多的主板,所以还是有点用处的。
感兴趣的读者请先读懂《PCI体系结构》这本书,这本书讲得很多,但是主要的章节可能就几百页。
本VHDL逻辑经过测试,通用逻辑支持ALTERA或者XILINX器件,是32位,暂时不支持64位。

  • 32位PIO:支持
  • 32位DMA读:支持
  • 32位DMA写:支持
  • DMA速率:111MB/s
  • 链式DMA:支持
    全时序逻辑设计,没有组合逻辑,所以在各种FPGA芯片上基本没有时序差异的问题。

github地址:https://github.com/jbl19860422/pci_logic.git
简单说明,最好是参考《PCI体系结构》来设计,有个时序设计软件叫timeXXX,可以比较方便的来设计时序。
1、状态机

-- 空闲状态
CONSTANT IDLE_NOR:STD_LOGIC_VECTOR(1 DOWNTO 0):="00";
-- 繁忙状态
CONSTANT BUSY:STD_LOGIC_VECTOR(1 DOWNTO 0):="01";
-- 数据阶段
CONSTANT SDATA:STD_LOGIC_VECTOR(1 DOWNTO 0):="10";
-- 总线切换周期时处于这个状态
CONSTANT TURNAROUND:STD_LOGIC_VECTOR(1 DOWNTO 0):="11";

PROCESS(RST,CLK)
BEGIN
  IF RST = '0' THEN
      STATUS <= IDLE_NOR;
  ELSIF CLK'EVENT AND CLK = '1' THEN
      CASE STATUS IS
          WHEN IDLE_NOR =>  
                IF FRAME = '0' THEN --FRAME下降沿,开始周期
                    STATUS <= BUSY;
                END IF;
         WHEN BUSY =>       --根据译码结果,判断处于什么周期
                IF ConfCS = '1' OR Bar0_CS = '1' OR Bar1_CS = '1' THEN
                    STATUS <= SDATA;
                ELSIF FRAME = '1' AND IRDY = '1' THEN --这里一般是DMA完成之类的,记不清了
                    STATUS <= IDLE_NOR;
                END IF;     
         WHEN SDATA =>      
                IF IRDY = '0' AND TRDY = '0' AND FRAME = '1' THEN                                       
                    STATUS <= TURNAROUND;   
                END IF;
          WHEN TURNAROUND => 
                IF FRAME = '1' THEN
                    STATUS <= IDLE_NOR;
                END IF;
     END CASE;
  END IF;
END PROCESS;

配置空间选中的译码逻辑:

PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        ConfCS <= '0';
    ELSIF Clk'EVENT AND Clk = '1' THEN--记得CBE在IDSEL='1'时,是命令,3到1为101可能是读也可能是写
        IF FRAME = '0' AND STATUS = IDLE_NOR AND IDSEL = '1' AND AD(1 DOWNTO 0) = "00" AND CBE(3 DOWNTO 1) = "101" THEN
            ConfCS <= '1';
        ELSIF TRDY = '0' AND IRDY = '0' THEN
            ConfCS <= '0';
        END IF;
    END IF;
END PROCESS;

数据校验输出逻辑:

PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        ParGenData <= (OTHERS => '0');
    ELSIF Clk'EVENT AND Clk = '1' THEN
        IF IRDY = '0' AND TRDY = '0' THEN
            ParGenData <= AD&CBE;
        ELSIF Curr_State_DMA = AddrPhase OR (Curr_State_DMA = DataPhase AND DMA_Dir = '1') THEN
            ParGenData <= AD&CBE;
        END IF;
    END IF;
END PROCESS;

PROCESS(RST ,Clk)
BEGIN
    IF RST = '0' THEN
        ParGen <= '0';
    ELSIF Clk'EVENT AND Clk = '1' THEN
        IF Curr_State_DMA = AddrPhase THEN
            ParGen <= '1';
        ELSIF STATUS = SDATA AND IRDY = '0' AND TRDY = '0' AND RW = '0' THEN
            ParGen <= '1';
        ELSIF Curr_State_DMA = DataPhase AND DMA_Dir = '1' AND IRDY = '0' AND TRDY = '0' THEN
            ParGen <= '1';
        ELSE
            ParGen <= '0';
        END IF;
    END IF;
END PROCESS;

PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        PAR <= 'Z';
    ELSIF Clk'EVENT AND Clk = '0' THEN
        IF ParGen = '1' THEN
            PAR <= ParGenData(0) XOR ParGenData(1) XOR ParGenData(2) XOR ParGenData(3) XOR ParGenData(4) XOR ParGenData(5)
                    XOR ParGenData(6) XOR ParGenData(7) XOR ParGenData(8) XOR ParGenData(9) XOR ParGenData(10) XOR ParGenData(11)
                    XOR ParGenData(12) XOR ParGenData(13) XOR ParGenData(14) XOR ParGenData(15) XOR ParGenData(16)
                    XOR ParGenData(17) XOR ParGenData(18) XOR ParGenData(19) XOR ParGenData(20) XOR ParGenData(21) 
                    XOR ParGenData(22) XOR ParGenData(23) XOR ParGenData(24) XOR ParGenData(25) XOR ParGenData(26)
                    XOR ParGenData(27) XOR ParGenData(28) XOR ParGenData(29) XOR ParGenData(30) XOR ParGenData(31)
                    XOR ParGenData(32) XOR ParGenData(33) XOR ParGenData(34) XOR ParGenData(35);
        ELSE
            PAR <= 'Z';
        END IF;
    END IF;
END PROCESS;

配置空间读写逻辑,请参考《PCI体系结构》:

------------------------------CONFIG R/W LOGIC--------------------------
PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        PCI_Config_Wr <= '0';
    ELSIF Clk'EVENT AND Clk = '1' THEN
        IF ConfCS = '1' AND IRDY = '0' AND STATUS = SDATA AND RW = '1' THEN
            PCI_Config_Wr <= '1';--生成写信号
            PCI_Config_WrData <= AD;--锁存数据
        ELSE
            PCI_Config_Wr <= '0';
        END IF;
    END IF;
END PROCESS;
    
PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        ConfReg(0) <= X"12341103";
        ConfReg(1) <= X"00000007";---Status|Command
        ConfReg(2) <= X"00000000";---class code|revision id
        ConfReg(3) <= X"00000000";---BIST|HeaderType|LatencyTimer|CacheLine
        ConfReg(4) <= X"00000000";--BASE0
        ConfReg(5) <= X"00000001";--BASE1
        ConfReg(6) <= X"00000000";--BASE2
        ConfReg(7) <= X"00000000";--BASE3
        ConfReg(8) <= X"00000000";--BASE4
        ConfReg(9) <= X"00000000";--BASE5
        ConfReg(10) <= X"00000000";--CardBus CIS Pointer
        ConfReg(11) <= X"00000000";--SubSystem Device ID|SubSystem Vender ID
        ConfReg(12) <= X"00000000";
        ConfReg(13) <= X"00000000";
        ConfReg(14) <= X"00000000";
        ConfReg(15) <= X"00000100";
    ELSIF Clk'EVENT AND Clk = '0' THEN
        IF PCI_Config_Wr = '1' AND ConfCS = '1' THEN--配置空间写有效时,根据地地址写入对应的配置寄存器
            CASE ADDRESS(5 DOWNTO 2) IS
                WHEN    BaseAddr0 => --对于BAR空间的写,需要按照PCI协议将低位表示大小的部分固定为0
                    ConfReg(4)(31 DOWNTO 8) <= PCI_Config_WrData(31 DOWNTO 8);
                    ConfReg(4)(7 DOWNTO 0) <= X"00";
                WHEN    BaseAddr1 =>
                    ConfReg(5)(31 DOWNTO 16) <= X"0000";
                    ConfReg(5)(15 DOWNTO 6) <= PCI_Config_WrData(15 DOWNTO 6);
                    ConfReg(5)(5 DOWNTO 0) <= "000001";--IO空间
                WHEN    X"1"    =>
                    IF CBE(0) = '0' THEN
                        ConfReg(1)(7 DOWNTO 0) <= PCI_Config_WrData(7 DOWNTO 0);
                    END IF;
                    
                    IF CBE(1) = '0' THEN
                        ConfReg(1)(15 DOWNTO 8) <= PCI_Config_WrData(15 DOWNTO 8);
                    END IF;
                    
                    IF CBE(2) = '0' THEN
                        ConfReg(1)(23 DOWNTO 16) <= PCI_Config_WrData(23 DOWNTO 16);
                    END IF;
                    
                    IF CBE(3) = '0' THEN
                        ConfReg(1)(31 DOWNTO 24) <= PCI_Config_WrData(31 DOWNTO 24);
                    END IF;
                WHEN    X"3" =>
                    IF CBE(0) = '0' THEN
                        ConfReg(3)(7 DOWNTO 0) <= PCI_Config_WrData(7 DOWNTO 0);
                    END IF;
                    
                    IF CBE(1) = '0' THEN
                        ConfReg(3)(15 DOWNTO 8) <= PCI_Config_WrData(15 DOWNTO 8);
                    END IF;
                    
                    IF CBE(2) = '0' THEN
                        ConfReg(3)(23 DOWNTO 16) <= PCI_Config_WrData(23 DOWNTO 16);
                    END IF;
                    
                    IF CBE(3) = '0' THEN
                        ConfReg(3)(31 DOWNTO 24) <= PCI_Config_WrData(31 DOWNTO 24);
                    END IF;
                WHEN X"F" =>
                    IF CBE(0) = '0' THEN
                        ConfReg(15)(7 DOWNTO 0) <= PCI_Config_WrData(7 DOWNTO 0);
                    END IF;
                    
                    IF CBE(1) = '0' THEN
                        ConfReg(15)(15 DOWNTO 8) <= PCI_Config_WrData(15 DOWNTO 8);
                    END IF;
                    
                    IF CBE(2) = '0' THEN
                        ConfReg(15)(23 DOWNTO 16) <= PCI_Config_WrData(23 DOWNTO 16);
                    END IF;
                    
                    IF CBE(3) = '0' THEN
                        ConfReg(15)(31 DOWNTO 24) <= PCI_Config_WrData(31 DOWNTO 24);
                    END IF;
                WHEN OTHERS => 
                    NULL;
            END CASE;
        END IF;
    END IF;
END PROCESS;
----------------------------------config end
PROCESS(Clk)--AD总线驱动逻辑
BEGIN
    IF Clk'EVENT AND Clk = '0' THEN
        IF RW = '0' AND ConfCS = '1' THEN
            AD <= ConfReg(conv_integer(ADDRESS(5 DOWNTO 2)));
        ELSIF RW = '0' AND Bar0_CS = '1' THEN--配置空间读取逻辑
            CASE ADDRESS(7 DOWNTO 2) IS
                WHEN "000000" =>
                    AD <= X"000000"&UUT_AD9642_SPI1_DataRecv;
                WHEN "000001" =>
                    AD <= X"000000"&UUT_AD9642_SPI2_DataRecv;
                WHEN "000010" =>--8
                    AD <= X"0000000"&IntReg;
                WHEN "000011" =>--C
                    AD <= X"0000000"&IntMask;
                WHEN "000101" =>--14
                    AD <= X"0000000"&"000"&Dma_Start;
                WHEN "000110" =>--18
                    AD <= DMA_DonePCIAddress;
                WHEN "000111" =>--1C
                    AD <= X"00000"&"00"&DMA_DoneCount;
                WHEN "001000" =>--20
                    AD <= X"0000000"&"000"&CH1_EN;
                WHEN "001001" =>--24
                    AD <= X"0000000"&"000"&CH2_EN;
                WHEN "001010" =>--28
                    AD <= X"00000"&"0"&UUT_DmaRd_FIFO1_DataCount;
                WHEN "001011" =>--2C
                    AD <= X"00000"&"0"&UUT_DmaRd_FIFO1_DataCount;
                WHEN "001100" =>--30
                    AD <= X"00000"&"00"&CH1_Thres;
                WHEN "001110" =>--34
                    AD <= X"00000"&"00"&CH2_Thres;
--              WHEN "001100" =>--30
--                  AD <= X"000000"&DMAIntCount;
--              WHEN "001101" =>--34
--                  AD <= X"000000"&DMAStatus;
--              WHEN "001111" =>--3C
--                  AD <= X"000000"&DMACmd;
                WHEN OTHERS =>
                    AD <= X"00000000";
            END CASE;
                --这里是BAR1的PIO读取逻辑,按照这种格式添加完寄存器后,这里扩展就行
        ELSIF RW = '0' AND Bar1_CS = '1' THEN
            CASE ADDRESS(5 DOWNTO 2) IS
                WHEN "0000" =>
                    AD <= Bar1TestReg1;
                WHEN OTHERS =>
                    AD <= X"00000000";
            END CASE;
                --DMA的输出,此时输出DMA的物理地址
        ELSIF Curr_State_DMA = AddrPhase THEN
            AD <= DMA_PCIAddress;
               --DMA的数据阶段,如果是DMA读,则输出DMA数据,DMA数据存在FIFO中,也可以存在DDR等,需要自己再稍微适配下
        ELSIF Curr_State_DMA = DataPhase AND DMA_Dir = '1' THEN
            AD <= X"0000"&"00"&UUT_DmaRd_FIFO1_Dout;
        ELSE
            AD <= "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
        END IF;
    END IF;
END PROCESS;

BAR0译码逻辑【其他BAR空间译码,参考这个一样写即可】:

------------------------BAR0 R/W LOGIC-------------------------------------
PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        Bar0_CS <= '0';
    ELSIF Clk'EVENT AND Clk = '1' THEN
        IF STATUS = IDLE_NOR AND FRAME = '0' AND AD(31 DOWNTO 8) = ConfReg(4)(31 DOWNTO 8) AND CBE(3 DOWNTO 1) = "011" THEN
            Bar0_CS <= '1';
        ELSIF IRDY = '0' AND TRDY = '0' THEN
            Bar0_CS <= '0';
        END IF;
    END IF;
END PROCESS;

PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        Bar0_Wr <= '0';
    ELSIF Clk'EVENT AND Clk = '1' THEN
        IF Bar0_CS = '1' AND IRDY = '0' AND STATUS = SDATA AND RW = '1' THEN
            Bar0_Wr <= '1';--BAR0
            Bar0_WrData <= AD;
        ELSE
            Bar0_Wr <= '0';
        END IF;
    END IF;
END PROCESS;

BAR空间的PIO写,其他BAR参考这里编写即可:

----------------------BAR0 WRITE LOGIC-----------------------------------
PROCESS(RST, Clk)---------------REGISTER WRITE
BEGIN
    IF RST = '0' THEN
        Bar0TestReg <= (OTHERS => '0');
        IntReg_Clr <= (OTHERS => '0');
        IntMask <= (OTHERS => '0');
        Dma_Start <= '0';
        CH1_EN <= '0';
        CH2_EN <= '0';
        UUT_AD9642_SPI1_Start <= '0';
        UUT_AD9642_SPI2_Start <= '0';
        UUT_DmaDesc_FIFO_Rst <= '0';
    ELSIF Clk'EVENT AND Clk = '0' THEN
        IF Bar0_CS = '1' AND Bar0_Wr = '1' THEN
            CASE ADDRESS(7 DOWNTO 2) IS
                WHEN "000000" =>--0
                    UUT_AD9642_SPI1_RW <= AD(23);
                    UUT_AD9642_SPI1_ADDR <= AD(20 DOWNTO 8);
                    UUT_AD9642_SPI1_DataSend <= AD(7 DOWNTO 0);
                    UUT_AD9642_SPI1_Start <= '1';
                WHEN "000001" =>--4
                    UUT_AD9642_SPI2_RW <= AD(23);
                    UUT_AD9642_SPI2_ADDR <= AD(20 DOWNTO 8);
                    UUT_AD9642_SPI2_DataSend <= AD(7 DOWNTO 0);
                    UUT_AD9642_SPI2_Start <= '1';
                WHEN "000010" =>--8
                    IntReg_Clr <= AD(3 DOWNTO 0);
                WHEN "000011" =>--C
                    IntMask <= AD(3 DOWNTO 0);
                WHEN "000101" =>--14
                    Dma_Start <= AD(0);
                WHEN "001100" =>--30
                    CH1_EN <= AD(0);
                WHEN "001101" =>--34
                    CH2_EN <= AD(0);
                WHEN "010000" =>--40
                    UUT_DmaDesc_FIFO_Rst <= '0';
                WHEN OTHERS => NULL;
            END CASE;
        ELSE
            UUT_AD9642_SPI1_Start <= '0';
            UUT_AD9642_SPI2_Start <= '0';
            IntReg_Clr <= (OTHERS => '0');
            UUT_DmaDesc_FIFO_Rst <= '0';
        END IF;
    END IF;
END PROCESS;

DMA总处理状态机:

PROCESS(RST, Clk)
BEGIN
    IF RST = '0' THEN
        Curr_State_Dma <= Idle;
    ELSIF Clk'EVENT AND Clk = '1' THEN
        CASE Curr_State_DMA IS
            WHEN Idle =>
                -- 每次DMA,应用程序通过驱动,使用PIO往DMA描述符FIFO中写入物理内存地址及数量
                --DMA_START:通过PIO写入,描述符FIFO非空,且有偶数个数据,可以开始启动DMA处理逻辑
                IF Dma_Start = '1' AND UUT_DmaDesc_FIFO_Empty = '0' AND UUT_DmaDesc_FIFO_DataCount(0) = '0' THEN
                    Curr_State_DMA <= ReadPCIAddr;
                END IF;
                -- 从FIFO读取DMA的物理地址
            WHEN ReadPCIAddr =>
                IF DmaPCIAddress_Rdy = '1' THEN
                    Curr_State_DMA <= ReadTransCount;
                END IF;
                -- 从FIFO读取DMA的字节数
            WHEN ReadTransCount =>
                IF DmaCount_Rdy = '1' THEN
                    Curr_State_DMA <= ReqPCIBus;
                END IF;
               -- 请求获取PCI总线控制权
            WHEN ReqPCIBus =>
                IF nGNT = '0' AND FRAME = '1' AND IRDY = '1' THEN
                    Curr_State_DMA <= AddrPhase;
                END IF;
              -- 如果是DMA读,可以直接读
              -- 如果是DMA写,则需要转换总线输入输出状态,需要一个转换周期
            WHEN AddrPhase =>
                IF DMA_Dir = '0' THEN
                    Curr_State_DMA <= DataPhase;
                ELSE
                    Curr_State_DMA <= TurnAroundPhase;
                END IF;
            WHEN TurnAroundPhase =>
                Curr_State_DMA <= DataPhase;
             -- 数据阶段,这里Devsel_counter的判断,参考《pci体系结构》,忘了哪章了
            WHEN DataPhase =>
                IF STOP = '0' OR Devsel_Counter = 6 OR DMA_Count = 0 THEN  -----
                    Curr_State_DMA <= EndPhase;
                END IF;
            WHEN EndPhase =>
                IF DMA_Count /= 0 THEN
                    Curr_State_DMA <= ReqPCIBus;
                ELSE
                    Curr_State_DMA <= Idle;
                END IF;
            WHEN OTHERS => NULL;
        END CASE;
    END IF;
END PROCESS;

DMA数据处理逻辑:

PROCESS(RST, CLK)
BEGIN
    IF RST = '0' THEN
        DMA_Count <= (OTHERS => '0');
        DMA_Channel <= '0';
    ELSIF CLK'EVENT AND CLK = '1' THEN
        IF DmaCount_Rdy = '1' AND Curr_State_DMA = ReadTransCount THEN
            DMA_Channel <= UUT_DmaDesc_FIFO_Dout(31);--第31位表示通道,目前支持两通道
            DMA_Count <= UUT_DmaDesc_FIFO_Dout(9 DOWNTO 0);
            DMA_DoneCount <= UUT_DmaDesc_FIFO_Dout(9 DOWNTO 0);--这时先读出初始数量
        ELSIF Curr_State_DMA = DataPhase THEN
            IF TRDY = '0' AND IRDY = '0' THEN
                DMA_Count <= DMA_Count - 1;--每完成一次交易,请求DMA数量减1,到0表示完成
            END IF;
        END IF;
    END IF;
END PROCESS;

PROCESS(RST, CLK)
BEGIN
    IF RST = '0' THEN
        DMA_PCIAddress <= (OTHERS => '0');
        DMA_Dir <= '0';
    ELSIF CLK'EVENT AND CLK = '1' THEN
        IF DmaPCIAddress_Rdy = '1' AND Curr_State_DMA = ReadPCIAddr THEN
            DMA_PCIAddress <= UUT_DmaDesc_FIFO_Dout(31 DOWNTO 2)&"00";
            DMA_DonePCIAddress <= UUT_DmaDesc_FIFO_Dout(31 DOWNTO 2)&"00";
            DMA_Dir <= UUT_DMADesc_FIFO_Dout(0);--第0位表示方向
            DMAIntFlag <= UUT_DMADesc_FIFO_Dout(1);-第1位表示DMA完成是否完成时,给PC中断,一般都会要吧
        ELSIF Curr_State_DMA = DataPhase THEN
            IF IRDY = '0' AND TRDY = '0' THEN--每完成一次交易DMA地址+1
                DMA_PCIAddress(31 DOWNTO 2) <= DMA_PCIAddress(31 DOWNTO 2) + 1;
            END IF;
        END IF;
    END IF;
END PROCESS;

还有一些DMA中断产生逻辑,中断屏蔽逻辑,至于PCI驱动,按照寄存器分配来编写即可,后面有时间再整理上传一份,可能也需要再调试下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容