代码之家  ›  专栏  ›  技术社区  ›  Robert Deml

Flash中的循环缓冲区

  •  6
  • Robert Deml  · 技术社区  · 15 年前

    我需要在闪存芯片中以循环队列存储不同长度的项目。每个项目都有它的封装,所以我可以计算出它有多大以及下一个项目从哪里开始。当缓冲区中有足够的项时,它将换行到开头。

    我可能会储存数万件物品。因此,从缓冲区的开头开始并读取到结尾并不理想,因为搜索到结尾需要时间。

    另外,因为它是循环的,所以我需要能够区分第一项和最后一项。

    最后一个问题是,这是存储在闪存中的,因此擦除每个块既耗时又只能对每个块执行设定次数。

    5 回复  |  直到 11 年前
        1
  •  14
  •   Craig McQueen Dr. Watson    14 年前

    首先,区块管理:

    在每个块的开头放一个较小的标题。您需要跟踪“最旧”和“最新”的主要内容是块编号,它只是增加模 . k k 小于您的最大值(例如0xFFFF),因此您可以很容易地判断什么是已擦除的块。

    =(n) +1)模k。注意不要被擦除的块(块编号为0xFFFF)或以某种方式损坏的数据(如不完全擦除)弄糊涂。

    每个街区内

    每个块开始时为空(每个字节为0xFF)。每条记录只是一条接一条地写。如果您有固定大小的记录,那么您可以通过一个简单的索引来访问它。如果您有可变大小的记录,那么要读取它,您必须从块的开始扫描,链表样式。

    如果您希望有可变大小的记录,但避免线性扫描,那么您可以在每个记录上有一个定义良好的标题。例如,使用0作为记录分隔符,以及 COBS -编码(或 COBS/R PPP protocol ).

    启动时,一旦知道最新的数据块,就可以对最新记录进行线性扫描。或者,如果您有固定大小的记录或记录分隔符,则可以进行二进制搜索。

    擦除调度

    对于某些闪存芯片,擦除块可能需要很长的时间,例如5秒。考虑提前一点将擦除任务安排为后台任务。例如,当当前块已满x%时,开始擦除下一个块。

    您可能需要对记录进行编号。我过去的做法是在每个块的标题中输入第一条记录的记录编号。然后,软件必须对块内每条记录的编号进行计数。

    校验和或CRC

    如果要检测损坏的数据(例如,由于意外电源故障导致的不完整写入或擦除),则可以向每个记录,甚至可能向块头添加校验和或CRC。注:块头CRC只覆盖头本身,而不覆盖记录,因为写入每个新记录时无法重新写入。

        2
  •  2
  •   Timm    15 年前

    在最初耗尽空间之前,添加记录非常简单,只需将它们写入缓冲区的末尾并更新尾部指针即可。

    当您需要回收空间时,请删除足够的记录,以便适合当前记录。删除记录时更新头指针。

    您需要跟踪释放了多少额外空间。如果保留指向最后一条记录末尾的指针,下次需要添加记录时,可以将其与指向第一条记录的指针进行比较,以确定是否需要删除更多记录。

    此外,如果这是NAND,您或闪存控制器将需要执行解块和磨损均衡,但这都应该位于比为循环缓冲区分配空间更低的层。

        3
  •  1
  •   Mikeb    15 年前

    我想我现在明白了。似乎你最大的问题是,在填满可用的录音空间后,接下来会发生什么?新数据应该覆盖最旧的数据,我相信这就是你所说的循环缓冲区。但由于数据不是固定长度的,您可能会覆盖多条记录。

    写入段需要跟踪表示下一条记录开始写入的地址。如果您提前知道要写入的块的大小,则可以判断是否将在逻辑缓冲区的末尾结束,并在“0”处重新开始。我不会把一张唱片分成结尾和开头两部分。

    一个单独的寄存器可以跟踪开始;这是尚未被覆盖的最旧数据。如果你去读取数据,这就是你要开始的地方。

    然后,给定写入开始地址和要提交的数据长度,数据写入器将检查是否应该撞击读寄存器,读寄存器将检查第一个块并查看长度,并前进到下一个记录,直到有足够的空间写入任何数据。在写入数据的末尾和最旧数据的开头之间,可能存在垃圾数据的间隙。但通过这种方式,您可以只编写一两个地址作为开销,而不是重新排列块。

        4
  •  0
  •   Mark    15 年前

    我认为有三种选择:

    选项1:将所有内容填充到相同的大小,这很简单,存储一个指向缓冲区头部和尾部的指针,以便知道从何处写入和从何处开始读取,使用每个对象的大小获得到下一个对象的偏移量,这意味着您需要像链接列表一样横切缓冲区,如果您需要项目5000,则称之为慢速。

    选项2:只在循环缓冲区中存储指向真实数据的指针,这样当您循环时就不必处理大小不匹配的问题。如果将真实数据存储在循环缓冲区中,而不将其填充,则可能会遇到这样的情况,即使用一个新数据对象过度编写多个项,我认为这是不正确的。

    将实际数据存储在闪存中的其他位置,大多数闪存都内置了某种磨损均衡功能,如果这样,您就不必担心多次覆盖同一位置,IC将找出实际存储在芯片上的位置,只需写入下一个可用的可用空间即可。

    选项3:如果对象真的可以是任意大小,那么在您应该只使用文件系统的时候,请按顺序命名文件,并在您完全记住时返回。如果您的新条目很大,则可能需要删除多个旧条目以适应它。这实际上只是选项2的扩展,因为选项2在许多方面都是一个简单的文件系统。

        5
  •  0
  •   Barak C    15 年前

    闪存中的“循环”可以根据块大小进行,这意味着您必须声明为该缓冲区分配了多少闪存块。

    缓冲区的实际大小将在每个特定时间介于n-1(n是块的数量)和n之间。

    每个块都应该从一个包含序列号或时间戳的头开始,该序列号或时间戳可用于确定哪个块比另一个块旧。

    每个项目都包含一个页眉和一个页脚。默认标题包含您想要的内容,但根据此标题,您必须知道项目的大小。默认页脚为0xFFFFFF。此值表示空终止。

    在RAM中,必须保存指向最旧块和最新块的指针,以及指向最旧项和最新项的指针。通电后,您将检查所有块,找到相关块并加载此成员。

    如果要存储新项目,请检查最新块是否包含足够的空间来存储该项目。如果确实如此,请将项目保存在上一个项目的末尾,并将上一个页脚更改为指向此项目。如果它没有足够的空间,则需要删除最旧的块。删除此块之前,请将最旧的块成员(RAM)更改为指向下一个块,将最旧的项更改为指向此块中的第一个项。

    我知道这个解释听起来可能很复杂,但过程非常简单,如果你写得正确,你甚至可以保证电源安全(记住写的顺序)。

    请注意,缓冲区的循环度不会保存在闪存中,但闪存中只包含一个包含项目的块,您可以根据块标题和项目标题确定这些项目的顺序