如何让图书馆的工作发挥作用?

How to make the library work work?

我的同事们一直担心将当前工作库的名称用作 work(指向当前库的别名)而不是明确地使用名称(例如 mylib ).

例如,如果您要引用同一个库中的另一个设计单元,您可以这样做:

my_inst : entity work.my_design

my_inst : entity mylib.my_design

我觉得使用 work 更灵活,因为它不依赖于您将设计编译到的库的名称。您可以将 mylib 重命名为 myawesomelib,它仍然有效。

我能想到 work 优势的一个明显例子是,如果您通常将设计单元和测试平台编译到同一个库中。那么,在您的测试台中,通过 work.

引用您的 UUT(设计)总是安全的

一个缺点是,如果(如上例所示)my_design 不再位于同一个库中,设计将停止工作;如果 my_design 或任何使用 my_designwork 的设计被分离,设计将被破坏并且必须更新参考。

在显式库名称上使用 work 还有其他明显的缺点吗?在分层使用 work 的情况下,是否有可能混淆哪个库是 "current working library"?

例如:

mylib1
   (top_design.vhd)
   ...
   my_inst1 : entity mylib2.my_design
   ...

mylib2
   (my_design.vhd)
   ...
   my_inst2 : entity work.my_sub_design
   ...

在编译 top_design.vhd 时,是否对 mylib2 包含的设计中的 work 引用有任何混淆?或者因为 my_design.vhd 已经被编译成 mylib2 就没有混淆了?

脚注:Never name your libraries work.

哦工作。我怎么恨你?让我来计算一下。

  1. 当一切正常时,代码依赖于编译顺序。最后编译的东西是什么棒。
  2. 代码容易出现冗余。每个 单个 常量都必须命名为完全不同的东西,这在大型设计中几乎是不可能的。
  3. 适合在多个地方声明 components。这些 component 声明经常发生冲突!
  4. 我见过在多个包文件中定义相同的常量和类型,一遍又一遍,又一次发生冲突。
  5. 没有杠杆能力

请,请,请使用库!他们在那里是有原因的。它使您的代码更有条理、更灵活、更便携。

组织

库应位于其自己的文件夹中。我喜欢将所有内容都放在一个顶级文件夹下,例如串口。在那之下,我将拥有 4 个目录:build、source、sim、docs。这样,您的模拟和您的文档都与库代码一起移动!好甜啊!?您无需担心在哪里可以找到那个愚蠢的 UART sim,您将始终知道它在哪里。顺便说一句,这意味着自检测试台是强制性的!我代表的不外乎。

灵活便携

如果您正在为一个项目编写代码,您会做出假设。 "Oh my clock is 25 MHz, so I can make this a constant." 但是,如果您为库编写代码,您会意识到自己做出了错误的假设。在 UART 示例中,您需要将波特率设置为 generic 而不是常数。这使您的代码更灵活,更易于其他人使用。

打包文件

每个库应该有一个包文件。您的所有组件都应包含在此包文件中。这样,如果您的实体发生变化,您只需要更新包文件中的组件即可。包文件应包含该库中使用的所有常量、函数和过程。同样,这允许您在 1 个地方进行更改。

结论

库是制作周到、可重用、易于移植的代码的方式。当我看到所有代码都编译成工作时,我感到畏缩。

我避免使用 work,因为它隐藏了您期望在其中找到标识符的上下文。将库名称包含在 mylib.foo 中可以明确说明 [=12] =] 你指的是与 work.foo 相反的东西,它是当前库中的 某处 ,无论那是什么。如果你想从它的库之外引用 foo,你无论如何都不能使用 work,所以你最终可能会根据你在层次结构中的位置而得到不同名称引用的同一个对象。

随意更改库名称的灵活性在现实世界中并不是特别有用,只会造成不必要的复杂性。将默认库命名为 work 的做法以及工具处理该库的不同方式进一步加剧了这种情况。选择一个好的库名称并坚持使用。我发现 work 最终成为那些懒得将可重用代码划分为逻辑部分的人的拐杖。即,将其全部转储到 work 并希望没有冲突。

我认为这是有趣的问题之一,因为没有明确的答案。这取决于。对我来说,这取决于项目背景和个人喜好。

@乔希。我不认为在重构库时您 可能 必须更改 work 引用的风险是反对使用 work 的论据。对于命名引用也是如此。

@拉塞尔。使用包而不是直接实例化仍然会给您留下 use x.uart_pkg.all; 中的 x 应该是什么的问题。不过,您将拥有更少的参考资料(以包中更多代码为代价)。

@凯文。我同意 mylib.foo 更明确,但我不认为我知道哪个 foo 被编译成 mylib 但我不知道引用文件也是 mylib 的一部分,也就是说,我会对 work.foo 中的 foo 所指的内容感到困惑。我没经历过,不是说不能这样。

现在你可能已经猜到我更喜欢使用 work。一个原因是我认为模块化是一种很好的设计,这意味着要避免依赖性。如果我可以避免依赖于我编译到的库的名称,那么在需要更改库名称时我可以降低代码更改的风险。我同意,如果您使用良好的库描述性名称,库名称问题就不会很常见,但它仍然会发生,至少对我而言:

  • 与外部库的名称冲突确实会发生,当它们发生时,如果您可以在不更改代码的情况下处理这种情况,那就更好了。特别是如果您决定从他们的代码中删除命名引用。他们可能会发布新版本,因此您必须重新做一遍。
  • 有时并没有真正的冲突,但外部库是用一个未建立的缩写命名的,你想要一个更具描述性的名称。有时该缩写会与您自己的缩写发生冲突,含义完全不同。
  • 当 VHDL 2008 标准将 vunit 设为保留字时,我什至让我的 vunit 库被劫持了。

还有一些项目您的策略并不重要。例如,您自己完成所有编码并将所有内容编译到一个库中的小型项目,因为它很简单。但是,我仍然会使用 work,因为它去掉了一些库语句并使事情变得更加简单。

我是 PoC-Library 的作者之一。我们决定使用 PoC 作为唯一的 VHDL 库名称。所有实体和包都被编译到这个单一的库中。因为 PoC 带有很多文件(~ 100)我们决定将 PoC 划分为子命名空间(也许有一天 VHDL 会支持这样的功能......)。

示例:
除法器组件被归类到算术模块的子命名空间 arith 中。该实体在我们的文档中(实际上)被引用为 PoC.arith.div。该实体名为 arith_div,位于名为 arith_div.vhdl 的文件中。每个子命名空间提供一个组件、类型、函数和常量包:例如arith.pkg.vhdl。实体可以通过两种方式实例化:

  1. 使用来自 arith.pkg.vhdl
    PoC.arithmyDiv : arith_div

  2. 使用实体关键字
    myDiv : entity PoC.arith_div

子命名空间层次结构最多可以有 2 层。一个例子是我们的片上 RAM PoC.mem.ocram.* 具有不同的端口变化:sp、sdp、esdp、tdp。为了缩短实体名称,我们只使用最后一个子命名空间作为名称的前缀,例如ocram_tdp。子命名空间名称必须是唯一的。

大多数实例引用都使用实体语法:myInst : entity PoC.xxx_yyy.

所以是的,我们希望我们的用户将所有源编译为 PoC :)。

我们如何组织测试平台、文档等?

与@Russell 的模式相反,PoC 对每个设施使用相同的目录结构:

o-netlist (precompiled netlists for a specific FPGA device)
|   o-<FPGA-NAME>
|     o-mem
|       o-ocram
o-sim (waveform configuration files: *.wcfg, *.wdo, *.gtkw)
| o-io
| | o-iic
| | o-uart
| o-mem
| | o-ocram
o-src (sources as *.vhdl or *.v files)
| o-io
| | o-iic
| | o-uart
| o-mem
| | o-ocram
o-tb (testbenches as *.vhdl or *.v files)
| o-io
| | o-iic
| | o-uart
| o-mem
|   o-ocram
o-xst (synthesis instruction to generate netlists of components)
| o-mem
|   o-ocram