2025年2月更新,Step to UEFI 文章索引:
一键USB通断器
在一些特别情况下,我们会碰到 USB 兼容性问题。比如,我正在使用的 DELL C2722DE 显示器,通过 TypeC 连接主机,除了能够显示之外,还内置了 USB Hub ,但是它和我的微软鼠标存在兼容性问题,如果插着显示器休眠,或者开机的话,进入 Windows之后鼠标是无法直接使用,必须插拔一次才能正常工作。
针对这种情况,这次设计了一个 USB 插拔装置,遇到问题的时候无需做插拔动作,而是通过按键一次自行完成一个插拔的模拟,这样可以方便使用。USB插拔动作可以分解为2步,插入时是先接通5V供电,然后接通D+/D-信号。如果仔细观察USB接头,可以发现USB D+/D- 引脚会比5V和GND 短一点,这样就能保证插入的时候是先接通供电,然后再接入信号的。这样可以避免插入时信号先于供电接通,电流直接倒灌到芯片中。类似的,拔出时,是先断开信号,然后再切断供电。
硬件方面使用了3个芯片:CH554 /CH442/SY6280AAC。CH554是一个单片机,这里作用是获得按钮状态,然后根据状态控制USB信号的切换和USB母头上的5V输出; CH442进行信号切换, SY6280AAC 是功率电子开关芯片,这里我们用用它控制USB供电输出。
CH442是一款低阻宽带双向模拟开关芯片,包含2路单刀双掷二选一开关。这里用作 USB 信号二选一。


PCB 设计如下:

焊接后的成品,刚好能装在外壳中:

CH554 外围只需要1个10K电阻,2个0.1uf 电容即可让它工作起来。然后通过P1 Header 4 下载数据非常方便。此外,CH_IN 适用于控制CH442信号切换,CTRL1用于控制SY6280AAC。
代码部分非常简单:
#define CH_IN 14
#define LED 15
#define CTRL1 16
#define KEY 17
void setup() {
pinMode(CH_IN,OUTPUT);
pinMode(LED,OUTPUT);
pinMode(CTRL1,OUTPUT);
pinMode(KEY,INPUT_PULLUP);
digitalWrite(CH_IN,LOW); // Switch to 1
digitalWrite(LED,LOW); // 不亮
digitalWrite(CTRL1,HIGH); // 供电
}
unsigned long int Elsp=0;
void loop() {
if (digitalRead(KEY)==LOW) {
digitalWrite(LED,HIGH); // 点亮 LED
digitalWrite(CH_IN,HIGH); // 断开数据线
delay(100);
digitalWrite(CTRL1,LOW); // 断开USB供电
Elsp=millis();
} else {
// Key 抬起
if ((Elsp!=0)&&(millis()-Elsp>500)) {
Elsp=0;
digitalWrite(LED,LOW); // 关闭 LED
digitalWrite(CTRL1,HIGH); // 开始USB供电
delay(100);
digitalWrite(CH_IN,LOW); // 接通数据线
}
}
}
适用范围:如果你遇到的问题通过插拔一次可以解决,并且出现问题的时候USB端口能够正常供电,那么可以尝试本次的设计。运气好的话,因为引入了切换元件使得USB信号发生变化,每次都可以直接用;稍微差一些的话,出现问题的时候按下按钮即可模拟插拔让设备工作起来。
制作一个PCIE设备的空驱动
一些情况下我们需要空驱动来避免设备管理器中出现 Yellow Bang(当然,这种驱动没有任何功能)。这个问题我请教了一下天杀,在他的指导下写了这篇问斩给。
本文介绍如何制作一个 PCIE 的空驱动。
本文提到的软件包括 OpenSSL (来自 https://slproweb.com/products/Win32OpenSSL.html的Win64OpenSSL_Light-3_5_0.exe),此外,签名使用到的文件都来自 Windows SDK 和 WDK。
一.准备用于签名的密钥文件
1. 生成私钥:openssl genrsa -out LABZprivate.key 2048
运行结果:

2. 创建证书签名请求(CSR):
openssl req -new -key LABZprivate.key -out LABZrequest.csr
运行结果:

3.生成自签名证书(.crt)文件
openssl x509 -req -days 365 -in LABZrequest.csr -signkey LABZprivate.key -out LABZcertificate.crt
运行结果:

4.转换为 pfx 格式
openssl pkcs12 -export -out LABZPFX.pfx -inkey LABZprivate.key -in LABZcertificate.crt
会要求你设定一个密码,这里使用 “labz123”
运行结果:

5. 生成 cer 文件
Openssl pkcs12 -in LABZPFX.pfx -clcerts -out LABZcert.cer -nodes
这里输入前面设定的密码 labz123

至此,需要用到的签名的密钥文件已经准备好了。
此外,还可以不用openssl来生成证书,WDK提供了一个更简单的生成自签名证书的工具:makecert.exe。有兴趣的朋友可以继续研究。
二.修改 INF文件
以如下设备为例,查看属性

将上述信息写入EmptyPciDriver.inf

三.使用工具生成 CAT文件以及签名
1.生成 CAT 文件(可以看作是 INF 的签名文件)
“C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\inf2cat.exe” /driver:”C:\ndrv ” /os:10_NI_X64

2.生成 INF 的 Hash
“C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe” sign /f LABZPFX.pfx /p labz123 /fd sha256 “c:\ndrv\EmptyPciDriver.cat”

四.测试
1.将前面的公钥加入目标机中(只用于测试,不要用于工作机)
Certutil -addstore root LABZcert.cer
2.将公钥加入受信任的发布者列表中
Certutil -addstore TrustedPublisher LABZcert.cer
然后像普通驱动一样安装即可:

安装后设备管理器中可以看到:

测试完成之后,拿去做正式签名,之后对于相同 VID DID SVID SDID的PCIE设备就可以直接使用了(无需步骤四提到在目标机中导入公钥的步骤)
本文提到的 INF 文件下载:
本文生成的签名文件:
ImageMagick VC 代码的一些细节
大多数时候命令行足够用了,但是对于一些无法写入同一条命令行的组合操作,如果直接使用 VC 编程可以大大提升效率。
// 读取文件
image.read(filename);
// 改变大小
image.resize(Geometry(image.columns() * resize/100, image.rows() *resize / 100));
// 设置背景颜色用于填充
image.backgroundColor(Color("Yellow"));
//旋转
image.rotate(angle);
// 特别主意,如果没有下面这个 repage 会导致计算坐标有问题
image.repage();
//以中心为原点裁剪图片
image.crop(Geometry(1920, 1080, (image.columns()/2 - 1920/2), (image.rows()/2 - 1080/2)));
// 保存图片
image.write(Output);
参考:
C# 编写的UEFI迷宫游戏
这次介绍的迷宫游戏是C#编写的3D游戏。基于前面介绍的bflat编译完成。项目地址在 https://github.com/MichalStrehovsky/uefimaze/tree/master。
下载好代码之后就可以进行编译(特别需要注意的是,需要在所在的 src目录下进行编译。如果你没有将 bflat放入path中,那么需要给出它的绝对路径):
bflat build --os:uefi --stdlib:zero -o:bootx64.efi

上下左右四个按键移动视角。编译之后生成bootx64.efi。下面是在 VirtualBox上测试的视频:
源代码:
编译后的代码:
在 Arduino Serial Plotter 上绘制正弦波的例子
void setup() {
Serial.begin(9600); // 初始化串口通信
}
void loop() {
static float angle = 0; // 角度变量
static const float step = 0.1; // 角度增量
// 计算正弦值(范围0-1)
float sinValue = (sin(angle) + 1) / 2; // 调整到0-1范围
// 输出到串口绘图仪
Serial.println(sinValue * 100); // 放大到0-100范围
angle += step; // 增加角度
if(angle >= 2*PI) angle = 0; // 重置角度
delay(50); // 控制波形刷新速度
}
运行结果

我的故乡:大庆市
当我写下标题的时候,忽然有一种30年前坐在教室里完成老师作文题目的感觉。相比散装的江苏省,东北一直是“整装”的。这句话的意思是,对于江苏人来说他们永远都会介绍自己是苏州人或者南京人,很少会碰见自称江苏人。但是对于东北人来说,无论黑龙江吉林或者辽宁人,通常都会自称:“东北人”。
我在大庆生活到了18岁,然后离开了这个城市去成都求学读书。相当于从祖国的最东走到大西南。大学四年一直是火车来回,当时并没有直达列车需要在北京中转。从大庆到北京,最快18小时;然后再坐27小时50分的车从北京到成都。好在路上总是有同学相伴,加之年轻并不会觉得无聊。三天的路程也只是弹指一挥间。
大庆是一座在荒原上平地而起的城市,它名称的来源是 1959年恰逢建国十周年,发现的大庆油田。等到了1979年,更名为大庆市。也因为它的“年轻”,以至于很多大庆子弟离开之后到其他地方都会觉得为什么当地的楼房那么旧。

我住的地方叫做“让胡路区”,关于这个名称的来源有一些说法:
1954年建滨洲铁路时设让胡路站,区以站名;又说因附近水泡子形状像芽葫芦(轧葫芦、亚葫芦)得名;轧葫芦泡西北端跑县城百里,南北斜长约十数里,两端水阔中间狭细如轧葫芦,故名;相传早年有一家山东人迁移到此,因水泡子其开头很象关里家的轧葫芦,故名轧葫芦泡;1954面简化为让胡路。【参考1】
大庆市的出名是因为“大庆油田”,从历史上来说,大庆市是先有的企业(油田),然后才有的市政府。在我小时候,那个只有“座机”的时代,这样的割裂非常明显。那时候大庆有2种座机号码,5开头的管理局号码(当年我家就是管理局号,现在我还记得很清楚“553455”)以及6开头的市政号码。两种号码经常无法互通,需要拨号很多次才能侥幸打通。如果想成功直播,那就只能在前面加上区号:0459。只是这样做的话,是按照长途收费的。
这是一座名副其实的油城,根据baidu百科:“截至2022年10月,中国陆上最大油田大庆油田被发现63年来,已累计生产原油逾24亿吨”。相当于全国人民2吨油。我小时候经常听到的说法是一顿原油的价格比不上一吨的酱油。所有的物资都是在国家的统一调配下进行的。
对于当年日本侵华战争时为什么没有发现大庆油田,大约有两种说法:
说法一:日本勘探技术的问题以及抗日武装的功劳
当时,日本有一个机构,叫满洲炭矿株式会社,早在1926年,它就专门跑到中国东北来找石油。到了1928年,日本人在中国东北寻找石油的心情愈加迫切。时任满铁地质调查所参事的日本地质专家新带国太郎就曾在1929年春天带着人,沿东清铁路到牡丹江上游的森林地带寻找石油,但经过两个多月的探察却一无所获。不死心的他在1930年4月又在满洲里的扎赉诺尔煤矿进行过第二次勘探,再次失败。不止这一路人马。为了在东北找到石油,日本当时投入了大量人力物力,也用尽了当时他们所掌握的一切勘探和采矿设备,但始终没有发现一滴油。直到1941年“太平洋战争”爆发,所有日本在中国勘探石油的队伍都被征调到了东南亚,参与那里的油田恢复和开采(英军和荷军在撤离时摧毁了油田),“在中国东北找到石油”这个目标最终被放弃。
是日本人勘探能力和技术水平不行吗?也不完全是,现在回过头来看,其实当时还是很凶险的。根据旅日学者徐静波的回忆,2010年,他和一位日本客人拜访时任大庆市副市长的栾莹,栾莹曾告诉他,当时日本人打的其中一口井,离后来喷油的“松基三井”只有2公里远——当时日本人打的井深度是1000米,而“松基三井”是打到1300米深时喷油的。就差300米。而根据黑龙江省安达市档案局局长、安达市志办主任李生的回忆,大庆油田的主力产油区有一口井叫“萨一井”,这口井当年下钻到680米的时候就遇到了油层。而这口井与当年日本人打的井距离更近——相距仅1公里。如果当时日本人把打井的方位再多挪一公里,或者多打几口井,那么就很有可能率先发现大庆油田。日本人当初之所以没发现大庆油田,除了所谓的“运气”和“技术”之外,按照李生的说法,还和当年共产党领导的东北抗联和地下抗日武装有很大关系。
在打出“萨一井”的那个杏树岗,当年就是当地抗日游击队的大本营。在频繁的骚扰和袭击下,日本人其实很难大张旗鼓地拉开架势打井钻油,换句话说,其实当年日本人也并非像我们想象的那样,能大摇大摆地在中国东北随意勘探。【参考2】
说法二:毗邻大庆市的安达市是当时的日本细菌战试验场【参考3】,日本鬼子自然不愿意认真进行勘探。
相比之下,第一种说法更令人信服。所谓的“国运”更是一代又一代人奋力托举出来的结果。
从我有记事起,大庆的生活条件并不好,主要是物资短缺。因为冬季的寒冷,能够吃到的蔬菜品种非常少,主要就是萝卜白菜土豆这样耐寒的贮存菜。秋天的时候,单位会“分菜”。通常就是企业(比如,我父亲的单位是勘探开发设计院),从地方购买白菜,然后按照人头分下去。早些年住在平房,有些家庭会有菜窖。这是高寒地区人民群众多年经验总结出来的经验。在地上挖出来一个坑,然后留个门,秋天的时候将白菜萝卜贮藏在其中,这样即便外面零下三十度,地窖种仍然能够维持一定温度使得蔬菜不至于被冻坏。有需要的时候打开门,顺着梯子下去取出来就可以吃到。此外,还有酸菜,是秋天的时候在家中准备巨大的缸,然后一层层的码上白菜最上面放上一块大石头放置在家中。温度不能太高也不能太低,在事宜的温度中,白菜会发酵,然后成为整个冬季的食物。告诉你这些的人不会告诉你在发酵的过程中白菜会发出异味,也不会告诉你,在这个过程中夜深人静的时候缸中微生物会产生气体,因此,这个庞然大物对于一个少年是恐怖的存在。当然,酸菜炒粉条和酸菜炒肉是非常下饭的存在,有机会的朋友可以尝试一下。

为了让我接收白菜/酸菜,我母亲告诉我白菜清热解毒,人一百天不吃白菜就会中毒,这种说法让我深信不疑。现在虽然我不在家乡,有机会还是很爱吃白菜蘸酱的。除了白菜,给我留下悲伤记忆的更多的就是萝卜。印象中我的幼儿园时光就是充斥着萝卜的味道。当地的一种做法是:将萝卜过水,让它变得松软,然后挤出水分,蘸着大酱吃。味道尚可接受,唯独萝卜过水产生的味道让人难忘…….
至于土豆,倒是没有多少悲伤的感觉,可能是这种东西做出来的土豆泥还有土豆炖粉条多多少少会有肉的存在,味道并不差吧。东北的土豆大约是沙地的缘故,吃起来有着香甜的感觉。土豆炖粉条配上辣椒油,让人瞬间有着温暖的感觉。
从我小时候起,一直听到的“菜篮子工程”(搜了一下,英文名“shopping basket program” 是农业部从1988年开始一直坚持进行的项目【参考4】,至今已经快四十年),随着这个项目的进行,冬天可以选择的蔬菜品种也越来越多,西红柿韭菜大头菜等等。
冬天的水果品种也非常少,印象中只有桔子苹果。我年少时,这些也主要靠单位“分”,市面上买不到。比较有名的苹果有“国光”“红富士”,我比较偏向口感清爽类型的,因此不太喜欢“黄元帅”这种面甜的。当时印象深刻的还有有时候单位会分品相很差的“爱国苹果”。应该是一些地方受灾,苹果没有卖相,然后国有企业承担社会责任,购买之后作为福利下发而来。
大庆本地没有苹果,本地的水果是被成为“沙果”的水果,个头较小,吃起来酸甜口。我不太爱吃,因为太小,没吃几口就没了。

偶然单位能发一些香蕉,这对于我们来说就是少见的热带水果。只是每次拿到的都是真正的“蕉绿”。等我走出家乡,再也没有看到过如此之绿的香蕉。大约是因为贮藏不易的原因,因此发下来的都是翠绿颜色的。到家之后,父亲会含住白酒喷洒在上面,然后用塑料袋仔细的包装起来,放在床下,然后全家人静待成熟,而我总是迫不及待的每隔几天就拉出来看看。

若干年后,我偶然间看到关于北京一所中学的故事,说这家中学以前生源一直是达官显贵,后来被人批判不得不向平民开放。为了筛选,学校搞了一个骚操作,选拔的时候只是面试,拿出热带水果的画册,比如:芒果,然后让学生辨认…….普通人哪怕有看报纸的机会看到的也都是黑白照片,自然不认识热带水果,而能认得此物的通常也都是富贵人家的子弟了。由此,最终招上来的仍然是家世显赫的子弟,
参考:
1.http://www.tcmap.com.cn/heilongjiang/ranghuluqu.html
2.https://zhuanlan.zhihu.com/p/138814640
4.https://baike.baidu.com/item/%E8%8F%9C%E7%AF%AE%E5%AD%90%E5%B7%A5%E7%A8%8B/7105237
Step to memory 003 DDR 拓扑结构的演变
继续 《PC 内存的秘密 第一部分》 原文在 https://www.bit-tech.net/reviews/tech/memory/the_secrets_of_pc_memory_part_1/5/
第五章 DDR 拓扑结构的演变
打个比方:噪音有多大才算吵闹?
这有点像在满是学生的教室里上课。如果学生聊天声音足够大,那么就会发生干扰老师授课的情况。类似的,如果电气噪声足够大,将会导致数据错误。
通常,每一代 DDR 内存对信号不确定性的容忍度都会降低。内存稳定性问题通常是主板工程和 BIOS 优化的问题。简而言之,这有点像班上的每个人都说话越来越快。任何异常大的噪音都会导致每个人更容易忘记谈话内容,而不是说话变慢,因为他们听不清对方在说什么。
DDR DIMM(模块)识别
每一代内存条在尺寸、引脚配置和凹槽键位置上都略有不同,这样的设计是为了避免内存条插入错误的插槽。
当今制造的大多数台式机主板仅支持单一类型的内存,但也有极少数例外,它们可以同时与无缓冲和寄存式 DIMM 配合使用。JEDEC 标准确实为能够与 U-DIMM 或 R-DIMM 配合使用的主板提供了便利,但不能同时与两者配合使用。因此,U-DIMM和R-DIMM内存使用相同的接口。

DDR 拓扑结构的演变
拓扑设计在提升内存信号质量方面起着重要作用。内存工程师普遍知道(尽管并非内存工程师独有),当内存性能提高时,信号更容易出错。为了避免这样的问题,每一代计算机 RAM 都需要新的设计,通过简化和缩短数据管道以便实现更好的电信号特性。

因为每代 DDR 都有信号质量的问题,但是只要内存频率保持在某个最大范围以下仍然能够保证数据正常,例如 让一个DDR2内存运行在 800MHz就不太可能遇到稳定性问题。拓扑是 DDR3 的一个非常重要的方面:其Fly-by拓扑的设计使得信号延迟可以预测,因此可以非常轻松地进行调整和补偿。DDR2 对称T型拓扑设计很容易导致信号中不可预测的“抖动”,在更高速度下更难修复。 Fly-by拓扑是菊花链(Daisy Chain)的一种特殊形式,其核心特征在于 极短的支路长度(通常小于5mm)和 单主干线结构。Fly-by拓扑可以翻译成“短支路菊花链”。不过为了统一和确定性,一般就直接使用Fly-by拓扑结构这个术语。

这里在【参考1】 https://blog.csdn.net/Deco0503/article/details/144983271 图片表述更加清晰:
T 分叉拓扑则类似一条河流,在分叉口,水流会分成几支,如果河道宽度、深度不一致,水流速度和流量就会产生差异,难以同步到达下游
可以看出Fly-by拓扑在高速信号传输中具有显著优势。其在信号完整性、实现复杂度和成本效益等方面的平衡,使其成为DDR4的最佳选择

在 https://blog.csdn.net/Baojiang_Gao/article/details/46968795 还能看到对于这两种拓扑结构更形象的描述:


Windows 下查看当前笔记本电池信息的方法
方法一,直接使用 Windows 提供的功能进行查看:
a.在cmd管理员权限下运行 pwercfg /batteryreport,即可生成一个报告文件

b.使用浏览器打开报告

c.这里可以看到一些电池的基本信息,比如:生产厂家,设计容量和上一次充电后的实际容量

d.最近三天的电池使用情况

e.电池的容量信息,可以看到刚开始使用的时候,充满电后电池容量可以达到 44507mWh, 但是经过几个月的时候,充满后的容量只有38383mWh了。

方法二:使用第三方工具查看。推荐 nirsoft 推出的 BatteryInfoView (https://www.nirsoft.net/utils/battery_information_view.html)。
软件使用起来比较简单和直观,运行之后直接就可以看到需要的信息:

电池这种东西看起来并不怎么起眼,但是如果想做好确实不太容易的事情,一方面是只有量大才有成本的优势,另外一方面如何能做的安全可靠也是挺大的学问。因为“安全可靠”这四个字并不太容易验证。差不多二十年前,有一家销量很靠前的品牌商请一个厂商代工。产品做好之后,代工厂推荐了一个型号的电池,但是这家以灵活制造按需生产的品牌商觉得太贵,非要用自己选择的电池,结果出货没多久就多次发生了自燃事故,甚至在一次会议中,诸多镜头前展示了自己的热情和能量。后来才知道,这个品牌商选择的电池有着灵巧轻便价格便宜的特点,唯一的缺陷是不太稳定。后来这家品牌商不得已只能宣布召回。不过时至今日,这家品牌商又宣布要彻底断绝中国供应链,不知道未来会不会再次出现这样的问题。
Insyde EXE BIOS Rom 提取器
很多时候,Insyde BIOS 是以 EXE 的形式给出来的,直接在 Windows下运行即可烧写当前设备。但是很多时候我们期望使用烧录器来烧写,这种情况,我们需要提取 EXE 中的BIOS ROM 文件。
本文根据【参考1】编写,主要步骤是:
- 使用 7z 解压 EXE 文件(选中EXE后解压即可)
- 之后使用十六进制工具打开解压出来的 winflash.fd 文件
- 在winflash.fd 中搜索 “$_IFLASH_BIOSIMG”,这个字符串起始位置+ 这个字符串长度+8字节就是 BIOS Rom 的起始位置
- 在winflash.fd 中搜索 “$_IFLASH_INI_IMG”,这个字符串起始位置-8字节就是 BIOS Rom 的结束位置
- 将中间内容粘贴为一个单独的文件就是这个 EXE 对应的 BIOS Rom了。
编写一个批处理和VC 来实现自动化。
对应的 Bat代码:
if "%1"=="" (
echo Please give the filename
exit /b 1
)
if NOT exist "%1" (
echo %1 not exist!
exit /b 1
)
mkdir tempdir
7z x %1 -otempdir -y
Ins2ROM
对应的 VC 代码:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
bool readFileToMemory(const std::string& filePath, std::vector<char>& buffer) {
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "Failed to open file: " << filePath << std::endl;
return false;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
buffer.resize(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Failed to read file: " << filePath << std::endl;
return false;
}
return true;
}
bool saveMemoryToFile(const std::string& filePath, const std::vector<char>& buffer, size_t start, size_t end) {
if (start >= end || end > buffer.size()) {
std::cerr << "Invalid start or end position." << std::endl;
return false;
}
std::ofstream outFile(filePath, std::ios::binary);
if (!outFile) {
std::cerr << "Failed to open output file: " << filePath << std::endl;
return false;
}
outFile.write(buffer.data() + start, end - start);
if (!outFile) {
std::cerr << "Failed to write to output file: " << filePath << std::endl;
return false;
}
return true;
}
int main() {
const std::string inputFilePath = ".\\tempdir\\winflash.fd";
const std::string outputFilePath = "BIOSRom.bin";
const std::string startPattern = "$_IFLASH_BIOSIMG";
const std::string endPattern = "$_IFLASH_INI_IMG";
std::cout << "Insyde EXE to BIOS ROM" << std::endl;
std::cout << " www.lab-z.com" << std::endl;
std::cout << " 2025 02" << std::endl;
std::vector<char> buffer;
if (!readFileToMemory(inputFilePath, buffer)) {
std::cerr << "Can't find" << inputFilePath << std::endl;
return 1;
}
auto startIt = std::search(buffer.begin(), buffer.end(), startPattern.begin(), startPattern.end());
auto endIt = std::search(buffer.begin(), buffer.end(), endPattern.begin(), endPattern.end());
if (startIt == buffer.end() || endIt == buffer.end() || startIt >= endIt) {
std::cerr << "Patterns not found or in wrong order." << std::endl;
return 1;
}
size_t startPos = std::distance(buffer.begin(), startIt) + startPattern.size()+ 8;
size_t endPos = std::distance(buffer.begin(), endIt)-8;
if (!saveMemoryToFile(outputFilePath, buffer, startPos, endPos)) {
return 1;
}
std::cout << "BIOSRom.bin successfully created." << std::endl;
return 0;
}
完整的编译后的工具打包在一起:
参考:
1. https://zhuanlan.zhihu.com/p/21169375283
Step to memory 002 计算机的存储层次
《PC 内存的秘密 第一部分》 本文来自
https://www.bit-tech.net/reviews/tech/memory/the_secrets_of_pc_memory_part_1/3/
第三章 DDR内存家族
计算机内存层次结构 不同类型的内存在计算机工程总体方案中发挥着不同的作用。它们根据用途、速度、复杂性和制造成本进行分类。
通常,更快的内存设计和制造成本更高。因此,它们的容量通常更有限。反之亦然,较慢的内存和存储系统几乎总是具有更大的容量。
计算机内存层次结构是一种金字塔结构,通常用于说明不同内存类型之间的显著差异。此图及其变体可在互联网上的各种参考网站(如维基百科)上找到。

此处所示的计算机内存层次结构版本代表了标准台式计算机中各种内存技术之间的不同性能水平。
DDR 内存系列
双倍数据速率的SDRAM (Double Data Rate SDRAM,缩写DDR) 是 1993 年推出的同步动态随机存取内存(synchronous dynamic random-access memory SDRAM ,缩写SDR) 技术的一次革命性进步。随着SDR技术在 1999 年达到极限,DDR 技术被设计为接下来十年的解决方案。
描述 DDR 内存规格的方法有两种。一种使用实际的 DRAM 芯片规格;另一种使用内存模块规格。这两种方法有时可以互换使用,但随着研究的深入,您会发现并非所有内存即便标称着相同的规格,彼此之间还是存在着差异。
DDR 各代之间的主要区别
除了物理差异之外,每一代 DDR 之间都有基础性的区别。每一项技术进步的目标都是为了解决更高性能相关的问题或副作用,例如信号噪声、偏移、抖动、时序不准确、干扰等等。
前几代中也会存在上述问题,只是在较慢的速度下这些问题通常不会导致严重的问题。而当内存的速度提升之后,就需要新技术来缓解高速信号的信号不确定性。更高的性能需要更严格的容差。

SDR 对内存数据采样发生在时钟的每一个上升沿,DDR 则是在上升沿和下降沿,因此,速度可以做到两倍。
时钟周期是指波形从波谷到达波峰再次到达波谷所需的时间,此处显示的正弦波信号实际形状介于方波和正弦波之间。在一个时钟周期内,时钟上升沿发送一次数据,下降沿发送一次,这样就实现了一个时钟周期发送2次数据。这是 DDR 技术的关键。

从下图也可以看到,同样的时间内,频率越高(周期越小),可以发送的数据也会越多:

下面表格是 DDR1-3 的一些简单对比:
Features | Unit | DDR1 | DDR2 | DDR3 |
Connecting Pins | ||||
DIMM | 184 | 240 | 240 | |
SO-DIMM | 100 200 | 200 | 204 | |
Registered DIMM | 184 | 240 | 240 | |
Mini-DIMM | – | 244 | – | |
Micro-DIMM | 172 | 214 | – | |
Packaging | 60-pin TSOP2 / 60-pin FBGA / LQFP | 60/84-pin FBGA | 78/96-pin FGBA / FBGA with Mirroring | |
Length | mm | |||
DIMM | 133.35 (5.25) | 133.35 (5.25) | 133.35 (5.25) | |
SO-DIMM | 66.7 (2.625) | 66.7 (2.625) | 66.7 (2.625) | |
Registered DIMM | 133.35 (6.25) | 133.35 (6.25) | 133.35 (6.25) | |
Mini-DIMM | – | 82 (3.23) | 83 (3.23) | |
Micro-DIMM | 54 (2.13) | 54 (2.13) | 54 (2.13) | |
Dram Core Freq | MHz | 100 – 200 | 100 – 200 | 100 – 200 |
Topology | Asymmetric T-Branch (T-Daisy) | Symmetric T-Branch | Fly-By with Termination | |
JEDEC Classified Data Rate | DDR-200/266/333/400 | DDR2-400/533/667/800 | DDR3-800/1066/1333/1600 | |
Prefetch Width | bits | 2 | 4 | 8 |
Bandwidth (theoretical) | GB/s | |||
Single-Channel | 1.6 / 2.13 / 2.67 / 3.2 | 3.2 / 4.26 / 5.34 / 6.4 | 6.4 / 8.53 / 10.67 / 12.8 | |
Dual-Channel | 2.2 / 4.26 / 5.32 / 6.4 | 6.4 / 8.52 / 10.68 / 12.8 | 12.8 / 17.06 / 21.34 / 25.6 | |
Module Capacity | 128MB – 1GB | 256MB – 4GB (8GB) | 512MB – 8GB (16GB) | |
Number of Banks | 4 | 4 or 8 | 8 | |
Read/Write Leveling | No | No | Yes | |
Supply Voltage | Volts | 2.5 +/- 0.2 | 1.8 +/- 0.1 | 1.5 +/- 0.075 |
Max Operating Temp | ºC (ºF) | 85 (185) | 85 (185) | 85 (185) |
I/O Width | x4 / x8 / x16 | x4 / x8 / x16 | x4 / x8 / x16 / x32 | |
DIMM Calibration Resistors | No | No | Yes | |
Burst Length | 2, 4 | 4, 8 | 4 (Burst Chop), 8 | |
RHoS | Vendor Dependent | Vendor Dependent | Vendor Dependent | |
Latency | ||||
CAS Latency (CL) | tCK | 2, 2.5, 3 | (2), 3, 4, 5, 6 | 5, 6, 7, 8, 9, 10, (11) |
Additive Latency (AL) | tCK | – | 0, 1, 2, 3, 4 | 0, 1, 2 |
READ Latency (RL) | tCK | CL | CL+AL | CL+AL |
WRITE Latency (WL) | tCK | 1 | RL-1 | 5, 6, 7, 8 |
CAS Write Latency (CWL) | tCK | – | – | 5, 6, 7 |
DQ Timing | ||||
READ | DLL aligns DQ, DQS to CK | DLL aligns DQ, DQS, DQSb, RDQS to CK | DLL aligns DQ, DQS, DQSb to CK | |
WRITE | Setup / Hold to DQS | Setup / Hold to DQS DQSb | Setup / Hold to DQS DQSb | |
Data Strobes | Single-ended data strobe | Single-ended or Differential data strobe | Differential data strobe with WRITE leveling | |
Termination | Motherboard Chipset | On-Die Termination | Dynamic On-Die | |
Driver Calibration | No | Off-Chip Driver (OCD) | On-Chip Self-Calibration with ZQ pin | |
DQ Driver Strength | Narrow Envelope | 18 +/-3 Ohms, OCD Calibration | 30-40 Ohms, ZQ pin Calibration | |
Interrupts | Yes | Wr-Wr, Rd-Rd (4n only) | Burst Chop for Rd and Wr | |
Reset Function | No | No | Yes (Asynchronous) | |
Thermal Sensor | No | SO-DIMM (on DIMM), FB-DIMM (on Die) | SO-DIMM (on DIMM), FB-DIMM (on Die) | |
Automatic Self Refresh (ASR) | Supported | Supported | Supported | |
Temp. Compensated Self Refresh (TCSR) | Supported | Supported | Supported | |
Self Refresh Temp (SRT) | <= 85 ºC | <= 95 ºC | <= 85 ºC, <= 95 ºC | |
Partial Array Self Refresh (PASR) | Supported | Supported | Supported | |
Deep Power Down (DPD) | Supported | Supported | Supported | |
Clock Stop Mode | Supported | Supported | Supported |
来源: JEDEC, Altera Corporation, Elpida Memory, Infineon Technologies, Kingston Technology, Micron Technology, MOSAID Technologies, Qimonda, Rambus, Samsung Semiconductor, Via Technology