新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > FPGA:數字示波器 2 - 雙端口 RAM

        FPGA:數字示波器 2 - 雙端口 RAM

        作者: 時間:2024-01-12 來源:EEPW編譯 收藏

        FIFO使我們能夠非常快速地獲得工作設計。
        但對于我們簡單的示波器來說,這有點矯枉過正。

        我們需要一種機制來存儲來自一個時鐘域(100MHz)的數據,并在另一個時鐘域(25MHz)中讀取數據。 一個簡單的雙端口RAM就可以做到這一點。 缺點是兩個時鐘域之間的所有同步(FIFO為我們所做的)現在必須“手動”完成。

        本文引用地址:http://www.104case.com/article/202401/454719.htm

        觸發

        “基于 FIFO”的示波器設計沒有明確的觸發機制。
        讓我們改變一下。 現在,每次從串行端口接收到字符時,示波器都會被觸發。 當然,這仍然不是一個非常有用的設計,但我們稍后會對其進行改進。

        我們使用“async_receiver”從串行端口接收數據:

        wire [7:0] RxD_data;
        async_receiver async_rxd(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));


        每當收到一個新角色時,“RxD_data_ready”就會升高一個時鐘。 我們用它來觸發示波器。

        同步

        我們需要將這種“RxD_data_ready變高”的信息從“clk”(25MHz)域傳輸到“clk_flash”(100MHz)域。

        首先,當接收到字符時,信號“startAcquisition”變為高電平。

        reg startAcquisition;
        wire AcquisitionStarted;

        always @(posedge clk)
        if(~startAcquisition)
          startAcquisition <= RxD_data_ready;
        else
        if(AcquisitionStarted)
          startAcquisition <= 0;


        我們使用 2 個觸發器形式的同步器(將此“startAcquisition”傳輸到另一個時鐘域)。

        reg startAcquisition1; always @(posedge clk_flash) startAcquisition1 <= startAcquisition;
        reg startAcquisition2; always @(posedge clk_flash) startAcquisition2 <= startAcquisition1;


        最后,一旦另一個時鐘域“看到”信號,它就會“回復”(使用另一個同步器“獲取”)。

        reg Acquiring;
        always @(posedge clk_flash)
        if(~Acquiring)
          Acquiring <= startAcquisition2;  // start acquiring?
        else
        if(&wraddress)  // done acquiring?
          Acquiring <= 0;

        reg Acquiring1; always @(posedge clk) Acquiring1 <= Acquiring;
        reg Acquiring2; always @(posedge clk) Acquiring2 <= Acquiring1;
        assign AcquisitionStarted = Acquiring2;


        回復將重置原始信號。

        雙端口RAM

        現在觸發器可用,我們需要一個雙端口RAM來存儲數據。
        請注意 RAM 的每一側如何使用不同的時鐘。

        ram512 ram_flash(
          .data(data_flash_reg), .wraddress(wraddress), .wren(Acquiring), .wrclock(clk_flash),
          .q(ram_output), .rdaddress(rdaddress), .rden(rden), .rdclock(clk)

        );


        使用二進制計數器可以輕松創建 ram 地址總線。
        首先是寫地址:

        reg [8:0] wraddress;
        always @(posedge clk_flash) if(Acquiring) wraddress <= wraddress + 1;


        和讀取地址:

        reg [8:0] rdaddress;
        reg Sending;
        wire TxD_busy;

        always @(posedge clk)
        if(~Sending)
          Sending <= AcquisitionStarted;
        else
        if(~TxD_busy)
        begin
          rdaddress <= rdaddress + 1;
          if(&rdaddress) Sending <= 0;
        end


        請注意每個計數器如何使用不同的時鐘。

        最后,我們將數據發送到 PC:

        wire TxD_start = ~TxD_busy & Sending;
        wire rden = TxD_start;

        wire [7:0] ram_output;
        async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(ram_output));

        完整的設計

        module oscillo(clk, RxD, TxD, clk_flash, data_flash);
        input clk;
        input RxD;
        output TxD;

        input clk_flash;
        input [7:0] data_flash;

        ///////////////////////////////////////////////////////////////////
        wire [7:0] RxD_data;
        async_receiver async_rxd(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));

        reg startAcquisition;
        wire AcquisitionStarted;

        always @(posedge clk)
        if(~startAcquisition)
          startAcquisition <= RxD_data_ready;
        else
        if(AcquisitionStarted)
          startAcquisition <= 0;

        reg startAcquisition1; always @(posedge clk_flash) startAcquisition1 <= startAcquisition ;
        reg startAcquisition2; always @(posedge clk_flash) startAcquisition2 <= startAcquisition1;

        reg Acquiring;
        always @(posedge clk_flash)
        if(~Acquiring)
          Acquiring <= startAcquisition2;
        else
        if(&wraddress)
          Acquiring <= 0;

        reg [8:0] wraddress;
        always @(posedge clk_flash) if(Acquiring) wraddress <= wraddress + 1;

        reg Acquiring1; always @(posedge clk) Acquiring1 <= Acquiring;
        reg Acquiring2; always @(posedge clk) Acquiring2 <= Acquiring1;
        assign AcquisitionStarted = Acquiring2;

        reg [8:0] rdaddress;
        reg Sending;
        wire TxD_busy;

        always @(posedge clk)
        if(~Sending)
          Sending <= AcquisitionStarted;
        else
        if(~TxD_busy)
        begin
          rdaddress <= rdaddress + 1;
          if(&rdaddress) Sending <= 0;
        end

        wire TxD_start = ~TxD_busy & Sending;
        wire rden = TxD_start;

        wire [7:0] ram_output;
        async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(ram_output));

        ///////////////////////////////////////////////////////////////////
        reg [7:0] data_flash_reg; always @(posedge clk_flash) data_flash_reg <= data_flash;

        ram512 ram_flash(
          .data(data_flash_reg), .wraddress(wraddress), .wren(Acquiring), .wrclock(clk_flash),
          .q(ram_output), .rdaddress(rdaddress), .rden(rden), .rdclock(clk)
        );

        endmodule




        關鍵詞: FPGA 數字示波器

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 贺兰县| 贺州市| 泽库县| 来凤县| 孝昌县| 太原市| 夏邑县| 会东县| 喀喇| 武邑县| 视频| 杭锦后旗| 句容市| 泸溪县| 盐亭县| 龙山县| 潜山县| 宁德市| 山西省| 明光市| 北票市| 榆林市| 禹城市| 松原市| 惠水县| 宝丰县| 阿克陶县| 铜梁县| 育儿| 桐乡市| 亚东县| 九台市| 淅川县| 宜良县| 木里| 尚义县| 海城市| 交城县| 乌拉特前旗| 都匀市| 射洪县|