Flash中的循环缓冲区

时间:2009-11-03 18:25:47

标签: embedded queue circular-buffer flash-memory

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

在闪存芯片中存储循环队列的好方法是什么?

我想存储数以万计的商品。所以从头开始读到缓冲区的末尾并不理想,因为搜索结束需要时间。

另外,因为它是圆形的,我需要能够区分第一个项目和最后一个项目。

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

5 个答案:

答案 0 :(得分:10)

首先,阻止管理:

在每个块的开头放置一个较小的标题。跟踪“最旧”和“最新”所需的主要内容是块编号,只需增加模数 k k 必须大于您的总块数。理想情况下,使 k 小于MAX值(例如0xFFFF),这样您就可以轻松判断什么是擦除块。

启动时,您的代码依次读取每个块的标题,并定位序列中的第一个和最后一个块,即n i + 1 =(n i < / sub> + 1)MODULO k。注意不要被擦除的块(块号例如0xFFFF)或以某种方式损坏的数据(例如不完全擦除)弄糊涂。

在每个区块内

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

如果您想拥有可变大小的记录,但避免线性扫描,那么您可以在每条记录上都有一个定义良好的标题。例如。使用0作为记录分隔符,COBS - 编码(或COBS/R - 编码)每条记录。或者使用您选择的字节作为分隔符,如果它出现在每条记录中,则“转义”该字节(类似于PPP protocol)。

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

删除时间安排

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

记录编号

您可能想要编号记录。我过去做的方法是在每个块的标题中放入第一条记录的记录号。然后软件必须计算块内每条记录的数量。

校验和或CRC

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

答案 1 :(得分:2)

保留一个单独的块,其中包含指向第一条记录的开头和最后一条记录的结尾的指针。您还可以保留更多信息,例如记录总数等。

在您最初用完空间之前,添加记录就像将它们写入缓冲区末尾并更新尾指针一样简单。

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

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

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

答案 2 :(得分:1)

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

我假设长度的变化量足够高,无法将所有内容填充到固定长度。

您的写段需要跟踪表示要写入的下一条记录的开头的地址。如果您知道要提前写入的块的大小,则可以判断是否要在逻辑缓冲区的末尾结束并从“0”开始。我不打算将一些记录分成最后一些,一些记录在开头。

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

然后数据写入器将检查写入起始地址及其即将提交的数据长度,如果它应该使读取寄存器碰撞,这将检查第一个块并查看长度,然后前进到下一个记录,直到有足够的空间来写任何数据。可能存在写入数据结束和最旧数据开始之间的垃圾数据差距。但是这样,你可以只写一两个地址作为开销,而不是重新排列块。

至少,这可能就是我要做的。 HTH

答案 3 :(得分:0)

我看到三个选项:

option1:将所有内容填充到相同的大小,这很简单,存储指向缓冲区头部和尾部的指针,以便您知道在哪里写入以及从哪里开始读取,使用每个对象的大小来得到一个偏移到下一个,这意味着你需要像链接列表那样横向缓冲区,如果你需要项目5000,它也很慢。

option2:只存储指向循环缓冲区中实际数据的指针,这样当你循环时就不必处理大小不匹配。如果你将真实数据存储在一个循环缓冲区中并且不填充它,你可能会遇到过多使用一个新数据对象的多个项目的情况,我认为这是不正确的。

将实际数据存储在闪存中的其他地方,大多数闪存都会内置一些磨损均衡,如果这样你不需要担心多次覆盖同一个位置,IC会找出实际存储它的位置在芯片上,只需写入下一个可用空间即可。

这意味着您需要为循环缓冲区选择最大大小,具体取决于数据的可变性。如果数据的大小变化很大,比如说只有几个字节,那么你应该将其填充并使用选项1.如果大小变化很大且不可预测,选择它可能的最大尺寸并找出有多少个对象该大小适合您的闪存,使用它作为缓冲区中的最大条目数。这意味着你浪费了一大堆空间。

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

答案 4 :(得分:0)

闪存中的“循环”可以基于块大小来完成,这意味着您必须声明为此缓冲区分配的闪存块数。

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

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

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

在RAM中,您必须保存指向最旧块的指针以及指向最旧项目和最新项目的最新块和指针。上电后,您将查看相关块并加载此成员。

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

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

请注意缓冲区的圆形度不会保存在闪存中,但闪存只包含一个块,其中的项目可以根据块标题和项目标题来决定这些项目的顺序是什么