├── .gitignore ├── .gitlab-ci.yml ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── doc ├── LLCL.pptx ├── LLCL_MIPS_report.pdf ├── correct.md ├── thoughts.md ├── 信号表.ods └── 信号表.xlsx ├── official ├── func_test │ ├── golden_trace.txt │ └── soft │ │ ├── func │ │ └── obj │ │ │ ├── axi_ram.mif │ │ │ ├── data_ram.coe │ │ │ ├── data_ram.mif │ │ │ ├── inst_ram.coe │ │ │ ├── inst_ram.mif │ │ │ ├── main.bin │ │ │ ├── main.data │ │ │ ├── main.elf │ │ │ └── test.s │ │ └── memory_game │ │ └── obj │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s ├── perf_test │ └── soft │ │ └── perf_func │ │ └── obj │ │ ├── allbench │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── bitcount │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── bubble_sort │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── coremark │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── crc32 │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── dhrystone │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── quick_sort │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── select_sort │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── sha │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ ├── stream_copy │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s │ │ └── stringsearch │ │ ├── axi_ram.coe │ │ ├── axi_ram.mif │ │ ├── inst_data.bin │ │ ├── main.elf │ │ └── test.s ├── soc_axi_func │ ├── rtl │ │ ├── CONFREG │ │ │ └── confreg.v │ │ ├── axi_wrap │ │ │ └── axi_wrap.v │ │ ├── ram_wrap │ │ │ └── axi_wrap_ram.v │ │ ├── soc_axi_lite_top.v │ │ └── xilinx_ip │ │ │ ├── axi_clock_converter │ │ │ └── axi_clock_converter.xci │ │ │ ├── axi_crossbar_1x2 │ │ │ └── axi_crossbar_1x2.xci │ │ │ ├── axi_ram │ │ │ └── axi_ram.xci │ │ │ └── clk_pll │ │ │ └── clk_pll.xci │ ├── run_vivado │ │ ├── mycpu_prj1 │ │ │ └── mycpu.xpr │ │ └── soc_lite.xdc │ └── testbench │ │ └── mycpu_tb.v └── soc_axi_perf │ ├── rtl │ ├── CONFREG │ │ └── confreg.v │ ├── axi_wrap │ │ └── axi_wrap.v │ ├── ram_wrap │ │ └── axi_wrap_ram.v │ ├── soc_axi_lite_top.v │ └── xilinx_ip │ │ ├── axi_clock_converter │ │ └── axi_clock_converter.xci │ │ ├── axi_crossbar_1x2 │ │ └── axi_crossbar_1x2.xci │ │ ├── axi_ram │ │ └── axi_ram.xci │ │ └── clk_pll │ │ └── clk_pll.xci │ ├── run_vivado │ ├── mycpu_prj1 │ │ ├── mycpu.xpr │ │ └── run_allbench.tcl │ └── soc_lite.xdc │ └── testbench │ └── mycpu_tb.v ├── project ├── build.properties └── plugins.sbt ├── repositories ├── rtl ├── axi_crossbar_3x1 │ └── axi_crossbar_3x1.xci ├── axi_crossbar_3x1_sim_netlist.v ├── axi_ram.v ├── divider │ └── divider.xci ├── dual_port_ram.v └── single_port_ram.v ├── scripts ├── generate_bitstream.tcl ├── generate_ip.tcl └── random_mem.py ├── src ├── main │ └── scala │ │ ├── cache │ │ ├── CacheRam.scala │ │ ├── DCache.scala │ │ ├── ICache.scala │ │ ├── LRUManager.scala │ │ └── WriteBuffer.scala │ │ ├── cpu │ │ ├── ALU.scala │ │ ├── BPU.scala │ │ ├── BranchPredictor.scala │ │ ├── CP0.scala │ │ ├── Comparator.scala │ │ ├── ComponentStage.scala │ │ ├── DCU.scala │ │ ├── DU.scala │ │ ├── EIU.scala │ │ ├── FetchInst.scala │ │ ├── HLU.scala │ │ ├── ICU.scala │ │ ├── JU.scala │ │ ├── MU.scala │ │ ├── MultiIssueCPU.scala │ │ ├── MyCpuTop.scala │ │ ├── RFU.scala │ │ ├── Stage.scala │ │ ├── Utils.scala │ │ └── defs │ │ │ ├── Config.scala │ │ │ ├── ConstantVal.scala │ │ │ └── Mips.scala │ │ ├── ip │ │ ├── AxiClockConverter.scala │ │ ├── Cache.scala │ │ ├── CrossBarIP.scala │ │ ├── DividerIP.scala │ │ ├── DualPortRam.scala │ │ ├── MultiplierIP.scala │ │ ├── RamPort.scala │ │ ├── SinglePortRam.scala │ │ ├── sim │ │ │ └── package.scala │ │ └── xpm_memory.scala │ │ ├── lib │ │ ├── Coproduct.scala │ │ ├── Decoder.scala │ │ ├── Key.scala │ │ ├── Optional.scala │ │ ├── Record.scala │ │ ├── Task.scala │ │ └── Updating.scala │ │ ├── tlb │ │ ├── MMU.scala │ │ └── TLB.scala │ │ └── util │ │ ├── Using.scala │ │ ├── VivadoConf.scala │ │ └── binary.scala └── test │ └── scala │ ├── cache │ ├── DCacheTest.scala │ ├── ICacheTest.scala │ ├── LRUTest.scala │ └── UncacheTest.scala │ ├── cpu │ ├── AxiMemorySim.scala │ ├── COEParse.scala │ ├── Sim.scala │ ├── SimulationSoc.scala │ ├── Simulator.scala │ ├── SimulatorPlugins.scala │ ├── confreg │ │ └── package.scala │ ├── display │ │ └── package.scala │ └── funcTest │ │ ├── FuncTestLowLatency.scala │ │ ├── FuncTestNormal.scala │ │ ├── PerfTest.scala │ │ └── package.scala │ ├── lib │ └── Decoder.scala │ └── ram │ └── AXIRam.scala ├── tests └── func │ ├── Makefile │ ├── Readme_first.txt │ ├── bin.lds.S │ ├── convert.c │ ├── include │ ├── asm.h │ ├── cpu_cde.h │ └── regdef.h │ ├── rules.make │ └── tests │ ├── and │ └── and.S │ └── or │ └── or.S └── utils └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | *.bak 4 | *.o 5 | *.a 6 | vivado.conf 7 | *.txt 8 | !golden_trace.txt 9 | 10 | # sbt specific 11 | .bsp/ 12 | .cache/ 13 | .history/ 14 | .lib/ 15 | dist/* 16 | target 17 | lib_managed/ 18 | src_managed/ 19 | project/boot/ 20 | project/plugins/project/ 21 | 22 | # Scala-IDE specific 23 | .scala_dependencies 24 | .worksheet 25 | 26 | .idea 27 | out 28 | 29 | # Eclipse 30 | bin/ 31 | .classpath 32 | .project 33 | .settings 34 | .cache-main 35 | # VSCode 36 | .vscode/ 37 | 38 | # Metals 39 | .metals/ 40 | .bloop/ 41 | project/project 42 | metals.sbt 43 | 44 | #User 45 | /*.vhd 46 | /*.v 47 | *.cf 48 | *.json 49 | *.vcd 50 | !tester/src/test/resources/*.vhd 51 | 52 | 53 | simWorkspace/ 54 | tmp/ 55 | null 56 | 57 | # Vivado 58 | *.hw/ 59 | *.runs/ 60 | *.sim/ 61 | *.tmp/ 62 | *.cache/ 63 | *.Xil/ 64 | *.jou 65 | *.ip_user_files/ -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Based on NonTrival-Mips project(https://github.com/trivialmips/nontrivial-mips) 2 | 3 | variables: 4 | VIVADO_DIR: "/opt/Xilinx/Vivado/2019.2" 5 | VIVADO: "${VIVADO_DIR}/bin/vivado" 6 | FUNC_TEST_WORK_DIR: "./official/soc_axi_func" 7 | PERF_TEST_WORK_DIR: "./official/soc_axi_perf" 8 | SBT_OPTS: "-Dsbt.global.base=sbt-cache/.sbtboot -Dsbt.boot.directory=sbt-cache/.boot -Dsbt.ivy.home=sbt-cache/.ivy -Dsbt.override.build.repos=true -Dsbt.repository.config=repositories" 9 | 10 | cache: 11 | key: "$CI_COMMIT_REF_SLUG" 12 | paths: 13 | - mycpu_top.v 14 | - mergeRTL.v 15 | - vivado.conf 16 | - official/**/rtl/xilinx_ip 17 | - official/**/run_vivado/mycpu_prj1/mycpu.sim 18 | - official/**/run_vivado/mycpu_prj1/mycpu.runs 19 | - official/**/run_vivado/mycpu_prj1/mycpu.cache 20 | - "sbt-cache/.ivy/cache" 21 | - "sbt-cache/.boot" 22 | - "sbt-cache/.sbtboot" 23 | - "sbt-cache/target" 24 | 25 | stages: 26 | - generate_verilog 27 | - init_test_environment 28 | - test 29 | - init_vivado 30 | - bitstreams 31 | 32 | spinal_verilog: 33 | stage: generate_verilog 34 | tags: 35 | - vivado 36 | script: 37 | - sbt ${SBT_OPTS} 'runMain cpu.Generate' 38 | artifacts: 39 | paths: 40 | - ./mycpu_top.v 41 | - ./mergeRTL.v 42 | 43 | 44 | init_test_environment: 45 | stage: init_test_environment 46 | tags: 47 | - vivado 48 | script: 49 | - echo -e "vivado {\n path = ${VIVADO_DIR}\n}" > vivado.conf 50 | 51 | 52 | func_test: 53 | stage: test 54 | tags: 55 | - vivado 56 | rules: 57 | - if: '$CI_COMMIT_MESSAGE =~ /\[func-test/i' 58 | script: 59 | - sbt ${SBT_OPTS} 'Test / runMain cpu.funcTest.FuncTestNormal' 60 | artifacts: 61 | paths: 62 | - ./simWorkspace/FuncTestNormal/*.vcd 63 | when: on_failure 64 | 65 | 66 | func_test_low_latency: 67 | stage: test 68 | tags: 69 | - vivado 70 | rules: 71 | - if: '$CI_COMMIT_MESSAGE =~ /\[func-test/i' 72 | script: 73 | - sbt ${SBT_OPTS} 'Test / runMain cpu.funcTest.FuncTestLowLatency' 74 | artifacts: 75 | paths: 76 | - ./simWorkspace/FuncTestLowLatency/*.vcd 77 | when: on_failure 78 | 79 | 80 | perf_test: 81 | stage: test 82 | tags: 83 | - vivado 84 | rules: 85 | - if: '$CI_COMMIT_MESSAGE =~ /\[perf-test/i' 86 | script: 87 | - sbt ${SBT_OPTS} 'Test / runMain cpu.funcTest.PerfTest' 88 | 89 | 90 | # Below is used for generating Bitstream 91 | 92 | build_func_ip: 93 | stage: init_vivado 94 | tags: 95 | - vivado 96 | variables: 97 | PROJECT_NAME: "mycpu" 98 | XPR_PATH: "run_vivado/mycpu_prj1/${PROJECT_NAME}.xpr" 99 | rules: 100 | - if: '$CI_COMMIT_MESSAGE =~ /\[build/i' 101 | script: 102 | - export JOBS_NUMBER=18 103 | - export AXI_RAM_COE_FILE="official/func_test/soft/func/obj/inst_ram.coe" 104 | - export PLL_FREQ=$(echo $CI_COMMIT_MESSAGE | grep -Po "(?<=\[build)(.*?)([0-9])+(?=M)" | grep -Eo "[0-9]+") 105 | - ${VIVADO} -mode tcl -source scripts/generate_ip.tcl ${FUNC_TEST_WORK_DIR}/${XPR_PATH} 106 | 107 | build_perf_ip: 108 | stage: init_vivado 109 | tags: 110 | - vivado 111 | variables: 112 | PROJECT_NAME: "mycpu" 113 | XPR_PATH: "run_vivado/mycpu_prj1/${PROJECT_NAME}.xpr" 114 | rules: 115 | - if: '$CI_COMMIT_MESSAGE =~ /\[build/i' 116 | script: 117 | - export JOBS_NUMBER=18 118 | - export AXI_RAM_COE_FILE="official/perf_test/soft/perf_func/obj/allbench/axi_ram.coe" 119 | - export PLL_FREQ=$(echo $CI_COMMIT_MESSAGE | grep -Po "(?<=\[build)(.*?)([0-9])+(?=M)" | grep -Eo "[0-9]+") 120 | - ${VIVADO} -mode tcl -source scripts/generate_ip.tcl ${PERF_TEST_WORK_DIR}/${XPR_PATH} 121 | 122 | 123 | generate_func_bit_stream: 124 | stage: bitstreams 125 | tags: 126 | - vivado 127 | variables: 128 | PROJECT_NAME: "mycpu" 129 | XPR_PATH: "run_vivado/mycpu_prj1/${PROJECT_NAME}.xpr" 130 | rules: 131 | - if: '$CI_COMMIT_MESSAGE =~ /\[build/i' 132 | script: 133 | - export JOBS_NUMBER=18 134 | - ${VIVADO} -mode tcl -source scripts/generate_bitstream.tcl ${FUNC_TEST_WORK_DIR}/${XPR_PATH} 135 | artifacts: 136 | paths: 137 | - ${FUNC_TEST_WORK_DIR}/run_vivado/mycpu_prj1/mycpu.runs/impl_1/soc_axi_lite_top.bit 138 | - ${FUNC_TEST_WORK_DIR}/run_vivado/mycpu_prj1/mycpu.runs/*/runme.log 139 | when: always 140 | 141 | generate_perf_bit_stream: 142 | stage: bitstreams 143 | tags: 144 | - vivado 145 | variables: 146 | PROJECT_NAME: "mycpu" 147 | XPR_PATH: "run_vivado/mycpu_prj1/${PROJECT_NAME}.xpr" 148 | rules: 149 | - if: '$CI_COMMIT_MESSAGE =~ /\[build/i' 150 | script: 151 | - export JOBS_NUMBER=18 152 | - ${VIVADO} -mode tcl -source scripts/generate_bitstream.tcl ${PERF_TEST_WORK_DIR}/${XPR_PATH} 153 | artifacts: 154 | paths: 155 | - ${PERF_TEST_WORK_DIR}/run_vivado/mycpu_prj1/mycpu.runs/impl_1/soc_axi_lite_top.bit 156 | - ${PERF_TEST_WORK_DIR}/run_vivado/mycpu_prj1/mycpu.runs/*/runme.log 157 | when: always 158 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "2.7.4" 2 | align.preset = more 3 | maxColumn = 1000 4 | docstrings.oneline = fold 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 dreamHuang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 第五届龙芯杯参赛作品 2 | 3 | 该项目包含清华大学琉璃晨露队在第五届“龙芯杯”全国大学生系统能力培养大赛的参赛作品 4 | 5 | 琉璃晨露队伍成员分别是黄嘉良、杨倚天、余泰来、刘松铭 6 | 7 | ## 简介 8 | 9 | 最终我们实现了一个顺序双发射八级流水线的处理器LLCL-MIPS,主频在龙芯提供的开发板上(fpga芯片是xilinx的xc7a200tfbg676-2)可以达到120MHz,支持MIPS32 R1的绝大多数指令,能够运行比赛时最新的稳定版Linux内核v5.13,在Linux下能够驱动VGA、PS/2、串口、Flash等外设。 10 | 11 | LLCL-MIPS没有采用经典的硬件描述语言如Verilog(或者SystemVerilog),而是基于SpinalHDL进行开发,由于项目初期组内对SpinalHDL并不熟悉,因开发周期短的因素有很大程度遗留的冗余代码,后期也无力重构了。基于我使用SpinalHDL的经验,在下面会对使用SpinalHDL开发流水线结构的处理器进行一些建议。 12 | 13 | CPU的流水段划分如下: 14 | 15 | - 取指/指令队列:第1-3级 16 | - 译码/发射:第4级 17 | - 执行:第5级 18 | - 访存:第6-7级 19 | - 写回:第8级 20 | 21 | 我们CPU的主要特点如下: 22 | 23 | - 两级分支预测:包括PHT和BHT,以及BTB 24 | - 可配置多路组L1缓存:分别有指令Cache和数据Cache,最终均使用的是2路组相连、单路4KiB的缓存 25 | - 指令队列,支持最多两条指令入队和出队,能够保证分支指令和延迟槽一起发出 26 | 27 | ## 代码文件说明 28 | 29 | CPU的代码都在`src/main/scala`下,代码文件的一些名字采用缩写的命名,这里列出可能不清楚的文件的含义 30 | 31 | | 文件名 | 说明 | 32 | | --------- | ------------------------------------------------------------ | 33 | | DCU/ICU | DCache/ICache Unit,CPU和Cache交互时进行转换的一个辅助单元 | 34 | | DU | Decoder Unit,译码单元 | 35 | | EIU | Exception Interrupt Unit,历史遗留的冗余模块,到最后只有数据包和常量定义 | 36 | | FetchInst | 指令队列的实现 | 37 | | HLU | HiLo Unit | 38 | | JU | Jump Unit执行阶段判断分支的模块 | 39 | | MU | Mem Unit,历史遗留的冗余模块,到最后只定义了访存是符号扩展还是零扩展的一个变量 | 40 | 41 | 我们的CPU控制是在MultiIssueCPU.scala里了,不得不承认代码有点混乱。可能给大家的帮助更多是对一些小模块的实现,整体的逻辑要理清楚还是比较复杂的。 42 | 43 | *实现的比较清楚的例如是CP0模块,对CP0寄存器进行了一定程度的抽象* 44 | 45 | --- 46 | 最后的性能CPU(main)和运行OS(cpu-os)的CPU在两个不同的分支上 47 | 48 | 我们还提供了一个7级流水的单发射的CPU(simple)作为参考,它的实现更加清晰,也包含了分支预测、缓存等基本模块 49 | 50 | 51 | ## 建议 52 | 53 | ### 对于SpinalHDL 54 | 55 | - SpinalHDL的最基本特点是给硬件表达增加了更强的语法和数据结构的支持,所以可以尝试大胆的使用函数调用、`Array | Map`这样的数据结构 56 | - SpinalHDL有很多package并没有在官方文档上提到,需要多在IDE中点来点去,自行探索( 57 | - SpinalHDL对于二维的信号支持很差,可以说无法实现像SystemVerilog下的二维logic数组,解决方案是使用`Vec`,但他们在硬件上应该是等价的,只是把二维数组拆成多个一维的信号了 (该问题似乎已经修复,那可以说又少了一个缺点) 58 | - **强烈建议**使用`spinal.lib.Stream`以及`spinal.lib.Flow`在你的流水线结构中,SpinalHDL实现了关于这两个类非常好用的方法以及一些相关的类,能够帮助你快速构建流水线 59 | 60 | ### 其他 61 | 62 | - 在写Cache的时候,可以使用Xilinx Parameterized Macro(XPM)声明BRAM或者LUTRAM,在Spinal下可以使用BlackBox套上 63 | 64 | - 有一些不在初赛要求的,但在运行Linux必要的指令,它的限制比较多,例如修改TLB、刷新Cache的指令,我们的做法是 65 | - 限制一次最多发射一条这样的指令 66 | - 在它发射后暂停整个流水线直到它的执行结束 67 | 68 | ## 致谢 69 | 70 | 除了队员的努力之外,我们还要特别感谢往届的学长,包括高一川、陈晟祺、陈嘉杰、王邈 71 | 72 | 项目参考了往届参赛的队伍作品,包括[nontrivial-MIPS](https://github.com/trivialmips)以及[NaiveMIPS](https://github.com/z4yx/NaiveMIPS-HDL) 73 | 74 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / scalaVersion := "2.13.1" 2 | ThisBuild / version := "1.0.0" 3 | ThisBuild / organization := "" 4 | ThisBuild / transitiveClassifiers := Seq(Artifact.SourceClassifier) 5 | 6 | val spinalVersion = "1.6.0" 7 | 8 | lazy val root = (project in file(".")) 9 | .settings( 10 | name := "llcl-mips", 11 | libraryDependencies ++= Seq( 12 | "com.github.spinalhdl" %% "spinalhdl-core" % spinalVersion, 13 | "com.github.spinalhdl" %% "spinalhdl-lib" % spinalVersion, 14 | compilerPlugin("com.github.spinalhdl" %% "spinalhdl-idsl-plugin" % spinalVersion), 15 | "com.typesafe" % "config" % "1.4.1" 16 | ), 17 | fork := true 18 | ) 19 | -------------------------------------------------------------------------------- /doc/LLCL.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/doc/LLCL.pptx -------------------------------------------------------------------------------- /doc/LLCL_MIPS_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/doc/LLCL_MIPS_report.pdf -------------------------------------------------------------------------------- /doc/correct.md: -------------------------------------------------------------------------------- 1 | ``` 2 | exe 阶段纠错 3 | 4 | ``` 5 | 6 | 7 | 8 | ``` 9 | 实例化两个 btb 比较好适配指令队列 10 | 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /doc/thoughts.md: -------------------------------------------------------------------------------- 1 | - 分支预测 2 | 3 | - `IF2`:分支?目标? 4 | - `BTB` 查到说明要跳转,知道分支也知道目标 5 | - `BTB` 没查到则没法跳,因此不跳,不知道分支也不知道目标 6 | - `ID`:目标? 7 | - 8 | - `EX`:分支? 9 | 10 | 11 | 12 | - `BTB` 只保存分支目标确定的跳转指令 13 | 14 | 15 | 16 | - 知道目标,知道要分支 17 | - `BTB` 中没有自己,则写入 `BTB`、`LRU` 18 | - `BTB` 中有自己,则写入 `LRU` 19 | - 知道目标,知道不要分支 20 | - `BTB` 中没有自己,则不管 21 | - `BTB` 中有自己,则写入 `BTB`、`LRU` 22 | - 知道目标,不知道要不要分支 23 | - 没法写入 `BTB`,只能不管 24 | - 不知道目标 25 | - 没法写入 `BTB`,只能不管 26 | 27 | 28 | 29 | - 由于要尽快跳转,因此读取 `BTB` 是在 `IF` 阶段 30 | - 由于要知道目标,知道是否分支才能写入 `BTB`,因此写入 `BTB` 是在 `EX` 阶段 31 | - (也可能 `ID` 阶段就已经知道了,那么要提前写入吗?)(先不了,允许多个阶段写入可能发生写入的冲突,还会造成需要多个阶段一起进行广播,太复杂,先不考虑) 32 | 33 | 34 | 35 | - 虽然是否写入在 `EX` 阶段才知道,但是如果要写入,则写入的数据是什么在 `ID` 阶段就能够知道 36 | 37 | 38 | 39 | - `IF` 阶段读取的值不一定准确,也没法准确 40 | - `EX` 阶段写入的值可以是准确的,通过前向广播写入的数据达到目的,归纳可以证明写入的数据都是准确的 41 | 42 | 43 | 44 | - 要读写的 `BTB` 与 `LRU` 由 `index` 决定,如果广播的 `index` 和自身相同,则收下广播的数据 45 | 46 | - `IF` 读取 `BTB` 时是否要采取写优先策略? 47 | - 好像可以采取 48 | 49 | 50 | 51 | - `BHT` 和 `PHT`。一条跳转指令,其 `PHT` 可能只有某一些项是有用的,其它的都不会用到,因此可以考虑多条跳转指令共用一个 `PHT` 52 | 53 | - 用哈希值寻址 `BHT`,用地址低位寻址 `PHT` 54 | - 写 `BHT`:写一项 55 | - 读 `BHT`:读一项 56 | - 写 `PHT`:写一项 57 | - 读 `PHT`:读一项 58 | 59 | 60 | 61 | - 一条指令是**目标确定的跳转指令** 62 | - 要修改 `LRU` 63 | - 如果 `BTB` 命中则不修改 `BTB`,未命中要修改 `BTB` 64 | - 根据跳转情况修改 `BHT`、`PHT` 65 | 66 | 67 | 68 | - `BHT` 和 `PHT` 跳转,`BTB` 命中 69 | 70 | 71 | 72 | - 为了避免前传问题,能尽量早写入的就尽量早写入 73 | - 由于中断异常是在 `MEM` 阶段判断的,因此写入最早不能超过 `MEM` 阶段 74 | 75 | 76 | 77 | 78 | - `CP0` 79 | 80 | - 对 `CP0` 写入不需要 `MEM` 的结果,因此 `CP0` 的写入放在 `MEM` 阶段 81 | - 82 | 83 | 84 | 85 | - 写:`MEM` 阶段 86 | - 87 | 88 | - 数据流 89 | 90 | - `CP0 -> TLB` 91 | 92 | - `TLB -> CP0` 93 | 94 | 95 | 96 | - `CP0 -> reg` 97 | 98 | - `reg -> CP0` 99 | 100 | 101 | 102 | - `reg -> stage.id` 103 | 104 | - 由 `CP0 -> reg` 则 `CP0 -> stage.exe` 105 | 106 | 107 | 108 | - `reg -> stage.exe` 109 | 110 | - `reg -> stage.mem.1` 111 | -------------------------------------------------------------------------------- /doc/信号表.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/doc/信号表.ods -------------------------------------------------------------------------------- /doc/信号表.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/doc/信号表.xlsx -------------------------------------------------------------------------------- /official/func_test/soft/func/obj/data_ram.coe: -------------------------------------------------------------------------------- 1 | memory_initialization_radix = 16; 2 | memory_initialization_vector = 3 | 00000000 4 | 00000000 5 | 00000000 6 | 00000000 7 | 00000000 8 | -------------------------------------------------------------------------------- /official/func_test/soft/func/obj/data_ram.mif: -------------------------------------------------------------------------------- 1 | 00000000000000000000000000000000 2 | 00000000000000000000000000000000 3 | 00000000000000000000000000000000 4 | 00000000000000000000000000000000 5 | 00000000000000000000000000000000 6 | -------------------------------------------------------------------------------- /official/func_test/soft/func/obj/main.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/func_test/soft/func/obj/main.bin -------------------------------------------------------------------------------- /official/func_test/soft/func/obj/main.data: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /official/func_test/soft/func/obj/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/func_test/soft/func/obj/main.elf -------------------------------------------------------------------------------- /official/func_test/soft/memory_game/obj/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/func_test/soft/memory_game/obj/inst_data.bin -------------------------------------------------------------------------------- /official/func_test/soft/memory_game/obj/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/func_test/soft/memory_game/obj/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/allbench/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/allbench/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/allbench/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/allbench/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/bitcount/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/bitcount/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/bitcount/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/bitcount/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/bubble_sort/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/bubble_sort/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/bubble_sort/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/bubble_sort/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/coremark/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/coremark/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/coremark/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/coremark/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/crc32/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/crc32/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/crc32/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/crc32/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/dhrystone/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/dhrystone/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/dhrystone/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/dhrystone/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/quick_sort/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/quick_sort/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/quick_sort/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/quick_sort/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/select_sort/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/select_sort/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/select_sort/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/select_sort/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/sha/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/sha/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/sha/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/sha/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/stream_copy/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/stream_copy/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/stream_copy/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/stream_copy/main.elf -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/stringsearch/inst_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/stringsearch/inst_data.bin -------------------------------------------------------------------------------- /official/perf_test/soft/perf_func/obj/stringsearch/main.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huang-jl/LLCL-MIPS/aa807d2462455982abfb9e2a68ae2e771c110ef3/official/perf_test/soft/perf_func/obj/stringsearch/main.elf -------------------------------------------------------------------------------- /official/soc_axi_func/rtl/axi_wrap/axi_wrap.v: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | -------------------------------------------------------------------------------- 3 | Copyright (c) 2016, Loongson Technology Corporation Limited. 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | 3. Neither the name of Loongson Technology Corporation Limited nor the names of 18 | its contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL LOONGSON TECHNOLOGY CORPORATION LIMITED BE LIABLE 25 | TO ANY PARTY FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- 32 | ------------------------------------------------------------------------------*/ 33 | 34 | module axi_wrap( 35 | input m_aclk, 36 | input m_aresetn, 37 | //ar 38 | input [3 :0] m_arid , 39 | input [31:0] m_araddr , 40 | input [3 :0] m_arlen , 41 | input [2 :0] m_arsize , 42 | input [1 :0] m_arburst, 43 | input [1 :0] m_arlock , 44 | input [3 :0] m_arcache, 45 | input [2 :0] m_arprot , 46 | input m_arvalid, 47 | output m_arready, 48 | //r 49 | output [3 :0] m_rid , 50 | output [31:0] m_rdata , 51 | output [1 :0] m_rresp , 52 | output m_rlast , 53 | output m_rvalid , 54 | input m_rready , 55 | //aw 56 | input [3 :0] m_awid , 57 | input [31:0] m_awaddr , 58 | input [3 :0] m_awlen , 59 | input [2 :0] m_awsize , 60 | input [1 :0] m_awburst, 61 | input [1 :0] m_awlock , 62 | input [3 :0] m_awcache, 63 | input [2 :0] m_awprot , 64 | input m_awvalid, 65 | output m_awready, 66 | //w 67 | input [3 :0] m_wid , 68 | input [31:0] m_wdata , 69 | input [3 :0] m_wstrb , 70 | input m_wlast , 71 | input m_wvalid , 72 | output m_wready , 73 | //b 74 | output [3 :0] m_bid , 75 | output [1 :0] m_bresp , 76 | output m_bvalid , 77 | input m_bready , 78 | 79 | output s_aclk, 80 | output s_aresetn, 81 | //ar 82 | output [3 :0] s_arid , 83 | output [31:0] s_araddr , 84 | output [3 :0] s_arlen , 85 | output [2 :0] s_arsize , 86 | output [1 :0] s_arburst, 87 | output [1 :0] s_arlock , 88 | output [3 :0] s_arcache, 89 | output [2 :0] s_arprot , 90 | output s_arvalid, 91 | input s_arready, 92 | //r 93 | input [3 :0] s_rid , 94 | input [31:0] s_rdata , 95 | input [1 :0] s_rresp , 96 | input s_rlast , 97 | input s_rvalid , 98 | output s_rready , 99 | //aw 100 | output [3 :0] s_awid , 101 | output [31:0] s_awaddr , 102 | output [3 :0] s_awlen , 103 | output [2 :0] s_awsize , 104 | output [1 :0] s_awburst, 105 | output [1 :0] s_awlock , 106 | output [3 :0] s_awcache, 107 | output [2 :0] s_awprot , 108 | output s_awvalid, 109 | input s_awready, 110 | //w 111 | output [3 :0] s_wid , 112 | output [31:0] s_wdata , 113 | output [3 :0] s_wstrb , 114 | output s_wlast , 115 | output s_wvalid , 116 | input s_wready , 117 | //b 118 | input [3 :0] s_bid , 119 | input [1 :0] s_bresp , 120 | input s_bvalid , 121 | output s_bready 122 | ); 123 | assign s_aclk = m_aclk ; 124 | assign s_aresetn = m_aresetn; 125 | //ar 126 | assign s_arid = m_arid ; 127 | assign s_araddr = m_araddr ; 128 | assign s_arlen = m_arlen ; 129 | assign s_arsize = m_arsize ; 130 | assign s_arburst = m_arburst; 131 | assign s_arlock = m_arlock ; 132 | assign s_arcache = m_arcache; 133 | assign s_arprot = m_arprot ; 134 | assign s_arvalid = m_arvalid; 135 | assign m_arready = s_arready; 136 | //r 137 | assign m_rid = m_rvalid ? s_rid : 4'd0 ; 138 | assign m_rdata = m_rvalid ? s_rdata : 32'd0 ; 139 | assign m_rresp = m_rvalid ? s_rresp : 2'd0 ; 140 | assign m_rlast = m_rvalid ? s_rlast : 1'd0 ; 141 | assign m_rvalid = s_rvalid; 142 | assign s_rready = m_rready; 143 | //aw 144 | assign s_awid = m_awid ; 145 | assign s_awaddr = m_awaddr ; 146 | assign s_awlen = m_awlen ; 147 | assign s_awsize = m_awsize ; 148 | assign s_awburst = m_awburst; 149 | assign s_awlock = m_awlock ; 150 | assign s_awcache = m_awcache; 151 | assign s_awprot = m_awprot ; 152 | assign s_awvalid = m_awvalid; 153 | assign m_awready = s_awready; 154 | //w 155 | assign s_wid = m_wid ; 156 | assign s_wdata = m_wdata ; 157 | assign s_wstrb = m_wstrb ; 158 | assign s_wlast = m_wlast ; 159 | assign s_wvalid = m_wvalid ; 160 | assign m_wready = s_wready ; 161 | //b 162 | assign m_bid = m_bvalid ? s_bid : 4'd0 ; 163 | assign m_bresp = m_bvalid ? s_bresp : 2'd0 ; 164 | assign m_bvalid = s_bvalid ; 165 | assign s_bready = m_bready ; 166 | endmodule 167 | -------------------------------------------------------------------------------- /official/soc_axi_func/run_vivado/soc_lite.xdc: -------------------------------------------------------------------------------- 1 | #set_property SEVERITY {Warning} [get_drc_checks RTSTAT-2] 2 | #时钟信号连接 3 | set_property PACKAGE_PIN AC19 [get_ports clk] 4 | set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets clk] 5 | create_clock -period 10.000 -name clk -waveform {0.000 5.000} [get_ports clk] 6 | 7 | #reset 8 | set_property PACKAGE_PIN Y3 [get_ports resetn] 9 | 10 | 11 | #LED 12 | set_property PACKAGE_PIN K23 [get_ports {led[0]}] 13 | set_property PACKAGE_PIN J21 [get_ports {led[1]}] 14 | set_property PACKAGE_PIN H23 [get_ports {led[2]}] 15 | set_property PACKAGE_PIN J19 [get_ports {led[3]}] 16 | set_property PACKAGE_PIN G9 [get_ports {led[4]}] 17 | set_property PACKAGE_PIN J26 [get_ports {led[5]}] 18 | set_property PACKAGE_PIN J23 [get_ports {led[6]}] 19 | set_property PACKAGE_PIN J8 [get_ports {led[7]}] 20 | set_property PACKAGE_PIN H8 [get_ports {led[8]}] 21 | set_property PACKAGE_PIN G8 [get_ports {led[9]}] 22 | set_property PACKAGE_PIN F7 [get_ports {led[10]}] 23 | set_property PACKAGE_PIN A4 [get_ports {led[11]}] 24 | set_property PACKAGE_PIN A5 [get_ports {led[12]}] 25 | set_property PACKAGE_PIN A3 [get_ports {led[13]}] 26 | set_property PACKAGE_PIN D5 [get_ports {led[14]}] 27 | set_property PACKAGE_PIN H7 [get_ports {led[15]}] 28 | 29 | #led_rg 0/1 30 | set_property PACKAGE_PIN G7 [get_ports {led_rg0[0]}] 31 | set_property PACKAGE_PIN F8 [get_ports {led_rg0[1]}] 32 | set_property PACKAGE_PIN B5 [get_ports {led_rg1[0]}] 33 | set_property PACKAGE_PIN D6 [get_ports {led_rg1[1]}] 34 | 35 | #NUM 36 | set_property PACKAGE_PIN D3 [get_ports {num_csn[7]}] 37 | set_property PACKAGE_PIN D25 [get_ports {num_csn[6]}] 38 | set_property PACKAGE_PIN D26 [get_ports {num_csn[5]}] 39 | set_property PACKAGE_PIN E25 [get_ports {num_csn[4]}] 40 | set_property PACKAGE_PIN E26 [get_ports {num_csn[3]}] 41 | set_property PACKAGE_PIN G25 [get_ports {num_csn[2]}] 42 | set_property PACKAGE_PIN G26 [get_ports {num_csn[1]}] 43 | set_property PACKAGE_PIN H26 [get_ports {num_csn[0]}] 44 | 45 | set_property PACKAGE_PIN C3 [get_ports {num_a_g[0]}] 46 | set_property PACKAGE_PIN E6 [get_ports {num_a_g[1]}] 47 | set_property PACKAGE_PIN B2 [get_ports {num_a_g[2]}] 48 | set_property PACKAGE_PIN B4 [get_ports {num_a_g[3]}] 49 | set_property PACKAGE_PIN E5 [get_ports {num_a_g[4]}] 50 | set_property PACKAGE_PIN D4 [get_ports {num_a_g[5]}] 51 | set_property PACKAGE_PIN A2 [get_ports {num_a_g[6]}] 52 | #set_property PACKAGE_PIN C4 :DP 53 | 54 | #switch 55 | set_property PACKAGE_PIN AC21 [get_ports {switch[7]}] 56 | set_property PACKAGE_PIN AD24 [get_ports {switch[6]}] 57 | set_property PACKAGE_PIN AC22 [get_ports {switch[5]}] 58 | set_property PACKAGE_PIN AC23 [get_ports {switch[4]}] 59 | set_property PACKAGE_PIN AB6 [get_ports {switch[3]}] 60 | set_property PACKAGE_PIN W6 [get_ports {switch[2]}] 61 | set_property PACKAGE_PIN AA7 [get_ports {switch[1]}] 62 | set_property PACKAGE_PIN Y6 [get_ports {switch[0]}] 63 | 64 | #btn_key 65 | set_property PACKAGE_PIN V8 [get_ports {btn_key_col[0]}] 66 | set_property PACKAGE_PIN V9 [get_ports {btn_key_col[1]}] 67 | set_property PACKAGE_PIN Y8 [get_ports {btn_key_col[2]}] 68 | set_property PACKAGE_PIN V7 [get_ports {btn_key_col[3]}] 69 | set_property PACKAGE_PIN U7 [get_ports {btn_key_row[0]}] 70 | set_property PACKAGE_PIN W8 [get_ports {btn_key_row[1]}] 71 | set_property PACKAGE_PIN Y7 [get_ports {btn_key_row[2]}] 72 | set_property PACKAGE_PIN AA8 [get_ports {btn_key_row[3]}] 73 | 74 | #btn_step 75 | set_property PACKAGE_PIN Y5 [get_ports {btn_step[0]}] 76 | set_property PACKAGE_PIN V6 [get_ports {btn_step[1]}] 77 | 78 | set_property IOSTANDARD LVCMOS33 [get_ports clk] 79 | set_property IOSTANDARD LVCMOS33 [get_ports resetn] 80 | set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}] 81 | set_property IOSTANDARD LVCMOS33 [get_ports {led_rg0[*]}] 82 | set_property IOSTANDARD LVCMOS33 [get_ports {led_rg1[*]}] 83 | set_property IOSTANDARD LVCMOS33 [get_ports {num_a_g[*]}] 84 | set_property IOSTANDARD LVCMOS33 [get_ports {num_csn[*]}] 85 | set_property IOSTANDARD LVCMOS33 [get_ports {switch[*]}] 86 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_key_col[*]}] 87 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_key_row[*]}] 88 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_step[*]}] 89 | 90 | 91 | set_false_path -from [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT1]] -to [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT0]] 92 | set_false_path -from [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT0]] -to [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT1]] 93 | -------------------------------------------------------------------------------- /official/soc_axi_perf/rtl/axi_wrap/axi_wrap.v: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | -------------------------------------------------------------------------------- 3 | Copyright (c) 2016, Loongson Technology Corporation Limited. 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | 3. Neither the name of Loongson Technology Corporation Limited nor the names of 18 | its contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL LOONGSON TECHNOLOGY CORPORATION LIMITED BE LIABLE 25 | TO ANY PARTY FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- 32 | ------------------------------------------------------------------------------*/ 33 | 34 | module axi_wrap( 35 | input m_aclk, 36 | input m_aresetn, 37 | //ar 38 | input [3 :0] m_arid , 39 | input [31:0] m_araddr , 40 | input [3 :0] m_arlen , 41 | input [2 :0] m_arsize , 42 | input [1 :0] m_arburst, 43 | input [1 :0] m_arlock , 44 | input [3 :0] m_arcache, 45 | input [2 :0] m_arprot , 46 | input m_arvalid, 47 | output m_arready, 48 | //r 49 | output [3 :0] m_rid , 50 | output [31:0] m_rdata , 51 | output [1 :0] m_rresp , 52 | output m_rlast , 53 | output m_rvalid , 54 | input m_rready , 55 | //aw 56 | input [3 :0] m_awid , 57 | input [31:0] m_awaddr , 58 | input [3 :0] m_awlen , 59 | input [2 :0] m_awsize , 60 | input [1 :0] m_awburst, 61 | input [1 :0] m_awlock , 62 | input [3 :0] m_awcache, 63 | input [2 :0] m_awprot , 64 | input m_awvalid, 65 | output m_awready, 66 | //w 67 | input [3 :0] m_wid , 68 | input [31:0] m_wdata , 69 | input [3 :0] m_wstrb , 70 | input m_wlast , 71 | input m_wvalid , 72 | output m_wready , 73 | //b 74 | output [3 :0] m_bid , 75 | output [1 :0] m_bresp , 76 | output m_bvalid , 77 | input m_bready , 78 | 79 | output s_aclk, 80 | output s_aresetn, 81 | //ar 82 | output [3 :0] s_arid , 83 | output [31:0] s_araddr , 84 | output [3 :0] s_arlen , 85 | output [2 :0] s_arsize , 86 | output [1 :0] s_arburst, 87 | output [1 :0] s_arlock , 88 | output [3 :0] s_arcache, 89 | output [2 :0] s_arprot , 90 | output s_arvalid, 91 | input s_arready, 92 | //r 93 | input [3 :0] s_rid , 94 | input [31:0] s_rdata , 95 | input [1 :0] s_rresp , 96 | input s_rlast , 97 | input s_rvalid , 98 | output s_rready , 99 | //aw 100 | output [3 :0] s_awid , 101 | output [31:0] s_awaddr , 102 | output [3 :0] s_awlen , 103 | output [2 :0] s_awsize , 104 | output [1 :0] s_awburst, 105 | output [1 :0] s_awlock , 106 | output [3 :0] s_awcache, 107 | output [2 :0] s_awprot , 108 | output s_awvalid, 109 | input s_awready, 110 | //w 111 | output [3 :0] s_wid , 112 | output [31:0] s_wdata , 113 | output [3 :0] s_wstrb , 114 | output s_wlast , 115 | output s_wvalid , 116 | input s_wready , 117 | //b 118 | input [3 :0] s_bid , 119 | input [1 :0] s_bresp , 120 | input s_bvalid , 121 | output s_bready 122 | ); 123 | assign s_aclk = m_aclk ; 124 | assign s_aresetn = m_aresetn; 125 | //ar 126 | assign s_arid = m_arid ; 127 | assign s_araddr = m_araddr ; 128 | assign s_arlen = m_arlen ; 129 | assign s_arsize = m_arsize ; 130 | assign s_arburst = m_arburst; 131 | assign s_arlock = m_arlock ; 132 | assign s_arcache = m_arcache; 133 | assign s_arprot = m_arprot ; 134 | assign s_arvalid = m_arvalid; 135 | assign m_arready = s_arready; 136 | //r 137 | assign m_rid = m_rvalid ? s_rid : 4'd0 ; 138 | assign m_rdata = m_rvalid ? s_rdata : 32'd0 ; 139 | assign m_rresp = m_rvalid ? s_rresp : 2'd0 ; 140 | assign m_rlast = m_rvalid ? s_rlast : 1'd0 ; 141 | assign m_rvalid = s_rvalid; 142 | assign s_rready = m_rready; 143 | //aw 144 | assign s_awid = m_awid ; 145 | assign s_awaddr = m_awaddr ; 146 | assign s_awlen = m_awlen ; 147 | assign s_awsize = m_awsize ; 148 | assign s_awburst = m_awburst; 149 | assign s_awlock = m_awlock ; 150 | assign s_awcache = m_awcache; 151 | assign s_awprot = m_awprot ; 152 | assign s_awvalid = m_awvalid; 153 | assign m_awready = s_awready; 154 | //w 155 | assign s_wid = m_wid ; 156 | assign s_wdata = m_wdata ; 157 | assign s_wstrb = m_wstrb ; 158 | assign s_wlast = m_wlast ; 159 | assign s_wvalid = m_wvalid ; 160 | assign m_wready = s_wready ; 161 | //b 162 | assign m_bid = m_bvalid ? s_bid : 4'd0 ; 163 | assign m_bresp = m_bvalid ? s_bresp : 2'd0 ; 164 | assign m_bvalid = s_bvalid ; 165 | assign s_bready = m_bready ; 166 | endmodule 167 | -------------------------------------------------------------------------------- /official/soc_axi_perf/run_vivado/mycpu_prj1/run_allbench.tcl: -------------------------------------------------------------------------------- 1 | file copy -force ../../../soft/perf_func/obj/bitcount/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 2 | restart 3 | run all 4 | 5 | 6 | file copy -force ../../../soft/perf_func/obj/bubble_sort/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 7 | restart 8 | run all 9 | 10 | 11 | file copy -force ../../../soft/perf_func/obj/coremark/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 12 | restart 13 | run all 14 | 15 | 16 | file copy -force ../../../soft/perf_func/obj/crc32/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 17 | restart 18 | run all 19 | 20 | 21 | file copy -force ../../../soft/perf_func/obj/dhrystone/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 22 | restart 23 | run all 24 | 25 | 26 | file copy -force ../../../soft/perf_func/obj/quick_sort/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 27 | restart 28 | run all 29 | 30 | 31 | file copy -force ../../../soft/perf_func/obj/select_sort/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 32 | restart 33 | run all 34 | 35 | 36 | file copy -force ../../../soft/perf_func/obj/sha/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 37 | restart 38 | run all 39 | 40 | 41 | file copy -force ../../../soft/perf_func/obj/stream_copy/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 42 | restart 43 | run all 44 | 45 | 46 | file copy -force ../../../soft/perf_func/obj/stringsearch/axi_ram.mif ./mycpu.sim/sim_1/behav/xsim/axi_ram.mif 47 | restart 48 | run all 49 | 50 | -------------------------------------------------------------------------------- /official/soc_axi_perf/run_vivado/soc_lite.xdc: -------------------------------------------------------------------------------- 1 | #set_property SEVERITY {Warning} [get_drc_checks RTSTAT-2] 2 | #时钟信号连接 3 | set_property PACKAGE_PIN AC19 [get_ports clk] 4 | set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets clk] 5 | create_clock -period 10.000 -name clk -waveform {0.000 5.000} [get_ports clk] 6 | 7 | #reset 8 | set_property PACKAGE_PIN Y3 [get_ports resetn] 9 | 10 | 11 | #LED 12 | set_property PACKAGE_PIN K23 [get_ports {led[0]}] 13 | set_property PACKAGE_PIN J21 [get_ports {led[1]}] 14 | set_property PACKAGE_PIN H23 [get_ports {led[2]}] 15 | set_property PACKAGE_PIN J19 [get_ports {led[3]}] 16 | set_property PACKAGE_PIN G9 [get_ports {led[4]}] 17 | set_property PACKAGE_PIN J26 [get_ports {led[5]}] 18 | set_property PACKAGE_PIN J23 [get_ports {led[6]}] 19 | set_property PACKAGE_PIN J8 [get_ports {led[7]}] 20 | set_property PACKAGE_PIN H8 [get_ports {led[8]}] 21 | set_property PACKAGE_PIN G8 [get_ports {led[9]}] 22 | set_property PACKAGE_PIN F7 [get_ports {led[10]}] 23 | set_property PACKAGE_PIN A4 [get_ports {led[11]}] 24 | set_property PACKAGE_PIN A5 [get_ports {led[12]}] 25 | set_property PACKAGE_PIN A3 [get_ports {led[13]}] 26 | set_property PACKAGE_PIN D5 [get_ports {led[14]}] 27 | set_property PACKAGE_PIN H7 [get_ports {led[15]}] 28 | 29 | #led_rg 0/1 30 | set_property PACKAGE_PIN G7 [get_ports {led_rg0[0]}] 31 | set_property PACKAGE_PIN F8 [get_ports {led_rg0[1]}] 32 | set_property PACKAGE_PIN B5 [get_ports {led_rg1[0]}] 33 | set_property PACKAGE_PIN D6 [get_ports {led_rg1[1]}] 34 | 35 | #NUM 36 | set_property PACKAGE_PIN D3 [get_ports {num_csn[7]}] 37 | set_property PACKAGE_PIN D25 [get_ports {num_csn[6]}] 38 | set_property PACKAGE_PIN D26 [get_ports {num_csn[5]}] 39 | set_property PACKAGE_PIN E25 [get_ports {num_csn[4]}] 40 | set_property PACKAGE_PIN E26 [get_ports {num_csn[3]}] 41 | set_property PACKAGE_PIN G25 [get_ports {num_csn[2]}] 42 | set_property PACKAGE_PIN G26 [get_ports {num_csn[1]}] 43 | set_property PACKAGE_PIN H26 [get_ports {num_csn[0]}] 44 | 45 | set_property PACKAGE_PIN C3 [get_ports {num_a_g[0]}] 46 | set_property PACKAGE_PIN E6 [get_ports {num_a_g[1]}] 47 | set_property PACKAGE_PIN B2 [get_ports {num_a_g[2]}] 48 | set_property PACKAGE_PIN B4 [get_ports {num_a_g[3]}] 49 | set_property PACKAGE_PIN E5 [get_ports {num_a_g[4]}] 50 | set_property PACKAGE_PIN D4 [get_ports {num_a_g[5]}] 51 | set_property PACKAGE_PIN A2 [get_ports {num_a_g[6]}] 52 | #set_property PACKAGE_PIN C4 :DP 53 | 54 | #switch 55 | set_property PACKAGE_PIN AC21 [get_ports {switch[7]}] 56 | set_property PACKAGE_PIN AD24 [get_ports {switch[6]}] 57 | set_property PACKAGE_PIN AC22 [get_ports {switch[5]}] 58 | set_property PACKAGE_PIN AC23 [get_ports {switch[4]}] 59 | set_property PACKAGE_PIN AB6 [get_ports {switch[3]}] 60 | set_property PACKAGE_PIN W6 [get_ports {switch[2]}] 61 | set_property PACKAGE_PIN AA7 [get_ports {switch[1]}] 62 | set_property PACKAGE_PIN Y6 [get_ports {switch[0]}] 63 | 64 | #btn_key 65 | set_property PACKAGE_PIN V8 [get_ports {btn_key_col[0]}] 66 | set_property PACKAGE_PIN V9 [get_ports {btn_key_col[1]}] 67 | set_property PACKAGE_PIN Y8 [get_ports {btn_key_col[2]}] 68 | set_property PACKAGE_PIN V7 [get_ports {btn_key_col[3]}] 69 | set_property PACKAGE_PIN U7 [get_ports {btn_key_row[0]}] 70 | set_property PACKAGE_PIN W8 [get_ports {btn_key_row[1]}] 71 | set_property PACKAGE_PIN Y7 [get_ports {btn_key_row[2]}] 72 | set_property PACKAGE_PIN AA8 [get_ports {btn_key_row[3]}] 73 | 74 | #btn_step 75 | set_property PACKAGE_PIN Y5 [get_ports {btn_step[0]}] 76 | set_property PACKAGE_PIN V6 [get_ports {btn_step[1]}] 77 | 78 | set_property IOSTANDARD LVCMOS33 [get_ports clk] 79 | set_property IOSTANDARD LVCMOS33 [get_ports resetn] 80 | set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}] 81 | set_property IOSTANDARD LVCMOS33 [get_ports {led_rg0[*]}] 82 | set_property IOSTANDARD LVCMOS33 [get_ports {led_rg1[*]}] 83 | set_property IOSTANDARD LVCMOS33 [get_ports {num_a_g[*]}] 84 | set_property IOSTANDARD LVCMOS33 [get_ports {num_csn[*]}] 85 | set_property IOSTANDARD LVCMOS33 [get_ports {switch[*]}] 86 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_key_col[*]}] 87 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_key_row[*]}] 88 | set_property IOSTANDARD LVCMOS33 [get_ports {btn_step[*]}] 89 | 90 | 91 | set_false_path -from [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT1]] -to [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT0]] 92 | set_false_path -from [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT0]] -to [get_clocks -of_objects [get_pins pll.clk_pll/inst/plle2_adv_inst/CLKOUT1]] 93 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.5.1 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn -------------------------------------------------------------------------------- /repositories: -------------------------------------------------------------------------------- 1 | [repositories] 2 | local 3 | huaweicloud-maven: https://repo.huaweicloud.com/repository/maven/ 4 | aliyun-maven-repo: https://maven.aliyun.com/repository/public/ 5 | aliyun-nexus: https://maven.aliyun.com/nexus/content/groups/public/ 6 | 7 | typesafe: https://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly 8 | 9 | sbt-plugin-repo: https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] 10 | sbt-plugin-repo1: https://dl.bintray.com/sbt/sbt-plugin-releases, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] 11 | sonatype-oss-releases 12 | maven-central 13 | sonatype-oss-snapshots -------------------------------------------------------------------------------- /rtl/single_port_ram.v: -------------------------------------------------------------------------------- 1 | module single_port_ram # 2 | ( 3 | parameter DATA_WIDTH = 32, //default data width of xpm ram(bits) 4 | parameter DEPTH = 128, //default depth of memory 5 | parameter MEMORY_PRIMITIVE = "auto", //"auto | distributed | block" 6 | parameter LATENCY = 1, //default latency is 1 cycle 7 | parameter WRITE_MODE = "write_first" 8 | ) 9 | ( 10 | input wire clk /* verilator public */, 11 | input wire rst /* verilator public */, 12 | input wire en /* verilator public */, //ram enable 13 | input wire we /* verilator public */, //write enable 14 | input wire [$clog2(DEPTH) -1:0] addr /* verilator public */, 15 | input wire [DATA_WIDTH - 1:0] din /* verilator public */, //write data 16 | output wire [DATA_WIDTH - 1:0] dout /* verilator public */ 17 | ); 18 | 19 | // xpm_memory_spram: Single Port RAM 20 | // Xilinx Parameterized Macro, version 2019.2 21 | 22 | xpm_memory_spram #( 23 | .ADDR_WIDTH_A($clog2(DEPTH)), // DECIMAL 24 | .AUTO_SLEEP_TIME(0), // DECIMAL 25 | .ECC_MODE("no_ecc"), // String 26 | .MEMORY_PRIMITIVE(MEMORY_PRIMITIVE), // String 27 | .MEMORY_SIZE(DATA_WIDTH * DEPTH), // DECIMAL 28 | .MESSAGE_CONTROL(0), // DECIMAL 29 | .READ_DATA_WIDTH_A(DATA_WIDTH), // DECIMAL 30 | .READ_LATENCY_A(LATENCY), // DECIMAL 31 | .READ_RESET_VALUE_A("0"), // String 32 | .USE_MEM_INIT(0), // DECIMAL 33 | .WRITE_DATA_WIDTH_A(DATA_WIDTH), // DECIMAL 34 | .WRITE_MODE_A(WRITE_MODE), // String 35 | .BYTE_WRITE_WIDTH_A(DATA_WIDTH) 36 | ) 37 | xpm_memory_spram_inst ( 38 | .douta(dout), // READ_DATA_WIDTH_A-bit output: Data output for port A read operations. 39 | .addra(addr), // ADDR_WIDTH_A-bit input: Address for port A write and read operations. 40 | .clka(clk), // 1-bit input: Clock signal for port A. 41 | .dina(din), // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations. 42 | .ena(en), // 1-bit input: Memory enable signal for port A. Must be high on clock 43 | // cycles when read or write operations are initiated. Pipelined 44 | // internally. 45 | 46 | .regcea(1'b0), // 1-bit input: Clock Enable for the last register stage on the output 47 | // data path. 48 | 49 | .rsta(rst), // 1-bit input: Reset signal for the final port A output register stage. 50 | // Synchronously resets output port douta to the value specified by 51 | // parameter READ_RESET_VALUE_A. 52 | 53 | .sleep(1'b0), // 1-bit input: sleep signal to enable the dynamic power saving feature. 54 | .wea(we) // WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: Write enable vector 55 | // for port A input data port dina. 1 bit wide when word-wide writes are 56 | // used. In byte-wide write configurations, each bit controls the 57 | // writing one byte of dina to address addra. For example, to 58 | // synchronously write only bits [15-8] of dina when WRITE_DATA_WIDTH_A 59 | // is 32, wea would be 4'b0010. 60 | 61 | ); 62 | 63 | // End of xpm_memory_spram_inst instantiation 64 | 65 | endmodule 66 | -------------------------------------------------------------------------------- /scripts/generate_bitstream.tcl: -------------------------------------------------------------------------------- 1 | # Based on NonTrival-Mips project(https://github.com/trivialmips/nontrivial-mips) 2 | update_compile_order -fileset sources_1 3 | 4 | # Check jobs number 5 | if {[info exists env(JOBS_NUMBER)]} { 6 | set jobs_number $env(JOBS_NUMBER) 7 | } else { 8 | set jobs_number 2 9 | } 10 | 11 | puts "JOBS NUMBER is $jobs_number" 12 | 13 | reset_run impl_1 14 | reset_run synth_1 15 | launch_runs -jobs $jobs_number impl_1 -to_step write_bitstream 16 | wait_on_run impl_1 17 | 18 | exit -------------------------------------------------------------------------------- /scripts/generate_ip.tcl: -------------------------------------------------------------------------------- 1 | # Based on NonTrival-Mips project(https://github.com/trivialmips/nontrivial-mips) 2 | 3 | update_compile_order -fileset sources_1 4 | 5 | # Check jobs number 6 | if {[info exists env(JOBS_NUMBER)]} { 7 | set jobs_number $env(JOBS_NUMBER) 8 | } else { 9 | set jobs_number 2 10 | } 11 | puts "JOBS NUMBER is $jobs_number" 12 | 13 | # Add our rtl 14 | if { [file exists "./mycpu_top.v"] == 1} { 15 | add_files -norecurse ./mycpu_top.v 16 | } 17 | if { [file exists "./mergeRTL.v"] == 1} { 18 | add_files -norecurse ./mergeRTL.v 19 | } 20 | 21 | # Add our ips 22 | set ip_paths [glob ./rtl/*/*.xci] 23 | foreach ip $ip_paths { 24 | add_files -norecurse $ip 25 | } 26 | 27 | # change coe files 28 | if {[info exists env(AXI_RAM_COE_FILE)]} { 29 | puts "COE is $env(AXI_RAM_COE_FILE)" 30 | # remove other coe_files 31 | remove_files inst_ram.coe 32 | remove_files axi_ram.coe 33 | # add new coe_files 34 | add_files -norecurse "[pwd]/$env(AXI_RAM_COE_FILE)" 35 | 36 | set_property -dict [list CONFIG.Coe_File "[pwd]/$env(AXI_RAM_COE_FILE)"] [get_ips axi_ram] 37 | set axi_ram_ip_dir [get_property IP_DIR [get_ips axi_ram]] 38 | generate_target all [get_files $axi_ram_ip_dir/axi_ram.xci] 39 | export_ip_user_files -of_objects [get_files $axi_ram_ip_dir/axi_ram.xci] -no_script -sync -force -quiet 40 | } 41 | 42 | # Set PLL frequency for cpu_clk 43 | if {([info exists env(PLL_FREQ)])} { 44 | set pll_freq $env(PLL_FREQ) 45 | } else { 46 | set pll_freq 50 47 | } 48 | puts "PLL frequency is $pll_freq" 49 | set_property -dict [list CONFIG.CLKOUT1_REQUESTED_OUT_FREQ $pll_freq] [get_ips clk_pll] 50 | 51 | update_compile_order -fileset sources_1 52 | 53 | # If IP cores are used 54 | if { [llength [get_ips]] != 0} { 55 | upgrade_ip [get_ips] 56 | 57 | foreach ip [get_ips] { 58 | create_ip_run [get_ips $ip] 59 | } 60 | 61 | set ip_runs [get_runs -filter {SRCSET != sources_1 && IS_SYNTHESIS && STATUS != "synth_design Complete!"}] 62 | 63 | if { [llength $ip_runs] != 0} { 64 | launch_runs -quiet -jobs $jobs_number {*}$ip_runs 65 | 66 | foreach r $ip_runs { 67 | wait_on_run $r 68 | } 69 | } 70 | 71 | } 72 | 73 | exit -------------------------------------------------------------------------------- /scripts/random_mem.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | 4 | STRB_WIDTH = 2 5 | 6 | def gen_args(): 7 | addr_width = input('Please input the address width: ') 8 | data_width = input('Please input the data width: ') 9 | strb_width = input('Please input the strb width: ') 10 | addr_width = int(addr_width) 11 | data_width = int(data_width) 12 | strb_width = int(strb_width) 13 | return addr_width, data_width, strb_width 14 | 15 | def gen_random_data(addr_width, data_width, strb_width): 16 | res = [] 17 | high_bound = (2 ** data_width) - 1 18 | for i in range(2**(addr_width-1-math.ceil(math.log2(strb_width)))): 19 | res.append(random.randint(0, high_bound // 2)) 20 | res.append(random.randint(high_bound//2, high_bound)) 21 | return res 22 | 23 | def dump(data, path='data.txt'): 24 | with open(path, 'w') as f: 25 | for item in data: 26 | f.write('{:0<8x}\n'.format(item)) 27 | 28 | if __name__ == '__main__': 29 | addr_width, data_width, strb_width = gen_args() 30 | data = gen_random_data(addr_width, data_width, strb_width) 31 | dump(data) -------------------------------------------------------------------------------- /src/main/scala/cache/CacheRam.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import spinal.core._ 4 | import scala.language.postfixOps 5 | 6 | /** @param blockSize 数据块的大小, bytes 7 | * @param wayNum 路个数 8 | * @param bankSize 一个bank的大小(例如ICache双发射就是8), bytes 9 | * @note ICache采用实Tag和虚Index, 10 | * 因此Cache一路大小必须小于等于一个页的大小。 11 | * 由于只支持4KiB的页,这里默认一路的大小为4KiB 12 | * @note DCache由于把TLB查询放到了EX阶段,因此理论上支持更大的单路大小 13 | */ 14 | case class CacheRamConfig( 15 | blockSize: Int = 32, 16 | depth: Int = 4 * 1024 / 32, 17 | wayNum: Int = 2, 18 | bankSize: Int = 4 19 | ) { 20 | def indexWidth: Int = log2Up(depth) 21 | 22 | def tagWidth: Int = 32 - indexWidth - offsetWidth 23 | 24 | /** Cache Line的字节偏移宽度 */ 25 | def offsetWidth: Int = log2Up(blockSize) 26 | 27 | /** Cache Line的bank个数 */ 28 | def bankNum: Int = blockSize / bankSize 29 | 30 | /** Cache Line的bank偏移宽度 */ 31 | def bankOffsetWidth: Int = log2Up(bankNum) 32 | 33 | /** Cache Line的word个数 */ 34 | def wordNum: Int = blockSize / 4 35 | 36 | /** Cache Line的word个数 */ 37 | def wordOffsetWidth: Int = log2Up(wordNum) 38 | 39 | /** Cache Line的bit数 */ 40 | def bitNum: Int = blockSize * 8 41 | 42 | /** Cache Line的组数 */ 43 | def setNum: Int = 1 << indexWidth 44 | } 45 | 46 | /** @param blockSize 一个数据块的大小:bytes 47 | * @param bankSize 一个bank的大小:bytes 48 | */ 49 | case class Block(blockSize: Int, bankSize: Int = 4) extends Bundle { 50 | val banks = Vec(Bits(bankSize * 8 bits), blockSize / bankSize) 51 | 52 | def apply(addr: UInt): Bits = banks(addr) 53 | 54 | def apply(idx: Int): Bits = banks(idx) 55 | 56 | def :=(that: Block): Unit = this.banks := that.banks 57 | } 58 | 59 | object Block { 60 | def getBitWidth(blockSize: Int): Int = blockSize * 8 61 | 62 | def fromBits(value: Bits, blockSize: Int, bankSize: Int): Block = { 63 | val res = Block(blockSize, bankSize) 64 | res.assignFromBits(value.resize(getBitWidth(blockSize))) 65 | res 66 | } 67 | } 68 | 69 | case class Meta(tagWidth: Int) extends Bundle { 70 | val tag = Bits(tagWidth bits) 71 | val valid = Bool 72 | } 73 | 74 | object Meta { 75 | def getBitWidth(tagWidth: Int): Int = tagWidth + 1 76 | 77 | def fromBits(value: Bits, tagWidth: Int): Meta = { 78 | val res = Meta(tagWidth) 79 | res.assignFromBits(value.resize(res.getBitsWidth)) 80 | res 81 | } 82 | } 83 | 84 | case class DMeta(tagWidth: Int) extends Bundle { 85 | val tag = Bits(tagWidth bits) 86 | val valid = Bool 87 | val dirty = Bool 88 | } 89 | 90 | object DMeta { 91 | def getBitWidth(tagWidth: Int): Int = tagWidth + 2 92 | def fromBits(value: Bits, tagWidth: Int): DMeta = { 93 | val res = DMeta(tagWidth) 94 | res.assignFromBits(value.resize(res.getBitsWidth)) 95 | res 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/scala/cache/ICache.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import cpu.defs.ConstantVal 4 | import ip._ 5 | import spinal.core._ 6 | import spinal.lib._ 7 | import spinal.lib.fsm._ 8 | import spinal.lib.bus.amba4.axi._ 9 | import scala.language.postfixOps 10 | 11 | class CPUICacheInterface(config: CacheRamConfig) extends Bundle with IMasterSlave { 12 | val stage1 = new Bundle { 13 | val read = Bool //是否读 14 | val index = UInt(config.indexWidth bits) //从地址中截取出虚索引 15 | // val keepRData = Bool //当前周期拉高这个信号,那么后面会保持**当前输入**index所读出的值 16 | // val cacheTags = Vec(Meta(config.tagWidth), config.wayNum) //读出来的各路tag 17 | // val cacheDatas = Vec(Block(config.blockSize), config.wayNum) //读出来的各路数据 18 | // val replaceIndex = UInt(log2Up(config.wayNum) bits) //LRU读出来要替换的路编号 19 | } 20 | 21 | val stage2 = new Bundle { 22 | val en = Bool //如果stage1检测到异常,那么拉低该信号就不会再进行访存 23 | val paddr = UInt(32 bits) //用来进行tag比较 24 | val rdata = Bits(64 bits) //返回读出来的数据 25 | val stall = Bool 26 | } 27 | 28 | override def asMaster(): Unit = { 29 | out(stage1.read, stage1.index, stage2.paddr, stage2.en) 30 | in(stage2.rdata, stage2.stall) 31 | } 32 | } 33 | 34 | /** @note ICache采用两级流水: 35 | * 第一级读Ram(包括data和tag等信息) 36 | * 第二级若命中则更新cache并且返回数据;否则暂停流水线进行访存 37 | */ 38 | class ICache(config: CacheRamConfig) extends Component { 39 | private implicit class ParseAddr(addr: UInt) { 40 | assert(addr.getBitsWidth == 32) 41 | def index: UInt = addr(config.offsetWidth, config.indexWidth bits) 42 | 43 | def cacheTag: Bits = addr(config.offsetWidth + config.indexWidth, config.tagWidth bits).asBits 44 | 45 | def bankOffset: UInt = addr(log2Up(config.bankSize), config.bankOffsetWidth bits) 46 | } 47 | 48 | val io = new Bundle { 49 | val cpu = slave(new CPUICacheInterface(config)) 50 | val axi = master( 51 | new Axi4(ConstantVal.AXI_BUS_CONFIG) 52 | ) 53 | } 54 | //解析Stage 2的物理地址 55 | val stage2 = new Area { 56 | val bankOffset: UInt = io.cpu.stage2.paddr.bankOffset 57 | val index: UInt = io.cpu.stage2.paddr.index 58 | val tag: Bits = io.cpu.stage2.paddr.cacheTag 59 | } 60 | 61 | val cacheRam = new Area { 62 | val dataRamConfig = BRamIPConfig( 63 | Block.getBitWidth(config.blockSize), 64 | ) 65 | val depth: Int = dataRamConfig.depth 66 | val tagBitWidth: Int = Meta.getBitWidth(config.tagWidth) 67 | val tagRamConfig = BRamIPConfig(tagBitWidth, depth * tagBitWidth) 68 | val tags = Array.fill(config.wayNum)(new SimpleDualPortBram(tagRamConfig)) 69 | val datas = Array.fill(config.wayNum)(new SimpleDualPortBram(dataRamConfig)) 70 | } 71 | 72 | val axiDefault = new Area { 73 | // Default value of AXI singal 74 | io.axi.ar.id := U"4'b0000" 75 | io.axi.ar.addr := io.cpu.stage2.paddr(config.offsetWidth until 32) @@ U( 76 | 0, 77 | config.offsetWidth bits 78 | ) 79 | io.axi.ar.lock := 0 80 | io.axi.ar.cache := 0 81 | io.axi.ar.prot := 0 82 | io.axi.ar.len := config.wordNum - 1 83 | io.axi.ar.size := U"3'b010" //2^2 = 4Bytes 84 | io.axi.ar.burst := B"2'b01" //INCR 85 | io.axi.ar.valid := False 86 | //r 87 | io.axi.r.ready := False 88 | //Write is always disabled for ICache 89 | //aw 90 | io.axi.aw.addr := 0 91 | io.axi.aw.id := U"4'b0000" 92 | io.axi.aw.lock := 0 93 | io.axi.aw.cache := 0 94 | io.axi.aw.prot := 0 95 | io.axi.aw.len := 0 96 | io.axi.aw.size := U"3'b010" //2^2 = 4Bytes 97 | io.axi.aw.burst := B"2'b01" //INCR 98 | io.axi.aw.valid := False 99 | //w 100 | io.axi.w.valid := False 101 | io.axi.w.last := False 102 | io.axi.w.strb := B"4'b1111" 103 | io.axi.w.data := 0 104 | //b 105 | io.axi.b.ready := False 106 | } 107 | 108 | /** ********************************* 109 | * ************ STAGE 1 ********** 110 | * ********************************* 111 | */ 112 | //only drive portB for read 113 | // 读ram 114 | val read = new Bundle { 115 | val tags = Vec(Meta(config.tagWidth), config.wayNum) 116 | val datas = Vec(Block(config.blockSize, config.bankSize), config.wayNum) 117 | val addr = UInt(config.indexWidth bits) 118 | addr := io.cpu.stage1.index 119 | for (i <- 0 until config.wayNum) { 120 | tags(i).assignFromBits(cacheRam.tags(i).io.portB.dout) 121 | datas(i).assignFromBits(cacheRam.datas(i).io.portB.dout) 122 | } 123 | } 124 | for (i <- 0 until config.wayNum) { 125 | cacheRam.tags(i).io.portB.en := True 126 | cacheRam.tags(i).io.portB.addr := read.addr 127 | // cacheRam.tags(i).io.portB.we := False 128 | // cacheRam.tags(i).io.portB.din.assignDontCare() 129 | 130 | cacheRam.datas(i).io.portB.en := True 131 | cacheRam.datas(i).io.portB.addr := read.addr 132 | // cacheRam.datas(i).io.portB.we := False 133 | // cacheRam.datas(i).io.portB.din.assignDontCare() 134 | } 135 | 136 | /** ********************************* 137 | * ************ STAGE 2 ********** 138 | * ********************************* 139 | */ 140 | 141 | val LRU = new Area { 142 | val mem = new LRUManegr(config.wayNum, config.setNum) 143 | // LRU写端口 144 | val access = UInt(log2Up(config.wayNum) bits) 145 | val we = False //默认为False 146 | mem.write.access := access 147 | mem.write.en := we 148 | mem.write.addr := stage2.index 149 | } 150 | 151 | // LRU不必担心同时读写一个地址的问题,因为真正使用LRU计算替换地址 152 | // 是在readMem的末尾阶段,此时不可能写LRU 153 | val replace = new Area { 154 | val wayIndex = LRU.mem.latestWayIndex(stage2.index) 155 | for (i <- 0 until config.wayNum) { 156 | when(!read.tags(i).valid)(wayIndex := i) //如果有未使用的cache line,优先替换它 157 | } 158 | } 159 | 160 | //判断是否命中cache,并得到要返回的值 161 | io.cpu.stage2.rdata := ConstantVal.INST_NOP.asBits ## ConstantVal.INST_NOP.asBits 162 | val hit = False 163 | val hitIndex = U(0, log2Up(config.wayNum) bits) 164 | for (i <- 0 until config.wayNum) { 165 | when((read.tags(i).tag === stage2.tag) & read.tags(i).valid) { 166 | hit := True 167 | hitIndex := i 168 | io.cpu.stage2.rdata := read.datas(i)(stage2.bankOffset) 169 | } 170 | } 171 | 172 | //更新LRU 173 | LRU.access := hitIndex 174 | when(io.cpu.stage2.en & hit) (LRU.we := True) 175 | 176 | //only drive portA for write 177 | val write = new Bundle { 178 | val en = B(0, config.wayNum bits) 179 | val meta = Meta(config.tagWidth) 180 | val data = Block(config.blockSize) 181 | val addr = UInt(config.indexWidth bits) 182 | addr := stage2.index 183 | } 184 | for (i <- 0 until config.wayNum) { 185 | cacheRam.tags(i).io.portA.en := True 186 | cacheRam.tags(i).io.portA.we := write.en(i) 187 | cacheRam.tags(i).io.portA.addr := write.addr 188 | cacheRam.tags(i).io.portA.din := write.meta.asBits 189 | 190 | cacheRam.datas(i).io.portA.en := True 191 | cacheRam.datas(i).io.portA.we := write.en(i) 192 | cacheRam.datas(i).io.portA.addr := write.addr 193 | cacheRam.datas(i).io.portA.din := write.data.asBits 194 | } 195 | 196 | //不命中处理:访存,并且写Cache Ram 197 | val counter = Counter(0 until config.wordNum) //从内存读入数据的计数器 198 | val recvBlock = Reg(Block(config.blockSize)) //从内存中读出的一行的寄存器 199 | 200 | val icacheFSM = new StateMachine { 201 | val waitAXIReady = new State 202 | val readMem = new State 203 | val refill = new State 204 | val finish = new State 205 | setEntry(stateBoot) 206 | disableAutoStart() 207 | 208 | stateBoot 209 | .whenIsActive { 210 | when(io.cpu.stage2.en & !hit)(goto(waitAXIReady)) 211 | } 212 | 213 | waitAXIReady.whenIsActive { 214 | io.axi.ar.valid := True 215 | counter.clear() 216 | when(io.axi.ar.ready)(goto(readMem)) 217 | } 218 | 219 | readMem.whenIsActive { 220 | io.axi.r.ready := True 221 | when(io.axi.r.valid) { 222 | counter.increment() 223 | recvBlock.banks(counter.value) := io.axi.r.data 224 | } 225 | when(io.axi.r.valid & io.axi.r.last) { 226 | write.en(replace.wayIndex) := True 227 | // 重写Cache的时候也需要更新LRU 228 | when(io.cpu.stage2.en) { 229 | LRU.we := True 230 | LRU.access := replace.wayIndex 231 | } 232 | //不命中则从writeData返回 233 | goto(refill) 234 | } 235 | } 236 | 237 | refill.whenIsActive { 238 | read.addr := stage2.index 239 | goto(finish) 240 | } 241 | finish.whenIsActive(goto(stateBoot)) 242 | } 243 | io.cpu.stage2.stall := !(icacheFSM.isActive(icacheFSM.finish) | 244 | icacheFSM.isActive(icacheFSM.stateBoot) & (!io.cpu.stage2.en | hit)) 245 | 246 | //写回cache ram的数据 247 | write.meta.tag := stage2.tag 248 | write.meta.valid := True 249 | for(i <- 0 until config.wordNum - 1) { 250 | write.data(i) := recvBlock(i) 251 | } 252 | write.data(config.wordNum - 1) := io.axi.r.data 253 | } 254 | 255 | object ICache { 256 | def main(args: Array[String]): Unit = { 257 | SpinalVerilog(new ICache(CacheRamConfig(wayNum = 4, bankSize = 8))).printPruned() 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/scala/cache/LRUManager.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import spinal.core._ 4 | import lib.Updating 5 | import scala.language.postfixOps 6 | 7 | object LRUManegr { 8 | def main(args: Array[String]): Unit = { 9 | // SpinalVerilog(new LRUManegr(8)) 10 | SpinalVerilog(new Component { 11 | val io = new Bundle { 12 | val x = in Bool () 13 | val y = out Bits (10 bits) 14 | } 15 | val temp = Vec(Reg(Bits(1 bits)) init (0), 10) 16 | io.y := temp.asBits 17 | }) 18 | } 19 | } 20 | 21 | class PLRU(bitLength: Int) extends Bundle { 22 | val prev = Vec(Reg(Bool) init (False), bitLength) 23 | val next = Vec(Bool, bitLength) 24 | } 25 | 26 | object LRUCalculator { 27 | def statusLength(wayNum: Int): Int = { 28 | wayNum match { 29 | case 2 => 1 30 | case 4 => 3 31 | case 8 => 7 32 | } 33 | } 34 | 35 | } 36 | 37 | /** @param wayNum 表示路数,仅支持2 4 8路组 38 | * @note 记录LRU需要的数据,cache采用的是Tree-based pseudo-LRU algorithm 39 | * @note 0表示上一半最近被使用 40 | * 1表示下一半最近被使用 41 | */ 42 | case class LRUCalculator(wayNum: Int) { 43 | assert(wayNum == 2 || wayNum == 4 || wayNum == 8) 44 | 45 | private def way2(status: Bits): UInt = { 46 | assert(status.getBitsWidth == 1) 47 | (!status(0)).asUInt 48 | } 49 | 50 | private def way4(status: Bits): UInt = { 51 | assert(status.getBitsWidth == 3) 52 | status(0) ? way2(status(1).asBits).resize(2) | 53 | U"1'b1" @@ way2(status(2).asBits) 54 | } 55 | 56 | private def way8(status: Bits): UInt = { 57 | assert(status.getBitsWidth == 7) 58 | status(0) ? way4(status(3 downto 1)).resize(3) | 59 | U"1'b1" @@ way4(status(6 downto 4)) 60 | } 61 | 62 | def leastRecentUsedIndex(status: Bits): UInt = { 63 | assert(status.getBitsWidth == LRUCalculator.statusLength(wayNum)) 64 | 65 | wayNum match { 66 | case 2 => way2(status.asBits) 67 | case 4 => way4(status.asBits) 68 | case 8 => way8(status.asBits) 69 | } 70 | } 71 | 72 | private def updateWay2(access: Bits, status: Bits): Bits = { 73 | assert(access.getBitsWidth == 1 && status.getBitsWidth == 1) 74 | access(0).asBits 75 | } 76 | 77 | private def updateWay4(access: Bits, status: Bits): Bits = { 78 | assert(access.getBitsWidth == 2 && status.getBitsWidth == 3) 79 | val res = Bits(3 bits) 80 | when(access(1)) { 81 | res := access(0) ## status(1) ## access(1) 82 | }.otherwise { 83 | res := status(2) ## access(0) ## access(1) 84 | } 85 | res 86 | } 87 | 88 | private def updateWay8(access: Bits, status: Bits): Bits = { 89 | assert(access.getBitsWidth == 3 && status.getBitsWidth == 7) 90 | val res = Bits(7 bits) 91 | when(access(2)) { 92 | res := updateWay4(access(1 downto 0), status(6 downto 4)) ## status(3 downto 1) ## access(2) 93 | }.otherwise { 94 | res := status(6 downto 4) ## updateWay4(access(1 downto 0), status(3 downto 1)) ## access(2) 95 | } 96 | res 97 | } 98 | 99 | def updateStatus(access: UInt, status: Bits): Bits = { 100 | assert(status.getBitsWidth == LRUCalculator.statusLength(wayNum)) 101 | wayNum match { 102 | case 2 => updateWay2(access.asBits, status) 103 | case 4 => updateWay4(access.asBits, status) 104 | case 8 => updateWay8(access.asBits, status) 105 | } 106 | } 107 | } 108 | 109 | //记录LRU需要的数据,cache采用的是Tree-based pseudo-LRU algorithm 110 | //0表示上一半最近被使用 111 | //1表示下一半最近被使用 112 | /** @wayNum 路数 113 | * @setSize 有多少项 114 | * @note 每一项对应一个cache的set,一个端口读,一个端口写,并且内置数据前传 115 | */ 116 | class LRUManegr(wayNum: Int, setSize: Int) extends Area { 117 | val width: Int = LRUCalculator.statusLength(wayNum) 118 | val calculator = new LRUCalculator(wayNum) 119 | 120 | val write = new Bundle { 121 | val addr = UInt (log2Up(setSize) bits) 122 | val access = UInt (log2Up(wayNum) bits) // 最近访问的编号 123 | val en = Bool () //写使能 124 | } 125 | 126 | val data = Vec(Reg(Bits(width bits)) init 0, setSize) 127 | val wdata = calculator.updateStatus(write.access, data(write.addr)) 128 | when(write.en) { 129 | data(write.addr) := wdata 130 | } 131 | 132 | def latestWayIndex(readAddr: UInt): UInt = { 133 | val addrWidth: Int = log2Up(setSize) 134 | assert(readAddr.getBitsWidth == addrWidth) 135 | val res = UInt(log2Up(wayNum) bits) 136 | res := calculator.leastRecentUsedIndex(data(readAddr)) 137 | res 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/scala/cache/WriteBuffer.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | import scala.language.postfixOps 6 | 7 | //depth: Fifo的大小 8 | case class WriteBufferConfig(blockSize: Int, tagWidth: Int, depth: Int = 16) { 9 | def dataWidth: Int = blockSize * 8 //数据的位宽 10 | 11 | def counterWidth: Int = log2Up(depth) + 1 //log2(depth) + 1 12 | 13 | def memAddrWidth: Int = log2Up(depth) 14 | 15 | def wordIndexWidth: Int = log2Up(blockSize / 4) 16 | } 17 | 18 | object WriteBuffer { 19 | def main(args: Array[String]): Unit = { 20 | SpinalVerilog(new WriteBuffer(WriteBufferConfig(32, 27, 8))) 21 | } 22 | } 23 | 24 | /** @note 第一个周期组合逻辑查询,第二个周期出数据或者给出写入数据 25 | * 内置前传逻辑 26 | */ 27 | class WriteBufferInterface(config: WriteBufferConfig) extends Bundle with IMasterSlave { 28 | val query = new Bundle { 29 | val tag: Bits = Bits(config.tagWidth bits) //要查询的tag 30 | val hit: Bool = Bool 31 | val rdata: Bits = Bits(32 bits) 32 | } 33 | val merge = new Bundle { 34 | //要查询和写合并的信号 35 | val write: Bool = Bool //是否把queryWData写入对应命中的cache中 36 | val byteEnable: Bits = Bits(4 bits) //要写入FIFO数据的byteEnable信号,长度为一个cache line的byte数 37 | val wdata: Word = Word() //要写入FIFO对应的数据 38 | val hit: Bool = Bool 39 | } 40 | val index: UInt = UInt(config.wordIndexWidth bits) //query或者merge对应的字偏移 41 | //要压入的数据 42 | val pushTag: Bits = Bits(config.tagWidth bits) 43 | val pushData = Block(config.blockSize) 44 | //要弹出的数据 45 | val popTag: Bits = Bits(config.tagWidth bits) 46 | val popData = Block(config.blockSize) 47 | val push: Bool = Bool //是否压入数据 48 | val pop: Bool = Bool //是否弹出数据 49 | 50 | val empty: Bool = Bool 51 | val full: Bool = Bool 52 | 53 | override def asMaster(): Unit = { 54 | in(query.rdata, query.hit, popTag, merge.hit, popData, empty, full) 55 | out( 56 | query.tag, 57 | merge.write, 58 | merge.byteEnable, 59 | merge.wdata, 60 | index, 61 | pushTag, 62 | pushData, 63 | push, 64 | pop 65 | ) 66 | } 67 | } 68 | 69 | //一个先进先出队列,由于是全连接,不适合使用XPM_MEMORY,直接用Reg存 70 | //用来缓存要写入内存的数据 71 | //blockSize: byte,一个cache block 的大小 72 | //tagWidth:表示队列中要存储的tag宽度,应该是cache中tag+index 73 | // 74 | //如果当某个数据进入Fifo后,再次被命中,那么该数据在Fifo中的位置不会改变(但是对应的数据会被加入cache中去) 75 | // 76 | //如果某个数据这个周期被请求pop,那么这个周期的写不会命中,因为下个周期才会被写入,但是数据已经不是Fifo中的有效数据了 77 | class WriteBuffer(config: WriteBufferConfig) extends Component { 78 | val io = slave(new WriteBufferInterface(config)) 79 | 80 | /* 81 | * Signal and Reg Definition 82 | */ 83 | val fifo = new Area { 84 | val tag = Vec(Reg(Bits(config.tagWidth bits)), config.depth) 85 | val data = Vec(Reg(Block(config.blockSize)), config.depth) 86 | val valid = Vec(Reg(Bool) init False, config.depth) 87 | } 88 | val popPtr = Counter(config.depth) 89 | val pushPtr = Counter(config.depth) 90 | val ptrMatch = popPtr === pushPtr 91 | val risingOccupancy = RegInit(False) 92 | when(io.push =/= io.pop)(risingOccupancy := io.push) 93 | io.full := risingOccupancy & ptrMatch 94 | io.empty := !risingOccupancy & ptrMatch 95 | /* 96 | * Default Value 97 | */ 98 | io.popTag := fifo.tag(popPtr.value) 99 | io.popData := fifo.data(popPtr.value) 100 | //logic of query read 101 | io.query.rdata.assignDontCare() 102 | io.query.hit := False 103 | io.merge.hit := False 104 | 105 | /* 106 | * Logic 107 | */ 108 | val writeData = Word() 109 | for (i <- 0 until config.depth) { 110 | //如果当前第i个是head并且正在pop,那么不命中,应该考虑去WB中寻找这个数据 111 | val hit = fifo.tag(i) === io.query.tag & fifo.valid(i) 112 | when(hit) { 113 | io.query.hit := True 114 | io.query.rdata := fifo.data(i)(io.index) 115 | } 116 | when(hit & !(io.pop & !io.empty & popPtr === i)) { 117 | io.merge.hit := True 118 | } 119 | when(hit & !(io.pop & !io.empty & popPtr === i) & io.merge.write) { 120 | fifo.data(i).banks(io.index) := writeData.asBits 121 | } 122 | } 123 | //logic of push and pop 124 | when(io.push & !io.full) { 125 | fifo.tag(pushPtr.value) := io.pushTag 126 | fifo.data(pushPtr.value).assignFromBits(io.pushData.asBits) 127 | fifo.valid(pushPtr.value) := True 128 | pushPtr.increment() 129 | } 130 | when(io.pop & !io.empty) { 131 | fifo.valid(popPtr.value) := False //把对应的数据无效了 132 | popPtr.increment() 133 | } 134 | 135 | //logic of write merge 136 | writeData := io.query.rdata 137 | for (j <- 0 until 4) { 138 | when(io.merge.byteEnable(j))(writeData(j) := io.merge.wdata(j)) 139 | } 140 | } 141 | 142 | case class Word(wordSize: Int = 4) extends Bundle { 143 | val data = Vec(Bits(8 bits), wordSize) 144 | def apply(idx: Int): Bits = data(idx) 145 | def apply(addr: UInt): Bits = data(addr) 146 | def :=(that: Bits): Unit = data.assignFromBits(that) 147 | } 148 | -------------------------------------------------------------------------------- /src/main/scala/cpu/BPU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | 5 | class BPU( 6 | bhtNumEntries: Int, 7 | bhtIndexWidth: Int, 8 | bhtDataWidth: Int, 9 | phtNumEntries: Int, 10 | phtIndexWidth: Int 11 | ) extends Component { 12 | val bht = Mem(Bits(bhtDataWidth bits), bhtNumEntries) randBoot 13 | val pht = Mem(UInt(2 bits), phtNumEntries) randBoot 14 | 15 | val io = new Bundle { 16 | val r = new Bundle { 17 | val bhtI = in UInt (bhtIndexWidth bits) 18 | val bhtV = out Bits (bhtDataWidth bits) 19 | 20 | val phtI = in UInt (phtIndexWidth bits) 21 | val phtV = out UInt (2 bits) 22 | } 23 | val w = new Bundle { 24 | val bhtEn = in Bool () 25 | val bhtI = in UInt (bhtIndexWidth bits) 26 | val bhtV = in Bits (bhtDataWidth bits) 27 | 28 | val phtEn = in Bool () 29 | val phtI = in UInt (phtIndexWidth bits) 30 | val phtV = in UInt (2 bits) 31 | } 32 | } 33 | 34 | io.r.bhtV := (io.w.bhtEn & io.w.bhtI === io.r.bhtI) ? 35 | io.w.bhtV | bht.readAsync(io.r.bhtI, writeFirst) 36 | // io.r.phtV := (io.w.phtEn & io.w.phtI === io.r.phtI) ? 37 | // io.w.phtV | pht.readAsync(io.r.phtI, writeFirst) 38 | io.r.phtV := pht.readAsync(io.r.phtI, writeFirst) 39 | bht.write(io.w.bhtI, io.w.bhtV, io.w.bhtEn) 40 | pht.write(io.w.phtI, io.w.phtV, io.w.phtEn) 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/cpu/BranchPredictor.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import ip._ 5 | import lib._ 6 | import defs.Config._ 7 | 8 | object BranchPredictor { 9 | val btbNumEntries = BTB.NUM_ENTRIES 10 | val btbNumWays = BTB.NUM_WAYS 11 | val btbIndexWidth = BTB.INDEX_WIDTH 12 | val bhtNumEntries = BHT.NUM_ENTRIES 13 | val bhtIndexWidth = BHT.INDEX_WIDTH 14 | val bhtDataWidth = BHT.DATA_WIDTH 15 | val phtNumEntries = PHT.NUM_ENTRIES 16 | val phtIndexWidth = PHT.INDEX_WIDTH 17 | 18 | object Address { 19 | val btbIndexOffset = 3 20 | val btbTagOffset = btbIndexOffset + btbIndexWidth 21 | val btbTagWidth = 32 - btbTagOffset 22 | 23 | val btbMetaWidth = 1 + btbTagWidth 24 | 25 | val bhtIndexOffset = 3 26 | val bhtBaseOffset = bhtIndexOffset + bhtIndexWidth 27 | 28 | val phtIndexOffset = 3 29 | 30 | def apply(): Address = new Address() 31 | } 32 | 33 | class Address extends UInt { 34 | setWidth(32) 35 | 36 | import Address._ 37 | def btbIndex: UInt = this(btbIndexOffset, btbIndexWidth bits) 38 | def btbTag: UInt = this(btbTagOffset, btbTagWidth bits) 39 | def bhtBase: UInt = this(bhtBaseOffset, bhtIndexWidth bits) 40 | def bhtIndex: UInt = this(bhtIndexOffset, bhtIndexWidth bits) 41 | def phtIndex: UInt = this(phtIndexOffset, phtIndexWidth bits) 42 | } 43 | 44 | val pc = Key(Address()).setName("bp_pc") 45 | val isDJump = Key(Bool()).setEmptyValue(False).setName("bp_isDJump") 46 | val jumpPC = Key(UInt(32 bits)).setName("bp_jumpPC") 47 | 48 | val btbHitLine = Key(Bits(btbNumWays bits)).setName("bp_btbHitLine") 49 | val btbHit = Key(Bool()).setEmptyValue(False).setName("bp_btbHit") 50 | val btbOH = Key(Bits(btbNumWays bits)).setName("bp_btbOH") 51 | val btbMeta = Key(Bits(Address.btbMetaWidth bits)).setName("bp_btbMeta") 52 | val btbData = Key(Bits(30 bits)).setName("bp_btbData") 53 | val btbP = Key(Bits(btbNumWays - 1 bits)).setName("bp_btbP") 54 | 55 | val bhtI = Key(UInt(bhtIndexWidth bits)).setName("bp_bhtI") 56 | val bhtV = Key(Bits(bhtDataWidth bits)).setName("bp_bhtV") 57 | val phtI = Key(UInt(phtIndexWidth bits)).setName("bp_phtI") 58 | val phtV = Key(UInt(2 bits)).setName("bp_phtV") 59 | val phtVP = Key(UInt(2 bits)).setName("bp_phtVP") 60 | val phtVM = Key(UInt(2 bits)).setName("bp_phtVM") 61 | } 62 | 63 | class BranchPredictor(number: Int) extends Area { 64 | setName(s"bp_$number") 65 | 66 | import BranchPredictor._ 67 | val btb = new Cache(btbNumEntries / btbNumWays, btbNumWays, btbIndexWidth, Address.btbMetaWidth, 30) 68 | val bpu = new BPU(bhtNumEntries, bhtIndexWidth, bhtDataWidth, phtNumEntries, phtIndexWidth) 69 | 70 | val write2 = new ComponentStage { 71 | val io = new Bundle { 72 | val assignJump = Bool() 73 | } 74 | /**/ 75 | btb.io.w.en := !!!(btbHit) | !!!(isDJump) 76 | // btb.io.w.en := stored(btbHit) | stored(isDJump) 77 | 78 | btb.io.w.oh := stored(btbOH) 79 | btb.io.w.index := stored(pc).btbIndex 80 | btb.io.w.meta := stored(btbMeta) 81 | btb.io.w.data := stored(btbData) 82 | btb.io.w.p := stored(btbP) 83 | /**/ 84 | bpu.io.w.bhtEn := !!!(isDJump) 85 | bpu.io.w.phtEn := !!!(isDJump) 86 | // bpu.io.w.bhtEn := stored(isDJump) 87 | // bpu.io.w.phtEn := stored(isDJump) 88 | 89 | bpu.io.w.bhtI := stored(bhtI) 90 | bpu.io.w.bhtV := stored(bhtV)(0, bhtDataWidth - 1 bits) ## io.assignJump 91 | bpu.io.w.phtI := stored(phtI) 92 | bpu.io.w.phtV := io.assignJump ? stored(phtVP) | stored(phtVM) 93 | } 94 | 95 | val write1 = new ComponentStage { 96 | val io = new Bundle { 97 | val issue = IssueEntry() 98 | } 99 | /**/ 100 | val jumpPC = io.issue.target 101 | val btbHitLine = io.issue.predict.btbHitLine 102 | val btbP = io.issue.predict.btbP 103 | val phtV = io.issue.predict.phtV 104 | /**/ 105 | output(pc) := io.issue.pc 106 | output(isDJump) := io.issue.branch.isDjump 107 | output(btbHit) := io.issue.predict.btbHit 108 | output(bhtV) := io.issue.predict.bhtV 109 | /**/ 110 | val valid = output(isDJump) 111 | val we = output(btbHit) ? btbHitLine | btb.plru(btbP) 112 | output(btbOH) := we 113 | output(btbMeta) := valid ## output(pc).btbTag 114 | output(btbData) := B(jumpPC)(2, 30 bits) 115 | btb.getP(btbP, we, valid, output(BranchPredictor.btbP)) 116 | /**/ 117 | output(bhtI) := output(pc).bhtBase ^ output(pc).bhtIndex 118 | output(phtI) := U(output(bhtV), phtIndexWidth bits) ^ output(pc).phtIndex 119 | output(phtVP) := U((phtV(1) | phtV(0)) ## (phtV(1) | !phtV(0))) 120 | output(phtVM) := U((phtV(1) & phtV(0)) ## (phtV(1) & !phtV(0))) 121 | } 122 | 123 | val read2 = new ComponentStage { 124 | val io = new Bundle { 125 | val btbHitLine = Bits(btbNumWays bits) 126 | val btbHit = Bool() 127 | val btbP = Bits(btbNumWays - 1 bits) 128 | val bhtV = Bits(bhtDataWidth bits) 129 | val phtV = UInt(2 bits) 130 | /**/ 131 | val assignJump = Bool() 132 | val jumpPC = UInt(32 bits) 133 | } 134 | /**/ 135 | bpu.io.r.phtI := stored(phtI) 136 | output(phtV) := bpu.io.r.phtV 137 | /**/ 138 | io.assignJump := output(btbHit) & output(phtV)(1) 139 | io.jumpPC := U(output(btbData) ## B"00") 140 | /**/ 141 | io.btbHitLine := output(btbHitLine) 142 | io.btbHit := output(btbHit) 143 | io.btbP := output(btbP) 144 | io.bhtV := stored(bhtV) 145 | io.phtV := output(phtV) 146 | /**/ 147 | btb.io.r.en := can.input 148 | val metaLine = btb.io.r.metaLine 149 | val dataLine = btb.io.r.dataLine 150 | output(btbP) := btb.io.r.p 151 | for (i <- 0 until BTB.NUM_WAYS) { 152 | output(btbHitLine)(i) := (True ## stored(pc).btbTag) === metaLine(i) 153 | } 154 | output(btbHit) := output(btbHitLine).orR 155 | output(btbData) := dataLine.oneHotAccess(output(btbHitLine)) 156 | } 157 | 158 | val read1 = new ComponentStage { 159 | val io = new Bundle { 160 | val nextPC = Address() 161 | val pc = Address() 162 | } 163 | /**/ 164 | btb.io.r.index := io.pc.btbIndex 165 | /**/ 166 | bpu.io.r.bhtI := io.pc.bhtBase ^ io.pc.bhtIndex 167 | output(bhtV) := bpu.io.r.bhtV 168 | output(phtI) := U(output(bhtV), phtIndexWidth bits) ^ io.pc.phtIndex 169 | /**/ 170 | output(pc) := io.pc 171 | } 172 | 173 | write2.interConnect() 174 | write1.send(write2) 175 | write1.interConnect() 176 | 177 | read2.interConnect() 178 | read1.send(read2) 179 | read1.interConnect() 180 | } 181 | -------------------------------------------------------------------------------- /src/main/scala/cpu/Comparator.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import scala.language.postfixOps 5 | 6 | object CompareOp extends SpinalEnum { 7 | /** NZ = not zero, EZ = equal zero */ 8 | val NZ, EZ = newElement() 9 | } 10 | 11 | /** 比较器,用于MOVN和MOVZ */ 12 | class Comparator extends Component { 13 | val io = new Bundle { 14 | val operand = in Bits (32 bits) 15 | val op = in(CompareOp) 16 | val mov = out Bool() 17 | } 18 | 19 | io.mov := io.op.mux( 20 | CompareOp.NZ -> (io.operand =/= 0), 21 | CompareOp.EZ -> (io.operand === 0) 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/cpu/ComponentStage.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.Utils._ 4 | import lib._ 5 | import spinal.core._ 6 | 7 | class ComponentStage extends Area { 8 | val stored = Record().setAsReg() 9 | val output = Record() 10 | val produced = Record() 11 | 12 | val will = new Bundle { 13 | val input = Bool().default(True) 14 | val output = Bool().default(False) 15 | } 16 | val can = new Bundle { 17 | val input = Bool().default(will.input) 18 | } 19 | 20 | def interConnect(): Unit = { 21 | /**/ 22 | produced.keys.foreach { case key => 23 | if (!hasAssignment(produced(key))) { 24 | if (output.contains(key)) { produced(key) := output(key) } 25 | else { produced(key) := stored(key) } 26 | } 27 | } 28 | /**/ 29 | } 30 | 31 | def bind(that: Stage): Unit = { 32 | will.output := that.will.output 33 | can.input := that.can.input 34 | } 35 | 36 | def send(that: ComponentStage): Unit = { 37 | when(that.will.input) { 38 | that.stored.keys.foreach { case key => 39 | if (hasInit(that.stored(key))) { that.stored(key) := produced(key) } 40 | } 41 | } 42 | when(that.can.input) { 43 | that.stored.keys.foreach { case key => 44 | if (!hasInit(that.stored(key))) { that.stored(key) := produced(key) } 45 | } 46 | } 47 | } 48 | def receive(that: Stage): Unit = { 49 | when(will.input) { 50 | stored.keys.foreach { case key => 51 | if (hasInit(stored(key))) { stored(key) := that.produced(key) } 52 | } 53 | } 54 | when(can.input) { 55 | stored.keys.foreach { case key => 56 | if (!hasInit(stored(key))) { stored(key) := that.produced(key) } 57 | } 58 | } 59 | } 60 | 61 | def clearWhenEmpty[T <: Data](key: Key[T]): Unit = { 62 | if (!hasInit(stored(key))) { 63 | stored(key).init(key.emptyValue.get) 64 | when(will.output & !will.input) { 65 | stored(key) := key.emptyValue.get 66 | } 67 | } 68 | } 69 | def !!![T <: Data](key: Key[T]): T = { 70 | clearWhenEmpty(key) 71 | stored(key) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/cpu/DCU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.defs.ConstantVal 4 | import spinal.core._ 5 | import Utils.{signExtend, zeroExtend} 6 | 7 | import scala.language.postfixOps 8 | 9 | // 帮助ME阶段进行地址检查和byteEnable转换 10 | class DCU1 extends Component { 11 | val io = new Bundle { 12 | val input = new Bundle { 13 | val byteOffset = in UInt (2 bits) //stage1 14 | val byteEnable = in UInt (2 bits) //stage1 15 | val wdata = in Bits (32 bits) //此时输入的wdata就是rtValue 16 | } 17 | val output = new Bundle { 18 | val wdata = out Bits (32 bits) //stage2 19 | val byteEnable = out Bits(4 bits) //stage1,只对写寄存器有用 20 | val addrValid = out Bool() //stage1 21 | } 22 | } 23 | 24 | val byteOffset = io.input.byteOffset 25 | //used by stage1 26 | switch(io.input.byteEnable) { 27 | is(0) { 28 | io.output.addrValid := True 29 | } 30 | is(1) { 31 | io.output.addrValid := byteOffset(0, 1 bits) === 0 32 | } 33 | is(3) { 34 | io.output.addrValid := byteOffset(0, 2 bits) === 0 35 | } 36 | default { 37 | io.output.addrValid := False 38 | } 39 | } 40 | 41 | switch(io.input.byteEnable) { 42 | is(0) { 43 | io.output.wdata := Vec.fill(4)(io.input.wdata(0, 8 bits)).asBits 44 | io.output.byteEnable := (B"1'b1" << byteOffset).resize(4) 45 | } 46 | is(1) { 47 | io.output.wdata := Vec.fill(2)(io.input.wdata(0, 16 bits)).asBits 48 | io.output.byteEnable := (B"2'b11" << byteOffset).resize(4) 49 | } 50 | default { 51 | // is(3) 52 | io.output.wdata := io.input.wdata 53 | io.output.byteEnable := B"4'b1111" 54 | } 55 | } 56 | 57 | } 58 | 59 | class DCU2 extends Component { 60 | val io = new Bundle { 61 | val input = new Bundle { 62 | val byteEnable = in Bits (4 bits) //这个byteEnable应该是经过DCU1转换过的 63 | val rdata = in Bits (32 bits) 64 | val extend = in(MU_EX()) 65 | } 66 | val output = new Bundle { 67 | val rdata = out Bits (32 bits) //stage2 68 | } 69 | } 70 | val signExt = Bits(32 bits) 71 | val unsignExt = Bits(32 bits) 72 | 73 | switch(io.input.byteEnable) { 74 | is(B"4'b0001") { 75 | signExt := signExtend(io.input.rdata(0, 8 bits)) 76 | unsignExt := zeroExtend(io.input.rdata(0, 8 bits)) 77 | } 78 | is(B"4'b0010") { 79 | signExt := signExtend(io.input.rdata(8, 8 bits)) 80 | unsignExt := zeroExtend(io.input.rdata(8, 8 bits)) 81 | } 82 | is(B"4'b0100") { 83 | signExt := signExtend(io.input.rdata(16, 8 bits)) 84 | unsignExt := zeroExtend(io.input.rdata(16, 8 bits)) 85 | } 86 | is(B"4'b1000") { 87 | signExt := signExtend(io.input.rdata(24, 8 bits)) 88 | unsignExt := zeroExtend(io.input.rdata(24, 8 bits)) 89 | } 90 | is(B"4'b0011") { 91 | signExt := signExtend(io.input.rdata(0, 16 bits)) 92 | unsignExt := zeroExtend(io.input.rdata(0, 16 bits)) 93 | } 94 | is(B"4'b1100") { 95 | signExt := signExtend(io.input.rdata(16, 16 bits)) 96 | unsignExt := zeroExtend(io.input.rdata(16, 16 bits)) 97 | } 98 | default { 99 | //is(B"4'b1111") 100 | signExt := io.input.rdata 101 | unsignExt := io.input.rdata 102 | } 103 | } 104 | io.output.rdata := (io.input.extend === MU_EX.s) ? signExt | unsignExt 105 | } -------------------------------------------------------------------------------- /src/main/scala/cpu/EIU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import lib.Optional 4 | import spinal.core._ 5 | import spinal.lib._ 6 | 7 | import scala.language.postfixOps 8 | 9 | object EIU_RD_SEL extends SpinalEnum { 10 | val BadVAddr, Count, Compare, Status, Cause, EPC = newElement() 11 | defaultEncoding = SpinalEnumEncoding("staticEncoding")( 12 | BadVAddr -> 0x40, 13 | Count -> 0x48, 14 | Compare -> 0x58, 15 | Status -> 0x60, 16 | Cause -> 0x68, 17 | EPC -> 0x70 18 | ) 19 | } 20 | 21 | object ExcCode extends SpinalEnum { 22 | val overflow, reservedInstruction,syscall, break, copUnusable, 23 | storeAddrError, loadAddrError, storeTLBError, loadTLBError, 24 | tlbModError, trapError = newElement() 25 | 26 | defaultEncoding = SpinalEnumEncoding("ExcCode")( 27 | tlbModError -> 0x01, 28 | loadTLBError -> 0x02, 29 | storeTLBError -> 0x03, 30 | loadAddrError -> 0x04, 31 | storeAddrError -> 0x05, 32 | reservedInstruction -> 0x0a, 33 | syscall -> 0x08, 34 | break -> 0x09, 35 | copUnusable -> 0x0b, 36 | overflow -> 0x0c, 37 | trapError -> 0x0d 38 | ) 39 | 40 | def addrRelated(error:SpinalEnumCraft[ExcCode.this.type]): Bool = { 41 | Utils.equalAny(error, loadAddrError, storeAddrError) 42 | } 43 | 44 | def tlbRelated(error:SpinalEnumCraft[ExcCode.this.type]): Bool = { 45 | Utils.equalAny(error, loadTLBError, storeTLBError, tlbModError) 46 | } 47 | 48 | /** Interrupt对应的ExcCode */ 49 | def Int: Bits = B"5'h0" 50 | } 51 | 52 | class EXCEPTION extends Bundle { 53 | val instFetch = Bool 54 | val excCode = Optional(ExcCode()) 55 | } 56 | -------------------------------------------------------------------------------- /src/main/scala/cpu/HLU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | 5 | object HLU_SRC extends SpinalEnum { 6 | val rs, alu = newElement() 7 | } 8 | 9 | class HLU extends Component { 10 | val hi_we = in Bool () 11 | val new_hi = in Bits (32 bits) 12 | val hi_v = out Bits (32 bits) 13 | 14 | val lo_we = in Bool () 15 | val new_lo = in Bits (32 bits) 16 | val lo_v = out Bits (32 bits) 17 | 18 | hi_v := RegNextWhen(new_hi, hi_we) init 0 19 | lo_v := RegNextWhen(new_lo, lo_we) init 0 20 | } 21 | 22 | object HLU { 23 | def main(args: Array[String]): Unit = { 24 | SpinalVerilog(new HLU) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/cpu/ICU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import scala.language.postfixOps 5 | 6 | class ICU extends Component { 7 | val io = new Bundle { 8 | val offset = in UInt(2 bits) //地址的末两位 9 | val addrValid = out Bool() 10 | } 11 | 12 | io.addrValid := io.offset(0, 2 bits) === U"2'b00" 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/cpu/JU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import scala.language.postfixOps 5 | 6 | object JU_OP extends SpinalEnum { 7 | val lz, gez, f, t, e, noe, lez, gz = newElement() 8 | } 9 | 10 | object JU_PC_SRC extends SpinalEnum { 11 | val rs, offset, index = newElement() 12 | } 13 | 14 | /** @note 加入分支预测后,JU的功能是在EX阶段判断是否发生跳转 */ 15 | class JU extends Component { 16 | 17 | // in 18 | val op = in(JU_OP) 19 | val a = in SInt (32 bits) 20 | val b = in SInt (32 bits) 21 | 22 | // out 23 | val jump = out Bool () 24 | 25 | jump := op.mux( 26 | JU_OP.lz -> (a < 0), 27 | JU_OP.gez -> (a >= 0), 28 | JU_OP.f -> False, 29 | JU_OP.t -> True, 30 | JU_OP.lez -> (a <= 0), 31 | JU_OP.gz -> (a > 0), 32 | JU_OP.noe -> (a =/= b), 33 | JU_OP.e -> (a === b) 34 | ) 35 | } 36 | 37 | object JU { 38 | def main(args: Array[String]): Unit = { 39 | SpinalVerilog(new JU) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/cpu/MU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import spinal.lib.{cpu => _, _} 5 | 6 | object MU_EX extends SpinalEnum { 7 | val s, u = newElement() 8 | } 9 | -------------------------------------------------------------------------------- /src/main/scala/cpu/MyCpuTop.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import defs.ConstantVal 4 | import ip.CrossBarIP 5 | 6 | import spinal.core._ 7 | import spinal.lib.{cpu => _, _} 8 | import spinal.lib.bus.amba4.axi._ 9 | import scala.language.postfixOps 10 | import STAGES._ 11 | 12 | case class DebugInterface() extends Bundle { 13 | val wb = new Bundle { 14 | val pc = UInt(32 bits) 15 | val rf = new Bundle { 16 | val wen = Bits(4 bits) 17 | val wnum = UInt(5 bits) 18 | val wdata = Bits(32 bits) 19 | } 20 | } 21 | } 22 | 23 | class MyCPUTop extends Component { 24 | setDefinitionName("mycpu_top") 25 | 26 | val io = new Bundle { 27 | val ext_int = in Bits (6 bits) 28 | 29 | val aclk = in Bool () 30 | val daclk = in Bool () 31 | val aresetn = in Bool () 32 | 33 | val axi = master(Axi4(ConstantVal.AXI_BUS_CONFIG)) 34 | 35 | val wid = out(UInt(ConstantVal.AXI_BUS_CONFIG.idWidth bits)) 36 | 37 | val debug = out(DebugInterface()) 38 | } 39 | 40 | val aClockDomain = ClockDomain( 41 | clock = io.aclk, 42 | reset = io.aresetn, 43 | config = ClockDomainConfig( 44 | resetActiveLevel = LOW 45 | ) 46 | ) 47 | 48 | val clockingArea = new ClockingArea(aClockDomain) { 49 | val cpu = new MultiIssueCPU 50 | val crossbar = CrossBarIP(3, 1) //3 x 1 AXI4 CrossBar 51 | CrossBarIP.connect( 52 | crossbar, 53 | Array(cpu.io.icacheAXI, cpu.io.dcacheAXI, cpu.io.uncacheAXI), 54 | Array(io.axi) 55 | ) 56 | cpu.io.externalInterrupt := io.ext_int 57 | // 每次发出写请求时就寄存一次wid 58 | val wid = RegNextWhen(io.axi.aw.id, io.axi.aw.valid, U(0)) 59 | io.wid := wid 60 | }.setName("") 61 | 62 | val ca = new ClockingArea( 63 | ClockDomain( 64 | io.daclk, 65 | io.aresetn, 66 | config = ClockDomainConfig( 67 | resetActiveLevel = LOW 68 | ) 69 | ) 70 | ) { 71 | val sel = RegInit(False) 72 | sel := !sel 73 | 74 | val cpu = clockingArea.cpu 75 | 76 | val wb = io.debug.wb 77 | when(sel) { 78 | wb.rf.wen := B(4 bits, default -> cpu.p1(WB).stored(cpu.rfuWE).pull) 79 | wb.rf.wdata := cpu.p1(WB).stored(cpu.rfuData).pull 80 | wb.rf.wnum := cpu.p1(WB).stored(cpu.rfuIndex).pull 81 | wb.pc := cpu.p1(WB).stored(cpu.pc).pull 82 | } otherwise { 83 | wb.rf.wen := B(4 bits, default -> cpu.p0(WB).stored(cpu.rfuWE).pull) 84 | wb.rf.wdata := cpu.p0(WB).stored(cpu.rfuData).pull 85 | wb.rf.wnum := cpu.p0(WB).stored(cpu.rfuIndex).pull 86 | wb.pc := cpu.p0(WB).stored(cpu.pc).pull 87 | } 88 | } 89 | 90 | noIoPrefix() 91 | 92 | addPrePopTask { () => 93 | io.axi.setName("") 94 | 95 | for (bt <- io.axi.flatten) { 96 | bt.setName(bt.getName().replace("payload", "").replace("_", "")) 97 | } 98 | } 99 | } 100 | 101 | object Generate { 102 | def main(args: Array[String]): Unit = { 103 | val report = SpinalVerilog(SpinalConfig(removePruned = true))(new MyCPUTop) 104 | report.mergeRTLSource("mergeRTL") 105 | report.printPruned() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/scala/cpu/RFU.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | 6 | object RFU_RD_SRC extends SpinalEnum { 7 | val pc, alu, hi, lo, mu, cp0 = newElement() 8 | } 9 | 10 | case class RegWrite() extends Bundle { 11 | val index = UInt(5 bits) 12 | val data = Bits(32 bits) 13 | } 14 | 15 | case class RegRead() extends Bundle with IMasterSlave { 16 | val index = UInt(5 bits) 17 | val data = Bits(32 bits) 18 | 19 | def asMaster = { 20 | out(index) 21 | in(data) 22 | } 23 | } 24 | 25 | class RFU(numW: Int = 1, numR: Int = 2) extends Component { 26 | /// 读写寄存器 27 | 28 | val io = new Bundle { 29 | val w = Vec(slave(Flow(RegWrite())), numW) 30 | val r = Vec(slave(RegRead()), numR) 31 | } 32 | 33 | // val mem = Vec(Reg(Bits(32 bits)), 32) 34 | val mem = Vec(RegInit(B"32'h0"), 32) 35 | // mem(0).init(0) 36 | 37 | for (i <- 0 until numW) { 38 | when(io.w(i).valid) { mem(io.w(i).index) := io.w(i).data } 39 | } 40 | 41 | for (i <- 0 until numR) { 42 | io.r(i).data := mem(io.r(i).index) 43 | for (j <- 0 until numW) { 44 | when(io.w(j).valid & io.w(j).index === io.r(i).index) { 45 | io.r(i).data := io.w(j).data 46 | } 47 | } 48 | } 49 | } 50 | 51 | object RFU { 52 | def main(args: Array[String]): Unit = { 53 | SpinalVerilog(new RFU) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/scala/cpu/Stage.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.Utils._ 4 | import lib._ 5 | import spinal.core._ 6 | 7 | class Stage extends Area with ValCallbackRec { 8 | // 9 | val stored = Record().setAsReg() // stage input 10 | val input = Record() // stage component input 11 | val output = Record() // stage component output 12 | val produced = Record() // stage output 13 | 14 | // 15 | val exceptionToRaise = Optional(ExcCode()) default None // User settable 16 | val prevException = Reg(Optional(ExcCode())) init None default None 17 | val exception = Optional(ExcCode()) 18 | exception := prevException orElse exceptionToRaise 19 | 20 | // 21 | val is = new Bundle { 22 | val empty = RegInit(True) // 当前该流水段是否为空转 23 | val done = True allowOverride // 当前该流水段是否已完成工作 用户定义 24 | } 25 | // 26 | val can = new Bundle { 27 | val flush = True allowOverride // 当前该流水段能否进行结果的作废 用户定义 28 | val input = Bool // 该流水段在下一个上升沿是否能接受一条有效指令 29 | } 30 | // 31 | val assign = new Bundle { 32 | val flush = False allowOverride // 是否要布置结果作废的任务 用户定义 33 | } 34 | // 35 | val to = new Bundle { 36 | val flush = RegInit(False) // 是否有结果作废的任务 37 | } 38 | // 39 | val want = new Bundle { 40 | val flush = Bool // 该流水段当前是否需要进行结果的作废 41 | } 42 | // 43 | val will = new Bundle { 44 | val flush = Bool // 当前处理结果将被作废 45 | val input = True allowOverride // 有一条有效指令将要移入 46 | val output = True allowOverride // 有一条有效指令将要移出 47 | } 48 | // 49 | is.empty := can.input & !will.input 50 | 51 | can.input := is.empty | will.output 52 | 53 | to.flush := want.flush & !can.flush 54 | 55 | want.flush := to.flush | assign.flush 56 | 57 | will.flush := want.flush & can.flush 58 | 59 | // 60 | val currentScope = DslScopeStack.get 61 | 62 | // Add every StageComponent defined inside the body of this. 63 | override def valCallbackRec(obj: Any, name: String) = { 64 | obj match { 65 | case stageComponent: StageComponent => addComponent(stageComponent) 66 | case _ => super.valCallbackRec(obj, name) 67 | } 68 | } 69 | 70 | def interConnect(): Unit = { 71 | /**/ 72 | produced.keys.foreach { case key => 73 | if (!hasAssignment(produced(key))) { 74 | if (output.contains(key)) { 75 | produced(key) := output(key) 76 | } else { 77 | produced(key) := input(key) 78 | } 79 | } 80 | } 81 | /**/ 82 | input.keys.foreach { case key => 83 | if (!hasAssignment(input(key))) { 84 | input(key) := stored(key) 85 | } 86 | } 87 | /**/ 88 | } 89 | 90 | // Called at initialization of StageComponent. 91 | def addComponent(component: StageComponent) = { 92 | component.input.keys foreach { case key => 93 | component.input(key) := input(key) 94 | } 95 | component.output.keys foreach { case key => 96 | output(key) := component.output(key) 97 | } 98 | 99 | component.input.whenAddedKey(new Record.AddedKeyCallback { 100 | def apply[T <: Data](key: Key[T], value: T) = { 101 | value := input(key) 102 | } 103 | }) 104 | 105 | component.output.whenAddedKey(new Record.AddedKeyCallback { 106 | def apply[T <: Data](key: Key[T], value: T) = { 107 | output(key) := value 108 | } 109 | }) 110 | } 111 | 112 | def connect(next: Stage, canPass: Bool) = { 113 | next.will.input := will.output & !will.flush 114 | will.output := !is.empty & (will.flush | is.done & canPass) 115 | 116 | when(next.will.input) { 117 | next.prevException := exception 118 | } elsewhen next.will.output { 119 | next.prevException := None 120 | } 121 | 122 | // Prioritize reset over send. 123 | atTheBeginning { 124 | when(next.will.input) { 125 | next.stored.keys foreach { case key => 126 | if (hasInit(next.stored(key))) { 127 | next.stored(key) := produced(key) 128 | } 129 | } 130 | } 131 | when(next.can.input) { 132 | next.stored.keys foreach { case key => 133 | if (!hasInit(next.stored(key))) { 134 | next.stored(key) := produced(key) 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | def atTheBeginning(body: => Unit) = { 142 | currentScope.push() 143 | val swapContext = currentScope.swap() 144 | body 145 | swapContext.appendBack() 146 | currentScope.pop() 147 | } 148 | 149 | def clearWhenEmpty[T <: Data](key: Key[T]): Unit = { 150 | if (!hasInit(stored(key))) { 151 | stored(key).init(key.emptyValue.get) 152 | when(will.output & (!will.input | prevException.nonEmpty)) { 153 | stored(key) := key.emptyValue.get 154 | } 155 | } 156 | } 157 | 158 | def !!![T <: Data](key: Key[T]): T = { 159 | clearWhenEmpty(key) 160 | stored(key) 161 | } 162 | } 163 | 164 | class StageComponent extends Area { 165 | val input = Record() 166 | val output = Record() 167 | } 168 | -------------------------------------------------------------------------------- /src/main/scala/cpu/Utils.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | import scala.language.postfixOps 6 | 7 | package object Utils { 8 | def hasAssignment(d: Data): Boolean = { 9 | d match { 10 | case b: BaseType => b.hasAssignement 11 | case o => o.flatten.forall(b => b.hasAssignement) 12 | } 13 | } 14 | def hasInit(d: Data): Boolean = { 15 | d match { 16 | case b: BaseType => b.hasInit 17 | case o => o.flatten.forall(b => b.hasInit) 18 | } 19 | } 20 | 21 | def signExtend(b: Bits, length: Int = 32) = b.asSInt.resize(length).asBits 22 | 23 | def zeroExtend(b: Bits, length: Int = 32) = b.asUInt.resize(length).asBits 24 | 25 | def instantiateWhen[T <: Data](data: => T, when: Boolean): T = { 26 | if (when) data else null.asInstanceOf[T] 27 | } 28 | 29 | def instantiateWhen[T <: SpinalEnum](data: => SpinalEnumElement[T], when: Boolean): SpinalEnumElement[T] = { 30 | if (when) data else null.asInstanceOf[SpinalEnumElement[T]] 31 | } 32 | 33 | def equalAny[T <: Data](left: T, right: T*): Bool = { 34 | val ans = Bits(right.length bits) 35 | right.zipWithIndex.foreach(ele => ans(ele._2) := (ele._1 === left)) 36 | ans.orR 37 | } 38 | 39 | def equalAny[T <: SpinalEnum](left: SpinalEnumCraft[T], right: SpinalEnumCraft[T]*): Bool = { 40 | val ans = Bits(right.length bits) 41 | right.zipWithIndex.foreach(ele => ans(ele._2) := (ele._1 === left)) 42 | ans.orR 43 | } 44 | 45 | /** 用于初始化CP0寄存器的 */ 46 | implicit class IntToFixedLengthBinaryString(value: Int) { 47 | def toBinaryString(length: Int): String = { 48 | val str = Integer.toBinaryString(value) 49 | assert(length >= str.length) 50 | if (length > str.length) { 51 | Array.fill(length - str.length)("0").mkString.concat(str) 52 | } else { 53 | str 54 | } 55 | } 56 | } 57 | 58 | object VAddr { 59 | def isUncachedSection(vaddr: UInt): Bool = { 60 | vaddr(29, 3 bits) === U"3'b101" 61 | } 62 | 63 | def isUnmappedSection(vaddr: UInt): Bool = { 64 | vaddr(30, 2 bits) === U"2'b10" 65 | } 66 | 67 | def isKseg0(vaddr: UInt): Bool = { 68 | vaddr(29, 3 bits) === U"100" 69 | } 70 | 71 | def isKseg1(vaddr: UInt): Bool = { 72 | vaddr(29, 3 bits) === U"101" 73 | } 74 | 75 | def isUseg(vaddr: UInt): Bool = { 76 | vaddr(31).asUInt === U"0" 77 | } 78 | 79 | def isUncached(vaddr: UInt, cacheAttr: Bits, K0: Bits, ERL: Bool): Bool = { 80 | //Uncache的情况: 81 | //1. 落在Uncached区域(kseg1) 82 | //2. 在mapped区域并且tlb的cacheAttr为2 83 | //3. 在kseg0并且此时CP0.Config.K0 = 2 84 | //4. 在kuseg并且此时CP0.Status.ERL = 1 85 | isUncachedSection(vaddr) | 86 | (!isUnmappedSection(vaddr) & cacheAttr === 2) | 87 | (isKseg0(vaddr) & K0 === 2) | 88 | (isUseg(vaddr) & ERL) 89 | } 90 | 91 | def isUnmapped(vaddr: UInt, ERL: Bool): Bool = { 92 | //Unmapped的情况 93 | //1. 在Unmapped区域 94 | //2. 在Kuseg并且CP0.Status.ERL = 1 95 | isUnmappedSection(vaddr) | (isUseg(vaddr) & ERL) 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/scala/cpu/defs/Config.scala: -------------------------------------------------------------------------------- 1 | package cpu.defs 2 | 3 | import spinal.core._ 4 | import scala.language.postfixOps 5 | 6 | class Config {} 7 | 8 | object Config { 9 | def NOP = B"32'0" 10 | def INIT_PC = U"hBFC00000" 11 | 12 | val ADDR_WIDTH = 32 13 | val INST_WIDTH = 32 14 | val BYTE_WIDTH = 8 15 | 16 | object BTB { 17 | val NUM_ENTRIES = 2048 18 | val NUM_WAYS = 4 19 | val INDEX_WIDTH = log2Up(NUM_ENTRIES / NUM_WAYS) 20 | } 21 | 22 | object BHT { 23 | val NUM_ENTRIES = 2048 24 | val INDEX_WIDTH = log2Up(NUM_ENTRIES) 25 | val DATA_WIDTH = 2 26 | } 27 | 28 | object PHT { 29 | val NUM_ENTRIES = BHT.NUM_ENTRIES * 4 30 | val INDEX_WIDTH = log2Up(NUM_ENTRIES) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/cpu/defs/ConstantVal.scala: -------------------------------------------------------------------------------- 1 | package cpu.defs 2 | 3 | import spinal.core._ 4 | import spinal.lib.bus.amba4.axi._ 5 | import Mips32InstImplicits._ 6 | 7 | object ConstantVal { 8 | def INST_NOP: Mips32Inst = B"00000000000000000000000000100000" 9 | def INIT_PC = U"hBFC00000" 10 | 11 | val AXI_BUS_CONFIG = Axi4Config( 12 | addressWidth = 32, 13 | dataWidth = 32, 14 | idWidth = 4, 15 | useRegion = false, 16 | useQos = false 17 | ) 18 | val Multiply_Latency = 3 19 | 20 | /** TLB相关配置 */ 21 | val PABITS = 32 //至少应该大于等于32,表示支持的物理地址宽度 22 | val TLBEntryNum = 32 //TLB表项的个数, 2的幂 23 | 24 | /** Cache配置 */ 25 | val IcacheLineSize = 32 //一个cacheLine的大小,单位是bytes, 2的幂 26 | val IcacheWayNum = 2 //icache中路数 27 | val IcacheSetsPerWay = 128 //影响Config1中的IS字段 28 | val IcacheFifoDepth = 8 //TODO ICache的victim cache深度 29 | 30 | val DcacheLineSize = 32 //一个cacheLine的大小,单位是bytes, 2的幂 31 | val DcacheWayNum = 2 //dcache中路数 32 | val DcacheSetsPerWay = 128 //影响Config1中的IS字段 33 | val DcacheFifoDepth = 2 //DCache的FIFO深度 34 | 35 | /** 时钟中断配置 */ 36 | val TimeInterruptEnable = false 37 | 38 | val DISABLE_VIRT_UART = true 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/ip/AxiClockConverter.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import cpu.defs.ConstantVal 4 | import spinal.core._ 5 | import spinal.lib.bus.amba4.axi._ 6 | import spinal.lib.{cpu => _, _} 7 | import ip.sim._ 8 | 9 | class AxiClockConverter extends BlackBox { 10 | setDefinitionName("axi_clock_converter") 11 | 12 | val io = new Bundle { 13 | val s_axi_aclk = in Bool () 14 | val s_axi_aresetn = in Bool () 15 | val s_axi = slave(Axi4(ConstantVal.AXI_BUS_CONFIG)) 16 | 17 | val m_axi_aclk = in Bool () 18 | val m_axi_aresetn = in Bool () 19 | val m_axi = master(Axi4(ConstantVal.AXI_BUS_CONFIG)) 20 | } 21 | 22 | noIoPrefix() 23 | addPrePopTask(() => { 24 | for ( 25 | axi <- Seq(io.s_axi, io.m_axi); 26 | bt <- axi.flatten 27 | ) { 28 | val idx = bt.getName().lastIndexOf('_') 29 | if (idx >= 0) { 30 | bt.setName(bt.getName().substring(0, idx).concat(bt.getName().substring(idx + 1))) 31 | } 32 | } 33 | }) 34 | 35 | if (isInSim) { 36 | addRTLPath("./rtl/axi_clock_converter_sim_netlist.v") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/scala/ip/Cache.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import spinal.core._ 4 | 5 | class SDPRAM(numEntries: Int, indexWidth: Int, entryWidth: Int) extends Component { 6 | val memGeneric = new xpm_memory_sdpram_generic 7 | memGeneric.ADDR_WIDTH_A = indexWidth 8 | memGeneric.ADDR_WIDTH_B = indexWidth 9 | memGeneric.BYTE_WRITE_WIDTH_A = entryWidth 10 | memGeneric.MEMORY_PRIMITIVE = "block" 11 | memGeneric.MEMORY_SIZE = numEntries * entryWidth 12 | memGeneric.READ_DATA_WIDTH_B = entryWidth 13 | memGeneric.READ_LATENCY_B = 1 14 | memGeneric.WRITE_DATA_WIDTH_A = entryWidth 15 | memGeneric.WRITE_MODE_B = "read_first" 16 | val mem = new xpm_memory_sdpram(memGeneric) 17 | 18 | val io = new Bundle { 19 | val ena = in Bool () 20 | val addra = in UInt (indexWidth bits) 21 | val dina = in Bits (entryWidth bits) 22 | 23 | val enb = in Bool () 24 | val addrb = in UInt (indexWidth bits) 25 | val doutb = out Bits (entryWidth bits) 26 | } 27 | 28 | val prevDina = RegNext(mem.io.dina) 29 | val currAddrb = RegNext(mem.io.addrb) 30 | val conflict = RegNext(io.ena & (io.addra === mem.io.addrb)) init False 31 | 32 | mem.io.ena := io.ena 33 | mem.io.addra := io.addra 34 | mem.io.dina := B(io.dina) 35 | mem.io.wea := B"1" 36 | 37 | mem.io.enb := True 38 | mem.io.addrb := io.enb ? io.addrb | currAddrb 39 | io.doutb.assignFromBits(conflict ? prevDina | mem.io.doutb) 40 | } 41 | 42 | class Cache(entriesPerWay: Int, numWays: Int, indexWidth: Int, metaWidth: Int, dataWidth: Int) extends Component { 43 | val metaMem = Seq.fill(numWays)(new SDPRAM(entriesPerWay, indexWidth, metaWidth)) 44 | val dataMem = Seq.fill(numWays)(new SDPRAM(entriesPerWay, indexWidth, dataWidth)) 45 | val pMem = Mem(Bits(numWays - 1 bits), entriesPerWay).randBoot() 46 | 47 | val io = new Bundle { 48 | val w = new Bundle { 49 | val en = in Bool () 50 | val oh = in Bits (numWays bits) 51 | val index = in UInt (indexWidth bits) 52 | val data = in Bits (dataWidth bits) 53 | val meta = in Bits (metaWidth bits) 54 | val p = in Bits (numWays - 1 bits) 55 | } 56 | val r = new Bundle { 57 | val en = in Bool () 58 | val index = in UInt (indexWidth bits) 59 | val dataLine = out Vec (Bits(dataWidth bits), numWays) 60 | val metaLine = out Vec (Bits(metaWidth bits), numWays) 61 | val p = out Bits (numWays - 1 bits) 62 | } 63 | } 64 | 65 | for (i <- 0 until numWays) { 66 | val we = io.w.en & io.w.oh(i) 67 | /**/ 68 | dataMem(i).io.ena := we 69 | dataMem(i).io.addra := io.w.index 70 | dataMem(i).io.dina := io.w.data 71 | 72 | dataMem(i).io.enb := io.r.en 73 | dataMem(i).io.addrb := io.r.index 74 | io.r.dataLine(i) := dataMem(i).io.doutb 75 | /**/ 76 | metaMem(i).io.ena := we 77 | metaMem(i).io.addra := io.w.index 78 | metaMem(i).io.dina := io.w.meta 79 | 80 | metaMem(i).io.enb := io.r.en 81 | metaMem(i).io.addrb := io.r.index 82 | io.r.metaLine(i) := metaMem(i).io.doutb 83 | } 84 | 85 | // 86 | io.r.p := pMem(io.r.index) 87 | when(io.w.en) { pMem(io.w.index) := io.w.p } 88 | 89 | // 90 | def plru(p: Bits, i: Int = 1, v: Bool = True): Bits = { 91 | if (i < numWays) { 92 | plru(p, i << 1 | 1, v & !p(i - 1)) ## plru(p, i << 1, v & p(i - 1)) 93 | } else { 94 | B(v) 95 | } 96 | } 97 | 98 | def getP(pIn: Bits, we: Bits, set: Bool, pOut: Bits, i: Int = 1): Bool = { 99 | if (i < numWays) { 100 | val l = getP(pIn, we, set, pOut, i << 1) 101 | val r = getP(pIn, we, set, pOut, i << 1 | 1) 102 | 103 | pOut(i - 1) := pIn(i - 1) 104 | when(l) { 105 | pOut(i - 1) := !set 106 | } 107 | when(r) { 108 | pOut(i - 1) := set 109 | } 110 | 111 | l | r 112 | } else { 113 | we(i % numWays) 114 | } 115 | } 116 | } 117 | 118 | object Cache { 119 | def main(args: Array[String]): Unit = { 120 | SpinalVerilog(new Cache(1024, 4, 10, 10, 10)) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/scala/ip/DividerIP.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import cpu.Utils 4 | import ip.sim._ 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | 8 | /** @param dataWidth 被除数和除数的宽度 9 | * @param detectZero 是否检测除0 10 | * @note Vivado的IP会自动把除数和被除数8字节对齐 11 | * 假设你的输入是33bit,那么输入会到40bit(文档没明确说怎么填充多余的位,估计是高7位填充0) 12 | * 此时输出的格式是B(0, 7 bits) ## 商(32 bit) ## B(0, 7 bits) ## 余数(32 bit) 13 | */ 14 | class DividerIP(dataWidth: Int = 32, detectZero: Boolean = false, name: String = "divider") 15 | extends SimulatedBlackBox { 16 | setDefinitionName(name) 17 | val alignedDataWidth = ((dataWidth + 7) / 8) * 8 18 | 19 | val io = new Bundle { 20 | val aclk = in Bool () 21 | 22 | /** 被除数 */ 23 | val dividend = new Bundle { 24 | setName("s_axis_dividend") 25 | val tdata = in UInt (alignedDataWidth bits) 26 | val tvalid = in Bool () 27 | }.simPublic() 28 | val divisor = new Bundle { 29 | setName("s_axis_divisor") 30 | val tdata = in UInt (alignedDataWidth bits) 31 | val tvalid = in Bool () 32 | }.simPublic() 33 | val dout = new Bundle { 34 | setName("m_axis_dout") 35 | val tdata = out Bits (2 * alignedDataWidth bits) 36 | val tvalid = out Bool () 37 | val tuser = Utils.instantiateWhen(out(Bool), detectZero) 38 | def divideByZero = tuser 39 | } 40 | } 41 | 42 | noIoPrefix() 43 | mapClockDomain(clock = io.aclk) 44 | 45 | val quotient = UInt(alignedDataWidth bits) 46 | val remainder = UInt(alignedDataWidth bits) 47 | io.dout.tdata := quotient ## remainder 48 | 49 | override def createSimJob() = { 50 | val pulled = new Area { 51 | val valid = pullFromOutside(io.dout.tvalid) 52 | val quotient = pullFromOutside(DividerIP.this.quotient) 53 | val remainder = pullFromOutside(DividerIP.this.remainder) 54 | val divideByZero = if (detectZero) pullFromOutside(io.dout.tuser) else null 55 | } 56 | 57 | Pipeline(20) 58 | .whenIdle { 59 | pulled.valid #= false 60 | pulled.quotient.randomize() 61 | pulled.remainder.randomize() 62 | if (detectZero) { 63 | pulled.divideByZero.randomize() 64 | } 65 | } 66 | .everyTick { schedule => 67 | if (io.dividend.tvalid.toBoolean && io.divisor.tvalid.toBoolean) { 68 | val dividend = io.dividend.tdata.toBigInt 69 | val divisor = io.divisor.tdata.toBigInt 70 | 71 | schedule { 72 | pulled.valid #= true 73 | val divideByZero = divisor == 0 74 | if (!divideByZero) { 75 | pulled.quotient #= dividend / divisor 76 | pulled.remainder #= dividend % divisor 77 | } else { 78 | pulled.quotient.randomize() 79 | pulled.remainder.randomize() 80 | } 81 | if (detectZero) { 82 | pulled.divideByZero #= divideByZero 83 | } 84 | } 85 | } 86 | } 87 | .toJob 88 | } 89 | } 90 | 91 | case class DividerJob( 92 | dividend: BigInt, 93 | divisor: BigInt 94 | ) 95 | 96 | object Divider { 97 | def main(args: Array[String]): Unit = { 98 | SpinalVerilog(new Component { 99 | val divider = new DividerIP(32, true) 100 | divider.io.dividend.assignDontCare() 101 | divider.io.divisor.assignDontCare() 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/scala/ip/MultiplierIP.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import cpu.Utils 4 | import ip.sim._ 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | 8 | import scala.language.postfixOps 9 | 10 | /** @param dataWidth 被除数和除数的宽度 */ 11 | class MultiplierIP(dataWidth: Int = 32, name: String = "multiplier") extends BlackBox { 12 | setDefinitionName(name) 13 | val io = new Bundle { 14 | val CLK = in Bool () 15 | val CE = in Bool () //clock enable, active high 16 | val SCLR = in Bool() //Synchronize Clear 17 | val A = in UInt (32 bits) 18 | val B = in UInt (32 bits) 19 | val P = out UInt (64 bits) 20 | } 21 | 22 | noIoPrefix() 23 | mapClockDomain(clock = io.CLK) 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/ip/RamPort.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ip.sim._ 4 | import spinal.core._ 5 | import spinal.core.sim._ 6 | import spinal.lib._ 7 | 8 | class ReadOnlyRamPort(dataWidth: Int, addrWidth: Int) extends Bundle with IMasterSlave { 9 | val en = Bool.simPublic() 10 | val addr = UInt(addrWidth bits).simPublic() 11 | val dout = Bits(dataWidth bits) 12 | 13 | override def asMaster(): Unit = { 14 | in(dout) 15 | out(en, addr) 16 | } 17 | 18 | /** 仿真行为,参见 [[Pipeline]] */ 19 | def handledBy(delayedPipeline: Pipeline, storage: Array[BigInt]) = { 20 | val doutPulled = pullFromOutside(dout) 21 | delayedPipeline 22 | .whenReset { 23 | doutPulled #= 0 24 | } 25 | .everyTick { schedule => 26 | if (en.toBoolean) { 27 | val addrValue = addr.toInt 28 | schedule { 29 | if (doutPulled.toBigInt != storage(addrValue)) 30 | doutPulled #= storage(addrValue) 31 | } 32 | } 33 | } 34 | } 35 | } 36 | 37 | object ReadOnlyRamPort { 38 | def apply(dataWidth: Int, addrWidth: Int) = new ReadOnlyRamPort(dataWidth, addrWidth) 39 | } 40 | 41 | object WriteMode extends Enumeration { 42 | 43 | /** 先写再读,读出写的值 */ 44 | val WriteFirst = Value("write_first") 45 | 46 | /** 先读再写,读出写之前的值 */ 47 | val ReadFirst = Value("read_first") 48 | 49 | /** 读出值不改变,保持之前的结果 */ 50 | val NoChange = Value("no_change") 51 | } 52 | 53 | class WriteOnlyRamPort(dataWidth: Int, addrWidth: Int) extends Bundle with IMasterSlave { 54 | val en = Bool.simPublic() 55 | val we = Bool.simPublic() //write enable 56 | val addr = UInt(addrWidth bits).simPublic() 57 | val din = Bits(dataWidth bits).simPublic() 58 | 59 | override def asMaster(): Unit = { 60 | out(en, we, addr, din) 61 | } 62 | 63 | /** 仿真行为,参见 [[Pipeline]] */ 64 | def handledBy(delayedPipeline: Pipeline, storage: Array[BigInt]) = { 65 | delayedPipeline 66 | .everyTick { schedule => 67 | if (en.toBoolean) { 68 | val addrValue = addr.toInt 69 | if (we.toBoolean) { 70 | val dinValue = din.toBigInt 71 | schedule { 72 | val oldValue = storage(addrValue) 73 | storage(addrValue) = dinValue 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | object WriteOnlyRamPort { 82 | def apply(dataWidth: Int, addrWidth: Int) = new WriteOnlyRamPort(dataWidth, addrWidth) 83 | } 84 | 85 | /** @param dataWidth 数据宽度,单位是bit 86 | * @param addrWidth ram地址宽度 87 | * @param writeMode 控制写入成功时 dout 返回值 88 | */ 89 | class RamPort(dataWidth: Int, addrWidth: Int, writeMode: WriteMode.Value) 90 | extends ReadOnlyRamPort(dataWidth, addrWidth) { 91 | val we = Bool.simPublic() //write enable 92 | val din = Bits(dataWidth bits).simPublic() 93 | 94 | override def asMaster(): Unit = { 95 | super.asMaster() 96 | out(we, din) 97 | } 98 | 99 | /** 仿真行为,参见 [[Pipeline]] */ 100 | override def handledBy(delayedPipeline: Pipeline, storage: Array[BigInt]) = { 101 | val doutPulled = pullFromOutside(dout) 102 | 103 | delayedPipeline 104 | .whenReset { 105 | doutPulled #= 0 106 | } 107 | .everyTick { schedule => 108 | if (en.toBoolean) { 109 | val addrValue = addr.toInt 110 | if (we.toBoolean) { 111 | val dinValue = din.toBigInt 112 | schedule { 113 | val oldValue = storage(addrValue) 114 | storage(addrValue) = dinValue 115 | 116 | writeMode match { 117 | case WriteMode.WriteFirst => 118 | if (doutPulled.toBigInt != dinValue) 119 | doutPulled #= dinValue 120 | case WriteMode.ReadFirst => 121 | if (doutPulled.toBigInt != oldValue) 122 | doutPulled #= oldValue 123 | case WriteMode.NoChange => 124 | // Nothing. 125 | } 126 | } 127 | } else { 128 | schedule { 129 | if (doutPulled.toBigInt != storage(addrValue)) 130 | doutPulled #= storage(addrValue) 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | object RamPort { 139 | def apply(dataWidth: Int, addrWidth: Int, writeMode: WriteMode.Value) = 140 | new RamPort(dataWidth, addrWidth, writeMode) 141 | } 142 | 143 | object RamPortDelayedPipelineImplicits { 144 | implicit class DelayedPipelineHandlesPort(delayedPipeline: Pipeline) { 145 | // 对 port 多态 146 | def handlePort(port: ReadOnlyRamPort, storage: Array[BigInt]): Pipeline = 147 | port.handledBy(delayedPipeline, storage) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/scala/ip/SinglePortRam.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ip.RamPortDelayedPipelineImplicits._ 4 | import ip.sim._ 5 | import spinal.core._ 6 | import spinal.lib._ 7 | 8 | class SinglePortRamBase(dataWidth: Int, size: Int, latency: Int, writeMode: WriteMode.Value) 9 | extends SimulatedBlackBox { 10 | val io = new Bundle { 11 | val clk = in Bool () 12 | val rst = in Bool () 13 | val port = slave(RamPort(dataWidth, log2Up(size / dataWidth), writeMode)).setName("") 14 | } 15 | 16 | noIoPrefix() 17 | mapClockDomain(clock = io.clk, reset = io.rst, resetActiveLevel = HIGH) 18 | addRTLPath("./rtl/single_port_ram.v") 19 | 20 | override def createSimJob() = { 21 | val storage = new Array[BigInt](size / dataWidth) 22 | Pipeline(latency) 23 | .whenReset { 24 | for (i <- storage.indices) { 25 | storage(i) = 0 26 | } 27 | } 28 | .handlePort(io.port, storage) 29 | .toJob 30 | } 31 | } 32 | 33 | /** @param dataWidth 数据宽度,单位是bit 34 | * @param size ram总的大小,单位是bit 35 | * @param latency 读端口的延迟 36 | */ 37 | class SinglePortBRam(dataWidth: Int = 32, size: Int = 4 * 1024 * 8, latency: Int = 1) 38 | extends SinglePortRamBase(dataWidth, size, latency, WriteMode.WriteFirst) { 39 | setDefinitionName("single_port_ram") 40 | val generic = new Generic { 41 | val DATA_WIDTH = dataWidth 42 | val DEPTH = size / dataWidth 43 | val MEMORY_PRIMITIVE = "block" 44 | val LATENCY = latency 45 | val WRITE_MODE = WriteMode.WriteFirst.toString 46 | } 47 | } 48 | 49 | /** @param dataWidth 数据宽度,单位是bit 50 | * @param size ram总的大小,单位是bit 51 | * @param latency 读端口的延迟 52 | */ 53 | class SinglePortLUTRam(dataWidth: Int = 32, size: Int = 4 * 1024 * 8, latency: Int = 0) 54 | extends SinglePortRamBase(dataWidth, size, latency, WriteMode.ReadFirst) { 55 | setDefinitionName("single_port_ram") 56 | val generic = new Generic { 57 | val DATA_WIDTH = dataWidth 58 | val DEPTH = size / dataWidth 59 | val MEMORY_PRIMITIVE = "distributed" 60 | val LATENCY = latency 61 | val WRITE_MODE = WriteMode.ReadFirst.toString 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/scala/ip/sim/package.scala: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.idslplugin.PostInitCallback 6 | 7 | import scala.collection.mutable 8 | 9 | package object sim { 10 | 11 | /** 检测是否是为仿真而综合 */ 12 | def isInSim = GenerationFlags.simulation.isEnabled 13 | 14 | /** 将要从外部给出的 IP 的输出拉取至顶层。 15 | * Verilator 只会在外部输入改变时重新计算模块内部各值。 16 | * 如果直接驱动 IP 的输出,依赖此输出的其他值不会更新,尤其是组合信号可能只会到下个周期才更新。 17 | */ 18 | def pullFromOutside[T <: Data](data: T): T = { 19 | // 在所有 Component 之外建立该输入,并 pull 到 data 所在位置。 20 | Component.push(null) 21 | val external = cloneOf(data).setWeakName(data.getName()).simPublic() 22 | Component.pop(null) 23 | 24 | data := Data.doPull(external, Component.current, useCache = true) 25 | 26 | // 顶层模块中得到的值为整个顶层模块的输入,仿真时应驱动此输入 27 | GlobalData.get.toplevel.pulledDataCache(external).asInstanceOf[T] 28 | } 29 | 30 | type Job = () => Unit 31 | 32 | trait JobCollector { 33 | val jobs = mutable.ArrayBuffer[Job]() 34 | } 35 | 36 | abstract class SimulatedBlackBox extends BlackBox with PostInitCallback { 37 | spinalSimWhiteBox() 38 | 39 | def createSimJob(): Job 40 | 41 | override def postInitCallback() = { 42 | if (isInSim) { 43 | GlobalData.get.toplevel.asInstanceOf[JobCollector].jobs += createSimJob() 44 | } 45 | super.postInitCallback() 46 | } 47 | } 48 | 49 | /** 模拟可能会有延迟的、自带流水线的模块。 50 | * 对于延迟 > 0 的情况, 51 | * 所有的读入输入操作被设计在时钟下沿,保证所有的延迟的写操作在之前完成。 52 | * 对于延迟 = 0 的情况,用 forkSensitive 模拟组合逻辑。 53 | * 此时要求 everyTick 回调在输入不变时,不写入输出;其副作用可重复执行,不产生意外影响。 54 | * (这意味着 .randomize 需要受到限制) 55 | */ 56 | case class Pipeline(latency: Int) { 57 | private val idleJob = mutable.ArrayBuffer[() => Unit]() 58 | private val resetJob = mutable.ArrayBuffer[() => Unit]() 59 | private val everyTickJobs = mutable.ArrayBuffer[((=> Unit) => Unit) => Unit]() 60 | private val clockDomain = ClockDomain.current 61 | 62 | def whenIdle(idleJob: => Unit) = { 63 | this.idleJob += { () => idleJob } 64 | this 65 | } 66 | 67 | def whenReset(resetJob: => Unit) = { 68 | this.resetJob += { () => resetJob } 69 | this 70 | } 71 | 72 | def everyTick(everyTickJob: ((=> Unit) => Unit) => Unit) = { 73 | everyTickJobs += everyTickJob 74 | this 75 | } 76 | 77 | def toJob: () => Unit = 78 | if (latency > 0) toJobQueued else toJobSensitive 79 | 80 | private def toJobQueued: () => Unit = { () => 81 | var tick: Long = 0 82 | var resetLastCycle = false 83 | 84 | val pendingJobs = mutable.Queue[(() => Unit, Long)]() 85 | 86 | resetJob.foreach { _() } 87 | idleJob.foreach { _() } 88 | 89 | while (true) { 90 | clockDomain.waitActiveEdge() 91 | 92 | if (clockDomain.isResetAsserted) { 93 | if (!resetLastCycle) { 94 | resetJob.foreach { _() } 95 | resetLastCycle = true 96 | } 97 | pendingJobs.clear() // May need to be customizable 98 | } 99 | 100 | if (pendingJobs.isEmpty || pendingJobs.front._2 != tick) { 101 | idleJob.foreach { _() } 102 | } else { 103 | while (pendingJobs.nonEmpty && pendingJobs.front._2 == tick) { 104 | pendingJobs.dequeue()._1() 105 | } 106 | } 107 | 108 | clockDomain.waitEdge(1) // 等待下沿 109 | 110 | if (clockDomain.isSamplingEnable) { 111 | def schedule(scheduled: => Unit) = pendingJobs.enqueue((() => scheduled, tick + latency)) 112 | everyTickJobs.foreach { _(schedule) } 113 | } 114 | 115 | tick += 1 116 | } 117 | } 118 | 119 | private def toJobSensitive: () => Unit = { () => 120 | var resetLastDeltaCycle = false 121 | val pendingJobs = mutable.ArrayBuffer[() => Unit]() 122 | 123 | resetJob.foreach { _() } 124 | idleJob.foreach { _() } 125 | 126 | forkSensitive { 127 | if (!clockDomain.isResetAsserted) { 128 | def schedule(scheduled: => Unit) = pendingJobs += (() => scheduled) 129 | everyTickJobs.foreach { _(schedule) } 130 | if (pendingJobs.nonEmpty) { 131 | pendingJobs.foreach { _() } 132 | pendingJobs.clear() 133 | } else { 134 | idleJob.foreach { _() } 135 | } 136 | } else if (!resetLastDeltaCycle) { 137 | resetJob.foreach { _() } 138 | resetLastDeltaCycle = true 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/scala/lib/Coproduct.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | 5 | case class Coproduct(hardTypes: HardType[_ <: Data]*) extends Bundle { 6 | object Type extends SpinalEnum { 7 | val elementsByType = hardTypes map (t => 8 | t -> newElement().setWeakName(t().getClass.getSimpleName) 9 | ) 10 | } 11 | 12 | val `type` = Type() 13 | val bits = Bits((hardTypes map { 14 | _.getBitsWidth 15 | }).max bits) 16 | 17 | def copy() = Coproduct(hardTypes: _*) 18 | 19 | private def find[T <: Data](data: T) = 20 | Type.elementsByType 21 | .find { 22 | data.getClass == _._1().getClass 23 | } 24 | .map { 25 | _._2 26 | } 27 | .get 28 | 29 | def :=[T <: Data](data: T) = { 30 | `type` := find(data) 31 | bits := data.asBits.resized 32 | } 33 | 34 | def init[T <: Data](data: T): this.type = { 35 | val that = copy() 36 | that := data 37 | this init that 38 | } 39 | 40 | def default[T <: Data](data: T): this.type = { 41 | val that = copy() 42 | that := data 43 | this default data 44 | } 45 | 46 | def is[T <: Data](target: HardType[T]) = `type` === find(target()) 47 | 48 | def whenIs[T <: Data](target: HardType[T])(block: T => Unit) = { 49 | when(`type` === find(target())) { 50 | block(asType(target)) 51 | } 52 | this 53 | } 54 | 55 | def asType[T <: Data](target: HardType[T]): T = { 56 | assert(`type` === find(target())) 57 | val result = target() 58 | result assignFromBits bits.resize(widthOf(target)) 59 | result 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala/lib/Decoder.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | import spinal.idslplugin.PostInitCallback 5 | 6 | import scala.collection.mutable 7 | import scala.language.existentials 8 | 9 | object NotConsidered extends Key(Bool) 10 | 11 | class Decoder(inputWidth: BitCount, enableNotConsidered: Boolean = false) extends Component { 12 | private val keys = mutable.Set[Key[_ <: Data]]() 13 | private val defaults = mutable.Map[Key[_ <: Data], () => Data]() 14 | private val encodings = 15 | mutable.Map[MaskedLiteral, mutable.Map[Key[_ <: Data], () => Data]]() 16 | 17 | val input = in Bits (inputWidth) 18 | val output = out(Record(keys.toSeq)) 19 | 20 | case class default[T <: Data](key: Key[T]) { 21 | keys.add(key) 22 | 23 | def to(value: => T): Unit = { 24 | assert(!(defaults contains key)) 25 | defaults(key) = () => value 26 | } 27 | 28 | def to[E <: SpinalEnum](value: => SpinalEnumElement[E])(implicit 29 | ev: =:=[SpinalEnumCraft[E], T] 30 | ): Unit = { 31 | to(ev(value())) 32 | } 33 | } 34 | 35 | if (enableNotConsidered) { 36 | default(NotConsidered) to True 37 | } 38 | 39 | private var currentOn: Option[on] = None 40 | 41 | case class on(mask: MaskedLiteral) { 42 | assert(mask.width == inputWidth.value) 43 | assert(currentOn.isEmpty, "lib.Decoder: `on` must not be nested.") 44 | 45 | val map = encodings.getOrElseUpdate(mask, mutable.Map()) 46 | 47 | def apply(body: => Unit) = { 48 | currentOn = Some(this) 49 | body 50 | currentOn = None 51 | } 52 | 53 | if (enableNotConsidered) { 54 | apply { 55 | set(NotConsidered) to False 56 | } 57 | } 58 | 59 | } 60 | 61 | case class set[T <: Data](key: Key[T]) { 62 | assert( 63 | currentOn.isDefined, 64 | "lib.Decoder: `set` must be called within an `on`." 65 | ) 66 | 67 | keys.add(key) 68 | 69 | def to(value: => T): Unit = { 70 | currentOn.get.map(key) = () => value 71 | } 72 | 73 | def to[E <: SpinalEnum](value: => SpinalEnumElement[E])(implicit 74 | ev: =:=[SpinalEnumCraft[E], T] 75 | ): Unit = { to(ev(value())) } 76 | } 77 | 78 | private def build() = { 79 | val keys = this.keys.toSeq 80 | 81 | val offsets = (keys 82 | .scanLeft(0) { case (currentOffset, key) => 83 | currentOffset + widthOf(key.`type`) 84 | }) 85 | val outputWidth = offsets.last 86 | 87 | val outputVector = B(0, outputWidth bits) 88 | 89 | val outputSegments: Map[Key[_ <: Data], Bits] = Map.from( 90 | for (i <- keys.indices) 91 | yield (keys(i) -> outputVector(offsets(i) until offsets(i + 1))) 92 | ) 93 | 94 | for (key <- keys) { 95 | output(key) assignFromBits outputSegments(key) 96 | } 97 | 98 | // Naive implementation 99 | for ((key, default) <- defaults) { 100 | outputSegments(key) := default().asBits 101 | } 102 | 103 | for ((maskedLiteral, map) <- encodings) { 104 | // The === is aware of the mask 105 | when(maskedLiteral === input) { 106 | for ((key, value) <- map) { 107 | outputSegments(key) := value().asBits 108 | } 109 | } 110 | } 111 | } 112 | 113 | addPrePopTask(build) 114 | } 115 | -------------------------------------------------------------------------------- /src/main/scala/lib/Key.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | 5 | case class Key[T <: Data](val `type`: HardType[T]) extends Nameable { 6 | setWeakName(this.getClass.getSimpleName.replace("$", "")) 7 | 8 | var emptyValue = Option.empty[T] 9 | def setEmptyValue(v: T) = { 10 | emptyValue = Some(v) 11 | 12 | this 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/lib/Optional.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | 6 | case class Optional[T <: Data](dataType: HardType[T]) extends Bundle { 7 | // If necessary, may apply Rust-like niche optimization to this? 8 | val isDefined = Bool 9 | val value = dataType() 10 | 11 | def copy() = Optional(dataType) 12 | 13 | def :=(data: T) = { 14 | value := data 15 | isDefined := True 16 | } 17 | def init(data: T) = { 18 | value init data 19 | isDefined init True 20 | this 21 | } 22 | def default(data: T): Optional[T] = { 23 | value default data 24 | isDefined default True 25 | this 26 | } 27 | 28 | def :=(none: None.type) = { 29 | value.assignDontCare() 30 | isDefined := False 31 | } 32 | def init(none: None.type) = { 33 | isDefined init False 34 | this 35 | } 36 | def default(none: None.type) = { 37 | val defaultValue = dataType() 38 | defaultValue.assignDontCare() 39 | value default defaultValue 40 | isDefined default False 41 | this 42 | } 43 | 44 | def whenIsDefined(block: T => Unit) = { 45 | when(isDefined) { 46 | block(get) 47 | } 48 | } 49 | 50 | def get = { 51 | assert(isDefined) 52 | value 53 | } 54 | 55 | def isEmpty = !isDefined 56 | def nonEmpty = isDefined 57 | 58 | def getOrElse(other: T) = { 59 | val result = dataType() 60 | when(isDefined) { 61 | result := value 62 | } otherwise { 63 | result := other 64 | } 65 | result 66 | } 67 | 68 | def orElse(other: Optional[T]) = { 69 | val result = Optional(dataType) 70 | when(isDefined) { 71 | result := this 72 | } otherwise { 73 | result := other 74 | } 75 | result 76 | } 77 | 78 | def toFlow = { 79 | val flow = Flow(dataType) 80 | flow.valid := isDefined 81 | flow.payload := value 82 | flow 83 | } 84 | } 85 | 86 | object Optional { 87 | def noneOf[T <: Data](`type`: HardType[T]) = { 88 | val optional = new Optional(`type`) 89 | optional := None 90 | optional 91 | } 92 | 93 | def some[T <: Data](value: T) = { 94 | val optional = new Optional(value) 95 | optional := value 96 | optional 97 | } 98 | 99 | def fromFlow[T <: Data](flow: Flow[T]) = { 100 | val optional = Optional(flow.payloadType) 101 | when(flow.valid) { 102 | optional := flow.payload 103 | } otherwise { 104 | optional := None 105 | } 106 | optional 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/scala/lib/Record.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | import scala.collection.mutable 5 | 6 | class Record extends Bundle { 7 | private val data = mutable.Map[Key[_ <: Data], Data]() 8 | private val addedKeyCallbacks = mutable.ArrayBuffer[Record.AddedKeyCallback]() 9 | 10 | private var direction: Option[IODirection] = None 11 | private var btFlags = 0 12 | 13 | def keys = data.keys 14 | def values = data.values 15 | 16 | def apply[T <: Data](key: Key[T]): T = { 17 | this += key 18 | data(key).asInstanceOf[T] 19 | } 20 | 21 | // Add a default value for the key if not exists 22 | def +=[T <: Data](key: Key[T]) = { 23 | if (!(data contains key)) { 24 | // Pretend the value was generated in the same scope as the record. 25 | parentScope.push() 26 | val value = key.`type`().setCompositeName(this, key.getName) 27 | 28 | direction match { 29 | case Some(d) => d(value) 30 | case None => 31 | } 32 | if ((btFlags & BaseType.isRegMask) != 0) value.setAsReg() 33 | if ((btFlags & BaseType.isAnalogMask) != 0) value.setAsAnalog() 34 | 35 | parentScope.pop() 36 | 37 | val pair = (key, value) 38 | data += pair 39 | 40 | for (cb <- addedKeyCallbacks) { 41 | cb(key, value) 42 | } 43 | } 44 | } 45 | 46 | def whenAddedKey(cb: Record.AddedKeyCallback) = { 47 | addedKeyCallbacks += cb 48 | } 49 | 50 | override def asInput(): this.type = { 51 | direction = Some(in) 52 | super.asInput() 53 | } 54 | 55 | override def asOutput(): this.type = { 56 | direction = Some(out) 57 | super.asOutput() 58 | } 59 | 60 | override def asInOut(): this.type = { 61 | direction = Some(inout) 62 | super.asInOut() 63 | } 64 | 65 | override def setAsDirectionLess(): this.type = { 66 | direction = None 67 | super.setAsDirectionLess() 68 | } 69 | 70 | override def copyDirectionOfImpl(that: Data): this.type = { 71 | direction = that.asInstanceOf[Record].direction 72 | super.copyDirectionOfImpl(that) 73 | } 74 | 75 | override def setAsReg(): this.type = { 76 | btFlags |= BaseType.isRegMask 77 | super.setAsReg() 78 | } 79 | 80 | override def setAsAnalog(): this.type = { 81 | btFlags |= BaseType.isAnalogMask 82 | super.setAsAnalog() 83 | } 84 | 85 | override def setAsComb(): this.type = { 86 | btFlags &= ~(BaseType.isRegMask | BaseType.isAnalogMask) 87 | super.setAsComb() 88 | } 89 | 90 | def contains[T <: Data](key: Key[T]) = data contains key 91 | def isEmpty = data.isEmpty 92 | def nonEmpty = data.nonEmpty 93 | } 94 | 95 | object Record { 96 | def apply(): Record = new Record 97 | def apply(key: Key[_ <: Data], keys: Key[_ <: Data]*): Record = apply( 98 | key +: keys 99 | ) 100 | def apply(keys: Seq[Key[_ <: Data]]) = { 101 | val record = new Record 102 | for (key <- keys) { 103 | record += key 104 | } 105 | record 106 | } 107 | 108 | trait AddedKeyCallback { 109 | def apply[T <: Data](key: Key[T], value: T): Unit 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/scala/lib/Task.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | 5 | class Task[T <: Data](assign: Bool, newValue: T, clear: Bool) { 6 | val th = RegInit(False) 7 | val has = th | assign 8 | th := clear ? False | has 9 | 10 | val tv = RegNextWhen(newValue, assign) 11 | val value = assign ? newValue | tv 12 | } 13 | 14 | object Task { 15 | def apply[T <: Data](assign: Bool, newValue: T, clear: Bool): Task[T] = 16 | new Task(assign, newValue, clear) 17 | } 18 | 19 | class SimpleTask[T <: Data](assign: Bool, clear: Bool) { 20 | val th = RegInit(False) 21 | val has = th | assign 22 | th := clear ? False | has 23 | } 24 | 25 | object SimpleTask { 26 | def apply[T <: Data](assign: Bool, clear: Bool): SimpleTask[T] = new SimpleTask(assign, clear) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/lib/Updating.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | 5 | case class Updating[T <: Data](val dataType: HardType[T]) extends Bundle { 6 | val next = dataType().allowOverride 7 | val prev = RegNext(next) 8 | next := prev 9 | 10 | def init(that: T) = { 11 | prev init that 12 | this 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/tlb/MMU.scala: -------------------------------------------------------------------------------- 1 | package tlb 2 | 3 | import cpu.Utils 4 | import cpu.defs.ConstantVal 5 | import spinal.core._ 6 | import spinal.lib._ 7 | import scala.language.postfixOps 8 | 9 | //TODO: 如果取指不经过cache怎么办 10 | //TODO: CP0的某些寄存器如果是特殊值,可以让kesg0变成Uncached 11 | //TODO: 统一valid为invalid;统一在一个地方进行地址检查? 12 | 13 | /** @param useTLB 是否开启TLB转换 14 | * @note MMU进行地址翻译后得到的结果 15 | */ 16 | case class MMUTranslationRes() extends Bundle { 17 | val paddr = UInt(32 bits) 18 | val cached = Bool 19 | } 20 | 21 | class CPUMMUInterface extends Bundle with IMasterSlave { 22 | val instVaddr = UInt(32 bits) //指令对应虚拟地址 23 | val dataVaddr = UInt(32 bits) //数据对应虚拟地址 24 | val instRes = MMUTranslationRes() 25 | val dataRes = MMUTranslationRes() 26 | 27 | override def asMaster(): Unit = { 28 | in(instRes, dataRes) 29 | out(instVaddr, dataVaddr) 30 | } 31 | } 32 | 33 | class MMU extends Component { 34 | val io = slave(new CPUMMUInterface) 35 | io.instRes.paddr := (B(0, 3 bits) ## io.instVaddr(0, 29 bits)).asUInt 36 | io.dataRes.paddr := (B(0, 3 bits) ## io.dataVaddr(0, 29 bits)).asUInt 37 | //不开启TLB的时候除了kseg1全是Cache的 38 | io.instRes.cached := !Utils.VAddr.isUncachedSection(io.instVaddr) 39 | io.dataRes.cached := !Utils.VAddr.isUncachedSection(io.dataVaddr) 40 | } 41 | 42 | object MMU { 43 | def main(args: Array[String]): Unit = { 44 | SpinalVerilog(new MMU) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/scala/tlb/TLB.scala: -------------------------------------------------------------------------------- 1 | package tlb 2 | 3 | import spinal.core._ 4 | import spinal.lib.{MuxOH, OHToUInt} 5 | import cpu.defs.ConstantVal 6 | import scala.language.postfixOps 7 | 8 | //R1仅支持4KiB的页 9 | 10 | object TLBConfig { 11 | // 常量 12 | val maskWidth = 16 13 | val vpn2Width = 19 14 | val asidWidth = 8 15 | val pfnWidth = ConstantVal.PABITS - 12 16 | val cacheAttrWidth = 3 17 | val tlbIndexWidth = log2Up(ConstantVal.TLBEntryNum) 18 | } 19 | 20 | /** TLB翻译的结果 */ 21 | class TLBTranslationRes extends Bundle { 22 | 23 | /** 翻译得到的物理地址 */ 24 | val paddr = UInt(32 bits) 25 | 26 | /** TLB Entry中对应的Cacheability and Coherency Attribute */ 27 | val cacheAttr = Bits(TLBConfig.cacheAttrWidth bits) 28 | 29 | val miss, dirty, invalid = Bool 30 | 31 | //根据每一个表项hit的情况,来对TLBTranslationRes进行赋值 32 | def assignSomeFromEntry(evenOddBit: Bool, hitEntry: TLBEntry): Unit = { 33 | when(evenOddBit) { 34 | //evenOddBit为1,选择pfn1 35 | this.paddr(12, 20 bits) := hitEntry.pfn1(0, 20 bits).asUInt 36 | this.dirty := hitEntry.D1 37 | this.invalid := !hitEntry.V1 38 | this.cacheAttr := hitEntry.C1 39 | }.otherwise { 40 | //evenOddBit为0,选择pfn0 41 | this.paddr(12, 20 bits) := hitEntry.pfn0(0, 20 bits).asUInt 42 | this.dirty := hitEntry.D0 43 | this.invalid := !hitEntry.V0 44 | this.cacheAttr := hitEntry.C0 45 | } 46 | } 47 | 48 | def setDefault():Unit = { 49 | paddr(12, 20 bits) := 0 50 | cacheAttr := 0 51 | miss := True 52 | dirty := True 53 | invalid := True 54 | } 55 | } 56 | 57 | class TLBEntry extends Bundle { 58 | val mask = Bits(TLBConfig.maskWidth bits) 59 | val vpn2 = Bits(TLBConfig.vpn2Width bits) 60 | val asid = Bits(TLBConfig.asidWidth bits) 61 | val pfn0, pfn1 = Bits(TLBConfig.pfnWidth bits) 62 | val C0, C1 = Bits(TLBConfig.cacheAttrWidth bits) 63 | val V0, V1, D0, D1, G = Bool 64 | } 65 | 66 | /** @note 由于是全连接的TLB,直接使用寄存器存储,因为要同时读出来所有的TLB Entry并比较 67 | * 此外该TLB的查询是双端口,支持inst和data同时查询 68 | */ 69 | class TLB extends Component { 70 | val io = new Bundle { 71 | val asid = in Bits (TLBConfig.asidWidth bits) //当前EntryHi对应的ASID 72 | val instVaddr = in UInt (32 bits) //指令对应虚拟地址 73 | val dataVaddr = in UInt (32 bits) //数据对应虚拟地址 74 | val instRes = out(new TLBTranslationRes) 75 | val dataRes = out(new TLBTranslationRes) 76 | 77 | //TLB指令相关的内容,包括tlbp, tlbr, tlbwi, tlbwr 78 | val index = in UInt (TLBConfig.tlbIndexWidth bits) //可能是Index,也可能是Random 79 | val wdata = in(new TLBEntry) //需要把要写入的TLB内容提前填充成一个Entry项 80 | val write = in Bool () //是否把wdata写入TLB中 81 | val rdata = out(new TLBEntry) 82 | 83 | //针对tlbp的 84 | val probeVPN2 = in Bits (TLBConfig.vpn2Width bits) 85 | val probeASID = in Bits (TLBConfig.asidWidth bits) //TODO: 这里的asid和上面的asid应该一直是一样的? 86 | val probeIndex = out Bits (32 bits) //这个值可以直接写回Index CP0寄存器 87 | } 88 | 89 | val addr = new Area { 90 | val instVPN2 = io.instVaddr(13, TLBConfig.vpn2Width bits).asBits //31 downto 13 91 | val dataVPN2 = io.dataVaddr(13, TLBConfig.vpn2Width bits).asBits 92 | val instEvenOddBit = io.instVaddr(12) 93 | val dataEvenOddBit = io.dataVaddr(12) 94 | val instOffset = io.instVaddr(0, 12 bits) 95 | val dataOffset = io.dataVaddr(0, 12 bits) 96 | } 97 | 98 | val entry = Vec(Reg(new TLBEntry), ConstantVal.TLBEntryNum) 99 | for (e <- entry) e init (new TLBEntry).getZero 100 | //读tlb 为tlbr服务 101 | io.rdata := entry(io.index) 102 | //写tlb 为tlbw服务 103 | val writeEntry = new TLBEntry 104 | when(io.write) { 105 | entry(io.index) := writeEntry 106 | } 107 | 108 | val default_ = new Area { 109 | io.instRes.paddr(0, 12 bits) := addr.instOffset 110 | io.dataRes.paddr(0, 12 bits) := addr.dataOffset 111 | io.instRes.setDefault() 112 | io.dataRes.setDefault() 113 | 114 | writeEntry := io.wdata 115 | io.probeIndex(31).assignFromBits(B"1'b1") 116 | io.probeIndex(0, 31 bits) := 0 117 | } 118 | for (i <- 0 until ConstantVal.TLBEntryNum) { 119 | when(TLB.entryMatch(entry(i), io.asid, addr.instVPN2)) { 120 | io.instRes.assignSomeFromEntry(addr.instEvenOddBit, entry(i)) 121 | io.instRes.miss := False 122 | } 123 | when(TLB.entryMatch(entry(i), io.asid, addr.dataVPN2)) { 124 | io.dataRes.assignSomeFromEntry(addr.dataEvenOddBit, entry(i)) 125 | io.dataRes.miss := False 126 | } 127 | when(TLB.entryMatch(entry(i), io.probeASID, io.probeVPN2)) { 128 | io.probeIndex(0, TLBConfig.tlbIndexWidth bits) := B(i) 129 | //probeIndex(31)为0表示命中TLB,为1表示未命中TLB 130 | io.probeIndex(31).assignFromBits(B"1'b0") 131 | } 132 | } 133 | 134 | //写入TLB内容的逻辑,会主动作用mask 135 | writeEntry.allowOverride 136 | writeEntry.vpn2 := TLB.getMaskedAddr(addr = io.wdata.vpn2, io.wdata.mask) 137 | writeEntry.pfn0 := TLB.getMaskedAddr(addr = io.wdata.pfn0, io.wdata.mask) 138 | writeEntry.pfn1 := TLB.getMaskedAddr(addr = io.wdata.pfn1, io.wdata.mask) 139 | } 140 | 141 | object TLB { 142 | def main(args: Array[String]): Unit = { 143 | SpinalVerilog(new TLB) 144 | } 145 | 146 | /** @param entry TLB中的一项 147 | * @param asid 当前entryHi中的ASID 148 | * @param vpn2 要查询虚拟地址的vpn2 149 | * @note 来判断一个表项是否匹配 150 | */ 151 | def entryMatch(entry: TLBEntry, asid: Bits, vpn2: Bits): Bool = 152 | TLB.VPN2Match(entry, vpn2) & (entry.asid === asid | entry.G) 153 | 154 | /** 检测TLB entry的vpn2是否匹配vpn2 */ 155 | def VPN2Match(entry: TLBEntry, vpn2: Bits): Bool = 156 | getMaskedAddr(entry.vpn2, entry.mask) === getMaskedAddr(vpn2, entry.mask) 157 | 158 | private def getMaskedAddr(addr: Bits, mask: Bits): Bits = { 159 | assert(mask.getBitsWidth == TLBConfig.maskWidth) 160 | addr & ~mask.resize(addr.getBitsWidth) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/scala/util/Using.scala: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import scala.language.reflectiveCalls 4 | 5 | object Using { 6 | def apply[T <: { def close() }, U](resource: T)(block: T => U): U = { 7 | try { 8 | block(resource) 9 | } finally { 10 | if (resource != null) resource.close() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/util/VivadoConf.scala: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import com.typesafe.config.ConfigFactory 4 | 5 | import java.io.File 6 | 7 | object VivadoConf { 8 | lazy val config = ConfigFactory.parseFile(new File("vivado.conf")) 9 | 10 | def vivadoPath = config.getString("vivado.path") 11 | } 12 | -------------------------------------------------------------------------------- /src/main/scala/util/binary.scala: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | package object binary { 4 | implicit class BigIntWithBinaryRepr(b: BigInt) { 5 | 6 | /** 转为小端序的定长字节数组 */ 7 | def toBinary(length: Int): Array[Byte] = { 8 | val bytes = b.toByteArray 9 | val padded = Array.fill[Byte](length - bytes.length)(0) ++ bytes 10 | padded.reverseIterator.take(length).toArray 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/scala/cache/ICacheTest.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.lib.slave 6 | import spinal.lib.bus.amba4.axi.{Axi4, Axi4Config} 7 | 8 | import scala.util.Random 9 | import scala.util.control.Breaks.{break, breakable} 10 | import ram._ 11 | 12 | import scala.io.Source 13 | 14 | //class ICacheTest extends Component { 15 | // val io = new Bundle { 16 | // val cpu = slave(new CPUICacheInterface) 17 | // } 18 | // 19 | // val cpu = new CPUICacheInterface 20 | // val iCache = new ICache(CacheRamConfig(32, 7, 8, true)) 21 | // val ram = new axi_ram(AXIRamConfig(32, ICacheTest.addrWidth, idWidth = 4)) 22 | // io.cpu >> cpu 23 | // cpu >> iCache.io.cpu 24 | // iCache.io.axi >> ram.io.axi 25 | //} 26 | // 27 | //object ICacheTest { 28 | // val rand = new Random(2021) 29 | // val addrWidth = 20 30 | // val iterNum = 500000 31 | // 32 | // def getMapAddr(addr: Long): Long = { 33 | // addr & ((0x1 << addrWidth) - 1) 34 | // } 35 | // 36 | // def getMemInitData: Map[Long, Long] = { 37 | // val source = Source.fromFile("./script/data.txt") 38 | // val lineIterator = source.getLines().zipWithIndex 39 | // var res = Map[Long, Long]() 40 | // for ((line, addr) <- lineIterator) { 41 | // res += ((addr * 4).toLong -> java.lang.Long.parseLong(line, 16)) 42 | // } 43 | // res 44 | // } 45 | // 46 | // //range: 后16位变动的范围 47 | // def getRandomAddr(range: Int): (Int, Long) = { 48 | // val baseRange = 1 << (addrWidth - 16) 49 | // val randomRamAddr = rand.nextInt(range) & 0xfffc 50 | // var cpuBase = (rand.nextInt(baseRange) & 0xffff) + 0xbfc0 51 | // cpuBase = Math.min(cpuBase, 0xffff) 52 | // val cpuAddr = ((cpuBase.toLong << 16) | randomRamAddr) 53 | // // println("base = 0x%x, rand = 0x%x, cpu addr = 0x%x".format(cpuBase, randomRamAddr, cpuAddr)) 54 | // (randomRamAddr, cpuAddr) 55 | // } 56 | // 57 | // def main(args: Array[String]): Unit = { 58 | // val compile = SimConfig.addSimulatorFlag("-Wno-CASEINCOMPLETE").addSimulatorFlag("-Wno-TIMESCALEMOD"). 59 | // addRtl("./rtl/axi_ram.v").withWave.allOptimisation.compile(new ICacheTest) 60 | // compile.doSim("ICache_small_range_random_read", 2021) { dut => { 61 | // dut.clockDomain.forkStimulus(10) 62 | // dut.clockDomain.assertReset() 63 | // sleep(10) 64 | // dut.clockDomain.deassertReset() 65 | // val memData = getMemInitData 66 | // dut.io.cpu.read #= false 67 | // for (index <- 0 to 10000) { 68 | // var waitCycle = rand.nextInt(3) 69 | // if (index == 0 && waitCycle == 0) waitCycle = 1 70 | // dut.clockDomain.waitRisingEdge(waitCycle) 71 | // val (_, randomAddr) = getRandomAddr(0x1000) 72 | // if (index % 1000 == 0) { 73 | // println("randomAddr = 0x%x".format(randomAddr)) 74 | // } 75 | // dut.io.cpu.read #= true 76 | // dut.io.cpu.addr #= randomAddr 77 | // dut.clockDomain.waitFallingEdge() 78 | // while (dut.io.cpu.stall.toBoolean) (dut.clockDomain.waitSampling()) 79 | // assert(dut.io.cpu.data.toLong == memData(getMapAddr(randomAddr))) 80 | // dut.io.cpu.read #= false 81 | // } 82 | // } 83 | // } 84 | // 85 | // compile.doSim("ICache_large_range_random_read", 2021) { dut => { 86 | // dut.clockDomain.forkStimulus(10) 87 | // dut.clockDomain.assertReset() 88 | // sleep(10) 89 | // dut.clockDomain.deassertReset() 90 | // val memData = getMemInitData 91 | // dut.io.cpu.read #= false 92 | // for (index <- 0 to iterNum) { 93 | // if (index % (iterNum / 10) == 0) println("Sim for %d / %d times".format(index, iterNum)) 94 | // var waitCycle = rand.nextInt(3) 95 | // if (index == 0 && waitCycle == 0) waitCycle = 1 96 | // dut.clockDomain.waitRisingEdge(waitCycle) 97 | // val (_, randomAddr) = getRandomAddr(0x1000) 98 | // dut.io.cpu.read #= true 99 | // dut.io.cpu.addr #= randomAddr 100 | // dut.clockDomain.waitFallingEdge() 101 | // while (dut.io.cpu.stall.toBoolean) (dut.clockDomain.waitSampling()) 102 | // assert(dut.io.cpu.data.toLong == memData(getMapAddr(randomAddr))) 103 | // dut.io.cpu.read #= false 104 | // } 105 | // } 106 | // } 107 | // } 108 | //} 109 | -------------------------------------------------------------------------------- /src/test/scala/cache/LRUTest.scala: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import cache.LRUTest.access 4 | import lib.Updating 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | 8 | import scala.util.Random 9 | 10 | class LRUTest(wayNum: Int = 8) extends Component { 11 | val io = new Bundle { 12 | val access = in UInt (log2Up(wayNum) bits) 13 | val update = in Bool () 14 | val next = out UInt (log2Up(wayNum) bits) 15 | } 16 | val manager = new LRUManegr(wayNum, 1) 17 | manager.write.en := io.update 18 | manager.write.access := io.access 19 | manager.write.addr := 0 20 | 21 | io.next := manager.latestWayIndex(0) 22 | } 23 | 24 | object LRUTest { 25 | //下面是两个手动模拟plru的测例 26 | def access(number: Int, status: Array[Array[Int]], width: Int): Unit = { 27 | val num: Array[Int] = Array.tabulate(width)(index => { 28 | (number >> (width - 1 - index)) & 0x1 29 | }) 30 | var pre = 0 31 | for (i <- 0 until width) { 32 | status(i)(pre) = num(i) 33 | pre = (pre << 1) | num(i) 34 | } 35 | } 36 | 37 | def getNext(status: Array[Array[Int]], width: Int): Int = { 38 | var res = 0 39 | for (i <- 0 until width) { 40 | if (status(i)(res) == 0) { 41 | res = (res << 1) | 1 42 | } else { 43 | res = (res << 1) | 0 44 | } 45 | } 46 | res 47 | } 48 | 49 | def main(args: Array[String]): Unit = { 50 | val rand = new Random(2021) 51 | for (width <- 1 to 3) { 52 | val way = 1 << width 53 | SimConfig 54 | .addSimulatorFlag("-Wno-CASEINCOMPLETE") 55 | .withWave 56 | .allOptimisation 57 | .compile(new LRUTest(way)) 58 | .doSim(s"LRU_Test_${width}", 2021) { dut => 59 | { 60 | dut.clockDomain.forkStimulus(10) 61 | dut.clockDomain.assertReset() 62 | sleep(10) 63 | dut.clockDomain.deassertReset() 64 | dut.clockDomain.waitSampling() 65 | var status: Array[Array[Int]] = Array(Array(0), Array(0, 0), Array(0, 0, 0, 0)) 66 | for (iter <- 0 until 20000) { 67 | val num = rand.nextInt(way) 68 | access(num, status, width) 69 | dut.io.access #= num 70 | dut.io.update #= true 71 | dut.clockDomain.waitSampling() 72 | dut.io.update #= false 73 | dut.clockDomain.waitFallingEdge() 74 | assert(dut.io.next.toInt == getNext(status, width)) 75 | dut.clockDomain.waitSampling(rand.nextInt(3)) 76 | } 77 | } 78 | } 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/scala/cpu/COEParse.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import util.Using 4 | import util.binary._ 5 | 6 | import java.io.File 7 | import scala.collection.mutable 8 | import scala.io.Source 9 | import scala.util.Try 10 | 11 | object COEParse { 12 | val radixRegex = raw"memory_initialization_radix = ([1-9][0-9]*);?".r 13 | val vectorRegex = raw"memory_initialization_vector =".r 14 | 15 | def apply(file: File): Try[Array[Byte]] = Try { 16 | Using(Source.fromFile(file)) { source => 17 | val lines = source.getLines 18 | val radix = lines.next match { 19 | case radixRegex(radixString) => 20 | radixString.toInt 21 | case _ => 22 | throw new Exception(s"Invalid radix in COE file ${file.getName}") 23 | } 24 | val buffer = mutable.ArrayBuffer[Byte]() 25 | 26 | lines.next match { 27 | case vectorRegex() => 28 | case _ => 29 | throw new Exception(s"Invalid format") 30 | } 31 | 32 | val widthInBytes = 4 33 | 34 | var x = 0 35 | for (line <- lines if line.trim.nonEmpty) { 36 | val numeral = if (line.last == ',') line.init else line 37 | 38 | x += 1 39 | for (byte <- BigInt(numeral, radix = radix).toBinary(widthInBytes)) { 40 | buffer += byte 41 | } 42 | } 43 | 44 | buffer.toArray 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/scala/cpu/Sim.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.display.DisplayerConfig 4 | import scopt.OptionParser 5 | import spinal.lib.bus.amba4.axi.sim.AxiMemorySimConfig 6 | 7 | import java.io.File 8 | import scala.util.{Failure, Success} 9 | 10 | object Sim { 11 | case class Config( 12 | finalPc: Option[Long], 13 | cpuClockPeriod: Int, 14 | sysClockPeriod: Int, 15 | instRamCOE: File, 16 | writeBackFile: Option[File], 17 | goldenTrace: Option[File], 18 | timeout: Option[Long] 19 | ) 20 | 21 | def main(args: Array[String]): Unit = { 22 | val parser = new OptionParser[Config]("sim") { 23 | head("LLCL-Mips simulator") 24 | 25 | opt[String]('f', "final-pc") 26 | .validate(x => 27 | if (x matches raw"\p{XDigit}{8}") success 28 | else failure("must be 8 digit hex number") 29 | ) 30 | .action((x, c) => c.copy(finalPc = Some(BigInt(x, radix = 16).toLong))) 31 | .valueName("") 32 | .text("PC where the simulation terminates (format: bfc00000)") 33 | 34 | opt[Int]('c', "cpu-clock") 35 | .action((x, c) => c.copy(cpuClockPeriod = x)) 36 | .valueName("") 37 | .text("period of CPU clock in simulation, defaults to 20") 38 | 39 | opt[Int]('s', "sys-clock") 40 | .action((x, c) => c.copy(sysClockPeriod = x)) 41 | .valueName("") 42 | .text("period of soc clock in simulation, defaults to 10") 43 | 44 | opt[File]('i', "inst-ram-coe") 45 | .required() 46 | .valueName("") 47 | .action((x, c) => c.copy(instRamCOE = x)) 48 | .text(".coe file to initialize inst ram") 49 | 50 | opt[File]('w', "write-back") 51 | .action((x, c) => c.copy(writeBackFile = Some(x))) 52 | .valueName("") 53 | .text("file to print write back data") 54 | 55 | opt[File]('g', "golden") 56 | .action((x, c) => c.copy(goldenTrace = Some(x))) 57 | .valueName("") 58 | .text("golden trace to compare write back data") 59 | 60 | opt[Long]('t', "timeout") 61 | .action((x, c) => c.copy(timeout = Some(x))) 62 | .valueName("") 63 | .text("terminate the simulation after such units of time") 64 | 65 | help("help") 66 | .text("prints help message") 67 | } 68 | 69 | val parsed = parser.parse( 70 | args, 71 | Config( 72 | finalPc = None, 73 | cpuClockPeriod = 20, 74 | sysClockPeriod = 10, 75 | instRamCOE = null, 76 | writeBackFile = None, 77 | goldenTrace = None, 78 | timeout = None 79 | ) 80 | ) 81 | 82 | if (parsed.isEmpty) sys.exit(1) 83 | val config = parsed.get 84 | 85 | val instRamData = COEParse(config.instRamCOE) match { 86 | case Success(d) => d 87 | case Failure(exception) => 88 | println(exception.getMessage) 89 | sys.exit(1) 90 | } 91 | 92 | val simulator = Simulator( 93 | Simulator.Config( 94 | mem = AxiMemorySimConfig( 95 | maxOutstandingReads = 4, 96 | maxOutstandingWrites = 4, 97 | readResponseDelay = 250, 98 | writeResponseDelay = 30, 99 | interruptProbability = 0, 100 | interruptMaxDelay = 10 101 | ), 102 | initSections = Seq(Simulator.MemSection(0x1fc00000, instRamData)), 103 | cpuClockPeriod = config.cpuClockPeriod, 104 | sysClockPeriod = config.sysClockPeriod 105 | ) 106 | ) 107 | 108 | simulator.addPlugin(PCBroadcastPlugin(10000)) 109 | 110 | funcTest.ConfRegs().addTo(simulator, displayerConfig = Some(DisplayerConfig(throttlePeriod = Some(10000)))) 111 | simulator.onSetupSim { context => 112 | context.mem.getElseAllocPage(0) 113 | } 114 | 115 | for (finalPc <- config.finalPc) { 116 | simulator.addPlugin(TerminatorPlugin(finalPc)) 117 | } 118 | 119 | if (config.writeBackFile.nonEmpty || config.goldenTrace.nonEmpty) { 120 | simulator.addPlugin(WriteBackProducerPlugin()) 121 | } 122 | 123 | for (file <- config.writeBackFile) { 124 | simulator.addPlugin(WriteBackFileLoggerPlugin(file)) 125 | } 126 | 127 | for (file <- config.goldenTrace) { 128 | simulator.addPlugin(WriteBackComparerPlugin(file)) 129 | } 130 | 131 | for (timeout <- config.timeout) { 132 | simulator.addPlugin(TimeoutPlugin(timeout)) 133 | } 134 | 135 | simulator.run(new SimulationSoc, workspaceName = "Sim") 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/test/scala/cpu/SimulationSoc.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.defs.ConstantVal 4 | import ip.sim.JobCollector 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.lib.bus.amba4.axi._ 9 | 10 | /** 对 MyCPUTop 的简单封装,使得其适用于仿真 */ 11 | class SimulationSoc extends Component with JobCollector { 12 | val io = new Bundle { 13 | val sysClock = in Bool () 14 | val cpuClock = in Bool () 15 | val reset = in Bool () 16 | 17 | val externalInterrupt = in Bits (6 bits) 18 | 19 | val axi = master(Axi4(ConstantVal.AXI_BUS_CONFIG)) 20 | 21 | val debugInterface = out(DebugInterface()) 22 | } 23 | 24 | val sysClockDomain = ClockDomain( 25 | clock = io.sysClock, 26 | reset = io.reset, 27 | config = ClockDomainConfig(resetActiveLevel = LOW) 28 | ) 29 | 30 | val cpuClockDomain = ClockDomain( 31 | clock = io.cpuClock, 32 | reset = io.reset, 33 | config = ClockDomainConfig(resetActiveLevel = LOW) 34 | ) 35 | 36 | val cpu = new MyCPUTop 37 | cpu.io.ext_int := io.externalInterrupt 38 | cpu.io.aclk := io.cpuClock 39 | cpu.io.aresetn := io.reset 40 | io.debugInterface := cpu.io.debug 41 | 42 | val clockConverter = new Axi4CC( 43 | axiConfig = ConstantVal.AXI_BUS_CONFIG, 44 | inputCd = cpuClockDomain, 45 | outputCd = sysClockDomain, 46 | arFifoSize = 4, 47 | rFifoSize = 4, 48 | awFifoSize = 4, 49 | wFifoSize = 4, 50 | bFifoSize = 4 51 | ) 52 | cpu.io.axi >> clockConverter.io.input 53 | clockConverter.io.output >> io.axi 54 | 55 | cpu.io.aclk.pull().simPublic() 56 | cpu.io.aresetn.pull().simPublic() 57 | } 58 | -------------------------------------------------------------------------------- /src/test/scala/cpu/Simulator.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.lib.bus.amba4.axi.sim.AxiMemorySimConfig 6 | import util.VivadoConf 7 | 8 | import scala.collection.mutable 9 | import scala.reflect.{ClassTag, classTag} 10 | 11 | object Simulator { 12 | case class MemSection( 13 | startAddr: Long, 14 | data: Array[Byte] 15 | ) 16 | 17 | case class Config( 18 | mem: AxiMemorySimConfig, 19 | initSections: Seq[MemSection] = Seq(), 20 | cpuClockPeriod: Long, 21 | sysClockPeriod: Long = 10 22 | ) 23 | 24 | trait Plugin { 25 | def setupSim(context: Simulator#Context): Unit 26 | def close(): Unit = {} 27 | } 28 | 29 | def compile[T <: SimulationSoc]( 30 | dut: => T, 31 | workspaceName: String, 32 | withWave: Boolean = true 33 | ): SimCompiled[T] = { 34 | val includeDir = VivadoConf.vivadoPath + "/data/verilog/src/xeclib" 35 | 36 | (if (withWave) SimConfig.withWave else SimConfig) 37 | .workspaceName(workspaceName) 38 | .withConfig(SpinalConfig().includeSimulation) 39 | .allOptimisation 40 | .addSimulatorFlag("-Wno-TIMESCALEMOD") 41 | .addSimulatorFlag("-Wno-INITIALDLY") 42 | // .addIncludeDir(includeDir) 43 | .addSimulatorFlag(s"-I$includeDir") 44 | .addSimulatorFlag("--timescale-override 1ns/1ns") 45 | .compile(dut) 46 | } 47 | } 48 | 49 | /** 封装的 CPU 仿真器,可配置时钟、内存等,支持添加插件。 */ 50 | case class Simulator(config: Simulator.Config) { 51 | import Simulator._ 52 | 53 | val plugins = mutable.ArrayBuffer[Plugin]() 54 | 55 | def addPlugin(plugin: Plugin): this.type = { 56 | plugins += plugin 57 | this 58 | } 59 | 60 | private def getPluginSeq[T <: Plugin: ClassTag]: Seq[T] = { 61 | val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]] 62 | 63 | plugins.filter(p => clazz.isAssignableFrom(p.getClass)).map(_.asInstanceOf[T]).toSeq 64 | } 65 | 66 | /** 获取某种特定类型的插件,仅在恰有一个插件满足条件时成功 */ 67 | def getPluginOption[T <: Plugin: ClassTag]: Option[T] = { 68 | getPluginSeq[T] match { 69 | case Seq(only) => Some(only) 70 | case _ => None 71 | } 72 | } 73 | 74 | /** 获取某种特定类型的插件,仅在恰有一个插件满足条件时成功 */ 75 | def getPlugin[T <: Plugin: ClassTag]: T = { 76 | getPluginSeq[T] match { 77 | case Seq() => 78 | throw new AssertionError(s"Plugin of type ${classTag[T].runtimeClass.getName} not loaded.") 79 | case Seq(only) => 80 | only 81 | case _ => 82 | throw new AssertionError( 83 | s"Reference to plugin of type ${classTag[T].runtimeClass.getName} is ambiguous" 84 | ) 85 | } 86 | } 87 | 88 | def onSetupSim(cb: Simulator#Context => Unit) = addPlugin(new Plugin { 89 | override def setupSim(context: Simulator#Context) = cb(context) 90 | }) 91 | 92 | /** 插件所需要的环境 */ 93 | class Context( 94 | val soc: SimulationSoc, 95 | val memorySim: AxiMemorySim, 96 | val sysClockDomain: ClockDomain, 97 | val cpuClockDomain: ClockDomain 98 | ) { 99 | def mem = memorySim.memory 100 | var openTrace = true 101 | 102 | def getPluginOption[T <: Plugin: ClassTag]: Option[T] = Simulator.this.getPluginOption 103 | def getPlugin[T <: Plugin: ClassTag]: T = Simulator.this.getPlugin 104 | } 105 | 106 | def run(dut: => SimulationSoc, workspaceName: String): Unit = 107 | run(dut, workspaceName, withWave = true) 108 | def run(dut: => SimulationSoc, workspaceName: String, withWave: Boolean): Unit = 109 | run(Simulator.compile(dut, workspaceName, withWave)) 110 | 111 | def run[T <: SimulationSoc](compiled: SimCompiled[T], name: String = "test") = 112 | compiled.doSimUntilVoid(name)(doSim _) 113 | 114 | private def doSim(soc: SimulationSoc): Unit = { 115 | soc.sysClockDomain.forkStimulus(period = config.sysClockPeriod) 116 | soc.cpuClockDomain.forkStimulus(period = config.cpuClockPeriod) 117 | 118 | soc.io.externalInterrupt #= 0 119 | 120 | val memorySim = AxiMemorySim(soc.io.axi, soc.sysClockDomain, config.mem) 121 | for (MemSection(addr, data) <- config.initSections) { 122 | memorySim.memory.writeArray(addr.toLong, data) 123 | } 124 | memorySim.start() 125 | 126 | for (job <- soc.jobs) { 127 | fork { 128 | job() 129 | } 130 | } 131 | 132 | val pluginThreadContext = new Context( 133 | soc = soc, 134 | memorySim = memorySim, 135 | sysClockDomain = soc.sysClockDomain, 136 | cpuClockDomain = soc.cpuClockDomain 137 | ) 138 | for (plugin <- plugins) { 139 | fork { 140 | plugin.setupSim(pluginThreadContext) 141 | } 142 | } 143 | 144 | onSimEnd { 145 | for (plugin <- plugins) { 146 | plugin.close() 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/test/scala/cpu/SimulatorPlugins.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core.sim._ 4 | 5 | import java.io.{BufferedWriter, File, FileWriter} 6 | import scala.collection.mutable 7 | import scala.io.Source 8 | 9 | /** 结束仿真的插件。 */ 10 | case class TerminatorPlugin(finalPc: Long) extends Simulator.Plugin { 11 | override def setupSim(context: Simulator#Context) = fork { 12 | println(f"Simulation will terminate when PC reaches $finalPc%08x.") 13 | 14 | context.sysClockDomain.waitSamplingWhere( 15 | context.soc.io.debugInterface.wb.pc.toLong == finalPc 16 | ) 17 | 18 | simSuccess() 19 | } 20 | } 21 | 22 | /** 输出 PC 的插件。 */ 23 | case class PCBroadcastPlugin(period: Int) extends Simulator.Plugin { 24 | override def setupSim(context: Simulator#Context) = fork { 25 | println(s"Broadcasting PC every $period CPU clock periods...") 26 | 27 | def pc = context.soc.io.debugInterface.wb.pc.toLong 28 | 29 | var tick: Long = 0 30 | while (true) { 31 | context.sysClockDomain.waitSampling(period) 32 | tick += period 33 | println(f"CPU running ($tick cycles), PC = $pc%08x") 34 | } 35 | } 36 | } 37 | 38 | case class WriteBack(pc: Long, addr: Int, data: Long) { 39 | def toTrace = f"1 $pc%08x $addr%02x $data%08x\n" 40 | } 41 | 42 | case class WriteBackProducerPlugin() extends Simulator.Plugin { 43 | private val callbacks = mutable.ArrayBuffer[WriteBack => Unit]() 44 | 45 | def addCallback(cb: WriteBack => Unit) = { 46 | callbacks += cb 47 | } 48 | 49 | override def setupSim(context: Simulator#Context) = fork { 50 | val debugWb = context.soc.io.debugInterface.wb 51 | 52 | while (true) { 53 | context.cpuClockDomain.waitSampling() 54 | context.cpuClockDomain.waitEdge(1) // 等待下沿 55 | if (debugWb.rf.wen.toInt != 0 && context.openTrace) { 56 | val wb = WriteBack( 57 | pc = debugWb.pc.toLong, 58 | addr = debugWb.rf.wnum.toInt, 59 | data = debugWb.rf.wdata.toLong 60 | ) 61 | callbacks foreach { _(wb) } 62 | } 63 | } 64 | } 65 | } 66 | 67 | /** 将写回数据写到文件中的插件。 */ 68 | case class WriteBackFileLoggerPlugin(file: File) extends Simulator.Plugin { 69 | val writer = new BufferedWriter(new FileWriter(file)) 70 | 71 | override def setupSim(context: Simulator#Context) = { 72 | println(s"Write back data will be logged to ${file.getAbsolutePath}...") 73 | 74 | context.getPlugin[WriteBackProducerPlugin].addCallback { wb => 75 | writer.write(wb.toTrace) 76 | } 77 | } 78 | 79 | override def close() = writer.close() 80 | } 81 | 82 | /** 将写回数据和给定数据比较的插件。 */ 83 | case class WriteBackComparerPlugin(file: File, failOnError: Boolean = true) 84 | extends Simulator.Plugin { 85 | val traceRegex = raw"1 (\p{XDigit}{8}) (\p{XDigit}{2}) (\p{XDigit}{8})".r 86 | 87 | val source = Source.fromFile(file) 88 | 89 | val lines = source.getLines.filter(_(0) != '0') 90 | var errorCount = 0 91 | 92 | override def setupSim(context: Simulator#Context) = { 93 | println(s"Write back data will be compared against ${file.getAbsolutePath}...") 94 | 95 | context.getPlugin[WriteBackProducerPlugin].addCallback { wb => 96 | if (errorCount >= 30) { 97 | // Do nothing 98 | } else if (!lines.hasNext) { 99 | println("=" * 40) 100 | println("Extra write back detected: ") 101 | println(wb.toTrace) 102 | println("=" * 40) 103 | if (failOnError) simFailure("Extra write back detected.") 104 | errorCount += 1 105 | } else { 106 | lines.next match { 107 | case line @ traceRegex(pc, addr, data) => 108 | if ( 109 | BigInt(pc, 16) != wb.pc || BigInt(addr, 16) != wb.addr || BigInt( 110 | data, 111 | 16 112 | ) != wb.data 113 | ) { 114 | println("=" * 40) 115 | println("Wrong write back detected: ") 116 | println(" Golden trace: ") 117 | println(line) 118 | println(" Simulation: ") 119 | println(wb.toTrace) 120 | println("=" * 40) 121 | if (failOnError) simFailure("Wrong write back detected.") 122 | errorCount += 1 123 | } 124 | case line => 125 | println("Invalid golden trace: ") 126 | println(line) 127 | if (failOnError) simFailure(s"Invalid golden trace.") 128 | errorCount += 1 129 | } 130 | } 131 | } 132 | } 133 | 134 | override def close() = source.close() 135 | } 136 | 137 | case class TimeoutPlugin(timeout: Long) extends Simulator.Plugin { 138 | override def setupSim(context: Simulator#Context) = SimTimeout(timeout) 139 | } 140 | -------------------------------------------------------------------------------- /src/test/scala/cpu/confreg/package.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core.sim._ 4 | 5 | import java.util.concurrent.atomic.AtomicInteger 6 | 7 | package object confreg { 8 | // confreg 中寄存器寄存器可能事实上应下个周期再写入 9 | 10 | case class Switch(addr: Long, interleaveAddr: Option[Long] = None) extends Simulator.Plugin { 11 | val data = Array.fill(8)(true) 12 | 13 | override def setupSim(context: Simulator#Context) = { 14 | context.memorySim.addSingleWordMappedIO( 15 | addr, 16 | new MappedIODevice with PerWord { 17 | override def readWord(addr: Long) = 18 | data.zipWithIndex.map { case (s, i) => if (s) 1 << i else 0 }.reduce(_ | _) 19 | } 20 | ) 21 | 22 | interleaveAddr foreach { iAddr => 23 | context.memorySim.addSingleWordMappedIO( 24 | iAddr, 25 | new MappedIODevice with PerWord { 26 | override def readWord(addr: Long) = 27 | data.zipWithIndex.map { case (s, i) => if (s) 1 << (i * 2 + 1) else 0 }.reduce(_ | _) 28 | } 29 | ) 30 | } 31 | } 32 | } 33 | 34 | case class IOSimu(addr: Int) extends Simulator.Plugin { 35 | var ioSimu = 0 36 | 37 | override def setupSim(context: Simulator#Context) = { 38 | context.memorySim.addSingleWordMappedIO( 39 | addr, 40 | new MappedIODevice with PerWord { 41 | override def readWord(addr: Long) = ioSimu 42 | 43 | override def writeWord(addr: Long, data: Int) = { 44 | ioSimu = ((data & 0xffff) << 16) | ((data & 0xffff0000) >>> 16) 45 | } 46 | } 47 | ) 48 | } 49 | } 50 | 51 | case class OpenTrace(addr: Int) extends Simulator.Plugin { 52 | override def setupSim(context: Simulator#Context) = { 53 | context.memorySim.addSingleWordMappedIO( 54 | addr, 55 | new MappedIODevice with PerWord { 56 | override def readWord(addr: Long) = if (context.openTrace) 1 else 0 57 | 58 | override def writeWord(addr: Long, data: Int) = { 59 | context.openTrace = data != 0 60 | } 61 | } 62 | ) 63 | } 64 | } 65 | 66 | case class Constant(addr: Int, value: Int) extends Simulator.Plugin { 67 | override def setupSim(context: Simulator#Context) = { 68 | context.memorySim.addSingleWordMappedIO( 69 | addr, 70 | new MappedIODevice with PerWord { 71 | override def readWord(addr: Long) = value 72 | } 73 | ) 74 | } 75 | } 76 | 77 | case class Register(addr: Int) extends Simulator.Plugin { 78 | var value = 0 79 | 80 | override def setupSim(context: Simulator#Context) = { 81 | context.memorySim.addSingleWordMappedIO( 82 | addr, 83 | new MappedIODevice with PerWord { 84 | override def readWord(addr: Long) = value 85 | 86 | override def writeWord(addr: Long, data: Int) = { value = data } 87 | } 88 | ) 89 | } 90 | } 91 | 92 | case class RepaintingRegister(addr: Int) extends Simulator.Plugin { 93 | var value = 0 94 | 95 | override def setupSim(context: Simulator#Context) = { 96 | context.memorySim.addSingleWordMappedIO( 97 | addr, 98 | new MappedIODevice with PerWord { 99 | override def readWord(addr: Long) = value 100 | 101 | override def writeWord(addr: Long, data: Int) = { 102 | value = data 103 | context.getPluginOption[display.DisplayerPlugin] foreach { _.triggerRepaint() } 104 | } 105 | } 106 | ) 107 | } 108 | } 109 | 110 | // 可能和官方的实现有细微的不一致,如果不要求精度应该可以忽略;未严格考虑线程同步? 111 | case class Timer(addr: Int) extends Simulator.Plugin { 112 | // 因为需要 += 1,所以需要原子性 113 | val value = new AtomicInteger(0) 114 | 115 | override def setupSim(context: Simulator#Context) = { 116 | context.memorySim.addSingleWordMappedIO( 117 | addr, 118 | new MappedIODevice with PerWord { 119 | override def readWord(addr: Long) = value.get() 120 | 121 | override def writeWord(addr: Long, data: Int) = { 122 | value.set(data) 123 | } 124 | } 125 | ) 126 | 127 | fork { 128 | while (true) { 129 | context.sysClockDomain.waitSampling() 130 | value.incrementAndGet() 131 | } 132 | } 133 | } 134 | } 135 | 136 | /** UART,将输出视作文字,每到换行打印。 */ 137 | case class TextUART(addr: Int) extends Simulator.Plugin { 138 | val buffer = new StringBuilder(capacity = 80) 139 | 140 | private def printBuffer(): Unit = { 141 | println(s"[UART] $buffer") 142 | buffer.clear() 143 | } 144 | 145 | override def setupSim(context: Simulator#Context) = { 146 | context.memorySim.addSingleWordMappedIO( 147 | addr, 148 | new MappedIODevice with PerByte { 149 | override def writeByte(addr: Long, data: Byte) = { 150 | val newChar = data.toChar 151 | if (newChar == '\n') { 152 | printBuffer() 153 | } else { 154 | buffer += newChar 155 | } 156 | } 157 | } 158 | ) 159 | 160 | onSimEnd { 161 | if (buffer.nonEmpty) { 162 | printBuffer() 163 | } 164 | } 165 | } 166 | } 167 | 168 | def printLed(value: Int) = (0 until 16) 169 | .map { i => 170 | if ((value & (1 << i)) != 0) 171 | Console.RED + '*' + Console.RESET 172 | else 173 | Console.WHITE + 'o' + Console.RESET 174 | } 175 | .mkString(" ") 176 | 177 | def printLedRg(value: Int) = value & 3 match { 178 | case 0 => Console.WHITE + '_' + Console.RESET 179 | case 1 => Console.GREEN + 'G' + Console.RESET 180 | case 2 => Console.RED + 'R' + Console.RESET 181 | case 3 => Console.YELLOW + 'X' + Console.RESET 182 | } 183 | 184 | def printNum(value: Int) = f"$value%08x" 185 | } 186 | -------------------------------------------------------------------------------- /src/test/scala/cpu/display/package.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import spinal.core.sim._ 4 | 5 | package object display { 6 | case class DisplayerConfig( 7 | throttlePeriod: Option[Int] 8 | ) 9 | 10 | abstract class DisplayerPlugin(config: DisplayerConfig) extends Simulator.Plugin { 11 | private var needRepaint = true 12 | 13 | def triggerRepaint() = { needRepaint = true } 14 | 15 | def paint(): Unit 16 | 17 | override def setupSim(context: Simulator#Context) = fork { 18 | onSimEnd { 19 | paint() 20 | } 21 | 22 | while (true) { 23 | waitUntil(needRepaint) 24 | needRepaint = false 25 | paint() 26 | 27 | for (p <- config.throttlePeriod) { 28 | context.sysClockDomain.waitSampling(p) 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/cpu/funcTest/FuncTestLowLatency.scala: -------------------------------------------------------------------------------- 1 | package cpu.funcTest 2 | 3 | import cpu._ 4 | import spinal.lib.bus.amba4.axi.sim.AxiMemorySimConfig 5 | 6 | import java.io.File 7 | import scala.util.{Failure, Success} 8 | 9 | object FuncTestLowLatency { 10 | def main(args: Array[String]): Unit = { 11 | val instRamData = COEParse(new File("official/func_test/soft/func/obj/inst_ram.coe")) match { 12 | case Success(d) => d 13 | case Failure(exception) => 14 | println(exception.getMessage) 15 | throw exception 16 | } 17 | 18 | val simulator = Simulator( 19 | Simulator.Config( 20 | mem = AxiMemorySimConfig( 21 | maxOutstandingReads = 4, 22 | maxOutstandingWrites = 4, 23 | readResponseDelay = 10, 24 | writeResponseDelay = 10, 25 | interruptProbability = 0, 26 | interruptMaxDelay = 0 27 | ), 28 | initSections = Seq(Simulator.MemSection(0x1fc00000, instRamData)), 29 | cpuClockPeriod = 10 30 | ) 31 | ) 32 | 33 | funcTest.ConfRegs().addTo(simulator) 34 | 35 | simulator 36 | .addPlugin(PCBroadcastPlugin(period = 10000)) 37 | .addPlugin(TimeoutPlugin(timeout = 20000000)) 38 | .addPlugin(TerminatorPlugin(0xbfc00100L)) 39 | .addPlugin(WriteBackProducerPlugin()) 40 | .addPlugin( 41 | WriteBackComparerPlugin(new File("official/func_test/golden_trace.txt")) 42 | ) 43 | .onSetupSim { context => 44 | context.mem.getElseAllocPage(0) 45 | } 46 | .run(new SimulationSoc, workspaceName = "FuncTestLowLatency") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/scala/cpu/funcTest/FuncTestNormal.scala: -------------------------------------------------------------------------------- 1 | package cpu.funcTest 2 | 3 | import cpu._ 4 | import spinal.lib.bus.amba4.axi.sim.AxiMemorySimConfig 5 | 6 | import java.io.File 7 | import scala.util.{Failure, Success} 8 | 9 | object FuncTestNormal { 10 | def main(args: Array[String]): Unit = { 11 | val instRamData = COEParse(new File("official/func_test/soft/func/obj/inst_ram.coe")) match { 12 | case Success(d) => d 13 | case Failure(exception) => 14 | println(exception.getMessage) 15 | throw exception 16 | } 17 | 18 | val simulator = Simulator( 19 | Simulator.Config( 20 | mem = AxiMemorySimConfig( 21 | maxOutstandingReads = 4, 22 | maxOutstandingWrites = 4, 23 | readResponseDelay = 200, 24 | writeResponseDelay = 30, 25 | interruptProbability = 10, 26 | interruptMaxDelay = 20 27 | ), 28 | initSections = Seq(Simulator.MemSection(0x1fc00000, instRamData)), 29 | cpuClockPeriod = 10 30 | ) 31 | ) 32 | 33 | funcTest.ConfRegs().addTo(simulator) 34 | 35 | simulator 36 | .addPlugin(PCBroadcastPlugin(period = 10000)) 37 | .addPlugin(TimeoutPlugin(timeout = 20000000)) 38 | .addPlugin(TerminatorPlugin(0xbfc00100L)) 39 | .addPlugin(WriteBackProducerPlugin()) 40 | .addPlugin( 41 | WriteBackComparerPlugin(new File("official/func_test/golden_trace.txt")) 42 | ) 43 | .onSetupSim { context => 44 | context.mem.getElseAllocPage(0) 45 | } 46 | .run(new SimulationSoc, workspaceName = "FuncTestNormal") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/scala/cpu/funcTest/PerfTest.scala: -------------------------------------------------------------------------------- 1 | package cpu.funcTest 2 | 3 | import cpu._ 4 | import spinal.lib.bus.amba4.axi.sim.AxiMemorySimConfig 5 | 6 | import java.io.File 7 | import scala.util.{Failure, Success} 8 | 9 | object PerfTest { 10 | def main(args: Array[String]): Unit = { 11 | val cpuClockPeriod = 10 12 | val sysClockPeriod = 10 13 | 14 | val tests = Seq( 15 | "bitcount", 16 | "bubble_sort", 17 | "coremark", 18 | "crc32", 19 | "dhrystone", 20 | "quick_sort", 21 | "select_sort", 22 | "sha", 23 | "stream_copy", 24 | "stringsearch" 25 | ) 26 | 27 | val socCompiled = 28 | Simulator.compile(new SimulationSoc, workspaceName = "PerfTest", withWave = false) 29 | 30 | val baseConfig = Simulator.Config( 31 | mem = AxiMemorySimConfig( 32 | maxOutstandingReads = 4, 33 | maxOutstandingWrites = 4, 34 | readResponseDelay = 250, 35 | writeResponseDelay = 30 36 | ), 37 | cpuClockPeriod = cpuClockPeriod, 38 | sysClockPeriod = sysClockPeriod 39 | ) 40 | 41 | val instRamData = COEParse( 42 | new File(s"official/perf_test/soft/perf_func/obj/allbench/axi_ram.coe") 43 | ) match { 44 | case Success(d) => d 45 | case Failure(exception) => 46 | println(exception.getMessage) 47 | throw exception 48 | } 49 | 50 | val results = for ((test, index) <- tests.zipWithIndex) yield { 51 | println("=" * 80) 52 | 53 | val simulator = Simulator( 54 | baseConfig.copy(initSections = Seq(Simulator.MemSection(0x1fc00000, instRamData))) 55 | ) 56 | 57 | val confRegs = funcTest.ConfRegs() 58 | confRegs.addTo( 59 | simulator, 60 | displayerConfig = Some(display.DisplayerConfig(throttlePeriod = Some(10000))) 61 | ) 62 | 63 | val testCase = index + 1 64 | // 开关低 4 位为 ~testCase,高 4 位为 0 65 | for (i <- 0 until 4) { 66 | confRegs.switch.data(i) = (testCase & (1 << i)) == 0 67 | } 68 | 69 | simulator 70 | .addPlugin(TimeoutPlugin(timeout = 20000000)) 71 | .addPlugin(TerminatorPlugin(0xbfc00100L)) 72 | .onSetupSim { context => 73 | context.mem.getElseAllocPage(0) 74 | } 75 | .run(socCompiled, name = test) 76 | 77 | (confRegs.led.value == 0xffff, confRegs.cr(1).value) 78 | } 79 | 80 | val gs132Counts = Seq(0x13cf7fa, 0x7bdd47e, 0x10ce6772, 0xaa1aa5c, 0x1fc00d8, 0x719615a, 81 | 0x6e0009a, 0x74b8b20, 0x853b00, 0x50a1bcc) 82 | 83 | for (((test, gs132Count), (pass, count)) <- (tests zip gs132Counts) zip results) { 84 | val passOrFail = if (pass) "PASS" else "FAIL" 85 | println(f"$test%12s: $passOrFail / $count%8x ($gs132Count%8x)") 86 | } 87 | 88 | val ratios = (results zip gs132Counts) 89 | .map { case ((_, count), gs132Count) => 90 | if (count != 0) 91 | gs132Count.toDouble / count.toDouble 92 | else 93 | 0.1 94 | } 95 | val formulaScore = math.pow(ratios.foldLeft(1.0) { _ * _ }, 1.0 / tests.length) 96 | val loopTimes = 10 97 | val estimatedScore = formulaScore / loopTimes 98 | 99 | val passAll = results forall { _._1 } 100 | 101 | println(if (passAll) "All tests passed!" else "Some test(s) failed!") 102 | println(f"Estimated score: ${estimatedScore}%3.3f") 103 | println(f"CPU clock period: ${cpuClockPeriod}%3dns") 104 | println(f"SOC clock period: ${sysClockPeriod}%3dns") 105 | println() 106 | println("NOTE: Each test is only run once under simulation. On board, each test is run 10") 107 | println("NOTE: times. The score shown above is already divided by 10 to estimate this") 108 | println("NOTE: behaviour.") 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/scala/cpu/funcTest/package.scala: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import cpu.confreg._ 4 | import cpu.display._ 5 | 6 | package object funcTest { 7 | val baseAddr = 0x1faf0000 8 | 9 | case class ConfRegs() { 10 | val cr = (0 until 8) map { i => Register(baseAddr | 0x8000 | (4 * i)) } 11 | 12 | val switch = Switch(baseAddr | 0xf020, Some(baseAddr | 0xf02c)) 13 | val ioSimu = IOSimu(baseAddr | 0xffec) 14 | val openTrace = OpenTrace(baseAddr | 0xfff8) 15 | val simuFlag = Constant(baseAddr | 0xfff4, value = 1) 16 | 17 | val timer = Timer(baseAddr | 0xe000) 18 | 19 | val led = RepaintingRegister(baseAddr | 0xf000) 20 | val ledRg0 = RepaintingRegister(baseAddr | 0xf004) 21 | val ledRg1 = RepaintingRegister(baseAddr | 0xf008) 22 | val num = RepaintingRegister(baseAddr | 0xf010) 23 | 24 | val textUart = TextUART(baseAddr | 0xfff0) 25 | 26 | def addTo(simulator: Simulator, displayerConfig: Option[DisplayerConfig] = None) = { 27 | for (reg <- cr) { 28 | simulator.addPlugin(reg) 29 | } 30 | 31 | simulator 32 | .addPlugin(switch) 33 | .addPlugin(ioSimu) 34 | .addPlugin(openTrace) 35 | .addPlugin(simuFlag) 36 | .addPlugin(timer) 37 | .addPlugin(textUart) 38 | 39 | for (config <- displayerConfig) { 40 | simulator 41 | .addPlugin(led) 42 | .addPlugin(ledRg0) 43 | .addPlugin(ledRg1) 44 | .addPlugin(num) 45 | .addPlugin(new DisplayerPlugin(config) { 46 | override def paint() = { 47 | val ledString = confreg.printLed(led.value) 48 | val ledRg0String = confreg.printLedRg(ledRg0.value) 49 | val ledRg1String = confreg.printLedRg(ledRg1.value) 50 | val numString = confreg.printNum(num.value) 51 | 52 | println(s"| $ledString | $ledRg0String $ledRg1String | $numString |") 53 | } 54 | }) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/scala/lib/Decoder.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | 6 | object Fruits extends SpinalEnum { 7 | val Apple, Pear, Orange = newElement() 8 | } 9 | 10 | object DecoderTest { 11 | def main(args: Array[String]) = { 12 | val k1 = Key(Bool).setName("k1") 13 | val k2 = Key(UInt(2 bits)).setName("k2") 14 | val k3 = Key(Fruits()).setName("k3") 15 | 16 | SimConfig 17 | .compile(new Decoder(4 bits) { 18 | default(k3) to Fruits.Orange 19 | default(k1) to True 20 | 21 | on(M"01--") { 22 | set(k1) to False 23 | set(k2) to U(3, 2 bits) 24 | } 25 | 26 | on(M"1--0") { 27 | set(k2) to U(2, 2 bits) 28 | set(k3) to Fruits.Pear 29 | } 30 | 31 | on(M"0---") { 32 | set(k3) to Fruits.Apple 33 | } 34 | on(M"-0-1") { 35 | set(k2) to U(1, 2 bits) 36 | } 37 | }) 38 | .doSim { dut => 39 | for (i <- 0 until 16) { 40 | dut.input #= i 41 | sleep(1) 42 | val v1 = dut.output(k1).toBoolean 43 | val v2 = dut.output(k2).toInt 44 | val v3 = dut.output(k3).toEnum 45 | assert(v1 == ((i & 12) != 4)) 46 | if ((i & 8) == 0) { 47 | assert(v3 == Fruits.Apple) 48 | } else if ((i & 1) == 0) { 49 | assert(v3 == Fruits.Pear) 50 | } else { 51 | assert(v3 == Fruits.Orange) 52 | } 53 | if (!v1) { 54 | assert(v2 == 3) 55 | } else if (v3 == Fruits.Pear) { 56 | assert(v2 == 2) 57 | } else if ((i & 5) == 1) { 58 | assert(v2 == 1) 59 | } else { 60 | assert(v2 == 0) 61 | } 62 | } 63 | } 64 | 65 | () 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/scala/ram/AXIRam.scala: -------------------------------------------------------------------------------- 1 | package ram 2 | 3 | import spinal.core._ 4 | import spinal.lib.bus.amba4.axi.{Axi4, Axi4Config} 5 | import spinal.lib._ 6 | 7 | case class AXIRamConfig( 8 | dataWidth: Int = 32, 9 | addrWidth: Int = 16, 10 | idWidth: Int = 8, 11 | pipelineOutput: Int = 0 12 | ) { 13 | def strbWidth: Int = dataWidth / 8 14 | } 15 | 16 | class axi_ram(config: AXIRamConfig) extends BlackBox { 17 | val generic = new Generic { 18 | val DATA_WIDTH = config.dataWidth 19 | val ADDR_WIDTH = config.addrWidth 20 | val STRB_WIDTH = config.strbWidth 21 | val ID_WIDTH = config.idWidth 22 | val PIPELINE_OUTPUT = config.pipelineOutput 23 | } 24 | 25 | val io = new Bundle { 26 | val axi = slave( 27 | new Axi4( 28 | Axi4Config( 29 | addressWidth = config.addrWidth, 30 | dataWidth = config.dataWidth, 31 | idWidth = config.idWidth, 32 | useRegion = false, 33 | useQos = false 34 | ) 35 | ) 36 | ) 37 | val clk = in Bool () 38 | val rst = in Bool () 39 | } 40 | 41 | noIoPrefix() 42 | mapClockDomain(clock = io.clk, reset = io.rst) 43 | 44 | private def renameIO(): Unit = { 45 | io.flatten.foreach(bt => { 46 | val pattern = "(.*?)_(ar|r|w|aw|b)_(.*?)".r 47 | var target = pattern.replaceFirstIn(bt.getName(), "s_$1_$2$3") 48 | target = "payload_".r.replaceAllIn(target, "") 49 | // println(s"origin = ${bt.getName()}, target = $target") 50 | bt.setName(target) 51 | }) 52 | } 53 | 54 | addPrePopTask(() => renameIO()) 55 | 56 | addRTLPath("./rtl/axi_ram.v") 57 | } 58 | -------------------------------------------------------------------------------- /tests/func/Makefile: -------------------------------------------------------------------------------- 1 | TOPDIR=$(shell pwd) 2 | 3 | # export LD_PRELOAD = 4 | CFLAGS := -D_KERNEL -fno-builtin -mips1 -DMEMSTART=0x80000000 -DMEMSIZE=0x04000 -DCPU_COUNT_PER_US=1000 -I $(TOPDIR)/include 5 | CFLAGS += -fno-reorder-blocks -fno-reorder-functions 6 | 7 | # # export TOPDIR AR CFLAGS 8 | CROSS_COMPILE ?= mipsel-linux- 9 | 10 | TESTS = $(patsubst tests/%/,%,$(wildcard tests/*/)) 11 | TEST_MAIN_TARGETS = $(foreach test,$(TESTS),test-$(test)) 12 | INCLUDES = $(wildcard include/*) 13 | 14 | TARGETS_PER_TEST = code.elf code.bin code.data test.s inst_ram.coe data_ram.coe inst_ram.mif data_ram.mif axi_ram.mif 15 | 16 | listsrcs = $(wildcard tests/$(1)/$(2)) 17 | listobjs = $(patsubst %.S,%.o,$(call listsrcs,$(1),*.S)) \ 18 | $(patsubst %.c,%.o,$(call listsrcs,$(1),*.c)) 19 | listtargets = $(foreach target,$(TARGETS_PER_TEST),tests/$(1)/obj/$(target)) 20 | 21 | # List all real files to be generated 22 | ALL_OBJS = $(foreach test,$(TESTS),$(call listobjs,$(test))) 23 | ALL_TARGETS = $(foreach test,$(TESTS),$(call listtargets,$(test))) 24 | ALL_FILES_TO_GENERATE = $(ALL_OBJS) $(ALL_TARGETS) 25 | 26 | .PHONY: all clean reset help $(TEST_MAIN_TARGETS) 27 | 28 | all: $(TEST_MAIN_TARGETS) $(ALL_FILES_TO_GENERATE) 29 | 30 | # Top rule for a test 31 | test-%: $(call listtargets,%) 32 | @ 33 | 34 | %/test.s: %/code.elf 35 | ${CROSS_COMPILE}objdump -alD $< > $@ 36 | 37 | %/axi_ram.mif: %/inst_ram.mif 38 | cp $< $@ 39 | 40 | # Feed file names with correct path into `convert`. 41 | %/inst_ram.coe: convert %/code.bin %/code.data 42 | ./convert \ 43 | $(foreach target,code.bin code.data inst_ram.coe data_ram.coe inst_ram.mif data_ram.mif,$*/$(target)) 44 | 45 | # The following three are generated by `convert` above. 46 | %/data_ram.coe: %/inst_ram.coe 47 | @ 48 | 49 | %/inst_ram.mif: %/inst_ram.coe 50 | @ 51 | 52 | %/data_ram.mif: %/inst_ram.coe 53 | @ 54 | 55 | %/code.bin: %/code.elf 56 | ${CROSS_COMPILE}objcopy -O binary -j .text $< $@ 57 | 58 | %/code.data: %/code.elf 59 | ${CROSS_COMPILE}objcopy -O binary -j .data $< $@ 60 | 61 | .SECONDEXPANSION: 62 | tests/%/obj/code.elf: bin.lds $$(call listobjs,$$*) 63 | mkdir -p tests/$*/obj 64 | ${CROSS_COMPILE}ld -g -T bin.lds -o $@ $(wordlist 2,$(words $^),$^) 65 | 66 | bin.lds: bin.lds.S 67 | ${CROSS_COMPILE}gcc -E -P -Umips -D_LOADER -U_MAIN $(CFLAGS) $< -o $@ 68 | 69 | convert: convert.c 70 | gcc $(ALIGNED) -std=c99 -o convert convert.c 71 | 72 | clean: 73 | rm -rf $(foreach test,$(TESTS),tests/$(test)/obj) 74 | rm -f $(foreach test,$(TESTS),tests/$(test)/*.o) 75 | 76 | reset: 77 | make clean 78 | rm -f bin.lds convert 79 | 80 | print: 81 | @echo $(TESTS) 82 | @echo $(TEST_MAIN_TARGETS) 83 | @echo $(INCLUDES) 84 | @echo $(ALL_OBJS) 85 | @echo $(ALL_TARGETS) 86 | 87 | help: 88 | @echo "#########################################################################" 89 | @echo "### help for compiling simple_func" 90 | @echo "#########################################################################" 91 | @echo "### options:" 92 | @echo "### make : get compiled result, saved under obj/ under each test 93 | @echo "### make clean: remove tests/**/*.o and tests/**/obj/ 94 | @echo "### make reset: "make clean" and remove convert, bin.lds" 95 | @echo "### make help : show help information" 96 | @echo "#########################################################################" 97 | 98 | -include rules.make 99 | -------------------------------------------------------------------------------- /tests/func/Readme_first.txt: -------------------------------------------------------------------------------- 1 | 尝试性的可以将自己写的 C 和汇编编译成内存初始化文件的框架。 2 | 3 | ### 测试组织方式 4 | 5 | tests 下的每个子目录对应一个测试。每个测试下可以写任意多 .S 或 .c,最后会被编译到一起。暂时按照官方的功能测试的规定,程序运行到 PC = 0xbfc00100 时结束(可在 .org 0x100 处死循环)。 6 | 7 | ### 编译 8 | 9 | 用 make 编译,会为每个测试在其子目录下的 obj 子目录生成二进制文件、inst_ram.coe 等内存初始化文件和可用来检查编译正确性的 test.s。 10 | 11 | 举例:现在 tests/or 是一个测试,目录下有 or.S。用 make 编译,会得到 tests/or/or.O(or.O 名字来自 or.S 而非目录名),并在 tests/or/obj 下生成 code.elf、inst_ram.coe、test.s 等等。 12 | 13 | ### 运行 14 | 15 | 尚待自动化。 16 | 17 | 在服务器上 vivado 中分别打开 func_test 下 cpu132_gettrace 和 myCPU 的 .xpr。展开左上角 sources 方格中 CPU 模块,找到 inst_ram 或 axi_ram 的 IP 并双击,在弹出窗口的 Other Options 页修改 coe file 到编译出来的 inst_ram.coe 文件(*)。注意确认后下方 console 是否报错,如果有错会修改失败。之后 Run behavioral simulation 即可。其中 cpu132_gettrace 负责生成正确的 golden_trace,myCPU 会与其比对,所以应该先在 cpu132 上跑。 18 | 19 | (*):暂时没有考虑怎么把 data ram 也加入。粗看官方的功能测试直接没管 data ram,把 inst_ram.coe 复制成 axi_ram.coe。 20 | -------------------------------------------------------------------------------- /tests/func/bin.lds.S: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(mips) 2 | ENTRY(_start) 3 | SECTIONS 4 | { 5 | /* Read-only sections, merged into text segment: */ 6 | . = 0xbfc00000; 7 | .text : 8 | { 9 | _ftext = . ; 10 | *(.text) 11 | *(.rodata*) 12 | *(.reginfo) 13 | *(.init) 14 | *(.stub) 15 | /* .gnu.warning sections are handled specially by elf32.em. */ 16 | *(.gnu.warning) 17 | rodata_end = .; 18 | } =0 19 | _etext = .; 20 | PROVIDE (etext = .); 21 | .fini : { *(.fini) } =0 22 | . = MEMSTART; 23 | .data : AT(rodata_end) 24 | { 25 | _fdata = . ; 26 | _stack = _fdata + MEMSIZE -32; 27 | *(.data) 28 | *(.data*) 29 | __CTOR_LIST__ = .; 30 | LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) 31 | *(.ctors) 32 | LONG(0) 33 | __CTOR_END__ = .; 34 | __DTOR_LIST__ = .; 35 | LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) 36 | *(.dtors) 37 | LONG(0) 38 | __DTOR_END__ = .; 39 | _gp = ALIGN(16) + 0x7ff0; 40 | *(.got.plt) *(.got) 41 | /* We want the small data sections together, so single-instruction offsets 42 | can access them all, and initialized data all before uninitialized, so 43 | we can shorten the on-disk segment size. */ 44 | *(.sdata) 45 | *(.lit8) 46 | *(.lit4) 47 | } 48 | _edata = .; 49 | PROVIDE (edata = .); 50 | 51 | data_size = SIZEOF(.data); 52 | data_load_start = LOADADDR(.data); 53 | __bss_start = .; 54 | _fbss = .; 55 | .sbss : { *(.sbss) *(.scommon) } 56 | .bss : 57 | { 58 | *(.dynbss) 59 | *(.bss) 60 | *(COMMON) 61 | } 62 | . = ALIGN(8); 63 | _end = . ; 64 | PROVIDE (end = .); 65 | 66 | . = ALIGN(32); 67 | 68 | .bigdata : { *(.bigdata) } 69 | 70 | . = ALIGN(256); 71 | _heap = . ; 72 | /* These are needed for ELF backends which have not yet been 73 | converted to the new style linker. */ 74 | .stab 0 : { *(.stab) } 75 | .stabstr 0 : { *(.stabstr) } 76 | /* DWARF debug sections. 77 | Symbols in the .debug DWARF section are relative to the beginning of the 78 | section so we begin .debug at 0. It's not clear yet what needs to happen 79 | for the others. */ 80 | .debug 0 : { *(.debug) } 81 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 82 | .debug_aranges 0 : { *(.debug_aranges) } 83 | .debug_pubnames 0 : { *(.debug_pubnames) } 84 | .debug_sfnames 0 : { *(.debug_sfnames) } 85 | .line 0 : { *(.line) } 86 | /* These must appear regardless of . */ 87 | .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } 88 | .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } 89 | } 90 | -------------------------------------------------------------------------------- /tests/func/convert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void binary_out(FILE *out, unsigned char *mem) 5 | { 6 | char tmp; 7 | unsigned char num[8]; 8 | num[0] = 1; 9 | num[1] = 2; 10 | num[2] = 4; 11 | num[3] = 8; 12 | num[4] = 16; 13 | num[5] = 32; 14 | num[6] = 64; 15 | num[7] = 128; 16 | for (int i = 3; i >= 0; i--) 17 | { 18 | for (int j = 7; j >= 0; j--) 19 | { 20 | if ((mem[i] & num[j]) != 0) 21 | tmp = '1'; 22 | else 23 | tmp = '0'; 24 | fprintf(out, "%c", tmp); 25 | } 26 | } 27 | fprintf(out, "\n"); 28 | return; 29 | } 30 | 31 | int main(int argc, char **argv) 32 | { 33 | FILE *in; 34 | FILE *out; 35 | 36 | int i, j, k; 37 | unsigned char mem[32]; 38 | // file names 39 | char *bin_fn, *data_fn, *inst_coe_fn, *data_coe_fn, *inst_mif_fn, *data_mif_fn; 40 | 41 | if (argc != 7) 42 | { 43 | fprintf(stderr, "Usage: convert \n"); 44 | exit(1); 45 | } 46 | 47 | bin_fn = argv[1]; 48 | data_fn = argv[2]; 49 | inst_coe_fn = argv[3]; 50 | data_coe_fn = argv[4]; 51 | inst_mif_fn = argv[5]; 52 | data_mif_fn = argv[6]; 53 | 54 | in = fopen(bin_fn, "rb"); 55 | out = fopen(inst_coe_fn, "w"); 56 | 57 | fprintf(out, "memory_initialization_radix = 16;\n"); 58 | fprintf(out, "memory_initialization_vector =\n"); 59 | while (!feof(in)) 60 | { 61 | if (fread(mem, 1, 4, in) != 4) 62 | { 63 | fprintf(out, "%02x%02x%02x%02x\n", mem[3], mem[2], mem[1], mem[0]); 64 | break; 65 | } 66 | fprintf(out, "%02x%02x%02x%02x\n", mem[3], mem[2], mem[1], mem[0]); 67 | } 68 | fclose(in); 69 | fclose(out); 70 | 71 | in = fopen(data_fn, "rb"); 72 | out = fopen(data_coe_fn, "w"); 73 | 74 | fprintf(out, "memory_initialization_radix = 16;\n"); 75 | fprintf(out, "memory_initialization_vector =\n"); 76 | while (!feof(in)) 77 | { 78 | if (fread(mem, 1, 4, in) != 4) 79 | { 80 | fprintf(out, "%02x%02x%02x%02x\n", mem[3], mem[2], mem[1], mem[0]); 81 | break; 82 | } 83 | fprintf(out, "%02x%02x%02x%02x\n", mem[3], mem[2], mem[1], mem[0]); 84 | } 85 | fclose(in); 86 | fclose(out); 87 | 88 | in = fopen(data_fn, "rb"); 89 | out = fopen(data_mif_fn, "w"); 90 | 91 | while (!feof(in)) 92 | { 93 | if (fread(mem, 1, 4, in) != 4) 94 | { 95 | binary_out(out, mem); 96 | break; 97 | } 98 | binary_out(out, mem); 99 | } 100 | fclose(in); 101 | fclose(out); 102 | 103 | in = fopen(bin_fn, "rb"); 104 | out = fopen(inst_mif_fn, "w"); 105 | 106 | while (!feof(in)) 107 | { 108 | if (fread(mem, 1, 4, in) != 4) 109 | { 110 | binary_out(out, mem); 111 | break; 112 | } 113 | binary_out(out, mem); 114 | } 115 | fclose(in); 116 | fclose(out); 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /tests/func/include/asm.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: asm.h,v 1.2 1998/03/16 09:03:02 pefo Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1992, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Ralph Campbell. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. All advertising materials mentioning features or use of this software 19 | * must display the following acknowledgement: 20 | * This product includes software developed by the University of 21 | * California, Berkeley and its contributors. 22 | * 4. Neither the name of the University nor the names of its contributors 23 | * may be used to endorse or promote products derived from this software 24 | * without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 | * SUCH DAMAGE. 37 | * 38 | * Copyright (C) 1989 Digital Equipment Corporation. 39 | * Permission to use, copy, modify, and distribute this software and 40 | * its documentation for any purpose and without fee is hereby granted, 41 | * provided that the above copyright notice appears in all copies. 42 | * Digital Equipment Corporation makes no representations about the 43 | * suitability of this software for any purpose. It is provided "as is" 44 | * without express or implied warranty. 45 | */ 46 | 47 | #ifndef _MIPS_ASM_H 48 | #define _MIPS_ASM_H 49 | 50 | #include 51 | 52 | #ifndef ABICALLS 53 | #define ABICALLS .abicalls 54 | #endif 55 | 56 | #if defined(ABICALLS) && !defined(_KERNEL) 57 | ABICALLS 58 | #endif 59 | 60 | #define RCSID(x) 61 | 62 | /* 63 | * Define how to access unaligned data word 64 | */ 65 | #if defined(__MIPSEL__) 66 | #define LWLO lwl 67 | #define LWHI lwr 68 | #define SWLO swl 69 | #define SWHI swr 70 | #else 71 | #if defined(__MIPSEB__) 72 | #define LWLO lwr 73 | #define LWHI lwl 74 | #define SWLO swr 75 | #define SWHI swl 76 | #else 77 | #error "__MIPSEL__ or __MIPSEB__ must be defined" 78 | #endif 79 | #endif 80 | 81 | /* 82 | * Code for setting gp reg if abicalls are used. 83 | */ 84 | #if defined(ABICALLS) && !defined(_KERNEL) 85 | #define ABISETUP \ 86 | .set noreorder; \ 87 | .cpload t9; \ 88 | .set reorder; 89 | #else 90 | #define ABISETUP 91 | #endif 92 | 93 | /* 94 | * Define -pg profile entry code. 95 | */ 96 | #if defined(GPROF) || defined(PROF) 97 | #define MCOUNT \ 98 | subu sp, sp, 32; \ 99 | .cprestore 16; \ 100 | sw ra, 28(sp); \ 101 | sw gp, 24(sp); \ 102 | .set noat; \ 103 | .set noreorder; \ 104 | move AT, ra; \ 105 | jal _mcount; \ 106 | subu sp, sp, 8; \ 107 | lw ra, 28(sp); \ 108 | addu sp, sp, 32; \ 109 | .set reorder; \ 110 | .set at; 111 | #else 112 | #define MCOUNT 113 | #endif 114 | 115 | /* 116 | * LEAF(x) 117 | * 118 | * Declare a leaf routine. 119 | */ 120 | #define LEAF(x) \ 121 | .align 3; \ 122 | .globl x; \ 123 | .ent x, 0; \ 124 | x: ; \ 125 | .frame sp, 0, ra; \ 126 | ABISETUP \ 127 | MCOUNT 128 | 129 | #define ALEAF(x) \ 130 | .globl x; \ 131 | x: 132 | 133 | /* 134 | * NLEAF(x) 135 | * 136 | * Declare a non-profiled leaf routine. 137 | */ 138 | #define NLEAF(x) \ 139 | .align 3; \ 140 | .globl x; \ 141 | .ent x, 0; \ 142 | x: ; \ 143 | .frame sp, 0, ra; \ 144 | ABISETUP 145 | 146 | /* 147 | * NON_LEAF(x) 148 | * 149 | * Declare a non-leaf routine (a routine that makes other C calls). 150 | */ 151 | #define NON_LEAF(x, fsize, retpc) \ 152 | .align 3; \ 153 | .globl x; \ 154 | .ent x, 0; \ 155 | x: ; \ 156 | .frame sp, fsize, retpc; \ 157 | ABISETUP \ 158 | MCOUNT 159 | 160 | /* 161 | * NNON_LEAF(x) 162 | * 163 | * Declare a non-profiled non-leaf routine 164 | * (a routine that makes other C calls). 165 | */ 166 | #define NNON_LEAF(x, fsize, retpc) \ 167 | .align 3; \ 168 | .globl x; \ 169 | .ent x, 0; \ 170 | x: ; \ 171 | .frame sp, fsize, retpc \ 172 | ABISETUP 173 | 174 | /* 175 | * END(x) 176 | * 177 | * Mark end of a procedure. 178 | */ 179 | #define END(x) \ 180 | .end x 181 | 182 | /* 183 | * Macros to panic and printf from assembly language. 184 | */ 185 | #define PANIC(msg) \ 186 | la a0, 9f; \ 187 | jal panic; \ 188 | MSG(msg) 189 | 190 | #define PRINTF(msg) \ 191 | la a0, 9f; \ 192 | jal printf; \ 193 | MSG(msg) 194 | 195 | #define MSG(msg) \ 196 | .rdata; \ 197 | 9: .asciiz msg; \ 198 | .text 199 | 200 | #define ASMSTR(str) \ 201 | .asciiz str; \ 202 | .align 3 203 | 204 | #if (_MIPS_SZPTR == 32) 205 | #define PTR_ADD add 206 | #define PTR_ADDU addu 207 | #define PTR_ADDI addi 208 | #define PTR_ADDIU addiu 209 | #define PTR_SUB sub 210 | #define PTR_SUBU subu 211 | #define PTR_L lw 212 | #define PTR_S sw 213 | #define PTR_LA la 214 | #define PTR_LI li 215 | #define PTR_SLL sll 216 | #define PTR_SLLV sllv 217 | #define PTR_SRL srl 218 | #define PTR_SRLV srlv 219 | #define PTR_SRA sra 220 | #define PTR_SRAV srav 221 | 222 | #define PTR_SCALESHIFT 2 223 | 224 | #define PTR .word 225 | #define PTRSIZE 4 226 | #define PTRLOG 2 227 | #endif 228 | 229 | #if (_MIPS_SZPTR == 64) 230 | #define PTR_ADD dadd 231 | #define PTR_ADDU daddu 232 | #define PTR_ADDI daddi 233 | #define PTR_ADDIU daddiu 234 | #define PTR_SUB dsub 235 | #define PTR_SUBU dsubu 236 | #define PTR_L ld 237 | #define PTR_S sd 238 | #define PTR_LA dla 239 | #define PTR_LI dli 240 | #define PTR_SLL dsll 241 | #define PTR_SLLV dsllv 242 | #define PTR_SRL dsrl 243 | #define PTR_SRLV dsrlv 244 | #define PTR_SRA dsra 245 | #define PTR_SRAV dsrav 246 | 247 | #define PTR_SCALESHIFT 3 248 | 249 | #define PTR .dword 250 | #define PTRSIZE 8 251 | #define PTRLOG 3 252 | #endif 253 | 254 | #endif /* !_MIPS_ASM_H */ 255 | -------------------------------------------------------------------------------- /tests/func/include/cpu_cde.h: -------------------------------------------------------------------------------- 1 | //soc confreg 2 | #define CONFREG_NULL 0xbfaf8ffc 3 | 4 | #define CONFREG_CR0 0xbfaf8000 5 | #define CONFREG_CR1 0xbfaf8004 6 | #define CONFREG_CR2 0xbfaf8008 7 | #define CONFREG_CR3 0xbfaf800c 8 | #define CONFREG_CR4 0xbfaf8010 9 | #define CONFREG_CR5 0xbfaf8014 10 | #define CONFREG_CR6 0xbfaf8018 11 | #define CONFREG_CR7 0xbfaf801c 12 | 13 | #define IO_SIMU_ADDR 0xbfafffec 14 | #define UART_ADDR 0xbfaffff0 15 | #define SIMU_FLAG_ADDR 0xbfaffff4 16 | #define OPEN_TRACE_ADDR 0xbfaffff8 17 | #define NUM_MONITOR_ADDR 0xbfaffffc 18 | #define LED_ADDR 0xbfaff000 19 | #define LED_RG0_ADDR 0xbfaff004 20 | #define LED_RG1_ADDR 0xbfaff008 21 | #define NUM_ADDR 0xbfaff010 22 | #define SWITCH_ADDR 0xbfaff020 23 | #define BTN_KEY_ADDR 0xbfaff024 24 | #define BTN_STEP_ADDR 0xbfaff028 25 | #define SW_INTER_ADDR 0xbfaff02c //switch interleave 26 | #define TIMER_ADDR 0xbfafe000 27 | 28 | #define SOC_LED (* (volatile unsigned *) LED_ADDR ) 29 | #define SOC_LED_RG0 (* (volatile unsigned *) LED_RG0_ADDR ) 30 | #define SOC_LED_RG1 (* (volatile unsigned *) LED_RG1_ADDR ) 31 | #define SOC_NUM (* (volatile unsigned *) NUM_ADDR ) 32 | #define SOC_SWITCHE (* (volatile unsigned *) SWITCH_ADDR ) 33 | #define SOC_BTN_KEY (* (volatile unsigned *) BTN_KEY_ADDR ) 34 | #define SOC_BTN_STEP (* (volatile unsigned *) BTN_STEP_ADDR ) 35 | #define SOC_TIMER (* (volatile unsigned *) TIMER_ADDR ) 36 | //#define disable_trace_cmp *((volatile int *)OPEN_TRACE_ADDR) = 0; \ 37 | // *((volatile int *)CONFREG_NULL ) = 0; \ 38 | // *((volatile int *)CONFREG_NULL ) = 0 39 | //#define enable_trace_cmp *((volatile int *)OPEN_TRACE_ADDR) = 1; \ 40 | // *((volatile int *)CONFREG_NULL ) = 0; \ 41 | *((volatile int *)CONFREG_NULL ) = 0 42 | #define trace_cmp_flag (*((volatile int *)OPEN_TRACE_ADDR)) 43 | #define disable_trace_cmp asm volatile( \ 44 | ".set noreorder;" \ 45 | "lui $25,0xbfb0\n\t" \ 46 | "sw $0,-0x7004($25)\n\t" \ 47 | "sw $0,-0x7004($25)\n\t" \ 48 | "sw $0,-0x8($25)\n\t" \ 49 | "sw $0,-0x7004($25)\n\t" \ 50 | "sw $0,-0x7004($25)\n\t" \ 51 | "lw $0,-0x7004($25)\n\t" \ 52 | "lw $25,-0x8($25)\n\t" \ 53 | ".set reorder" \ 54 | :::"$25" \ 55 | ) 56 | #define disable_trace_cmp_s .set noreorder; \ 57 | lui k1,0xbfb0; \ 58 | sw $0,-0x7004(k1); \ 59 | sw $0,-0x7004(k1); \ 60 | sw $0,-0x8(k1); \ 61 | sw $0,-0x7004(k1); \ 62 | sw $0,-0x7004(k1); \ 63 | lw $0,-0x7004(k1); \ 64 | lw k1,-0x8(k1); \ 65 | .set reorder; \ 66 | 67 | #define disable_num_monitor_s .set noreorder; \ 68 | lui k1,0xbfb0; \ 69 | sw $0,-0x7004(k1); \ 70 | sw $0,-0x7004(k1); \ 71 | sw $0,-0x4(k1); \ 72 | sw $0,-0x7004(k1); \ 73 | sw $0,-0x7004(k1); \ 74 | lw $0,-0x7004(k1); \ 75 | lw k1,-0x4(k1); \ 76 | .set reorder; \ 77 | 78 | #define enable_trace_cmp asm volatile( \ 79 | ".set noreorder;" \ 80 | "lui $25,0xbfb0\n\t" \ 81 | "sw $0,-0x7004($25)\n\t" \ 82 | "sw $0,-0x7004($25)\n\t" \ 83 | "sw $25,-8($25)\n\t" \ 84 | "sw $0,-0x7004($25)\n\t" \ 85 | "sw $0,-0x7004($25)\n\t" \ 86 | "lw $0,-0x7004($25)\n\t" \ 87 | "lw $25,-0x8($25)\n\t" \ 88 | ".set reorder" \ 89 | :::"$25" \ 90 | ) 91 | 92 | #define enable_trace_cmp_s .set noreorder; \ 93 | lui k1,0xbfb0; \ 94 | sw $0,-0x7004(k1); \ 95 | sw $0,-0x7004(k1); \ 96 | sw k1,-8(k1); \ 97 | sw $0,-0x7004(k1); \ 98 | sw $0,-0x7004(k1); \ 99 | lw $0,-0x7004(k1); \ 100 | lw k1,-0x8(k1); \ 101 | .set reorder; \ 102 | 103 | #define write_confreg_cr(num,data) *((volatile int *)(CONFREG_CR0+4*num)) = data 104 | #define read_confreg_cr(num,data) data=*((volatile int *)(CONFREG_CR0+4*num)) 105 | #define NOP addu zero, zero, zero 106 | #define LI(reg, imm) \ 107 | li reg, imm 108 | -------------------------------------------------------------------------------- /tests/func/include/regdef.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: regdef.h,v 1.3 1999/01/27 04:46:06 imp Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1992, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Ralph Campbell. This file is derived from the MIPS RISC 9 | * Architecture book by Gerry Kane. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 3. All advertising materials mentioning features or use of this software 20 | * must display the following acknowledgement: 21 | * This product includes software developed by the University of 22 | * California, Berkeley and its contributors. 23 | * 4. Neither the name of the University nor the names of its contributors 24 | * may be used to endorse or promote products derived from this software 25 | * without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 | * SUCH DAMAGE. 38 | * 39 | * @(#)regdef.h 8.1 (Berkeley) 6/10/93 40 | */ 41 | #ifndef _MIPS_REGDEF_H_ 42 | #define _MIPS_REGDEF_H_ 43 | 44 | #define zero $0 /* always zero */ 45 | #define AT $at /* assembler temp */ 46 | #define v0 $2 /* return value */ 47 | #define v1 $3 48 | #define a0 $4 /* argument registers */ 49 | #define a1 $5 50 | #define a2 $6 51 | #define a3 $7 52 | #define t0 $8 /* temp registers (not saved across subroutine calls) */ 53 | #define t1 $9 54 | #define t2 $10 55 | #define t3 $11 56 | #define t4 $12 57 | #define t5 $13 58 | #define t6 $14 59 | #define t7 $15 60 | #define s0 $16 /* saved across subroutine calls (callee saved) */ 61 | #define s1 $17 62 | #define s2 $18 63 | #define s3 $19 64 | #define s4 $20 65 | #define s5 $21 66 | #define s6 $22 67 | #define s7 $23 68 | #define t8 $24 /* two more temp registers */ 69 | #define t9 $25 70 | #define k0 $26 /* kernel temporary */ 71 | #define k1 $27 72 | #define gp $28 /* global pointer */ 73 | #define sp $29 /* stack pointer */ 74 | #define s8 $30 /* one more callee saved */ 75 | #define ra $31 /* return address */ 76 | #define fp $30 77 | 78 | #define c0_index $0 79 | #define c0_random $1 80 | #define c0_entrylo0 $2 81 | #define c0_entrylo1 $3 82 | #define c0_conf $3 83 | #define c0_context $4 84 | #define c0_pagemask $5 85 | #define c0_wired $6 86 | #define c0_info $7 87 | #define c0_badvaddr $8 88 | #define c0_count $9 89 | #define c0_entryhi $10 90 | #define c0_compare $11 91 | #define c0_status $12 92 | #define c0_cause $13 93 | #define c0_epc $14 94 | #define c0_prid $15 95 | #define c0_config $16 96 | #define c0_lladdr $17 97 | #define c0_watchlo $18 98 | #define c0_watchhi $19 99 | #define c0_xcontext $20 100 | #define c0_framemask $21 101 | #define c0_diagnostic $22 102 | #define c0_debug $23 103 | #define c0_depc $24 104 | #define c0_performance $25 105 | #define c0_ecc $26 106 | #define c0_cacheerr $27 107 | #define c0_taglo $28 108 | #define c0_taghi $29 109 | #define c0_errorepc $30 110 | #define c0_desave $31 111 | 112 | #endif /* !_MIPS_REGDEF_H_ */ 113 | -------------------------------------------------------------------------------- /tests/func/rules.make: -------------------------------------------------------------------------------- 1 | .S.o: 2 | ${CROSS_COMPILE}gcc -O2 $(CFLAGS) -fno-pic -mno-abicalls -g -DGUEST -I include -I . -c -o $@ $< -nostdinc -nostdlib 3 | .c.o: 4 | ${CROSS_COMPILE}gcc -O2 $(CFLAGS) -fno-pic -mno-abicalls -g -DGUEST -I include -I . -c -o $@ $< -nostdinc -nostdlib 5 | .S.s: 6 | ${CROSS_COMPILE}gcc -O2 $(CFLAGS) -fno-pic -mno-abicalls -g -DGUEST -I include -I . -S -fverbose-asm -o $@ $< -nostdinc -nostdlib 7 | .c.s: 8 | ${CROSS_COMPILE}gcc -O2 $(CFLAGS) -fno-pic -mno-abicalls -g -DGUEST -I include -I . -S -fverbose-asm -o $@ $< -nostdinc -nostdlib 9 | -------------------------------------------------------------------------------- /tests/func/tests/and/and.S: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | .set noreorder 6 | .globl _start 7 | .globl start 8 | .globl __main 9 | _start: 10 | start: 11 | li t0, 1 12 | li t1, 2 13 | and t2, t0, t1 14 | b finish 15 | 16 | .org 0x100 17 | finish: 18 | b finish 19 | -------------------------------------------------------------------------------- /tests/func/tests/or/or.S: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | .set noreorder 6 | .globl _start 7 | .globl start 8 | .globl __main 9 | _start: 10 | start: 11 | li t0, 1 12 | li t1, 2 13 | or t2, t0, t1 14 | b finish 15 | 16 | .org 0x100 17 | finish: 18 | b finish 19 | -------------------------------------------------------------------------------- /utils/main.py: -------------------------------------------------------------------------------- 1 | from pyeda.inter import * 2 | 3 | if __name__ == '__main__': 4 | X = ttvars('x', 6) 5 | f = truthtable(X, '10010000111111110---------------11-111--00-0--------------------') 6 | print(espresso_tts(f)) 7 | f = truthtable(X, '-00-0000--------1-----------------------00-0--------------------') 8 | print(espresso_tts(f)) 9 | --------------------------------------------------------------------------------