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