新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > FPGA:数字示波器 1 - 首款设计

FPGA:数字示波器 1 - 首款设计

作者:时间:2024-01-12来源:EEPW编译收藏

以下是此处构建的内容:

本文引用地址:http://www.eepw.com.cn/article/202401/454716.htm

接收两个时钟:

  • 一个缓慢的“系统”时钟,固定在25MHz。

  • ADC采样时钟(更快,假设100MHz),连接到ADC和

拥有这两个时钟为设计提供了灵活性。 但这也意味着我们需要一种方法将信息从一个时钟域传输到另一个时钟域。 为了验证硬件是否正常工作,让我们走一条简单的路线,使用FIFO。 从ADC采集的样本以全ADC速度(100MHz)存储在 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




关键词: FPGA 数字示波器

评论


相关推荐

技术专区

关闭