为UDB开发时的灵感来源

好了,我们已经知道对UDB进行编程所需的一切。 但是要知道是一回事,而要知道又是另一回事。 因此,今天我们将讨论在何处以及如何汲取灵感以提高自己的技能,并在何处获得经验。 从文档翻译中可以看出,有一些干燥的知识并不总是与实际实践联系在一起的(我在很长的注释中提请注意这一点,直到最近的翻译为止)。 实际上,文章视图的统计数据表明,阅读翻译的人越来越少。 甚至有人提议打断这个周期,因为这没意思,但是只剩下两个部分,因此,最后,只是决定减少准备工作的速度。 通常,控制器的文档是必需的,但不是自给自足的。 还有什么可以得到启发的?



首先,我可以推荐出色的文档AN82156使用UDB数据路径设计PSoC Creator组件 。 在其中,您将找到典型的解决方案以及几个标准项目。 此外,在文档开头,使用UDB编辑器进行开发,最后使用Datapath Config Tool进行开发,也就是说,该文档涵盖了开发的所有方面。 但是不幸的是,从单个PSoC芯片的价格来看,我要说的是,如果它只能解决本文档中描述的问题,那么控制器将被大大高估。 无需PSoC即可完成PWM和标准串行端口。 幸运的是,PSoC任务的范围更加广泛。 因此,在阅读完AN82156之后,我们开始寻找其他灵感来源。

下一个有用的来源是PSoC Creator随附的示例。 我已经在公司文档翻译的一部分的注释中提到了它们(您可以在此处看到)。 它们大约存储在此处(磁盘可能有所不同):

E:\ Program Files(x86)\ Cypress \ PSoC Creator \ 4.2 \ PSoC Creator \ psoc \ content \ CyComponentLibrary。

您应该查找* .v文件(即verilog文本)或* .vhd,因为VHDL语言的语法需要更多描述,并且在这种语言中,有时您会发现Verilog程序员看不到的有趣细微差别。 麻烦的是这些不是示例,而是现成的解决方案。 很好,它们经过了完美的调试,但是对于简单的程序员,我们与赛普拉斯的程序员有不同的目标。 我们的任务是在短时间内做一些辅助工作,然后在我们的项目中开始使用它,这将花费我们大部分时间。 理想情况下,它应该解决今天分配给我们的任务,如果明天我们想将相同的代码插入另一个项目,那里的一切都会稍有不同,那么明天我们将在这种情况下完成它。 对于赛普拉斯开发人员而言,该组件是最终产品,因此他们可以将大部分时间花在该组件上。 他们必须为所有人提供一切。 因此,当我观看这些文字时,我感到难过。 对于刚开始寻找从哪里获得灵感的人来说,它们太复杂了。 但是这些文本作为参考非常合适。 创建自己的东西时,需要许多有价值的设计。

也有非常有趣的角落。 例如,现在,我将以“黄油”的形式说出用于建模的模型(很久以前,一位严厉的老师劝阻我不要以“建模”以外的任何其他方式来翻译模拟)。 它们可以在目录中找到。
E:\ Program Files(x86)\ Cypress \ PSoC Creator \ 4.2 \ PSoC Creator \ warp \ lib \ sim。

对于程序员来说,在Verilogue上最有趣的目录是:

E:\ Program Files(x86)\ Cypress \ PSoC Creator \ 4.2 \ PSoC Creator \ warp \ lib \ sim \ presynth \ vlg。

文档中组件的描述很好。 但是这里描述了所有标准组件的行为模型。 有时,这比文档(用繁重的语言编写,并省略一些基本细节)要好。 当该组件或组件的行为不清楚时,值得尝试通过查看此目录中的文件来准确地理解它。 最初,我尝试在Google上进行搜索,但是在很多论坛上,我碰到的只是推理而没有细节。 这就是具体细节。

不过,参考书很棒,但是在哪里可以找到教科书,可以从中学到什么呢? 老实说,没什么特别的。 UDB编辑器没有很多好的现成示例。 我很幸运,当我突然决定使用RGB LED时,我在UDB编辑器下遇到了一个漂亮的例子(我在开始整个周期的文章中对此进行了介绍 )。 但是,如果您使用搜索引擎进行大量工作,那么仍然会有“数据路径配置工具”的示例,这就是为什么我撰写上一篇文章的原因,以便每个人都可以理解如何使用此工具。 这里有一个精彩的页面,其中收集了许多示例。

此页面上的内容由第三方开发人员开发,但已通过赛普拉斯验证。 也就是说,正是我们所需要的:我们也是第三方开发人员,但我们希望从经过精确验证的内容中学习。 让我们看一个发现此页面的示例-平方根硬件计算器。 最终用户将其包含在信号处理路径中,从而将元件扔到电路上。 在此示例中,我们将训练以分析相似的代码,然后每个人都可以开始独立游泳。 因此,可以从链接下载必要的示例。

我们检查一下。 有示例(每个人都将独立考虑),并且在\ CJCU_SquareRoot \ Library \ CJCU_SquareRoot.cylib目录中有库。

对于每种类型(整数或定点)以及每个位,都有一个解决方案。 应该注意这一点。 在UDB编辑器中进行开发时,通用性很好,但是使用数据路径编辑工具进行开发时,正如您所看到的,人们会像这样受折磨。 如果您不能普遍做到这一点,请不要害怕(但如果效果更好)。

在顶层(电路),我不会停下来,我们正在研究的不是使用PSoC,而是使用UDB。 让我们看一个中等复杂度的选项-16位,但为整数。 它位于目录CJCU_B_Isqrt16_v1_0中。

首先要做的是扩展固件的过渡图。 如果没有它,我们甚至都不会猜测采用了哪种平方根算法,因为Google提供了几种根本不同的算法供您选择。



到目前为止,还不清楚,但是可以预见。 需要添加更多信息。 我们看一下状态编码。 令人惊讶的是,它们没有以通常的增量二进制代码进行编码。



我已经在文章中提到了这种方法,但是在特定示例中我从未使用过这种方法。 让我提醒您,RAM ALU动态配置只有三个地址输入。 也就是说,ALU可以执行八种操作之一。 如果自动机具有更多状态,则“每个状态都有自己的操作”规则将变得不可能。 因此,选择的状态中ALU的操作是相同的,它们具有提供给动态配置的RAM地址的三位(通常是低位),它们以相同的方式编码,其余的以不同的方式编码。 如何添加这样的纸牌已经是开发人员的问题。 所研究代码的开发人员完全如上所述折叠。

将此信息添加到图形中,并以相似的颜色对在ALU中执行相同功能的状态进行着色。



尚未显示任何模式,但是我们继续打开图表。 我们打开数据路径编辑工具,然后研究其中的逻辑。

请注意,我们有两个连接在一起的数据路径块。 当我们做自己的事情时,我们可能也需要这样做(尽管数据路径编辑工具可以创建已经链接成链的块,所以这并不可怕):



在读取(并填写)与ALU相对应的图形时,我们总是打开带有下图的文档:



的确,此示例的开发人员照顾了我们并填写了注释字段。 现在,我们可以使用它们来了解配置的内容。 同时,我们为自己指出,编写注释对于那些将随代码一起来的人以及对我们来说都是有用的,因为六个月后我们会忘记它的所有内容。

我们看一下与状态0和12对应的X000代码:



从注释中,已经很清楚那里发生了什么(将寄存器D0的内容复制到寄存器A0,将D1的内容复制到寄存器A1。了解了这一点,我们对未来进行了直观的了解,并在设置字段中找到了类似的条目:



在那里我们看到ALU在PASS模式下运行,移位寄存器也是PASS ,因此没有其他动作真正执行。

在此过程中,我们查看了Verilog中的文本,并看到寄存器D0和D1的值等于什么:



如果需要,可以通过选择“视图”->“初始寄存器值”在“数据路径配置工具”中看到相同的内容:





为了进行查看,直接分析Verilog代码以创建自己的版本更为方便-通过编辑器工作,以免记住语法。

同样,我们分析(首先是在评论中窥视)ALU的所有其他功能:



考虑到新知识,我们重做自动机的过渡图:



某种情况已经迫在眉睫,但是到目前为止,我还不能确定Google在此图中发现的任何算法。 相反,对于某些人,您可以自信地说不是他们,但即使是可信的人,我仍然无法给出肯定的答案是他们。 混淆了寄存器FIFO F0和F1的有效使用。 通常在文件中

\ CJCU_SquareRoot \库\ CJCU_SquareRoot.cylib \ CJCU_Isqrt_v1_0 \ API \ CJCU_Isqrt.c

可以看出F1用于传递参数并返回结果:



相同的文字:
void `$INSTANCE_NAME`_ComputeIsqrtAsync(uint`$regWidth` square) { /* Set up FIFOs, start the computation. */ CY_SET_REG`$dpWidth`(`$INSTANCE_NAME`_F1, square); CY_SET_REG8(`$INSTANCE_NAME`_CTL, 0x01); } … uint`$resultWidth` `$INSTANCE_NAME`_ReadIsqrtAsync() { /* Read back result. */ return CY_GET_REG`$dpWidth`(`$INSTANCE_NAME`_F1); } 


但是只有一种论据和一种结果。 为什么在工作过程中对FIFO的调用如此之多? FIFO0与它有什么关系? 把我切成碎片,但似乎作者利用了文档翻译中遇到的模式,当该模块代替一个完整的FIFO时,充当了一个寄存器。 假设作者决定扩展寄存器集。 如果是这样,那么它们的方法将对我们的实际工作有用。让我们研究细节。 实际上,文档讨论了使用FIFO的不同方法。 您可以-这样,您可以-这样,但您可以-这样。 并没有细节。 我们再次有机会了解最佳国际惯例。 作者如何处理FIFO?

首先,这些是信号分配:

  wire f0_load = (state == B_SQRT_STATE_1 || state == B_SQRT_STATE_4); wire f1_load = (state == B_SQRT_STATE_1 || state == B_SQRT_STATE_3 || state == B_SQRT_STATE_9 || state == B_SQRT_STATE_11); wire fifo_dyn = (state == B_SQRT_STATE_0 || state == B_SQRT_STATE_12); 

其次,这是到Datapath的连接:

  /* input */ .f0_load(f0_load), /* input */ .f1_load(f1_load), /* input */ .d0_load(1'b0), /* input */ .d1_load(fifo_dyn), 

从控制器的描述中,并不清楚这是什么意思。 但是从应用笔记中,我发现此设置应归咎于一切:



顺便说一下,正是由于此设置,无法使用UDB编辑器描述此块。 当这些控制位处于ON状态时,FIFO可以在不同的源和接收器上工作。 如果Dx_LOAD等于1,则Fx与系统总线交换,如果为零,则与此处选择的寄存器交换:



事实证明,F0总是与寄存器A0交换,并且在状态12和0中与F1交换-与系统总线(以上传结果并加载参数)在其他状态中与A1交换。
此外,从Verilog代码中,我们发现在F0中,数据将以状态1和4加载,而在F1中将以状态1、3、9、11加载。

将获取的知识添加到图中。 为了避免在操作序列中造成混乱,现在还应该用Verilogov的箭头替换分配标记“ la UDB Editor”,以强调信号源是进入模块之前信号的值。



从算法分析的角度来看,一切都已经很清楚了。 在我们面前是对这种算法的修改:

 uint32_t SquareRoot(uint32_t a_nInput) { uint32_t op = a_nInput; uint32_t res = 0; uint32_t one = 1uL << 30; // The second-to-top bit is set: use 1u << 14 for uint16_t type; use 1uL<<30 for uint32_t type // "one" starts at the highest power of four <= than the argument. while (one > op) { one >>= 2; } while (one != 0) { if (op >= res + one) { op -= res + one; res += one << 1; } res >>= 1; one >>= 2; } return res; } 

仅就我们的系统而言,它看起来更像这样:

 uint32_t SquareRoot(uint32_t a_nInput) { uint32_t op = a_nInput; uint32_t res = 0; uint32_t one = 1uL << 14; // The second-to-top bit is set while (one != 0) { if (op >= res + one) { op -= res + one; res += one << 1; } res >>= 1; one >>= 2; } return res; } 

状态4和10明确编码了字符串:

  res >>= 1; 

为不同的分支机构。

该行是:

  one >>= 2; 

它是由一对状态6和7或一对状态9和7显式编码的。现在,我要大声疾呼:“好吧,发明者是同一作者!”,但是很快就会明白为什么两个分支有这样的困难(在C代码中有一个分支和解决方法)。

状态2编码条件分支。 状态7对循环语句进行编码。 步骤2中的比较操作非常昂贵。 通常,在大多数步骤中,寄存器A0包含变量1。 但是在步骤1中,将变量1卸载到F0,而是加载值res + 1 ,然后在步骤2中执行减法以进行比较,并在步骤3和8中恢复了值1 。 为什么在步骤4中再次将A0复制到F0,我不明白。 也许这是一种雏形。

还有待弄清楚谁是res和谁op 。 我们知道条件比较op和res + 1。 在状态1中,将A0( 一个 )和A1相加。 因此,A1是res 。 事实证明,在状态11中A1也是res ,并且是进入F1的他,他被馈送到函数的输出。 状态1中的F1显然是op 。 我建议介绍变量裤子的颜色差异。 我们将res表示为红色,将op表示为绿色,将其中一个表示为棕色(对比度不高,但是其他颜色的对比度甚至更低)。



实际上,整个真相是揭示出来的。 我们将看到A1如何从F1暂时发生变化,以进行比较和计算,如何将相同的差用于比较(实际上是生成位C)以及参与公式。 我们甚至看到为什么C算法中的空白空间(旁路)由自动机的过渡图的长分支编码(在该分支中,寄存器的交换与在主代码分支中发生的交换相同)。 我们看到了一切。

唯一永不折磨我的问题是作者如何将FIFO切换到单字节模式? 该文档说,为此,您需要将辅助控制寄存器中的CLR位提升为一个单元,但是我看不到API中有类似的条目。 也许有人会理解这一点并写在评论中。

好吧,并使用所获得的技能以相反的顺序发展自己的东西。

结论


要开发基于UDB开发“固件”的技能,不仅可以阅读文档,而且可以从其他人的设计中汲取灵感。 PSoC Creator随附的代码可以用作参考,编译器随附的行为模型将帮助您更好地理解文档中的含义。 本文还提供了指向第三方制造商的一组示例的链接,并显示了解析此类示例之一的过程。

在此基础上,可以认为与UDB合作的版权文章周期已完成。 我很高兴他能帮助某人获得在实践中有用的知识。 前面有几本文档的翻译版本,但统计数据表明几乎没有人阅读它们。 他们的计划是干净的,以免使主题简而言之。

Source: https://habr.com/ru/post/zh-CN449960/


All Articles