├── .gitignore ├── CNAME ├── LICENSE ├── README.md ├── docs ├── .vuepress │ ├── config.js │ ├── public │ │ ├── favicon.ico │ │ └── hero.png │ └── styles │ │ └── palette.styl ├── 1_Preparations │ ├── 1-1_Installation.md │ ├── 1-2_Vivado.md │ └── 1-3_Editor.md ├── 2_SingleCycle │ ├── 2-1_Basic.md │ ├── 2-2_Design.md │ ├── 2-3_Verilog.md │ └── 2-4_Testbench.md ├── 3_Pipelining │ ├── 3-0_Instructions.md │ ├── 3-1_Basic.md │ ├── 3-2_Datapath&Control.md │ ├── 3-3_Hazards.md │ ├── 3-4_BranchPrediction.md │ ├── 3-5_Design.md │ └── 3-6_IP.md ├── 4_Peripherals │ ├── 4-0_Basic.md │ └── 4-1_VGA.md └── README.md ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | db.json 4 | *.log 5 | node_modules/ 6 | .vscode/ 7 | docs/.vuepress/dist 8 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | zanpu.spencerwoo.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | 307 | including for purposes of Section 3(b); and 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public 411 | licenses. Notwithstanding, Creative Commons may elect to apply one of 412 | its public licenses to material it publishes and in those instances 413 | will be considered the “Licensor.” The text of the Creative Commons 414 | public licenses is dedicated to the public domain under the CC0 Public 415 | Domain Dedication. Except for the limited purpose of indicating that 416 | material is shared under a Creative Commons public license or as 417 | otherwise permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the 425 | public licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 🚡 Build Your PC 4 | 5 | [🎃 实验要求](#-实验要求2019-版) | [📑 文档目录](#-文档目录) | [🎁 参考资料](#-参考资料与推荐阅读) 6 | 7 | [![Netlify Status](https://api.netlify.com/api/v1/badges/e307ad01-d0ba-4c39-bbe7-42ed13bbd3da/deploy-status)](https://app.netlify.com/sites/zanpu/deploys) 8 | 9 | **辛苦三星期,造台计算机!** 10 | 11 | > 本项目为 BIT 大四小学期: 12 | > 13 | > - 计算机组成原理课程设计 14 | > - 汇编与接口技术课程设计 15 | > 16 | > 两门课程的基本设计流程与参考资料的非官方整理。 17 | 18 | ## 🎃 实验要求(2019 版) 19 | 20 | - **计算机组成原理课程设计** 21 | - 每一位同学自行完成一个支持 MIPS 指令子集:Lui、Addiu、Add、Lw、Sw、Beq、j,以及一随机抽取的指令的单周期 CPU,并给出仿真结果 22 | - 每组同学共同完成一个多周期的或者流水线的 CPU,仿真并在精工板上验证 23 | - **汇编与接口课程设计** 24 | - 每一位同学为自己设计的 CPU 写一个测试程序(MIPS 汇编程序或者 C 语言 + MIPS 编译器) 25 | - 根据精工板资源,按组完成计算机外设接口设计,如 VGA 控制器、LCD、UART、蓝牙等,测试并形成 IP 核 26 | - 应用 ② 中产生的 IP 核到自行设计的计算机系统中,在精工板上实现。 27 | 28 | ## 📑 文档目录 29 | 30 | 如果没有意外,那么你的设计流程应该和以下过程一致。 31 | 32 | ### 准备工作 33 | 34 | - [安装与环境部署](https://zanpu.spencerwoo.com/1_preparations/1-1_installation.html) 35 | - [利用 Vivado 创建项目](https://zanpu.spencerwoo.com/1_Preparations/1-2_Vivado.html) 36 | - [使用 VS Code 作为 Vivado 的默认代码编辑器](https://zanpu.spencerwoo.com/1_Preparations/1-3_Editor.html) 37 | 38 | ### 个人项目 - 单周期 CPU 39 | 40 | > 单周期 CPU 是个人项目中的必要起步项目,在单周期 CPU 的构建过程中我们才能理解流水线 CPU 的具体工作原理。 41 | 42 | - [单周期 CPU 的基础知识](https://zanpu.spencerwoo.com/2_SingleCycle/2-1_Basic.html) 43 | - [单周期 CPU 的设计思路](https://zanpu.spencerwoo.com/2_SingleCycle/2-2_Design.html) 44 | - [单周期 CPU 的具体代码实现](https://zanpu.spencerwoo.com/2_SingleCycle/2-3_Verilog.html) 45 | - [单周期 CPU 的行为仿真](https://zanpu.spencerwoo.com/2_SingleCycle/2-4_Testbench.html) 46 | 47 | ### 团队项目 - 多周期 CPU / 流水线 CPU 48 | 49 | > 我们团队选择实现流水线 CPU,因此接下来的参考文档只介绍流水线 CPU 的设计流程。 50 | 51 | - [流水线 CPU 准备实现的指令](https://zanpu.spencerwoo.com/3_Pipelining/3-0_Instructions.html) 52 | - [流水线 CPU 的基础知识](https://zanpu.spencerwoo.com/3_Pipelining/3-1_Basic.html) 53 | - [数据通路与信号控制](https://zanpu.spencerwoo.com/3_Pipelining/3-2_Datapath&Control.html) 54 | - [Hazards 与其避免措施](https://zanpu.spencerwoo.com/3_Pipelining/3-3_Hazards.html) 55 | - [分支预测 Branch Prediction](https://zanpu.spencerwoo.com/3_Pipelining/3-4_BranchPrediction.html) 56 | - [流水线 CPU 的设计](https://zanpu.spencerwoo.com/3_Pipelining/3-5_Design.html) 57 | - [利用 IP 核对 CPU 进行封装](https://zanpu.spencerwoo.com/3_Pipelining/3-6_IP.html) 58 | 59 | ### 团队项目 - 外部设备 Peripherals 60 | 61 | > 本次小学期实验最后需要与精工开发板进行整合,需要通过 VGA 等接口调用来让 CPU 能够实现与外设接口的整合,实现一个完整的计算机系统。 62 | 63 | - [外部设备的基础配置](https://zanpu.spencerwoo.com/4_Peripherals/4-0_Basic.html) 64 | - [以 VGA 为例子对外部设备进行信号输出](https://zanpu.spencerwoo.com/4_Peripherals/4-1_VGA.html) 65 | 66 | ## 🎁 参考资料与推荐阅读 67 | 68 | - [How does a CPU work - Hackernoon](https://hackernoon.com/how-does-a-cpu-work-af3488d182a2) 69 | - [University of Washington - Course CSE378](https://courses.cs.washington.edu/courses/cse378/09wi/lectures.html) 70 | - [A single-cycle MIPS processor](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec07.pdf) 71 | - [Intro to Pipelining](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec09.pdf) 72 | - [Pipelined datapath and control](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec10.pdf) 73 | - [Pipelining and Data Hazards](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec11.pdf) 74 | - [Hazards](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec12.pdf) 75 | - [Branching, Performance](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec13.pdf) 76 | - [针对参加龙芯杯的若干建议 - Silverster98/bit_nscscc_suggestion](https://github.com/Silverster98/bit_nscscc_suggestion) 77 | 78 | --- 79 | 80 | **🚡 Build Your PC** ©2019 Spencer Woo. Released under the [CC BY-SA 4.0 International License](./LICENSE). 81 | 82 | Authored and maintained by Spencer Woo. 83 | 84 | [@Portfolio](https://spencerwoo.com) | [@GitHub](https://github.com/spencerwooo) | [@BIT](http://www.bit.edu.cn/) 85 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Build Your PC', 3 | description: '辛苦三星期,造台计算机!', 4 | head: [ 5 | ['link', { 6 | rel: 'icon', 7 | href: '/favicon.ico' 8 | }], 9 | ['meta', { 10 | name: 'theme-color', 11 | content: '#00ABE9' 12 | }], 13 | ['meta', { 14 | name: 'apple-mobile-web-app-capable', 15 | content: 'yes' 16 | }], 17 | ['meta', { 18 | name: 'apple-mobile-web-app-status-bar-style', 19 | content: 'black' 20 | }], 21 | ['link', { 22 | rel: 'apple-touch-icon', 23 | href: '/favicon.ico' 24 | }], 25 | ['meta', { 26 | name: 'msapplication-TileImage', 27 | content: '/favicon.ico' 28 | }], 29 | ['meta', { 30 | name: 'msapplication-TileColor', 31 | content: '#06BDFF' 32 | }] 33 | ], 34 | themeConfig: { 35 | nav: [{ 36 | text: '准备工作', 37 | items: [{ 38 | text: '安装与环境部署', 39 | link: '/1_Preparations/1-1_Installation' 40 | }, 41 | { 42 | text: '利用 Vivado 创建项目', 43 | link: '/1_Preparations/1-2_Vivado' 44 | }, 45 | { 46 | text: '使用 VS Code 作为 Vivado 的默认代码编辑器', 47 | link: '/1_Preparations/1-3_Editor' 48 | }] 49 | }, 50 | { 51 | text: '单周期 CPU', 52 | items: [{ 53 | text: '单周期 CPU 的基础知识', 54 | link: '/2_SingleCycle/2-1_Basic' 55 | }, 56 | { 57 | text: '单周期 CPU 的设计思路', 58 | link: '/2_SingleCycle/2-2_Design' 59 | }, 60 | { 61 | text: '单周期 CPU 的具体代码实现', 62 | link: '/2_SingleCycle/2-3_Verilog' 63 | }, 64 | { 65 | text: '单周期 CPU 的行为仿真', 66 | link: '/2_SingleCycle/2-4_Testbench' 67 | }] 68 | }, 69 | { 70 | text: '流水线 CPU', 71 | items: [{ 72 | text: '流水线 CPU 准备实现的指令', 73 | link: '/3_Pipelining/3-0_Instructions' 74 | },{ 75 | text: '流水线 CPU 的基础知识', 76 | link: '/3_Pipelining/3-1_Basic' 77 | },{ 78 | text: '数据通路与信号控制', 79 | link: '/3_Pipelining/3-2_Datapath&Control' 80 | },{ 81 | text: 'Hazards 与其避免措施', 82 | link: '/3_Pipelining/3-3_Hazards' 83 | },{ 84 | text: '分支预测 Branch Prediction', 85 | link: '/3_Pipelining/3-4_BranchPrediction' 86 | },{ 87 | text: '流水线 CPU 的设计', 88 | link: '/3_Pipelining/3-5_Design' 89 | },{ 90 | text: '利用 IP 核对 CPU 进行封装', 91 | link: '/3_Pipelining/3-6_IP' 92 | }] 93 | },{ 94 | text: '外部设备', 95 | items: [{ 96 | text: '外部设备的基础配置', 97 | link: '/4_Peripherals/4-0_Basic' 98 | }, 99 | { 100 | text: '以 VGA 为例子对外部设备进行信号输出', 101 | link: '/4_Peripherals/4-1_VGA' 102 | }] 103 | }, 104 | { 105 | text: 'GitHub', 106 | link: 'https://github.com/spencerwooo/build-your-pc-docs' 107 | }, 108 | ], 109 | sidebar: { 110 | '/1_Preparations/': [ 111 | '1-1_Installation', 112 | '1-2_Vivado', 113 | '1-3_Editor' 114 | ], 115 | '/2_SingleCycle/': [ 116 | '2-1_Basic', 117 | '2-2_Design', 118 | '2-3_Verilog', 119 | '2-4_Testbench' 120 | ], 121 | '/3_Pipelining/': [ 122 | '3-0_Instructions', 123 | '3-1_Basic', 124 | '3-2_Datapath&Control', 125 | '3-3_Hazards', 126 | '3-4_BranchPrediction', 127 | '3-5_Design', 128 | '3-6_IP' 129 | ], 130 | '/4_Peripherals/': [ 131 | '4-0_Basic', 132 | '4-1_VGA' 133 | ] 134 | }, 135 | lastUpdated: 'Last Updated' 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spencerwooo/build-your-pc-docs/3b0c422dfcf2bb00f4e38ec5737fecda172c2cf1/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spencerwooo/build-your-pc-docs/3b0c422dfcf2bb00f4e38ec5737fecda172c2cf1/docs/.vuepress/public/hero.png -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | $accentColor = #4caf50 2 | -------------------------------------------------------------------------------- /docs/1_Preparations/1-1_Installation.md: -------------------------------------------------------------------------------- 1 | # 安装与环境部署 2 | 3 | > Vivado 设计套件,是 FPGA 厂商赛灵思公司 2012 年发布的集成设计环境。包括高度集成的设计环境和新一代从系统到 IC 级的工具。这也是一个基 于AMBA AXI4 互联规范、IP-XACT IP 封装元数据、工具命令语言(TCL)、Synopsys 系统约束(SDC)以及其它有助于根据客户需求量身定制设计流程并符合业界标准的开放式环境。 4 | 5 | ## 安装 Vivado 6 | 7 | Vivado Design Suite 是 Xilinx 为 HDL 设计的综合和分析而生产的软件套件,我们会利用 Vivado 编写设计我们的 MIPS 指令集等项目。本实验由于开发板限制,建议使用 2017.2 版本。 8 | 9 | Vivado 受到出口管制的限制,「官网安装途径」需要首先在官网注册账户进行身份认证,才能下载使用。注册账户之后,我们进入 [Xilinx - Vivado archive](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive.html),点击 2017.2,选择 Vivado HLx 2017.2: WebPACK and Editions - Windows Self Extracting Web Installer 下载。 10 | 11 | ![](https://i.loli.net/2019/08/27/6GNc5nwXMod7IPA.png) 12 | 13 | 接下来,我们直接双击运行 `exe` 可执行文件,安装 Vivado。 14 | 15 | 在下面的步骤中,我们依次: 16 | 17 | - 登录 Xilinx 账户 18 | - 同意全部协议 19 | - 选择安装「WebPACK」版本 Vivado 20 | 21 | ![](https://i.loli.net/2019/08/27/IoqjHT6eFJUW9MR.png) 22 | 23 | - 勾选安装以下选项: 24 | 25 | ![](https://i.loli.net/2019/08/27/NYS378cW2T9Cd4K.png) 26 | 27 | 接下来就可以顺序安装了,等待全部组件下载安装大概需要 1 小时左右(网速 1~2 MB/s 情况下)。在安装过程会要求选择安装两个驱动以及 WinPCAP,选择确认安装即可。这样 Vivado 的安装就完成了。 28 | 29 | ## 附 30 | 31 | 精工开发板(数字逻辑课程使用过): 32 | 33 | ![](https://i.loli.net/2019/08/27/VgFOB3XDAS6uoet.jpg) 34 | -------------------------------------------------------------------------------- /docs/1_Preparations/1-2_Vivado.md: -------------------------------------------------------------------------------- 1 | # 利用 Vivado 创建项目 2 | 3 | ## 创建工程项目 4 | 5 | 首先确认,我们使用的开发板型号为: 6 | 7 | ``` 8 | xc7a35tcsg324-1 9 | ``` 10 | 11 | 接下来,点击打开 Vivado,选择创建项目(Create Project): 12 | 13 | ![](https://i.loli.net/2019/08/27/JUIuQGpcLMg4aAi.png) 14 | 15 | 之后,选择项目名称 Project Name 以及项目创建位置: 16 | 17 | ![](https://i.loli.net/2019/08/27/vgeTp9wc6lHiGLy.png) 18 | 19 | 然后选择项目种类为 RTL Project,并将下方「Do not specify sources at this time」勾选。Source 文件我们在创建项目之后再进行创建: 20 | 21 | ![](https://i.loli.net/2019/08/27/dTZmjKGJcsNDViR.png) 22 | 23 | 接下来,选择 Parts 标签,并搜索我们实验使用开发板型号 `xc7a35tcsg324-1`,并选择这一开发板: 24 | 25 | ![](https://i.loli.net/2019/08/27/nEWkuFKgcAIDNx8.png) 26 | 27 | 最后,检查项目汇总,确认无误之后,选择 Finish: 28 | 29 | ![](https://i.loli.net/2019/08/27/HdPn3gvibyMJ1QA.png) 30 | 31 | 这样,项目就创建成功了。 32 | 33 | ## 新建 Verilog 工程文件 34 | 35 | ![](https://i.loli.net/2019/08/27/wORYmygshMWFTdj.png) 36 | 37 | Verilog 是一门硬件描述语言,我们在接下来的实验中会利用 Verilog 编写组合逻辑电路和时许逻辑电路,最后创造自己的 CPU。在 Vivado 的项目管理中,我们可以看到: 38 | 39 | - Design Sources 40 | - Constraints 41 | - Simulation Sources 42 | 43 | 这三个文件夹。其中,Design Sources 就是我们的「Verilog 硬件设计工程文件」集中地。我们选择上方的加号,然后选择创建「Add or create design sources」: 44 | 45 | ![](https://i.loli.net/2019/08/27/jl7azF6ECwUHDkM.png) 46 | 47 | 之后,选择「Create File」,并填写文件名称: 48 | 49 | ![](https://i.loli.net/2019/08/27/bu6QH1R3ZmiJxFP.png) 50 | 51 | 然后就可以看到新文件出现在预览列表之中。我们接下来选择 Finish: 52 | 53 | ![](https://i.loli.net/2019/08/27/2GPuFwxOCqKMUka.png) 54 | 55 | 然后,在 Define Module 处选择「OK」,即可: 56 | 57 | ![](https://i.loli.net/2019/08/27/CXoWtlLOdFanBsx.png) 58 | 59 | 我们接下来就可以看到刚刚创建的 Verilog 文件出现在我们的文件树中了。 60 | 61 | ## 综合项目、配置 Constraint 62 | 63 | 我们在刚刚创建的文件中编写一个简单的频闪灯 Verilog 代码: 64 | 65 | ```verilog 66 | `timescale 1ns / 1ps 67 | 68 | // Flicker light 69 | 70 | module pc( 71 | input wire clk, 72 | input wire rst, 73 | output wire led_out 74 | ); 75 | 76 | reg [31:0] cnt; 77 | reg led_light; 78 | 79 | assign led_out = led_light; 80 | 81 | always @ (posedge clk) begin 82 | if (rst == 1'b0) begin 83 | cnt <= 32'b0; 84 | led_light <= 1'b0; 85 | end 86 | else begin 87 | if (cnt == 24'hffffff) begin 88 | cnt <= 32'b0; 89 | if (led_light == 1'b0) begin 90 | led_light <= 1'b1; 91 | end 92 | else begin 93 | led_light <= 1'b0; 94 | end 95 | end 96 | else begin 97 | cnt <= cnt + 1; 98 | end 99 | end 100 | end 101 | endmodule 102 | ``` 103 | 104 | 接下来,我们点击「Run Synthesis」综合项目源代码: 105 | 106 | ![](https://i.loli.net/2019/08/28/5VUxmqFgrckT94d.png) 107 | 108 | 项目综合完成之后,选择「Open Synthesized Design」,配置管脚: 109 | 110 | ![](https://i.loli.net/2019/08/28/koLNK48FTb9n2dX.png) 111 | 112 | 之后,我们找到菜单中「Window - I/O Ports」,进行输入输出端口的配置: 113 | 114 | ![](https://i.loli.net/2019/08/28/h4iC1IS5JFgtvMb.png) 115 | 116 | 打开「I/O Ports」的配置选项卡之后,我们需要进行如下的调整: 117 | 118 | - 上面代码中声明的输入有两个,分别是时钟信号 `clk`、以及复位信号 `rst`,并由一个 LED 灯输出 `led_out`,我们需要分别进行这样的设置: 119 | - 将三个输入输出端口的标准 `I/O Std` 分别设置为 `LVCMOS33` 120 | - 根据开发板的配置,将: 121 | - 时钟信号 `clk` 的 Package Pin 设置为 T5 122 | - 复位信号 `rst` 的 Package Pin 设置为 P15 123 | - LED 灯输出信号 `led_out` Package Pin 设置为 K2 124 | 125 | ![](https://i.loli.net/2019/08/28/mqiy6ForcQVeYzx.png) 126 | 127 | 然后,我们保存 Constraint 文件,即可开始下一步的操作。 128 | 129 | ## 将程序烧入开发板 130 | 131 | ![](https://i.loli.net/2019/08/28/3f6mLZspWq1GUQB.png) 132 | 133 | 接下来,我们依次选择「Run Implementation」、「Generate Bitstream」,等等全部完成之后,选择「Open Hardware Manager」并将开发板连接至电脑。在「Hardware Manager」中,我们找到开发板的选项,右键选择「Program Device」即可将开发板烧制完成。 134 | -------------------------------------------------------------------------------- /docs/1_Preparations/1-3_Editor.md: -------------------------------------------------------------------------------- 1 | # 使用 VS Code 作为 Vivado 的默认代码编辑器 2 | 3 | ![](https://i.loli.net/2019/08/27/l7Ntgd6sGWb9XVL.png) 4 | 5 | 由于 Vivado 默认的代码编辑器实在不好用,因此我们可以使用 VS Code 进行 Verilog 的代码编写,并利用 Vivado 进行项目管理、综合、仿真与调试等。 6 | 7 | ## 更换默认代码编辑器 8 | 9 | 在 Vivado 的 Tools 中,点击选择设置「Settings」: 10 | 11 | ![](https://i.loli.net/2019/08/27/4ySQ7Ih6sOuPLeC.png) 12 | 13 | 之后,寻找「Text Editor」的配置项目,并将当前代码编辑器「Current Editor」更换为自定义编辑器「Custom Editor」: 14 | 15 | ![](https://i.loli.net/2019/08/27/P9Mq5SkxVJ82eoG.png) 16 | 17 | 接下来点击右侧「...」编辑,并填入 VS Code 的启动路径以及启动参数: 18 | 19 | ``` 20 | "C:/Users/<用户名>/AppData/Local/Programs/Microsoft VS Code/Code.exe" [file name] -l[line number] 21 | ``` 22 | 23 | ![](https://i.loli.net/2019/08/27/aNZ6Iohyf3RWQFv.png) 24 | 25 | 需要注意,VS Code 的启动路径就是 `code.exe` 的位置。在 Windows 开始菜单中右键选择打开文件位置,找到 VS Code 快捷方式,然后再右键选择打开文件位置即可找到。 26 | 27 | ## 配置 VS Code 的 Verilog 编写环境 28 | 29 | 使用 VS Code 编写 Verilog 需要: 30 | 31 | - Verilog 语言支持(比如代码高亮) 32 | - 实时代码检查 33 | - 代码自动格式化 34 | 35 | 这三个功能。 36 | 37 | ### Verilog 语言支持插件 38 | 39 | 我们首先安装插件:[Verilog HDL/SystemVerilog](https://marketplace.visualstudio.com/items?itemName=mshr-h.VerilogHDL),这样 VS Code 就有了基础的 Verilog 语法支持。 40 | 41 | ![](https://i.loli.net/2019/08/27/NjvlBmrAIn4R8XU.png) 42 | 43 | 之后,为了加入 Vivado 的实时代码检查功能,我们需要配置 `xvlog` 的环境变量。我们找到 Vivado 的安装路径,将 `bin` 文件夹的路径(一般是 `C:\Xilinx\Vivado\2017.2\bin`)加入环境变量 44 | 45 | ![](https://i.loli.net/2019/08/27/DsFZ3LbV4N2lSzP.png) 46 | 47 | 检查一下。我们打开 PowerShell,输入 `xvlog -version`,如果出现了 Vivado Simulator 的版本信息,表明我们的环境变量配置成功。 48 | 49 | ![](https://i.loli.net/2019/08/27/nfWDC5SG8y1Mrea.png) 50 | 51 | 之后,我们在 VS Code 的配置项目中找到 `Verilog > Linting: Linter` 的配置项,将其修改为 `xvlog` 即可: 52 | 53 | ![](https://i.loli.net/2019/08/27/6UXS9iZ7QmFNVaA.png) 54 | 55 | 为了能够配置 Verilog 的自动补全等功能,我们需要安装 `universal-ctags`。使用 scoop 包管理,我们在 Windows 上面直接输入: 56 | 57 | ```powershell 58 | scoop install universal-ctags 59 | ``` 60 | 61 | 即可安装 `universal-ctags`. 62 | 63 | ### Verilog 代码自动格式化插件 64 | 65 | ![](https://i.loli.net/2019/08/27/QWiVmlJBOKZD6Fo.png) 66 | 67 | 为了让 VS Code 能够自动格式化 Verilog 代码,我们需要安装:[verilog-formatter](https://marketplace.visualstudio.com/items?itemName=IsaacT.verilog-formatter) 这一插件。 68 | 69 | 同时,我们需要在 [HayasiKei/istyle-verilog-formatter](https://github.com/HayasiKei/istyle-verilog-formatter/releases/) 处下载最新编译的 iStyle Verilog Formatter 的 Windows 版本。解压得到 `iStyle.exe` 之后,我们选择一个妥当的位置放置可执行文件,并复制文件路径。 70 | 71 | 之后,我们在 VS Code 中找到 `Verilog-formatter > Istyle: Path`,并将刚刚的 `iStyle.exe` 的文件路径填入即可。之后,我们在 `Verilog-formatter > Istyle: Style` 配置项处选择一个格式化的风格(比如 `K&R`),就可以通过快捷键 `Ctrl + Shift + P` 并输入 Format Document 来格式化 Verilog 代码。 72 | 73 | ![](https://i.loli.net/2019/08/27/N4WLjS6DUpJwG3d.png) 74 | 75 | 使用 VS Code 来编写 Verilog 的体验极佳,推荐大家都进行如上的配置。 76 | -------------------------------------------------------------------------------- /docs/2_SingleCycle/2-1_Basic.md: -------------------------------------------------------------------------------- 1 | # 单周期 CPU 的基础知识 2 | 3 | CPU 本质上就是一个数字逻辑电路,所以我们设计 CPU 的时候实际上就是在设计电路。我们在使用 Verilog 的时候,实际上是在利用 Verilog 硬件描述语言来设计 CPU 的逻辑电路结构。因此,我们需要: 4 | 5 | - 先进行电路设计 6 | - 再进行 Verilog 的代码编写 7 | 8 | 只有当我们的电路被清晰的分解为结构图中各个模块与模块之间的连接、模块内部的数据通路和状态机、数据通路中的电路逻辑以及状态机中的状态转换图,那么接下来的 Verilog 代码设计就是一个简单的「翻译」而已。 9 | 10 | ## 单周期 CPU 11 | 12 | 单周期 CPU(Single Cycle Processor)是指一条指令在一个时钟周期内完成并开始下一条指令的执行。由时钟的「上升沿」和「下降沿」控制相关操作。两个相邻的「上升沿」或「下降沿」之间的时间间隔就是 CPU 的「时钟周期」。 13 | 14 | ## CPU 执行指令的实际过程 15 | 16 | 一个 CPU 执行指令的基本流程是这样的: 17 | 18 | | | 步骤 | 行为 | 19 | | :---: | :--------: | :---------------------------------------------------------------------------------------------------------------------------------: | 20 | | 1 | 取指令 | CPU 根据 PC(程序计数器 - Program Counter)中的指令地址,在指令存储器中获取相应的指令,之后 PC 的值会自动改变移动到下一条指令的地址 | 21 | | 2 | 指令译码 | 对获取的指令进行分析,确定这个指令要完成什么操作,改变相应的控制信号 | 22 | | 3 | 指令执行 | 相关组件获取控制信号,执行相应操作,并将结果反馈 | 23 | | 4 | 存储器访问 | 如果指令设计读取、存储内存,则需要对存储器中相应地址进行读取或者写入 | 24 | | 5 | 结果写回 | 将得到的数据(访问存储器或者修改其它寄存器的值获得)写回相应的寄存器 | 25 | | 6 | 循环 1-5 | ... | 26 | 27 | ## MIPS32 指令集子集 28 | 29 | 根据 MIPS32 指令集的设计,我们需要实现的指令有以下三种类型: 30 | 31 | ![](https://i.loli.net/2019/08/28/Utaqn6Hi8P2uygj.png) 32 | 33 | 所有的指令长度均为 32 比特。 34 | 35 | 根据实验要求,我们本次需要设计的指令共有这样几个:LUI、ADDIU、ADD、LW、SW、BEQ、J 以及一个抽签得到的指令(我的是 SUBU)。这些指令具体是这样的: 36 | 37 | ### 由 ALU 负责的指令 38 | 39 | - `LUI`:将 16 位立即数 imm 写入寄存器 rt 的高 16 位,寄存器 rt 的低 16 位置 0 40 | - `ADDIU`:将寄存器 rs 的值与**有符号扩展**至 32 位的立即数 imm 相加,结果写入 rt 寄存器中 41 | - `ADD`:将寄存器 rs 的值与寄存器 rt 的值相加,结果写入寄存器 rd 中。如果产生溢出,则触发整型溢出例外(IntegerOverflow) 42 | 43 | ![](https://i.loli.net/2019/08/29/IxHobe5zrAXSVUJ.png) 44 | 45 | ### 访存指令 46 | 47 | - `LW`:将 base 寄存器的值加上符号扩展后的立即数 offset 得到访存的虚地址,据此虚地址从存储器中读取连续 4 个字节的值并进行符号扩展,写入到 rt 寄存器中(不对错误进行处理) 48 | - `SW`:将 base 寄存器的值加上符号扩展后的立即数 offset 得到访存的虚地址,据此虚地址将 rt 寄存器存入存储器中(不对错误进行处理) 49 | 50 | ![](https://i.loli.net/2019/08/29/nmYfwMSDu3lWayC.png) 51 | 52 | ### 转移指令(无条件或条件跳转) 53 | 54 | - `BEQ`:如果寄存器 rs 的值等于寄存器 rt 的值则转移,否则顺序执行。转移目标由立即数 offset 左移 2 位并进行有符号扩展的值加上该分支指令对应的延迟槽指令的 PC 计算得到 55 | - `J`:无条件跳转。跳转目标由该分支指令对应的延迟槽指令的 PC 最高 4 位与立即数 `instr_index` 左移 2 位后的值拼接得到 56 | 57 | ![](https://i.loli.net/2019/08/29/LMIoTsnupXyRHjN.png) 58 | 59 | ### 我小组三人随机抽到的指令 60 | 61 | - `SUBU`:将寄存器 rs 的值与寄存器 rt 的值相减,结果写入 rd 寄存器中 62 | - `SRLV`:由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行逻辑右移,结果写入寄存器 rd 中 63 | - `ORI`:寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑或,结果写入寄存器 rt 中 64 | 65 | ![](https://i.loli.net/2019/08/29/PgDXB7M9AHd6mhS.png) 66 | 67 | ## CPU 组成中的主要元件 68 | 69 | - 指令存储器(Instruction Memory) 70 | - 数据存储器(Data Memory) 71 | - 寄存器堆(Register File) 72 | - 指令寄存器(Instruction Register) 73 | - 程序计数器(Program Counter) 74 | - 控制单元(Control Unit) 75 | 76 | ## 多周期、流水线 CPU 77 | 78 | ![1_FJxls8ZBHc3l3tTKxrO6Sg.png](https://i.loli.net/2019/09/03/kvNdwDGsaHOzebC.png) 79 | 80 | 在流水线 CPU 中,为了将指令更有效地执行,我们会将 CPU 的取值、译码、执行的过程按照流水线的方式进行组织,从而提高 CPU 的执行、运算效率。在之后的实验中,我们会通过这种方式进行 CPU 指令的执行与实现。 81 | -------------------------------------------------------------------------------- /docs/2_SingleCycle/2-2_Design.md: -------------------------------------------------------------------------------- 1 | # 单周期 CPU 的设计思路 2 | 3 | 熟悉了我们需要实现的 8 条指令之后,我们就需要设计 CPU 的数据通路。 4 | 5 | ## 基本的数据通路图 6 | 7 | 我在最终设计的数据通路图中,主要的硬件元件包括: 8 | 9 | - 指令存储器:Instruction Memory 10 | - 数据存储器:Data Memory 11 | - 寄存器堆:Register File 12 | - 程序计数器:Program Counter 13 | - 专用于处理跳转指令的 NPC:Next Program Counter 14 | - 控制单元:Control Unit 15 | - 算术逻辑单元:ALU 16 | - 符号扩展、移位二合一模块:Extend Module 17 | - 多处多路选择器:Multiplexer 18 | 19 | **值得注意的是**:单周期 CPU 在实际实现的过程中并不需要指令寄存器(IR)。 20 | 21 | 具体数据通路图大致如下: 22 | 23 | ![](https://i.loli.net/2019/09/02/LIoGAp8nbwtxV1e.png) 24 | 25 | 其中,主要的逻辑控制信号 Control Signals(蓝色)有: 26 | 27 | | 信号 | 功能 | 28 | | :--------: | :-------------------------------------------------------------------------------------: | 29 | | `RegWrite` | 写寄存器使能信号 | 30 | | `MemWrite` | 读数据存储器使能信号 | 31 | | `ALUOp` | ALU 控制信号(区分算术指令,比如 `ADD`、`SUB` 等) | 32 | | `RegSrc` | 选择将 ALU 计算结果、数据存储器输出或 Extend 模块输出写入寄存器 | 33 | | `RegDst` | 写入寄存器 rt 、rd 二选一 | 34 | | `ALUSrc` | 选择 ALU 源操作数来自寄存器或符号扩展的立即数(区分算术指令结果与 `LW`、`SW` 指令结果) | 35 | | `NPCOp` | 决定下一指令地址 NPC,或为 PC + 4,或为 BEQ 目标指令或 J 目标指令 | 36 | | `ExtOp` | Extend 模块控制信号源(`LUI` 移位 16 位、有无符号扩展) | 37 | 38 | 由于需要: 39 | 40 | - 实现 `BEQ` 和 `J`,立即数的位数不一样,因此 15 - 0 位的 `imm16` 代表 `BEQ` 指令的偏移量,25 - 0 位的 `imm26` 代表 `J` 指令的偏移量 41 | - 实现 `BEQ`,需要将 rs、rt 寄存器的值相减得到并判断是否为 0,因此引入 `Zero` 控制信号,用来判断是否跳转 42 | 43 | 对于 Extend 模块,由于我们需要实现: 44 | 45 | - `LUI`:将 rt 高 16 位填入 imm16,低 16 位置零。需要左移 16 位操作 46 | - `ADDIU`:需要对 rs 有符号扩展为 32 位 47 | - `LW`、`SW`:需要对 imm16 进行无符号扩展为 32 位 48 | 49 | 因此将这三个功能统一放置在 Extend 模块进行处理,并利用控制信号 `ExtOp` 进行控制。 50 | 51 | ## 主要控制逻辑的真值表 52 | 53 | ![](https://i.loli.net/2019/09/02/5vR41AYxGjQsiwV.png) 54 | 55 | 其中,对于 ALU 算术指令,我们只需要实现加法和减法,`ALUOp` 功能表如下(为了方便后续扩展,我设计的 `ALUOp` 有三位): 56 | 57 | | `ALUOp[2:0]` | 功能 | 描述 | 58 | | :----------: | :-----: | :----: | 59 | | 000 | Default | 缺省值 | 60 | | 001 | Y=A+B | 加 | 61 | | 010 | Y=A-B | 减 | 62 | 63 | 对于 Extend 模块(用于统一进行符号扩展或移位操作),`ExtOp` 功能表如下: 64 | 65 | | `ExtOp[1:0]` | 功能 | 指令 | 66 | | :----------: | :-------------------------------: | :----: | 67 | | 00 | Default | 缺省值 | 68 | | 01 | 将 16 位立即数 imm 向左移位 16 位 | LUI | 69 | | 10 | 立即数 imm 有符号扩展至 32 位 | ADDIU | 70 | | 11 | 立即数 offset 无符号扩展至 32 位 | LW、SW | 71 | 72 | 对于 `RegSrc` 信号,我们需要选择写入寄存器的源: 73 | 74 | | `RegSrc[1:0]` | 功能 | 指令 | 75 | | :-----------: | :------------------: | :-------------: | 76 | | 00 | Default | 缺省值 | 77 | | 01 | 来自 ALU | ADDIU、ADD、SUB | 78 | | 10 | 来自 Data Memory | LW | 79 | | 11 | 来自 Extend 模块输出 | LUI | 80 | 81 | 对于 NPC 模块,`NPCOp` 信号需要决定我们执行正常下一条取值(PC + 4)、BEQ 跳转或 J 跳转: 82 | 83 | | `NPCOp[2:0]` | 功能 | 指令 | 84 | | :----------: | :----------: | :------: | 85 | | 000 | Default | 缺省值 | 86 | | 001 | 普通跳转 | 正常指令 | 87 | | 010 | J 型直接跳转 | J | 88 | | 011 | BEQ 型跳转 | BEQ | 89 | -------------------------------------------------------------------------------- /docs/2_SingleCycle/2-3_Verilog.md: -------------------------------------------------------------------------------- 1 | # 单周期 CPU 的具体代码实现 2 | 3 | ![](https://i.loli.net/2019/09/02/8jenxBwHP2vOk3C.png) 4 | 5 | 单周期 CPU 的源代码开源于:[spencerwooo/single-cycle-processor](https://github.com/spencerwooo/single-cycle-processor) 6 | 7 | ## 项目结构 8 | 9 | 项目具体结构如下所示: 10 | 11 | ``` 12 | . 13 | ├── LICENSE 14 | ├── README.md 15 | ├── single-cycle-cpu.cache 16 | ├── single-cycle-cpu.hw 17 | ├── single-cycle-cpu.ip_user_files 18 | ├── single-cycle-cpu.runs 19 | ├── single-cycle-cpu.sim 20 | ├── single-cycle-cpu.srcs 21 | │   ├── constrs_1 22 | │   │   └── new 23 | │   ├── sim_1 24 | │   │   └── new 25 | │   │   └── testbench.v 26 | │   └── sources_1 27 | │   └── new 28 | │   ├── alu.v 29 | │   ├── control_unit.v 30 | │   ├── data_memory.v 31 | │   ├── extend.v 32 | │   ├── instruction_head.v 33 | │   ├── instruction_memory.v 34 | │   ├── mux.v 35 | │   ├── npc.v 36 | │   ├── pc.v 37 | │   ├── register_file.v 38 | │   └── top.v 39 | ├── single-cycle-cpu.tbcode 40 | │   ├── data_memory.txt 41 | │   ├── instructions.txt 42 | │   └── register.txt 43 | └── single-cycle-cpu.xpr 44 | 45 | 29 directories, 80 files 46 | ``` 47 | 48 | 可以看到,在 `single-cycle-cpu.srcs` 文件夹下就是全部的源代码,其中 `sources_1` 中是 CPU 的实现代码、`sim_1` 中是 Testbench 仿真激励文件。 49 | 50 | 在 `single-cycle-cpu.tbcode` 中,是我们的指令、GPR 通用寄存器以及 Data Memory 数据存储器初始化文件。具体功能见:[单周期 CPU 的行为仿真 - 添加仿真激励文件](./2-4_Testbench.md#添加仿真激励文件). 51 | 52 | 其余文件就是项目的编译中间文件,或说明文档等。 53 | 54 | ## 模块调用 55 | 56 | 在撰写完成我们的全体模块之后,需要通过一个顶层模块来将我们的全部模块进行连接起来,即模块的整体调用。我们在项目中定义一个顶层模块 `top.v`,并设置输入信号: 57 | 58 | ```verilog 59 | module top( 60 | input wire clk, 61 | input wire rst 62 | ); 63 | // ... 64 | endmodule 65 | ``` 66 | 67 | 同时,我们再声明顶端模块的 **两个内部端口**: 68 | 69 | ```verilog 70 | // Instruction fetch module i/o 71 | wire[31:0] pc; 72 | wire[31:0] npc; 73 | ``` 74 | 75 | 之后,比如我们需要调用 PC 模块,那么就可直接: 76 | 77 | ```verilog 78 | // Instruction fetch modules: PC, NPC and Instruction_Memory 79 | pc ZAN_PC(.clk(clk), 80 | .rst(rst), 81 | .npc(npc), 82 | .pc(pc)); 83 | ``` 84 | 85 | 其中前面的 `pc` 跟定义 PC 模块的 `pc.v` 保持一致,后面的 `ZAN_PC` 为我们当前文件调用模块名。在内部声明模块 I/O 端口时,我们通过 `.调用模块端口(顶端模块端口)` 的语法格式进行调用。 86 | 87 | 顶端模块的功能就是将其余模块利用 `wire` 导线进行连接,因此在顶端模块内部,我们会定义用于连接各个模块输入输出的内部端口。这样我们就能让全部模块连接起来,成为完整的 CPU 电路。 88 | 89 | ## 需要注意的要点 90 | 91 | ### 指令存储器的 I/O 声明 92 | 93 | 取指令时我们需要在指令存储器中将输入 PC 变量初始化为 `wire[11:2]`: 94 | 95 | ```verilog 96 | /* Module: Instruction Memory 97 | */ 98 | 99 | module instruction_memory( 100 | // PC address (address for instruction) 101 | input wire[11:2] pc_addr, 102 | 103 | output wire[31:0] instruction 104 | ); 105 | 106 | //... 107 | endmodule 108 | ``` 109 | 110 | 同时,在顶端模块中调用指令存储器时也需要这样声明: 111 | 112 | ```verilog 113 | instruction_memory ZAN_INSTR_MEM(.pc_addr(pc[11:2]), 114 | .instruction(instruction)); 115 | ``` 116 | 117 | ### 数据存储器的 I/O 声明 118 | 119 | 与指令存储器同理,对于数据存储器,在声明模块时: 120 | 121 | ```verilog 122 | /* Module: Data Memory 123 | */ 124 | 125 | module data_memory( 126 | input wire clk, 127 | input wire[11:2] mem_addr, // Data memory target address 128 | 129 | // ... 130 | ); 131 | // ... 132 | endmodule 133 | ``` 134 | 同时,顶端模块中调用数据存储器时: 135 | 136 | ```verilog 137 | // Module: Data Memory 138 | data_memory ZAN_DATA_MEM(.clk(clk), 139 | .mem_write(mem_write), 140 | .mem_addr(alu_result[11:2]), 141 | .write_mem_data(reg2_data), 142 | .read_mem_data(read_mem_data)); 143 | ``` 144 | -------------------------------------------------------------------------------- /docs/2_SingleCycle/2-4_Testbench.md: -------------------------------------------------------------------------------- 1 | # 单周期 CPU 的行为仿真 2 | 3 | Testbench 是一种验证的手段,可以看做模拟实际环境的输入激励和输出校验的一种“虚拟平台”。在这个平台上可以对设计从软件层面上进行分析和校验,类似于一个激励的产生器。 4 | 5 | ## 添加仿真激励文件 6 | 7 | 在 Vivado 文件树窗口上,点击 Add Sources,并选择 Add or create simulation sources: 8 | 9 | ![](https://i.loli.net/2019/09/02/gRXoulDAbhJpsfU.png) 10 | 11 | 点击 Create File 并设置文件名为 testbench,接下来一直选择下一步直到创建完成。 12 | 13 | ![](https://i.loli.net/2019/09/02/rxG2Kq3lCiR9Zpn.png) 14 | 15 | 在文件树上,我们在 Simulation Sources 下可以找到我们刚刚创建的仿真激励文件 `testbench.v`: 16 | 17 | ![](https://i.loli.net/2019/09/02/ThW6dMqiC5sH9Aw.png) 18 | 19 | 接下来,我们进行仿真激励文件的代码撰写。这里我们仍然使用 Verilog 语言,下面是一个示范。 20 | 21 | ```verilog 22 | `timescale 1ns / 1ps 23 | 24 | /* 25 | * Testbench 26 | */ 27 | 28 | module testbench(); 29 | reg clk; 30 | reg rst; 31 | top ZAN_TOP(clk, rst); 32 | 33 | initial begin 34 | // Load instructions 35 | $readmemh("../../../single-cycle-cpu.tbcode/instructions.txt", ZAN_TOP.ZAN_INSTR_MEM.im); 36 | // Load register initial values 37 | $readmemh("../../../single-cycle-cpu.tbcode/register.txt", ZAN_TOP.ZAN_REG_FILE.gpr); 38 | // Load memory data initial values 39 | $readmemh("../../../single-cycle-cpu.tbcode/data_memory.txt", ZAN_TOP.ZAN_DATA_MEM.dm); 40 | 41 | rst = 1; 42 | clk = 0; 43 | 44 | #30 rst = 0; // 30ns 时刻 CPU 开始运行 45 | #500 $stop; // 500ns 时刻 CPU 停止 46 | end 47 | 48 | always 49 | #20 clk = ~clk; // 每隔 20ns 时钟信号 clk 翻转一次 50 | endmodule 51 | ``` 52 | 53 | 与此同时,我们需要将指令存储器、GPR 寄存器以及数据存储器通过读入文件的方式进行初始化,也就是上方代码中三处 `$readmemh` 的工作。我们可以在另外一个文件夹下定义这三个文件,并在其中撰写指令、数据等。比如: 54 | 55 | - 在 `instructions.txt` 中初始化指令存储器: 56 | 57 | ``` 58 | 0x24010005 59 | 0x24020005 60 | ``` 61 | 62 | 其中,上面操作是初始化为以下两条指令: 63 | 64 | ```nasm 65 | addiu $1, $0, 5 66 | addiu $2, $0, 5 67 | ``` 68 | 69 | - 在 `register.txt` 中初始化 GPR 寄存器: 70 | 71 | ``` 72 | 0x00000000 73 | 0x00000000 74 | 0x00000000 75 | 0x00000000 76 | 0x00000000 77 | 0x00000000 78 | 0x00000000 79 | 0x00000000 80 | 0x00000000 81 | 0x00000000 82 | 0x00000000 83 | 0x00000000 84 | ``` 85 | 86 | - 在 `data_memory.txt` 中初始化数据寄存器: 87 | 88 | ``` 89 | 0x00000001 90 | 0x0000000f 91 | ``` 92 | 93 | ## 进行仿真测试 94 | 95 | 我们选择左侧菜单的 Run Simulation,并选择 Run Behavioral Simulation 进行行为仿真。 96 | 97 | ![](https://i.loli.net/2019/09/02/V2sNr8Gk9hqdFjZ.png) 98 | 99 | 在调整时间单位后可以观察到如下波形(点击放大镜增大或缩小时间单位,拖动黄色游标可以观察任意时刻信号的值)。 100 | 101 | ![](https://i.loli.net/2019/09/02/PXg2tShNECkIoO4.png) 102 | 103 | **同时在左侧,我们可以看到任意时间点的各个模块输入输出信号值,以及 CPU 中的寄存器值、数据存储器值等等,从而进行我们 CPU 的调试工作。** 104 | 105 | 由于个人项目的单周期 CPU 并不需要进行综合、实现以及烧入开发板,因此我们只需要在仿真之后确认我们的单周期 CPU 已经实现了全部指令与功能之后即可。 106 | 107 | 具体的测试项目集请参考:[spencerwooo/single-cycle-processor - README.md](https://github.com/spencerwooo/single-cycle-processor) 108 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-0_Instructions.md: -------------------------------------------------------------------------------- 1 | # 流水线 CPU 准备实现的指令 2 | 3 | 共 31 条 MIPS 指令,包含了大多数基本指令操作。 4 | 5 | ## ADDI 6 | 7 | ![](https://i.loli.net/2019/09/04/HgdI9fBOnvFTyz4.png) 8 | 9 | 将寄存器 rs 的值与有符号扩展至 32 位的立即数 imm 相加,结果写入 rt 寄存器中。如果产生溢出,则触发整型溢出例外(IntegerOverflow)。 10 | 11 | ## ADDIU 12 | 13 | ![](https://i.loli.net/2019/09/07/9kwaGSe37ntRV8O.png) 14 | 15 | 将寄存器 rs 的值与有符号扩展至 32 位的立即数 imm 相加,结果写入 rt 寄存器中。 16 | 17 | ## SLTIU 18 | 19 | ![](https://i.loli.net/2019/09/04/b3qOumSxyY1Ha46.png) 20 | 21 | 将寄存器 rs 的值与 **有符号扩展至 32 位的立即数 imm** 进行无符号数比较,如果寄存器 rs 中的值小,则寄存器 rt 置 1;否则寄存器 rt 置 0。 22 | 23 | ## ANDI 24 | 25 | ![](https://i.loli.net/2019/09/04/UKXJ75IOv6qGt34.png) 26 | 27 | 寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑与,结果写入寄存器 rt 中。 28 | 29 | ## ORI 30 | 31 | ![](https://i.loli.net/2019/09/04/sWTFh2ryUpXweGc.png) 32 | 33 | 寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑或,结果写入寄存器 rt 中。 34 | 35 | ## XORI 36 | 37 | ![](https://i.loli.net/2019/09/04/ZfXHP6SowubDIVE.png) 38 | 39 | 寄存器 rs 中的值与 0 扩展至 32 位的立即数 imm 按位逻辑异或,结果写入寄存器 rt 中。 40 | 41 | ## LUI 42 | 43 | ![](https://i.loli.net/2019/09/04/RaVt2qgHKzBm6Fb.png) 44 | 45 | 将 16 位立即数 imm 写入寄存器 rt 的高 16 位,寄存器 rt 的低 16 位置 0。 46 | 47 | ## ADD 48 | 49 | ![](https://i.loli.net/2019/09/04/JPe8jB6p1MHRh4u.png) 50 | 51 | 将寄存器 rs 的值与寄存器 rt 的值相加,结果写入寄存器 rd 中。如果产生溢出,则触发整型溢出例外(IntegerOverflow)。 52 | 53 | ## ADDU 54 | 55 | ![](https://i.loli.net/2019/09/04/MnNjyIgvkwOTpH9.png) 56 | 57 | 将寄存器 rs 的值与寄存器 rt 的值相加,结果写入 rd 寄存器中。 58 | 59 | ## SUB 60 | 61 | ![](https://i.loli.net/2019/09/04/53kCDcLPSIxtqls.png) 62 | 63 | 将寄存器 rs 的值与寄存器 rt 的值相减,结果写入 rd 寄存器中。如果产生溢出,则触发整型溢出例外(IntegerOverflow)。 64 | 65 | ## SUBU 66 | 67 | ![](https://i.loli.net/2019/09/04/F9lgLsoJayH8SwO.png) 68 | 69 | 将寄存器 rs 的值与寄存器 rt 的值相减,结果写入 rd 寄存器中。 70 | 71 | ## SLT 72 | 73 | ![](https://i.loli.net/2019/09/04/ZnJTe2yg9aUdAEY.png) 74 | 75 | 将寄存器 rs 的值与寄存器 rt 中的值进行有符号数比较,如果寄存器 rs 中的值小,则寄存器 rd 置 1;否则寄存器 rd 置 0。 76 | 77 | ## SLTU 78 | 79 | ![](https://i.loli.net/2019/09/04/ePGZXS8WHwaVyJm.png) 80 | 81 | 将寄存器 rs 的值与寄存器 rt 中的值进行无符号数比较,如果寄存器 rs 中的值小,则寄存器 rd 置 1;否则寄存器 rd 置 0。 82 | 83 | ## AND 84 | 85 | ![](https://i.loli.net/2019/09/04/FYEpNqKzU7VXyQo.png) 86 | 87 | 寄存器 rs 中的值与寄存器 rt 中的值按位逻辑与,结果写入寄存器 rd 中。 88 | 89 | ## OR 90 | 91 | ![](https://i.loli.net/2019/09/04/jwUKpFHhvYuSaQl.png) 92 | 93 | 寄存器 rs 中的值与寄存器 rt 中的值按位逻辑或,结果写入寄存器 rd 中。 94 | 95 | ## NOR 96 | 97 | ![](https://i.loli.net/2019/09/04/1MgQvpBA7r6yeOY.png) 98 | 99 | 寄存器 rs 中的值与寄存器 rt 中的值按位逻辑或非,结果写入寄存器 rd 中。 100 | 101 | ## XOR 102 | 103 | ![](https://i.loli.net/2019/09/04/IrKfFVwjWgAQMsZ.png) 104 | 105 | 寄存器 rs 中的值与寄存器 rt 中的值按位逻辑异或,结果写入寄存器 rd 中。 106 | 107 | ## SLL 108 | 109 | ![](https://i.loli.net/2019/09/04/qoTa3hD8LYtE6s5.png) 110 | 111 | 由立即数 sa 指定移位量,对寄存器 rt 的值进行逻辑左移,结果写入寄存器 rd 中。 112 | 113 | ## SRL 114 | 115 | ![](https://i.loli.net/2019/09/04/92GyI3TSvinDUZt.png) 116 | 117 | 由立即数 sa 指定移位量,对寄存器 rt 的值进行逻辑右移,结果写入寄存器 rd 中。 118 | 119 | ## SRA 120 | 121 | ![](https://i.loli.net/2019/09/04/ru36Rwl2vJEHygC.png) 122 | 123 | 由立即数 sa 指定移位量,对寄存器 rt 的值进行算术右移,结果写入寄存器 rd 中。 124 | 125 | ## SLLV 126 | 127 | ![](https://i.loli.net/2019/09/04/MKtd2BqLa3uNRWx.png) 128 | 129 | 由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行逻辑左移,结果写入寄存器 rd 中。 130 | 131 | ## SRLV 132 | 133 | ![](https://i.loli.net/2019/09/04/ZBz3twhUVR5PxHY.png) 134 | 135 | 由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行逻辑右移,结果写入寄存器 rd 中。 136 | 137 | ## SRAV 138 | 139 | ![](https://i.loli.net/2019/09/04/y1aU6Ts8hcNkLnZ.png) 140 | 141 | 由寄存器 rs 中的值指定移位量,对寄存器 rt 的值进行算术右移,结果写入寄存器 rd 中。 142 | 143 | ## LW 144 | 145 | ![](https://i.loli.net/2019/09/04/8G5KrfQwcIXHmq3.png) 146 | 147 | 将 base 寄存器的值加上符号扩展后的立即数 offset 得到访存的虚地址,如果地址不是 4 的 整数倍则触发地址错例外,否则据此虚地址从存储器中读取连续 4 个字节的值并进行符号 扩展,写入到 rt 寄存器中。 148 | 149 | ## SW 150 | 151 | ![](https://i.loli.net/2019/09/04/s2blrRkZdaN3iuI.png) 152 | 153 | 将 base 寄存器的值加上符号扩展后的立即数 offset 得到访存的虚地址,如果地址不是 4 的 整数倍则触发地址错例外,否则据此虚地址将 rt 寄存器存入存储器中。 154 | 155 | ## BEQ 156 | 157 | ![](https://i.loli.net/2019/09/04/YVQo8PJmhp3TXEb.png) 158 | 159 | 如果寄存器 rs 的值等于寄存器 rt 的值则转移,否则顺序执行。转移目标由立即数 offset 左 移 2 位并进行有符号扩展的值加上该分支指令对应的延迟槽指令的 PC 计算得到。 160 | 161 | ## BNE 162 | 163 | ![](https://i.loli.net/2019/09/04/X7kWZiUdYoIK169.png) 164 | 165 | 如果寄存器 rs 的值不等于寄存器 rt 的值则转移,否则顺序执行。转移目标由立即数 offset 左移 2 位并进行有符号扩展的值加上该分支指令对应的延迟槽指令的 PC 计算得到。 166 | 167 | ## J 168 | 169 | ![](https://i.loli.net/2019/09/04/1EsaZnvdS9rL4bH.png) 170 | 171 | 无条件跳转。跳转目标由该分支指令对应的延迟槽指令的 PC 的最高 4 位与立即数 instr_index 左移 2 位后的值拼接得到。 172 | 173 | ## JAL 174 | 175 | ![](https://i.loli.net/2019/09/04/zsogk4BLyxwFKPb.png) 176 | 177 | 无条件跳转。跳转目标由该分支指令对应的延迟槽指令的 PC 的最高 4 位与立即数 instr_index 左移 2 位后的值拼接得到。同时将该分支对应延迟槽指令之后的指令的 PC 值保存至第 31 号通用寄存器中。 178 | 179 | ## JR 180 | 181 | ![](https://i.loli.net/2019/09/04/5pOtUcfQ9nmjFC7.png) 182 | 183 | 无条件跳转。跳转目标为寄存器 rs 中的值。 184 | 185 | ## JALR 186 | 187 | ![](https://i.loli.net/2019/09/04/MkzjLU4Y3GmN6Ob.png) 188 | 189 | 无条件跳转。跳转目标为寄存器 rs 中的值。同时将该分支对应延迟槽指令之后的指令的 PC 值保存至寄存器 rd 中。 190 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-1_Basic.md: -------------------------------------------------------------------------------- 1 | # 流水线 CPU 的基础知识 2 | 3 | ## 以 LW 为例子对指令进行分解 4 | 5 | 指令 LW 是一个非常典型的,涉及到 CPU 执行的五个阶段的指令。CPU 一条指令执行过程中的五个大致阶段有: 6 | 7 | - IF - Instruction Fetch:取指令 8 | - ID - Instruction Decode:指令译码 9 | - EX - Execute:指令执行 10 | - MEM - Memory:访存 11 | - WB - Writeback:结果写回 12 | 13 | 那么对于 LW 来说,这五大阶段分别需要这样的工作: 14 | 15 | - IF:读取指令存储器 IM,获得指令 16 | - ID:对获取的指令进行译码,得知指令为 LW 17 | - EX:通过 ALU 计算得知取存储器的目标地址 18 | - MEM:读取数据存储器的目标地址,获得对应数据 19 | - WB:将取到的数据写回到目标寄存器 20 | 21 | ## CPU 指令执行的优化 22 | 23 | 为了更方便的看 LW 指令的执行过程,我们去掉 PC、NPC 等分支、跳转模块,只看涉及到的模块的数据通路: 24 | 25 | ![](https://i.loli.net/2019/09/03/UtW1kRGmuz4y37L.png) 26 | 27 | 可以看到,指令执行的整个过程中,许多硬件都没有发挥任何作用,处于闲置状态。我们可以通过 Pipelining 的方式来让 CPU 的硬件从时间角度更有效的发挥作用。比如: 28 | 29 | - 我们可以在 ID 模块当前指令译码的同时让 IF 模块直接加载下一条指令 30 | 31 | ![](https://i.loli.net/2019/09/03/LeEaH46pzdXNRni.png) 32 | 33 | - 当然,我们也可以在前一条指令到 EX 指令执行的阶段,将加载好的当前指令进行译码,同时再加载第三条指令 34 | 35 | ![](https://i.loli.net/2019/09/03/VXx4LCeTpWKNRGM.png) 36 | 37 | 等等。这也就是「流水线」CPU 的具体指令执行方式。 38 | 39 | ## 流水线 CPU 的设计 40 | 41 | 更为详细的说,流水线 CPU 在一条指令的执行过程中,会先将指令通过上面提到的五个基本模块进行处理: 42 | 43 | ![](https://i.loli.net/2019/09/03/XUk4Czw8367NeWu.png) 44 | 45 | 这样,利用「流水线」的思想,我们就可以将五个模块的执行过程进行组合,从时间的维度进行重叠,从而加速 CPU 指令执行的「吞吐量」,提高 CPU 执行指令的效率。 46 | 47 | ![](https://i.loli.net/2019/09/03/dMQ1C2tZYIBX6U9.png) 48 | 49 | ## 无关阶段的处理 50 | 51 | 但是,并不是所有的指令都会经历上面的五大阶段,比如 R-Type 指令只需要 IF、ID、EX、WB 这四个阶段,不需要经历访存的过程。然而,每个工作单元(模块)每条指令只能用一次。同时,为了避免冲突,对所有指令来说,**每个工作单元的使用必须在指令的相同阶段进行**。 52 | 53 | 比如,如果出现了下面的情况: 54 | 55 | - LW 指令在 WB 阶段(它的第五阶段)写入寄存器 56 | - R-Type 指令在 WB 阶段(他的第四阶段)同时写入寄存器 57 | 58 | 那么寄存器写入端口就炸了。┑( ̄Д  ̄)┍ 59 | 60 | ![](https://i.loli.net/2019/09/03/byFehnCfx5q7OZz.png) 61 | 62 | 为了避免这种情况的出现,我们需要**保证所有指令的执行过程都经历五个阶段**,如果一条指令不需要某一阶段的时候,我们认为为其加上 NOP 阶段,代表本阶段此指令什么都不干。 63 | 64 | ![](https://i.loli.net/2019/09/03/kp1i4eIqw2VmWba.png) 65 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-2_Datapath&Control.md: -------------------------------------------------------------------------------- 1 | # 数据通路与信号控制 2 | 3 | ## 对「单周期 CPU」数据通路的修改 4 | 5 | 流水线 CPU 的提高吞吐量的主要方法就是「让多条指令同时执行」。因此,我们需要在同一个时钟周期内进行多项操作,比如: 6 | 7 | - 同时让 PC 自增、与计算两个寄存器之和 8 | - 在取指令的同时执行另外一条指令的读写操作 9 | 10 | 因此,对于「流水线 CPU」来说,我们也需要复制硬件元件,来让同一个时钟周期中需要多次使用到的硬件能够同时运转。 11 | 12 | ### 寄存器堆 13 | 14 | 15 | 16 | 对于寄存器堆来说,我们只需要一个寄存器堆就能支持 ID 和 WB 阶段,因为「写入」和「读取」分别是通过寄存器堆的不同端口进行的;同时,「写入」操作只发生在一个时钟周期的前半段,而「读取」操作只发生在后半段。 17 | 18 | ### 流水线寄存器 19 | 20 | 流水线寄存器:Pipeline registers,用来控制整个数据通路中不同流水阶段里信号、数据的传递。五级流水分为 IF、ID、EX、MEM 和 WB 五个阶段,我们分别设置 IF/ID、ID/EX、EX/MEM、MEM/WB 这四个流水线寄存器,用来连接流水的五个阶段。 21 | 22 | 为了方便描述,我们将数据通路精简为如下图所示: 23 | 24 | ![](https://i.loli.net/2019/09/04/sZuUGLXpPh67qYM.png) 25 | 26 | ## 将数据经由流水线寄存器前递 27 | 28 | 在后面阶段所需要的数据必须经由流水线寄存器进行前递。比如 LW 指令需要将数据存储器中的数据写入目标寄存器中,目标寄存器最终写入的数据是由指令在第四阶段 MEM 才获取到的,而在第五阶段 WB 才写回。因此,rd(目标寄存器)必须经由全部四个流水线寄存器前递,如下图标红部分所示: 29 | 30 | ![](https://i.loli.net/2019/09/04/fuKn2cYEwiNJvGD.png) 31 | 32 | ## 将控制信号经由流水线寄存器前递 33 | 34 | 控制信号也需要进行前递。值得注意的是,控制信号的生成与单周期 CPU 中的生成过程一致:在取指令之后,ID 模块对指令译码并生成相应的控制信号。但是和之前情况一样的是,这些控制信号很多时候一直到指令执行的第五流水阶段才会用到,因此控制信号可以随着其他数据一同在流水线寄存器中进行前递。 35 | 36 | 我们根据流水线的阶段将控制信号进行分类: 37 | 38 | ![](https://i.loli.net/2019/09/04/t1v5WXGblO3QwaE.png) 39 | 40 | 这样我们就也可以将控制信号一同进行前递: 41 | 42 | ![](https://i.loli.net/2019/09/04/fe5nGVjH6cWOsXu.png) 43 | 44 | 事实上,到这里,如果我们不考虑各个流水周期之间输入输出的依赖,以及控制信号的传递问题,我们的流水线 CPU 以及基本完成了。但是,由于我们在流水线 CPU 运行的过程中会出现「同时执行多条指令」的过程,因此我们无法避免数据相关、控制信号冲突等问题,这些问题我们统称为 Hazards。接下来,我们就需要利用增加控制硬件的方法来对 Hazard 进行消除。 45 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-3_Hazards.md: -------------------------------------------------------------------------------- 1 | # Hazards 与其避免措施 2 | 3 | 在前文我们提到了:「流水线 CPU 由于会并行的执行多条指令,因此会产生数据、指令的相关性问题。」我们统称这些相关型问题为:Hazards。 4 | 5 | ## Data Hazard —— 数据冲突 6 | 7 | 以下面这几条指令为例: 8 | 9 | ```nasm 10 | sub $2, $1, $3 11 | and $12, $2, $5 12 | or $13, $6, $2 13 | add $14, $2, $2 14 | sw $15, 100($2) 15 | ``` 16 | 17 | 我们将其大致的五级流水执行过程画出来,并用箭头标注前后两条指令之间的数据相关(数据依赖): 18 | 19 | ![](https://i.loli.net/2019/09/05/S7cIyGTKs3NtRZ2.png) 20 | 21 | 可以看到,SUB 指令的结果是 AND 指令的输入,也是 OR 指令的输入;而 ADD 指令与 SW 指令的输入同样依赖 SUB 指令。其中,红线由前指向后,因此从时间流动的角度,红线就代表一处「数据冲突」,即 Data Hazard. 22 | 23 | ### 解决方法:数据前递 Data Forwarding 24 | 25 | 事实上,对 SUB 指令来说,其结果的产生是在其第三流水阶段:EX,也就是整个流水线 CPU 的第三个时钟周期。而对 SUB 之后的 AND 以及 OR 来说,寄存器 $2 的值是在它们的第三阶段 EX 需要的,也就是流水线 CPU 的第 4-5 个时钟周期。纵观整个流水过程,事实上 $2 的值是在第 3 个时钟周期产生,并在第 4-5 个时钟周期需要,因此我们只需要越过流水线五个阶段中 WB(WriteBack)的过程,让第 3 个时钟周期里面计算产生的 $2 寄存器值 **直接赋值** 给第 4-5 个时钟周期指令 ADD 以及 OR 指令即可。这种避免「Data Hazard」的方法叫做:数据前递(Data Forwarding)。 26 | 27 | 我们新增一个硬件元件:Forwarding Unit(前递组件)专门用来处理数据的前递,在出现 Data Hazard 的时候将 ALU 的输出赋予给正确的输入。下图中的 ForwardA 与 ForwardB 就是一个简单的例子: 28 | 29 | ![](https://i.loli.net/2019/09/05/oqBmp5nKeb69TIr.png) 30 | 31 | ### 判断出现 Data Hazard 的情况 32 | 33 | #### EX/MEM 类型的 Data Hazard 34 | 35 | 我们在上面例子中出现的 Data Hazard 事实上是 EX/MEM 过程的 Data Hazard,如何让硬件知道接下来的指令会触发 Data Hazard 是一个亟需解决的问题。 36 | 37 | 首先,EX/MEM Data Hazard 会出现在: 38 | 39 | - 当前指令(比如下图中的 AND 指令)在 EX 阶段,且: 40 | - 上一条指令(比如下图中的 SUB 指令)会写入寄存器堆(Register File,也就是下图的 $2),且: 41 | - 上一条指令的写入地址是当前指令 EX 阶段中 ALU 输入寄存器(也就是下图的 $2)的一个 42 | 43 | ![](https://i.loli.net/2019/09/05/Exm6YFIPhQJkA8M.png) 44 | 45 | 我们利用类似「类」的语法来描述流水线寄存器中的数据,比如 `ID/EX.RegisterRt` 就表示 ID/EX 流水线寄存器中的 rt 寄存器值。那么,EX/MEM Data Hazard 的触发条件就是: 46 | 47 | ``` 48 | // ALU 第一个操作数 49 | if (EX/MEM.RegWrite = 1 && 50 | EX/MEM.RegisterRd == ID/EX.RegisterRs) { 51 | ForwardA = 2 52 | } 53 | 54 | // ALU 第二个操作数 55 | if (EX/MEM.RegWrite = 1 && 56 | ID/EX.RegisterRd == ID/EX.RegisterRt) { 57 | ForwardB = 2 58 | } 59 | ``` 60 | 61 | #### MEM/WB 类型的 Data Hazard 62 | 63 | 第二种会出现的 Data Hazard 就是 MEM/WB 类型的 Data Hazard. 64 | 65 | MEM/WB Data Hazard 会出现在当前指令处于 EX 阶段,而两个时钟周期之前的指令将同一个寄存器同时更新了两次,比如: 66 | 67 | ```nasm 68 | add $1, $2, $3 69 | add $1, $1, $4 70 | sub $5, $5, $1 71 | ``` 72 | 73 | ![](https://i.loli.net/2019/09/05/zCkNWR4BXe1lrnj.png) 74 | 75 | 可以看到,上一条指令以及上上条指令都更新了寄存器 $1,但是只有最新的结果(也就是第二条 ADD 指令的结果)才需要被前递。因此,我们需要进行如下的处理: 76 | 77 | ``` 78 | // ALU 第一个操作数 79 | if (MEM/WB.RegWrite == 1 && 80 | MEM/WB.RegisterRd == ID/EX.RegisterRs && 81 | (EX/MEM.RegisterRd != ID/EX.RegisterRs || EX/MEM.RegWrite == 0)) { 82 | ForwardA = 1 83 | } 84 | 85 | // ALU 第二个操作数 86 | if (MEM/WB.RegWrite == 1 && 87 | MEM/WB.RegisterRd == ID/EX.RegisterRt && 88 | (EX/MEM.RegisterRd != ID/EX.RegisterRt || EX/MEM.RegWrite == 0)) { 89 | ForwardB = 1 90 | } 91 | ``` 92 | 93 | ### 新增了 Forwarding Unit 的数据通路 94 | 95 | ![](https://i.loli.net/2019/09/05/siONeVDZqrkAW1B.png) 96 | 97 | 增加了 Forwarding Unit 的五级流水 CPU 已经能够处理算术运算中涉及到的 Data Hazard,但是对于 LW、SW 等涉及到存储、获取数据存储器中「字」的数据,还是会有尚未解决的问题。 98 | 99 | 事实上,MIPS 指令集中的每一条指令至多只写入一个寄存器,这让我们的 Forwarding 工作非常简单,只需要处理一个寄存器数据的前递即可。 100 | 101 | ## Data Hazard —— 访存冲突 102 | 103 | 虽然前面介绍的方法规避了 ALU 算术运算的数据冲突,但是对于需要访问数据存储器的指令(比如 LW)来说,我们并不能规避类似下面的指令带来的数据冲突: 104 | 105 | ```nasm 106 | lw $2, 20($3) 107 | and $12, $2, $5 108 | ``` 109 | 110 | ![](https://i.loli.net/2019/09/05/h7iTvVH8frkQZ15.png) 111 | 112 | ### 解决方法 1:Stalling and forwarding 113 | 114 | ![](https://i.loli.net/2019/09/05/nPhSAeL1zVIYgN2.png) 115 | 116 | 事实上,在 LW 指令的 MEM 阶段,我们就获得了相应的数据,那么,对于下一条 AND 指令,我们只需要在其 EX 阶段前将流水线 Stall 住一个时钟周期,即可将 Data Memory 的数据前递至正确的地方。这种解决方法也叫向流水线中引入一个 bubble。 117 | 118 | 但是,在现实世界中,几乎所有的指令与指令直接都存在或多或少的访存冲突,而如果我们像上面介绍的向流水线中引入 bubble,那么我们会给整个流水线造成不小的性能问题。也就是:Stalling delays the entire pipeline. 119 | 120 | ### 解决方法 2:`Stall <=> NOP` 转换 121 | 122 | ![](https://i.loli.net/2019/09/05/YfqB3K8wPShLxau.png) 123 | 124 | 第二种更为合理的解决方法就是「引入 NOP(No Operation)指令」,比如上面介绍的例子中,指令 LW 之后的 ADD 只需要延迟一时钟周期再执行即可。于是,我们在原有的 ADD 指令之前插入一个 NOP 指令,表示这一部分不执行任何指令。这样,我们就可以在不大影响流水线 CPU 整体性能的前提下处理访存冲突。 125 | 126 | ### 检测访存冲突 127 | 128 | 对于「访存冲突」,我们也需要进行硬件层级的检测,以便通过 Hazard Unit 来对其进行处理。 129 | 130 | ## Control Hazard 131 | 132 | 在处理指令的跳转时,我们对「是否跳转」以及「跳转目标」的判断大多都是在 EX 阶段进行的: 133 | 134 | - 计算跳转目标地址 135 | - 比较源寄存器的大小关系以及计算 Zero 控制信号 136 | 137 | 因此对跳转的判断大多情况下都需要在 EX 阶段才能得出结果。但是,我们在流水线 CPU 中需要知道下一条指令取哪一条,这样流水线才能顺利的顺序执行。这种情况下,我们就会遇到 Control Hazard 的问题。 138 | 139 | ![](https://i.loli.net/2019/09/05/BCJoALHxTUP8fg2.png) 140 | 141 | ### 解决方法 1:Stalling 142 | 143 | ![](https://i.loli.net/2019/09/05/dVKhAWnNJmybiYg.png) 144 | 145 | Stalling 永远都是一种解决方法,我们完全可以让跳转指令停下来,等待结果的出现,再继续执行。但是随之而来的就是性能的下降问题。因此这种方法并非最为优雅的解决方案。 146 | 147 | ### 解决方法 2:Branch Prediction 分支预测 148 | 149 | 第二种更为优雅的解决方法是「分支预测」。我们需要在硬件层面去预测「跳转指令」是否会被执行,然后按照预测取下一条指令。如果预测失败,那么我们就需要将错误路线上面的指令 Flush 掉,去重新加载正确的指令。 150 | 151 | Branch Prediction 相对比较复杂,我们在下一部分进行更为具体的介绍。 152 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-4_BranchPrediction.md: -------------------------------------------------------------------------------- 1 | # 分支预测 Branch Prediction 2 | 3 | ## 最简单的分支预测手段 4 | 5 | 最为简单的分支预测手段就是「假装全部分支都不会执行」,从硬件角度来说,这种「分支预测」的实现手段最为简单,因为我们只需要直接让 PC 进行自增,获取下一条指令即可,不需要做任何改动。 6 | 7 | ![](https://i.loli.net/2019/09/05/gyHjoaWe15DdSNc.png) 8 | 9 | 但是如果我们预测如果 **出现了偏差**,那我们可 **需要负责** 的!我们需要将已经开始执行的指令流水 Flush 掉,然后重新开始正确指令分支的执行。 10 | 11 | ![](https://i.loli.net/2019/09/05/9kx8chBWg7Yzisy.png) 12 | 13 | ## Flushing 剪枝 14 | 15 | 由于我们指令集中 BEQ 指令的判断实际上非常简单,因此我们完全可以直接将这部分判断加入 ID 阶段,在源寄存器的值读取之后直接比较二者大小即可。 16 | 17 | 这样以来,如果我们「预测失败」,我们只需要 Flush 掉一条指令。 18 | 19 | ![](https://i.loli.net/2019/09/05/iVO5RezkrLPCmax.png) 20 | 21 | 对于 BEQ 指令来说,如果 BEQ 的 rs 和 rt 是相等的,那么我们只需要将前面取来的一条指令 Flush 掉即可。我们可以 IF 阶段将需要 Flush 掉的指令用一条 NOP 指令替换,来取消这一指令。 22 | 23 | MIPS 在实际实现过程中往往使用的是下面的指令作为 NOP: 24 | 25 | ```nasm 26 | sll $0, $0, $0 27 | ``` 28 | 29 | Flushing 的过程会向流水线中引入一个 bubble,代表我们这一条流水线会引入一个时钟周期的延迟。 30 | 31 | 引入 Flushing 的数据通路大致如下: 32 | 33 | ![](https://i.loli.net/2019/09/05/RW1fnruq5Yye3ol.png) 34 | 35 | 总体来说,我们流水线 CPU 的 Hazards 就有这些,包括: 36 | 37 | - Structural Hazards:由于数据通路设计问题与硬件问题导致出现结构性冲突,可以通过增加硬件的方式来解决(设计完善的数据通路不会遇到类似问题,因此我们没有介绍。) 38 | - Data Hazards:由于指令需要尚未更新的寄存器值而出现的数据冲突。对 R-Type 指令出现的 Data Hazards 我们可以通过「数据前递」来解决;对于 Load 数据寄存器中数据出现的 hazard,我们只能 Stall 整条流水线,通过延迟进行处理 39 | - Control Hazards:当 CPU 无法判断下一条指令取哪条好的时候出现。我们可以通过将判断步骤提前,来减少流水线的延迟;我们也可以通过「分支预测」来提高分支利用率,让正确的预测节省 CPU 的处理时间 40 | 41 | 到这里,我们就可以开始着手设计五级流水线 CPU 了。( •̀ ω •́ )y 42 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-5_Design.md: -------------------------------------------------------------------------------- 1 | # 流水线 CPU 的设计 2 | 3 | 流水线 CPU 的代码已经开源于:[zan-pu/pipelined-zanpu](https://github.com/zan-pu/pipelined-zanpu) 4 | 5 | ## 五级流水线 CPU 的数据通路 6 | 7 | ![pipeline new-pipeline.png](https://i.loli.net/2019/09/11/12y9m6sYFhKB74A.png) 8 | 9 | 其中: 10 | 11 | - 流水寄存器包括: 12 | - IF/ID 寄存器 13 | - ID/EX 寄存器 14 | - EX/MEM 寄存器 15 | - MEM/WB 寄存器 16 | - Forwarding Unit 前递单元位于 EX 执行阶段,根据写信号 `MEM.RegWrite`、`WB.RegWrite` 以及写寄存器地址、rs 寄存器地址和 rt 寄存器地址判断是否需要进行数据前递,进行 Data Hazard 的解决 17 | - Stall Unit 优化暂停单元控制各个流水寄存器的流通,保证 LW、SW 等读写寄存器指令能够完整执行结束再执行后续指令,以保证访存冲突的解决 18 | 19 | ## 部分控制信号对应的真值表 20 | 21 | | | RegDst | RegWrite | ALUSrc | ALUOp | MemWrite | RegSrc | ExtOp | NPCOp | Zero | 22 | |-------|--------|----------|--------|-------|----------|---------|----------|-------|------| 23 | | LUI | RT | 1 | × | × | × | IMM | SFT16 | NEXT | × | 24 | | ADDI | RT | 1 | 1 | ADD | × | ALU | SIGNED | NEXT | × | 25 | | ADDIU | RT | 1 | 1 | ADD | × | ALU | SIGNED | NEXT | × | 26 | | SLTIU | RT | 1 | 1 | SLT | × | ALU | SIGNED | NEXT | × | 27 | | ANDI | RT | 1 | 1 | AND | × | ALU | UNSIGNED | NEXT | × | 28 | | ORI | RT | 1 | 1 | OR | × | ALU | UNSIGNED | NEXT | × | 29 | | XORI | RT | 1 | 1 | XOR | × | ALU | UNSIGNED | NEXT | × | 30 | | ADD | RD | 1 | 0 | ADD | × | ALU | × | NEXT | × | 31 | | ADDU | RD | 1 | 0 | ADD | × | ALU | × | NEXT | × | 32 | | SUB | RD | 1 | 0 | SUB | × | ALU | × | NEXT | × | 33 | | SUBU | RD | 1 | 0 | SUB | × | ALU | × | NEXT | × | 34 | | SLT | RD | 1 | 0 | SLT | × | ALU | × | NEXT | × | 35 | | SLTU | RD | 1 | 0 | SLT | × | ALU | × | NEXT | × | 36 | | AND | RD | 1 | 0 | AND | × | ALU | × | NEXT | × | 37 | | OR | RD | 1 | 0 | OR | × | ALU | × | NEXT | × | 38 | | NOR | RD | 1 | 0 | NOR | × | ALU | × | NEXT | × | 39 | | XOR | RD | 1 | 0 | XOR | × | ALU | × | NEXT | × | 40 | | SLL | RD | 1 | 0 | SLL | × | ALU | × | NEXT | × | 41 | | SRL | RD | 1 | 0 | SRL | × | ALU | × | NEXT | × | 42 | | SRA | RD | 1 | 0 | SRA | × | ALU | × | NEXT | × | 43 | | SLLV | RD | 1 | 0 | SLLV | × | ALU | × | NEXT | × | 44 | | SRLV | RD | 1 | 0 | SRLV | × | ALU | × | NEXT | × | 45 | | SRAV | RD | 1 | 0 | SRAV | × | ALU | × | NEXT | × | 46 | | LW | RT | 1 | 1 | ADD | × | MEM | UNSIGNED | NEXT | × | 47 | | SW | × | 0 | 1 | ADD | 1 | × | UNSIGNED | NEXT | × | 48 | | BEQ | × | × | 0 | × | × | × | × | NEXT | 0 | 49 | | BEQ | × | 0 | × | × | × | × | OFFSET | 1 | | 50 | | BNE | × | × | 0 | × | × | × | × | NEXT | 1 | 51 | | BNE | × | 0 | × | × | × | × | OFFSET | 0 | | 52 | | J | × | × | × | × | × | × | × | JUMP | × | 53 | | JAL | REG_31 | 1 | × | × | × | JMP_DST | × | JUMP | × | 54 | | JR | × | × | × | × | × | × | × | RS | × | 55 | | JALR | RD | 1 | × | × | × | JMP_DST | × | RS | × | 56 | 57 | 由于 Forwarding Unit 以及 Stall Unit 涉及到的控制信号较多,因此不在这里列出。 58 | 59 | ## 控制信号 60 | 61 | 所有控制信号的定义全部在 [definitions.vh](https://github.com/zan-pu/pipelined-zanpu/blob/master/pipelined-zanpu.srcs/sources_1/new/definitions.vh) 中声明。 62 | 63 | ```verilog 64 | /* --- Control Signals --- */ 65 | 66 | // Register Write EN 67 | `define REG_WRITE_EN 1'b1 // Enable register write 68 | `define REG_WRITE_DIS 1'b0 // Disable register write 69 | 70 | // ExtOp Control Signals 71 | `define EXT_OP_LENGTH 2 // Length of Signal ExtOp 72 | `define EXT_OP_DEFAULT 2'b00 // ExtOp default value 73 | `define EXT_OP_SFT16 2'b01 // LUI: Shift Left 16 74 | `define EXT_OP_SIGNED 2'b10 // ADDIU: `imm16` signed extended to 32 bit 75 | `define EXT_OP_UNSIGNED 2'b11 // LW, SW: `imm16` unsigned extended to 32 bit 76 | 77 | // ALUSrc Control Signals 78 | `define ALU_SRC_REG 1'b0 // ALU source: register file 79 | `define ALU_SRC_IMM 1'b1 // ALU Source: immediate 80 | 81 | // ALU Control Signals 82 | `define ALU_OP_LENGTH 4 // Length of signal ALUOp 83 | `define ALU_OP_DEFAULT 4'b0000 // ALUOp default value 84 | `define ALU_OP_ADD 4'b0001 // ALU add 85 | `define ALU_OP_SUB 4'b0010 // ALU sub 86 | `define ALU_OP_SLT 4'b0011 // ALU slt 87 | `define ALU_OP_AND 4'b0100 // ALU and 88 | `define ALU_OP_OR 4'b0101 // ALU or 89 | `define ALU_OP_XOR 4'b0110 // ALU xor 90 | `define ALU_OP_NOR 4'b0111 // ALU nor 91 | `define ALU_OP_SLL 4'b1000 // ALU sll, with respect to sa 92 | `define ALU_OP_SRL 4'b1001 // ALU srl, with respect to sa 93 | `define ALU_OP_SRA 4'b1010 // ALU sra, with respect to sa 94 | `define ALU_OP_SLLV 4'b1011 // ALU sllv, with respect to rs 95 | `define ALU_OP_SRLV 4'b1100 // ALU srlv, with respect to rs 96 | `define ALU_OP_SRAV 4'b1101 // ALU srav, with respect to rs 97 | 98 | `define OVERFLOW_TRUE 1'b1 99 | `define OVERFLOW_FALSE 1'b0 100 | 101 | // Memory Write EN 102 | `define MEM_WRITE_EN 1'b1 // Enable memory write 103 | `define MEM_WRITE_DIS 1'b0 // Disable memory write 104 | 105 | // RegSrc Control Signals 106 | `define REG_SRC_LENGTH 3 // Length of signal RegSrc 107 | `define REG_SRC_DEFAULT 3'b000 // Register default value 108 | `define REG_SRC_ALU 3'b001 // Register write source: ALU 109 | `define REG_SRC_MEM 3'b010 // Register write source: Data Memory 110 | `define REG_SRC_IMM 3'b011 // Register write source: Extended immediate 111 | `define REG_SRC_JMP_DST 3'b100 // Register write source: Jump destination 112 | 113 | // RegDst Control Signals 114 | `define REG_DST_LENGTH 2 115 | `define REG_DST_DEFAULT 2'b00 // Register write destination: default 116 | `define REG_DST_RT 2'b01 // Register write destination: rt 117 | `define REG_DST_RD 2'b10 // Register write destination: rd 118 | `define REG_DST_REG_31 2'b11 // Register write destination: 31 bit gpr 119 | 120 | // NPCOp Control Signals 121 | `define NPC_OP_LENGTH 3 // Length of NPCOp 122 | `define NPC_OP_DEFAULT 3'b000 // NPCOp default value 123 | `define NPC_OP_NEXT 3'b001 // Next instruction: {PC + 4} 124 | `define NPC_OP_JUMP 3'b010 // Next instruction: {PC[31:28], instr_index, 2'b00} 125 | `define NPC_OP_OFFSET 3'b011 // Next instruction: {PC + 4 + offset} 126 | `define NPC_OP_RS 3'b100 // Next instruction: {rs} 127 | 128 | // Branching signals 129 | `define BRANCH_TRUE 1'b1 // Branch to true 130 | `define BRANCH_FALSE 1'b0 // Branch to false 131 | ``` 132 | 133 | ## 前递单元 Forwarding Unit 和其他数据冲突的控制信号定义 134 | 135 | ```verilog 136 | /* --- Hazard Control --- */ 137 | 138 | // Forward Control Signals 139 | `define FORWARD_ONE_CYCLE 2'b10 140 | `define FORWARD_TWO_CYCLE 2'b01 141 | 142 | // Stall IN Signals 143 | `define EXE_REGW 2'b01 144 | `define MEM_REGW 2'b10 145 | `define NON_REGW 2'b00 146 | 147 | // Stall Control Signals 148 | `define EXE_STALL 4'b0111 149 | `define MEM_STALL 4'b1111 150 | `define NON_STALL 4'b0000 151 | 152 | // LW init 153 | `define EN_LW_DEFAULT 1'b0 154 | ``` 155 | -------------------------------------------------------------------------------- /docs/3_Pipelining/3-6_IP.md: -------------------------------------------------------------------------------- 1 | # 利用 IP 核对 CPU 进行封装 2 | 3 | [IP 核](https://china.xilinx.com/products/intellectual-property.html)(知识产权核):是那些己验证的、可重利用的、具有某种确定功能的 IC 模块。为了让我们实现的 CPU 能够在开发板上面进行输出,我们需要将在测试过程中输入的几个模块进行 IP 核封装: 4 | 5 | - 指令存储器 Instruction Memory 6 | - 数据存储器 Data Memory 7 | 8 | 这样,我们就可以将 CPU 进行综合、实现以及烧入开发板,从而进行外设的控制,实现 CPU 的真正功能。 9 | 10 | ## 封装指令存储器 Instruction Memory 11 | 12 | 指令存储器的工作主要就是根据 PC 输入的指令地址,输出相对应的指令。在我们自己的实现过程中,我们是这样设计指令存储器的: 13 | 14 | ![](https://i.loli.net/2019/09/11/sVtQyuGh4nCpkLw.png) 15 | 16 | 其中输入的 `wire[11:2] PC` 就是取指地址,输出的就是指令本身,十六进制的 `wire[31:0] instruction`。具体的代码实现如下: 17 | 18 | ```verilog 19 | module instruction_memory( 20 | input wire[11:2] instruction_addr, // PC fetch instruction address 21 | 22 | output wire[31:0] instruction // IM fetch instruction from register 23 | ); 24 | 25 | reg[31:0] im[`IM_LENGTH:0]; 26 | assign instruction = im[instruction_addr]; 27 | endmodule 28 | ``` 29 | 30 | 在 Vivado 中,我们可以将这部分直接利用 IP 核进行生成,从而将指令(也就是 MIPS 汇编利用 MARS 编译出的机器码)直接写入 Instruction Memory 中,烧入开发板,实现在开发板上运行我们自己实现的指令的功能。 31 | 32 | 我们找到左侧边栏的 IP Catalog,点击,搜索「Distributed Memory Generator」,然后双击它: 33 | 34 | ![](https://i.loli.net/2019/09/11/2U8GNW7jMLtYTuZ.png) 35 | 36 | 在弹出的 IP 核自定义窗口中,我们定义 IP 核名称为 `instruction_mem`,并选择: 37 | 38 | - 位深度为 1024 39 | - 位宽为 32 位 40 | - 存储器种类为 ROM(只读) 41 | - 并加载 RST & Initialization 中的 coe 文件 42 | 43 | ![](https://i.loli.net/2019/09/11/bdgu3DmlZCTjq7p.png) 44 | 45 | 其中,coe 文件是一个 IP 核读取初始化信息(比如要运行指令、要初始化数据存储器)的文件,文件形如 `instruction.coe`。我们就在 coe 文件中加载我们准备执行的指令。比如: 46 | 47 | ``` 48 | memory_initialization_radix = 16; 49 | memory_initialization_vector = 50 | 3c013010 51 | 52 | ... // 16 进制机器码 53 | ``` 54 | 55 | 之后,我们在 IP Sources 中找到我们刚刚生成的 IP 核,点击 Instantiation Template,找到 `instruction_mem.veo`,在其中,我们可以找到初始化我们的指令存储器模块的代码: 56 | 57 | ![](https://i.loli.net/2019/09/11/eGZSsv9Bfxi1y2c.png) 58 | 59 | 我们回到我们 CPU 的顶层模块,在其中按照上面的格式进行初始化: 60 | 61 | ```verilog 62 | instruction_mem u_instruction_mem ( 63 | .a (pc[11:2] ), // input wire [9 : 0] a 64 | .spo (instruction) // output wire [31 : 0] spo 65 | ); 66 | ``` 67 | 68 | 和我们之前自己实现的指令存储器进行对比: 69 | 70 | ```verilog 71 | // 自己实现的指令存储器初始化 72 | instruction_memory u_instruction_memory( 73 | .instruction_addr (pc[11:2] ), 74 | .instruction (instruction ) 75 | ); 76 | ``` 77 | 78 | 可以发现,前后两次的模块调用代码几乎一致,因此我们并不需要太大改动就能将 IP 核接入我们自己实现的 CPU 中。 79 | 80 | ## 封装数据存储器 Data Memory 81 | 82 | 利用 IP 核封装数据存储器和指令存储器的步骤一致,我们只需要做下面几点改动: 83 | 84 | - 修改 IP 核名称为 `data_mem` 85 | - 修改存储器种类为 Single Port RAM(单口输出输出 RAM) 86 | - 并加载单独的 Data Memory COE 文件,比如: 87 | 88 | ``` 89 | memory_initialization_radix = 16; 90 | memory_initialization_vector = 91 | 00000000 92 | 00000000 93 | 94 | ... // 共 1024 行,初始化 1024 个 32 位宽存储器 95 | 96 | 00000000 97 | ``` 98 | 99 | 生成 Data Memory 之后,我们就可以在其 Instantiation Template 中找到相应的初始化模板文件 `data_mem.veo`,比如: 100 | 101 | ```verilog 102 | data_memory_ip your_instance_name ( 103 | .a(a), // input wire [9 : 0] a 104 | .d(d), // input wire [31 : 0] d 105 | .clk(clk), // input wire clk 106 | .we(we), // input wire we 107 | .spo(spo) // output wire [31 : 0] spo 108 | ); 109 | ``` 110 | 111 | 我们再和之前自己实现的 Data Memory 进行对比: 112 | 113 | ```verilog 114 | data_memory u_data_memory( 115 | .clk (clk ), 116 | .en_mem_write (en_mem_write_mem ), 117 | .mem_addr (alu_result_out[11:2] ), 118 | .write_mem_data (reg2_data_mem ), 119 | .read_mem_data (read_mem_data ) 120 | ); 121 | ``` 122 | 123 | 因此,我们也可以非常方便的将 IP 核生成的 Data Memory 直接嵌入我们的 CPU 中,和最初仿真的方法一样,进行测试。 124 | 125 | ## 仿真、测试 126 | 127 | 我们运行一个斐波那契数列的样例代码: 128 | 129 | ```nasm 130 | # Compute first twelve Fibonacci numbers and put in array, then print 131 | .data 132 | fibs: .word 0 : 20 # "array" of 20 words to contain fib values 133 | nouse: .word 0 134 | size: .word 20 # size of "array" 135 | 136 | .text 137 | 138 | la $t0, fibs # $t0 = &fibs 139 | la $t5, size # $t5 = &size 140 | lw $t5, 0($t5) # $t5 = *$t5 --> $t5 = 20 141 | 142 | li $t2, 1 # $t2 = 1 143 | sw $t2, 0($t0) # store F[0] with 1 144 | sw $t2, 4($t0) # store F[1] with 1 145 | 146 | ori $t6, $zero, 2 # $t6 = 2 147 | sub $t1, $t5, $t6 # the number of loop is (size-2) 148 | ori $t7, $zero, 1 # the lastest loop 149 | 150 | Loop: 151 | slt $t4, $t1, $t7 # $t4 = ($t1 < 1) ? 1 : 0 152 | beq $t4, $t7, Loop_End # repeat if not finished yet error 153 | lw $a0, 0($t0) # $a0 = F[n] 154 | lw $a1, 4($t0) # $a1 = F[n+1] 155 | jal fibonacci # $v0 = fibonacci( F[n], F[n+1] ) 156 | sw $v0, 8($t0) # store F[n+2] 157 | addi $t0, $t0, 4 # $t0 point to F[n+1] 158 | addi $t1, $t1, -1 # loop counter decreased by 1 159 | j Loop 160 | 161 | 162 | Loop_End: 163 | lui $t6, 0xABCD # $t6 = 0xABCD0000 # addi $t6,$0,0xABCD# 164 | addi $t0, $t0, 8 # point to the next address of the lastest fibonacci unit 165 | sw $t6, 0($t0) # *$t0 = $t6 166 | Loop_Forever: 167 | j Loop_Forever # loop forever 168 | 169 | fibonacci : 170 | add $v0, $a0, $a1 # $v0 = x + y 171 | jr $ra # return 172 | ``` 173 | 174 | 这段代码主要功能是将 Register File 中的 $4 和 $5 初始化为 1,并将 $4 + $5 的结果写入 $2 中,不断重复 20 次,得到第 20 项斐波那契数列的值。利用 MARS,我们将 MIPS 汇编编译成机器码,为了处理跳转时出现的控制冲突,我们在涉及到冲突的地方加上空指令 `sll $0, $0, 0`。 175 | 176 | ![](https://i.loli.net/2019/09/12/xchUlXLvkYfZGWm.png) 177 | 178 | 编译完成的机器码大致如下(点击展开): 179 | 180 |
181 | 182 | ``` 183 | memory_initialization_radix=16; 184 | memory_initialization_vector= 185 | 3c011001 186 | 00000000 187 | 00000000 188 | 00000000 189 | 00000000 190 | 00000000 191 | 34280000 192 | 3c011001 193 | 342d0054 194 | 8dad0000 195 | 240a0001 196 | ad0a0000 197 | ad0a0004 198 | 340e0002 199 | 01ae4822 200 | 340f0001 201 | 012f602a 202 | 00000000 203 | 00000000 204 | 00000000 205 | 00000000 206 | 00000000 207 | 118f000d 208 | 00000000 209 | 00000000 210 | 8d040000 211 | 8d050004 212 | 0c10002a 213 | 00000000 214 | 00000000 215 | ad020008 216 | 21080004 217 | 2129ffff 218 | 08100010 219 | 00000000 220 | 00000000 221 | 3c0eabcd 222 | 21080008 223 | ad0e0000 224 | 08100027 225 | 00000000 226 | 00000000 227 | 00851020 228 | 00000000 229 | 00000000 230 | 00000000 231 | 00000000 232 | 00000000 233 | 03e00008 234 | ``` 235 | 236 |
237 | 238 | **同时,由于这段代码涉及到了初始化存储器,我们也需要将 Data Memory 进行初始化**。我们直接利用 MARS 将上述代码的代码段 `.text` 和数据段 `.data` 全部以 16 进制的格式导出,并写入 coe 文件,配置 IP 核。 239 | 240 | ![](https://i.loli.net/2019/09/12/WQ6aqdm8oIPuihS.png) 241 | 242 | 之后我们开始仿真。我们直接配置仿真文件 `testbench.v` 如下: 243 | 244 | ```verilog 245 | `timescale 1ns / 1ps 246 | 247 | /* 248 | * Testbench 249 | */ 250 | 251 | module testbench(); 252 | reg clk; 253 | reg rst; 254 | 255 | // 初始化顶层模块 256 | zanpu_top u_zanpu_top( 257 | .clk (clk ), 258 | .rst (rst ) 259 | ); 260 | 261 | initial begin 262 | rst = 1; 263 | clk = 0; 264 | 265 | #30 rst = 0; 266 | end 267 | 268 | always 269 | #20 clk = ~clk; 270 | endmodule 271 | 272 | ``` 273 | 274 | 运行仿真之后,我们观察 $4、$5 和 $2 号寄存器,在初始化完成之后,我们就可以看到 16 进制的斐波那契数列的计算过程。 275 | 276 | ![](https://i.loli.net/2019/09/12/hFR97GfYeiTA8Cp.png) 277 | 278 | 与此同时,这部分数据也被写入 Data Memory,我们同样可以抓取到。 279 | 280 | 利用 IP 核对指令和数据存储器进行封装能够让我们的代码在开发板上面运行,接下来的外设部分需要这样的操作。 281 | -------------------------------------------------------------------------------- /docs/4_Peripherals/4-0_Basic.md: -------------------------------------------------------------------------------- 1 | # 外部设备的基础配置 2 | 3 | 为了让我们的 CPU 能够真正发挥功能,我们需要为开发板进行外设的适配。 4 | 5 | ## 更改 CPU 的组织结构 6 | 7 | ![](https://i.loli.net/2019/09/12/ekzlpwiGSrtDXdE.png) 8 | 9 | CPU 对外部设备的控制是通过写入(`SW`)和读取(`LW`)外设中的数据存储器来实现的。我们需要将外部设备和 CPU 独立开来,将时钟信号 clk 和复位信号 rst 通过一个公共的顶层模块同时引线连接在 CPU 和外部设备上面。 10 | 11 | 对于 VGA 显示来说,就是一个专有的时钟信号 `clk_65m`(下一节进行配置)以及 VGA 的输出引脚: 12 | 13 | - 横向刷新率 `hs` 14 | - 纵向刷新率 `vs` 15 | - 红色 `r` 16 | - 绿色 `g` 17 | - 蓝色 `b` 18 | 19 | 以及必要的存储访问引脚(类似于数据存储器 Data Memory): 20 | 21 | - 写使能信号:`wen` 22 | - 写入数据:`wire[31:0] wdata` 23 | - 目标地址:`wire[3:0] addr` 24 | - 读取数据:`wire rdata` 25 | 26 | 这样,我们再引入一个统一的顶层模块 soc,用来连接 CPU 和外设。那么,我们连接 VGA 的代码大致如下: 27 | 28 | ```verilog 29 | vga u_vga( 30 | .clk (clk ), 31 | .rstn (~rst ), 32 | .wen (vga_wen ), 33 | .wdata (vga_wdata ), 34 | .addr (vga_addr ), 35 | .rdata (vga_rdata ), 36 | .hs (hs ), 37 | .vs (vs ), 38 | .red (red ), 39 | .green (green ), 40 | .blue (blue ) 41 | ); 42 | ``` 43 | 44 | 连接 CPU 的代码大致如下: 45 | 46 | ```verilog 47 | zanpu_top u_zanpu_top( 48 | .clk (clk ), 49 | .rst (rst ), 50 | .io_wen (io_wen ), 51 | .io_addr (io_addr ), 52 | .io_wdata (io_wdata ), 53 | .io_rdata (io_rdata ) 54 | ); 55 | ``` 56 | 57 | 同时,如果想要在开发板上面有相应的输入,我们需要在 soc 上面加上一个输入信号: 58 | 59 | ```verilog 60 | input wire[3:0] btn_key 61 | ``` 62 | 63 | 可以看到,对于顶层模块 soc 来说,输入信号有: 64 | 65 | - 时钟信号:`clk` 66 | - 复位信号:`rst` 67 | - 按键:`wire[3:0] btn_key` 68 | 69 | 输出信号包括 VGA 的必要输出: 70 | 71 | ```verilog 72 | output wire hs, 73 | output wire vs, 74 | output wire[3:0] red, 75 | output wire[3:0] green, 76 | output wire[3:0] blue 77 | ``` 78 | 79 | 通过规定目标写入地址范围(利用掩码进行划分),我们可以控制 CPU 的 `SW` 指令向正确的目标数据存储器(写入自己 CPU 的数据存储器还是外设的数据存储器)写入对应的数据。同样,我们也可也通过掩码来规定读入目标地址范围,来判断读取按键对应存储器还是外设存储器的数据。具体代码实现如下: 80 | 81 | - 读取按键数据存储器地址或外设(VGA)数据存储器地址([`soc.v`](https://github.com/zan-pu/pipelined-zanpu/blob/soc/pipelined-zanpu.srcs/sources_1/new/soc.v)): 82 | 83 | ```verilog 84 | assign io_rdata = (io_addr[31:6] == 26'b1111_1111_1111_1111_0000_0000_00) ? vga_rdata : {27'b0, btn_key_t}; 85 | ``` 86 | 87 | - 写入 CPU 数据存储器地址或外设(VGA)数据存储器地址([`zanpu_top.v`](https://github.com/zan-pu/pipelined-zanpu/blob/soc/pipelined-zanpu.srcs/sources_1/new/zanpu_top.v)) 88 | 89 | ```verilog 90 | wire access_io = (alu_result_out[31:16] == 16'hffff) ? 1'b1 : 1'b0; 91 | wire[31:0] dm_rdata; 92 | wire dm_wen; 93 | assign io_wen = (access_io && en_mem_write_mem); 94 | assign dm_wen = (access_io == 1'b0 && en_mem_write_mem); 95 | 96 | assign read_mem_data = (access_io == 1'b1) ? io_rdata : dm_rdata; 97 | ``` 98 | 99 | 接下来,我们以 VGA 为例子进行外设接口的实现。 100 | -------------------------------------------------------------------------------- /docs/4_Peripherals/4-1_VGA.md: -------------------------------------------------------------------------------- 1 | # 以 VGA 为例子对外部设备进行信号输出 2 | 3 | VGA 驱动模块的代码开源在:[zan-pu/vga-driver](https://github.com/zan-pu/vga-driver) 4 | 5 | ## VGA 显示的实现 6 | 7 | VGA 显示器扫描方式从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT 对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的时间称为垂直扫描时间,其倒数称为场频率,即屏幕的刷新频率,常见的有 60Hz,75Hz 等等,但标准的 VGA 显示的场频 60Hz。其扫描示意图如下图所示: 8 | 9 | ![](https://i.loli.net/2019/09/15/2d8E3H9erUQCGoq.png) 10 | 11 | VGA 包含行时序与场时序两个部分,行时序包含: 12 | 13 | - Horizontal Sync Pulse 14 | - Horizontal Front Porch 15 | - Horizontal Front Active Video 16 | - Horizontal Back Porch 17 | 18 | 这四个参数,其时序图如下: 19 | 20 | ![](https://i.loli.net/2019/09/15/X6VdC5FNDY4Jt9s.png) 21 | 22 | 相应的,场时序的参数类似,场时序图如下: 23 | 24 | ![](https://i.loli.net/2019/09/15/s6RzbvkgX7w23qt.png) 25 | 26 | 在具体的实现过程中,我们需要输出一个 1024*768 分辨率,60Hz 的视频信号。其各个参数的计算公式大致为: 27 | 28 | ``` 29 | Pixel Clock = (Screen Refresh Frequency) * (Hor Active Video + Hor Front Porch + Hor Synv Pulse + Hor Back Porch) * (Ver Active Video + Ver Front Porch + Ver Synv Pulse + Ver Back Porch) 30 | ``` 31 | 32 | ![](https://i.loli.net/2019/09/15/SgfRNwxL8EzBtle.png) 33 | 34 | 查表可知,我们所需要的信号参数与具体实现大致如下: 35 | 36 | ```verilog 37 | `define RST_EN 1'b0 38 | 39 | module vga_top #( 40 | parameter H_DISPLAY = 11'd1024, 41 | parameter H_FRONT_PORCH = 11'd24, 42 | parameter H_SYNC_PULSE = 11'd136, 43 | parameter H_BACK_PORCH = 11'd160, 44 | parameter H_TOTAL = 11'd1344, 45 | 46 | parameter V_DISPLAY = 10'd768, 47 | parameter V_FRONT_PORCH = 10'd3, 48 | parameter V_SYNC_PULSE = 10'd6, 49 | parameter V_BACK_PORCH = 10'd29, 50 | parameter V_TOTAL = 10'd806 51 | )( 52 | input wire clk, 53 | input wire rstn, 54 | 55 | output wire hs, 56 | output wire vs, 57 | output wire[3:0] red, 58 | output wire[3:0] green, 59 | output wire[3:0] blue 60 | ); 61 | 62 | ... 63 | 64 | endmodule 65 | ``` 66 | 67 | 值得注意的是,我们的 VGA 模块所需时钟信号为 65MHz 的频率,因此我们需要通过 MMCM 模块将 100MHz 的板载时钟信号转换为 65MHz 的 VGA 时钟信号。我们这里创建一个 IP 核:Clocking Wizard,并将其输出频率设置为 65MHz,再把 locked 端口禁用即可。 68 | 69 | ![](https://i.loli.net/2019/09/15/NgpSOjqK2sm1nyZ.png) 70 | 71 | 之后,我们就可以利用这一模块将我们的输入 `clk` 信号转化为 65MHz,并将反转的 `rst` 信号输入(VGA 模块的 reset 信号与我们其他模块的是相反的): 72 | 73 | ```verilog 74 | // 65 MHz clock signal 75 | 76 | wire clk_vga; 77 | 78 | clk_wiz u_clk_wiz ( 79 | // Clock out ports 80 | .clk_out1(clk_vga), 81 | 82 | // Status and control signals 83 | .reset(~rstn), 84 | 85 | // Clock in ports 86 | .clk_in1(clk)); 87 | ``` 88 | 89 | 接下来,我们创建三个寄存器变量 r、g、b,来与 VGA 输出的 r、g、b 信号匹配,从而能够输出扫描到的 RGB 颜色: 90 | 91 | ```verilog 92 | /* RGB signals */ 93 | reg[3:0] reg_red; 94 | reg[3:0] reg_green; 95 | reg[3:0] reg_blue; 96 | 97 | assign red = reg_red; 98 | assign green = reg_green; 99 | assign blue = reg_blue; 100 | ``` 101 | 102 | 之后,我们控制行刷新与场刷新: 103 | 104 | ```verilog 105 | /* Horizonal & Vertical refresh pulses */ 106 | 107 | reg h_reg; 108 | reg v_reg; 109 | 110 | assign hs = h_reg; 111 | assign vs = v_reg; 112 | 113 | reg[10:0] h_count; 114 | reg[10:0] v_count; 115 | 116 | /* horizontal scan and vertical scan */ 117 | 118 | always @ (posedge clk_vga) begin 119 | if (rstn == `RST_EN) begin 120 | h_count <= 11'b0; 121 | v_count <= 11'b0; 122 | end 123 | else begin 124 | if (h_count == H_TOTAL - 1) begin 125 | h_count <= 11'b0; 126 | if (v_count == V_TOTAL - 1) begin 127 | v_count <= 11'b0; 128 | end 129 | else begin 130 | v_count <= v_count + 1'b1; 131 | end 132 | end 133 | else begin 134 | h_count <= h_count + 1'b1; 135 | end 136 | end 137 | end 138 | 139 | always @ (posedge clk_vga) begin 140 | if (h_count < H_SYNC_PULSE) begin 141 | h_reg <= 1'b0; 142 | end 143 | else begin 144 | h_reg <= 1'b1; 145 | end 146 | end 147 | 148 | always @ (posedge clk_vga) begin 149 | if (v_count < V_SYNC_PULSE) begin 150 | v_reg <= 1'b0; 151 | end 152 | else begin 153 | v_reg <= 1'b1; 154 | end 155 | end 156 | ``` 157 | 158 | 最后,我们可以利用 `h_count` 以及 `v_count` 控制 VGA 在某个范围的输出颜色: 159 | 160 | ```verilog 161 | always @ (posedge clk_vga) begin 162 | 163 | /* --- Game board --- */ 164 | if (h_count < 1140 && h_count > 500 && v_count < 740 && v_count > 100) begin 165 | reg_red <= 4'b0100; 166 | reg_green <= 4'b0010; 167 | reg_blue <= 4'b0001; 168 | end 169 | 170 | /* --- Out of bounds, paint white --- */ 171 | else begin 172 | reg_red <= 4'b1111; 173 | reg_green <= 4'b1111; 174 | reg_blue <= 4'b1111; 175 | end 176 | end 177 | ``` 178 | 179 | ## VGA 接口 Constraints 的设置 180 | 181 | 根据精工开发板的资料,以及上一节介绍的内容来说,我们总共需要配置如下的管脚: 182 | 183 | - VGA 的五个输出信号: 184 | 185 | ```verilog 186 | output wire hs, 187 | output wire vs, 188 | output wire[3:0] red, 189 | output wire[3:0] green, 190 | output wire[3:0] blue 191 | ``` 192 | 193 | - 按钮的输入信号: 194 | 195 | ```verilog 196 | input wire[3:0] btn_key 197 | ``` 198 | 199 | - 输入时钟信号: 200 | 201 | ```verilog 202 | input wire clk 203 | ``` 204 | 205 | - 输入复位信号: 206 | 207 | ```verilog 208 | input wire rst 209 | ``` 210 | 211 | 针对我们的开发板,在综合项目之后,我们选择 Open Synthesized Design,并打开 Window > I/O Ports,开始管脚约束的配置。 212 | 213 | 根据精工开发板的开发文档,EES-338 上的 VGA 接口(J1)通过 14 位信号线与 FPGA 连接,红、绿、蓝三个 颜色信号各占 4 位,另外还包括行同步和场同步信号: 214 | 215 | ![](https://i.loli.net/2019/09/15/NWFvBSbxqeHL9Dk.png) 216 | 217 | 系统时钟 100MHz,输出的时钟信号直接与 FPGA 全局时钟输入引脚(T5)相连: 218 | 219 | ![](https://i.loli.net/2019/09/15/5cWfkG4weoRAbNh.png) 220 | 221 | 四个专用按键分别用于逻辑复位 RST(S8 和 S6)和擦除 FPGA 配置 PROG(S7 和 S5), 当设计中不需要外部触发复位时,RST 按键可以用作其他逻辑触发功能。没有按下时输出高电平,按下为低电平: 222 | 223 | ![](https://i.loli.net/2019/09/15/nk1Uq4pHjclYhmx.png) 224 | 225 | 最终,我们配置的约束大致如下: 226 | 227 | ![](https://i.loli.net/2019/09/15/JIYAQywvSqneO56.png) 228 | 229 | 接下来,经过 Implementation 和 Generate Bitstream,我们的 VGA Driver 就能直接上板运行,输出一个大致如下的图像(不同显示器上面的 VGA 图像的颜色显示可能不同): 230 | 231 | ![](https://i.loli.net/2019/09/15/gI6mxqBSrXZobwM.jpg) 232 | 233 | 这个图像的本意是一个迷宫,其设计草稿以及对应功能如下: 234 | 235 | - `00` WALL 墙 236 | - `01` ROAD 路 237 | - `10` DEST 目标 238 | - `11` PLYR 人 239 | 240 | ![](https://i.loli.net/2019/09/15/labpNemFKSuBsPf.png) 241 | 242 | 不过最终时间仓促,我们并没有完全实现全部功能。但是,我们的基本思路是完全没有问题的,因此按照这样介绍的设计思路,我们完全可以成功实现利用 CPU 来控制人在迷宫中行走,最终得到完整的游戏体验。 243 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /hero.png 4 | actionText: 进入文档 5 | actionLink: /1_Preparations/1-1_Installation.md 6 | features: 7 | - title: 🎫 8 | details: 基础的 Vivado 使用与编码技巧 9 | - title: 👑 10 | details: 简单的 MIPS 单周期 CPU 实现 11 | - title: 🧧 12 | details: 完备的经典五级 RISC 流水 CPU 13 | footer: 2019 ©Spencer Woo. Released under the CC BY-SA 4.0 International License. 14 | --- 15 | 16 | [🎃 实验要求](#-实验要求2019-版) | [📑 文档目录](#-文档目录) | [🎁 参考资料](#-参考资料与推荐阅读) 17 | 18 | # 🚡 Build Your PC 19 | 20 | **辛苦三星期,造台计算机!** 21 | 22 | > 本项目为 BIT 大四小学期: 23 | > 24 | > - 计算机组成原理课程设计 25 | > - 汇编与接口技术课程设计 26 | > 27 | > 两门课程的基本设计流程与参考资料的非官方整理。 28 | 29 | ## 🎃 实验要求(2019 版) 30 | 31 | - **计算机组成原理课程设计** 32 | - 每一位同学自行完成一个支持 MIPS 指令子集:Lui、Addiu、Add、Lw、Sw、Beq、j,以及一随机抽取的指令的单周期 CPU,并给出仿真结果 33 | - 每组同学共同完成一个多周期的或者流水线的 CPU,仿真并在精工板上验证 34 | - **汇编与接口课程设计** 35 | - 每一位同学为自己设计的 CPU 写一个测试程序(MIPS 汇编程序或者 C 语言 + MIPS 编译器) 36 | - 根据精工板资源,按组完成计算机外设接口设计,如 VGA 控制器、LCD、UART、蓝牙等,测试并形成 IP 核 37 | - 应用 ② 中产生的 IP 核到自行设计的计算机系统中,在精工板上实现。 38 | 39 | ## 📑 文档目录 40 | 41 | 如果没有意外,那么你的设计流程应该和以下过程一致。 42 | 43 | ### 准备工作 44 | 45 | - [安装与环境部署](./1_Preparations/1-1_Installation.md) 46 | - [利用 Vivado 创建项目](./1_Preparations/1-2_Vivado.md) 47 | - [使用 VS Code 作为 Vivado 的默认代码编辑器](./1_Preparations/1-3_Editor.md) 48 | 49 | ### 个人项目 - 单周期 CPU 50 | 51 | > 单周期 CPU 是个人项目中的必要起步项目,在单周期 CPU 的构建过程中我们才能理解流水线 CPU 的具体工作原理。 52 | 53 | - [单周期 CPU 的基础知识](./2_SingleCycle/2-1_Basic.md) 54 | - [单周期 CPU 的设计思路](./2_SingleCycle/2-2_Design.md) 55 | - [单周期 CPU 的具体代码实现](./2_SingleCycle/2-3_Verilog.md) 56 | - [单周期 CPU 的行为仿真](./2_SingleCycle/2-4_Testbench.md) 57 | 58 | ### 团队项目 - 多周期 CPU / 流水线 CPU 59 | 60 | > 我们团队选择实现流水线 CPU,因此接下来的参考文档只介绍流水线 CPU 的设计流程。 61 | 62 | - [流水线 CPU 准备实现的指令](./3_Pipelining/3-0_Instructions.md) 63 | - [流水线 CPU 的基础知识](./3_Pipelining/3-1_Basic.md) 64 | - [数据通路与信号控制](./3_Pipelining/3-2_Datapath&Control.md) 65 | - [Hazards 与其避免措施](./3_Pipelining/3-3_Hazards.md) 66 | - [分支预测 Branch Prediction](./3_Pipelining/3-4_BranchPrediction.md) 67 | - [流水线 CPU 的设计](./3_Pipelining/3-5_Design.md) 68 | - [利用 IP 核对 CPU 进行封装](./3_Pipelining/3-6_IP.md) 69 | 70 | ### 团队项目 - 外部设备 Peripherals 71 | 72 | > 本次小学期实验最后需要与精工开发板进行整合,需要通过 VGA 等接口调用来让 CPU 能够实现与外设接口的整合,实现一个完整的计算机系统。 73 | 74 | - [外部设备的基础配置](./4_Peripherals/4-0_Basic.md) 75 | - [以 VGA 为例子对外部设备进行信号输出](./4_Peripherals/4-1_VGA.md) 76 | 77 | ## 🎁 参考资料与推荐阅读 78 | 79 | - [How does a CPU work - Hackernoon](https://hackernoon.com/how-does-a-cpu-work-af3488d182a2) 80 | - [University of Washington - Course CSE378](https://courses.cs.washington.edu/courses/cse378/09wi/lectures.html) 81 | - [A single-cycle MIPS processor](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec07.pdf) 82 | - [Intro to Pipelining](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec09.pdf) 83 | - [Pipelined datapath and control](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec10.pdf) 84 | - [Pipelining and Data Hazards](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec11.pdf) 85 | - [Hazards](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec12.pdf) 86 | - [Branching, Performance](https://courses.cs.washington.edu/courses/cse378/09wi/lectures/lec13.pdf) 87 | - [针对参加龙芯杯的若干建议 - Silverster98/bit_nscscc_suggestion](https://github.com/Silverster98/bit_nscscc_suggestion) 88 | 89 | --- 90 | 91 | **🚡 Build Your PC** ©2019 Spencer Woo. Released under the [CC BY-SA 4.0 International License](./LICENSE). 92 | 93 | Authored and maintained by Spencer Woo. 94 | 95 | [@Portfolio](https://spencerwoo.com) | [@GitHub](https://github.com/spencerwooo) | [@BIT](http://www.bit.edu.cn/) 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "build-your-pc-docs", 3 | "version": "1.0.0", 4 | "description": "Guide to building your own CPU.", 5 | "main": "index.js", 6 | "repository": "git@github.com:spencerwooo/build-your-pc-docs.git", 7 | "author": "spencerwoo ", 8 | "license": "Attribution-ShareAlike 4.0 International", 9 | "private": true, 10 | "scripts": { 11 | "docs:dev": "vuepress dev docs", 12 | "docs:build": "vuepress build docs" 13 | }, 14 | "devDependencies": { 15 | "vuepress": "^1.0.4" 16 | } 17 | } 18 | --------------------------------------------------------------------------------