CRZ's Blog

Crossover使用说明

安装普通 .exe 程序

单独创建瓶子

  1. 点击安装后,点击右上角的安装未列出应用
  2. 选择下载好的 .exe 安装文件;
  3. 选择容器:
    • Bottle Name:随意,例如软件名称;
    • Bottle Type:选择合适的 Windows 版本即可;
  4. 安装。接下来会弹出 Windows 软件自己的安装界面,像在 Windows 上一样正常执行安装即可。

安装结束后,CrossOver 通常会自动生成软件图标,之后直接从对应瓶子中启动即可。

安装进已有瓶子

例如某个 .exe 是 Steam 游戏需要的补丁、组件或附加程序:

  1. 选中目标瓶子,例如 Steam;
  2. 选择 Install Application into Bottle;
  3. 后续同上

这样安装程序所写入的文件、注册表和 Windows 组件都会进入这个 Steam 瓶子。

文件管理

以steam中游戏的文件替换为例:

  1. 打开 CrossOver 主界面
  2. 左侧选中安装 Steam 的瓶子;
  3. 点击 Open C: Drive

Finder 打开后,进入steam,按以下路径选择即可

1
2
3
4
5
Program Files (x86)
└── Steam
└── steamapps
└── common
└── 游戏名称

At the End of the World

前言

关于世界征服,本篇采用了一些私设:

  1. 费加罗和奥兹在开始世界征服之前已经有了自己的住所;
  2. 世界征服开始于密斯拉未满百岁之时,即约 1500—1400 年前。

正文

奥兹还在世界征服。

听说它最近在央国大开杀戒呢。

要不要趁奥兹在外地去袭击魔王城堡?这家伙周游世界,想必又攒了不少好东西。

不过,那个没品味的奥兹,说不定根本认不出来一路上遇见的宝贝,把玛那石一顿啃完,就又去赶下一个场子了。世界上最无趣旅行者的称号可以让给它。整天和奥兹作伴四处横行,光是想想都后背一凉啊,也只有费加罗这种恶趣味的家伙有心思跟着它转了。它倒是比奥兹识货些,但我宁愿去闯双子的结界,也没兴趣拜访费加罗的家,装模作样的,屋随其主,远远眺一眼就想掉头。

算了,我可不像奥兹那么闲。我一要守着我的船按时上岗,二要收集散落的古籍精进咒术,三要补充高品质玛那石提升力量……天呐,怎么有这么多事等着我去做。但是为了迎战奥兹,我必须加快训练的节奏才行。

世界征服,最后一站,肯定要来死之湖吧?

我还没和任何人聊过这个话题,只是,没有征服过我的领土的话,算什么世界征服啊。等到奥兹走完一圈世界,最后来到死之湖,向我正式宣战,我再用早已今非昔比的魔力将它击溃。奥兹的玛那石是什么味道的呢?我一定会尝到的。啊啊,好完美的结局。

在这之前,还是不去城堡找麻烦了。最好也别向切雷塔透露计划,总感觉那个人去双子家串门时会聊漏嘴。等我真的打败奥兹之后,它可再没有借口把我当小孩敷衍了。

于是就这样,一个人探险,一个人历练,过了数百年。切雷塔有时会来找我玩,不过它呆不了多久就会去找下一个乐子;我也不觉得无聊,试想,有这样一个美梦近在眼前,再怎么无欲无求的人也会感到愉悦吧。

在雪原里被不知名的远古巨兽按倒在地、闲来无事抱臂躺在船上望天、又一次独自吞下新鲜玛那石……有那么多契机让我完善脑海里的画面。奥兹是会推门闯进我的小屋,还是一杖敲碎我的湖面?费加罗多半会替它谋划最后一战吧,至于奥兹听不听,又是另一回事了。不理它也好,这一战,一定要只属于我们两个才圆满,奥兹嘛,堂堂正正决斗的决心总还是有的。

切雷塔还是知道了我的计划,但它这次没像以前那样笑得那么过分,我觉得它是被我独自升级的效率打动了。等事成之后,分一块魔王的玛那石给它也不是不可以;不管是前世界第一的玛那石,还是前追求对象的玛那石,切雷塔肯定都感兴趣。

可是,什么叫奥兹结束了世界征服?

谁允许它在前来挑战我之前停止世界征服?

我甚至是在梦之森和欧文玩的时候才被告知了消息。顺带一提,欧文也知道我的计划,等回来再找它算账,那副得意忘形的嘴脸,这次真是格外让人烦躁。

我觉得我有必要去要个说法。连一千年都坚持不到,还有什么资格做世界第一……

直接空间移动到奥兹的城堡门前。一打开传送门,厚重的风雪压着空气卷来,暗沉中,还没来得及动手闯入,倒是撞上了奥兹的帮凶。那个人,被奥兹拒之门外了啊。

不管它,现在就进去。

“密斯拉,别去打扰奥兹了吧。”

奇了怪,费加罗今天居然主动招惹我。它要找我打,我也不是不能奉陪,但不是今天啊。

我没理它,径直向大门走去。

“ポッシデオ。”

我闪身躲开。为什么没一件顺心事呢?几百年的余韵,仿佛一霎就被冲毁了。

“费加罗,别来打扰的是你才对吧。我可不会任由自己被拒之门外。”

它没说话。

雪一直落,要把世界淹没似的。

我明白进门前必有一战了。

召唤出魔道具,骷髅眼窝中准备燃起火焰,身侧的风雪忽然一乱,另一道声音传了过来。

“密斯拉,今天先回去吧。”

切雷塔也来了?

“你也来拦我。”

“嗯。”

“到底为什么?”

切雷塔停了一下。

“因为你现在进去,也得不到你想要的东西。”

它知道我问的不是我为什么不能进去。切雷塔明明知道的。

但是,好吧。

纷飞的雪花劈头盖脸地砸下来,我看着紧闭的大门,又看了看这俩没出息的门神。一个不说话,一个没有笑。如果我执意进去,这两个人都不会手下留情。

更重要的是,即使进了门,奥兹也不会重新开始世界征服了。我有这种感觉,就在刚刚这一瞬。

骷髅眼窝里的火焰熄灭了。

它怎么能放弃呢?

我转身踏进传送门,临走前又回头看了一眼。风雪把城堡遮得严严实实,仿佛无人居住其中。

算了。等奥兹哪天重新想起来,世界上还有一个地方没有被它征服,自然会来死之湖找我。

我等着就是了。

后记

没有人关心但是本质上是一篇世界征服压抑产物(

Just Another Day

前言

提前谢罪把王子大人写漏了TT

正文

贤者大人张罗着要给这届魔法使们拍张合影。

最早赶来的是米契尔和央国的小家伙,叽叽喳喳地围着贤者,合照还没个影,就计划着等会儿要怎么交换相机互拍。

听起来就麻烦。

我可不要留下陪你们玩,贤者大人也不准被耽搁,早早陪我上床睡觉才是正事。要不是这人一脸可怜鬼缠身的衰样,小型犬一样追着我保证,我才不会这么早就坐在水池边,什么事也不做,干等着那些散漫的家伙一个个出现。

刺耳的风从上空划过,溅出几簇水花洒在我肩上。不用抬头也知道,南国的迷糊鬼骑扫帚赶到了,大个子和费加罗也随后跟来;东边那四个倒是安静,结伙一起从魔法舍走出来,又一起钻进树荫下,不声不响地等着;央国的小子和奥兹并肩往这边行来,天哪,它又在拍奥兹的肩膀。跟奥兹勾肩搭背是什么感觉?想想都有点犯恶心,都怪奥兹,好想决斗。

不行,为了今晚,暂且让它一回。

我移开眼。西国的烦人鬼们也到了,这下烟花声、笑声、不知道谁领头唱起来的歌声全部混杂在一起,简直吵死人了。

所以说,到底为什么突然要拍照啊,贤者大人,又是那个世界的什么奇怪节日吗?

“诶,不是为了什么纪念日啦,只是,なんでもない日也可以聚在一起做些什么,这就是伙伴呀,密斯拉。况且这也可以作为留给下一任贤者的资料嘛!”

抱着头,手臂微微压住耳朵,在池边躺下,不知怎么又想起它的回答。明明最后一个来也没影响,让贤者一个人在那招待就是了,而不是现在这副傻样,把主动权递给还没来的人。但说不清为什么,那家伙一边笑着,一边牵着我的手,回过神来的时候,我已经坐在了水池旁。

“呀,密斯拉。”

黏糊糊的声音从头顶落下。

“你居然会老实等人?”

是欧文拉着布拉德利,一副亲亲热热的模样。那双异色的眼睛往前面的相机上一瞥,忽然眯了起来。

“看见了吗,布拉德利?下次试试用这个对付它怎么样?它脑袋笨,同时处理不过来两件事的,这边因为拍照晃了眼,那边就顾不上杀你了哦。”

看起来那么好说话的人,嘴里吐出来的东西怎么这么难懂呢?总感觉,这人又意有所指。

完全听不明白啊。它俩自己估计还要纠缠半天,等会儿再想吧,反正它们也打不过我。

“贤者大人,快开始吧,总算到齐了。”

我坐起来,伸伸懒腰,没理会旁边人的怪声怪气。

北国的双子总算到了,两个人你一声我一语,腻乎得像甜蜜的脓——欧文总爱这么形容,现在听来倒很合适。倚老卖老的家伙,居然让我等了这么久。

我挑了个最边上的位置站着,离这几个一肚子坏水的人远远的。

偏偏欧文今天很不对劲。没去缠着它家那个目玉の人,反倒挤来我旁边,也不说话,只是隔一会儿就转转眼球,从旁边斜我一眼。

搞不懂,我今天可没空奉陪它的馊主意。

眼看身边这家伙窸窸窣窣了半天,贤者大人总算把这一大群人都塞进了镜头里。

“三、二、一——”

前面的相机发出轻响。

白光闪过的瞬间,欧文突然贴过来,一把挽住我的手臂,夹得死紧。

这人今天到底怎么回事?找茬还是挑衅?

算了。照片都拍完了,没工夫跟它计较。该带贤者大人回去睡觉了。

转过头,准备甩手去抓人,箍在手臂上的力道却突然先一步松开。紧接着,又一道白光闪过眼前。我猛地侧过身去,可还是晚了一步。

“密斯拉呀,在魔法舍待久了,连这种程度都反应不过来了吗?以前明明不会被这种东西骗到的,被驯化的北国魔法使——哎呀,听起来好有趣。”

颧骨上传来一阵刺痛,有温热的液体淌下来。

“看吧布拉德利,我说了,这家伙的脑瓜只能单线程运转。”

欧文退开半步,笑眯眯地举起提包。

居然让欧文伤了我。好,这下不做个了断的话,今天是睡不成觉了。

我抬手抹掉碍事的血迹。

“アルシム。”

咒语落下,魔力凝成刀刃向那边刺去。周围传来几声惊呼,当然还混有老东西们装模作样的尖叫声。

对面早有准备,向后一跃。刀光擦过它的身侧,撕开一截衣角。

不过,欧文还真是没说错,一步退,步步退,从前我哪有这么多可让步的地方。一开始把贤者带回去陪我睡觉的话,我哪儿用得着费这些劲啊。

“嘿,密斯拉,这就生气了?陪贤者它们整天瞎混,连脑袋都跟着变迟钝了吗?”

“说什么呢?”

从今天见面起,欧文好像就一直在扯一些听不懂的话啊。

“啧,记性有够烂啊。”

它撇撇嘴,对我的反应相当不满意的样子,故技重施,又射出一道白色光箭。

我瞬间凝起光球,迎面撞上去,将光箭截在半空。

这人从头到尾都过于反常了,放在以前,它早该把箱子里那怪物放出来咬人的。

顷刻间,数道白光轰然对碰,无数碎片往四周飞散又炸开。

不想继续纠缠了。我召唤出魔道具,准备速战速决。

“欧文。”

它侧身避开一道炸开的白光。

“你今天到底在翻哪笔旧账?”

欧文的动作停了一瞬。

“……你不知道?”

“所以我才问你啊。”

它脸上的笑一下子消失了,猛地拉开手提箱。蛰伏其中的怪物探出三个脑袋,朝我张开满是尖牙的嘴。

总算对劲了。

我举起魔道具,对准它,骷髅眼窝中燃起幽色,冥火翻涌着,迎面撞上扑来的三头怪物。火焰和利齿剧烈相碰,掀起气浪向四周扫去。贤者它们又准备开始劝架了,可这回根本与我无关啊,我只是想睡个觉而已。刚刚还在给我们合影的相机也被掀翻了,那种东西,怎么样都好。

ケルベロス被冥焰轰翻在地,三个脑袋东倒西歪地叠在一起,彻底没了动静。我再次举起骷髅头,紫光在头顶铺展开来,一口气朝着欧文袭去。

“嘶——”

挡在它面前的魔法彻底崩碎。

我穿过尚未散尽的火焰,一把揪住它的衣领,将人重重按倒在地。骷髅空洞的眼窝重新燃起幽光,该是最后一击了。

“请您去死吧。”

“哎呀,密斯拉。”

被压在地上的人忽然笑了。

“你不看看旁边吗?”

才不会上它的当。

骷髅眼中的火焰烧得更盛了。

“真的不看吗?”欧文眯起眼睛,“明明四百年前,你对它还挺感兴趣的。”

四百年前?

啊,是那一次。

我终于明白它在扯什么了,但那又如何?请它再一次去死吧。

火光骤然落下。

就在冥焰即将吞没欧文的瞬间,另一道魔力突然从侧面撞了进来。火焰偏开半寸,擦着欧文的胸口轰进地面。它闷哼一声,整个人被余波掀了出去,重重摔在地上。

“哎呀,差一点就结束了。”

布拉德利站在不远处,慢悠悠地收回魔道具,脸上完全没有妨碍了决斗的歉意。

“先别急着杀嘛,密斯拉。它都提示到四百年前了,你至少回答一下再动手吧?”

“它说四百年前的时候,我就想起来了啊。”

欧文撑在地上的手顿了一下。

“就是你突然拿出一个黑色匣子对准我,结果话还没说完,就被我杀掉的那次吧?”

我看向它。

一阵白光而已,甚至算不上刺眼,当然不会影响我继续攻击。相机磕碜着,掉落在还没凉透的遗体身侧。唔,那天,我本来是打算直接回死之湖的,不过,我已经不记得后来为什么没能立刻动身,只是在原地蹲着,盯了半天尸体,临走前又捡起相机,对着欧文按了一下。

现在想想,还是不知道当时为什么要那么做。

“所以,你是对后来我给你拍的那张照片不满意吗?”

“你这家伙!”

它睁大眼睛,瞪着我的脸。这副表情,倒是又恢复到之前好搭话的样子了。突然有点懒得继续了。

“欧文,你还要我重新给你拍一张吗?我懒得动了啊,你需要的话,自己死一次吧。”

“嘁,谁要你拍尸体啊,真是恶趣味。”

好麻烦的人。

“那我走啦。贤者大人,回去睡觉,说好了的哦。”

旁边的布拉德利突然漏出声笑,拍拍欧文的肩,又被唰一下拍开。

“喂,我可是好心帮你。上次拍的是尸体,这次难得还活着,你真不想再来一张?”

“你闭嘴。”

本来就青白得跟死了没区别的脸色更难看了,欧文没理布拉德利,眯眼盯了我半天,泄了气似的。

“算了,跟睡不着觉的可怜鬼计较什么。”

撑起身,嗤笑一声,欧文无声无息地消失在空气中。

好吧,我歪歪脖子,虽然还是不太懂,但总算是可以回去睡觉了对吧。

后记

ミスラ说如果要向我表达好意的话,请给我千年不变的爱,到那时再考虑。

博主很想要强调北兄妹在关系性上的千年不变而绝非爱情,但是写着写着有点变流水账了()

不爱正视自己的两人,有着永远也不必要重新审视的关系,会翻旧账但是具体翻没翻过去谁也不在意。我实在太喜欢这种放任身心随水流沉浮的肆意感了。

Tears

前言

基于活动剧情《紅蓮の大地で遊ぶ愛し子》展开的往事。

小头作祟产物烦请多多包涵。

正文

那个西国的哲学家这几天总不厌其烦地找我搭话,自顾自地说起它收集全魔法舍眼泪的宏大计划。据它所说,魔法使留下的泪有强化魔法科学装置的力量,这是个已经得到实验验证的结论,虽然暂时原理不明,但战备时期特殊对待,难道我不想见证奇迹的发生吗?

“奥兹给了你眼泪吗?我可不想在这上面输给它啊。”

西国魔法使总是这么烦人,我避开它的眼神,试图打发走讨厌的野猫。

奥兹当然不会把体液遗留在外,或者说,但凡是对咒术略知一二的人,都不会留下这种把柄啊,这是有多不把我当回事。

说到底,我们这样的人,根本就不会流泪吧。

我只看过切雷塔的眼泪,约定成立的那个瞬间,我看见了泪光在它眼里闪烁。定下约定的明明是我自己,为什么是它坐在草地上又哭又笑的呢?就好像又在提醒我,自己和切雷塔毕竟已经不是一样的人了。

那它的眼泪还会和我眼眶里打转的液体是同一种味道吗?

天哪,我那个时候怎么没想过要尝一下它的眼泪,再也没有解答的机会了。或者当时应该直接问切雷塔的,那滴泪明明顺着它的脸颊流到了嘴角,况且之前那次,这个霸道的人真的吃过我的泪液来着。

在红莲炎山给贤者它们讲的故事,到封印就戛然而止了,多亏 diaugur 那家伙没有透视封印之力,千百年来只记得复读切雷塔叫我名字而已。

其实我早体会过贤者口中的桑拿,切雷塔结束故事的时候,我可是就趴在火山口旁边,眼睁睁看着自己的对手被强制暂停动静。不安恼怒的精灵把岩浆舞得到处都是,火山灰遮天蔽日,本就炎热的火山口被彻底闷成蒸笼,那热气,可不是姆鲁用魔法装置能模拟出来的呢。

巨大的 diaugur 就这样消失在眼前,我侧着脸,盯着只剩嘶嘶白气的岩床,不知道该摆出什么表情。那是沙子从手中滑落的感觉,我多想站起来,却发现竟然控制不了自己的魔力——我还没悟出精灵、支配与力量的关系,也没把此地精灵的暴躁真正看进眼里。

好烦躁,好想把大家伙揪回来,好想撑起身来继续作战,明明还没有决出胜负不是吗,为什么站不起来,大腿被咬穿而已,这样的伤我早受过无数遍了啊。

我咬着牙,试图控制残破的四肢,死命瞪着火山口,想往那边爬过去。可是,一双手轻飘飘地按住了我。

那个我一直假装忽略的人,把我翻过身来,用魔法定住我。我看见它凑过来,一脸笑意,胡乱拨弄着我的脸。

“哭了吗,密斯拉,因为被夺走了猎物?”

我不想理这个人,它也不在意没有回应,自顾自地念叨着什么“果然还是小孩子”,又俯下身来。我没法再去看消失的魔兽了,切雷塔占领了我的视线,越来越近,清晰的人像逐渐变成色块的交织。金色长发散落在我身上,和伤口黏在一起,又随着它的动作把血肉磨开。好痛,好热,气息还在靠近,瞳孔一直在放大,什么都看不清了,直到有什么湿答答的东西彻底触碰到它。

它想尝我眼球的味道吗?

炙热的软体挑衅着眼白,眼皮被手拉开,就这样直瞪瞪地被侵犯,一下,又一下。分泌的泪液下一瞬间就被刮去,好像一切液体都被眼前的炽热蒸发干净。

眼球又麻又干,只是每每在被触碰的瞬间微微跳动。

好想眨眼。为什么我耳边只余眼球起跳的声音。

“这下真的哭了呀,密斯拉。”

突然传来杂糅着惊讶的调笑,它在说什么,不对,它怎么在说话?我试着眨动眼皮,一滴泪从另一只被冷落的眼眶滑出。再眨,试着聚焦视线,用手撑起身躯,我瞪着这个讨厌的人。

“总算冷静下来了,你看,现在可以控制四肢了呢。”

完全没有被我的眼神吓到!

“切雷塔,我会回来找我的猎物的。”

脸上早已糊成一片,我用力抹了抹,却赶不走那种黏腻感,血迹也好,涎液也罢,早就在热气的蒸腾下凝固了,或许还混合着那滴泪。它看着我脏兮兮的样子,笑着应了声,挽住我的手带着我往死之湖飞去。

哈,切雷塔这副不称职的老师模样,现在只有我知道了。眼泪的味道,再也不会有人为我解答了。

I'm Only Sleeping

前言

密密的最终幻想(

正文

有多久没睡过完整的一觉了。

白日没有运送尸体的活找上门,无聊地度过一天光阴,在困倦中逐渐失去意识,一夜无梦,第二日懒洋洋地被太阳照醒;或是历经一番大战,修复好受损的血肉,就地躺下任凭自己昏迷,醒来再回船上待业。

我好想再完整地睡一觉。

上好的熊皮一定要准备一张,我要裹着它睡在雪被里。剩下的肉就在一旁的篝火上烧着吧,噢,讲起这个,烤肉酱好像用完了啊,我得抽个空去森林里再煮一锅,这次双子应该没那么多废话了吧。诶,好像忘了什么,之前不是说要和布拉德利一起去搜刮双子的房间来着,后来怎么没去呢?好烦,现在奥兹晚上恢复魔力了,就算趁天黑出击,大概也会被拦下来。那对双子总是搬救兵,丧失了半身力量的下场就是这样吗,真可怜。还是直接去闯魔王的城堡吧,也该到了一决胜负的时候了,我会证明自己才是夜之帝王的。日子就该这么过才对啊,马上就想传送过去打上一架。

“クーレ・メミニ。”

什么动静。

我睁开眼,侧身躲开,但还是被蹭掉一大块皮。

“アルシム。”

欧文,它怎么在这里?

“密斯拉,闯进梦之森,不需要先向主人打声招呼吗?”

啊,我在梦之森,大灾厄还没结束,觉也根本没睡成。做了个可笑的白日梦,现在还被吵醒了,真烦人。

“你是蠢货吗,呵呵……听不懂话也没关系,来了梦之森,总要留下点什么来滋养它吧,睡不着觉的你连梦境都没法提供,我就替可怜的它主持一下公道吧。”

好想睡觉。

总之,先解决眼前的挑衅吧,说不定,这次打累了可以睡个好觉呢。

Both Sides, Now

前言

本文灵感来源于Joni Mitchell的同名歌曲。

正文

又飘过一朵云。

这次是羽毛覆盖的峡谷,还是北国巨兽的翅膀?

我不知道,我只是躺在船上,看着云团聚拢又消散。

听起来文艺又无聊吗?唔,其实我也不知道自己是在看什么,或是在等待什么的出现。只是,风雪过境的日子太多,这样能够捕捉到云朵轨迹的时刻,我好像不想错过。

魔法使的生命太漫长,再稀少的瞬间,叠加起来也算不上稀奇。可我还是不明白,所以仍会一次次等待它出现又离去,连打斗也可以暂且放在一边。

从前我一直是这样看云的。切雷塔来之前是这样,走之后也是这样。

我以为我们可以一直这样看着云。我还是躺在那条船上,头枕着手,微眯着眼,望着难得的晴天。身边或许会有魔女突然降落,这样的时刻,即使是聒噪的它也会愿意只是坐在船侧,静静地感受身旁的吐息;或许直到云消散也不会有人来,但那又如何。魔法使的时间看不到尽头,云朵总会在下一次出现,行踪不定的那个人,也总会在某天又带来吵闹的笑声。

可我还是不懂云,尽管我已经看遍了它,从各个角度。有那么多事,我本来可以去做,却被云挡住了去路。我想去追问它的变幻莫测,不知不觉间,反倒一路跟着它来到了南国。死之湖的云厚重浓烈,飘到这里,却变得缥缈柔软。我不明白,明明一直在看着它,我还是不懂它为什么要变化。

船只在第谷湖上晃晃悠悠,我躺在上面,想找回熟悉的感觉,却浑身不得劲。这里的云可以从日出逗留到日落,可以被骤雨打散又在晴时恢复如初,一点都不像我地盘上的那家伙。可真懒散啊,我才不要奉陪,你就慢慢悠悠地留在这儿散步吧。猛地坐起来,没有知会任何人,我悄悄地回去了。

我好像有一点懂它了,在我不再能等到它之后。那些家伙都说它变了,我也依旧这样觉得,可或许,我曾坚信的那些也只是一场幻象呢,厚重却一吹即灭,无形却阴魂不散,这听起来,不是最荒谬的错觉吗?这家伙,真是给我留下了个大麻烦呢。

linux操作

Linux

What is Linux?

  • 一个Linux发行版(例如Ubuntu):
    • Linux内核
    • GNU项目:一个自由软件集合
    • 其它软件和库

Linux基础操作

1
命令名 [选项][参数]...
  • [ ]:意为可选0~n个
  • 可通过man命令来查看具体命令的使用说明
    1
    man [选项] 命令

目录操作

1
cd directory

在需要键入文件名或目录名时: 可以使用 Tab 自动补足全名,双击 Tab 可以显示所有可能选项

  • /:根目录
  • ~:当前用户主目录
    • 普通用户($):/home/username
    • root(系统管理员)用户(#):/root
  • .:当前目录
  • ..:上一级目录
  • -:上一次访问的目录
1
ls [选项] [目录]
  • 选项(常用):
    • -a 显示隐藏的文件
    • -l 每行只列出一个文件
1
mkdir [选项] 目录
1
pwd [选项]
  • 作用:输出当前目录的绝对路径
1
rmdir [选项] 目录
  • 作用:删除目录

文件操作

支持对多个文件的操作,例如使用*来匹配任意长度的字符串

1
touch [选项] 文件名
  • 作用:当文件存在时更新文件的时间戳,当文件不存在时创建新文件。
1
rm [选项] 文件
  • 选项:
    • -r:递归删除目录及其内容,删除非空目录必须有此选项,否则无法删除
    • -f:强制删除,不提示用户确认,忽略不存在的目录
    • -i:逐一提示用户确认每个将要被删除的文件
1
cp [选项] 源文件 目标路径
  • 选项:
    • -r:递归复制目录及其子目录内的所有内容
1
mv [选项] 源文件 目标路径
  • 作用:
    • move
    • rename:例如mv test.txt test2.txt
  • 选项:
    • -v:显示详细的操作信息
    • -i:覆盖前询问
    • -u:(仅在有必要时移动)仅在源文件较新,或目标文件不存在时,才执行移动操作
  • 多源移动;当有多个源时,最后一个参数必须是目录
1
diff [选项] 文件1 文件2
  • 作用:比较
  • 选项:
    • -b:不检查空白字符的不同
    • -B:不检查空行
    • -1:仅显示有无差异,不显示详细信息
1
cat 文件
  • 作用:将文件内容输出到标准输出
1
echo <text>
  • 作用:print
  • 默认添加换行符,所以即使echo ''并非空(需要 -n去除)

查找操作

1
find [路径] <选项>
  • 作用:在给定路径/当前目录下查找
  • 选项:
    • -name <文件名>:指定需要查找的文件名
1
grep [选项] PATTERN FILE

(PATTERN是匹配字符串,FILE是文件或目录的路径)

  • 作用:输出匹配PATTERN的文件和相关的行。
  • 选项:
    • -a:不忽略二进制数据进行搜索。
    • -i:忽略大小写差异。
    • -r:从目录中递归查找。
    • -n:显示行号。
    • -v:反向过滤
    • -w:按独立单词匹配
    • -o:只输出匹配到的部分
  • 例如:grep hello a.txt
  • 可使用正则表达式作匹配
    • '^a':匹配行首为a的行
    • 'end$':匹配行尾为end的行
    • '[0-9]'
1
tree [选项] [目录名]
  • 作用:输出一个目录下的文件树。
  • 选项:
    • -a:列出全部文件(包含隐藏文件)
    • -d:只列出目录

快捷键

  • ctrl + c:终止程序执行
  • ctrl + z:挂起当前程序
  • ctrl + d:终止输入(相当于输入一个EOF)
  • ctrl + l:清屏
  • ctrl + s/ctrl + q:暂停/启动终端

GNU工具

GCC

即GNU编译器套件。

1
gcc [选项] 源代码文件
  • 作用:编译源代码文件
  • 选项:-o example
  • 若编译成功,同目录下出现a.out可执行文件
    • 执行./a.out即可输出
    • 需要传参?可类比:./casegen.out add 100 > case_add

make

make学习博客

  • make工具:根据Makefile中规则,自动化编译和构建项目
  • Makeflie
    • 由一系列rules构成,每条rule形式如下:
      1
      2
      <target> : <prerequisites> 
      [tab] <commands>
    • target
      • 通常是文件名:指明Make命令所要构建的对象
      • 也可以是某个操作的名字,这称为phony ,例如:
        1
        2
        3
        .PHONY: clean
        clean:
        rm *.o temp
      • 若Make命令没有指定目标,默认会执行Makefile文件的第一个目标
    • prerequisites(前置条件)
      • 通常是一组文件名,之间用空格分隔
      • 用法:生成多个文件alt text
      • 本质:若某个文件变化,会导致目标过期,则当前目标依赖该文件
        • 注意区分与伪目标(调用就执行,不关心过期)
    • commands(命令)
      • 注意:需要注意的是,每行命令在一个单独的shell中执行。这些Shell之间没有继承关系alt text
        • 这种“独立”只反映在shell变量上,而不影响磁盘上文件的改变
    • 多级Makefile:
      • 调用下级Makefile:
        • make -C subfile xxx
    • Grammar
      1. #
      2. echoing回声
      • 正常情况下,make会打印每条命令,然后再执行,这就叫做回声
      • 在命令的前面加上@,就可以关闭回声
      1. wildcard通配符alt text
      2. 模式匹配:%
      3. 变量和赋值符
        1. 调用时,要把变量放在$()
        2. 赋值运算符:常用:= alt text
      4. 内置变量
      • $(CC):指向当前使用的编译器
      1. 自动变量
        1. $@:代指当前target
        2. $<:代指第一个前置条件
        3. $?:代指比目标更新的所有前置条件
        4. $^:代指所有前置条件,之间以空格分隔
        5. $*:指代匹配符%匹配的部分
      2. 循环和判断:遵循bash语法

ctags

~/.vimrc中添加配置:

1
2
set tags=tags
set autochdir
  • 功能:代码跳转
    • ctrl+]跳转至相关定义文件位置
    • ctrl+o返回跳转前位置

tmux

终端复用工具 输入tmux进入tmux新对话

  • 重要快捷键:alt text

Git

alt text

  • 工作区
  • 暂存区
  • 版本库 S
  • untracked:未跟踪,未被git跟踪的状态
  • unmodified:未修改,版本库版本和工作区版本一致
  • modified:已修改,版本库版本和工作区版本不一致
  • staged:已暂存,修改版本已提交至暂存区,但尚未上传

Git常用指令

基本提交

git init git clone <URL>:“克隆”一个已有的储存库到当前目录下 git status:查看当前分支状态 git add:将新文件/已修改文件加入暂存区

  • git add .:add所有修改进入暂存区
  • git add <filename>

git checkout:切换分支(需要保证当前所有文件状态为未修改git commit:git commit -m <message>

  • 将暂存区的修改提交到储存库中

git log:查询提交记录

撤销指令

  • 工作区撤销 git restore <file> 把工作区文件恢复到暂存区版本
  • 暂存区撤销 git restore --staged <file> 把文件从暂存区取出,但还保留工作区文件
  • 删除未跟踪文件 git clean -f

版本回退

  • 版本库回到上一个提交 git reset --hard HEAD^
  • 版本库回到指定提交 git reset --hard <hash>
    • hash: 对应提交的hash值,可用git log获取

远程本地同步

  • 远程->本地:git pull [远程仓库名] [远程仓库分支]:[本地仓库分支]
  • 本地->远程:git push [远程仓库名] [本地仓库分支]:[远程仓库分支] git restore:取消修改
  • 未add入暂存区时:git restore <filename> 来撤销对这个文件的修改,使其退回到上一个 commit 的状态
  • 已add入暂存区后:git restore --staged <filename>将文件移除暂存区,再次restore

Shell

Shell,即操作系统的“外壳”(相对“内核”而言),是用于访问操作系统服务的用户界面

分类

GUI Shell

  • GUI:
    • Graphical User Interface

CLI Shell

  • CLI:
    • Command Line Interface:命令行界面

Shell脚本语言

Shell脚本的执行

  • 约定脚本文件的文件名以.sh结尾
  • 执行Shell脚本:./文件名
    • 前提:脚本文件有“执行”权限
    • chmod +x filename添加执行权限

Bash Shell语法基础

1
2
3
#!/bin/bash
#My first Shell script!
echo "Hello, world!"

alt text

  1. 变量:无需指定类型,但是等号两边不允许空格
  • 使用${var_name}/$var_name可以获取变量的值
  • 数组:i=(1 2 3 4)
  1. 脚本参数alt text 特殊变量:
    • $#:传递的参数个数
    • $*:一个字符串,内容是传递的全部参数
    • $?:函数返回值
    • $N:当N>=10时,需使用${N} 使用特殊变量时,尽量加上双引号"",防止解析错误
  2. 条件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    if condition1
    then
    command11
    command12
    ......
    elif condition2
    then
    command21
    command22
    ......
    else
    command31
    command32
    ......
    fi
  • 整数比较方法:((condition))
  1. 循环
1
2
3
4
5
while condition
do
command
...
done
  1. 计算
    • 以下等价:
      • let i=i+1
      • i=$((i+1))
    • 运算符:
      • (()):结果非零时返回0(真)
      • $(()):直接返回计算数值结果
      • []:条件为真时返回0
        • [ -s < file> ]:文件非空
      • [[]]:支持&&等逻辑符
  2. 函数
  • 定义:
    1
    2
    3
    4
    function fun_name() {
    body...
    return int_value;
    }
    (如果要返回值,必须返回一个[0,255]之间的整数)
  • 调用: fun_name param1 param2 ... paramN
  1. 变量字符串分割
    1
    2
    ${name:1:3} //${变量:起始位置:长度}
    ${name:0:(-3) }//去掉末尾3个字符

sed

通过命令编辑和输出文本的工具

1
sed [选项] '命令' 文件
  • 选项
    • -n:安静模式,只显示经过sed处理的内容。否则显示输入文本的所有内容。
    • -i:直接修改读取的档案内容,而不是输出到屏幕。否则,只输出不编辑
    • -e: 允许在同一行里执行多条命令,例如sed -e '4astr ' -e 's/str/aaa/' my.txt
  • 命令:
    • <行号>a<内容>: 新增,在行号后新增一行相应内容。行号可以是“数字”,在这一行之后新增,也可以是“起始行,终止行”,在其中的每一行后新增。当不写行号时,在每一行之后新增。使用$表示最后一行。后面的命令同理。
    • <行号>c<内容>:取代。用内容取代相应行的文本。
    • <行号>i<内容>:插入。在当前行的上面插入一行文本。
    • <行号>d:删除当前行的内容。
    • <行号>p:输出选择的内容。通常与选项-n一起使用。
      • ‘n,mp’:输出[n,m]
    • s//:将(正则表达式)匹配的内容替换为
      • 默认只作用于每一行的第一个匹配项
      • 末尾标识符g:作用于所有,e.g.sed -i "s/char/int/g" file

awk

文本处理工具 awk '条件 { 动作 }' 文件名

  • awk默认:

    • 一次读取1行
    • 把这一行拆成若干字段,用空白字符分割每一列
    • 执行规则
  • 常用内置变量

    • $0:整行
    • $n:第n列
    • NF:当前行的字段数
    • NR:当前是第几行
    • OFS:输出字符分隔符
  • 例如:

    • awk '$2 > 80 {print $0}' score.txt:当第二列大于80时,输出一整行
    • awk -F ',' '{print $1, $3}' test.csv:输入分隔符为,
  • 一定要注意用''包裹条件&动作,防止解析错误

重定向

在命令后添加

  • > output.txt:覆盖原有内容,若不存在则新建
  • >> output.txt:追加内容 即可将输出重定向到文件中,而非原本的控制台上
  • <:输入重定向
  • 标准流:alt text
    • >1> 的简写 tips:
    • 重定向不能创建目录,只能创建文件

管道

管道符号|可以对命令进行连接 alt text e.g.+

1
grep -n "key" file1.txt | cut -d ":" -f 1
  • cut:
    • -d ":":以":"作为分隔符
    • -f n:取第n块
      • -f n、m
      • -f n-m

vim

vim学习

Vim

Linux系统下的开源文本编辑器

  • 基本输入操作
    • 创建文件(touch)
    • 打开文件
      1
      vim helloworld.c
    • 输入内容:按I进入输入模式
    • 保存并退出vim
      1. esc回到命令模式
      2. shift+;进入底线命令模式
      3. 输入wenter,保存文件
      4. 再进入底线命令模式,输入q即可关闭文件回到CLI shell (3、4可合并为wq

配置

  • 配置文件:.vimrc,位于用户主目录下
  • .vimrc配置

Normal模式

复制粘贴

如何复制选中内容到系统剪切板?

Undo/Redo

  • u:undo
  • <C-r>:redo

delete

  • x:删当前光标所在的一个字符
  • dd:删除当前行,并把删除的行存到剪贴板里

移动光标

  • 0:到行头
  • ^:到行首第一个非空字符
  • :<n>:移动到第n行
  • gg:移动到第一行
  • <C-f>/<C-b>:前后翻整页

可视模式

输入v进入

复制粘贴

以下仅限于vim内部操作

  • d:剪切选中的文本段
  • y:复制
  • p:粘贴

BUAA-CO-P7 MIPS微系统

通关纪念!

目标简述

在P6的基础上,实现一个简单的计算机系统(MIPS微系统)。

三种外设

  • Timer(计时器):计时部件,可以按照配置定时产生时钟中断
  • Memory(存储器):外置IM、DM(类似P6)
  • InterruptGenerator(中断发生器):随机产生外部中断信号,产生的中断信号在CPU响应前会持续置高

支持异常处理的CPU

  1. 异常

    1. 内部异常:
      • 由于指令执行错误导致的“事件”
      • 例如:加法溢出,除法除零
    2. 外部中断
      • 由于外部设备信号导致的“事件”
      • 例如:计时器信号
  2. 内部异常检测

  3. 外部中断检测

  4. CP0:接收异常,储存异常类型,实现精确异常

  5. 异常处理

软硬件接口

  1. 外设的接口设计——系统桥
    • 连接CPU和外设的功能设备

      • CPU — SystemBridge — { DM , Timer , InterruptGen , … }
    • 给CPU提供一种接口,使得CPU可以按地址读写复杂多变的外设

      • CPU不再关心外设长什么样,它只会按地址读写(用统一的方法访问外设)
      • 所有外设都是“DM”
  2. CPU的接口设计
    • 封装成单周期CPU

宏观PC

  • 把复杂的多级流水线CPU封装成单周期CPU
  • 宏观PC:整个 CPU “宏观”运行指令所对应的PC地址。我把CP0放在了M级,所以把M_PC作为宏观PC。
    • 该指令之前的所有指令序列对 CPU 的更新已完成
    • 该指令及其之后的指令序列对 CPU 的更新未完成

顶层设计

整体层次 顶层数据通路示意图

任务清单

最终架构图 设计思路:从CP0开始,一步步推进。

协处理器CP0

  • 功能 接收内部异常和外部中断,产生异常控制

  • 设计 本质上是一组特殊寄存器。

    寄存器 编号 功能
    SR(State Register) 12 配置异常的功能,即:
    当前CPU:
    1.能不能处理外部中断(IE)
    2.能处理哪些外部中断(IM)
    3.是否正在处理异常(EXL)
    Cause 13 记录异常发生的原因和情况
    - 外部中断源:IP
    - 内部异常类型:ExcCode)
    EPC 14 记录异常处理结束后需要返回的PC
    寄存器 功能域 位域 解释
    SR(State Register) IM(Interrupt Mask) 15:10 分别对应六个外部中断源,相应位置 1 表示允许中断,置 0 表示禁止中断。
    控制能处理哪些外部中断
    SR(State Register) EXL(Exception Level) 1 只要进入异常(I&E)就置1,处理完异常、执行eret时置0
    SR(State Register) IE(Interrupt Enable) 0 全局中断使能,该位置 1 表示允许外部中断
    Cause BD(Branch Delay) 31 异常是否发生在分支延迟槽中
    tip:只有内部异常才可能导致BD为1
    Cause IP(Interrupt Pending) 15:10 为 6 位待决的中断位,分别对应 6 个外部中断,相应位置 1 表示有中断,置 0 表示无中断
    反映外部中断源
    Cause ExcCode 6:2 内部异常编码,记录当前发生的是什么内部异常。
  • 模块规格

    信号名 方向 位宽 描述
    clk I 1 时钟信号
    reset I 1 复位信号
    CP0WE I 1 CP0 写使能信号
    CP0Address I 5 要访问哪个CP0寄存器(SR/Cause/EPC)
    CP0WD I 32 写入 CP0 的数据
    VPC I 32 受害 PC(发生异常时的 PC)
    BDIn I 1 是否处于延迟槽
    ExcCodeIn I 5 内部异常类型编码
    HWInt I 6 外部硬件中断信号,对应Cause_IP/SR_IM
    EXLclr I 1 清除 EXL,用于执行 eret
    CP0RD O 32 从 CP0 读出的数据
    EPC O 32 EPC 寄存器输出值
    Req O 1 是否进入异常处理程序
    1: PC ← 异常入口地址

tips

  1. CP0AddressCP0WE 是针对 CPU 执行 mfc0 / mtc0 指令时访问 CP0 寄存器用的,而异常更新并不会经过CP0Address。这两条路径几乎完全分离;

  2. Req生成逻辑

    • Req = 外部中断|内部异常
    • 外部中断(Interrupt): 能处理中断(SR.IE) & 未在处理异常(!SR.EXL) & 存在外部中断源且该中断源可以被处理(SR.IM&HWInt) 需要处理外部中断(Interrupt置高)
      • 外部中断看情况处理!
    • 内部异常(Execption): 未在处理异常(!SR.EXL) & 存在内部异常(ExcCodeIn) 需要处理内部异常(Execption置高)
      • 内部异常必须处理!
  3. CP0何时被写? 顺序如下:

    1. HWInt无论如何会被更新到Cause.IP
    2. Req == 1 更新SR.EXL/Cause.BD/EPC
      1. Exception? 更新Cause.ExcCode,否则置零
    3. EXLclr == 1 即:执行eret,将SR.EXL置零
    4. CP0WE == 1 写对应寄存器
      • CP0 寄存器的初始值均为 0,未实现位始终保持 0
  4. CP0接线

    • M_CP0WE\M_EXLclr来自控制信号M_ControlBus(增加Control)
    • M_PC来自M
    • M_ExcCodein来自内部异常检测,M_HWInt来自外部异常检测
    • M_BDin:BranchControl生成BDin信号,向后流水,从Reg_M取出
    • M_CP0RD:向后流水,传入WDsel
    • Req:接入IFU/Reg_D/Reg_E/Reg_M,实现精确异常
    • EPC:核心在于理解eret没有延迟槽
      • 绝对不执行已经被取到F中的错误指令(eret后面紧跟的指令清空)
      • eret指令在D时,插入一个 nop(类似likely清空延迟槽——D_Flush)
  5. Req如何实现精确异常?

    • 精确异常:
      • 异常受害指令之前的所有指令都生效
      • 异常指令以及之后的指令都不产生任何副作用
      • 返回后从异常指令(or上一条(BDin))重新执行
    • 流水线表现
      • W级:正常执行
      • M级:指令停止,进入异常处理
      • E/D/F:flush
    • 优先级:reset > Req > stall > (To_nop) > likely造成的D_flush
    • 具体实现
      • IFU:PC跳转至异常处理地址
      • Reg_D:清空instr,PC正常流水
      • Reg_E:清空所有,PC、BD正常流水
      • Reg_M:清空所有,PC、BD正常流水
      • Reg_W:清空所有
    • 特别规定,在进入中断或异常状态时,如果受害指令及其后续指令已经改变了MDU的状态,则无需恢复。考虑以下情况:
      • mult 在 E 级启动了乘法运算(其它乘除法指令同理),流水到 M 级时产生了中断
        • MDU不用恢复
      • mthi 在 E 级修改了 HI 寄存器(mtlo 同理),流水到 M 级时产生了中断。
        • MDU不用恢复
      • mult 在 E 级,受害指令在 M 级,此时还未改变 MDU 状态,不应开始乘法计算,其它乘除法指令同理。
      • mthi 在 E 级,受害指令在 M 级,此时还未改变 MDU 状态,不应修改 HI 寄存器的值,mtlo 同理。

    总之:

    • MDU接入req。如果req&!Busy,那么MDU freeze(MDU不应开始运算/改变HI、LO状态)
  6. 关于PC的处理

    • 核心问题:flush造成的nop的PC到底应该怎么取,才保证外部中断后EPC更新不出错?
    • nop的分类
      • 天然指令nop:不管
      • 异常指令视作nop:PC、BDin、ExcCode正常流水,其它清零
      • D_flush替换的nop:PC正常流水
      • stall插入的bubble(nop):PC、BDin正常流水,其它清零
  7. 中断和异常优先级:中断优先级高于异常优先级,即当有异常提交至 CP0 寄存器时,若有中断发生,则硬件应先响应中断强制将Cause.IP记为5'b0

内部异常检测与流水

  • 异常与中断码

    异常码 助记符与名称 指令与类型 描述
    0 Int(外部中断) 所有指令 中断请求,来源于计时器与外部中断。
    4 AdEL(取指异常) 所有指令 PC 地址未字对齐。
    PC 地址超过 0x3000 ~ 0x6ffc。
    4 AdEL(取数异常) lw 取数地址未与 4 字节对齐。
    lh 取数地址未与 2 字节对齐。
    lh, lb 取 Timer 寄存器的值。
    load 型指令 计算地址时加法溢出。
    load 型指令 取数地址超出 DM、Timer0、Timer1、中断发生器范围。
    5 AdES(存数异常) sw 存数地址未 4 字节对齐。
    sh 存数地址未 2 字节对齐。
    sh, sb 存 Timer 寄存器的值。
    store 型指令 计算地址加法溢出。
    store 型指令 向计时器 Count 寄存器存值。
    store 型指令 存数地址超出 DM、Timer0、Timer1、中断发生器范围。
    8 Syscall(系统调用) syscall 系统调用。
    10 RI(未知指令) - 未知指令码。
    12 Ov(溢出异常) add, addi, sub 算术溢出。
  • 指令异常检测位置

    异常与中断码 异常 检测位置(到该阶段才能检测) 检测指标
    0 外部中断Int E \
    4 AdEL D PC
    4 AdEL E ALUresult
    5 AdES E ALUresult/operand1、operand2
    8 Syscall D Instrction
    10 RI(未知指令) D Instruction
    12 Ov(算术溢出) E operand1、operand2、ALUop
  • ExceptionDetect(指令异常检测模块)

    • 功能 检测内部异常类型,生成ExcCode
    • 模块规格
      信号名 方向 位宽 描述
      D_ControlBus I 32 D ControlBus
      E_ControlBus I 32 E ControlBus
      D_PC I 32 D PC
      E_ExcCode I 5 从E取出的ExcCode
      E_operand1 I 32 ALU operand1
      E_operand2 I 32 ALU operand2
      D_ExcCode O 5 存入E的ExcCode
      E_NewExcCode O 5 真正存入M的ExcCode
      2nop O 1 是否将D中指令视为nop
  • tips:

    • 发生取指异常或 RI 异常后视为 nop 直至提交到 CP0(保留BDin,PC和ExcCode)
    • 内部异常优先级:AdEL最大(先有取,再有译)
    • Timer中count映射的地址?
      • 看Timer中count的定义 & 整个Timer映射的地址范围
      • e.g.
        • Timer0: 0x0000_7F00 ~ 0x0000_7F0B (长度 12 字节 = 3 个 4 字节寄存器)
        • Timer:``define count mem[2]`
        • count:0x0000_7F08 ~ 0x0000_7F0B

BDinGenerator

  • BDin 异常发生的这条指令是否位于延迟槽(B/J - 延迟槽(该指令)) 上一条指令是不是B/J

  • 功能 生成BDin,向后流水

  • 原理 分支跳转指令无论跳转与否,延迟槽指令为受害指令时 BD 均需要置位。

  • 模块规格

    信号名 方向 位宽 描述
    D_ifB I 1 D级指令是否是B指令
    D_ifJ I 1 D级指令是否是J指令
    BDin O 1 D级指令紧跟的指令是否位于延迟槽

tip:eret没有延迟槽但要跳转

Bridge系统桥

  • 功能

    1. 地址解码:当前地址属于哪个设备
    2. 把CPU的读写信号、写数据转发给对应外设
    3. 从对应外设返回数据给CPU
  • 架构

      CPU 给地址、数据、byteen
      ↓
      Bridge 内部解码
      ↓
      Bridge 产生 DM_Byteen / Timer_Byteen / Int_Byteen
      ↓
      Bridge 选择外设返回数据 RD
    

计时器

  • 功能:根据设定的时间来定时产生中断信号

单周期CPU的封装

宏观PC:M_PC

HCU新增控制

1
2
3
4
wire stall_eret = D_eret & (
(E_mtc0 & (E_rd_addr == 5'd14)) ||
(M_mtc0 & (M_rd_addr == 5'd14))
);

DM

  • DM写入要屏蔽异常 wire M_TrueMemWrite = M_MemWrite & (!Req)

思考题

  1. 请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?

    • 通过外设控制器发出的中断信号 + OS 注册的中断服务程序,CPU 被动接收并处理输入数据。
  2. 请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)

    • CPU 在异常发生时必须立即进入可信、统一、受保护的处理入口,才能保证系统继续正确运行。
    • 如果允许用户自定义入口,虽然仍能处理中断,但会出现无法保存现场、破坏内核权限、系统不可恢复等问题,使 CPU 无法提供可靠的操作系统支持。
  3. 为何与外设通信需要 Bridge?

    • 我们可能随时需要增加外设,但不可能每增加一种外设就给CPU增加响应接口。为此我们设计Bridge来连接外设和CPU,将所有外设都映射为一段地址(“DM”),使得CPU能以统一的方式访问不同外设。
  4. 请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并分别针对每一种模式绘制状态移图。

    • 异:计时结束动作不同
      • 模式0:单次中断,计时结束持续输出中断信号
      • 模式1:循环计时,每次计时归零输出一周期中断信号
    • 同:计时过程相同 timer
  5. 倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?

    • 问题:EPC出错
    • 保留PC、BDin
  6. 为什么 jalr 指令为什么不能写成 jalr $31, $31?

    • 从jalr的语义来看,PC转向被调用函数,函数入口地址保存在 GPR[rs]中。但如果rs和rd相同,那么将PC+8写入rd的操作就会覆盖保存的函数入口地址,使得程序无法返回调用点。

BUAA-CO-P6 基于verilog的流水线MIPS架构CPU(2)

任务

  • 将P5中支持{add,sub,ori,lui,lw,sw,beq,jr,jal,nop}十条指令的CPU扩展,要求支持以下指令集:

          add,sub,and,or,slt,sltu,
          addi,andi,ori,lui,
          mult,multu,div,divu,mfhi,mflo,mthi,mtlo,
          lw,lb,lh,sw,sb,sh,
          beq,bne,jr,jal,
          nop
    
  • 不再自行模拟IM和DM,而由提供的外设传递instruction和相关数据给CPU

前提

  1. 不会连续出现乘除指令
  2. 乘除指令之后一定是mfhi/mflo

需要改动的地方

IM和DM

不用自己实现IM和DM了。

  • 相当于给出了IM和DM的外置设备,只需要考虑怎么将cpu和IM、DM连接起来
  • 在MEM阶段设置BE模块来负责数据交互
    • 原MemWrite变为byteEn总开关
    • 原MemRead现在没有意义了

MDU

在EX阶段新增乘除模块来实现乘除相关指令

  • 我们只需要把乘除法相关指令阻塞在D
  • 在HCU中增加MD_stall的判断

Control

指令集规模扩大,为提高添加指令的效率和conrol代码的简洁性,我们对指令进行分类,可大致分为: - R_cal - I_cal - MD - Load & Save - Branch & Jump

计算用组合逻辑积累

  1. 乘除相关
    1
    2
    3
    4
    //madd
    {temp_hi, temp_lo} <= {hi, lo} + $signed($signed(64'd0) + $signed(rs) * $signed(rt));
    // 或者
    {temp_hi, temp_lo} <= {hi, lo} + $signed({{32{rs[31]}}, rs[31:0]} * $signed({{32{rt[31]}}, rt[31:0]})); // 手动进行符号位扩展

模块设计

ID

HCU

  • 转发:新增了数据来源(M_HI/M_LO)
    转发到/数据来源 M锁存的ALUresult W_WD M_PC8 M_HI M_LO E_PC8
    ID stage RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData
    EX stage RtData/RsData RtData/RsData RtData/RsData RtData/RsData RtData/RsData 不存在
    MEM stage 不存在 RtData 不存在 不存在 不存在 不存在
  • 暂停:增加了MD_stall
    • assign MD_stall = (D_isMDinstr & (E_MDUop != 3'b0 | E_Busy));
    • 即:当E中MDUstart/busy且D中为MD相关指令,stall

EX

MDU

  • 整体设计
    • 能够实现和乘除有关的功能
  • 模块规格
    信号名 方向 位宽 描述
    reset I 1 同步复位
    clk I 1 clk
    op I 3 MDUop
    000:未start
    001:mult start
    010:multu start
    011:div start
    100:divu start
    operand1 I 32 默认RsData
    operand2 I 32 默认RtData
    we_HI I 1 HI写使能信号
    we_LO I 1 LO写使能信号
    Busy O 1 MDU繁忙信号
    HI O 32 HI data
    LO O 32 LO data

MEM

BE

  • 模块设计
    • 将字节写使能信号传入外设DM
  • 模块规格
    信号名 方向 位宽 描述
    Address I 32 读/写地址,默认是ALUresult
    MemWrite I 1 写使能信号
    LoadType I 2 load模式选择信号
    SaveType I 2 Save模式选择信号
    MemWriteData I 32 要被处理准备被写入的数据
    MemReadData 从DM中读出的对应Word
    LoadData O 32 要Load到rt中的数据
    SaveData O 32 要Save到DM中的数据
    byteEn O 4 要Save到DM对应字的字节位置

思考题

  1. 为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?

    • 乘除的实现时间远长于ALU中的简单运算,如果整合进去,那么EX阶段的时间就会被拉长,降低效率
    • 独立的HI、LO:
      1. 运算结果是64位/两个数,而通用寄存器只有32位;
      2. 可以简化寄存器堆的写回逻辑(否则GRF要实现同时写入两个数据,与现有GRF结构不同)
  2. 真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。

    • 真实的CPU实际上是通过专用硬件电路 + 内部流水线 + 乱序调度来实现乘法,而除法通过使用 SRT 算法来实现(搜索得到)
  3. 请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?

    • 我在MDU内部设计了count来计算阻塞周期数,实际上这就是一个有限状态机的实现。
    • 在HCU中增加了MD_stall(具体见上MDU)
  4. 请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)

    • DM内部不再需要区分写入指令的方法(sw/sb/sh/…),它只需要根据Address和字节使能信号决定要不要写入。
  5. 请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?

    • 不是,是一个字
    • 当我们需要紧密地存储数据时,按字节读写效率会更高(比如处理字符串时)
  6. 为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?

    • 把控制信号合成为总线向后传,要用的时候再利用宏取出。当需要新增控制信号(如增加乘除法相关的 we_HI/LO)时,只需修改位宽定义和宏,而无需修改流水线寄存器(Reg_E, Reg_M等)的端口列表;
    • 在增加指令时把指令分为R-cal\I-cal\Branch and Jump\Load and Save\MD相关,再把它加入对应的控制信号表。填表的过程就是梳理指令全过程、思考待改变模块的过程,因此填完表之后基本就知道要怎么修改cpu。
    • 转发采取from - to的思路,只要增加的指令没有涉及到转发source的增加就不用改HCU。(如果要改,类似:添加MD相关指令之后,在HCU和顶层数据选择逻辑中加入了M_HI/M_LO,在WDsel的来源中增加了W_HI/W_LO)
  7. 在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?

    • 转发:按照以下from - to表来列举
      转发到/数据来源 M锁存的ALUresult W_WD M_PC8 M_HI M_LO E_PC8
      ID stage R_cal/I_cal - nop - Branch R_cal/I_cal/Load - nop - nop - Branch jal - (jump处)nop - Branch mfhi - nop - Branch mflo - nop - Branch jal - (jump处)Branch
      EX stage R_cal/I_cal - R_cal/I_cal/MD相关 R_cal/I_cal/MD相关/jal/Load - nop - R_cal/I_cal/MD相关 jal - (jump处)R_cal/I_cal/MD相关 mfhi - R_cal/I_cal/MD相关 mflo - R_cal/I_cal/MD相关 不存在
      MEM stage 不存在 R_cal/I_cal/MD相关/jal/Load - Save 不存在 不存在 不存在 不存在
    • 暂停:
      • E_Conflict:
        1
        2
        3
        4
        ori $1,$0,3
        beq $1,$0,label
        label:
        nop
      • M_Conflict:
        1
        2
        3
        4
        5
        lw $1, 0($0)      
        nop
        beq $1, $0, label
        label:
        nop
  8. 如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。

    • 手动构造:
      • 根据7中表格覆盖每一种转发和暂停的场合
      • 加入 $0 寄存器作为读写目标,确保不会错误地覆盖 $0 的值

Control表

R_cal

控制信号\指令 add sub and or slt sltu
RegWrite 1 1 1 1 1 1
aluControlSel 000 000 000 000 000 000
ALUop 0000 0001 0010 0011 0101 0110
MemWrite 0 0 0 0 0 0
MemRead 0 0 0 0 0 0
EXTop 0 0 0 0 0 0
RegDst 000 000 000 000 000 000
WDsel 00 00 00 00 00 00
NPCop 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00
ifB 0 0 0 0 0 0
ifJ 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000
useRt 1 1 1 1 1 1
islink 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0
isLikely 0 0 0 0 0 0
LoadType 0 0 0 0 0 0
SaveType 0 0 0 0 0 0
MDUop 000 000 000 000 000 000
we_HI 0 0 0 0 0 0
we_LO 0 0 0 0 0 0

I_cal

控制信号\指令 addi andi ori lui
RegWrite 1 1 1 1
aluControlSel 001 001 001 001
ALUop 0000 0010 0011 0100
MemWrite 0 0 0 0
MemRead 0 0 0 0
EXTop 1 0 0 0
RegDst 001 001 001 001
WDsel 00 00 00 00
NPCop 000 000 000 000
MemWriteDataOp 00 00 00 00
ifB 0 0 0 0
ifJ 0 0 0 0
BranchOp 000 000 000 000
useRt 0 0 0 0
islink 0 0 0 0
isConditionalLink 0 0 0 0
isLikely 0 0 0 0
LoadType 0 0 0 0
SaveType 0 0 0 0
MDUop 000 000 000 000
we_HI 0 0 0 0
we_LO 0 0 0 0

MD相关

控制信号\指令 mult multu div divu mfhi mflo mthi mtlo
RegWrite 0 0 0 0 1 1 0 0
aluControlSel 0 0 0 0 0 0 0 0
ALUop 0000 0000 0000 0000 0000 0000 0000
MemWrite 0 0 0 0 0 0 0 0
MemRead 0 0 0 0 0 0 0 0
EXTop 0 0 0 0 0 0 0 0
RegDst 000 000 000 000 000 000 000 000
WDsel 000 000 000 000 011 100 000 000
NPCop 000 000 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00 00 00
ifB 0 0 0 0 0 0 0 0
ifJ 0 0 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000 000 000
useRt 1 1 1 1 0 0 0 0
islink 0 0 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0 0 0
isLikely 0 0 0 0 0 0 0 0
LoadType 0 0 0 0 0 0 0 0
SaveType 0 0 0 0 0 0 0 0
MDUop 001 010 011 100 000 000 000 000
we_HI 0 0 0 0 0 0 1 0
we_LO 0 0 0 0 0 0 0 1

Load & Save

控制信号\指令 lb lh lw sb sh sw
RegWrite 1 1 1 0 0 0
aluControlSel 001 001 001 001 001 001
ALUop 0000 0000 0000 0000 0000 0000
MemWrite 0 0 0 1 1 1
MemRead 1 1 1 0 0 0
EXTop 1 1 1 1 1 1
RegDst 001 001 001 000 000 000
WDsel 01 01 01 00 00 00
NPCop 000 000 000 000 000 000
MemWriteDataOp 00 00 00 00 00 00
ifB 0 0 0 0 0 0
ifJ 0 0 0 0 0 0
BranchOp 000 000 000 000 000 000
useRt 0 0 0 1 1 1
islink 0 0 0 0 0 0
isConditionalLink 0 0 0 0 0 0
isLikely 0 0 0 0 0 0
LoadType 010 100 000 000 000 000
SaveType 0000 0000 0000 0010 0001 0000
MDUop 000 000 000 000 000 000
we_HI 0 0 0 0 0 0
we_LO 0 0 0 0 0 0

Branch & Jump

控制信号\指令 beq jal jr bne
RegWrite 0 1 0 0
aluControlSel 000 000 000 000
ALUop 0000 0000 0000 0000
MemWrite 0 0 0 0
MemRead 0 0 0 0
EXTop 1 0 0 1
RegDst 000 011 000 000
WDsel 00 10 00 00
NPCop 001 010 011 001
MemWriteDataOp 00 00 00 00
ifB 1 0 0 1
ifJ 0 1 1 0
BranchOp 000 000 000 001
useRt 1 0 0 1
islink 0 1 0 0
isConditionalLink 0 0 0 0
isLikely 0 0 0 0
LoadType 000 000 000 000
SaveType 0000 0000 0000 0000
MDUop 000 000 000 000
we_HI 0 0 0 0
we_LO 0 0 0 0

Copyright © 2026 CRZ's Blog

Powered by Hexo • Theme Candelas