FPGA:数字示波器 2 - 双端口 RAM
FIFO使我们能够非常快速地获得工作设计。
但对于我们简单的示波器来说,这有点矫枉过正。
我们需要一种机制来存储来自一个时钟域(100MHz)的数据,并在另一个时钟域(25MHz)中读取数据。 一个简单的双端口RAM就可以做到这一点。 缺点是两个时钟域之间的所有同步(FIFO为我们所做的)现在必须“手动”完成。
触发
“基于 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 |
加入微信
获取电子行业最新资讯
搜索微信公众号:EEPW
或用微信扫描左侧二维码