-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
423 lines (204 loc) · 284 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>【CO】P7课下--支持异常中断处理的流水线CPU</title>
<link href="/2024/12/08/%E3%80%90CO%E3%80%91P7%E8%AF%BE%E4%B8%8B-%E6%94%AF%E6%8C%81%E5%BC%82%E5%B8%B8%E4%B8%AD%E6%96%AD%E5%A4%84%E7%90%86%E7%9A%84%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/"/>
<url>/2024/12/08/%E3%80%90CO%E3%80%91P7%E8%AF%BE%E4%B8%8B-%E6%94%AF%E6%8C%81%E5%BC%82%E5%B8%B8%E4%B8%AD%E6%96%AD%E5%A4%84%E7%90%86%E7%9A%84%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>P7在P6基础上增加了异常和中断的处理,可以处理异常指令和相应外部中断。本文为P7课下流水线CPU的设计思路和具体细节,仅供参考。</p> </div><h1 id="【CO】P7课下–支持异常中断处理的流水线CPU"><a href="#【CO】P7课下–支持异常中断处理的流水线CPU" class="headerlink" title="【CO】P7课下–支持异常中断处理的流水线CPU"></a>【CO】P7课下–支持异常中断处理的流水线CPU</h1><h2 id="设计方案"><a href="#设计方案" class="headerlink" title="设计方案"></a>设计方案</h2><p>P7的教程实在是让人摸不着头脑,但总体上还是可以分为一些任务:</p><ol><li>增加流水线识别异常指令</li><li>增加外部中断</li><li>增加协处理器以便响应异常和中断<br>我是按照3、2、1的顺序进行的,在进行P7时,尤其能感受到模块化思想的应用!模块接入了信号并进行处理,至于信号怎么来的呢?不用管,交给别的模块就好了,有了这样的思想,才能更有条理地搭建CPU。</li></ol><h3 id="增加协处理器以便响应异常和中断"><a href="#增加协处理器以便响应异常和中断" class="headerlink" title="增加协处理器以便响应异常和中断"></a>增加协处理器以便响应异常和中断</h3><p>CP0其实要做的事情也很清晰:接收异常和中断信号,根据SR寄存器的设置,做出是否相应的判断,然后输出req异常信号,指示流水线进入异常处理程序,相应异常的同时记录EPC、Cause等信息。这里要注意优先级的问题,reset信号永远是优先级最高(教程中似乎并没强调reset是同步还是异步,应该无所谓)。然后如果收到EXLClr信号,那么将exl域置为0,表示此时可以相应异常。之后如果req信号为1,即此时出现异常,那么bd、exccode、exl、epc都要做出更新,最后才是WE时,可以对SR和EPC进行写入操作</p><h3 id="增加外部中断"><a href="#增加外部中断" class="headerlink" title="增加外部中断"></a>增加外部中断</h3><p>这里需要增加桥来进行CPU和IM、DM、TC0、TC1的交互,桥说白了就是一个交换机,CPU想对外部模块(IM、DM、TC0、TC1和外部中断)进行操作和响应,对于CPU来说,它是看不到外面的世界的,所以他能做的只有对一些特殊的内存地址进行读写操作,桥则把CPU对这些规定好的特殊地址转化为对外部模块的操作。这些外部模块都由课程组给出,TC和CPU、Bridge在顶层模块之下,IM和DM在tb中实现。</p><h3 id="增加流水线识别异常指令"><a href="#增加流水线识别异常指令" class="headerlink" title="增加流水线识别异常指令"></a>增加流水线识别异常指令</h3><p>这部分细节还是蛮多的。首先我们要在每一级判断指令是否异常,指令异常是通过流水线寄存器传递下去保证先后顺序的,不能直接给CP0处理器,否则可能会出现处理顺序错误。同时每一级之间的异常也有优先级,流水下来的异常优先级最高,其次才是本级检测的异常,同一级的不同异常间优先级无所谓,毕竟在同一级不会出现多个异常。当M级的CP0(CP0不一定非得在M级,不过M级处理起来比较方便,因为所有异常到这里都已经判断完成了)发出异常信号时,此时各流水线寄存器需要将待更新的PC设置为4180,并将其他信息全部清零,这样使流水线中正在流水的异常指令后的指令不再被处理(变为nop),直到进入异常处理程序。这个地方DE寄存器有一定的特殊性,考虑阻塞时插入空泡,如果空泡的pc和bd(是否是延迟槽信息)被清空为0,那么如果nop到达M级,此时恰有中断信号,那么写入的EPC将是错误的,同时bd会影响跳转的位置,所以这两条信息是不能被清零的,而是要继承D级。实现方式如下:</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs Verilog"><span class="hljs-keyword">always</span> @(<span class="hljs-keyword">posedge</span> clk) <span class="hljs-keyword">begin</span><br> <span class="hljs-keyword">if</span>(reset===<span class="hljs-number">1'b1</span>||clear===<span class="hljs-number">1'b1</span>||req)<span class="hljs-keyword">begin</span><br> E_pc<=reset?<span class="hljs-number">0</span>:req?<span class="hljs-number">32'h00004180</span>:(clear?D_pc:<span class="hljs-number">32'b0</span>);<br>E_db<=reset?<span class="hljs-number">0</span>:(clear)?D_db:<span class="hljs-number">0</span>;<br>E_exc_code<=<span class="hljs-number">5'b0</span>;<br> E_instr<=<span class="hljs-number">32'b0</span>;<br> E_RD1<=<span class="hljs-number">32'b0</span>;<br> E_RD2<=<span class="hljs-number">32'b0</span>;<br> E_EXT<=<span class="hljs-number">32'b0</span>;<br>E_zero<=<span class="hljs-number">0</span>;<br> <span class="hljs-keyword">end</span><br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">begin</span><br> E_pc<=D_pc;<br> E_instr<=D_instr;<br> E_RD1<=D_RD1;<br> E_RD2<=D_RD2;<br> E_EXT<=D_EXT;<br>E_zero<=D_zero;<br>E_db<=D_db;<br>E_exc_code<=D_exc_code;<br> <span class="hljs-keyword">end</span><br> <span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure><p>同时仍要注意优先级,reset优先级始终是最高的,检查的时候不妨问问自己reset、stall、req分别同时到来时,会发生什么。</p><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><ol><li>请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?<br>鼠标和键盘的输入信号能够被 CPU 知晓,是通过硬件生成信号、控制器缓存与传输、中断机制触发、CPU 响应处理、以及操作系统和应用程序进一步解析和使用的层层协作实现的。</li><li>请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)<br>如果用户自定义入口地址,理论上是可以实现我们希望的功能的,但是可能会导致用户程序占用共享资源与其他部件产生冲突,同时还应该考虑权限问题,如果服务器等大型多用户计算机允许用户自定义入口,那么有可能会通过入口程序获得本没有权限获得的数据,导致可能发生安全问题。</li><li>为何与外设通信需要 Bridge?<br>Bridge为所有外设和CPU提供了桥梁。对于CPU来说,CPU并不知道外面的世界是什么样的,只能读写内存,我们的桥中可以对一些特殊位置的读写指令进行处理,使其实现与外设的交流。对于外设来说,如果没有桥的存在,那么每当增加一项外设,那么就要在硬件接口等方面做出许多更改,这显然是不切实际的,桥使外设的添加更加方便。同时桥也可以起到缓冲和统一的作用,缓冲数据的读写,同时使不同协议之间的数据传输能正常进行。</li><li>请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并针对每一种模式绘制状态移图。<br>模式0下中断信号将在响应后再进入下一个周期,需要手动重启。模式1下中断信号将自动周期性产生。<br><img src="/img/0.png" alt="0"><br><img src="/img/1.png" alt="1"></li><li>倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?<br>如果所有信息均为空,那么写入的EPC会出现错误。所以为了避免这样的问题,在DE流水线寄存器插入气泡时,应当保留原pc和bd(是否为延迟槽指令),这样才能保证写入EPC的正确。</li><li>为什么 <code>jalr</code> 指令为什么不能写成 <code>jalr $31, $31</code>?<br>因为如果该指令的延迟槽为宏观PC时,此时若发生中断,那么写入操作将无法正确进行。</li></ol>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Verilog</tag>
<tag>流水线CPU</tag>
<tag>P7课下</tag>
</tags>
</entry>
<entry>
<title>【CO】P6课下--支持更多指令的流水线CPU</title>
<link href="/2024/11/28/%E3%80%90CO%E3%80%91P6%E8%AF%BE%E4%B8%8B-%E6%94%AF%E6%8C%81%E6%9B%B4%E5%A4%9A%E6%8C%87%E4%BB%A4%E7%9A%84%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/"/>
<url>/2024/11/28/%E3%80%90CO%E3%80%91P6%E8%AF%BE%E4%B8%8B-%E6%94%AF%E6%8C%81%E6%9B%B4%E5%A4%9A%E6%8C%87%E4%BB%A4%E7%9A%84%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>P6在P5基础上添加了更多的指令,相应地在结构上会有一些变化,本文为P6课下流水线CPU的设计思路和具体细节,仅供参考。</p> </div><h1 id="【CO】P6课下–支持更多指令的流水线CPU"><a href="#【CO】P6课下–支持更多指令的流水线CPU" class="headerlink" title="【CO】P6课下–支持更多指令的流水线CPU"></a>【CO】P6课下–支持更多指令的流水线CPU</h1><h2 id="设计方案"><a href="#设计方案" class="headerlink" title="设计方案"></a>设计方案</h2><p>有了P5的流水线框架,P6的任务就更加有条理和清晰。P6的任务可以分为以下几点:</p><ol><li>实现存储器外置</li><li>实现存储器支持按字节访存</li><li>实现乘除槽支持乘除相关指令</li><li>添加一些R型和I型指令<br>下面也就从这些任务出发对已有的流水线CPU进行改造。</li></ol><h3 id="实现存储器外置"><a href="#实现存储器外置" class="headerlink" title="实现存储器外置"></a>实现存储器外置</h3><p>存储器外置部分不需要我们自己实现,在课程组公开的<code>mips_tb.v</code>中我们可以了解输出的逻辑。存储器外置将CPU和IM、DM作为同等位置的模块,通过一些接口实现模块之间的数据交流。在这部分我们需要做的工作有删除GRF中的输出部分,删除IM和DM模块,将相关信息传递给要求中对应的接口。</p><div class="note note-info"> <p>在教程“在线测试相关说明”节下的“额外说明中”,“有些同学在 P5 中采用 AT 法后,不再需要使用 <code>w_grf_we</code>”指的是AT法在CTRL中对于不需要写入GRF的指令,将这部分指令的A设置为0,testbench中的输出逻辑为<code>if (w_grf_we && (w_grf_addr != 0))</code>所以只需要把w_grf_we设置为1就可以保证正常输出,当然保留CTRL中关于w_grf_we的判断也不会影响。</p> </div><h3 id="实现存储器支持按字节访存"><a href="#实现存储器支持按字节访存" class="headerlink" title="实现存储器支持按字节访存"></a>实现存储器支持按字节访存</h3><p>要了解我们在字节使能模块(BE)和数据扩展模块(DE)中如何操作,我们还是应该先阅读testbench中的相关逻辑。<br><img src="/img/p6/p1.png" alt="写入逻辑"><br>结合教程我们可以得到两个信息:</p><ol><li>在BE中需要输出字节使能信号和待写入的数据</li><li>之前的dm_we信号不再需要,对于非存入指令只需要将其m_data_byteen设置为4’b0000<br>同时还有最重要的一点:<code>我们将一个word分为四个byte对每一个byte考虑</code>,这一点是我们处理访存指令的关键。我们还可以发现实际上我们对访问和存取进行了分离,存取指令所需要的信号和数据都由BE产生,访问指令得到的数据由DE产生,这样做也正满足课程组要求的高内聚低耦合,减少了指令间的耦合。</li></ol><h4 id="BE实现思路"><a href="#BE实现思路" class="headerlink" title="BE实现思路"></a>BE实现思路</h4><p>BE模块是为store类指令服务的,需要输出4位字节使能信号和待写入数据。根据指令和操作内存的地址,我们可以得到对应位上的字节使能信号,如sb指令操作的内存地址后两位是2’b01,那么输出的字节使能信号即为4’b0010。之所以只看操作内存地址的后两位是因为字节已经是操作的最小单位,后两位已经足够为我们指示要操作的字节位置。也可以这样理解:我们根据前30位找到了待操作的字(字对齐的原理),然后再根据后两位确定需要操作的字节。<br>对于待写入数据,我们可以通过位拼接输出32位的数据,我们读取到的数据缺失的部分我们可以用任意的数来填充,因为根据testbench我们可以知道,只会根据byteen信号把有效位置的字节进行写入,所以其他位置是什么并不重要。</p><h4 id="DE实现思路"><a href="#DE实现思路" class="headerlink" title="DE实现思路"></a>DE实现思路</h4><p>DE模块是为load类指令服务的,需要将读到的数据按要求进行扩展。对于进入模块的32位读到的数据,我们根据地址后两位确定我们要读的数据部分进行截取,可以通过宏定义的方式来实现:</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs Verilog"><span class="hljs-meta">`<span class="hljs-keyword">define</span> word m_data_rdata[31:0]</span><br><span class="hljs-meta">`<span class="hljs-keyword">define</span> half m_data_rdata[(16*m_data_addr[1]+15)-:16]</span><br><span class="hljs-meta">`<span class="hljs-keyword">define</span> byt m_data_rdata[(8*m_data_addr[1:0]+7)-:8]</span><br><span class="hljs-meta">`<span class="hljs-keyword">define</span> half_sign m_data_rdata[16*m_data_addr[1]+15]</span><br><span class="hljs-meta">`<span class="hljs-keyword">define</span> byt_sign m_data_rdata[8*m_data_addr[1:0]+7] </span><br></code></pre></td></tr></table></figure><p>之后根据指令要求分别进行扩展即可。</p><h3 id="实现乘除槽支持乘除相关指令"><a href="#实现乘除槽支持乘除相关指令" class="headerlink" title="实现乘除槽支持乘除相关指令"></a>实现乘除槽支持乘除相关指令</h3><p>首先我们把乘除指令进行分类:</p><ul><li>md类:mult、multu、div、divu</li><li>mf类:mflo、mfhi</li><li>mt类:mtlo、mthi<br>只有这些指令需要进入MDU模块,我们可以认为MDU和ALU是并行的,这样可以帮助我们更好地理解乘除指令的阻塞。<br>首先我们明确,乘除指令和其他指令没有区别,都是每个时钟周期向下流水一级(非阻塞),这样在md类的指令到达E级时,MDU模块接收到start信号开始进行乘除操作,由于我们需要模拟乘除操作的延迟,我们不能立刻将得到的答案存入HI和LO寄存器中,可以先暂存,等busy结束后立刻存入。虽然MDU在忙着进行好几个周期的计算,但指令并不是在这里等着MDU计算,而是下一时钟周期就流水到了下一级,那么我们会有这样的问题:如果前面的流水级需要md指令的计算结果那么该怎么办呢?md指令在向下流水的过程中并没有把自己的结果打包打走啊?首先需要md指令的计算结果需要通过mf指令把结果转移到GRF中的普通寄存器,所以md类指令在M、W级不会作为转发的来源。那么为了保证mf指令能将HI、LO中的值正确地转移到GRF中,当MDU处于start或busy状态时mf指令必须阻塞在D级。同样为了保证mt指令能将HI、LO寄存器正确更新,mt类指令也应在相同情况下阻塞在D级。所以阻塞条件应该在原基础上添加乘除指令带来的阻塞,选择在顶层模块进行添加,不对STALL模块内部调整(不想加接口…)。<br><img src="/img/p6/p2.png" alt="乘除阻塞"><br>对于md和mt类指令,需要接收转发,通过AT法实现即可。对于mf类指令,可能会作为转发的来源,因此在M级和W级进行转发时需要添加新的选择,即乘除类指令转发MDU结果,将该结果在流水线寄存器中流水传递下去并在转发时判断当前级是否为mf类指令即可。<br>总结一下,E级MDU处于start或busy状态,D级如果是乘除指令(md、mt、mf)那么阻塞直到计算完成;md和mt需要接收转发,通过AT法实现即可;mf需要作为来源进行转发,转发来源增添MDU的结果。<div class="note note-info"> <p>新增的乘除指令不会从E级向D级转发,因为其经过D级并没有得到什么更多的有用的信息。</p> </div></li></ul><h3 id="添加R型和I型指令"><a href="#添加R型和I型指令" class="headerlink" title="添加R型和I型指令"></a>添加R型和I型指令</h3><p>支持的指令集包括:</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm"><span class="hljs-keyword">add, </span><span class="hljs-keyword">sub, </span><span class="hljs-keyword">and, </span><span class="hljs-keyword">or, </span><span class="hljs-keyword">slt, </span><span class="hljs-keyword">sltu </span><br><span class="hljs-keyword">addi, </span><span class="hljs-keyword">andi, </span><span class="hljs-keyword">ori,lui </span><br><span class="hljs-keyword">lb, </span><span class="hljs-keyword">lh, </span><span class="hljs-keyword">lw, </span><span class="hljs-keyword">sb, </span><span class="hljs-keyword">sh, </span><span class="hljs-keyword">sw </span><br><span class="hljs-keyword">mult, </span><span class="hljs-keyword">multu, </span><span class="hljs-keyword">div, </span><span class="hljs-keyword">divu, </span><span class="hljs-keyword">mfhi, </span><span class="hljs-keyword">mflo, </span><span class="hljs-keyword">mthi, </span><span class="hljs-keyword">mtlo </span><br><span class="hljs-keyword">beq, </span><span class="hljs-keyword">bne</span><br><span class="hljs-keyword"></span><span class="hljs-keyword">j </span> <span class="hljs-keyword">jal </span> <span class="hljs-keyword">jr</span><br></code></pre></td></tr></table></figure><p>显然我们把这些指令进行分类会极大提高控制信号驱动译码时的可读性和准确性,同时添加新指令时也会更加清晰,因此我们在CTRL中对这些指令进行分类:</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs Verilog"><span class="hljs-keyword">assign</span> jump_and_link=(jal)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> cal_r=(add|sub|And|Or|slt|sltu|sll)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> cal_i=(addi|andi|ori|lui)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> load=(lw|lh|lhu|lb|lbu)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> store=(sw|sh|sb)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> md=(mult|multu|div|divu)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> mf=(mflo|mfhi)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> mt=(mtlo|mthi)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br><span class="hljs-keyword">assign</span> branch=(beq|bne)?<span class="hljs-number">1'b1</span>:<span class="hljs-number">1'b0</span>;<br></code></pre></td></tr></table></figure><p>这是该部分我们做的最重要的工作,剩余的工作便是将ALU和NPC进行扩展以支持新指令,并将CTRL中的控制信号由指令类型进行驱动。</p><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><ol><li>为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?<div class="note note-info"> <p>答:乘除法运算需要的硬件复杂性更高同时计算的延迟更大,如果整合进ALU中,会导致ALU处的关键路径延迟增大,降低整体的性能;将乘除法部件抽离出来有助于降低延迟,同时可以与ALU并行运算,提高效率。独立的HI、LO寄存器可以使乘除法的设计更加清晰,指令集的设计中不需要对通用寄存器再进行额外的管理,同时可以减少乘除法导致的流水线冲突,提高运行效率。</p> </div></li><li>真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。<div class="note note-info"> <p>答:现代流水线CPU主要有迭代方法和流水线方法两种实现方式。迭代方法基于逐步的加法和移位操作完成乘法,或者使用减法和移位完成除法。优点在于设计简单,面积占用小,只需要32位加法器和一些逻辑电路,缺点是执行速度较慢,适合乘除法指令占比低的情况。流水线方法将乘法或除法操作分解为多个流水线阶段,如部分积生成、部分积归并等,可以在流水线中同时处理多条指令。处理效率高,适合乘除法指令较多的情况,缺点在于硬件复杂度高。</p> </div></li><li>请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?<div class="note note-info"> <p>答:E级如果处于Busy或Start状态,如果D级指令为乘除法相关的指令,则阻塞在D级直到E级完成了计算。</p> </div></li><li>请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)<div class="note note-info"> <p>答:字节使能信号使写操作能准确控制字节的写入,每个字节是否写通过该位是否是1可以清晰判断,避免了错误的数据覆盖,调试时也只需要关心特定位置的字节而不需要考虑对齐和其他问题。同时字节使能信号使对多种宽度的数据的修改统一起来,便于扩展,在实际硬件设计的时候内存模块也可以实现统一设计。同时在性能上,字节使能信号能够支持对不同字节的并行修改,相较于逐个字节修改提高了内存的读写效率。</p> </div></li><li>请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢? <div class="note note-info"> <p>答:不是一字节,而是32位的字,再根据指令对我们需要的字节进行操作。在数据体积较小同时对延迟比较敏感的时候,按字节读写可以减少不需要的数据读写,从而降低由此产生的延迟,提高效率。</p> </div></li><li>为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?<div class="note note-info"> <p>答:(1)根据指令类型和指令行为对指令进行分类,译码时使指令对应的信号能够更清晰,在添加新指令时也能根据其类型快速译码而不需要逐个添加,处理数据冲突时根据指令行为可以迅速判断转发和阻塞策略,思路会更加直观。(2)模块间高内聚低耦合,减少模块间的耦合,在顶层数据通路中传递信号,使顶层通路更清晰,模块间各司其职,也便于调试和问题定位。(3)规范模块和信号命名,不同流水级信号和模块在命名中体现出来,使数据传递通路更清晰。</p> </div></li><li>在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?<div class="note note-info"> <p>答:乘除指令与乘除指令、计算指令、分支指令、跳转链接指令、访存指令。通过AT法实现阻塞和转发解决冲突。<br>测试样例:</p><figure class="highlight mips"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs mips"><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t1</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">1</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t2</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">2</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t3</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">3</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t4</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">4</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t5</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">5</span><br><span class="hljs-keyword">mult </span>$<span class="hljs-built_in">t3</span>,$<span class="hljs-built_in">t4</span><br><span class="hljs-keyword">mflo </span>$<span class="hljs-built_in">t5</span><br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">t5</span>,<span class="hljs-number">0</span>($<span class="hljs-built_in">t4</span>)<br><span class="hljs-keyword">lw </span>$<span class="hljs-built_in">t6</span>,<span class="hljs-number">0</span>($<span class="hljs-built_in">t4</span>)<br><span class="hljs-keyword">add </span>$<span class="hljs-built_in">t5</span>,$<span class="hljs-built_in">t2</span>,$<span class="hljs-built_in">t3</span><br><span class="hljs-keyword">beq </span>$<span class="hljs-built_in">t5</span>,$<span class="hljs-built_in">t4</span>,lable2<br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t1</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">1</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t2</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">2</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t3</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">3</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t4</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">4</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t5</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">5</span><br><span class="hljs-keyword">mult </span>$<span class="hljs-built_in">t6</span>,$<span class="hljs-built_in">t6</span><br><span class="hljs-keyword">mflo </span>$<span class="hljs-built_in">t7</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t7</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">7</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t7</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">8</span><br><span class="hljs-keyword">mthi </span>$<span class="hljs-built_in">t7</span><br><span class="hljs-keyword">jal </span>lable<br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t8</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">8</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t9</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">9</span><br><span class="hljs-symbol">lable:</span><br><span class="hljs-keyword">mult </span>$<span class="hljs-built_in">ra</span>,$<span class="hljs-built_in">t1</span><br><span class="hljs-symbol">lable2:</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-number">0</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">0</span> <br></code></pre></td></tr></table></figure> </div></li><li>如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证<strong>覆盖</strong>了所有需要测试的情况;如果你是<strong>完全随机</strong>生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了<strong>特殊的策略</strong>,比如构造连续数据冒险序列,请你描述一下你使用的策略如何<strong>结合了随机性</strong>达到强测的效果。<div class="note note-info"> <p>答:手动构造样例,分块构造,例如将分支指令放在D级测试阻塞,E级放一个计算指令,M级放一个访存指令,这样可以测试阻塞功能是否正常,然后用五个ori指令重置流水线,实现分块。这样分别测试不同情况下产生的数据冲突是否能正确解决。</p> </div></li></ol>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Verilog</tag>
<tag>流水线CPU</tag>
<tag>P6课下</tag>
</tags>
</entry>
<entry>
<title>【随笔】初雪</title>
<link href="/2024/11/27/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%88%9D%E9%9B%AA/"/>
<url>/2024/11/27/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%88%9D%E9%9B%AA/</url>
<content type="html"><![CDATA[<h2 id="初雪"><a href="#初雪" class="headerlink" title="初雪"></a>初雪</h2><p>昨天早上去SKP排队拿鞋,七点多就到了在冷风里冻着,不过好在最后运气不错拿了双45,哈哈现在还在手里,等黄牛砸完价再趁机出手嘿嘿。<br>下午睡了个昏天黑地,睡醒之后也不想吃饭,也不想学习,就抱着手机看呀看。忽然,看朋友圈才知道外面下雪了。穿着我的四季拖鞋就下去了。给日哥和然哥打视频炫耀了一下,我是今年禁止熏人群里第一个走在雪上的人嘿嘿。时间不算太晚又给老爸打视频炫耀了一下。<br>回去点了顿麦当劳,吃完十二点半多了,上床就是一顿大睡。<br>第二天早上睡醒就感觉不太对劲了,好像是感冒了吧。中午抢EVA电影票也没抢到,以后应该去淘票票和大麦上去抢,电影资料馆自己的app实在是差劲啊。<br>仔细一想剩下这段时间事情还是挺多的:</p><ul><li>P6课下得搭好</li><li>毛概小组展示</li><li>计组理论</li><li>校园寻宝筹备</li><li>离散作业</li><li>科研课堂中期报告(下周用)<br>这周炸开了要。。。<br><img src="/img/%E5%88%9D%E9%9B%AA/1.jpg" alt="昨天的雪"></li></ul>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>【随笔】最近</title>
<link href="/2024/11/24/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E6%9C%80%E8%BF%91/"/>
<url>/2024/11/24/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E6%9C%80%E8%BF%91/</url>
<content type="html"><![CDATA[<p>最近不知为何,想的东西很多,索性写下来吧。</p><h2 id="感觉"><a href="#感觉" class="headerlink" title="感觉"></a>感觉</h2><p>今天来教室的路上,突然发现,我好像一直在追求感觉。上了大二以来,我似乎一直在追求之前令我心安的那种旧感觉,但是似乎没有找到新的让我平静的感觉。什么感觉能令我心安呢?爸爸的办公室,那是我从小到大写作业学习的地方,疫情的时候还在里面住着上网课;聊城一中,我的高中母校,我和朋友们共同奋斗的地方,让我成长的地方;卫校的房子,我小时候的家;沙河教五二楼,大一的时候我经常在那里上自习。我的感觉似乎总和地点相关,在这些地方,总可以让我感到平静。到了学院路以来,我似乎一直在追求这些感觉,在三号楼上自习的感觉挺好的,尤其是写写随笔,不过除此之外没什么了。这里人太多了,让我有些心慌,我不喜欢太多人,所以我现在不怎么去图书馆了,我更喜欢人少一点的教室。我想,还是要静下心来做一些有意义的事吧,这样或许可以帮到我。总觉得心里慌乱,静不下来,无法专注地做一些事,不知道该干什么,那就学习吧!</p><h2 id="痛苦"><a href="#痛苦" class="headerlink" title="痛苦"></a>痛苦</h2><p>最近还有在想,如果人对自己会遭遇的痛苦提前做好准备,那么痛苦真正到来的时候,还会感到痛苦吗?会不会使人麻木?或者说体验到的痛苦是否变质?之前看到有人说,他的父亲去世的时候,他甚至没有流泪,但是在一个晚上下班回家,却自己一个人在车上哭得很难受。和前任分手的时候,那天晚上我记得只是和室友说了一句:“妈的分手了”,在禁止熏人里吐了一番苦水,过了一段时间才感觉难受,我有些纳闷这种有延迟的痛苦是如何产生的呢?人还真是神奇!那么如果为以后要遭遇的痛苦提前做好心理准备,压抑的痛苦是否会失去很多意义呢?</p><hr><p>还是词不达意啊,想的东西很难用文字描述出来,不写了,该去写作业了。</p>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>【CO】P5课上总结</title>
<link href="/2024/11/23/%E3%80%90CO%E3%80%91P5%E8%AF%BE%E4%B8%8A%E6%80%BB%E7%BB%93/"/>
<url>/2024/11/23/%E3%80%90CO%E3%80%91P5%E8%AF%BE%E4%B8%8A%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>P5第一次上机出了一些很不好的问题,归根到底还是准备不充分吧。趁这个机会把P5课上总结一下,指导一下两天后的第二次上机。</p> </div><h1 id="P5课上总结"><a href="#P5课上总结" class="headerlink" title="P5课上总结"></a>P5课上总结</h1><p>P5课上添加的指令可以分为计算类指令,分支跳转类指令,访存类指令。下面分别分析并总结一些方法。</p><h2 id="计算类指令"><a href="#计算类指令" class="headerlink" title="计算类指令"></a>计算类指令</h2><p>计算类指令较为容易,对ALU进行修改即可。然后在ALU中自上而下对不同的信号添加新指令。</p><h2 id="分支跳转类指令"><a href="#分支跳转类指令" class="headerlink" title="分支跳转类指令"></a>分支跳转类指令</h2><p>分支跳转类指令进一步可以根据链接方式分为条件链接、无条件链接和无链接。分支跳转部分其实也存在条件的问题,一般来说,分支指令都是有条件的,跳转指令不需要条件(分支条件和链接条件往往是相同的,即如果分支则发生一种行为,否则进行另一种行为)。分支跳转部分可以在D级完成,对于条件的判断对CMP模块进行扩展即可。问题的关键在于链接方式。下面分开来具体分析。</p><h3 id="无链接"><a href="#无链接" class="headerlink" title="无链接"></a>无链接</h3><p>这种指令当然十分简单,和beq、j没什么区别,对CTRL,NPC进行修改即可。</p><h3 id="无条件链接"><a href="#无条件链接" class="headerlink" title="无条件链接"></a>无条件链接</h3><p>无条件链接比如jal,如果是分支指令的话,则是有条件分支,无条件链接。这个时候由于对寄存器有写操作,会涉及转发和阻塞的问题。在CTRL里要记得在A那里指定写入的目标寄存器(应该是RA吧,不过如果是rt或者rd也无妨),在jump_and_link那里记得加上该新指令,以便于转发的判断。这种无条件链接不需要在流水中传递额外的信号,和jal基本上一样。</p><h3 id="有条件链接"><a href="#有条件链接" class="headerlink" title="有条件链接"></a>有条件链接</h3><p>这种指令需要特别注意。是否分支(和是否链接)在D级完成判断,这时注意!!!<br>第一,一定要把是否链接的判断信号流水下去,因为是否链接会影响到CTRL中对新指令写入寄存器(A)的判断和是否链接(jump_and_link)以及各种其他信号的判断,并且在CTRL中一定要是<code>新指令&&链接条件成立</code>这样去添加新指令,这样才能把两种情况分开。对于一般信号,AT信号,jump_and_link都是如此,所以要在流水线寄存器中从D级开始流水链接信号,同时CTRL增加端口接收链接信号。分支信号不需要传入流水线和CTRL,因为其在D级作用于NPC即可,只需要对NPC接收信号的端口和内部适当修改。<br>第二,对于清空延迟槽的操作,实现的操作是清空FD寄存器,清空延迟槽信号的产生一定要判断此时是否阻塞,因为如果阻塞,那么对于是否清空延迟槽这一行为的判断信号是不准确的,如果此时清空,那么会误删延迟槽。所以,清空延迟槽时一定要判断此时是否阻塞。FD寄存器的清空信号在数据通路中产生即可(判断npc_op,stall和清空延迟槽对应的条件(CMP中产生))。</p><h2 id="访存类指令"><a href="#访存类指令" class="headerlink" title="访存类指令"></a>访存类指令</h2><p>相比于一般的lw和sw,课上添加的访存指令可能存在这样的情况:存入寄存器时有条件判断到底存入哪个寄存器,这种情况下我们只能在新指令到达W级时才能知道到底写入哪个寄存器。所以新指令在E、M时的阻塞条件我们都无法判断,一种思路是暴力阻塞,即只要E、M级的指令是新指令就阻塞,这样大概率会超时。不超时的思路是,在新指令在E、M级时,同时判断D级的指令是否需要新指令可能写入的寄存器的数据,如果需要那么阻塞。这里在对stall进行改写时,最好和原来的AT法隔离开来,另写一个判断然后再或上去得到要输出的stall,这样不会干扰其他指令通过AT法实现的stall的判断。</p><hr><p>此外还为npc和ext预留了一位信号,防止上机现加产生遗漏。</p><blockquote><p>希望两天后上机顺利吧</p></blockquote><hr><p>虽然第三题条件访存被卡了一个点,两题万岁吧。</p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Verilog</tag>
<tag>流水线CPU</tag>
<tag>P5课上</tag>
</tags>
</entry>
<entry>
<title>【随笔】午后琐记</title>
<link href="/2024/11/16/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/"/>
<url>/2024/11/16/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/</url>
<content type="html"><![CDATA[<h1 id="午后琐记"><a href="#午后琐记" class="headerlink" title="午后琐记"></a>午后琐记</h1><p> 今天上午一觉醒来,已经是十点五十了。不同的是这一觉睡得很踏实,应该跟昨天人体工程和打球消耗很大的体力有关,不用起来上课,不用debug,虽然起床的那一刻起我就决定了下午去自习写作业。起床洗漱后,把堆积很久的衣服带到洗衣机里洗一洗。令我有些讨厌的是,洗衣机里留下了上个人的袜子,我想,尼玛啊有点素质吗袜子都要放洗衣机洗。还好洗衣机可以筒自洁,缓解了我的一些膈应。不过我还是克制住了把他的袜子扔到地上的冲动,放到了洗衣机对面的饮水机顶上,希望那里还有很多灰可以给他来点教训。<br> 处理完可以用洗衣机洗的衣服,我开始洗裤头和袜子,用的是老爸教我的方法,用很烫的热水泡了一会儿,这样确实洗的挺干净的。然后我拿来了浴巾和毛巾,最近几周我发觉我的浴巾有一股很难闻的味道,应该是每天洗澡然后浴巾根本晾不干,并且挂浴巾的地方晒不到太阳导致的。我倒了很多洗衣液,当然还是用的很烫的热水。说实话毛巾和浴巾还是很难洗的,冲水也不是不冲也不是,只能一遍一遍稀释,让上面残留的洗衣液少一些。哦差点忘了,我回去拿浴巾和毛巾的时候发现有两件衣服被落下没丢到洗衣机里,只好手洗。手洗和机洗的差别还是挺大的,因为机洗会有一个脱水环节,洗完的衣服会更干一些,不像我手洗的衣服还会往下滴答水。<br> 晾衣服也是一个考验,衣服架子不是很够,所以我选择把那些速干的衣服三件放在一起晾,样子显得十分滑稽。就这样上午就过去了。去合一吃了一碗麻辣烫,再去快递站拿托老爸老妈给我买的卫生纸。回去给手机充了会儿电,就出来自习了。<br> 作业还是挺多的,但是我不是很想写。计组让我很疲惫,这几天本想给自己休息一下,在写博客的时候发现hexo的一个bug,修了好几天bug想了很多解决方案,最后受测试数据的限制只在源代码基础上更改了六个字符,其中还有一个是空格。拉取申请提交上去了不过还没有审核,我感觉自己的拉取申请写的像一坨狗屎,不过也就那样吧,我也写不出更好的、更有说服力的话了。希望Hexo项目组能采纳我的建议吧,要是能把我的完全体更改方案也接纳那就太好了!我觉得Hexo的test里的情况没有实际意义,不能说明什么,不过谁知道呢?或许是我的认知太狭隘了吧。<br> 唉,感觉好累啊。上了大二以来好像越来越没有自己的生活了。生活节奏一团乱麻,熬夜,不想吃饭,没有食欲但是每次吃的还挺多。TD,博雅一次没有刷过,阳光打卡也很久没打了。每天就是写作业,玩手机,睡觉。相册里的照片没多几张,之前看到美好的东西都会拍照的,现在没有拍过。只有自己去动物园的时候拍了很多。挺羡慕动物园里的小动物的。这样挺不好的,但是我感觉好累啊,没有力量去让自己的生活更有色彩一点,这几天还是先多睡觉吧,休息休息。神经和身体高度紧张劳累后的调节还是得慢慢学慢慢探索啊!<br> 放几张大熊猫吧!<br><img src="/img/%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/9ae6f1c27143804d46d8a0dfead59c3.jpg" alt="IMG_7664"><br><img src="/img/%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/e2ced732bdf1ec0907b3044d18f4c86.jpg" alt="IMG_7664"><br><img src="/img/%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/462002ffa5682047749dd6033cb1066.jpg" alt="IMG_7664"><br><img src="/img/%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/74ca407ce1983f5d0a6bed030cec291.jpg" alt="IMG_7664"><br><img src="/img/%E5%8D%88%E5%90%8E%E7%90%90%E8%AE%B0/ebc23173dd22fd12c0663741bcc0909.jpg" alt="IMG_7664"></p>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title>【CO】P5课下--流水线CPU</title>
<link href="/2024/11/16/%E3%80%90CO%E3%80%91P5%E8%AF%BE%E4%B8%8B--%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/"/>
<url>/2024/11/16/%E3%80%90CO%E3%80%91P5%E8%AF%BE%E4%B8%8B--%E6%B5%81%E6%B0%B4%E7%BA%BFCPU/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>本文为P5课下流水线CPU的设计思路和具体细节,仅供参考。</p> </div><h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>进入P5,计组实验进入了流水线环节,搭完流水线和写这篇文章相隔两周(多谢P3课下的RAM给了我这次机会),这两周真是给自己放了一个长假。神经高度紧张后的放松是十分必要的,连续好几周的计组实在让我心力交瘁。不过太过放松自然也不是好事,就像现在,我的P5设计文档刚开始写,评测机也没有搭,理论作业今晚ddl,不过我还是有信心完成的(评测机够呛了,留到P6吧)。</p><h1 id="【CO】P5课下–流水线CPU"><a href="#【CO】P5课下–流水线CPU" class="headerlink" title="【CO】P5课下–流水线CPU"></a>【CO】P5课下–流水线CPU</h1><h2 id="总体设计思路"><a href="#总体设计思路" class="headerlink" title="总体设计思路"></a>总体设计思路</h2><p>在流水线CPU的搭建这部分,我认为最重要的是理解流水线的工作机制:1.每一流水级同一周期内执行的指令是不同的。2.每到一流水级,获取自己需要的数据(上一级传递,转发与阻塞),每离开一流水级,把自己的东西全部打包打走(传递到下一级)。只要能准确地做到这两件事,就可以实现流水线的流动。和之前的单周期CPU相比,区别我们可以从两个方面去看:物理结构上区分了流水级,指令并且无论是否需要在某一流水级实现功能,都必须流过,并且是单向的;数据通路上不同流水级之间相互影响,数据需要在不同流水级之间传递,传递方向不是单向的。基于这样的特点,我们在介绍物理结构时以流水级为单位,在介绍数据通路时以指令为单位,重点关注指令在每个流水级的行为。</p><h3 id="指令集合"><a href="#指令集合" class="headerlink" title="指令集合"></a>指令集合</h3><p>与P4相同,课下提交要求实现的指令包括add(u),sub(u),ori,lui,beq,lw,sw,nop,j,jal,jr。在此基础上添加了移位指令sll。分类如下:</p><table><thead><tr><th>R型指令</th><th>I型指令</th><th>J型指令</th></tr></thead><tbody><tr><td>add(u),sub(u),sll(nop),jr</td><td>ori,lui,beq,lw,sw</td><td>j,jal</td></tr></tbody></table><h3 id="模块设计"><a href="#模块设计" class="headerlink" title="模块设计"></a>模块设计</h3><p>之前的CPU模块包括:PC,IM,NPC,GRF,EXT,ALU,DM,CTRL等模块。流水线部分为了清晰地区分流水级,我们做出调整,将PC和IM合并作为F(Fetch)级的IFU模块;添加一个CMP模块,负责分支指令的比较(之前这部分是在ALU通过减法判断的,为了流水线的效率我们将CMP提取出来),与NPC,GRF,EXT构成D(Decode)级。ALU构成E(Execute)级,DM构成M(Memory)级。同时还有W(Write Back)回写级,将数据回写入GRF内,所以可以认为GRF既是D级元件也是W级元件。<br>同时,为了实现数据在流水线间的流动传递,在两流水级之间我们都要有一个流水线寄存器,负责接收上一流水级传递来的数据。所以抽象一下,我们的流水线寄存器其实就是这样:<code>F | D | E | M | W</code>。<br>对于CTRL,我们有两种选择,一是分布式译码,每一流水级都对CTRL进行实例化,获取在该流水级需要的数据,这样做的好处是在数据流水时只需要把指令和在该级获得的数据流水,而不需要把大量的控制信号流水,缺点是需要多次实例化控制器。二是集中式译码,在D级便将所有控制信号全部得出,优点在于只需要实例化一次控制器,缺点在于控制信号需要随指令一直流水传递。我认为,对于课程要求来说,分布式译码便于我们添加指令,如果有新的指令需要我们在某一级之后才能判断信号的值,分布式译码会让这样的过程更清晰;同时如果有新的控制信号出现,采用集中式译码需要在每一级流水线寄存器都添加新的端口;再加上我不喜欢把太多数据流水下去,更希望模块化独立化减少流水级之间的耦合,便于debug,所以选择采用分布式译码。当然,译码方式的选择仁者见仁,根据自己的喜好选择即可。<br>以上的模块我们之前都接触过,除此之外我们还需要一个STALL模块来控制阻塞信号的产生,用来处理数据冒险,具体内容我们在下面介绍。<br>综上我们的模块包括IFU,NPC,GRF,CMP,EXT,ALU,DM,CTRL,STALL,以及各流水线寄存器。顶层模块为mips,在模块命名和顶层进行模块实例化时,我们采用以下命名规则:</p><ol><li>模块命名为<code>大写流水级_大写模块名</code>,如:<code>D_CMP</code>,<code>E_ALU</code>。特别的,阻塞器和控制器模块分别为<code>STALL</code>和<code>CTRL</code>。</li><li>流水线寄存器命名为<code>大写两流水级_REG</code>,如<code>EM_REG</code>。</li><li>模块实例化时命名为小写模块名,如<code>ifu</code>,<code>npc</code>。特别的,阻塞器实例化名称为<code>Stall</code>,控制器实例化名称为<code>大写流水级_ctrl</code>,如:<code>D_ctrl</code>,<code>E_ctrl</code>。</li><li>流水线寄存器实例化命名为<code>大写两流水级_reg</code>,如<code>DE_reg</code>。<br>总体结构如下:<br><img src="/img/p5/p1.png" alt="总体结构"></li></ol><h3 id="数据通路"><a href="#数据通路" class="headerlink" title="数据通路"></a>数据通路</h3><p>除了一般的流水通路外,我们还需要转发和阻塞的策略来处理冒险情况的发生。转发的情况可以这样理解:需要的数据已经产生,不过在更后面的流水级中,这时候通过旁路转发便可以获得数据。阻塞的情况是:需要的数据还未产生,此时我们必须要阻塞流水线,直到需要的数据产生。我们采用AT法进行判断,具体内容我们在后面介绍。</p><h2 id="具体模块设计"><a href="#具体模块设计" class="headerlink" title="具体模块设计"></a>具体模块设计</h2><h3 id="F级"><a href="#F级" class="headerlink" title="F级"></a>F级</h3><h4 id="F-IFU"><a href="#F-IFU" class="headerlink" title="F_IFU"></a>F_IFU</h4><h5 id="端口说明"><a href="#端口说明" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>npc</td><td>input</td><td>[31:0]</td><td>下一条指令(未加0x00003000)</td></tr><tr><td>pc_we</td><td>input</td><td></td><td>pc是否可继续</td></tr><tr><td>pc</td><td>output</td><td>[31:0]</td><td>当前指令pc(加了0x00003000)</td></tr><tr><td>instr</td><td>output</td><td>[31:0]</td><td>当前指令的机器码</td></tr></tbody></table><h3 id="FD-REG"><a href="#FD-REG" class="headerlink" title="FD_REG"></a>FD_REG</h3><h5 id="端口说明-1"><a href="#端口说明-1" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>F_pc</td><td>input</td><td>[31:0]</td><td>接收F的pc值</td></tr><tr><td>F_instr</td><td>input</td><td>[31:0]</td><td>接收F的机器码</td></tr><tr><td>FD_we</td><td>input</td><td></td><td>是否要更新寄存器中的值(阻塞冻结)</td></tr><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>D_pc</td><td>output</td><td>[31:0]</td><td>传递给D级的pc</td></tr><tr><td>D_instr</td><td>output</td><td>[31:0]</td><td>传递给D级的instr</td></tr></tbody></table><div class="note note-info"> <p>当FD_we为0时,此时FD_REG被冻结,这样设计是满足阻塞的需要。</p> </div><h3 id="D级"><a href="#D级" class="headerlink" title="D级"></a>D级</h3><h4 id="D-EXT"><a href="#D-EXT" class="headerlink" title="D_EXT"></a>D_EXT</h4><h5 id="端口说明-2"><a href="#端口说明-2" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>in</td><td>input</td><td>[15:0]</td><td>输入需要扩展的16位立即数</td></tr><tr><td>EXTOp</td><td>input</td><td></td><td>选择符号扩展或是0扩展</td></tr><tr><td>ans</td><td>output</td><td>[31:0]</td><td>输出扩展结果</td></tr></tbody></table><h5 id="控制信号"><a href="#控制信号" class="headerlink" title="控制信号"></a>控制信号</h5><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>1’b0</td><td>0扩展</td></tr><tr><td>1’b1</td><td>符号扩展</td></tr></tbody></table><h4 id="D-GRF"><a href="#D-GRF" class="headerlink" title="D_GRF"></a>D_GRF</h4><h5 id="端口说明-3"><a href="#端口说明-3" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>A1</td><td>input</td><td>[4:0]</td><td>rs的编号</td></tr><tr><td>A2</td><td>input</td><td>[4:0]</td><td>rt的编号</td></tr><tr><td>WR</td><td>input</td><td>[4:0]</td><td>写入的寄存器的编号</td></tr><tr><td>WD</td><td>input</td><td>[31:0]</td><td>写入的数据</td></tr><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>grf_we</td><td>input</td><td></td><td>grf写入使能</td></tr><tr><td>W_pc</td><td>input</td><td>[31:0]</td><td>W级的pc值</td></tr><tr><td>RD1</td><td>output</td><td>[31:0]</td><td>读到的rs值</td></tr><tr><td>RD2</td><td>output</td><td>[31:0]</td><td>读到的rt值</td></tr></tbody></table><h5 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h5><div class="note note-danger"> <ol><li>GRF有关于写的操作,数据都要来自于W级,读操作的数据来自D级,读写是分离的。<br><img src="/img/p5/p2.png" alt="读写分离"></li><li>GRF的读操作,读出的数据需要进行内部转发,即W级向D级的转发。<br><img src="/img/p5/p3.png" alt="内部转发"></li></ol> </div><h4 id="D-NPC"><a href="#D-NPC" class="headerlink" title="D_NPC"></a>D_NPC</h4><h5 id="端口说明-4"><a href="#端口说明-4" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>D_pc</td><td>input</td><td>[31:0]</td><td>当前D级的pc</td></tr><tr><td>F_pc</td><td>input</td><td>[31:0]</td><td>当前F级的pc</td></tr><tr><td>zero</td><td>input</td><td></td><td>rs和rt的值是否相等</td></tr><tr><td>ra_data</td><td>input</td><td>[31:0]</td><td>jr时回到的寄存器的值</td></tr><tr><td>imm16</td><td>input</td><td>[15:0]</td><td>16位立即数</td></tr><tr><td>imm26</td><td>input</td><td>[25:0]</td><td>26位立即数</td></tr><tr><td>npc_op</td><td>input</td><td>[1:0]</td><td>选择npc来源</td></tr><tr><td>npc</td><td>output</td><td>[31:0]</td><td>输出npc的值(未加0x00003000)</td></tr></tbody></table><h5 id="控制信号-1"><a href="#控制信号-1" class="headerlink" title="控制信号"></a>控制信号</h5><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>2’b01</td><td>j型跳转</td></tr><tr><td>2’b10</td><td>jr跳转</td></tr><tr><td>2’b11</td><td>beq跳转</td></tr><tr><td>其他</td><td>F_pc+4</td></tr></tbody></table><h5 id="注意事项-1"><a href="#注意事项-1" class="headerlink" title="注意事项"></a>注意事项</h5><div class="note note-danger"> <p>在计算j和beq指令跳转或分支到的下一条指令时,指令集说明中的pc+4为D_pc+4,而由于延迟槽的存在,如果顺序执行下一条指令,那么npc应该是F_pc+4,为延迟槽指令后的指令。</p> </div><h4 id="D-CMP"><a href="#D-CMP" class="headerlink" title="D_CMP"></a>D_CMP</h4><h5 id="端口说明-5"><a href="#端口说明-5" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>rs_data</td><td>input</td><td>[31:0]</td><td>接收用来判断的rs的值</td></tr><tr><td>rt_data</td><td>input</td><td>[31:0]</td><td>接收用来判断的rt的值</td></tr><tr><td>eq</td><td>output</td><td></td><td>两值是否相等</td></tr></tbody></table><h5 id="注意事项-2"><a href="#注意事项-2" class="headerlink" title="注意事项"></a>注意事项</h5><div class="note note-danger"> <p>这里是实际上D级唯一需要除指令外数据的地方,rs、rt寄存器的值这里需要接收转发,除了W级的内部转发,还需要有M级的转发。接收转发的条件是来源级写入的目标寄存器和需要读的rs或rt寄存器相同。<br><img src="/img/p5/p4.png" alt="D级转发"></p> </div><div class="note note-info"> <p>理解了D_CMP是D级唯一需要除指令外数据的地方,就可以明白E级向D级的转发是不必要的。如果D级的指令是beq,那么E级的指令不能是跳转和分支指令,因为课程文档里有这样的明确规定:“编译器保证<strong>延迟槽中的指令不能为分支或跳转指令</strong>。”所以如果存在E向D的转发,那么E级已有的数据一定有是D级需要的。这样的指令只能是jal,那么这就会产生矛盾。所以目前为了满足P5的要求,我们不需要E级向D级转发。</p> </div><h3 id="DE-REG"><a href="#DE-REG" class="headerlink" title="DE_REG"></a>DE_REG</h3><h5 id="端口说明-6"><a href="#端口说明-6" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>D_pc</td><td>input</td><td>[31:0]</td><td>接收D的pc值</td></tr><tr><td>D_instr</td><td>input</td><td>[31:0]</td><td>接收D的机器码</td></tr><tr><td>clear</td><td>input</td><td></td><td>是否要清空寄存器中的值(阻塞添加气泡)</td></tr><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>D_RD1</td><td>input</td><td>[31:0]</td><td>D级读到的rs值</td></tr><tr><td>D_RD2</td><td>input</td><td>[31:0]</td><td>D级读到的rt值</td></tr><tr><td>D_zero</td><td>input</td><td></td><td>D级得到的rs和rt是否相等的判断</td></tr><tr><td>D_EXT</td><td>input</td><td>[31:0]</td><td>D级得到的扩展后的值</td></tr><tr><td>E_pc</td><td>output</td><td>[31:0]</td><td>传递给E级的pc</td></tr><tr><td>E_instr</td><td>output</td><td>[31:0]</td><td>传递给E级的机器码</td></tr><tr><td>E_RD1</td><td>output</td><td>[31:0]</td><td>传递给E级的rs值</td></tr><tr><td>E_RD2</td><td>output</td><td>[31:0]</td><td>传递给E级的rt值</td></tr><tr><td>E_EXT</td><td>output</td><td>[31:0]</td><td>传递给E级的扩展后结果</td></tr><tr><td>E_zero</td><td>output</td><td></td><td>传递给E级的是否相等的判断</td></tr></tbody></table><div class="note note-info"> <p>当clear为1时,此时DE_REG被清空,相当于在当前的D级指令前插入了一个气泡。</p> </div><h3 id="E级"><a href="#E级" class="headerlink" title="E级"></a>E级</h3><h4 id="E-ALU"><a href="#E-ALU" class="headerlink" title="E_ALU"></a>E_ALU</h4><h5 id="端口说明-7"><a href="#端口说明-7" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>A</td><td>input</td><td>[31:0]</td><td>第一个运算数</td></tr><tr><td>B</td><td>input</td><td>[31:0]</td><td>第二个运算数</td></tr><tr><td>sll</td><td>input</td><td>[4:0]</td><td>sll移位的位数</td></tr><tr><td>alu_op</td><td>input</td><td>[2:0]</td><td>选择ALU进行的操作</td></tr><tr><td>ans</td><td>output</td><td>[31:0]</td><td>运算的结果</td></tr></tbody></table><h5 id="控制信号-2"><a href="#控制信号-2" class="headerlink" title="控制信号"></a>控制信号</h5><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>3’b000</td><td>A+B</td></tr><tr><td>3’b001</td><td>A-B</td></tr><tr><td>3’b010</td><td>AorB</td></tr><tr><td>3’b011</td><td>B<<sll</td></tr><tr><td>3’b100</td><td>B<<16(lui)</td></tr><tr><td>其他</td><td>32’b0</td></tr></tbody></table><h5 id="注意事项-3"><a href="#注意事项-3" class="headerlink" title="注意事项"></a>注意事项</h5><div class="note note-danger"> <p>注意:</p><ol><li>E级从流水线寄存器读到的rs、rt值不是E级的rs、rt的值,还需要接收来自M级和W级的转发,因为此时的M级,也就是上一周期的E级在经过了E级后,有可能会得到新的值还未更新进寄存器,所以这个时候E级要接收M级和W级的转发,得到的才是真正的E级的rs和rt的值,也是接下来E级向M级传递的值。</li><li>进入ALU的运算数和E级的rs、rt寄存器的值并不相同。第二个运算数还可能是扩展后的立即数。</li></ol> </div><h3 id="EM-REG"><a href="#EM-REG" class="headerlink" title="EM_REG"></a>EM_REG</h3><h5 id="端口说明-8"><a href="#端口说明-8" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>E_pc</td><td>input</td><td>[31:0]</td><td>接收E的pc值</td></tr><tr><td>E_instr</td><td>input</td><td>[31:0]</td><td>接收E的机器码</td></tr><tr><td>E_RD2</td><td>input</td><td>[31:0]</td><td>接收E的rt值</td></tr><tr><td>E_alu_ans</td><td>input</td><td>[31:0]</td><td>接收E计算出的计算结果</td></tr><tr><td>E_zero</td><td>input</td><td></td><td>接收E的判断结果(来自D)</td></tr><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>M_pc</td><td>output</td><td>[31:0]</td><td>传递给M级的pc</td></tr><tr><td>M_instr</td><td>output</td><td>[31:0]</td><td>传递给M级的机器码</td></tr><tr><td>M_RD2</td><td>output</td><td>[31:0]</td><td>传递给M级的rt值</td></tr><tr><td>M_alu_ans</td><td>output</td><td>[31:0]</td><td>传递给M级的ALU计算结果</td></tr><tr><td>M_zero</td><td>output</td><td></td><td>传递给M级的是否相等的判断</td></tr></tbody></table><h3 id="M级"><a href="#M级" class="headerlink" title="M级"></a>M级</h3><h4 id="M-DM"><a href="#M-DM" class="headerlink" title="M_DM"></a>M_DM</h4><h5 id="端口说明-9"><a href="#端口说明-9" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>addr</td><td>input</td><td>[31:0]</td><td>读或写的目标地址</td></tr><tr><td>DataWrite</td><td>input</td><td>[31:0]</td><td>写入DM的数据</td></tr><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>dm_we</td><td>input</td><td></td><td>DM写使能信号</td></tr><tr><td>M_pc</td><td>input</td><td>[31:0]</td><td>M级的pc值</td></tr><tr><td>ans</td><td>output</td><td>[31:0]</td><td>读到或写入的值</td></tr></tbody></table><h3 id="MW-REG"><a href="#MW-REG" class="headerlink" title="MW_REG"></a>MW_REG</h3><h5 id="端口说明-10"><a href="#端口说明-10" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>clk</td><td>input</td><td></td><td>时钟信号</td></tr><tr><td>reset</td><td>input</td><td></td><td>复位信号</td></tr><tr><td>M_pc</td><td>input</td><td>[31:0]</td><td>M级的pc</td></tr><tr><td>M_instr</td><td>input</td><td>[31:0]</td><td>M级的instr</td></tr><tr><td>M_alu_ans</td><td>input</td><td>[31:0]</td><td>M级的ALU计算结果</td></tr><tr><td>M_dm_read</td><td>input</td><td>[31:0]</td><td>M级的DM读到的数据</td></tr><tr><td>M_zero</td><td>input</td><td></td><td>M级是否相等判断结果</td></tr><tr><td>W_pc</td><td>output</td><td>[31:0]</td><td>传递给W级的pc</td></tr><tr><td>W_instr</td><td>output</td><td>[31:0]</td><td>传递给W级的instr</td></tr><tr><td>W_alu_ans</td><td>output</td><td>[31:0]</td><td>传递给W级的ALU计算结果</td></tr><tr><td>W_dm_read</td><td>output</td><td>[31:0]</td><td>传递给W级的DM中读到的数据</td></tr><tr><td>W_zero</td><td>output</td><td></td><td>传递给W级的是否相等的判断结果</td></tr></tbody></table><blockquote><p>zero信号的传递我发现是真没用,但是搭cpu的时候一直传过来了,也就懒得改了</p></blockquote><p>关于CTRL和STALL,这两个模块和转发和阻塞关系紧密,于是放到数据通路分析再来介绍。</p><h2 id="数据通路分析"><a href="#数据通路分析" class="headerlink" title="数据通路分析"></a>数据通路分析</h2><p>除了上面的流水通路,我们还需要转发旁路和阻塞来保证流水线的正常运作。转发和阻塞都是我们为了解决冒险采取的策略。至于采用哪种策略,我们需要通过AT法来分析。A即判断前级数据来源寄存器和后级数据写入寄存器是否相等。T即判断T_use和T_new的大小关系,决定是否阻塞。根据课程组要求,在D级即判断是否阻塞,因此需计算D级的T_use。当T_use≥T_new时,说明在使用数据之前数据已经产生,所以此时可以转发,当T_use<T_new时,使用数据时数据还没有产生,此时必须阻塞。理论如此,但我们实际实现时可以采用无脑转发的策略。即不管T的关系,只要A满足了转发条件就转发,这样做是合理的原因在于:如果转发的数据是需要的,此时一定不会阻塞,如果此时发生了阻塞,那么转发过去的数据也是无用的。所以我们只需要判断是否阻塞即可。</p><h3 id="阻塞"><a href="#阻塞" class="headerlink" title="阻塞"></a>阻塞</h3><p>根据定义,我们可以得到T_use和T_new:</p><table><thead><tr><th>指令</th><th>A(目标寄存器)</th><th>T_use_rs</th><th>T_use_rt</th><th>T_new_E</th><th>T_new_M</th><th>T_new_W</th></tr></thead><tbody><tr><td>add</td><td>rd</td><td>1</td><td>1</td><td>1</td><td>0</td><td>0</td></tr><tr><td>sub</td><td>rd</td><td>1</td><td>1</td><td>1</td><td>0</td><td>0</td></tr><tr><td>sll</td><td>rd</td><td>3</td><td>1</td><td>1</td><td>0</td><td>0</td></tr><tr><td>ori</td><td>rt</td><td>1</td><td>3</td><td>1</td><td>0</td><td>0</td></tr><tr><td>lui</td><td>rt</td><td>3</td><td>3</td><td>1</td><td>0</td><td>0</td></tr><tr><td>beq</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>lw</td><td>rt</td><td>1</td><td>3</td><td>2</td><td>1</td><td>0</td></tr><tr><td>sw</td><td>0</td><td>1</td><td>2</td><td>0</td><td>0</td><td>0</td></tr><tr><td>j</td><td>0</td><td>3</td><td>3</td><td>0</td><td>0</td><td>0</td></tr><tr><td>jal</td><td>31</td><td>3</td><td>3</td><td>0</td><td>0</td><td>0</td></tr><tr><td>jr</td><td>0</td><td>0</td><td>3</td><td>0</td><td>0</td><td>0</td></tr></tbody></table><p>上表中,如果不需要数据则T_use为3,如果不产生数据则T_new为0。<br>T的产生,我们将其放在CTRL中产生,我们的CTRL采用分布式译码,每一流水级都有CTRL在,实例化时只需要读出所在级的T即可。</p><div class="note note-info"> <p>这里我们要有模块化思想,减少耦合,我们的CTRL只负责产生T,这一点根据指令即可做到。STALL模块负责使用T,判断是否要阻塞,不必考虑T从哪里来。所以模块间分工要明确,各司其职即可。引用wxm助教的话:<br><span class="label label-success">同样地,模块内部计算这些信号时,我们也不考虑上层如何使用这些信号,只需要在模块内部正确给出这些信号。 这样,我们考虑一条指令的转发逻辑时,再也不是”这是一条 jal 指令,它的 T_new 为 0,后续的指令无需阻塞,如果后续的指令写入地址为 31, 则需要将它的 pc + 8 转发给后续指令“。 而是这样:”这是一条 jal 指令,它的 T_new 为 0,写入地址为 31 , wData 为 pc + 8“;同时在上层我们发现:”D 级指令与某一级的指令地址均为 31,指令的 T_new <= T_use ,无需阻塞。使用该指令的 wData 。“这样,jal 的转发过程就完成了。</span></p> </div><h3 id="转发"><a href="#转发" class="headerlink" title="转发"></a>转发</h3><p>其实转发只有有限的旁路,我们可通过表格整理出来:</p><table><thead><tr><th>转出</th><th>转入</th></tr></thead><tbody><tr><td>M级</td><td>D级、E级</td></tr><tr><td>W级</td><td>D级、E级、M级</td></tr></tbody></table><p>看起来相当的简单,当然,重要的是我们转发过去的数据是什么。我们从指令的角度考虑,转发的值都是寄存器的值,对于寄存器有操作的指令只有add、sub、sll、ori、lui、lw、jal。考虑在M级向D和E转发的值,这时不考虑lw,因为此时还没有产生数据,我们会发现除了jal,其他的值都是经过E级后ALU的计算结果,jal的转发值应该是M_pc+8(因为有延迟槽所以是+8)。所以M级的转发值只有这两种。同理,对于W级,多出的转发值只需要再加上W_dm_read即可。</p><div class="note note-info"> <p>接收转发时注意数据的优先级顺序,要优先接收来自较早层级的转发,防止接收本该被覆盖的值。<br><img src="/img/p5/p5.png" alt="转发优先级"><br>于是,考虑这样的情况,D级接收了来自M级的转发,则下个周期E级接收W级的转发,但这个时候W转发值的优先级已经不如M级高了,所以能保证我们转发机制的正确性。</p> </div><h3 id="CTRL和STALL的结构"><a href="#CTRL和STALL的结构" class="headerlink" title="CTRL和STALL的结构"></a>CTRL和STALL的结构</h3><h4 id="CTRL"><a href="#CTRL" class="headerlink" title="CTRL"></a>CTRL</h4><h5 id="端口说明-11"><a href="#端口说明-11" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>instr</td><td>input</td><td>[31:0]</td><td>接收指令机器码</td></tr><tr><td>npc_op</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>alu_op</td><td>output</td><td>[2:0]</td><td></td></tr><tr><td>grf_we</td><td>output</td><td></td><td></td></tr><tr><td>dm_we</td><td></td><td></td><td></td></tr><tr><td>ext_op</td><td></td><td></td><td></td></tr><tr><td>OpCode</td><td>output</td><td>[5:0]</td><td></td></tr><tr><td>FuncCode</td><td>output</td><td>[5:0]</td><td></td></tr><tr><td>rs</td><td>output</td><td>[4:0]</td><td></td></tr><tr><td>rt</td><td>output</td><td>[4:0]</td><td></td></tr><tr><td>rd</td><td>output</td><td>[4:0]</td><td></td></tr><tr><td>shamt</td><td>output</td><td>[4:0]</td><td></td></tr><tr><td>imm16</td><td>output</td><td>[15:0]</td><td></td></tr><tr><td>imm26</td><td>output</td><td>[25:0]</td><td></td></tr><tr><td>A</td><td>output</td><td>[4:0]</td><td>写入的目标寄存器</td></tr><tr><td>alu_slt</td><td>output</td><td></td><td>1’b1则ALU的第二个操作数是扩展后的立即数,1’b0则是rt</td></tr><tr><td>T_use_rs</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>T_use_rt</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>T_new_E</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>T_new_M</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>T_new_W</td><td>output</td><td>[1:0]</td><td></td></tr><tr><td>jump_and_link</td><td>output</td><td></td><td>跳转连接指令的标志,这标志着需要转发的值为pc+8</td></tr></tbody></table><div class="note note-info"> <p>实例化时这些端口按需取用即可。</p> </div><h4 id="STALL"><a href="#STALL" class="headerlink" title="STALL"></a>STALL</h4><h5 id="端口说明-12"><a href="#端口说明-12" class="headerlink" title="端口说明"></a>端口说明</h5><table><thead><tr><th>端口</th><th>input/output</th><th>位宽</th><th>功能</th></tr></thead><tbody><tr><td>T_use_rs</td><td>input</td><td>[1:0]</td><td></td></tr><tr><td>T_use_rt</td><td>input</td><td>[1:0]</td><td></td></tr><tr><td>T_new_E</td><td>input</td><td>[1:0]</td><td></td></tr><tr><td>T_new_M</td><td>input</td><td>[1:0]</td><td></td></tr><tr><td>T_new_W</td><td>input</td><td>[1:0]</td><td></td></tr><tr><td>D_rs</td><td>input</td><td>[4:0]</td><td></td></tr><tr><td>D_rt</td><td>input</td><td>[4:0]</td><td></td></tr><tr><td>E_A3</td><td>input</td><td>[4:0]</td><td>E的写入目的寄存器</td></tr><tr><td>M_A3</td><td>input</td><td>[4:0]</td><td>M的写入目的寄存器</td></tr><tr><td>stall</td><td>output</td><td></td><td>阻塞信号</td></tr></tbody></table><h5 id="注意事项-4"><a href="#注意事项-4" class="headerlink" title="注意事项"></a>注意事项</h5><p>判断是否阻塞时,只需要判断E和M是否影响D即可,因为W级可以内部转发将值给D,不会发生阻塞,当然也可以从AT的角度去看,W的T_new都是0,不会发生阻塞。<br>阻塞时,发生的操作为:</p><ul><li>冻结pc(pc_we为0)</li><li>清空DE_REG(clear为1)</li><li>冻结FD(FD_we为0)</li></ul><hr><p>到这里我们的流水线CPU便可以搭建完成了</p><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><ol><li><p>我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。</p><div class="note note-info"> <p>答:把分支判断从E级提前到了D级,从AT法的角度来看可以认为使分支指令的T_use减少了1,这样更容易产生冒险并进行阻塞,所以并不是总能提高效率。例如</p><figure class="highlight mips"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs mips"><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t1</span>,<span class="hljs-number">1</span><br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">t1</span>,<span class="hljs-number">0</span>($<span class="hljs-built_in">zero</span>)<br><span class="hljs-keyword">lw </span>$<span class="hljs-built_in">t2</span>,<span class="hljs-number">0</span>($<span class="hljs-built_in">zero</span>)<br><span class="hljs-keyword">add </span>$<span class="hljs-number">0</span>,$<span class="hljs-number">0</span>,$<span class="hljs-number">0</span><br><span class="hljs-keyword">beq </span>$<span class="hljs-built_in">t1</span>,$<span class="hljs-built_in">t2</span>,lable<br></code></pre></td></tr></table></figure><p>如果在E级进行判断则是不需要阻塞的,而在D级进行判断则需要阻塞,导致效率的降低。</p> </div></li><li><p>因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?</p><div class="note note-info"> <p>答:因为有了延迟槽,对于 jal 等需要将指令地址写入寄存器的指令,其后的指令一定会紧跟执行,所以要写回的应该是延迟槽后的指令,也就是PC+8。</p> </div></li><li><p>我们要求大家所有转发数据都来源于流水寄存器而不能是功能部件(如 DM 、 ALU ),请思考为什么?</p><div class="note note-info"> <p>答:功能部件基本上都是组合逻辑,存在延迟同时输出不稳定,从流水寄存器转发则可以避免。</p> </div></li><li><p>我们为什么要使用 GPR 内部转发?该如何实现?</p><div class="note note-info"> <p>答:如果不进行内部转发,则在W和D产生数据冒险时,读到的结果会是错误的。<br>实现如下:</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs Verilog"><span class="hljs-keyword">assign</span> RD1=(A1===<span class="hljs-number">5'b0</span>)?<span class="hljs-number">32'b0</span>:<br> (A1===WR)?WD:<br> grf[A1];<br><span class="hljs-keyword">assign</span> RD2=(A2===<span class="hljs-number">5'b0</span>)?<span class="hljs-number">32'b0</span>:<br> (A2===WR)?WD:<br> grf[A2];<br></code></pre></td></tr></table></figure> </div></li><li><p>我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路?</p><div class="note note-info"> <p>答:需求者来自于D、E、M,供给者来源于M、W。转发数据通路存在M向D、E的转发,转发数据为pc+8或alu_ans;W向D、E、M的转发,转发数据为pc+8或alu_ans或dm_read。</p> </div></li><li><p>在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。</p><div class="note note-info"> <p>答:可以把指令分为R型计算、I型计算、访存、分支跳转。</p><ul><li>计算类指令需要更改CTRL、ALU</li><li>访存类指令需要更改CTRL、以及进入DM的输入通路</li><li>分支跳转类指令需要修改CTRL、NPC,同时注意转发通路。</li></ul> </div></li><li><p>确定你的译码方式,简要描述你的译码器架构,并思考该架构的优势以及不足。</p><div class="note note-info"> <p>答:采用了分布式译码,我认为,对于课程要求来说,分布式译码便于我们添加指令,如果有新的指令需要我们在某一级之后才能判断信号的值,分布式译码会让这样的过程更清晰;同时如果有新的控制信号出现,采用集中式译码需要在每一级流水线寄存器都添加新的端口;再加上我不喜欢把太多数据流水下去,更希望模块化独立化减少流水级之间的耦合,便于debug,所以选择采用分布式译码。好处是不需要把大量的控制信号流水,缺点是需要多次实例化控制器。</p> </div></li></ol><h2 id="测试方案"><a href="#测试方案" class="headerlink" title="测试方案"></a>测试方案</h2><p>手动构造数据,自动进行对拍。时间不充足(休息太久)没有随机生成数据,手动构造时采取分块构造的策略,每一部分指令验证某一功能,块与块之间联系几乎没有。对转发和阻塞进行着重验证。</p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Verilog</tag>
<tag>P5课下</tag>
<tag>流水线CPU</tag>
</tags>
</entry>
<entry>
<title>【Hexo】Fluid主题美化</title>
<link href="/2024/11/14/%E3%80%90Hexo%E3%80%91Fluid%E4%B8%BB%E9%A2%98%E7%BE%8E%E5%8C%96/"/>
<url>/2024/11/14/%E3%80%90Hexo%E3%80%91Fluid%E4%B8%BB%E9%A2%98%E7%BE%8E%E5%8C%96/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>最近一直忙于CO,深感疲惫,于是打算对博客进行美化。既是为了放松一下心情,也是创造一个更愉悦的写作环境,让自己多写一些东西。</p> </div><h2 id="美化清单"><a href="#美化清单" class="headerlink" title="美化清单"></a>美化清单</h2><ol><li><a href="#%E5%85%A8%E5%B1%8F%E8%83%8C%E6%99%AF">全屏背景</a></li><li><a href="#%E8%90%BD%E9%9B%AA%E5%8A%A8%E7%94%BB">落雪动画</a></li><li><a href="#%E6%AF%9B%E7%8E%BB%E7%92%83%E4%B8%BB%E9%9D%A2%E6%9D%BF">毛玻璃主面板</a></li><li><a href="#%E7%AD%BE%E5%90%8D%E5%8A%A8%E7%94%BB">签名动画</a></li><li><a href="#%E6%A0%87%E7%AD%BE%E5%8F%98%E5%8C%96">标签变化</a></li><li><a href="#%E6%96%87%E7%AB%A0%E6%BB%91%E5%85%A5%E5%8A%A8%E7%94%BB">文章滑入动画</a></li><li><a href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4">运行时间</a></li><li><a href="#%E8%B0%83%E6%95%B4%E5%B1%80%E9%83%A8%E6%96%87%E5%AD%97%E9%A2%9C%E8%89%B2">调整局部文字颜色</a></li></ol><h2 id="自定义美化原理和一般流程"><a href="#自定义美化原理和一般流程" class="headerlink" title="自定义美化原理和一般流程"></a>自定义美化原理和一般流程</h2><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>实现以上的自定义美化借助的是<a href="https://hexo.io/zh-cn/api/injector.html">Hexo的注入器功能</a>。通过注入器功能,我们可以在指定位置注入HTML代码,实现元素的增加和JavaScript脚本的引入和执行。同时Fluid支持我们引入<a href="https://hexo.fluid-dev.com/docs/guide/#%E8%87%AA%E5%AE%9A%E4%B9%89-js-css-html">自定义JS/CSS文件</a>。这样我们就可以把自定义的JS和CSS文件引入到最终渲染的HTML页面中,实现我们想要的自定义美化效果。<br>首先来看注入器:<br>我们需要在博客根目录下的<code>script</code>文件夹新建一个JavaScript文件,例如<code>injector.js</code>(名字随意,只要是js后缀即可)。以我的Js文件为例:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs JS"><span class="hljs-keyword">const</span> { <span class="hljs-attr">root</span>: siteRoot = <span class="hljs-string">"/"</span> } = hexo.<span class="hljs-property">config</span>;<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_begin"</span>, <span class="hljs-string">`<div id="web_bg"></div>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/backgroundize.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/snow.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/title.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/sign.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/scrollAnimation.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/time-insert.js"></script>`</span>);<br><br>hexo.<span class="hljs-property">extend</span>.<span class="hljs-property">injector</span>.<span class="hljs-title function_">register</span>(<span class="hljs-string">"body_end"</span>,<span class="hljs-string">`<script src="<span class="hljs-subst">${siteRoot}</span>js/time.js"></script>`</span>);<br></code></pre></td></tr></table></figure><p>我们再来观察HTML代码: </p><p><img src="/img/%E7%BE%8E%E5%8C%96/p1.png" alt="HTML"> </p><p>这里我们可以看到body_begin的两条注释之间有我们在injector中注入的诸多<code>div</code>块,同时两条注释之间有我们注入的Js脚本块和canvas画布。相信到这里已经可以明白注入器的工作效果。 </p><div class="note note-danger"> <p>在写这里时,我发现了一个Hexo的bug,有时间会在另一篇文章中详细写出。我对injector的源码进行了更改,现在我的Pull Request仍在审核,如果通过那么我将很高兴! </p> </div> <h3 id="一般流程"><a href="#一般流程" class="headerlink" title="一般流程"></a>一般流程</h3><p>想要自定义美化效果,我们只需要引入Js文件和CSS文件。<br>首先在注入器中加入我们希望注入的代码块,建议将Js脚本注入<code>body_end</code>部分,同时对照开发者工具中的HTML代码选择适当的注入位置。注入器中的Js文件都会被自动加入到网页资源中,因此不需要在<code>_config.fluid.yml</code>中的<code>custom_js</code>中重复引入。</p><div class="note note-info"> <p>注意这里的Js文件存放的位置应该在博客根目录下的<code>source</code>文件夹新建一个<code>js</code>文件夹目录,存放我们所有的Javascript文件。</p> </div> <p>然后我们引入CSS文件。我们需要在<code>_config.fluid.yml</code>中的<code>custom_css</code>选项中引入我们的自定义css文件,注意这里的根目录都是<code>source</code>文件夹。 </p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs YAML"><span class="hljs-attr">custom_css:</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-string">/css/glassBg.css</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-string">/css/sign.css</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-string">/css/part-text.css</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-string">/css/scrollAnimation.css</span><br></code></pre></td></tr></table></figure><div class="note note-info"> <p>注意这里的css文件存放的位置应该在博客根目录下的<code>source</code>文件夹新建一个<code>css</code>文件夹目录,存放我们所有的css文件。</p> </div> <p>这样我们就可以方便地添加自定义的美化效果。</p><h2 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h2><h3 id="全屏背景"><a href="#全屏背景" class="headerlink" title="全屏背景"></a>全屏背景</h3><p>思路是在body部分添加一个div块使背景图片填满整个屏幕并且固定其位置,呈现出的效果即实现了背景全屏。同时删除原只在banner部分存在的背景图,并将banner部分的蒙版改为透明,使其不影响全屏效果的呈现。 </p><p>backgroundize.js:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-variable language_">document</span><br><br> .<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'#web_bg'</span>)<br><br> .<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'style'</span>, <span class="hljs-string">`background-image: <span class="hljs-subst">${<span class="hljs-variable language_">document</span>.querySelector(<span class="hljs-string">'.banner'</span>).style.background.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">0</span>]}</span>;position: fixed;width: 100%;height: 100%;z-index: -1;background-size: cover;`</span>);<br><br><span class="hljs-variable language_">document</span><br><br> .<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">"#banner"</span>)<br><br> .<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'style'</span>, <span class="hljs-string">'background-image: url()'</span>)<br><br><span class="hljs-variable language_">document</span><br><br> .<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">"#banner .mask"</span>)<br><br> .<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'style'</span>, <span class="hljs-string">'background-color:rgba(0,0,0,0)'</span>)<br></code></pre></td></tr></table></figure><h3 id="落雪动画"><a href="#落雪动画" class="headerlink" title="落雪动画"></a>落雪动画</h3><p>在整个body上创建画布并创建雪花运动对象,模拟其运动即可。原文链接:<a href="https://ihuan.me/2172.html">分享两种圣诞节雪花特效JS代码(网站下雪效果)</a>。 </p><p>snow.js:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-comment">/* 控制下雪 */</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">snowFall</span>(<span class="hljs-params">snow</span>) {<br><br> <span class="hljs-comment">/* 可配置属性 */</span><br><br> snow = snow || {};<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxFlake</span> = snow.<span class="hljs-property">maxFlake</span> || <span class="hljs-number">200</span>; <span class="hljs-comment">/* 最多片数 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">flakeSize</span> = snow.<span class="hljs-property">flakeSize</span> || <span class="hljs-number">10</span>; <span class="hljs-comment">/* 雪花形状 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">fallSpeed</span> = snow.<span class="hljs-property">fallSpeed</span> || <span class="hljs-number">1</span>; <span class="hljs-comment">/* 坠落速度 */</span><br><br>}<br><br><span class="hljs-comment">/* 兼容写法 */</span><br><br>requestAnimationFrame = <span class="hljs-variable language_">window</span>.<span class="hljs-property">requestAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">mozRequestAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">webkitRequestAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">msRequestAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">oRequestAnimationFrame</span> ||<br><br> <span class="hljs-keyword">function</span>(<span class="hljs-params">callback</span>) { <span class="hljs-built_in">setTimeout</span>(callback, <span class="hljs-number">1000</span> / <span class="hljs-number">60</span>); };<br><br>cancelAnimationFrame = <span class="hljs-variable language_">window</span>.<span class="hljs-property">cancelAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">mozCancelAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">webkitCancelAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">msCancelAnimationFrame</span> ||<br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">oCancelAnimationFrame</span>;<br><br><span class="hljs-comment">/* 开始下雪 */</span><br><br>snowFall.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">start</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){<br><br> <span class="hljs-comment">/* 创建画布 */</span><br><br> snowCanvas.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>);<br><br> <span class="hljs-comment">/* 创建雪花形状 */</span><br><br> createFlakes.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>);<br><br> <span class="hljs-comment">/* 画雪 */</span><br><br> drawSnow.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>)<br><br>}<br><br><span class="hljs-comment">/* 创建画布 */</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">snowCanvas</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-comment">/* 添加Dom结点 */</span><br><br> <span class="hljs-keyword">var</span> snowcanvas = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">"canvas"</span>);<br><br> snowcanvas.<span class="hljs-property">id</span> = <span class="hljs-string">"snowfall"</span>;<br><br> snowcanvas.<span class="hljs-property">width</span> = <span class="hljs-variable language_">window</span>.<span class="hljs-property">innerWidth</span>;<br><br> snowcanvas.<span class="hljs-property">height</span> = <span class="hljs-variable language_">window</span>.<span class="hljs-property">innerHeight</span>;<br><br> snowcanvas.<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">"style"</span>, <span class="hljs-string">"position: fixed; top: 0; left: 0; z-index: 1; pointer-events: none;"</span>);<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementsByTagName</span>(<span class="hljs-string">"body"</span>)[<span class="hljs-number">0</span>].<span class="hljs-title function_">appendChild</span>(snowcanvas);<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">canvas</span> = snowcanvas;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">ctx</span> = snowcanvas.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">"2d"</span>);<br><br> <span class="hljs-comment">/* 窗口大小改变的处理 */</span><br><br> <span class="hljs-variable language_">window</span>.<span class="hljs-property">onresize</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<br><br> snowcanvas.<span class="hljs-property">width</span> = <span class="hljs-variable language_">window</span>.<span class="hljs-property">innerWidth</span>;<br><br> snowcanvas.<span class="hljs-property">height</span> = <span class="hljs-variable language_">window</span>.<span class="hljs-property">innerHeight</span>;<br><br> }<br><br>}<br><br><span class="hljs-comment">/* 雪运动对象 */</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">flakeMove</span>(<span class="hljs-params">canvasWidth, canvasHeight, flakeSize, fallSpeed</span>) {<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * canvasWidth); <span class="hljs-comment">/* x坐标 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * canvasHeight); <span class="hljs-comment">/* y坐标 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">size</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * flakeSize + <span class="hljs-number">2</span>; <span class="hljs-comment">/* 形状 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxSize</span> = flakeSize; <span class="hljs-comment">/* 最大形状 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">0.2</span> + fallSpeed; <span class="hljs-comment">/* 坠落速度 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">fallSpeed</span> = fallSpeed; <span class="hljs-comment">/* 坠落速度 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velY</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span>; <span class="hljs-comment">/* Y方向速度 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velX</span> = <span class="hljs-number">0</span>; <span class="hljs-comment">/* X方向速度 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">stepSize</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() / <span class="hljs-number">30</span>; <span class="hljs-comment">/* 步长 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">step</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>()*<span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span>*<span class="hljs-number">2</span>; <span class="hljs-comment">/* 步数 */</span><br><br>}<br><br>flakeMove.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">update</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">var</span> x = <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span>,<br><br> y = <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span>;<br><br> <span class="hljs-comment">/* 左右摆动(余弦) */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velX</span> *= <span class="hljs-number">0.98</span>;<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">velY</span> <= <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span>) {<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velY</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span><br><br> }<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velX</span> += <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">cos</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">step</span> += <span class="hljs-number">.05</span>) * <span class="hljs-variable language_">this</span>.<span class="hljs-property">stepSize</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span> += <span class="hljs-variable language_">this</span>.<span class="hljs-property">velY</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span> += <span class="hljs-variable language_">this</span>.<span class="hljs-property">velX</span>;<br><br> <span class="hljs-comment">/* 飞出边界的处理 */</span><br><br> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span> >= canvas.<span class="hljs-property">width</span> || <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span> <= <span class="hljs-number">0</span> || <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span> >= canvas.<span class="hljs-property">height</span> || <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span> <= <span class="hljs-number">0</span>) {<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">reset</span>(canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>)<br><br> }<br><br>};<br><br><span class="hljs-comment">/* 飞出边界-放置最顶端继续坠落 */</span><br><br>flakeMove.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">reset</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params">width, height</span>) {<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * width);<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span> = <span class="hljs-number">0</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">size</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxSize</span> + <span class="hljs-number">2</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">1</span> + <span class="hljs-variable language_">this</span>.<span class="hljs-property">fallSpeed</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velY</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">speed</span>;<br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">velX</span> = <span class="hljs-number">0</span>;<br><br>};<br><br><span class="hljs-comment">// 渲染雪花-随机形状(此处可修改雪花颜色!!!)</span><br><br>flakeMove.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">render</span> = <span class="hljs-keyword">function</span>(<span class="hljs-params">ctx</span>) {<br><br> <span class="hljs-keyword">var</span> snowFlake = ctx.<span class="hljs-title function_">createRadialGradient</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span>, <span class="hljs-number">0</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">size</span>);<br><br> snowFlake.<span class="hljs-title function_">addColorStop</span>(<span class="hljs-number">0</span>, <span class="hljs-string">"rgba(255, 255, 255, 0.9)"</span>); <span class="hljs-comment">/* 此处是雪花颜色,默认是白色 */</span><br><br> snowFlake.<span class="hljs-title function_">addColorStop</span>(<span class="hljs-number">.5</span>, <span class="hljs-string">"rgba(255, 255, 255, 0.5)"</span>); <span class="hljs-comment">/* 若要改为其他颜色,请自行查 */</span><br><br> snowFlake.<span class="hljs-title function_">addColorStop</span>(<span class="hljs-number">1</span>, <span class="hljs-string">"rgba(255, 255, 255, 0)"</span>); <span class="hljs-comment">/* 找16进制的RGB 颜色代码。 */</span><br><br> ctx.<span class="hljs-title function_">save</span>();<br><br> ctx.<span class="hljs-property">fillStyle</span> = snowFlake;<br><br> ctx.<span class="hljs-title function_">beginPath</span>();<br><br> ctx.<span class="hljs-title function_">arc</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">x</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">y</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">size</span>, <span class="hljs-number">0</span>, <span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span> * <span class="hljs-number">2</span>);<br><br> ctx.<span class="hljs-title function_">fill</span>();<br><br> ctx.<span class="hljs-title function_">restore</span>();<br><br>};<br><br><span class="hljs-comment">/* 创建雪花-定义形状 */</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">createFlakes</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">var</span> maxFlake = <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxFlake</span>,<br><br> flakes = <span class="hljs-variable language_">this</span>.<span class="hljs-property">flakes</span> = [],<br><br> canvas = <span class="hljs-variable language_">this</span>.<span class="hljs-property">canvas</span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < maxFlake; i++) {<br><br> flakes.<span class="hljs-title function_">push</span>(<span class="hljs-keyword">new</span> <span class="hljs-title function_">flakeMove</span>(canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">flakeSize</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">fallSpeed</span>))<br><br> }<br><br>}<br><br><span class="hljs-comment">/* 画雪 */</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">drawSnow</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">var</span> maxFlake = <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxFlake</span>,<br><br> flakes = <span class="hljs-variable language_">this</span>.<span class="hljs-property">flakes</span>;<br><br> ctx = <span class="hljs-variable language_">this</span>.<span class="hljs-property">ctx</span>, canvas = <span class="hljs-variable language_">this</span>.<span class="hljs-property">canvas</span>, that = <span class="hljs-variable language_">this</span>;<br><br> <span class="hljs-comment">/* 清空雪花 */</span><br><br> ctx.<span class="hljs-title function_">clearRect</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>);<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> e = <span class="hljs-number">0</span>; e < maxFlake; e++) {<br><br> flakes[e].<span class="hljs-title function_">update</span>();<br><br> flakes[e].<span class="hljs-title function_">render</span>(ctx);<br><br> }<br><br> <span class="hljs-comment">/* 一帧一帧的画 */</span><br><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">loop</span> = <span class="hljs-title function_">requestAnimationFrame</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<br><br> drawSnow.<span class="hljs-title function_">apply</span>(that);<br><br> });<br><br>}<br><br><span class="hljs-comment">/* 调用及控制方法 */</span><br><br><span class="hljs-keyword">var</span> snow = <span class="hljs-keyword">new</span> <span class="hljs-title function_">snowFall</span>({<span class="hljs-attr">maxFlake</span>:<span class="hljs-number">60</span>});<br><br>snow.<span class="hljs-title function_">start</span>();<br></code></pre></td></tr></table></figure><h3 id="毛玻璃主面板"><a href="#毛玻璃主面板" class="headerlink" title="毛玻璃主面板"></a>毛玻璃主面板</h3><p>首先在<code>_config.fluid.yml</code>中调整board_color:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs YAML"><span class="hljs-attr">board_color:</span> <span class="hljs-string">"#ffffffad"</span><br><br><span class="hljs-attr">board_color_dark:</span> <span class="hljs-string">"#000000ad"</span><br></code></pre></td></tr></table></figure><p>然后添加css文件:</p><p>glassBg:</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-id">#board</span> {<br> -webkit-backdrop-<span class="hljs-attribute">filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">15px</span>);<br> backdrop-<span class="hljs-attribute">filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">15px</span>);<br>}<br><br><span class="hljs-selector-id">#toc</span> {<br> <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;<br> <span class="hljs-attribute">top</span>: <span class="hljs-number">4rem</span>;<br> <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--board-bg-color);<br> <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;<br> -webkit-backdrop-<span class="hljs-attribute">filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">15px</span>);<br> backdrop-<span class="hljs-attribute">filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">15px</span>);<br>}<br><br></code></pre></td></tr></table></figure><p>这样便可以为主面板board部分和侧边栏toc部分都添加了模糊样式,实现了毛玻璃效果。 </p><div class="note note-danger"> <p>注意:修改某些yml、css、js文件后更新部署页面内容后在浏览器上查看可能会没有变化,这是因为浏览器会将这些静态资源缓存,以提高访问效率。为了查看变化,可以在开发者工具中的网络菜单下选择“禁用缓存”然后刷新,也可以手动清除浏览器缓存。</p> </div><h3 id="签名动画"><a href="#签名动画" class="headerlink" title="签名动画"></a>签名动画</h3><p>原理可以在这里学习:<a href="https://www.bilibili.com/video/BV1Qi4y1Y7Sp/?vd_source=af1e2ae99bc588177f6c0b3ca5e3060d">SVG的描边动画</a>。SVG可以在<a href="https://danmarshall.github.io/google-font-to-svg-path/">Google Font</a>获取。 </p><p>对应的sign.js:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-keyword">const</span> navbarBrand = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.container a'</span>);<br><br>navbarBrand.<span class="hljs-property">innerHTML</span> = <span class="hljs-string">`</span><br><span class="hljs-string"></span><br><span class="hljs-string"><svg class="svg" width="516.211" height="104.591" viewBox="0 0 516.211 104.591" xmlns="http://www.w3.org/2000/svg"></span><br><span class="hljs-string"></span><br><span class="hljs-string"><g class="g" id="svgGroup" stroke-linecap="round" </span><br><span class="hljs-string">fill-rule="evenodd" font-size="9pt" stroke="#000" </span><br><span class="hljs-string">stroke-width="0.25mm" fill="none" style="stroke:#000;stroke-width:0.25mm;fill:none"></span><br><span class="hljs-string"></span><br><span class="hljs-string"><path d="省略的内容" vector-effect="non-scaling-stroke"/></span><br><span class="hljs-string"></span><br><span class="hljs-string"></g></span><br><span class="hljs-string"></span><br><span class="hljs-string"></svg></span><br><span class="hljs-string"></span><br><span class="hljs-string">`</span>;<br><br><span class="hljs-keyword">const</span> paths = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.container .navbar-brand .svg .g path'</span>)<br><br><span class="hljs-keyword">const</span> len = paths.<span class="hljs-title function_">getTotalLength</span>()<br><br>paths.<span class="hljs-property">style</span>.<span class="hljs-title function_">setProperty</span>(<span class="hljs-string">'--l'</span>, len)<br></code></pre></td></tr></table></figure><p>对应的sign.css:</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.svg</span> {<br> <span class="hljs-attribute">width</span>: <span class="hljs-number">250pt</span>;<br> <span class="hljs-attribute">height</span>: <span class="hljs-number">50pt</span>;<br>}<br><br><span class="hljs-selector-class">.svg</span> <span class="hljs-selector-tag">path</span> {<br> <span class="hljs-attribute">stroke</span>: white;<br> <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">1pt</span>;<br> <span class="hljs-attribute">stroke-linecap</span>: round;<br> <span class="hljs-attribute">stroke-dasharray</span>: <span class="hljs-built_in">var</span>(--l);<br> <span class="hljs-attribute">stroke-dashoffset</span>: <span class="hljs-built_in">var</span>(--l);<br> <span class="hljs-attribute">fill</span>: none;<br> <span class="hljs-attribute">fill-rule</span>: nonzero;<br> <span class="hljs-attribute">animation</span>: stroke <span class="hljs-number">25s</span> forwards;<br> -webkit-<span class="hljs-attribute">animation</span>: stroke <span class="hljs-number">25s</span> forwards;<br>}<br><br><span class="hljs-keyword">@keyframes</span> stroke {<br> <span class="hljs-selector-tag">to</span> {<br> <span class="hljs-attribute">stroke-dashoffset</span>: <span class="hljs-number">0</span>;<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>实现过程是让一条虚线的长度等于path的长度,起始偏移量也等于path的长度,这样起始状态是看不到任何线条的,然后通过css绘制关键帧,结束时偏移量为0,这样实现了结束时线条完全显现的效果。</p><h3 id="标签变化"><a href="#标签变化" class="headerlink" title="标签变化"></a>标签变化</h3><p>思路很简单,利用JavaScript监视页面状态是否变化即可。<br>title.js:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-keyword">var</span> <span class="hljs-title class_">OriginTitle</span> = <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span>;<br><br><span class="hljs-keyword">var</span> titleTime;<br><br><span class="hljs-variable language_">document</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'visibilitychange'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">document</span>.<span class="hljs-property">hidden</span>) {<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span> = <span class="hljs-string">'(ง •̀_•́)ง‼这里是mRNA的Blog~'</span>;<br><br> <span class="hljs-built_in">clearTimeout</span>(titleTime);<br><br> }<br><br> <span class="hljs-keyword">else</span> {<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span> = <span class="hljs-string">'( つ•̀ω•́)つ欢迎回来!'</span>;<br><br> titleTime = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span> = <span class="hljs-title class_">OriginTitle</span>;<br><br>}, <span class="hljs-number">2000</span>);<br><br> }<br><br>});<br></code></pre></td></tr></table></figure><h3 id="文章滑入动画"><a href="#文章滑入动画" class="headerlink" title="文章滑入动画"></a>文章滑入动画</h3><p>借鉴了大佬的文章和代码,感谢大佬!<a href="https://qingshaner.com/Hexo%2520fluid%25E4%25B8%25BB%25E9%25A2%2598%25E9%25A6%2596%25E9%25A1%25B5%25E6%25B7%25BB%25E5%258A%25A0%25E6%2596%2587%25E7%25AB%25A0%25E6%25BB%2591%25E5%2585%25A5%25E5%258A%25A8%25E7%2594%25BB/">原文链接</a><br>scrollAnimation.js: </p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-keyword">const</span> cards = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">'.index-card'</span>)<br><br><span class="hljs-keyword">if</span> (cards.<span class="hljs-property">length</span>) {<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.row'</span>).<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'style'</span>, <span class="hljs-string">'overflow: hidden;'</span>)<br><br> <span class="hljs-keyword">const</span> coefficient = <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">clientWidth</span> > <span class="hljs-number">768</span> ? <span class="hljs-number">.5</span> : <span class="hljs-number">.3</span><br><br> <span class="hljs-keyword">const</span> origin = <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">clientHeight</span> - cards[<span class="hljs-number">0</span>].<span class="hljs-title function_">getBoundingClientRect</span>().<span class="hljs-property">height</span> * coefficient<br><br> <span class="hljs-keyword">function</span> <span class="hljs-title function_">throttle</span>(<span class="hljs-params">fn, wait</span>) {<br><br> <span class="hljs-keyword">let</span> timer = <span class="hljs-literal">null</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">const</span> context = <span class="hljs-variable language_">this</span>;<br><br> <span class="hljs-keyword">const</span> args = <span class="hljs-variable language_">arguments</span>;<br><br> <span class="hljs-keyword">if</span> (!timer) {<br><br> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><br> fn.<span class="hljs-title function_">apply</span>(context, args);<br><br> timer = <span class="hljs-literal">null</span>;<br><br>}, wait)<br><br> }<br><br> }<br><br> }<br><br> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handle</span>(<span class="hljs-params"></span>) {<br><br> cards.<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">card</span> =></span> {<br><br> card.<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'style'</span>, <span class="hljs-string">`--state: <span class="hljs-subst">${(card.getBoundingClientRect().top - origin) < <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>}</span>;`</span>)<br><br> })<br><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">1</span>)<br><br> }<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">"scroll"</span>, <span class="hljs-title function_">throttle</span>(handle, <span class="hljs-number">100</span>));<br><br>}<br></code></pre></td></tr></table></figure><p>scrollAnimation.css: </p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.index-card</span> {<br> <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span>;<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-built_in">calc</span>(<span class="hljs-number">1.5</span> - <span class="hljs-number">0.5</span> * <span class="hljs-built_in">var</span>(--state)));<br> <span class="hljs-attribute">opacity</span>: <span class="hljs-built_in">var</span>(--state);<br> <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">2rem</span>;<br>}<br><br><span class="hljs-selector-class">.index-img</span> <span class="hljs-selector-tag">img</span> {<br> <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><h3 id="运行时间"><a href="#运行时间" class="headerlink" title="运行时间"></a>运行时间</h3><p>首先在适当的位置插入div块和脚本,我选择在底部的统计信息和备案信息间插入。<br><img src="/img/%E7%BE%8E%E5%8C%96/p2.png" alt="运行时间"><br>time-insert.js:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><br><span class="hljs-keyword">var</span> newDiv = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">"div"</span>);<br><br>newDiv.<span class="hljs-property">innerHTML</span> = <span class="hljs-string">`</span><br><span class="hljs-string"></span><br><span class="hljs-string"> <span id="time"></span></span><br><span class="hljs-string"></span><br><span class="hljs-string"> <script src="/js/time.js"></script></span><br><span class="hljs-string"></span><br><span class="hljs-string"> `</span>;<br><br><span class="hljs-variable language_">document</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">"DOMContentLoaded"</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">var</span> div1 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementsByClassName</span>(<span class="hljs-string">"statistics"</span>)[<span class="hljs-number">0</span>];<br><br> <span class="hljs-keyword">var</span> div2 = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementsByClassName</span>(<span class="hljs-string">"beian"</span>)[<span class="hljs-number">0</span>];<br><br> <span class="hljs-comment">/*调试检查*/</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(div1); <br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(div2); <br> <br> <span class="hljs-keyword">if</span> (div1 && div2) {<br><br> div1.<span class="hljs-property">parentNode</span>.<span class="hljs-title function_">insertBefore</span>(newDiv, div2);<br><br> }<br><br>});<br></code></pre></td></tr></table></figure><p>这里为了保证能正确找到div1和div2,选择在DOM内容加载完成后进行插入操作。</p><p>计算运行时间的脚本time.js内容如下:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs JavaScript"><span class="hljs-keyword">var</span> now = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">createtime</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">var</span> grt= <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">"07/23/2024 16:37:00"</span>);<br><br> now.<span class="hljs-title function_">setTime</span>(now.<span class="hljs-title function_">getTime</span>()+<span class="hljs-number">250</span>);<br><br> days = (now - grt ) / <span class="hljs-number">1000</span> / <span class="hljs-number">60</span> / <span class="hljs-number">60</span> / <span class="hljs-number">24</span>; dnum = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(days);<br><br> hours = (now - grt ) / <span class="hljs-number">1000</span> / <span class="hljs-number">60</span> / <span class="hljs-number">60</span> - (<span class="hljs-number">24</span> * dnum); hnum = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(hours);<br><br> <span class="hljs-keyword">if</span>(<span class="hljs-title class_">String</span>(hnum).<span class="hljs-property">length</span> ==<span class="hljs-number">1</span> ){hnum = <span class="hljs-string">"0"</span> + hnum;} minutes = (now - grt ) / <span class="hljs-number">1000</span> /<span class="hljs-number">60</span> - (<span class="hljs-number">24</span> * <span class="hljs-number">60</span> * dnum) - (<span class="hljs-number">60</span> * hnum);<br><br> mnum = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(minutes); <span class="hljs-keyword">if</span>(<span class="hljs-title class_">String</span>(mnum).<span class="hljs-property">length</span> ==<span class="hljs-number">1</span> ){mnum = <span class="hljs-string">"0"</span> + mnum;}<br><br> seconds = (now - grt ) / <span class="hljs-number">1000</span> - (<span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * dnum) - (<span class="hljs-number">60</span> * <span class="hljs-number">60</span> * hnum) - (<span class="hljs-number">60</span> * mnum);<br><br> snum = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">round</span>(seconds); <span class="hljs-keyword">if</span>(<span class="hljs-title class_">String</span>(snum).<span class="hljs-property">length</span> ==<span class="hljs-number">1</span> ){snum = <span class="hljs-string">"0"</span> + snum;}<br><br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">"time"</span>).<span class="hljs-property">innerHTML</span> = <span class="hljs-string">"本站已安全运行 "</span>+ dnum+<span class="hljs-string">" 天 "</span>+ hnum + <span class="hljs-string">" 小时 "</span> + mnum + <span class="hljs-string">" 分 "</span> + snum + <span class="hljs-string">" 秒"</span>;<br><br>}<br><br><span class="hljs-built_in">setInterval</span>(<span class="hljs-string">"createtime()"</span>,<span class="hljs-number">250</span>);<br></code></pre></td></tr></table></figure><h3 id="调整局部文字颜色"><a href="#调整局部文字颜色" class="headerlink" title="调整局部文字颜色"></a>调整局部文字颜色</h3><p>如果在<code>_config.fluid.yml</code>中直接调整,那么改变的是所有文字的颜色,为了仅调整页面底部footer区域内文字的颜色,可以利用css中的<code>!important</code>。<br>part-text.css: </p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-tag">footer</span> {<br> <span class="hljs-attribute">color</span>: <span class="hljs-number">#c58989</span><span class="hljs-meta">!important</span>;<br>}<br><br><span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">span</span> {<br> <span class="hljs-attribute">color</span>: <span class="hljs-number">#c58989</span><span class="hljs-meta">!important</span>;<br>}<br><br><span class="hljs-selector-class">.beian</span> {<br> <span class="hljs-attribute">color</span>: <span class="hljs-number">#c58989</span><span class="hljs-meta">!important</span>;<br>}<br><br><span class="hljs-selector-class">.beian</span> <span class="hljs-selector-tag">span</span> {<br> <span class="hljs-attribute">color</span>: <span class="hljs-number">#c58989</span><span class="hljs-meta">!important</span>;<br>}<br><br><span class="hljs-selector-class">.beian</span> <span class="hljs-selector-tag">a</span> {<br> <span class="hljs-attribute">color</span>: <span class="hljs-number">#c58989</span><span class="hljs-meta">!important</span>;<br>}<br></code></pre></td></tr></table></figure><p>这样就完成了局部文字颜色的调整,如果希望调整其他区域,可以利用开发者工具找到对应位置的代码块,在css里进行修改即可。</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>根据我的个人喜好完成了以上美化,看着装修好后的博客,写作的想法强烈了许多。小提一句,通过我的域名mrna16.top来访问是通过http访问的,这样会导致浏览器警告不安全,本想趁这个机会配置一下,但是好像需要对服务器nginx很麻烦地配置一通,于是作罢,毕竟还有mrna16.github.io可以通过https访问,至于何时将top网址的http访问转接到https,随缘吧。</p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>Fluid</tag>
<tag>JavaScript</tag>
<tag>css</tag>
<tag>前端</tag>
</tags>
</entry>
<entry>
<title>【OOPre】BUAA 2024秋季 OOpre总结</title>
<link href="/2024/11/06/%E3%80%90OOPre%E3%80%91BUAA%202024%E7%A7%8B%E5%AD%A3%20oopre%E6%80%BB%E7%BB%93/"/>
<url>/2024/11/06/%E3%80%90OOPre%E3%80%91BUAA%202024%E7%A7%8B%E5%AD%A3%20oopre%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<h2 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h2><p><img src="/img/oopre/Arch.png" alt="Arch"></p><h3 id="架构设计"><a href="#架构设计" class="headerlink" title="架构设计"></a>架构设计</h3><ul><li>MainClass对输入进行解析,并执行相应的顶层操作。</li><li>CE为战斗力接口,Equipment、Bottle、Adventurer实现了CE接口。</li><li>Good为物品接口,Equipment、Bottle实现了Good接口。</li><li>Equipment下有Axe、Sword、Blade三个子类。</li><li>Bottle下有HpBottle、DefBottle、AtkBottle三个子类。</li><li>Fragment为碎片类,与其他类、接口无继承、实现关系。</li></ul><h3 id="架构调整"><a href="#架构调整" class="headerlink" title="架构调整"></a>架构调整</h3><ul><li>提出实现战斗力单位要求时,考虑战斗力单位具有相似的行为,如计算战斗力,查看战斗力等,添加了CE战斗力接口。</li><li>提出背包要求时,为了将装备和瓶子能一起放入背包,添加了Good物品接口,使一个对于Good的容器能盛放装备和瓶子。</li><li>装备和瓶子中的方法和属性对子类等是通用的,故作为父类。</li></ul><h2 id="JUnit心得"><a href="#JUnit心得" class="headerlink" title="JUnit心得"></a>JUnit心得</h2><p>为了保证覆盖率的要求,编写的JUnit对于各类、方法、分支都必须进行充分的测试。在这个过程中即使数据不够强,也能检测出简单的Bug,减少很多由于疏忽造成的错误,同时也使自己对于容易出现的Bug更加警觉。当然趁此机会思考编写强力的测试数据对程序进行测试而不是仅为了满足覆盖率要求才是应该做的,我个人做的不够好,希望能在以后注意并改正。</p><h2 id="OOpre心得"><a href="#OOpre心得" class="headerlink" title="OOpre心得"></a>OOpre心得</h2><h3 id="面向过程到面向对象"><a href="#面向过程到面向对象" class="headerlink" title="面向过程到面向对象"></a>面向过程到面向对象</h3><p>面向过程像是修路,面向对象像是造车。面向对象理解起来更加具象,同时封装、继承、多态等概念又使得能在此基础上进行抽象和模块化,便于扩展和复用。这点要比面向过程舒服得多。同时Java还注重内容的安全和访问权限,这点是之前未接触过的,更加贴近实际工程和应用,加深了我对编程的理解。</p><h3 id="合作开发与代码扩展性和规范性"><a href="#合作开发与代码扩展性和规范性" class="headerlink" title="合作开发与代码扩展性和规范性"></a>合作开发与代码扩展性和规范性</h3><p>在OOpre课程中了解了Git,Git的作用不止提交作业,在以后进行合作开发的过程中离不开Git的使用,OOpre课程使我对这一点的认识更加深刻。同时CheckStyle这一贴近企业开发的工具使我更加注意自己的代码风格,注意代码规范。迭代作业的形式也让我在编程过程中形成了考虑代码可扩展性和兼容性的良好思考方式。</p><h2 id="建议"><a href="#建议" class="headerlink" title="建议"></a>建议</h2><p>课程内容可以由作业内容引出,让大家课下用到的是课上学到的东西,否则仅仅是从指导书学东西而非将应用课堂知识。</p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>OOPre</tag>
</tags>
</entry>
<entry>
<title>【CO】ISE的仿真工作流程浅析</title>
<link href="/2024/11/06/%E3%80%90CO%E3%80%91ISE%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E6%B5%85%E6%9E%90/"/>
<url>/2024/11/06/%E3%80%90CO%E3%80%91ISE%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E6%B5%85%E6%9E%90/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>对于ISE的仿真工作流程的一些个人见解</p> </div><h1 id="ISE的仿真工作流程浅析"><a href="#ISE的仿真工作流程浅析" class="headerlink" title="ISE的仿真工作流程浅析"></a>ISE的仿真工作流程浅析</h1><h2 id="仿真是什么"><a href="#仿真是什么" class="headerlink" title="仿真是什么"></a>仿真是什么</h2><p>我们编写Verilog程序,是通过代码来模拟硬件操作。将代码转化为硬件行为并反映出来,这是ISE仿真去做的事情。至于我们看到的图形化界面所呈现的波形等信息,只是对仿真结果的呈现。并不是真正的仿真。</p><h2 id="ISE仿真工作流程"><a href="#ISE仿真工作流程" class="headerlink" title="ISE仿真工作流程"></a>ISE仿真工作流程</h2><p>在编写P4评测机时,对于ISE执行的命令其实只有两条:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">1. ISE 的安装路径/bin/nt64/fuse -nodebug -prj mips.prj -o mips.exe mips_tb<br>2. mips.exe -nolog -tclbatch mips.tcl<br></code></pre></td></tr></table></figure><p>第一条命令是对mips_tb进行编译,编译结果为mips.exe文件。执行这一步骤会在当前工作目录下产生两项关键文件(当然还有一些fuse的脚本之类的不过不在讨论范围内):isim文件夹和mips.exe可执行程序。如果我们把产生的该.exe文件移至其他目录下执行,则会报错找不到isim文件夹,这告诉我们两条信息: </p><ol><li>.exe的执行依赖于执行的工作目录 </li><li>这里编译产生的.exe依赖于isim文件夹。</li></ol><p>这里的isim文件夹即为mips.exe进行仿真所依赖的文件,是必不可少的。同时我们还应该知道,<code>.exe</code>的执行是和当前工作目录有关的,故应当把isim文件夹和生成的mips.exe程序放在同一目录下。</p><p>mips.prj是执行的项目所包含的所有文件,内容如:</p><figure class="highlight isbl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs isbl"><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\ALU.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\CTRL.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\datapath.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\DM.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\EXT.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\grf.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\IM.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\mips.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\mips_tb.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\npc.v"</span><br><span class="hljs-variable">verilog</span> <span class="hljs-variable"><span class="hljs-class">work</span></span> <span class="hljs-string">"C:\Users\UserX\Desktop\p4_auto_test\mips\pc.v"</span><br></code></pre></td></tr></table></figure><div class="note note-info"> <p>如果verilog程序中使用了<code>$readmemh()</code>来读取相对路径指向的文件,那么当前目录为工作目录。</p> </div><p>第二条命令是按照mips_tcl中规定的方式执行mips.exe,mips_tcl中的内容如:</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk">run <span class="hljs-number">200</span>us;<br><span class="hljs-keyword">exit</span><br></code></pre></td></tr></table></figure><p>所以总结一下ISE的仿真流程:</p><ol><li>编译文件产生.exe可执行程序和isim文件夹(仿真依赖文件)。</li><li>运行.exe并产生输出。</li></ol><p>运行评测命令后目录结构如下:<br><img src="/img/ise/p1.png" alt="目录结构"></p><h2 id="Add-Source和New-Source的一些区别"><a href="#Add-Source和New-Source的一些区别" class="headerlink" title="Add Source和New Source的一些区别"></a>Add Source和New Source的一些区别</h2><p>Add Source是通过相对路径进行调用而不是通过拷贝,即Add Source的文件并没有在当前项目的工作目录下,只是被当前项目调用。New Source是在当前项目目录(即工作目录)下新建文件。<br>但是在实例化过程中并不会产生影响:<br><img src="/img/ise/p3.png" alt="p3"><br><img src="/img/ise/p4.png" alt="p4"><br>因为这些文件都在.prj中被包括进了项目内:<br><img src="/img/ise/p2.png" alt="prj文件"><br>同时我们也可有这样的猜想:<br>在寻找文件时,会优先在当前项目目录下进行查找:第一张图中没有导入datapath时有一个问号标志,后面的路径是<code>E:\ise\</code>。</p><hr><p>这是我在讨论区回答问题时的一些总结。原文连接:<a href="http://cscore.buaa.edu.cn/#/discussion_area/1494/1798/posts">http://cscore.buaa.edu.cn/#/discussion_area/1494/1798/posts</a></p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>ISE</tag>
<tag>Verilog</tag>
</tags>
</entry>
<entry>
<title>【CO】P4课下——单周期CPU(Verilog实现)</title>
<link href="/2024/11/03/%E3%80%90CO%E3%80%91P4%E8%AF%BE%E4%B8%8B--%E5%8D%95%E5%91%A8%E6%9C%9FCPU(Verilog%E5%AE%9E%E7%8E%B0)/"/>
<url>/2024/11/03/%E3%80%90CO%E3%80%91P4%E8%AF%BE%E4%B8%8B--%E5%8D%95%E5%91%A8%E6%9C%9FCPU(Verilog%E5%AE%9E%E7%8E%B0)/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>本文为P4课下单周期CPU设计思路与具体细节,仅供参考</p> </div><h1 id="P4课下–单周期CPU的Verilog实现"><a href="#P4课下–单周期CPU的Verilog实现" class="headerlink" title="P4课下–单周期CPU的Verilog实现"></a>P4课下–单周期CPU的Verilog实现</h1><h2 id="总体设计方案"><a href="#总体设计方案" class="headerlink" title="总体设计方案"></a>总体设计方案</h2><h3 id="指令集合"><a href="#指令集合" class="headerlink" title="指令集合"></a>指令集合</h3><p>课下提交要求实现的指令包括add(u),sub(u),ori,lui,beq,lw,sw,nop,j,jal,jr。在此基础上添加了移位指令sll。分类如下:</p><table><thead><tr><th>R型指令</th><th>I型指令</th><th>J型指令</th></tr></thead><tbody><tr><td>add(u),sub(u),sll(nop),jr</td><td>ori,lui,beq,lw,sw</td><td>j,jal</td></tr></tbody></table><h3 id="模块设计"><a href="#模块设计" class="headerlink" title="模块设计"></a>模块设计</h3><p>从模块划分上看,P4与P3基本相同,包括PC,NPC,IM,GRF,ALU,DM,EXT,CTRL等模块。这些模块分别作为项目下的一个.v文件。为了将这些模块能更清晰地进行实例化,添加了datapath.v和mips.v作为上层模块来进行实例化。</p><h3 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a>整体架构</h3><p><img src="/img/P4/p1.png" alt="整体架构"></p><h2 id="具体模块设计"><a href="#具体模块设计" class="headerlink" title="具体模块设计"></a>具体模块设计</h2><h3 id="PC"><a href="#PC" class="headerlink" title="PC"></a>PC</h3><h4 id="端口说明"><a href="#端口说明" class="headerlink" title="端口说明"></a>端口说明</h4><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>next_pc</td><td>接收来自NPC模块指示的下一条指令地址,并在时钟上升沿更新</td></tr><tr><td>clk</td><td>时钟信号</td></tr><tr><td>reset</td><td>异步复位信号,为1’b1时将当前指令设置为起始位置</td></tr><tr><td>pc</td><td>输出当前pc</td></tr></tbody></table><h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><p>时序逻辑实现对寄存器<code>exe_pc</code>的更新,assign对pc输出端口进行连接。</p><h3 id="NPC"><a href="#NPC" class="headerlink" title="NPC"></a>NPC</h3><h4 id="端口说明-1"><a href="#端口说明-1" class="headerlink" title="端口说明"></a>端口说明</h4><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>pc</td><td>接收当前指向的指令地址</td></tr><tr><td>pc_add_four</td><td>输出当前pc+4的值,便于jal指令跳转时将pc+4的值存入$ra中</td></tr><tr><td>IMM</td><td>接收当前J型指令的26位转移地址</td></tr><tr><td>beq_offset</td><td>接收beq指令中16位偏移量</td></tr><tr><td>RA</td><td>接收读出的jr指令转移至$31指向的地址(仅设计了jr $ra)</td></tr><tr><td>zero</td><td>接收ALU返回的rs rt寄存器值是否相等的信号,用于beq</td></tr><tr><td>NPCOp</td><td>接收CTRL返回的下一个PC所指向的位置类型控制信号</td></tr><tr><td>npc</td><td>向PC传递经NPC模块得到的下一条执行指令的位置</td></tr></tbody></table><p>其中,控制信号NPCOp的功能具体如下:</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>2’b00</td><td>npc为pc+4</td></tr><tr><td>2’b01</td><td>npc为j/jal指向的指令</td></tr><tr><td>2’b10</td><td>npc为$ra指向的指令</td></tr><tr><td>2’b11</td><td>npc为pc+4/beq分支的指令,取决于zero的输入</td></tr></tbody></table><h4 id="实现-1"><a href="#实现-1" class="headerlink" title="实现"></a>实现</h4><p>利用多目运算符和assign语句对端口进行赋值即可。</p><div class="note note-danger"> <p>注意:</p><ol><li>使用某中间变量(不准确的称呼)时,首先应声明为wire型,同时要注意端口位宽的匹配。</li><li>此处pc接收的应未减0x00003000,并且输出npc前减去0x00003000以保持与pc的配合。</li></ol> </div><h3 id="IM"><a href="#IM" class="headerlink" title="IM"></a>IM</h3><p>利用initial块中的<code>$readmemh("code.txt",rom);</code>语句即可实现从当前目录下读取文件并放入指定位置。</p><div class="note note-danger"> <p>注意:<br>在声明寄存器阵列作为IM时,应当从低到高,如:<code>reg [31:0] rom [0:4096];</code>。这样做可以保证ISE和VCS仿真结果是相同的。</p> </div><h3 id="GRF"><a href="#GRF" class="headerlink" title="GRF"></a>GRF</h3><h4 id="端口说明-2"><a href="#端口说明-2" class="headerlink" title="端口说明"></a>端口说明</h4><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>A1</td><td>读取的第一个寄存器编号(rs)</td></tr><tr><td>A2</td><td>读取的第二个寄存器编号(rt)</td></tr><tr><td>WR</td><td>写入数据的目标寄存器(rd/rt/$ra)</td></tr><tr><td>WD</td><td>写入目标寄存器的数据</td></tr><tr><td>RD1</td><td>A1中的数据</td></tr><tr><td>RD2</td><td>A2中的数据</td></tr><tr><td>clk</td><td>时钟信号</td></tr><tr><td>reset</td><td>异步复位信号</td></tr><tr><td>WE</td><td>写入信号</td></tr><tr><td>pc</td><td>当前pc,以便于输出信息</td></tr></tbody></table><h4 id="实现-2"><a href="#实现-2" class="headerlink" title="实现"></a>实现</h4><p>在时序逻辑中实现对内容的更新。<span class="label label-warning">注意不要对零寄存器进行更新,也不要输出信息。在判断条件中应当注意这一点。</span></p><h3 id="ALU"><a href="#ALU" class="headerlink" title="ALU"></a>ALU</h3><h4 id="端口说明-3"><a href="#端口说明-3" class="headerlink" title="端口说明"></a>端口说明</h4><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>A</td><td>进行运算的第一个数</td></tr><tr><td>B</td><td>进行运算的第二个数</td></tr><tr><td>sll</td><td>进行sll操作需要左移的位数</td></tr><tr><td>ALUOp</td><td>控制信号,选择ALU进行的操作</td></tr><tr><td>zero</td><td>输出两个寄存器中的值是否相等,为beq指令时的NPC提供是否分支的依据</td></tr><tr><td>ans</td><td>输出运算结果</td></tr></tbody></table><p>其中,控制信号ALUOp的功能具体如下:</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>3’b000</td><td>A+B</td></tr><tr><td>3’b001</td><td>A-B</td></tr><tr><td>3’b010</td><td>A</td></tr><tr><td>3’b011</td><td>B<<sll</td></tr><tr><td>3’b100</td><td>B<<16</td></tr></tbody></table><h4 id="实现电路"><a href="#实现电路" class="headerlink" title="实现电路"></a>实现电路</h4><p>同样使用assign和多目运算符配合实现。Verilog中默认为无符号数,所以这里不需要考虑符号带来的问题。<span class="label label-info">Verilog中4’b0认为是无符号数,但是0认为是有符号数。</span></p><h3 id="DM"><a href="#DM" class="headerlink" title="DM"></a>DM</h3><p>和IM几乎相同。<span class="label label-danger">P3中我的DMOp非常别扭,DM出的问题(只接了ld没有接str)也导致了P3课上挂掉:sob:,P4中对DMOp对应功能进行了更改,使其不再那么别扭。</span></p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>1’b0</td><td>读出数据</td></tr><tr><td>1’b1</td><td>写入数据</td></tr></tbody></table><h3 id="EXT"><a href="#EXT" class="headerlink" title="EXT"></a>EXT</h3><p>选择0扩展或符号扩展,一条简单的assign语句。</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>1’b0</td><td>0扩展</td></tr><tr><td>1’b1</td><td>符号扩展</td></tr></tbody></table><h3 id="CTRL"><a href="#CTRL" class="headerlink" title="CTRL"></a>CTRL</h3><h4 id="端口说明-4"><a href="#端口说明-4" class="headerlink" title="端口说明"></a>端口说明</h4><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>OpCode</td><td>获取指令的操作码</td></tr><tr><td>FuncCode</td><td>获取指令的功能码</td></tr><tr><td>NPCOp</td><td>模块控制信号,控制NPC操作</td></tr><tr><td>ALUOp</td><td>模块控制信号,控制ALU操作</td></tr><tr><td>WE</td><td>模块控制信号,控制GRF读写</td></tr><tr><td>DMOp</td><td>模块控制信号,控制DM读写</td></tr><tr><td>WRSlt</td><td>数据通路控制信号,控制写入的寄存器来源</td></tr><tr><td>WDSlt</td><td>数据通路控制信号,控制写入的数据来源</td></tr><tr><td>ALUSlt</td><td>数据通路控制信号,控制进入ALU的第二个数来源</td></tr><tr><td>EXTOp</td><td>模块控制信号,控制EXT扩展方式</td></tr></tbody></table><h4 id="实现-3"><a href="#实现-3" class="headerlink" title="实现"></a>实现</h4><p>和P3中的设计思路一样,分为与逻辑(识别)和或逻辑(赋值)。这里要比Logisim中简单的多。</p><h2 id="数据通路分析"><a href="#数据通路分析" class="headerlink" title="数据通路分析"></a>数据通路分析</h2><p>和P3的设计一样,下面再次给出:<br> </p><p>WRSlt对应的选择器选择进行写入操作的寄存器编号:</p><table><thead><tr><th>WRSlt</th><th>来源</th></tr></thead><tbody><tr><td>2’b00</td><td>rd</td></tr><tr><td>2’b01</td><td>rt</td></tr><tr><td>2’b10</td><td>$ra</td></tr></tbody></table><p>WDSlt对应的选择器选择写入寄存器的数据:</p><table><thead><tr><th>WDSlt</th><th>来源</th></tr></thead><tbody><tr><td>2’b00</td><td>pc+4</td></tr><tr><td>2’b01</td><td>ALU计算得到的ans</td></tr><tr><td>2’b10</td><td>DM读取的数据</td></tr></tbody></table><p>ALUSlt对应的选择器选择进入ALU模块的第二个数:</p><table><thead><tr><th>ALUSlt</th><th>来源</th></tr></thead><tbody><tr><td>1’b0</td><td>RD2</td></tr><tr><td>1’b1</td><td>EXT扩展后的结果</td></tr></tbody></table><p>综上,给出每条指令对应的各个控制信号:</p><table><thead><tr><th>指令</th><th>NPCOp</th><th>WE</th><th>ALUOp</th><th>DMOp</th><th>WRSlt</th><th>WDSlt</th><th>ALUSlt</th><th>EXTOp</th></tr></thead><tbody><tr><td>add(u)</td><td>2’b00</td><td>1’b1</td><td>3’b000</td><td>1’b0</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>sub(u)</td><td>2’b00</td><td>1’b1</td><td>3’b001</td><td>1’b0</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>sll</td><td>2’b00</td><td>1’b1</td><td>3’b011</td><td>1’b0</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>ori</td><td>2’b00</td><td>1’b1</td><td>3’b010</td><td>1’b0</td><td>2’b01</td><td>2’b01</td><td>1’b1</td><td>1’b0</td></tr><tr><td>lui</td><td>2’b00</td><td>1’b1</td><td>3’b100</td><td>1’b0</td><td>2’b01</td><td>2’b01</td><td>1’b1</td><td>1’b0</td></tr><tr><td>beq</td><td>2’b11</td><td>1’b0</td><td>3’b001</td><td>1’b0</td><td>2’b00</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>lw</td><td>2’b00</td><td>1’b1</td><td>3’b000</td><td>1’b0</td><td>2’b01</td><td>2’b10</td><td>1’b1</td><td>1’b1</td></tr><tr><td>sw</td><td>2’b00</td><td>1’b0</td><td>3’b000</td><td>1’b1</td><td>2’b00</td><td>2’b00</td><td>1’b1</td><td>1’b1</td></tr><tr><td>j</td><td>2’b01</td><td>1’b0</td><td>3’b000</td><td>1’b0</td><td>2’b10</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>jal</td><td>2’b01</td><td>1’b1</td><td>3’b000</td><td>1’b0</td><td>2’b00</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>jr</td><td>2’b10</td><td>1’b0</td><td>3’b000</td><td>1’b0</td><td>2’b10</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr></tbody></table><h3 id="datapath和mips"><a href="#datapath和mips" class="headerlink" title="datapath和mips"></a>datapath和mips</h3><p>实例化以上模块即可,注意位宽的匹配和实例化过程中变量的对应。</p><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><ol><li><p>阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?</p><p><img src="/img/P4/p2.png" alt="示例"></p></li></ol> <div class="note note-info"> <p>答:addr信号来自于ALU模块,位数是[11:2]因为DM中按字存储,每个字有4个字节,所以左移两位才对应着DM中储存的字的位置。</p> </div><ol start="2"><li><p>思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣。</p> <div class="note note-info"> <p>答:<br><strong>指令对应的控制信号如何取值</strong>: </p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs verilog"><span class="hljs-keyword">always</span>@(*) <span class="hljs-keyword">begin</span> <br> <span class="hljs-keyword">case</span>(OpCode)<br> <span class="hljs-number">6</span>’b101011: <span class="hljs-keyword">begin</span><br> DMOp=<span class="hljs-number">1</span>’b1<br> ...<br> <span class="hljs-keyword">end</span><br> ...<br> <span class="hljs-keyword">endcase</span><br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure><p><strong>控制信号每种取值所对应的指令</strong>: </p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs verilog"><span class="hljs-keyword">assign</span> add=(OpCode==<span class="hljs-number">6'b000000</span>&&FuncCode==<span class="hljs-number">6'b100000</span>)?<span class="hljs-number">1</span>:<span class="hljs-number">0</span>;<br> <span class="hljs-keyword">assign</span> NPCOp=(add|sub|sll|ori|lui|lw|sw)?<span class="hljs-number">2'b00</span>:<br> (j|jal)?<span class="hljs-number">2'b01</span>:<br> (jr)?<span class="hljs-number">2'b10</span>:<br> <span class="hljs-number">2'b11</span>;<br></code></pre></td></tr></table></figure><p>记录<strong>指令对应的控制信号如何取值</strong>便于观察每条指令的运作,与其他指令可以独立开来,添加指令时更方便。<br>记录<strong>控制信号每种取值所对应的指令</strong>便于对控制信号进行观察,检查控制信号是否按照预期的设计输出。</p> </div></li><li><p>在相应的部件中,复位信号的设计都是<strong>同步复位</strong>,这与 P3 中的设计要求不同。请对比<strong>同步复位</strong>与<strong>异步复位</strong>这两种方式的 reset 信号与 clk 信号优先级的关系。</p> <div class="note note-info"> <p>答:<strong>同步复位</strong>中reset信号优先级低于clk信号;<br><strong>异步复位</strong>中reset信号优先级高于clk信号。</p> </div></li><li><p>C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的。提示:阅读《MIPS32® Architecture For Programmers Volume II: The MIPS32® Instruction Set》中相关指令的 Operation 部分。</p> <div class="note note-info"> <p>答:add和addi都会对溢出进行检查,如果溢出,则抛出<code>SignalException(IntegerOverflow)</code>异常,而addu和addiu不进行溢出检查,所以在忽略溢出的前提下,两种指令是等价的。</p> </div></li></ol><h2 id="测试方案"><a href="#测试方案" class="headerlink" title="测试方案"></a>测试方案</h2><p>此次自动化测试相对比较简单,原因在于ISE可以通过命令行操作编译项目后直接获得控制台输出,同时在模块设计过程中已实现了向控制台的输出。我们只需要捕获这些输出并且与编译后的C程序输出进行对拍即可。</p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Verilog</tag>
<tag>单周期CPU</tag>
<tag>P4课下</tag>
</tags>
</entry>
<entry>
<title>【CO】P3课下——单周期CPU(Logisim实现)</title>
<link href="/2024/10/24/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B--%E5%8D%95%E5%91%A8%E6%9C%9FCPU(Logisim%E5%AE%9E%E7%8E%B0)/"/>
<url>/2024/10/24/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B--%E5%8D%95%E5%91%A8%E6%9C%9FCPU(Logisim%E5%AE%9E%E7%8E%B0)/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <p>本文为P3课下单周期CPU设计思路与具体细节,仅供参考</p> </div><h1 id="P3课下–单周期CPU的Logisim实现"><a href="#P3课下–单周期CPU的Logisim实现" class="headerlink" title="P3课下–单周期CPU的Logisim实现"></a>P3课下–单周期CPU的Logisim实现</h1><h2 id="总体设计方案"><a href="#总体设计方案" class="headerlink" title="总体设计方案"></a>总体设计方案</h2><h3 id="指令集合"><a href="#指令集合" class="headerlink" title="指令集合"></a>指令集合</h3><p>课下提交要求实现的指令包括add(u),sub(u),ori,lui,beq,lw,sw,nop,具体实现过程中在此基础上又添加了j,jal,jr等跳转指令和移位指令sll。根据机器码类别将其分类:</p><table><thead><tr><th>R型指令</th><th>I型指令</th><th>J型指令</th></tr></thead><tbody><tr><td>add(u),sub(u),sll(nop),jr</td><td>ori,lui,beq,lw,sw</td><td>j,jal</td></tr></tbody></table><h3 id="模块设计"><a href="#模块设计" class="headerlink" title="模块设计"></a>模块设计</h3><p>根据功能划分需要设计以下模块:</p><ul><li>取指令——IFU,包括PC(程序计数器),NPC(Next PC),IM(指令存储器)。其中要求PC用寄存器实现并且具有异步复位功能,起始地址为<strong>0x00003000</strong>。地址范围要求为0x00003000~0x00006FFF。<div class="note note-info"> <p>由地址范围可知,地址宽度为0x00004000,即为2^14,但每条指令大小为32bit,每个地址对应的容量为1个字节(8bit),因此相邻两条指令的地址差异为4,我们引入指令字的概念,利用ROM作为IM,每个位置储存一条指令,那么我们仅需要2^12个指令字,即12位ROM便可以满足我们的需要。</p> </div></li><li>寄存器读写——GRF,由32个寄存器组成的寄存器堆,具有异步复位功能</li><li>内存读写——DM,数据存储器,可以理解为内存,要求使用RAM实现,具有异步复位功能,地址范围为0x00000000~0x00002FFF。<div class="note note-danger"> <p>地址宽度为0x00003000,即为3*2^12,根据和指令字相同的原理,12位RAM即可满足我们的需要。注意!这样做我们仅能以字为单位对内存进行操作。但是转念再去想,如果不指定内存的对齐方式,如何去存储都会产生问题:按字对齐那么对于半字、字节操作很麻烦,按字节对齐那么会多占用两位地址,同时如果储存字或半字需要对多个地址的内存进行操作。因此仍然选择按字存储,选择地址位数为12,数据位数为32的RAM作为DM。</p> </div></li><li>数学运算——ALU,算数逻辑单元,实现的运算包括加、减、或、逻辑左移、lui(高位存储)。</li><li>位数扩展——EXT,位扩展单元,根据指令的不同进行0扩展(ori,lui)或符号扩展(beq,lw,sw)</li><li>控制器——CTRL,根据输入指令的操作码和功能码输出各个模块和数据通路的控制信号。</li></ul><h3 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a>整体架构</h3><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p1.png" alt="整体架构"></p><h2 id="具体模块设计"><a href="#具体模块设计" class="headerlink" title="具体模块设计"></a>具体模块设计</h2><h3 id="PC"><a href="#PC" class="headerlink" title="PC"></a>PC</h3><h4 id="端口说明"><a href="#端口说明" class="headerlink" title="端口说明"></a>端口说明</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p2.png" alt="示意图"></p><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>next_pc</td><td>接收来自NPC模块指示的下一条指令地址,并在时钟上升沿更新</td></tr><tr><td>clk</td><td>时钟信号</td></tr><tr><td>reset</td><td>异步复位信号,为1’b1时将当前指令设置为起始位置</td></tr></tbody></table><h4 id="实现电路"><a href="#实现电路" class="headerlink" title="实现电路"></a>实现电路</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p3.png" alt="电路图"><br><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p4.png" alt="顶层接入"><br>输出的pc为符合题目要求的指令地址值,next_pc返回的是未经+0x00003000的地址位置,这样设计是为了能利用寄存器自带的异步复位功能,这里对0x00003000加加减减纯属是为了满足题目输出pc地址范围的要求。</p><h3 id="NPC"><a href="#NPC" class="headerlink" title="NPC"></a>NPC</h3><h4 id="端口说明-1"><a href="#端口说明-1" class="headerlink" title="端口说明"></a>端口说明</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p5.png" alt="示意图"></p><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>pc</td><td>接收当前指向的指令地址</td></tr><tr><td>pc+4</td><td>输出当前pc+4的值,便于jal指令跳转时将pc+4的值存入$ra中</td></tr><tr><td>IMM</td><td>接收当前J型指令的26位转移地址</td></tr><tr><td>beq_offset</td><td>接收beq指令中16位偏移量</td></tr><tr><td>RA</td><td>接收读出的jr指令转移至$31指向的地址(仅设计了jr $ra)</td></tr><tr><td>zero</td><td>接收ALU返回的rs rt寄存器值是否相等的信号,用于beq</td></tr><tr><td>NPCOp</td><td>接收CTRL返回的下一个PC所指向的位置类型控制信号</td></tr><tr><td>npc</td><td>向PC传递经NPC模块得到的下一条执行指令的位置</td></tr></tbody></table><p>其中,控制信号NPCOp的功能具体如下:</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>2’b00</td><td>npc为pc+4</td></tr><tr><td>2’b01</td><td>npc为j/jal指向的指令</td></tr><tr><td>2’b10</td><td>npc为$ra指向的指令</td></tr><tr><td>2’b11</td><td>npc为pc+4/beq分支的指令,取决于zero的输入</td></tr></tbody></table><h4 id="实现电路-1"><a href="#实现电路-1" class="headerlink" title="实现电路"></a>实现电路</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p6.png" alt="电路图"></p><div class="note note-danger"> <p>注意:此处pc接收的应未减0x00003000,并且输出npc前减去0x00003000以保持与pc的配合</p> </div><h3 id="IM"><a href="#IM" class="headerlink" title="IM"></a>IM</h3><p>结构相对简单,输入为希望读取的地址,输出为读到的指令机器码。</p><h3 id="GRF"><a href="#GRF" class="headerlink" title="GRF"></a>GRF</h3><h4 id="端口说明-2"><a href="#端口说明-2" class="headerlink" title="端口说明"></a>端口说明</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p7.png" alt="示意图"></p><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>A1</td><td>读取的第一个寄存器编号(rs)</td></tr><tr><td>A2</td><td>读取的第二个寄存器编号(rt)</td></tr><tr><td>WR</td><td>写入数据的目标寄存器(rd/rt/$ra)</td></tr><tr><td>WD</td><td>写入目标寄存器的数据</td></tr><tr><td>RD1</td><td>A1中的数据</td></tr><tr><td>RD2</td><td>A2中的数据</td></tr><tr><td>clk</td><td>时钟信号</td></tr><tr><td>reset</td><td>异步复位信号</td></tr><tr><td>WE</td><td>写入信号</td></tr></tbody></table><h4 id="实现电路-2"><a href="#实现电路-2" class="headerlink" title="实现电路"></a>实现电路</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p8.png" alt="电路图"><br>实现思路很简单,注意对应关系不要混乱即可,推荐自动化对.circ文件内容进行修改。</p><h3 id="ALU"><a href="#ALU" class="headerlink" title="ALU"></a>ALU</h3><h4 id="端口说明-3"><a href="#端口说明-3" class="headerlink" title="端口说明"></a>端口说明</h4><p> <img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p9.png" alt="示意图"></p><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>A</td><td>进行运算的第一个数</td></tr><tr><td>B</td><td>进行运算的第二个数</td></tr><tr><td>sll</td><td>进行sll操作需要左移的位数</td></tr><tr><td>ALUOp</td><td>控制信号,选择ALU进行的操作</td></tr><tr><td>zero</td><td>输出两个寄存器中的值是否相等,为beq指令时的NPC提供是否分支的依据</td></tr><tr><td>ans</td><td>输出运算结果</td></tr></tbody></table><p>其中,控制信号ALUOp的功能具体如下:</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>3’b000</td><td>A+B</td></tr><tr><td>3’b001</td><td>A-B</td></tr><tr><td>3’b010</td><td>A</td></tr><tr><td>3’b011</td><td>B<<sll</td></tr><tr><td>3’b100</td><td>B<<16</td></tr></tbody></table><h4 id="实现电路-3"><a href="#实现电路-3" class="headerlink" title="实现电路"></a>实现电路</h4><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p10.png" alt="电路图"><br>利用Logisim自带的元件进行算术操作,实现较为简单。ALU关键在于数据通路,在下面分析。</p><h3 id="DM"><a href="#DM" class="headerlink" title="DM"></a>DM</h3><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p11.png" alt="电路图"><br>想法也较为简单,使用之前提到的RAM即可,其中DMOp作为读写控制信号,其功能具体如下:</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>1’b0</td><td>写入数据</td></tr><tr><td>1’b1</td><td>读出数据</td></tr></tbody></table><h3 id="EXT"><a href="#EXT" class="headerlink" title="EXT"></a>EXT</h3><p><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p12.png" alt="电路图"><br>选择0扩展或符号扩展</p><table><thead><tr><th>信号</th><th>功能</th></tr></thead><tbody><tr><td>1’b0</td><td>0扩展</td></tr><tr><td>1’b1</td><td>符号扩展</td></tr></tbody></table><h3 id="CTRL"><a href="#CTRL" class="headerlink" title="CTRL"></a>CTRL</h3><h4 id="端口说明-4"><a href="#端口说明-4" class="headerlink" title="端口说明"></a>端口说明</h4><p> <img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p13.png" alt="示意图"></p><table><thead><tr><th>端口</th><th>功能</th></tr></thead><tbody><tr><td>OpCode</td><td>获取指令的操作码</td></tr><tr><td>FuncCode</td><td>获取指令的功能码</td></tr><tr><td>NPCOp</td><td>模块控制信号,控制NPC操作</td></tr><tr><td>ALUOp</td><td>模块控制信号,控制ALU操作</td></tr><tr><td>WE</td><td>模块控制信号,控制GRF读写</td></tr><tr><td>DMOp</td><td>模块控制信号,控制DM读写</td></tr><tr><td>WRSlt</td><td>数据通路控制信号,控制写入的寄存器来源</td></tr><tr><td>WDSlt</td><td>数据通路控制信号,控制写入的数据来源</td></tr><tr><td>ALUSlt</td><td>数据通路控制信号,控制进入ALU的第二个数来源</td></tr><tr><td>EXTOp</td><td>模块控制信号,控制EXT扩展方式</td></tr></tbody></table><p>CTRL输出的控制信号可以分为模块控制信号和数据通路控制信号,模块控制信号使模块进行不同的操作,数据通路控制信号控制进入模块的数据来源。根据指令码和操作码的不同,CTRL输出不同的控制信号。具体的指令和控制信号的控制关系我们在数据通路分析部分给出。</p><h4 id="实现电路-4"><a href="#实现电路-4" class="headerlink" title="实现电路"></a>实现电路</h4><p>实现电路可分为两部分:与逻辑和或逻辑,与逻辑负责识别指令,或逻辑负责提供指令对应的控制信号,将这两部分分开有助于添加指令,在连线时可以先把对应的操作码和功能码输入,然后根据颜色与门电路连接,可以减小犯错误的概率。<br><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p14.png" alt="与逻辑"><br><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p15.png" alt="或逻辑"></p><h2 id="数据通路分析"><a href="#数据通路分析" class="headerlink" title="数据通路分析"></a>数据通路分析</h2><p>数据通路控制着每条指令从读入到执行数据在各个模块间的流动轨迹,根据不同的指令控制数据的流向是实现指令的关键,利用多路选择器和其选择信号对数据的流向进行控制。<br>在电路设计中,我在三处设置了选择器。<br><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p16.png" alt="数据通路"><br>WRSlt对应的选择器选择进行写入操作的寄存器编号:</p><table><thead><tr><th>WRSlt</th><th>来源</th></tr></thead><tbody><tr><td>2’b00</td><td>rd</td></tr><tr><td>2’b01</td><td>rt</td></tr><tr><td>2’b10</td><td>$ra</td></tr></tbody></table><p>WDSlt对应的选择器选择写入寄存器的数据:</p><table><thead><tr><th>WDSlt</th><th>来源</th></tr></thead><tbody><tr><td>2’b00</td><td>pc+4</td></tr><tr><td>2’b01</td><td>ALU计算得到的ans</td></tr><tr><td>2’b10</td><td>DM读取的数据</td></tr></tbody></table><p>ALUSlt对应的选择器选择进入ALU模块的第二个数:</p><table><thead><tr><th>ALUSlt</th><th>来源</th></tr></thead><tbody><tr><td>1’b0</td><td>RD2</td></tr><tr><td>1’b1</td><td>EXT扩展后的结果</td></tr></tbody></table><p>综上,我们给出每条指令对应的各个控制信号,这也是CTRL连线的重要依据:</p><table><thead><tr><th>指令</th><th>NPCOp</th><th>WE</th><th>ALUOp</th><th>DMOp</th><th>WRSlt</th><th>WDSlt</th><th>ALUSlt</th><th>EXTOp</th></tr></thead><tbody><tr><td>add(u)</td><td>2’b00</td><td>1’b1</td><td>3’b000</td><td>1’b1</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>sub(u)</td><td>2’b00</td><td>1’b1</td><td>3’b001</td><td>1’b1</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>sll</td><td>2’b00</td><td>1’b1</td><td>3’b011</td><td>1’b1</td><td>2’b00</td><td>2’b01</td><td>1’b0</td><td>1’b0</td></tr><tr><td>ori</td><td>2’b00</td><td>1’b1</td><td>3’b010</td><td>1’b1</td><td>2’b01</td><td>2’b01</td><td>1’b1</td><td>1’b0</td></tr><tr><td>lui</td><td>2’b00</td><td>1’b1</td><td>3’b100</td><td>1’b1</td><td>2’b01</td><td>2’b01</td><td>1’b1</td><td>1’b0</td></tr><tr><td>beq</td><td>2’b11</td><td>1’b0</td><td>3’b001</td><td>1’b1</td><td>2’b00</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>lw</td><td>2’b00</td><td>1’b1</td><td>3’b000</td><td>1’b1</td><td>2’b01</td><td>2’b10</td><td>1’b1</td><td>1’b1</td></tr><tr><td>sw</td><td>2’b00</td><td>1’b0</td><td>3’b000</td><td>1’b0</td><td>2’b00</td><td>2’b00</td><td>1’b1</td><td>1’b1</td></tr><tr><td>j</td><td>2’b01</td><td>1’b0</td><td>3’b000</td><td>1’b1</td><td>2’b10</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>jal</td><td>2’b01</td><td>1’b1</td><td>3’b000</td><td>1’b1</td><td>2’b00</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr><tr><td>jr</td><td>2’b10</td><td>1’b0</td><td>3’b000</td><td>1’b1</td><td>2’b10</td><td>2’b00</td><td>1’b0</td><td>1’b0</td></tr></tbody></table><p>有了这张表,我们便可以在CTRL中进行连线为每条指令规划数据通路。当我们添加指令时,按该表的逻辑分析数据通路再进行CTRL中的连线即可。 </p><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><ol><li>上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。 <div class="note note-info"> <p>答: 状态存储功能:PC、GRF、DM<br>状态转移功能:NPC、CTRL、GRF、DM</p> </div></li><li>现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。<div class="note note-info"> <p>答:我认为在理论上是合理的,在实践中有些不合理。IM对使用过程来说只需要只读即可,所以采用一个ROM是合理的。DM和GRF都是读取和写入都需要。DM每次仅进行读或写,所以可以采用一个读写分离的RAM实现,并且每个周期仅进行一次读或写,对读写速度要求不高,所以没必要使用寄存器构建,那样会占用大量的寄存器。GRF读和写可以认为是相互不干预的,并且需要高速读写,所以用寄存器搭建是很好的选择。以上说明了本次作业模块实现器件是合理的,但是仍然可以优化,比如用一个RAM来作为存储器,既存储指令,又存储数据,在实践中可以更节省成本。</p> </div></li><li>在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。<div class="note note-info"> <p>答:设计了NPC(Next Program Counter),PC的指向可以简单分为三种情况:PC+4,分支指向,跳转指向,这三种不同的情况如何转移,如果全在PC模块中实现有些臃肿复杂,并且参考状态机的状态转移,可以将此部分抽离出来作为一个状态转移电路,这是该模块的设计的初衷。具体设计中,根据指令的不同要求,对指令的不同部分进行不同的操作,这些数学操作并不引入到ALU中完成,而是在模块内部完成,一是这些数学操作的结果对于其他非IFU模块来说没有用,无法合并数据通路,导致走线混乱,二是这些数学操作特殊,并不仅是简单的加减乘除,更适合在模块内部单独考虑完成。需要哪些数据,便从外部引入,通过控制信号和内部计算,将下一条指令的地址返回PC即可。</p> </div></li><li>事实上,实现 <code>nop</code> 空指令,我们并不需要将它加入控制信号真值表,为什么?<div class="note note-info"> <p>答:从语义上来看,<code>nop</code>指令不进行任何操作,不会对数据或者状态进行修改,因此保持控制信号的默认状态或者保持上一条指令的状态即可。从机器码上来看,<code>nop</code>即为<code>sll $0,$0,0</code>,而GRF中<code>$zero</code>不会改变,并且该操作的数据通路不会影响其他模块,所以控制信号也就无所谓了。在我的设计中,由于添加了<code>sll</code>指令,<code>nop</code>也就顺带可以作为<code>sll $0,$0,0</code>执行了,但是控制信号是为<code>sll</code>设计的,<code>nop</code>不需要控制信号,自然可以被兼容至设计的控制信号中</p> </div></li><li>阅读 Pre 的 <a href="http://cscore.buaa.edu.cn/tutorial/mips/mips-6/mips6-1/">“MIPS 指令集及汇编语言”</a> 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。 <div class="note note-info"> <p>答:从指令覆盖情况来看,没有涉及sub(u)指令,而这是很容易出现溢出的操作,也是设计的易错点。同时<code>sw</code>和<code>lw</code>指令在涉及偏移量时,仅考虑了正整数,没有测试出现负数的情况,从而无法测试设计时扩展方式的正误。综上,测试数据较弱,仅测试了容易想到的地方,容易出现Bug的地方没有进行相应测试。</p> </div></li></ol><h2 id="测试方案"><a href="#测试方案" class="headerlink" title="测试方案"></a>测试方案</h2><h3 id="测试用例"><a href="#测试用例" class="headerlink" title="测试用例"></a>测试用例</h3><p>给出的测试用例:</p><figure class="highlight mips"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs mips"><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a0</span>, $<span class="hljs-number">0</span>, <span class="hljs-number">123</span> <br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a1</span>, $<span class="hljs-built_in">a0</span>, <span class="hljs-number">456</span> <br><span class="hljs-keyword">lui </span>$<span class="hljs-built_in">a2</span>, <span class="hljs-number">123</span> <span class="hljs-comment"># 符号位为 0 </span><br><span class="hljs-keyword">lui </span>$<span class="hljs-built_in">a3</span>, <span class="hljs-number">0xffff</span> <span class="hljs-comment"># 符号位为 1 </span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a3</span>, $<span class="hljs-built_in">a3</span>, <span class="hljs-number">0xffff</span> <span class="hljs-comment"># $a3 = -1 </span><br><span class="hljs-keyword">add </span>$<span class="hljs-built_in">s0</span>, $<span class="hljs-built_in">a0</span>, $<span class="hljs-built_in">a2</span> <span class="hljs-comment"># 正正 </span><br><span class="hljs-keyword">add </span>$<span class="hljs-built_in">s1</span>, $<span class="hljs-built_in">a0</span>, $<span class="hljs-built_in">a3</span> <span class="hljs-comment"># 正负 </span><br><span class="hljs-keyword">add </span>$<span class="hljs-built_in">s2</span>, $<span class="hljs-built_in">a3</span>, $<span class="hljs-built_in">a3</span> <span class="hljs-comment"># 负负 </span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t0</span>, $<span class="hljs-number">0</span>, <span class="hljs-number">0x0000</span> <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a0</span>, <span class="hljs-number">0</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a1</span>, <span class="hljs-number">4</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a2</span>, <span class="hljs-number">8</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a3</span>, <span class="hljs-number">12</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">s0</span>, <span class="hljs-number">16</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">s1</span>, <span class="hljs-number">20</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">s2</span>, <span class="hljs-number">24</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">lw </span>$<span class="hljs-built_in">a0</span>, <span class="hljs-number">0</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">lw </span>$<span class="hljs-built_in">a1</span>, <span class="hljs-number">12</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a0</span>, <span class="hljs-number">28</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a1</span>, <span class="hljs-number">32</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a0</span>, $<span class="hljs-number">0</span>, <span class="hljs-number">1</span> <br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a1</span>, $<span class="hljs-number">0</span>, <span class="hljs-number">2</span> <br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">a2</span>, $<span class="hljs-number">0</span>, <span class="hljs-number">1</span> <br><span class="hljs-keyword">beq </span>$<span class="hljs-built_in">a0</span>, $<span class="hljs-built_in">a1</span>, loop1 <span class="hljs-comment"># 不相等 </span><br><span class="hljs-keyword">beq </span>$<span class="hljs-built_in">a0</span>, $<span class="hljs-built_in">a2</span>, loop2 <span class="hljs-comment"># 相等</span><br><span class="hljs-symbol">loop1:</span><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a0</span>, <span class="hljs-number">36</span>($<span class="hljs-built_in">t0</span>) <br><span class="hljs-symbol">loop2:</span><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">a1</span>, <span class="hljs-number">40</span>($<span class="hljs-built_in">t0</span>)<br></code></pre></td></tr></table></figure><p>在此基础上添加了<code>sub</code>指令的测试和<code>sw</code>与<code>lw</code>偏移量为负数的指令:</p><figure class="highlight mips"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs mips"><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t0</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">3</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">t1</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">2</span><br><span class="hljs-keyword">sub </span>$<span class="hljs-built_in">t2</span>,$<span class="hljs-built_in">t1</span>,$<span class="hljs-built_in">t0</span><br><span class="hljs-keyword">ori </span>$<span class="hljs-built_in">s1</span>,$<span class="hljs-number">0</span>,<span class="hljs-number">4</span><br><span class="hljs-keyword">sw </span>$<span class="hljs-built_in">t2</span>,-<span class="hljs-number">4</span>($<span class="hljs-built_in">s1</span>)<br><span class="hljs-keyword">lw </span>$<span class="hljs-built_in">t3</span>,-<span class="hljs-number">4</span>($<span class="hljs-built_in">s1</span>)<br></code></pre></td></tr></table></figure><h3 id="自动化测试"><a href="#自动化测试" class="headerlink" title="自动化测试"></a>自动化测试</h3><p>最初的思路是把Mars源码进行修改,使其能输出所需格式的信息。但是在解压缩Jar包并反编译后对源码修改完成(我也不知道是否成功)后,再次打包回Jar包时遇到了麻烦,JDK报错类重复,在编译环节似乎就出现了问题,当时已经是凌晨四点左右了,我的精力已经达到了极限,实在是不想,也无力去寻找解决办法,于是回宿舍睡觉去了。<br>第二天醒来,我打算尝试新的方法,即不利用Mars,自行模拟CPU执行指令,并输出我想要的信息。而最关键的就是内存和寄存器实现。一开始我的想法是直接在Python脚本中完成模拟,但是本人Python水平实在有限,并且C对于指针、内存操作的便利性又让我不舍得放弃。于是打算在脚本中通过命令行编译、执行C程序,执行Logisim并处理其输出,最终将二者输出对比,进行评测。<br>但是实现的过程并不是一帆风顺,在脚本中通过gcc编译C程序的过程中出现了问题,这一步索性手动完成。于是,我的半自动评测机完成了。<br>需要手动完成的部分有:</p><ol><li>在Mars中将16进制文件导出,命名为<code>"tst_hex.txt"</code></li><li>复制一份”tst_hex.txt”取别名并在其第一行添加一行<code>v2.0 raw</code></li><li>将副本导入<code>p3_cpu.circ</code>的ROM中</li><li>执行py脚本</li></ol><p>需要的环境和工具有:</p><ol><li>JDK包</li><li>GCC等C语言编译器</li></ol><p>其中支持的指令包括上面已实现的所有指令。<br>受限于时间,需手动完成的部分实在是无法自动化(22:00前就要提交了,现在是21:15),第一次手写评测机,实在是仓促,也没有经验,让大家见笑了。不过我还是蛮开心自豪的,在这个过程中我也学到了很多。<br>文件结构如下:<br><img src="/img/%E3%80%90CO%E3%80%91P3%E8%AF%BE%E4%B8%8B%E2%80%94%E2%80%94%E5%8D%95%E5%91%A8%E6%9C%9FCPU/p17.png" alt="文件结构"></p><p>模拟CPU执行指令的C程序:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">define</span> _CRT_SECURE_NO_WARNINGS</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdlib.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><string.h></span></span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_REGS 32</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> MEM_SIZE 4096</span><br><br><span class="hljs-type">unsigned</span> <span class="hljs-type">char</span> memory[MEM_SIZE];<br><span class="hljs-type">int</span> registers[NUM_REGS] = { <span class="hljs-number">0</span> };<br><span class="hljs-type">int</span> pc = <span class="hljs-number">0</span>;<br><br><span class="hljs-comment">// 操作指令的枚举</span><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">OPCODES</span> {</span> R = <span class="hljs-number">0</span>, LW = <span class="hljs-number">35</span>, SW = <span class="hljs-number">43</span>, BEQ = <span class="hljs-number">4</span>, J = <span class="hljs-number">2</span>, ORI = <span class="hljs-number">13</span>, LUI = <span class="hljs-number">15</span>, JAL = <span class="hljs-number">3</span> };<br><br><span class="hljs-comment">// 从文件中加载十六进制指令</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">load_instructions</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">char</span>* filename, <span class="hljs-type">unsigned</span> <span class="hljs-type">long</span> <span class="hljs-type">long</span>* instructions, <span class="hljs-type">int</span> max_instructions)</span> {<br> FILE* file = fopen(filename, <span class="hljs-string">"r"</span>);<br> <span class="hljs-keyword">if</span> (!file) {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"file error!"</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br> <span class="hljs-type">char</span> line[<span class="hljs-number">10</span>];<br> <span class="hljs-type">int</span> count = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (fgets(line, <span class="hljs-keyword">sizeof</span>(line), file) && count < max_instructions) {<br> instructions[count++] = strtoull(line, <span class="hljs-literal">NULL</span>, <span class="hljs-number">16</span>);<br> }<br> fclose(file);<br> <span class="hljs-keyword">return</span> count;<br>}<br><br><span class="hljs-comment">// 解码指令</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">decode_instruction</span><span class="hljs-params">(<span class="hljs-type">unsigned</span> <span class="hljs-type">long</span> <span class="hljs-type">long</span> instruction, <span class="hljs-type">int</span>* opcode, <span class="hljs-type">int</span>* rs, <span class="hljs-type">int</span>* rt, <span class="hljs-type">int</span>* rd, <span class="hljs-type">int</span>* shamt, <span class="hljs-type">int</span>* func, <span class="hljs-type">int</span>* imm, <span class="hljs-type">int</span>* addr)</span> {<br> *opcode = (instruction >> <span class="hljs-number">26</span>) & <span class="hljs-number">0x3F</span>;<br> *rs = (instruction >> <span class="hljs-number">21</span>) & <span class="hljs-number">0x1F</span>;<br> *rt = (instruction >> <span class="hljs-number">16</span>) & <span class="hljs-number">0x1F</span>;<br> *rd = (instruction >> <span class="hljs-number">11</span>) & <span class="hljs-number">0x1F</span>;<br> *shamt = (instruction >> <span class="hljs-number">6</span>) & <span class="hljs-number">0x1F</span>;<br> *func = instruction & <span class="hljs-number">0x3F</span>;<br> *imm = instruction & <span class="hljs-number">0xFFFF</span>;<br> *addr = instruction & <span class="hljs-number">0x3FFFFFF</span>;<br>}<br><br><span class="hljs-comment">// 执行指令</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">execute_instruction</span><span class="hljs-params">(<span class="hljs-type">int</span> opcode, <span class="hljs-type">int</span> rs, <span class="hljs-type">int</span> rt, <span class="hljs-type">int</span> rd, <span class="hljs-type">int</span> shamt, <span class="hljs-type">int</span> func, <span class="hljs-type">int</span> imm, <span class="hljs-type">int</span> addr)</span> {<br> <span class="hljs-keyword">switch</span> (opcode) {<br> <span class="hljs-keyword">case</span> R: <span class="hljs-comment">// R型指令</span><br> <span class="hljs-keyword">switch</span> (func) {<br> <span class="hljs-keyword">case</span> <span class="hljs-number">32</span>: <span class="hljs-comment">// add(u)</span><br> registers[rd] = (<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)registers[rs] + (<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)registers[rt];<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rd, registers[rd]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rd, registers[rd]);<br> pc += <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">34</span>: <span class="hljs-comment">// sub(u)</span><br> registers[rd] = (<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)registers[rs] - (<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)registers[rt];<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rd, registers[rd]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rd, registers[rd]);<br> pc += <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>: <span class="hljs-comment">//sll</span><br> registers[rd] = registers[rt] << shamt;<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rd, registers[rd]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rd, registers[rd]);<br> pc += <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">8</span>: <span class="hljs-comment">//jr</span><br> pc = registers[rs];<br> <span class="hljs-keyword">break</span>;<br> }<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> LW: <span class="hljs-comment">// lw</span><br> registers[rt] = *(<span class="hljs-type">int</span>*)&memory[registers[rs] + imm];<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rt, registers[rt]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rt, registers[rt]);<br> pc += <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> SW: <span class="hljs-comment">// sw</span><br> *(<span class="hljs-type">int</span>*)&memory[registers[rs] + imm] = registers[rt];<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : *0x%08x <= %d\n", pc + 0x3000, registers[rs] + imm, registers[rt]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : *0x%08x <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, registers[rs] + imm, registers[rt]);<br> pc += <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> BEQ: <span class="hljs-comment">// beq</span><br> <span class="hljs-keyword">if</span> (registers[rs] == registers[rt]) {<br> pc += <span class="hljs-number">4</span> + (imm << <span class="hljs-number">2</span>);<br> }<br> <span class="hljs-keyword">else</span><br> {<br> pc += <span class="hljs-number">4</span>;<br> }<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> ORI: <span class="hljs-comment">//ori</span><br> registers[rt] = registers[rs] | ((<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)imm);<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rt, registers[rt]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rt, registers[rt]);<br> pc = pc + <span class="hljs-number">4</span>;<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> LUI: <span class="hljs-comment">//lui</span><br> registers[rt] = imm << <span class="hljs-number">16</span>;<br> <span class="hljs-comment">//fprintf(out, "@0x%08x : $%d <= %d\n", pc + 0x3000, rt, registers[rt]);</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"@0x%08x : $%d <= %d\n"</span>, pc + <span class="hljs-number">0x3000</span>, rt, registers[rt]);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> JAL: <span class="hljs-comment">//jal</span><br> registers[<span class="hljs-number">31</span>] = pc + <span class="hljs-number">4</span>;<br> pc = ((pc >> <span class="hljs-number">28</span>) << <span class="hljs-number">28</span>) | (((<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)((addr << <span class="hljs-number">2</span>) << <span class="hljs-number">5</span>)) >> <span class="hljs-number">5</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> J: <span class="hljs-comment">// j</span><br> pc = ((pc >> <span class="hljs-number">28</span>) << <span class="hljs-number">28</span>) | (((<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span>)((addr << <span class="hljs-number">2</span>) << <span class="hljs-number">5</span>)) >> <span class="hljs-number">5</span>);<br> <span class="hljs-keyword">break</span>;<br> }<br>}<br><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">unsigned</span> <span class="hljs-type">long</span> <span class="hljs-type">long</span> instructions[<span class="hljs-number">100</span>];<br> <span class="hljs-type">int</span> num_instructions = load_instructions(<span class="hljs-string">"./tst_hex.txt"</span>, instructions, <span class="hljs-number">100</span>);<br> <span class="hljs-comment">//FILE* out = fopen("tst_out.txt", "w");</span><br> <span class="hljs-keyword">if</span> (num_instructions < <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">777</span>;<br> }<br> pc = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (pc / <span class="hljs-number">4</span> < num_instructions) {<br> <span class="hljs-type">int</span> opcode, rs, rt, rd, shamt, func, imm, addr;<br> decode_instruction(instructions[pc / <span class="hljs-number">4</span>], &opcode, &rs, &rt, &rd, &shamt, &func, &imm, &addr);<br> execute_instruction(opcode, rs, rt, rd, shamt, func, imm, addr);<br> }<br> <span class="hljs-comment">//fclose(out);</span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br></code></pre></td></tr></table></figure><p>python执行命令行操作获得自己的输出并对拍:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> os<br><span class="hljs-keyword">import</span> subprocess<br><span class="hljs-keyword">import</span> difflib<br><br>script_dir = os.path.dirname(os.path.abspath(__file__))<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">execute_c_program</span>(<span class="hljs-params">c_program_path</span>):<br> <span class="hljs-comment"># gcc_path = "C:\\MinGW\\bin\\gcc" # 替换为实际路径</span><br> <span class="hljs-comment"># compile_cmd = f"gcc {c_program_path} -o output_program"</span><br> execute_cmd = os.path.join(script_dir, <span class="hljs-string">"output_program.exe"</span>)<br><br> <span class="hljs-keyword">try</span>:<br> <span class="hljs-comment"># subprocess.run(compile_cmd, shell=True, check=True)</span><br> result = subprocess.run(execute_cmd, shell=<span class="hljs-literal">True</span>, check=<span class="hljs-literal">True</span>, capture_output=<span class="hljs-literal">True</span>, text=<span class="hljs-literal">True</span>, cwd=script_dir)<br> <span class="hljs-keyword">return</span> result.stdout<br> <span class="hljs-keyword">except</span> subprocess.CalledProcessError <span class="hljs-keyword">as</span> e:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">"Error executing C program:"</span>, e)<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">execute_logisim</span>(<span class="hljs-params">logisim_path, circuit_file</span>):<br> cmd = <span class="hljs-string">f"java -jar <span class="hljs-subst">{logisim_path}</span> <span class="hljs-subst">{circuit_file}</span> -tty table"</span> result = subprocess.run(cmd, shell=<span class="hljs-literal">True</span>, capture_output=<span class="hljs-literal">True</span>, text=<span class="hljs-literal">True</span>, cwd=script_dir)<br> <span class="hljs-keyword">return</span> result.stdout<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">format_logisim_output</span>(<span class="hljs-params">output</span>):<br> lines = output.strip().split(<span class="hljs-string">'\n'</span>)<br> formatted_lines = []<br><br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> lines:<br> values = line.split(<span class="hljs-string">'\t'</span>) <span class="hljs-comment"># 根据制表符分隔</span><br> <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(values) > <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> values[<span class="hljs-number">2</span>] == <span class="hljs-string">'1'</span>:<br> hex_1 = <span class="hljs-built_in">hex</span>(<span class="hljs-built_in">int</span>(values[<span class="hljs-number">1</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>))[<span class="hljs-number">2</span>:].zfill(<span class="hljs-number">8</span>)<br> hex_1 = <span class="hljs-string">'0x'</span> + hex_1<br> hex_3 = <span class="hljs-built_in">int</span>(values[<span class="hljs-number">3</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>)<br> hex_4 = <span class="hljs-built_in">int</span>(values[<span class="hljs-number">4</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>)<br> formatted_lines.append(<span class="hljs-string">f"@<span class="hljs-subst">{hex_1}</span> : $<span class="hljs-subst">{hex_3}</span> <= <span class="hljs-subst">{hex_4}</span>"</span>) <span class="hljs-comment"># 自定义格式</span><br> <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(values) > <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> values[<span class="hljs-number">5</span>] == <span class="hljs-string">'1'</span>:<br> hex_1 = <span class="hljs-built_in">hex</span>(<span class="hljs-built_in">int</span>(values[<span class="hljs-number">1</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>))[<span class="hljs-number">2</span>:].zfill(<span class="hljs-number">8</span>)<br> hex_1 = <span class="hljs-string">'0x'</span> + hex_1<br> hex_6 = <span class="hljs-built_in">hex</span>(<span class="hljs-built_in">int</span>(values[<span class="hljs-number">6</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>))[<span class="hljs-number">2</span>:].zfill(<span class="hljs-number">8</span>)<br> hex_6 = <span class="hljs-string">'0x'</span> + hex_6<br> hex_7 = <span class="hljs-built_in">int</span>(values[<span class="hljs-number">7</span>].replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>), <span class="hljs-number">2</span>)<br> formatted_lines.append(<span class="hljs-string">f"@<span class="hljs-subst">{hex_1}</span> : *<span class="hljs-subst">{hex_6}</span> <= <span class="hljs-subst">{hex_7}</span>"</span>) <span class="hljs-comment"># 自定义格式</span><br> <span class="hljs-keyword">return</span> formatted_lines<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">compare_files</span>(<span class="hljs-params">file1, file2</span>):<br> <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(file1, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> f1, <span class="hljs-built_in">open</span>(file2, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> f2:<br> diff = difflib.unified_diff(<br> f1.readlines(),<br> f2.readlines(),<br> fromfile=<span class="hljs-string">'file1'</span>,<br> tofile=<span class="hljs-string">'file2'</span><br> )<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">list</span>(diff)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">count_lines</span>(<span class="hljs-params">file_path</span>):<br> <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(file_path, <span class="hljs-string">'r'</span>, encoding=<span class="hljs-string">'utf-8'</span>) <span class="hljs-keyword">as</span> file:<br> line_count = <span class="hljs-built_in">sum</span>(<span class="hljs-number">1</span> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> file)<br> <span class="hljs-keyword">return</span> line_count<br><br><span class="hljs-comment"># 路径 </span><br>c_program_path = os.path.join(script_dir,<span class="hljs-string">"mips_cpu.c"</span>)<br>logisim_path = os.path.join(script_dir, <span class="hljs-string">"logisim.exe"</span>)<br>circuit_file = os.path.join(script_dir, <span class="hljs-string">"test_p3.circ"</span>)<br>output_log_c = os.path.join(script_dir, <span class="hljs-string">"output_c_program.log"</span>)<br>output_log_logisim = os.path.join(script_dir, <span class="hljs-string">"output_logisim.log"</span>)<br>hex_txt = os.path.join(script_dir, <span class="hljs-string">"tst_hex.txt"</span>)<br><br><span class="hljs-comment"># 运行C程序并记录结果 </span><br>c_output = execute_c_program(c_program_path)<br><span class="hljs-keyword">if</span> c_output:<br> <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(output_log_c, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> f:<br> f.write(c_output)<br><br>p = count_lines(hex_txt)<br><br><span class="hljs-comment"># 运行Logisim并获取输出 </span><br>logisim_output = execute_logisim(logisim_path, circuit_file)<br><br><span class="hljs-comment"># 格式化Logisim输出 </span><br>formatted_output = format_logisim_output(logisim_output)<br><span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(output_log_logisim, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> f:<br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> formatted_output[:p]:<br> f.write(line + <span class="hljs-string">'\n'</span>)<br><br><span class="hljs-comment"># 比较两个输出日志的差异 </span><br>differences = compare_files(output_log_c, output_log_logisim)<br><span class="hljs-keyword">if</span> differences:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">"Differences found between C program output and Logisim output:"</span>)<br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> differences:<br> <span class="hljs-built_in">print</span>(line)<br><span class="hljs-keyword">else</span>:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">"No differences found."</span>)<br></code></pre></td></tr></table></figure><hr><p> <em><strong>完</strong></em></p>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>CO</tag>
<tag>Logisim</tag>
<tag>P3课下</tag>
<tag>单周期CPU</tag>
</tags>
</entry>
<entry>
<title>【Hexo】hexo+fluid博客在github及个人远程服务器的部署</title>
<link href="/2024/08/23/%E3%80%90Hexo%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/"/>
<url>/2024/08/23/%E3%80%90Hexo%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/</url>
<content type="html"><![CDATA[<div class="note note-primary"> <blockquote><p>理论学习和动手实践是个相互纠缠穿插,贯穿始终并最终归于统一的过程</p></blockquote> </div><h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>大一的暑假,也就是2024年的7月底左右,我开始了建立自己博客网站的实践。在此之前,我对相关的知识可以说是一无所知。最初当然是在网上搜素各种教程,了解到了Hexo。但是教程只能解决一部分问题,并且随着相关程序的更新迭代以及不同系统存在的差异,几乎在每个阶段我都遇到了问题。<span class="label label-success">在这里我想感谢2206 kai_Ker学长,浏览学长的博客使我有了建立自己博客网站的想法,在我遇到问题难以解决时,学长很耐心地帮助我,让我对相应知识的理解更加深刻,再次感谢学长!</span>本篇我将把自己的建站过程进行梳理,对于难以解决的问题提供解决方案,希望可以帮到同样想要建站或建站过程中遇到和我相同问题的伙伴。</p><div class="note note-info"> <blockquote><p>相关知识:Hexo,git,Nodejs,Fluid,GitHub Pages,Linux,Ubuntu,nginx</p></blockquote> </div><h1 id="Hexo"><a href="#Hexo" class="headerlink" title="Hexo"></a>Hexo</h1><h2 id="什么是Hexo"><a href="#什么是Hexo" class="headerlink" title="什么是Hexo"></a>什么是Hexo</h2><div class="note note-info"> <blockquote><p>Hexo 是一个快速、简洁且高效的博客框架。 Hexo 使用 <a href="http://daringfireball.net/projects/markdown/">Markdown</a>(或其他标记语言)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。</p></blockquote> </div><p>我们平时在浏览器中看到的网页都是由<code>html</code>这种超文本标记语言编写的,而Hexo可以理解为为我们搭建了一个可以拿过来就用的框架,并将我们用<a href="http://daringfireball.net/projects/markdown/"><code>Markdown</code></a>(Markdown语言可点击链接阅读官方文档自行学习)这种轻量化的,简单的标记语言写成的文章渲染为html代码,呈现为网页填充在已经为我们准备好的框架里。</p><h2 id="运行环境和安装"><a href="#运行环境和安装" class="headerlink" title="运行环境和安装"></a>运行环境和安装</h2><p>Hexo需要配置<a href="https://git-scm.com/"><code>Git</code></a>和<a href="https://nodejs.org/zh-cn"><code>Nodejs</code></a>环境(建议安装最新版本,安装教程均可自行搜索,注意勾选Add to PATH等类似选项,这样可以免去自行配置环境变量等麻烦)。Git是一种分布式的版本管理工具,可以理解为一个公司的技术人员共同开发一个程序,他们所写的代码所做的改动可能都是同时进行的,Git便是解决这种问题的版本管理工具。Nodejs是一种Javascript运行环境,同时Nodejs自带的<code>npm</code>程序包管理工具可以方便地让我们安装各种依赖(可以简单理解为类似应用商店。<br>配置好Git和Nodejs环境后,我们可以在自己的本地硬盘上新建一个hexo目录,如D:/hexo。打开目录,右键选择<code>Git Bash Here</code>即可打开Git命令行界面。<br>检查Git版本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ Git --version<br></code></pre></td></tr></table></figure><p>检查npm版本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm -v<br></code></pre></td></tr></table></figure><p>检查Nodejs版本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ node -v<br></code></pre></td></tr></table></figure><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p1.png" alt="示例"><br>如果成功出现以上界面,则说明环境配置成功。运行以下命令安装hexo:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install -g hexo-cli<br></code></pre></td></tr></table></figure><p>(<code>-g</code>表示全局安装,<code>cli</code>为<code>Command-Line Interface</code>即命令行界面之意)<br>安装完成后输入以下命令检查版本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo -v<br></code></pre></td></tr></table></figure><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p2.png" alt="示例"><br>接下来进行初始化:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo init<br></code></pre></td></tr></table></figure><p>此时我们的文件夹内会出现很多文件<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p3.png" alt="仓库下的文件"><br>输入以下指令进行缓存释放和页面渲染</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo clean&&hexo g<br></code></pre></td></tr></table></figure><div class="note note-warning"> <p>此时我们发现文件夹下多了<span class="label label-info">public</span>文件夹,注意此文件夹为呈现的网页文件的根目录,即一切资源均从<span class="label label-info">public</span>文件夹开始寻找。故建议在<span class="label label-info">source</span>文件夹下建立<span class="label label-info">/img</span>文件夹储存博客文章中的图片,插入图片格式例如:</p><figure class="highlight md"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs md">![<span class="hljs-string">插入图片示例</span>](<span class="hljs-link">/img/example.jpg</span>)<br></code></pre></td></tr></table></figure> </div><p>至此,网页文件已经渲染成功,我们输入以下指令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo s<br></code></pre></td></tr></table></figure><p>即可打开本地服务器,打开<code>http://localhost:4000/</code>即可在本地预览网页内容。</p><h1 id="主题"><a href="#主题" class="headerlink" title="主题"></a>主题</h1><div class="note note-info"> <p>Hexo框架有很多可供选择的主题,选择自己喜欢的主题,参考相应用户使用文档完成配置即可。以下以Fluid主题为例。</p> </div><p>在Git命令行界面输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install --save hexo-theme-fluid<br></code></pre></td></tr></table></figure><p>之后创建文件_config.fluid.yml,将主题的[<code>_config.yml</code>](<a href="https://github.com/fluid-dev/hexo-theme-fluid/blob/master/_config.yml">hexo-theme-fluid/_config.yml at master · fluid-dev/hexo-theme-fluid (github.com)</a>)文件内容复制进去即可。完成后如下修改目录下的_config.yml文件:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">theme:</span> <span class="hljs-string">fluid</span><br><span class="hljs-attr">language:</span> <span class="hljs-string">zh-CN</span><br></code></pre></td></tr></table></figure><div class="note note-danger"> <p>注意<code>.yml</code>文件缩进,冒号后应有一空格</p> </div><p>修改完成后创建<span class="label label-info">关于页</span></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo new page about<br></code></pre></td></tr></table></figure><p>创建后修改<code>/source/about/index.md</code>,添加<code>layout</code>属性:</p><figure class="highlight md"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs md">---<br>title: 标题<br>date: 2024-xx-xx xx:xx:xx<br><span class="hljs-section">layout: about</span><br><span class="hljs-section">---</span><br><br>正文<br></code></pre></td></tr></table></figure><div class="note note-warning"> <p>注意:不设置关于页的layout属性会无法显示布局</p> </div><div class="note note-info"> <p>主题的自定义配置可参考<a href="https://hexo.fluid-dev.com/docs/">Hexo Fluid 用户手册 (fluid-dev.com)</a></p> </div><hr><blockquote><p><strong>至此我们完成了hexo的本地配置和主题美化,下面我们介绍如何将本地文件推送至Github Pages和远程服务器</strong></p></blockquote><hr><div class="note note-primary"> <p>为了让别人能看到我们的博客,我们需要有自己的站点。Github Pages和云服务器都是为了解决我们的这一需求。可以认为Github Pages为我们免费提供了一个站点,我们可以将自己的静态网页托管到Github,其他用户通过Github为我们提供的指定的<code>username.github.io</code>来访问我们的静态网页。当然,我们也可以购买自己的云服务器,通过Nginx等方式反向代理来呈现我们的网页。相比于Github Pages,通过国内云服务器的访问连接的稳定性和速度都会更好,当然在操作和配置上可能也会稍微麻烦一点。</p> </div><h1 id="Github-Pages"><a href="#Github-Pages" class="headerlink" title="Github Pages"></a>Github Pages</h1><p>首先我们需要一个Github账户,自行注册即可。下文涉及的<code>username</code>即为注册时的用户名。(<span class="label label-warning">国内网络访问Github连接稳定性随缘,包括前面的步骤hexo init等操作,需要在Github上将仓库资源Pull到本地,都可能因为网络问题导致失败,可以多尝试几次,也可以尝试代理方法...</span>)</p><h2 id="本地连接Github"><a href="#本地连接Github" class="headerlink" title="本地连接Github"></a>本地连接Github</h2><p>注册账户之后,我们怎么将本地电脑与Github相连接呢?这里我们采用<span class="label label-info">SSH连接</span>的方式。通俗来讲,我们有锁(公钥)和对应的钥匙(私钥)。我们可以把锁(公钥)交给别人,这是可以公开的,但是钥匙(私钥)必须保密,只能掌握在我们自己手里。当我们手中的钥匙和别人手中的锁可以匹配时,即可连接成功。<span class="label label-warning">注意:同一公钥可以重复使用,相当于我们的钥匙可以开很多纹路相同的锁,只要私钥不被泄露,是没有问题的</span></p><h3 id="配置用户名和邮箱"><a href="#配置用户名和邮箱" class="headerlink" title="配置用户名和邮箱"></a>配置用户名和邮箱</h3><p>这里我们先为Git配置用户名和邮箱,是对Git的操作。打开Git命令行输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-variable">$git</span> config --global user.name <span class="hljs-string">"username"</span> <span class="hljs-comment"># 这里username输入自己的用户名,不必须但建议与自己的Github用户名保持一致</span><br><span class="hljs-variable">$git</span> config --global user.email <span class="hljs-string">"user@example.com"</span> <span class="hljs-comment"># 这里user@example.com输入自己的邮箱,同上</span><br></code></pre></td></tr></table></figure><h3 id="生成密钥"><a href="#生成密钥" class="headerlink" title="生成密钥"></a>生成密钥</h3><p>我们通过<code>win</code>+<code>R</code>,输入<code>cmd</code>打开命令行(区别于Git Bash Here打开的Git命令行,称为命令行),输入以下命令产生公钥和私钥:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">ssh-keygen -t rsa -C <span class="hljs-string">"Github邮箱地址"</span><br></code></pre></td></tr></table></figure><blockquote><p>这里<code>-C “user@example.com”</code>为注释(Comment),添加在私钥文件中,可以辅助Github更好地识别私钥,可以省略。</p></blockquote><p>出现的选项均回车即可(选择密钥位置,私钥使用密码,确认私钥使用密码,均回车后密钥保存在默认位置,使用私钥不需要密码)。然后我们打开<code>C:\Users\用户名\.ssh</code>目录,勾选“显示隐藏项目”后,出现:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p4.png" alt=".ssh目录"><br>记事本打开<code>id_rsa.pub</code>文件后复制文件内容</p><h3 id="添加密钥"><a href="#添加密钥" class="headerlink" title="添加密钥"></a>添加密钥</h3><p>进入Github主页,点击Settings</p><blockquote><p>题外话,我在预载《黑神话:悟空》的时候,忘记关闭代理,导致我的代理流量跑光,所以我在写这篇博客的时候也遇到了Github难以进入的问题,所以我选择用当初为了加速Steam安装的加速器Watt Toolkit(Steam++)加速Github,我觉得可以算是羊毛出在羊身上了。</p></blockquote><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p5.png" alt="Github"><br>点击SSH and GPG keys<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p6.png" alt="Github"><br>点击New SSH key<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p7.png" alt="Github"><br>Title随便取,Key type选择Authentication Key,Key中内容粘贴之前复制的id_rsa.pub文件内容即可。<br>保存后在命令行输入:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">ssh -T git@github.com<br></code></pre></td></tr></table></figure><p>输入后出现“Are you sure you want to continue connecting”,输入“yes”即可。连接成功后出现以下提示:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p8.png" alt="连接成功"></p><h2 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h2><p>点击Repository,点击新建仓库<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p9.png" alt="Repository"><br>新建仓库界面注意:</p><ul><li>Repository Name必须是<code>用户名.github.io</code>,这是Github为我们准备好的特殊仓库名称。</li><li>仓库类型选择<code>Public</code></li><li>勾选<code>Add a README file</code><br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p10.png" alt="Repository"><div class="note note-warning"> <p>处于文明及历史原因,Github新建仓库默认分支现名为<code>main</code>,替代之前的<code>master</code>。</p> </div></li></ul><h2 id="静态网站文件推送至Github远程仓库"><a href="#静态网站文件推送至Github远程仓库" class="headerlink" title="静态网站文件推送至Github远程仓库"></a>静态网站文件推送至Github远程仓库</h2><p>我们首先安装hexo部署插件,在hexo目录下打开Git命令行,输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install hexo-deployer-git --save<br></code></pre></td></tr></table></figure><p>打开hexo目录下的<code>_config.yml</code>文件,修改配置:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p11.png" alt="deploy配置"></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br><span class="hljs-attr">repo:</span> <span class="hljs-string">git@github.com:username/username.github.io.git</span><br><span class="hljs-attr">branch:</span> <span class="hljs-string">main</span><br></code></pre></td></tr></table></figure><p>其中<code>type</code>为配置类型,<code>repo</code>为仓库位置,这里我们已经通过SSH连接,<code>branch</code>为提交分支,我们选择Github默认的<code>main</code>分支。<br>配置完成后保存退出,在hexo目录打开Git命令行,输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo clean<br>$ hexo g<br>$ hexo d<br></code></pre></td></tr></table></figure><p>打开网址<code>username.github.io</code>,刷新一下即可看到博客网页。</p><hr><blockquote><p><strong>至此我们已可以通过<code>username.github.io</code>访问我们的博客网页,目前仍然是0成本。下面介绍的在远程云服务器上部署博客网页需要购买云服务器和域名,并且需要进行Linux系统上的命令行操作。</strong></p></blockquote><hr><div class="note note-primary"> <p>我购买的是阿里云ECS服务器最基础的2核2G版本,装载的Linux发行版系统镜像为<code>Ubuntu 22.04</code>版本,以下教程适合于同样搭载<code>Ubuntu</code>或<code>Debian</code>的服务器,因为Debian和基于Debian的Ubuntu搭载的软件包管理工具为<code>apt</code>,而CentOS等其他发行版本搭载的软件包管理工具为<code>yum</code>。所以安装Ubuntu/Debian的uu在查阅其他教程时会发现无法执行<code>sudo yum install -y nginx</code>命令的情况,不建议因此在Ubuntu上安装yum,这就像在安卓手机上安装苹果App Store,将命令改为<code>sudo apt-get install -y nginx</code>即可。在服务器上创建实例时,添加安全组时,由于我们需要通过SSH方式与本地连接,因此需要勾选<code>SSH(22)</code>端口,同时为了保证使用HTTP和HTTPS协议能够访问服务器,我们需要勾选<code>HTTP(80)</code>和<code>HTTPS(443)</code>端口,其余端口如<code>MySQL(3306)</code>,<code>Redis(6379)</code>等也可以勾选。</p> </div><h1 id="云服务器配置"><a href="#云服务器配置" class="headerlink" title="云服务器配置"></a>云服务器配置</h1><p>我们的整体思路是,在服务器上配置Git,并且将本地与服务器上的远程仓库相连接,通过将本地静态文件以Git形式推送到远程仓库,并通过钩子自动将网页文件检出到工作目录下,通过Nginx反向代理将我们的静态网页文件呈现在Web界面上。</p><h2 id="创建博客根目录"><a href="#创建博客根目录" class="headerlink" title="创建博客根目录"></a>创建博客根目录</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd /home<br>mkdir www<br>cd /www<br>mkdir blog<br></code></pre></td></tr></table></figure><h2 id="Nodejs及Nginx环境安装"><a href="#Nodejs及Nginx环境安装" class="headerlink" title="Nodejs及Nginx环境安装"></a>Nodejs及Nginx环境安装</h2><p>安装Nodejs及Nginx:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">sudo apt-get install nodejs<br>sudo apt-get install nginx<br></code></pre></td></tr></table></figure><p>安装后检查版本号:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">node -v<br>nginx -v<br></code></pre></td></tr></table></figure><h2 id="Git配置"><a href="#Git配置" class="headerlink" title="Git配置"></a>Git配置</h2><h3 id="安装Git"><a href="#安装Git" class="headerlink" title="安装Git"></a>安装Git</h3><p>首先我们切换到超级用户<code>root</code>:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">sudo su root<br></code></pre></td></tr></table></figure><p>安装Git:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">apt-get install git<br></code></pre></td></tr></table></figure><p>安装成功后,检查版本号,输入:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">git --version<br></code></pre></td></tr></table></figure><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p12.png" alt="Git安装成功"></p><h3 id="添加账户"><a href="#添加账户" class="headerlink" title="添加账户"></a>添加账户</h3><p>为服务器添加名为<code>git</code>的用户:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">adduser git<br></code></pre></td></tr></table></figure><h3 id="修改权限"><a href="#修改权限" class="headerlink" title="修改权限"></a>修改权限</h3><p>添加后修改用户权限,使git用户能通过<code>sudo</code>以root用户身份来执行命令。为了修改配置用户权限的文件<code>/etc/sudoers</code><br>输入以下命令更改权限:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">chmod 640 /etc/sudoers<br></code></pre></td></tr></table></figure><div class="note note-info"> <p>这里<code>chmod</code>为修改权限指令,<code>640</code>中每一位分别表示不同类别用户权限,第一位为<code>文件/目录所有者(User)</code>,第二位为<code>文件/目录所属用户组(Group)</code>,第三位为<code>其他用户(Others)</code>。每一位的数字为权限所对应的二进制数字的十进制值,最大值为<code>7</code>,即<code>rwx</code>,其中<code>r</code>,<code>w</code>,<code>x</code>分别表示<code>读</code>,<code>写</code>,<code>执行</code>等。这里的<code>6</code>即为<code>110</code>,即<code>rw-</code>表示所有者(root)具有<code>写</code>和<code>读</code>的权限。</p> </div><p>打开该文件:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /etc/sudoers<br></code></pre></td></tr></table></figure><p>如图所示添加以下代码(进入Vim编辑器后按下键盘上的<code>I</code>,进入编辑模式(<code>INSERT</code>)。编辑完成后按下<code>Esc</code>,键入<code>:wq</code>保存并退出,其中<code>w</code>为保存,<code>q</code>为退出):</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">git ALL=(ALL:ALL)ALL<br></code></pre></td></tr></table></figure><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p13.png" alt="sudoers文件更改"></p><div class="note note-info"> <p>这里表示<code>git</code>用户在任何主机(第一个ALL),都可以以任何身份(第二个ALL),作为任何一个用户组的成员(第三个ALL),执行任何命令(第四个ALL)。添加后git用户可通过<code>sudo</code>命令以root身份发布指令。</p> </div><p>保存并退出后将目录权限修改回去:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">chmod 400 /etc/sudoers<br></code></pre></td></tr></table></figure><h3 id="设置密码"><a href="#设置密码" class="headerlink" title="设置密码"></a>设置密码</h3><p>为git用户设置密码:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">passwd git<br></code></pre></td></tr></table></figure><div class="note note-warning"> <p>注意:输入密码时不会显示,输入完成后按下回车键即可</p> </div><h3 id="配置密钥"><a href="#配置密钥" class="headerlink" title="配置密钥"></a>配置密钥</h3><p>首先切换到git用户,并切至git主目录(/home/git),创建目录<code>.ssh</code>并进入,进入后生成密钥:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs shell">su git #切换账户<br>cd ~ #切至主目录<br>mkdir .ssh #新建目录<br>cd .ssh #进入目录<br>ssh-keygen #生成密钥<br></code></pre></td></tr></table></figure><p>同上,按三次回车,在默认位置生成没有密码的密钥。<br>之后执行:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd ~/.ssh<br>cp id_rsa.pub authorized_keys<br></code></pre></td></tr></table></figure><p>将<code>id_rsa.pub</code>复制到<code>authorized_keys</code>中,目录下会产生一个<code>authorized_keys</code>文件。<br>修改<code>authorized_keys</code>文件和<code>.ssh</code>目录权限:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">chmod 600 ~/.ssh/authorized_keys<br>chmod 700 ~/.ssh<br></code></pre></td></tr></table></figure><p>之后打开之前在<strong>本地</strong>生成的公钥文件<code>id_rsa.pub</code>,复制内容,然后编辑服务器<code>authorized_keys</code>文件:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim ~/.ssh/authorized_keys<br></code></pre></td></tr></table></figure><p>换行后粘贴本地公钥至其末尾:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p14.png" alt="授权公钥"></p><h3 id="远程连接"><a href="#远程连接" class="headerlink" title="远程连接"></a>远程连接</h3><p>打开本地命令行(<code>win</code>+<code>R</code>输入<code>cmd</code>),输入以下命令远程连接服务器git账户:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">ssh -v git@服务器公网IP<br></code></pre></td></tr></table></figure><div class="note note-info"> <p><code>-v</code>意为以详细模式(verbose mode)打开,可省略</p> </div><p>出现以下提示,即连接成功:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p15.png" alt="远程连接成功"></p><h2 id="仓库配置"><a href="#仓库配置" class="headerlink" title="仓库配置"></a>仓库配置</h2><p>首先切换至git账户:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">su git<br></code></pre></td></tr></table></figure><p>创建仓库目录和钩子文件:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd ~ # 进入主目录<br>git init --bare blog.git # 初始化裸仓库blog.git<br>chown -R git:git blog.git # 递归地更改所有者为git,所属组为git<br>vim ~/blog.git/hooks/post-receive # 创建并编辑钩子<br></code></pre></td></tr></table></figure><div class="note note-info"> <p>钩子(hooks)可理解为在进行某种Git操作时自动触发的脚本,我们通过钩子来使仓库分支发生变更时自动将文件检出至工作目录下,实现自动更新的目的。</p> </div><p>编辑post-receive内容为:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br>git --work-tree=/home/www/blog --git-dir=/home/git/blog.git checkout -f<br></code></pre></td></tr></table></figure><div class="note note-info"> <p>脚本中,工作目录设置为<code>/home/www/blog</code>,仓库目录设置为<code>/home/git/blog.git</code>,<code>checkout</code>为检出操作,将仓库中文件检出至工作目录,<code>-f</code>意为强制执行。</p> </div><p>赋予钩子可执行权限和<code>/home/www/blog</code>目录可写权限:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">sudo chmod +x ~/blog.git/hooks/post-receive<br>sudo chmod +x go+w /home/www/blog<br></code></pre></td></tr></table></figure><p>为了使我们的钩子脚本能够正常执行,我们还需要配置仓库的<code>dentCurrentBranch</code>选项:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /home/git/blog.git/config<br></code></pre></td></tr></table></figure><p>打开文件后在文件末尾添加:</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[receive]</span><br><span class="hljs-attr">denyCurrentBranch</span> = ignore<br></code></pre></td></tr></table></figure><p><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p16.png" alt="接收推送"></p><div class="note note-danger"> <p>在 Git 的早期版本中,默认的行为是禁止向当前检出的分支推送新提交,以防止“非快进式”(non-fast-forward)的推送覆盖掉远程仓库中其他人可能正在工作的内容。这是因为如果远程仓库的分支被意外覆盖,那么正在该分支上工作的其他用户可能会丢失他们的工作。所以我们需要关闭<code>denyCurrentBranch</code>,我们的钩子脚本才能正常执行。</p> </div><div class="note note-info"> <p>检查钩子脚本是否正常执行,可以在<code>/home/git(或其他位置)</code>新建一个<code>check.txt</code>文本文件(<code>vim /home/git/check.txt</code>),在<code>post-receive</code>钩子中添加一行<code>echo "okk" >> /home/git/check.txt</code>,检查文本文件内容判断钩子文件是否执行。</p> </div><h2 id="Nginx代理配置"><a href="#Nginx代理配置" class="headerlink" title="Nginx代理配置"></a>Nginx代理配置</h2><h3 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h3><p>首先我们先来了解Nginx目录下相关目录和文件:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p17.png" alt="Nginx目录"></p><ul><li><code>nginx.conf</code>为配置文件,其内容中<ul><li><code>include /etc/nginx/conf.d/*.conf;</code> 意为包含/etc/nginx/conf.d目录下所有以.conf为后缀的文件。</li><li><code>include /etc/nginx/sites-enabled/*;</code>意为包含/etc/nginx/sites-enabled目录下所有文件。</li></ul></li><li><code>sites-available</code>为监听的所有请求。</li><li><code>sites-enabled</code>为允许访问的所有请求,其内容为指向<code>sites-available</code>中文件的软链接。<div class="note note-danger"> <p>注意:可自行阅读<code>sites-available</code>中的<code>default</code>默认文件,之后删除<code>sites-available</code>中的<code>default</code>文件和<code>sites-enabled</code>中对应的<code>default</code>链接。因为<code>default</code>中默认的根目录为<code>var/www/index.html</code>,并且<code>default</code>链接是被<code>nginx.conf</code>include在内的,如果不删除,访问服务器公网地址仅能看见Nginx默认的欢迎页面。</p> </div></li></ul><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><p>我们来配置访问请求对应服务,下面所举的例子均以我的域名<code>mrna16.top</code>为例:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /etc/nginx/sites-available/mrna16<br></code></pre></td></tr></table></figure><p>编辑内容为:</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-section">server</span> { <br> <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>; <br> <span class="hljs-attribute">server_name</span> mrna16.top; <br> <span class="hljs-attribute">root</span> /home/www/blog;<br> <span class="hljs-attribute">index</span> index.html; <br> <span class="hljs-section">location</span> / { <br> <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ =<span class="hljs-number">404</span>; <br> } <br>}<br></code></pre></td></tr></table></figure><p>为了能使<code>www.mrna16.top</code>也能访问:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /etc/nginx/sites-available/wwwmrna16<br></code></pre></td></tr></table></figure><p>编辑内容为:</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-section">server</span> { <br> <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>; <br> <span class="hljs-attribute">server_name</span> www.mrna16.top; <br> <span class="hljs-attribute">root</span> /home/www/blog;<br> <span class="hljs-attribute">index</span> index.html; <br> <span class="hljs-section">location</span> / { <br> <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ =<span class="hljs-number">404</span>; <br> } <br>}<br></code></pre></td></tr></table></figure><p>完成编辑后我们通过以下命令在<code>sites-enabled</code>目录中产生软链接:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">sudo ln -s /etc/nginx/sites-available/mrna /etc/nginx/sites-enabled/mrna <br>sudo ln -s /etc/nginx/sites-available/wwwmrna /etc/nginx/sites-enabled/wwwmrna<br></code></pre></td></tr></table></figure><div class="note note-info"> <p><code>-s</code>即为产生软链接/符号链接(symbolic link)。我们的文件都在磁盘中放置,想要找到某一文件,我们需要知道它的索引,当索引条数为0时,文件将无法找到,内核将文件内容从磁盘上删除(多个文件名可以指向同一索引节点,类似链表)。硬链接就相当于链接到这里的索引节点,而软链接则相当于创建一个新的文件,作为索引的指针,记录索引的位置,当我们访问软链接时,获取到的是索引,系统通过索引再找到真正想要访问的文件。形象地说,我们的文件就像药店的药材,放在不同的抽屉里,硬链接相当于给抽屉贴上标签,标明什么药在抽屉里;软链接则相当于拿一张纸,专门来记录哪味药在哪个抽屉里。药换位置,标签换位置,但是纸上记下的信息没有改变。软硬链接各有特点。<br>硬链接:</p><ul><li>无法给目录创建硬链接</li><li>硬链接无法跨文件系统访问</li><li>文件位置移动时,硬链接仍然可以访问到</li><li>硬链接大小和文件大小相同<br>软链接:</li><li>可以给目录创建软链接</li><li>软连接可以跨文件系统甚至跨设备访问</li><li>文件位置移动,软连接无法访问</li><li>软链接需要系统额外分配空间建立索引节点和存储文件路径</li><li>软链接不会增加文件的索引条数</li></ul> </div><h2 id="本地推送"><a href="#本地推送" class="headerlink" title="本地推送"></a>本地推送</h2><p>打开hexo目录下的<code>_config.yml</code>,修改<code>deploy</code>选项:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p11.png" alt="deploy配置"></p><div class="note note-info"> <p>@后为服务器公网IP</p> </div><p>保存后,在hexo目录下打开Git命令行,输入命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo clean<br>$ hexo g<br>$ hexo d<br></code></pre></td></tr></table></figure><p>出现以下提示则推送成功:<br><img src="/img/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91hexo-fluid%E5%8D%9A%E5%AE%A2%E5%9C%A8github%E5%8F%8A%E4%B8%AA%E4%BA%BA%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E9%83%A8%E7%BD%B2/p18.png" alt="推送成功"></p><hr><p> <em><strong>完</strong></em></p><div class="note note-success"> <p>这是我的第一篇技术博客,经验不足,还望多多包涵。如果这篇文章能帮到你,那就太好了!如果对于文章内容有问题和建议,欢迎移步<a href="/about/index.html">关于页</a>获取我的联系方式,感谢批评指正,期待多多交流!</p> </div>]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>Fluid</tag>
<tag>git</tag>
<tag>Nodejs</tag>
<tag>Github Pages</tag>
<tag>Linux</tag>
<tag>Ubuntu</tag>
<tag>nginx</tag>
<tag>阿里云</tag>
</tags>
</entry>
<entry>
<title>【随笔】建站</title>
<link href="/2024/08/06/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%BB%BA%E7%AB%99/"/>
<url>/2024/08/06/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%BB%BA%E7%AB%99/</url>
<content type="html"><![CDATA[<h1 id="最初"><a href="#最初" class="headerlink" title="最初"></a>最初</h1><blockquote><p><strong>我想把某些瞬间写下来</strong></p></blockquote><h2 id="对技术的好奇"><a href="#对技术的好奇" class="headerlink" title="对技术的好奇"></a>对技术的好奇</h2><p>一个偶然的机会,我在搜索引擎中发现了学长的博客。第一反应是,震撼,这个词我觉得并不夸张。然后,很精致的背景图片,一段话,从打字机中跳出来,就像一片静谧的花园,花园的主人浇灌着自己的喜欢的花。路过的人喜欢的话就翻翻看看,没有人停下也没事,独自欣赏就好了。我想,我也想有自己的博客。</p><h2 id="思想的储藏室"><a href="#思想的储藏室" class="headerlink" title="思想的储藏室"></a>思想的储藏室</h2><p>之前常常怀疑自己是不是有些过度思考的倾向,于是便有意识地去回避思考。上了大学,又觉得自己是不是每天思考太少,时间和精力很多都用来处理生活的一些琐碎和意义不大的事情。思考不应该被回避,不应该被忽略。虽然每个阶段的所思所想局限于截止当时的见识,处境,但是这并不是没有意义的。就像摄影,按下快门定格瞬间。每个阶段的思考,放在人生的长度来看,无非也就某些瞬间。我想把某些瞬间写下来。</p><h1 id="然后"><a href="#然后" class="headerlink" title="然后"></a>然后</h1><blockquote><p><strong>道阻且长,行则将至</strong></p></blockquote><h2 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h2><p>搭建过程其实也就是在网上找找教程,一步一步跟着来。总会有问题的,那就把报错复制下来,百度,问GPT,总有解决办法的。我采用的是常见的hexo框架。暑假做家教之余,就每天一点一点慢慢学慢慢搞,nodejs和git,Markdown,部署github pages静态网页,Linux…每部分涉及到的内容,一开始都是不理解的,跟着教程走,只缘身在此山中;走着走着,理解就会更深,视角就会更广,横看成岭侧成峰。这种感觉还是挺不错的。未来会学更多的东西,不过我觉得,道阻且长,行则将至。</p><h2 id="希望"><a href="#希望" class="headerlink" title="希望"></a>希望</h2><p>搭建博客是一次性的工作,写博客是持久性的工作。我的域名购买了10年,当然也是因为这样买更便宜,我希望自己能在日常的疲惫和琐碎中脱离出来一会儿,摆脱浮躁的社会和人心,静下来,就是这样写一些什么,打理打理自己的花园。后面我会在这里更新自己的学习总结和生活随笔,嘟囔一些碎碎念。</p>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>随笔</tag>
<tag>碎碎念</tag>
</tags>
</entry>
</search>