FPGA:數(shù)字示波器 2 - 雙端口 RAM
FIFO使我們能夠非??焖俚孬@得工作設(shè)計。
但對于我們簡單的示波器來說,這有點(diǎn)矯枉過正。
我們需要一種機(jī)制來存儲來自一個時鐘域(100MHz)的數(shù)據(jù),并在另一個時鐘域(25MHz)中讀取數(shù)據(jù)。 一個簡單的雙端口RAM就可以做到這一點(diǎn)。 缺點(diǎn)是兩個時鐘域之間的所有同步(FIFO為我們所做的)現(xiàn)在必須“手動”完成。
觸發(fā)
“基于 FIFO”的示波器設(shè)計沒有明確的觸發(fā)機(jī)制。
讓我們改變一下。 現(xiàn)在,每次從串行端口接收到字符時,示波器都會被觸發(fā)。 當(dāng)然,這仍然不是一個非常有用的設(shè)計,但我們稍后會對其進(jìn)行改進(jìn)。
我們使用“async_receiver”從串行端口接收數(shù)據(jù):
wire [7:0] RxD_data; async_receiver async_rxd(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data)); |
每當(dāng)收到一個新角色時,“RxD_data_ready”就會升高一個時鐘。 我們用它來觸發(fā)示波器。
同步
我們需要將這種“RxD_data_ready變高”的信息從“clk”(25MHz)域傳輸?shù)健癱lk_flash”(100MHz)域。
首先,當(dāng)接收到字符時,信號“startAcquisition”變?yōu)楦唠娖健?/span>
reg startAcquisition; wire AcquisitionStarted; always @(posedge clk) if(~startAcquisition) startAcquisition <= RxD_data_ready; else if(AcquisitionStarted) startAcquisition <= 0; |
我們使用 2 個觸發(fā)器形式的同步器(將此“startAcquisition”傳輸?shù)搅硪粋€時鐘域)。
reg startAcquisition1; always @(posedge clk_flash) startAcquisition1 <= startAcquisition; reg startAcquisition2; always @(posedge clk_flash) startAcquisition2 <= startAcquisition1; |
最后,一旦另一個時鐘域“看到”信號,它就會“回復(fù)”(使用另一個同步器“獲取”)。
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; |
回復(fù)將重置原始信號。
雙端口RAM
現(xiàn)在觸發(fā)器可用,我們需要一個雙端口RAM來存儲數(shù)據(jù)。
請注意 RAM 的每一側(cè)如何使用不同的時鐘。
ram512 ram_flash( .data(data_flash_reg), .wraddress(wraddress), .wren(Acquiring), .wrclock(clk_flash), .q(ram_output), .rdaddress(rdaddress), .rden(rden), .rdclock(clk) ); |
使用二進(jìn)制計數(shù)器可以輕松創(chuàng)建 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 |
請注意每個計數(shù)器如何使用不同的時鐘。
最后,我們將數(shù)據(jù)發(fā)送到 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)); |
完整的設(shè)計
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 |
評論