FPGA:數字示波器 2 - 雙端口 RAM
FIFO使我們能夠非??焖俚孬@得工作設計。
但對于我們簡(jiǎn)單的示波器來(lái)說(shuō),這有點(diǎn)矯枉過(guò)正。
我們需要一種機制來(lái)存儲來(lái)自一個(gè)時(shí)鐘域(100MHz)的數據,并在另一個(gè)時(shí)鐘域(25MHz)中讀取數據。 一個(gè)簡(jiǎn)單的雙端口RAM就可以做到這一點(diǎn)。 缺點(diǎn)是兩個(gè)時(shí)鐘域之間的所有同步(FIFO為我們所做的)現在必須“手動(dòng)”完成。
觸發(fā)
“基于 FIFO”的示波器設計沒(méi)有明確的觸發(fā)機制。
讓我們改變一下。 現在,每次從串行端口接收到字符時(shí),示波器都會(huì )被觸發(fā)。 當然,這仍然不是一個(gè)非常有用的設計,但我們稍后會(huì )對其進(jìn)行改進(jìn)。
我們使用“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)); |
每當收到一個(gè)新角色時(shí),“RxD_data_ready”就會(huì )升高一個(gè)時(shí)鐘。 我們用它來(lái)觸發(fā)示波器。
同步
我們需要將這種“RxD_data_ready變高”的信息從“clk”(25MHz)域傳輸到“clk_flash”(100MHz)域。
首先,當接收到字符時(shí),信號“startAcquisition”變?yōu)楦唠娖健?/span>
reg startAcquisition; wire AcquisitionStarted; always @(posedge clk) if(~startAcquisition) startAcquisition <= RxD_data_ready; else if(AcquisitionStarted) startAcquisition <= 0; |
我們使用 2 個(gè)觸發(fā)器形式的同步器(將此“startAcquisition”傳輸到另一個(gè)時(shí)鐘域)。
reg startAcquisition1; always @(posedge clk_flash) startAcquisition1 <= startAcquisition; reg startAcquisition2; always @(posedge clk_flash) startAcquisition2 <= startAcquisition1; |
最后,一旦另一個(gè)時(shí)鐘域“看到”信號,它就會(huì )“回復”(使用另一個(gè)同步器“獲取”)。
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
現在觸發(fā)器可用,我們需要一個(gè)雙端口RAM來(lái)存儲數據。
請注意 RAM 的每一側如何使用不同的時(shí)鐘。
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)制計數器可以輕松創(chuàng )建 ram 地址總線(xiàn)。
首先是寫(xiě)地址:
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 |
請注意每個(gè)計數器如何使用不同的時(shí)鐘。
最后,我們將數據發(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)); |
完整的設計
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 |
評論