FPGA:數字示波器 1 - 首款設計
以下是此處構建的內容:
FPGA 接收兩個時鐘:
一個緩慢的“系統”時鐘,固定在25MHz。
ADC采樣時鐘(更快,假設100MHz),連接到ADC和FPGA。
擁有這兩個時鐘為設計提供了靈活性。 但這也意味著我們需要一種方法將信息從一個時鐘域傳輸到另一個時鐘域。 為了驗證硬件是否正常工作,讓我們走一條簡單的路線,使用FIFO。 從ADC采集的樣本以全ADC速度(100MHz)存儲在FPGA FIFO中。
然后,FIFO內容被讀回、序列化,并以更慢的速度(115200波特)在串行端口上發送。 最后,我們將串行輸出連接到接收每個字節并顯示信號跡線的 PC。
對于第一次嘗試,沒有跟蹤觸發機制。 ADC存儲以隨機間隔啟動,因此跡線將左右跳轉,但目前還好。
設計注意事項
在100MHz頻率下,FIFO在大約10us內充滿。 這是相當快的。 一旦吃飽了,我們就必須停止喂食。 存儲的內容需要完全發送到 PC,然后才能再次開始饋送 FIFO。
這里使用的串行通信工作在 115200 波特,因此大約為 10KB/s。 1024 個樣本傳輸大約需要 100 毫秒。 在此期間,示波器是“盲”的,因為我們丟棄了來自ADC的數據。 所以它在 99.99% 的時間里是盲目的。 這是此類體系結構的典型特征。
當我們稍后添加觸發機制時,這可以部分補償,因為當觸發器處于布防狀態時,它會以全ADC速度工作,并且只要觸發條件發生,它就可以保持布防狀態。 稍后會詳細介紹。
注冊輸入
ADC輸出數據總線使用8個引腳連接到FPGA,我們稱之為“data_flash[7:0]”。 這些產品的速度高達100MHz。 由于速度很快,因此最好在它們進入 FPGA 時立即“注冊”它們。
reg [7:0] data_flash_reg; always @(posedge clk_flash) data_flash_reg <= data_flash; |
現在,“data_flash_reg”完全位于FPGA內部,可以饋送到FPGA FIFO。
先進先出
FIFO 為 1024 字深 x 8 位寬。 由于我們每個時鐘從ADC接收8位,因此我們可以存儲1024個ADC樣本。 在100MHz時,填滿FIFO大約需要10us。
FIFO使用FPGA內部提供的同步靜態RAM模塊。 每個存儲塊通常可以存儲 512x8 位。因此,FIFO 使用 2 個塊。
FIFO邏輯本身是使用FPGA供應商的“函數生成器”創建的。 Xilinx 稱其為“coregen”,而 Altera 則稱其
“Megafunctions wizard”。在這里,讓我們使用 Altera 的 Quartus 來創建這個文件。
所以現在,使用FIFO只是一個連接問題。
fifo myfifo(.data(data_flash_reg), .wrreq(wrreq), .wrclk(clk_flash), .wrfull(wrfull), .wrempty(wrempty), .q(q_fifo), .rdreq(rdreq), .rdclk(clk), .rdempty(rdempty)); |
使用FIFO很好,因為它可以處理不同的時鐘。 我們將FIFO的寫入側連接到“clk_flash”(100MHz),將FIFO的讀取側連接到“clk”(25MHz)。
FIFO為每個時鐘域提供完整和空信號。 例如,“wrempty”是可以在寫入時鐘域(“clk_flash”)中使用的空信號,“rdempty”可以在讀取時鐘域(“clk”)中使用。
使用FIFO很簡單:寫入它只需斷言“wrreq”信號(并將數據提供給“.data”端口),同時從中讀取斷言“rdreq”(數據來自“.q”端口)。
寫入 FIFO
要開始寫入 FIFO,我們等到它為空。 當然,在上電時(配置FPGA之后),這是真的。
只有當它滿了時,我們才會停下來。 然后這個過程又開始了......我們等到它是空的......喂它直到它吃飽為止......停。
reg fillfifo; always @(posedge clk_flash) if(~fillfifo) fillfifo <= wrempty; // start when empty else fillfifo <= ~wrfull; // stop when full assign wrreq = fillfifo; |
讀取到FIFO
我們從FIFO讀取,只要它不是空的。每個字節讀取都發送到串行輸出。
wire TxD_start = ~TxD_busy & ~rdempty; assign rdreq = TxD_start; async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(q_fifo)); |
我們使用 async_transmitter 模塊對數據進行序列化,并將其傳輸到一個名為“TxD”的引腳。
完整的設計
我們的第一個工作示波器設計,不是很好嗎?
module oscillo(clk, TxD, clk_flash, data_flash); input clk; output TxD; input clk_flash; input [7:0] data_flash; reg [7:0] data_flash_reg; always @(posedge clk_flash) data_flash_reg <= data_flash; wire [7:0] q_fifo; fifo myfifo(.data(data_flash_reg), .wrreq(wrreq), .wrclk(clk_flash), .wrfull(wrfull), .wrempty(wrempty), .q(q_fifo), .rdreq(rdreq), .rdclk(clk), .rdempty(rdempty)); // The flash ADC side starts filling the fifo only when it is completely empty, // and stops when it is full, and then waits until it is completely empty again reg fillfifo; always @(posedge clk_flash) if(~fillfifo) fillfifo <= wrempty; // start when empty else fillfifo <= ~wrfull; // stop when full assign wrreq = fillfifo; // the manager side sends when the fifo is not empty wire TxD_busy; wire TxD_start = ~TxD_busy & ~rdempty; assign rdreq = TxD_start; async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(q_fifo)); endmodule |
評論