
在先前的文章中,我们已经见过
Avalon-MM总线,其中MM代表Memory Mapped,即投影到内存上。 该轮胎用途广泛。 几个主设备(Master)和几个从设备可以连接到它。 我们已经同时连接了两个领先的设备(指令主设备和数据主设备),因为NIOS II处理器具有哈佛架构,因此命令和数据总线是不同的,但是许多作者将它们连接到相同的通用设备上,以简化外部软件开发去公交车。
如果总线上的一个块具有直接存储器访问(DMA)功能,则它还将包含该总线的主机。
实际上,该轮胎的主要不便之处就是基于这一事实(很多领导者,很多追随者)。 设计从站时,我们必须解码地址。 当我碰巧成为领导者时,仲裁就引起了更多注意。 但是贯穿整个系列文章的一条红线断言,Redd的开发是项目的辅助部分,它不需要太多的工作。 而且,如果我们可以将自己从例行程序中解放出来,则必须将其从例行程序中解放出来。

该周期的所有文章:
- 为Redd中安装的FPGA开发最简单的“固件”,并以内存测试为例进行调试
- 为Redd中安装的FPGA开发最简单的“固件”。 第2部分。程序代码
- 开发自己的内核以嵌入基于FPGA的处理器系统
- 以访问FPGA的示例为中央处理器Redd开发程序
我们已经知道的
Avalon接口规范文档(一般来说,我不提供直接链接,因为它们总是会变化,因此整个网络到处都是带有无效链接的文章,因此将名称驱动到搜索引擎中更容易找到当前位置)报告除了
Avalon-MM总线之外,还有一条
Avalon-ST总线,其中ST代表Stream,即流。 事实是,传输的数据通常具有流结构。 是的,甚至是硬盘的经典扇区。 它具有固定的大小。 它应该从头到尾传递。 即使我们在可寻址区域中考虑它,地址也将线性增加。 而且,如果您使用FIFO块进行存储,则其中的地址将完全对我们隐藏。 他们是,但是与他们合作不是我们的关注。
同样的情况也适用于许多其他流数据:它们总是从头到尾,依次放置在存储库中。 这正是流协议用于传输此类数据的方式。 除了缺少显式寻址之外,
Avalon-ST总线
还很有趣,因为它始终连接两个设备:一个源和一个接收器。 总是有两个。 一种设备始终是源,第二种设备始终是接收器。 因此,与该总线的仲裁问题无关。 这是连接到该总线的典型设备对的样子:

这是此总线的典型信号:

此外,
错误行是可选的,它们传输由我们分配的二进制错误代码,我们可以说没有错误代码。 如上所述,仅在进一步执行多路分解时才需要通道号线。 如果不是,则不需要通道号。 我们将暂时没有它。 剩下三行:实际上,数据,就绪信号和数据确认信号(选通)。 好吧,另一个时钟信号,因为总线是同步的。
从文档中还可以看出,可能还有另外三个信号,将明确定义的数据包的传输属性添加到总线上:

通常,轮胎非常有趣,今天我们将开始对其进行试验。 众所周知,FPGA通过以
FT245-SYNC模式工作的
FT2232H桥接器连接到Redd大楼的USB总线。 严格来说,通过此接口传递的数据是相当流的数据。 今天,我们将学习如何将这些数据传输到基于NIOS II的处理器系统。 遗憾的是,尽管
FT245-SYNC协议是流式传输,但仍不完全符合
Avalon-ST总线。 为了节省芯片支脚,它具有双向数据总线,而
Avalon-ST总线是单向的。 因此,我们必须制作一个可以协调但不匹配协议的块。
在上一篇文章中,我们已经熟悉
FT245-SYNC协议。 让我提醒您,它的描述可以在文档
AN_130 FT2232H中找到,该文档
在FT245样式同步FIFO模式下使用 。 这是从网桥到FPGA传输的典型时序图

总的来说,作为一名程序员,我对传输的包会清楚地标记开始和结束这一事实非常感兴趣。 好吧,要使其在UDP协议的逻辑上更具逻辑性,因为如果传输采用TCP样式,则必须向流中添加特殊的基准数据,这些数据将花费在我的编程,工作量和处理器周期上……看起来像RXF系列可以帮助我们。 我们检查...我们在
上一篇文章中填写了FPGA中用于测量性能的“固件”,并将示波器探头连接到RXF线。 作为Redd中央处理器的测试程序,我们使用基础(也用于衡量性能),而不是发送大量数据,而是发送0x400字节的单块。
uint8_t temp [maxBlockSize]; memset (temp,0,sizeof (temp)); uint32_t dwWritten; FT_Write(ftHandle0, temp, 0x400, &dwWritten);
我们在RXF线上获得以下图片:

显然,微电路接收到0x200字节的缓冲区(即,一个USB2.0 HS数据包中可以容纳多少字节),然后将它们发送到通道。 通常,这很奇怪,因为文档指出每个方向都使用两个缓冲区。 在传输过程中,第二个缓冲区应该有时间填满。 las 填充的结束显然已经晚了。 实际上,这说明了为什么性能不能达到理论上的每秒52兆字节:大部分时间(虽然不是50%)根本没有被传输。
但是一种或另一种方式,我们发现只有在数据包大小不超过0x200字节的情况下,才有可能在RXF负边缘检测到数据包的开始。 如果我们只向设备发送少量数据的命令,这是可以实现的。 但是,如果我们发送大量数据流,我们将不得不使用连续的通道,其逻辑类似于UART(或者说类似于TCP通道),纯粹是通过编程突出显示数据包边界。
通常,为了简化表示,我们以流媒体版本为基础。 我们今天不会考虑打包。 好吧,我们以哪种版本的
Avalon-ST总线为基础很明确。 我们开始设计块。 如上所述,因为
FT245FIFO总线
是双向的,而
Avalon-ST总线是单向的,所以我们不仅要制造网桥,还要制造交换机。 也就是说,有必要一次制作两条
Avalon-ST总线:输出和输入。

我们开始缓慢地开发一种自动机,它将实现我们所需的逻辑。 当然,在本文中将最大程度地简化此逻辑。 让我们开始从FPGA到PC传输数据,因为此过程有点简单(您无需切换OE线的状态,这在
上一篇文章中已经讨论过)。 也就是说,我们正在实现接收器端口。
从
Avalon-ST总线的侧面,我选择了以下操作模式(文档中有很多操作模式,但这是最接近
FT245-SYNC的接口):

让我提醒您信号的方向:

也就是说,我们只需要等待总线上的确认(
有效 ),然后单击数据并用
就绪行来记录此事实。
从
FT245_FIFO一侧看
,该协议如下所示:

事实证明,我们必须等待TXE信号并使用WR#信号对数据进行选通(这两个信号的极性都是相反的)。
TXE#在功能上与
ready非常相似,而WR#
有效 。 细节有些不同,但是逻辑是相似的。
事实证明,我们可以选择一个单一状态的PC,在其中将执行某些行的最简单换向。 进入此状态的条件是双方都准备好进行传输,即(TXE#== 0)AND(valid == 1)。 一旦准备就绪,我们将回到空闲状态。
自动机的过渡图仍然很简单:

切换表是这样的(信号的名称不明确,索引被添加到其中,名称是唯一的,没有索引):
从源到FT245_FIFO的传输要稍微复杂一些。 正如我们
在上一篇文章中看到
的 ,复杂的是使用OE#信号切换方向:

对于
Avalon_ST总线
,一切都与以前相同,因此第二次不再显示图片,但是现在我们位于“源”位置。
此处,RXF#线对应于
有效线,RD#线对应于
就绪线。 好吧,好的,在机器上添加几个状态:

在此状态下激活的信号具有以下逻辑:
显然,该方案不是最理想的。 与缓冲区溢出或欠载相关的各种细微差别。 但是,不应该有任何数据丢失,但是为了获得最佳性能,您必须从某个地方开始!
我们开始将开发的理论转移到SystemVerilog代码。 是的,我们不能使用SystemVerilog的所有功能。 在一个案例中,我写了
一篇大文章 ,我在一个实际的开发环境中测试了该语言的精美功能的实用综合性。 在这里,我们只要求使用接口,因为系统将具有
Avalon-ST类型的两个实例。 las,嗯。 这是测试代码:
interface AvalonST #(parameter width=8)(input clk); logic [width-1:0] data; logic ready; logic valid; modport source (input clk, ready, output data,valid); modport sink (input clk, data, valid, output ready); endinterface module FT245toAvalonST ( AvalonST.source source, AvalonST.sink sink ); //assign source.ready = sink.valid; assign sink.ready = source.valid; endmodule
它是在主编译器中完美合成的(删除注释时的注释行会导致错误,以确保合成器能够正确解释所有内容),但是在为该代码检查组件的
Analyze Synthesis Files按钮时,会
生成错误,表明
AvalonST类型未知。 也就是说,分析不是基于SystemVerilog,而是基于纯Verilog。 真可惜

而且,语言是正确确定的,只是分析器无法理解端口之间的接口。

通常,您必须使用丑陋的旧语法。
使用此语法,我们得到以下模块接口: module FT245toAvalonST ( input clk, input reset, inout [7:0] ft245_data, input logic ft245_rxf, input logic ft245_txe, output logic ft245_rd, output logic ft245_wr, output logic ft245_oe, output logic ft245_siwu, input logic source_ready, output logic source_valid, output logic[7:0] source_data, output logic sink_ready, input logic sink_valid, input logic[7:0] sink_data );
粗鲁,老式的,但是你能做什么。
我们实现了自动机的过渡图,没有任何多余的装饰: // enum {idle, toPC, dropOE, fromPC} state = idle; // always_ff @(posedge clk,posedge reset) begin if (reset == 1) begin state <= idle; end else begin case (state) idle: begin if ((ft245_txe == 0) && (sink_valid == 1)) state <= toPC; else if ((ft245_rxf == 0)&&(source_ready == 1)) state <= dropOE; end toPC: begin if (!((ft245_txe == 0) && (sink_valid == 1))) state <= idle; end dropOE: begin state <= fromPC; end fromPC: begin if (!((ft245_rxf == 0)&&(source_ready == 1))) state <= idle; end endcase end end
但是,输出的控制需要一些解释。
部分安装是在“额头上”完成的: // // , , // - . always_comb begin ft245_oe <= 1; ft245_rd <= 1; ft245_wr <= 1; source_valid <= 0; sink_ready <= 0; // , // assign- case (state) idle: begin end toPC: begin ft245_wr <= !(sink_valid); sink_ready <= !(ft245_txe); end dropOE: begin ft245_oe <= 0; end fromPC: begin ft245_oe <= 0; ft245_rd <= !(source_ready); source_valid <= !(ft245_rxf); end endcase end
但是,例如,对于双向数据总线,应采用典型的解决方案。 我们记得,它在接口部分中声明如下:
inout [7:0] ft245_data,
并可以通过通常的方式进行阅读。 对于我们的情况,我们只需将所有数据包装在传出的
Avalon-ST总线的数据上:
// assign source_data = ft245_data;
但总的来说,您始终可以以自己喜欢的任何方式读取总线。 但是您应该使用多路复用器对其进行写入。 当我们将数据写入总线时,该数据应来自任何其他预先准备的总线。 通常,将
reg类型的变量(或新
逻辑 )封装在模块中。 在我们的情况下,这样的总线已经存在。 这是接收器
_数据总线。 在其他情况下,将输出状态Z如果您熟悉电路,那么您将了解典型的输出缓冲区。 它要么跳过任何输入数据,要么进入Z状态。 在我们的代码中,此多路复用器如下所示:
// inout- assign ft245_data = (state == toPC) ? sink_data : 8'hzz;
和另一个信号ft245_siwu。 我们从不使用它,因此根据FT2232H的文档,将其拉为一体:
// FTDI : // Tie this pin to VCCIO if not used. assign ft245_siwu = 1;
实际上,仅此而已。
整个模块如下所示: module FT245toAvalonST ( input clk, input reset, inout [7:0] ft245_data, input logic ft245_rxf, input logic ft245_txe, output logic ft245_rd, output logic ft245_wr, output logic ft245_oe, output logic ft245_siwu, input logic source_ready, output logic source_valid, output logic[7:0] source_data, output logic sink_ready, input logic sink_valid, input logic[7:0] sink_data ); // enum {idle, toPC, dropOE, fromPC} state = idle; // always_ff @(posedge clk,posedge reset) begin if (reset == 1) begin state <= idle; end else begin case (state) idle: begin if ((ft245_txe == 0) && (sink_valid == 1)) state <= toPC; else if ((ft245_rxf == 0)&&(source_ready == 1)) state <= dropOE; end toPC: begin if (!((ft245_txe == 0) && (sink_valid == 1))) state <= idle; end dropOE: begin state <= fromPC; end fromPC: begin if (!((ft245_rxf == 0)&&(source_ready == 1))) state <= idle; end endcase end end // // , , // - . always_comb begin ft245_oe <= 1; ft245_rd <= 1; ft245_wr <= 1; source_valid <= 0; sink_ready <= 0; // , // assign- case (state) idle: begin end toPC: begin ft245_wr <= !(sink_valid); sink_ready <= !(ft245_txe); end dropOE: begin ft245_oe <= 0; end fromPC: begin ft245_oe <= 0; ft245_rd <= !(source_ready); source_valid <= !(ft245_rxf); end endcase end // - c , ... // FTDI : // Tie this pin to VCCIO if not used. assign ft245_siwu = 1; // inout- assign ft245_data = (state == toPC) ? sink_data : 8'hzz; // assign source_data = ft245_data; endmodule
我们如何在
前面的文章之一中详细研究了如何将模块包括在处理器系统可用列表中,所以我只在图中显示了结果。 我记得为了实现它,我必须添加两个
AVALON-ST总线,一个
Conduit总线,从错误定义的
AVALON-MM总线中提取信号,并且当该总线中没有单个信号时,只需删除它即可。 在此过程中,该图显示了我为
AVALON-ST总线选择的设置(每个符号8位,无错误,最大通道为零,等待时间为零)。

随着对接轮胎模块的开发,就是这样。 但是a。 发展只是工作的开始。 实施要困难得多。 从屏幕上滚动条的位置可以看出,文章的结尾仍然很远。 因此,我们开始创建一个简单的项目,该项目将
FT245-SYNC总线接头与
AVALON-ST总线结合使用。 这是最简单的。 一个严肃的项目不适合合理大小的单个文章的框架。 现在,我将简单地进行简化,以使读者的注意力足以吸引其余部分的阅读,从而使他们不会放弃一句话。 第一个简化是
FT2232H芯片
本身为
FT245_SYNC提供了60 MHz
时钟 。 我可以在系统中添加两条时钟线,但是一旦大家看到,我们将拥有如此蜘蛛网,我的母亲不会感到烦恼。 如果我仍然注意不同的时钟线,我们都会感到困惑。 因此,我只是简单地宣布,今天我们的处理器系统将使用
FT2232H芯片作为时钟,而不是使用常规发生器作为时钟。
为什么不能总是这样做? 非常简单:只要
FT2232H不处于245_SYNC模式,它的输出端就不会有这些脉冲。 也就是说,您必须首先运行中央处理器的程序,然后才将所有内容加载到FPGA中。 如果我们要为外部客户创建系统,那么这样的解决方案将带来很多问题。 我从经验中知道,他们会定期打电话给我们,说什么都行不通,我们会提醒酒吧,但是那会有所帮助。 但是我们正在做内部事务,并且只会在实验室条件下使用它。 也就是说,在此任务的框架内,这是允许的。
但这带来了新的挑战。 我们的频率为60 MHz,而当前使用的SDRAM时钟块与50 MHz的频率紧密相关。 是的,我检查了,可以提交60个,但是我们假装不要超出允许的模式。 在接下来的文章中,我将尝试展示如何替换此硬块,但今天我们只说由于SDRAM的时钟单元无法在使用的频率上工作,因此将其从SDRAM处理器系统中排除。 该程序及其数据将完全位于FPGA的内部存储器中。 通过实验发现,在当今的配置中,FPGA最多可为该业务占用28 KB的RAM。 事实证明,您可以承受交易量,而不是2的倍数幂。
另外,我们将使用标准时钟和复位单元。 它的重置与我们用于SDRAM的重置略有不同。 为了使本文不复杂,我将利用这样一个事实,即正在开发的系统将始终在调试器的控制下工作,因此,我将从JTAG子系统启动重置以进行调试。
总的来说,我们得到了基本处理器系统的示意图(当前最困难的Reset线突出显示,蓝色标记在信号源上):

已针对时钟和复位块调整频率的位置:

对于RAM-卷:

今天,我们需要在终端中显示文本。 因此,我们将向系统添加一个有趣的块:

使用此块,我们将能够调用类似于printf的函数。 除了AVALON_MM总线,它还必须连接中断请求输出。

就是这样,处理器系统的采购已完成。 是时候嵌入我们的部队了。 他将数据发送到哪里? 在可供我们使用的模块中,有一个非常有趣的两端口FIFO存储器。 它的魅力在于,可以将一个端口配置为
AVALON-ST模式并将其连接到我们的设备,将第二个端口配置为
AVALON_MM模式并使用NIOS II处理器进行工作。 这个绝佳的街区位于:

我们有两条
Avalon-ST总线 (一条用于读取,另一条用于写入),因此我们还需要两个FIFO块。 现在,我将详细介绍其中的一个,我们绕开几公里的网络(以及一堆带有图片的文本屏幕),大约第二秒钟,我们说“可以类推”,仅表示差异。 因此,目前,我们仅向系统添加一个块并查看其设置。 有很多设置。 可以简单地指出所需的值,以便每个人都以该文章为参考,但是突然有人遇到需要配置的情况,但是无法访问网络(因此也无法访问该文章)。 因此,我将迭代添加设置。 首先很明显,然后-根据系统要求,一次又一次地进行对话。 因此,每个人都会感觉到这个过程,并且可以随时重复该过程。 这样啊 默认情况下,我们获得了以下设置:

现在,我将执行FIFO,该FIFO从
Avalon-ST收集数据,并将其上传到
Avalon-MM 。 事实证明,第一个编辑将是这样的:

我得到了一个有趣的警告:

事实证明,当至少一个端口投影到存储器上时,
Avalon-ST总线宽度必须严格为32位。 我们有一个8位总线。 我将告诉您如何在位深度上达成共识,但是现在我们在这里做的是32位总线和8位字符。 好吧,如理论部分所述,禁用批处理模式。

接下来是容量。 假设我排队256个字(即1024个字节):

现在的状态。 刚开始,我并不重视它,并且程序变得死机了。 所以现在我知道需要这种状态。 由于我们将以编程方式使用输出端口,因此我们为其添加状态。

并捕获错误:

那好 添加双时钟。 由于我们只有一个,因此只需将两个输入连接到同一条时钟线即可。
h 总共有:

但是,现在将此业务连接到通用系统还为时过早。 我们发现,8位的
Avalon-ST总线离开了我们开发的模块,并且应该包括32位的模块。 我们怎么样 重塑您的街区? 不行 一切都摆在我们面前。 这对我们有帮助:

将其添加到系统。 此外,由于它是一层,纯粹是为了美观,因此我们使用相应的箭头将其放置在块和FIFO之间:

我们进行以下设置:在输入上,我们有8位总线,在32位输出上。 不使用分组信号,使用
就绪和
有效信号。

现在是编织网络的时候了。 首先,我将放置流传输线(在图中,它们都被突出显示,标记位于数据接收器上):

也就是说,来自我们模块的Source的信号将到达适配器的输入。 并从适配器输出到FIFO输入。 正如我所说,流协议中的所有连接都是点对点的。
好吧,现在我们挂起了复位线,时钟线,并将所有东西都连接到系统总线和中断...

好吧……现在,根据相同的原理,我们添加了FIFO以将数据发布到
FT245SYNC 。 只有在那里,数据才以32位格式从
Avalon-MM进入FIFO。 它们通过32合8适配器,然后到达我们模块的SINK输入,该模块未连接到当前电路中……我们得到了最终电路的以下片段(那里的存储器只有一个时钟):

在先前文章中描述的实验中,我们已经很好地拟定了进一步的形式(
大部分是本篇文章
中的内容 )。 我们将向量分配给处理器。 对于系统,我们称自动分配中断号和地址。 我们保存系统...每个人都记得保存的系统名称必须与项目名称匹配,以便系统位于层次结构的顶层? 将系统添加到项目中,为项目做一个粗略的草稿,分配分支。 我个人作弊:我将项目草案的* .qsf文件中的作业复制到了当前的完成作业中(您可以将我的项目并复制相应的* .qsf行到您自己的作业中,但是您可以仅通过GUI来分配所有分支)。 我特别注意一个事实,即clk信号连接到支路23,而不是25,如先前项目中那样。 我提醒您,这里是FT2232输出的滴答声。

太好了! 硬件已准备就绪。 我们传递给软件。 我们从哪里开始? 今天,这个问题是不值得的。 如果我们从运行在NIOS II处理器上的程序开始,那么对我们来说将无济于事。 首先,我们必须将FT2232置于245-SYNC模式,然后我们的处理器系统才会接收时钟脉冲。 因此,我们从中央处理器的代码开始。
我们得到这样的东西: #include <cstdio> #include <sys/time.h> #include <unistd.h> #include "ftd2xx.h" FT_HANDLE OpenFT2232H() { FT_HANDLE ftHandle0; static FT_DEVICE ftDevice; // int nDevice = 0; while (true) { // if (FT_Open(nDevice, &ftHandle0) != FT_OK) { printf("No FT2232 found\n"); // , return 0; } // ? if (FT_GetDeviceInfo(ftHandle0, &ftDevice, NULL, NULL, NULL, NULL) == FT_OK) { // , if (ftDevice == FT_DEVICE_2232H) { // , AN130 FT_SetBitMode(ftHandle0, 0xff, 0x00); usleep(1000000); //Sync FIFO mode FT_SetBitMode(ftHandle0, 0xff, 0x40); FT_SetLatencyTimer(ftHandle0, 2); FT_SetUSBParameters(ftHandle0, 0x10000, 0x10000); return ftHandle0; } } // FT_Close(ftHandle0); // nDevice += 1; } printf("No FT2232 found\n"); } int main() { FT_HANDLE ftHandle0 = OpenFT2232H(); if (ftHandle0 == 0) { printf("Cannot open device\n"); return -1; } int item; bool bWork = true; while (bWork) { printf("1 - Send 16 bytes\n"); printf("2 - Send 256 bytes\n"); printf("3 - Receive loop\n"); printf("0 - Exit\n"); scanf("%d", &item); switch (item) { case 0: bWork = false; break; case 1: { static const unsigned char data[0x10] = { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; DWORD dwWritten; FT_Write(ftHandle0, (void*)data, sizeof(data), &dwWritten); } break; case 2: { unsigned char data[0x100]; for (size_t i = 0; i < sizeof(data); i++) { data[i] = (unsigned char)i; } DWORD dwWritten; FT_Write(ftHandle0, (void*)data, sizeof(data), &dwWritten); } break; case 3: { DWORD dwRxBytes; DWORD dwRead; DWORD buf[0x100]; while (true) { FT_GetQueueStatus(ftHandle0, &dwRxBytes); if (dwRxBytes != 0) { printf("Received %d bytes (%d DWORDs)\n", dwRxBytes, dwRxBytes / sizeof(buf[0])); if (dwRxBytes > sizeof(buf)) { dwRxBytes = sizeof(buf); } FT_Read(ftHandle0, buf, dwRxBytes, &dwRead); for (DWORD i = 0; i < dwRxBytes / sizeof(buf[0]);i++) { printf("0x%X, ",buf[i]); } printf("\n"); } } } break; } } // , FT_Close(ftHandle0); return 0; }
从上一篇文章开始我们就熟悉函数OpenFT2232H()。是她打开了FT2232设备并将其置于我们需要的模式。程序成功启动后,我们立即获得时钟脉冲,并具有它们为NIOS II调试程序的能力。好吧,主要功能的功能就像凳子一样简单。发送一些数据(1),发送大量数据(2),接收数据(3)。请注意,所有数据均以四个字节的倍数的块形式发送。这就是全部,因为我们有一个8合32适配器。在它的出口处,数据应为双字。否则,一切都是显而易见的。为NIOS II开发程序时,应首先配置BSP。提醒您,我是根据Hello World Small模板创建程序本身的。 BSP中已更改的字段在下图中以红色标记(在上一篇文章中详细讨论了如何从模板创建程序以及如何更正BSP )。让我提醒您,我选择了树的根,即Settings元素,以便所有设置立即在右侧可见。
接下来,生成BSP,并根据我的习惯,将文件名hello_world_small.c更改为hello_world_small.cpp,此后我清理项目,以免重命名导致任何错误。我会从表面上对这项工作进行验证(真正的测试人员肯定会彻底测试超过FIFO大小的大量数据的传输,但是本文的目的是展示基本原理,而不是确保由于其疯狂的大小而导致无法读取)。我将分两个阶段展示基本原理。第一步是验证从中央处理器到NIOS II的数据传输。为此,我开发了以下代码: extern "C" { #include "sys/alt_stdio.h" #include <system.h> #include <altera_avalon_fifo_util.h> } #include <stdint.h> int main() { while (1) { int level = IORD_ALTERA_AVALON_FIFO_LEVEL(FIFO_0_OUT_CSR_BASE); if (level != 0) { alt_printf("0x%x words received:\n",level); for (int i=0;i<level;i++) { alt_printf("0x%x,",IORD_ALTERA_AVALON_FIFO_DATA (FIFO_0_OUT_BASE)); } alt_printf("\n"); } } /* Event loop never exits. */ while (1); return 0; }
该程序正在等待数据出现在FIFO中。如果它们出现在那里,请显示它们。进行测试。首先,我假装忘了开始计时。因此,在打开Redd之后,我加载了FPGA“固件”,然后尝试运行NIOS II的调试程序。我收到此消息:
如果两者相同,则意味着您真的忘记了开始为处理器系统计时。但是现在您知道了如何快速识别它。并且要消除,有必要并且充分地运行我们为中央处理器编写的程序。一旦启动并初始化FT2232桥接器,时钟脉冲将进入我们的处理器,并且可以重复开始调试的过程。而且,到那时可以完成用于中央处理器的程序。时钟脉冲将无处可去:桥已配置为FT245-SYNC模式。在中央处理器程序中,按1。根据情况,终端中将出现以下两种情况:接收到0x2个字:
0x3020100,0x7060504,
接收到0x2个字:
0xb0a0908,0xf0e0d0c,或:收到0x3个字:
0x3020100,0x7060504,0xb0a0908,
收到0x1个字:
0xf0e0d0c,原则上,可能有1个,然后是3个双字,但是我没有想到。这完全取决于在第一个显示开始之前总线上有多少时间运行字节。如果启动了它,那么到最后它所有其他字节都将有时间运行,因为通过JTAG进行数据传输不是一个快速的过程。如果总线使用了分组信号,则程序只有在完成分组接收后才能看到数据。在某些情况下,这很好(没有数据包,为什么我应该看到它?特别是如果数据包是暂时的),在某些情况下却很糟糕(FIFO是黑盒,对于最终处理,应将数据复制到可寻址的RAM中,最好与接收数据并行进行) )传输的数据以Little Endian表示法放在双字中。让我提醒您,以下数组已传递: static const unsigned char data[0x10] = { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };
没错
如果您在中央处理器的程序中选择第2项,则会出现一条消息(为了便于阅读,在准备本文时,各行均已格式化): 0x3 words received: 0x3020100,0x7060504,0xb0a0908, 0x3d words received: 0xf0e0d0c, 0x13121110,0x17161514,0x1b1a1918,0x1f1e1d1c, 0x23222120,0x27262524,0x2b2a2928,0x2f2e2d2c, 0x33323130,0x37363534,0x3b3a3938,0x3f3e3d3c, 0x43424140,0x47464544,0x4b4a4948,0x4f4e4d4c, 0x53525150,0x57565554,0x5b5a5958,0x5f5e5d5c, 0x63626160,0x67666564,0x6b6a6968,0x6f6e6d6c, 0x73727170,0x77767574,0x7b7a7978,0x7f7e7d7c, 0x83828180,0x87868584,0x8b8a8988,0x8f8e8d8c, 0x93929190,0x97969594,0x9b9a9998,0x9f9e9d9c, 0xa3a2a1a0,0xa7a6a5a4,0xabaaa9a8,0xafaeadac, 0xb3b2b1b0,0xb7b6b5b4,0xbbbab9b8,0xbfbebdbc, 0xc3c2c1c0,0xc7c6c5c4,0xcbcac9c8,0xcfcecdcc, 0xd3d2d1d0,0xd7d6d5d4,0xdbdad9d8,0xdfdedddc, 0xe3e2e1e0,0xe7e6e5e4,0xebeae9e8,0xefeeedec, 0xf3f2f1f0,0xf7f6f5f4,0xfbfaf9f8,0xfffefdfc,
一切也都是真实的。我们继续检查倒档。我们将NIOS II的程序替换为: /* - 2 */ uint32_t buf[] = {0x11223344,0x55667788,0x99aabbcc,0xddeeff00}; for (uint32_t i=0;i<sizeof(buf)/sizeof(buf[0]);i++) { IOWR_ALTERA_AVALON_FIFO_DATA (FIFO_1_IN_BASE,buf[i]); }
我们在程序中为中央处理器选择第3点,并为NIOS II运行此版本的程序。我们得到
:接收到16个字节(4个DWORD)0x11223344、0x55667788、0x99AABBCC,0xDDEEFF00,两个通道都工作不正常。我们会以其他方式检查出来。结论
本文讨论了Avalon-ST总线流协议的基础。通过该协议,可以组织Redd中央处理器与FPGA中实现的处理器系统的连接。读者了解了中央处理器和辅助处理器之间最简单的交互方法。在此处下载在开发过程中创建的项目。但是,有关流协议及其使用的知识非常基础。在后续文章中,将展示如何通过这些协议将数据有效地保存在Redd板上的动态RAM中。