跳转至

config 库

Caution

还没更新,可能是过期内容,谨慎参考

不难发现,随着往后推进,就越靠近硬件,功能就越特化。config 就是为了根据配置文件配置 FPGA,所以既有抽象的逻辑表达式部分,也有一点硬件细节。该库包含两个部分,用于读取配置文件的 LogicParser,和用于配置 FPGA 的 MemoryConfigLogicParser 会对原来的纯逻辑表达式进行一系列的拓展,以满足设置输入输出、逻辑、分除、定标器监视的需求。MemoryConfig就是纯粹的设置 FPGA 内存了,是非常枯燥重复的东西,但如果不了解 FPGA 中的内存细节,就很难理解里面的内容。

LogicParser

定义和实现在 include/config/logic_parser.hsrc/config/logic_parser.cpp

前面提到了,这个类就是用来读取配置文件的,而配置文件的语法在配置语法一节中已经说明了。配置语法与之前一直处理的逻辑表达式对比,区别主要体现在两点:

  • 每个表达式都变成了赋值语句,增加了左式和等于号
  • 增加了定义分除的语法,会在右式的最右边增加除号和一个数

当然,好消息是去掉这些不同的,剩下的都是相同的,可以套用之前的代码处理。一个简单的想法就是,我们去掉这些不同点就好了。

方法 Read 用来读取文件,因为我们要一行行读取,所以读取后使用方法 Parse 来处理一整行的表达式。所以 Parse 也是关键之处,它将先处理掉和纯逻辑表达式不同的部分,再用之前的方法处理逻辑表达式,最后将两者结合,记录下有用信息。

处理拓展语法

因为引入了赋值和分除语法,所以在词法分析阶段就要进行拓展。我们首先看右式,右式拓展了分除语法,所以使用方法ExtendDividerLex来处理,基本想法就是先看看有没有除号,有的话就记录下来,标示一下,然后去掉;然后对剩下的纯逻辑表达式部分用 Lexer 处理。看完了右式,还不能忘记左式,所以我在ExtendDividerLex 外面又包装了 ExtendLeftLex,这个函数会先调用 LeftSideLex处理左式和等于号,然后抽出右式传递给ExtendDividerLex。如此即完成了拓展语句的词法分析。

接下来是语法分析,因为除了常规的或门和与门外,还要处理分除后的与或;前面的配置语法也提到了,如果右式有分除,必须作为右式的第一个变量。这是因为在 FPGA 的内部实现中,分除结果和其他东西的与或使用的不一样的逻辑,我一般称之为分除门,不是使用常规的与门和或门,所以需要额外的处理。这里使用方法 ExtendDividerParse 来处理。该方法流程如下

  • 对表达式进行语法检查,检查通过后开始解析表达式
  • 检查是否使用了分除,如果有,就先跳过
  • 解析不包含分除的表达式,就是直接调用 SLRSyntaxParser::Parse 来处理
  • 标准化表达式
  • 根据标准形式的逻辑表达式记录要生成的与门和或门,或者生成时钟,通过调用 GenerateGates 处理
  • 重新处理分除门,调用 GenerateDividerGate 实现

语法检查

上述 ExtendDividerParse 的第一步就是语法检查,包括变量名检查和冲突检查两部分。因为配置文件的语法固定了变量名,所以变量名检查包括

  • 变量名是否符合语法要求
  • 该变量能否在左式或者右式出现
  • 左式和右式的组合是否有意义。

冲突检查会检查

  • 一个端口是否设置了多个输出来源
  • 一个端口是否同时被定义为输入和输出端
  • 一个前面板端口是否同时被设置为网口和 LEMO 口输入或者输出
  • 一个定标器是否被设置为监视多个信号
  • 使用分除前是否对其定义

生成门

GenerateGatesGenerateDividerGate 可能会令人稍许迷惑,因为标准形式的表达式实际上已经隐含了与门和或门。所以这两个函数实际上不产生任何新的门,但是会提取表达式中关于与门和或门的信息,甄别多行表达式中的重复的门,将这些门记录成更贴近 FPGA 中设置的样式。简单来说,就是表达式中的门的元素,对应的是子表达式和变量,而这里记录的门的元素,代表变量和其他门的序号。比如说表达式 A0 & A1,产生的与门会记录下信息0和1,用来表示 A0 和 A1,所以我说是更贴近 FPGA 中的样式。这段写的有点乱,看不懂的还是看代码吧。

获取结果

解析后的结果可以通过一系列方法得到。比如说 OrGateSize 获取或门的数量,OrGate 获取特定或门的信息,具体见代码。

小结

LogicParser 就是读取配置文件,将配置信息转化成贴近 FPGA 的与门、或门、分除、定标器信息,方便 MemoryConfig 读取。

MemoryConfig

定义和实现在 include/config/memory_config.hsrc/config/memory_config.cpp

前面提到过,这个类 MemoryConfig 就是很枯燥的东西,基本上是输入输出。其内部有一个结构 Memory,用来保存和 FPGA 对应的内存值,所有需要写到 FPGA 内的值都会保存在这里面,比如说网口的输入输出、或门与门的设置、时钟、分除等的设置。

MemoryConfig 有两个 Read 函数用于读取配置,可以从 LogicParser 或者低级配置文件中读取。有一个 Write 函数用于写低级配置文件,还有输出流 operator<< 用于将内存内容打印到屏幕中。

因为要用于设置 FPGA,我设置了函数 MapMemory 将保存的设置写入到 FPGA(因为这了使用到 xillybuslite,所以可以依靠内存映射直接把设置写入到 FPGA中)。后者也可以通过 GetMemory 得到前面提到的指向内部结构体 Memory 的指针。

除此之外只有 ConvertSource 可能令人略微迷惑,这是因为我在 LogicParser 中对不同信号的序号设置和 FPGA 中不一样,所以从 LogicParser 中读取设置时需要进行转换。

这就是这个类的内容了,主要就是输入输出,我个人觉得都是重复劳动,没啥可以细说的。

总结

config 库分为了两部分,LogicParser 用来读取配置文件,解析其中的表达式并转换成中间数据,MemoryConfig则是将中间数据转换成和 FPGA 内存对应的数据,以方便映射,或者是读/写低级配置文件。当然,MemoryConfig 中的 Memory 其实不完全和 FPGA 的内存对应,还有一定的改进空间。