├── .gitignore ├── .gitmodules ├── .scalafmt.conf ├── Makefile ├── README.md ├── bootrom ├── Makefile ├── bootrom.S ├── bootrom.img └── linker.ld ├── build.sbt ├── src └── main │ ├── resources │ └── vsrc │ │ └── VirtualRam256.v │ └── scala │ ├── ALU.scala │ ├── BPU.scala │ ├── Bus.scala │ ├── CSR.scala │ ├── CachePortProxy.scala │ ├── CherrySpringsConfig.scala │ ├── Constant.scala │ ├── Core.scala │ ├── DCache.scala │ ├── DataType.scala │ ├── Decode.scala │ ├── DecodeTable.scala │ ├── Elaborate.scala │ ├── FPGA.scala │ ├── HartIDAllocator.scala │ ├── ICache.scala │ ├── IFU.scala │ ├── LSU.scala │ ├── MDU.scala │ ├── MicroOp.scala │ ├── RegFile.scala │ ├── SRAM.scala │ ├── SimTop.scala │ ├── SoC.scala │ ├── TLB.scala │ ├── TLSilentClient.scala │ ├── TLTraceBuffer.scala │ ├── UART.scala │ ├── Utils.scala │ ├── VirtualRam.scala │ └── testchipip │ └── Serdes.scala └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | ### Project Specific stuff 2 | test_run_dir/* 3 | ### XilinxISE template 4 | # intermediate build files 5 | *.bgn 6 | *.bit 7 | *.bld 8 | *.cmd_log 9 | *.drc 10 | *.ll 11 | *.lso 12 | *.msd 13 | *.msk 14 | *.ncd 15 | *.ngc 16 | *.ngd 17 | *.ngr 18 | *.pad 19 | *.par 20 | *.pcf 21 | *.prj 22 | *.ptwx 23 | *.rbb 24 | *.rbd 25 | *.stx 26 | *.syr 27 | *.twr 28 | *.twx 29 | *.unroutes 30 | *.ut 31 | *.xpi 32 | *.xst 33 | *_bitgen.xwbt 34 | *_envsettings.html 35 | *_map.map 36 | *_map.mrp 37 | *_map.ngm 38 | *_map.xrpt 39 | *_ngdbuild.xrpt 40 | *_pad.csv 41 | *_pad.txt 42 | *_par.xrpt 43 | *_summary.html 44 | *_summary.xml 45 | *_usage.xml 46 | *_xst.xrpt 47 | 48 | # project-wide generated files 49 | *.gise 50 | par_usage_statistics.html 51 | usage_statistics_webtalk.html 52 | webtalk.log 53 | webtalk_pn.xml 54 | 55 | # generated folders 56 | iseconfig/ 57 | xlnx_auto_0_xdb/ 58 | xst/ 59 | _ngo/ 60 | _xmsgs/ 61 | ### Eclipse template 62 | *.pydevproject 63 | .metadata 64 | .gradle 65 | bin/ 66 | tmp/ 67 | *.tmp 68 | *.bak 69 | *.swp 70 | *~.nib 71 | local.properties 72 | .settings/ 73 | .loadpath 74 | 75 | # Eclipse Core 76 | .project 77 | 78 | # External tool builders 79 | .externalToolBuilders/ 80 | 81 | # Locally stored "Eclipse launch configurations" 82 | *.launch 83 | 84 | # CDT-specific 85 | .cproject 86 | 87 | # JDT-specific (Eclipse Java Development Tools) 88 | .classpath 89 | 90 | # Java annotation processor (APT) 91 | .factorypath 92 | 93 | # PDT-specific 94 | .buildpath 95 | 96 | # sbteclipse plugin 97 | .target 98 | 99 | # TeXlipse plugin 100 | .texlipse 101 | ### C template 102 | # Object files 103 | *.o 104 | *.ko 105 | *.obj 106 | *.elf 107 | 108 | # Precompiled Headers 109 | *.gch 110 | *.pch 111 | 112 | # Libraries 113 | *.lib 114 | *.a 115 | *.la 116 | *.lo 117 | 118 | # Shared objects (inc. Windows DLLs) 119 | *.dll 120 | *.so 121 | *.so.* 122 | *.dylib 123 | 124 | # Executables 125 | *.exe 126 | *.out 127 | *.app 128 | *.i*86 129 | *.x86_64 130 | *.hex 131 | 132 | # Debug files 133 | *.dSYM/ 134 | ### SBT template 135 | # Simple Build Tool 136 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 137 | 138 | target/ 139 | lib_managed/ 140 | src_managed/ 141 | project/boot/ 142 | .history 143 | .cache 144 | ### Emacs template 145 | # -*- mode: gitignore; -*- 146 | *~ 147 | \#*\# 148 | /.emacs.desktop 149 | /.emacs.desktop.lock 150 | *.elc 151 | auto-save-list 152 | tramp 153 | .\#* 154 | 155 | # Org-mode 156 | .org-id-locations 157 | *_archive 158 | 159 | # flymake-mode 160 | *_flymake.* 161 | 162 | # eshell files 163 | /eshell/history 164 | /eshell/lastdir 165 | 166 | # elpa packages 167 | /elpa/ 168 | 169 | # reftex files 170 | *.rel 171 | 172 | # AUCTeX auto folder 173 | /auto/ 174 | 175 | # cask packages 176 | .cask/ 177 | ### Vim template 178 | [._]*.s[a-w][a-z] 179 | [._]s[a-w][a-z] 180 | *.un~ 181 | Session.vim 182 | .netrwhist 183 | *~ 184 | ### JetBrains template 185 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 186 | 187 | *.iml 188 | 189 | ## Directory-based project format: 190 | .idea/ 191 | # if you remove the above rule, at least ignore the following: 192 | 193 | # User-specific stuff: 194 | # .idea/workspace.xml 195 | # .idea/tasks.xml 196 | # .idea/dictionaries 197 | 198 | # Sensitive or high-churn files: 199 | # .idea/dataSources.ids 200 | # .idea/dataSources.xml 201 | # .idea/sqlDataSources.xml 202 | # .idea/dynamic.xml 203 | # .idea/uiDesigner.xml 204 | 205 | # Gradle: 206 | # .idea/gradle.xml 207 | # .idea/libraries 208 | 209 | # Mongo Explorer plugin: 210 | # .idea/mongoSettings.xml 211 | 212 | ## File-based project format: 213 | *.ipr 214 | *.iws 215 | 216 | ## Plugin-specific files: 217 | 218 | # IntelliJ 219 | /out/ 220 | 221 | # mpeltonen/sbt-idea plugin 222 | .idea_modules/ 223 | 224 | # JIRA plugin 225 | atlassian-ide-plugin.xml 226 | 227 | # Crashlytics plugin (for Android Studio and IntelliJ) 228 | com_crashlytics_export_strings.xml 229 | crashlytics.properties 230 | crashlytics-build.properties 231 | ### C++ template 232 | # Compiled Object files 233 | *.slo 234 | *.lo 235 | *.o 236 | *.obj 237 | 238 | # Precompiled Headers 239 | *.gch 240 | *.pch 241 | 242 | # Compiled Dynamic libraries 243 | *.so 244 | *.dylib 245 | *.dll 246 | 247 | # Fortran module files 248 | *.mod 249 | 250 | # Compiled Static libraries 251 | *.lai 252 | *.la 253 | *.a 254 | *.lib 255 | 256 | # Executables 257 | *.exe 258 | *.out 259 | *.app 260 | ### OSX template 261 | .DS_Store 262 | .AppleDouble 263 | .LSOverride 264 | 265 | # Icon must end with two \r 266 | Icon 267 | 268 | # Thumbnails 269 | ._* 270 | 271 | # Files that might appear in the root of a volume 272 | .DocumentRevisions-V100 273 | .fseventsd 274 | .Spotlight-V100 275 | .TemporaryItems 276 | .Trashes 277 | .VolumeIcon.icns 278 | 279 | # Directories potentially created on remote AFP share 280 | .AppleDB 281 | .AppleDesktop 282 | Network Trash Folder 283 | Temporary Items 284 | .apdisk 285 | ### Xcode template 286 | # Xcode 287 | # 288 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 289 | 290 | ## Build generated 291 | build/ 292 | DerivedData 293 | 294 | ## Various settings 295 | *.pbxuser 296 | !default.pbxuser 297 | *.mode1v3 298 | !default.mode1v3 299 | *.mode2v3 300 | !default.mode2v3 301 | *.perspectivev3 302 | !default.perspectivev3 303 | xcuserdata 304 | 305 | ## Other 306 | *.xccheckout 307 | *.moved-aside 308 | *.xcuserstate 309 | ### Scala template 310 | *.class 311 | *.log 312 | /.bsp 313 | 314 | # sbt specific 315 | .cache 316 | .history 317 | .lib/ 318 | dist/* 319 | target/ 320 | lib_managed/ 321 | src_managed/ 322 | project/boot/ 323 | project/plugins/project/ 324 | 325 | # Scala-IDE specific 326 | .scala_dependencies 327 | .worksheet 328 | ### Java template 329 | *.class 330 | 331 | # Mobile Tools for Java (J2ME) 332 | .mtj.tmp/ 333 | 334 | # Package Files # 335 | *.jar 336 | *.war 337 | *.ear 338 | 339 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 340 | hs_err_pid* 341 | 342 | # Misc 343 | .vscode/ 344 | .metals/ 345 | .bloop/ 346 | project/ 347 | 348 | # DRAMsim3 349 | dramsim3.json 350 | dramsim3epoch.json 351 | dramsim3.txt 352 | 353 | # Build 354 | *.v 355 | !src/main/resources/vsrc/*.v 356 | *.f 357 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rocket-chip"] 2 | path = rocket-chip 3 | url = https://github.com/chipsalliance/rocket-chip.git 4 | [submodule "difftest"] 5 | path = difftest 6 | url = https://github.com/CherrySprings/difftest.git 7 | [submodule "rocket-chip-inclusive-cache"] 8 | path = rocket-chip-inclusive-cache 9 | url = https://github.com/chipsalliance/rocket-chip-inclusive-cache.git 10 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 2.6.4 2 | 3 | maxColumn = 120 4 | align = most 5 | continuationIndent.defnSite = 2 6 | assumeStandardLibraryStripMargin = true 7 | docstrings = ScalaDoc 8 | lineEndings = preserve 9 | includeCurlyBraceInSelectChains = false 10 | danglingParentheses = true 11 | 12 | align.tokens.add = [ 13 | { 14 | code = ":" 15 | }, 16 | { 17 | code = ":=" 18 | }, 19 | { 20 | code = "=" 21 | }, 22 | { 23 | code = "<>" 24 | }, 25 | { 26 | code = "->" 27 | } 28 | ] 29 | 30 | newlines.alwaysBeforeCurlyBraceLambdaParams = false 31 | newlines.alwaysBeforeMultilineDef = false 32 | newlines.implicitParamListModifierForce = [before] 33 | 34 | verticalMultiline.atDefnSite = true 35 | 36 | optIn.annotationNewlines = true 37 | 38 | rewrite.rules = [SortImports, PreferCurlyFors, AvoidInfix] 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR = ./build 2 | SRC = src/main/scala/$(wildcard *.scala) 3 | NUM_CORES ?= 2 4 | TARGET ?= fast-sim 5 | EMU_FLAGS = EMU_TRACE=1 EMU_CXX_EXTRA_FLAGS="-DFIRST_INST_ADDRESS=0x80000000" NUM_CORES=$(NUM_CORES) WITH_CHISELDB=0 WITH_CONSTANTIN=0 6 | 7 | verilog: $(SRC) 8 | @mkdir -p $(BUILD_DIR) 9 | sbt "run -n $(NUM_CORES) --target $(TARGET) -td $(BUILD_DIR)" 10 | @mv *.v $(BUILD_DIR) 11 | @mv *.f $(BUILD_DIR) 12 | @mv *.graphml $(BUILD_DIR) 13 | 14 | emu: verilog 15 | cd difftest && $(MAKE) $(EMU_FLAGS) emu -j 16 | 17 | emu2: 18 | cd difftest && $(MAKE) $(EMU_FLAGS) emu -j 19 | 20 | clean: 21 | -rm -rf $(BUILD_DIR) 22 | 23 | .PHONY: verilog clean 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CherrySprings 2 | -------------------------------------------------------------------------------- /bootrom/Makefile: -------------------------------------------------------------------------------- 1 | bootrom_img = bootrom.img 2 | 3 | CROSS_COMPILE ?= riscv64-unknown-elf- 4 | GCC = $(CROSS_COMPILE)gcc 5 | OBJCOPY = $(CROSS_COMPILE)objcopy 6 | 7 | all: $(bootrom_img) 8 | 9 | %.img: %.bin 10 | dd if=$< of=$@ bs=128 count=1 11 | 12 | %.bin: %.elf 13 | $(OBJCOPY) -O binary $< $@ 14 | 15 | %.elf: %.S linker.ld 16 | $(GCC) -Tlinker.ld $< -nostdlib -static -Wl,--no-gc-sections -o $@ 17 | -------------------------------------------------------------------------------- /bootrom/bootrom.S: -------------------------------------------------------------------------------- 1 | #define HART_ID_ALLOCATOR 0x00020000 2 | #define DRAM_BASE 0x80000000 3 | 4 | .section .text.start, "ax", @progbits 5 | .globl _start 6 | _start: 7 | li t0, HART_ID_ALLOCATOR 8 | ld t1, 0(t0) 9 | csrw mhartid, t1 10 | li s0, DRAM_BASE 11 | csrr a0, mhartid 12 | jr s0 13 | nop 14 | -------------------------------------------------------------------------------- /bootrom/bootrom.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CherrySprings/CherrySprings/638d37f8566220ec1b9b393c77771e268d1ac4fd/bootrom/bootrom.img -------------------------------------------------------------------------------- /bootrom/linker.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | ROM_BASE = 0x10000; /* ... but actually position independent */ 4 | 5 | . = ROM_BASE; 6 | .text.start : { *(.text.start) } 7 | } 8 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | val chiselVersion = "3.5.6" 2 | 3 | lazy val commonSettings = Seq( 4 | scalaVersion := "2.13.10", 5 | scalacOptions ++= Seq( 6 | "-deprecation", 7 | "-unchecked" 8 | ), 9 | libraryDependencies ++= Seq( 10 | "org.scala-lang" % "scala-reflect" % scalaVersion.value, 11 | "org.json4s" %% "json4s-jackson" % "4.0.6", 12 | "org.scalatest" %% "scalatest" % "3.2.14" % "test" 13 | ) 14 | ) 15 | 16 | lazy val chiselSettings = Seq( 17 | libraryDependencies ++= Seq( 18 | "edu.berkeley.cs" %% "chisel3" % chiselVersion, 19 | "edu.berkeley.cs" %% "chiseltest" % "0.5.5" 20 | ), 21 | addCompilerPlugin(("edu.berkeley.cs" % "chisel3-plugin" % chiselVersion).cross(CrossVersion.full)) 22 | ) 23 | 24 | lazy val cde = (project in file("rocket-chip/cde")) 25 | .settings(commonSettings) 26 | .settings( 27 | Compile / scalaSource := baseDirectory.value / "cde/src/chipsalliance/rocketchip" 28 | ) 29 | 30 | lazy val hardfloat = (project in file("rocket-chip/hardfloat")) 31 | .settings(commonSettings, chiselSettings) 32 | 33 | lazy val rocketMacros = (project in file("rocket-chip/macros")) 34 | .settings(commonSettings) 35 | 36 | lazy val rocketchip = (Project("rocket-chip", file("rocket-chip/src"))) 37 | .settings(commonSettings, chiselSettings) 38 | .settings( 39 | Compile / scalaSource := baseDirectory.value / "main" / "scala", 40 | Compile / resourceDirectory := baseDirectory.value / "main" / "resources" 41 | ) 42 | .dependsOn(cde) 43 | .dependsOn(hardfloat) 44 | .dependsOn(rocketMacros) 45 | 46 | lazy val difftest = (project in file("difftest")) 47 | .settings(commonSettings, chiselSettings) 48 | 49 | lazy val rocketchipInclusiveCache = (project in file("rocket-chip-inclusive-cache")) 50 | .settings(commonSettings, chiselSettings) 51 | .settings( 52 | Compile / scalaSource := baseDirectory.value / "design/craft/inclusivecache/src" 53 | ) 54 | .dependsOn(rocketchip) 55 | 56 | lazy val cherrysprings = (project in file(".")) 57 | .settings(commonSettings, chiselSettings) 58 | .dependsOn(rocketchip) 59 | .dependsOn(cde) 60 | .dependsOn(difftest) 61 | .dependsOn(rocketchipInclusiveCache) 62 | -------------------------------------------------------------------------------- /src/main/resources/vsrc/VirtualRam256.v: -------------------------------------------------------------------------------- 1 | module VirtualRam256 ( 2 | input clk, 3 | input en, 4 | input [63:0] addr, 5 | output [255:0] rdata, 6 | input [255:0] wdata, 7 | input [31:0] wmask, 8 | input wen 9 | ); 10 | 11 | `ifndef SYNTHESIS 12 | `ifdef DIFFTEST 13 | 14 | import "DPI-C" function void ram_write_helper 15 | ( 16 | input longint wIdx, 17 | input longint wdata, 18 | input longint wmask, 19 | input bit wen 20 | ); 21 | 22 | import "DPI-C" function longint ram_read_helper 23 | ( 24 | input bit en, 25 | input longint rIdx 26 | ); 27 | 28 | wire [255:0] wmask256; 29 | 30 | assign rdata = {ram_read_helper(en, {addr, 2'b11}), ram_read_helper(en, {addr, 2'b10}), 31 | ram_read_helper(en, {addr, 2'b01}), ram_read_helper(en, {addr, 2'b00})}; 32 | 33 | assign wmask256 = {{8{wmask[31]}}, {8{wmask[30]}}, {8{wmask[29]}}, {8{wmask[28]}}, 34 | {8{wmask[27]}}, {8{wmask[26]}}, {8{wmask[25]}}, {8{wmask[24]}}, 35 | {8{wmask[23]}}, {8{wmask[22]}}, {8{wmask[21]}}, {8{wmask[20]}}, 36 | {8{wmask[19]}}, {8{wmask[18]}}, {8{wmask[17]}}, {8{wmask[16]}}, 37 | {8{wmask[15]}}, {8{wmask[14]}}, {8{wmask[13]}}, {8{wmask[12]}}, 38 | {8{wmask[11]}}, {8{wmask[10]}}, {8{wmask[ 9]}}, {8{wmask[ 8]}}, 39 | {8{wmask[ 7]}}, {8{wmask[ 6]}}, {8{wmask[ 5]}}, {8{wmask[ 4]}}, 40 | {8{wmask[ 3]}}, {8{wmask[ 2]}}, {8{wmask[ 1]}}, {8{wmask[ 0]}}}; 41 | 42 | always @(posedge clk) begin 43 | ram_write_helper({addr, 2'b11}, wdata[255:192], wmask256[255:192], wen & en); 44 | ram_write_helper({addr, 2'b10}, wdata[191:128], wmask256[191:128], wen & en); 45 | ram_write_helper({addr, 2'b01}, wdata[127: 64], wmask256[127: 64], wen & en); 46 | ram_write_helper({addr, 2'b00}, wdata[ 63: 0], wmask256[ 63: 0], wen & en); 47 | end 48 | 49 | `endif 50 | `endif 51 | 52 | endmodule 53 | -------------------------------------------------------------------------------- /src/main/scala/ALU.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import Constant._ 4 | import org.chipsalliance.cde.config._ 5 | 6 | // Rocket ALU 7 | class ALU(implicit p: Parameters) extends CherrySpringsModule { 8 | val io = IO(new Bundle { 9 | val uop = Input(new MicroOp) 10 | val in1 = Input(UInt(xLen.W)) 11 | val in2 = Input(UInt(xLen.W)) 12 | val out = Output(UInt(xLen.W)) 13 | val adder_out = Output(UInt(xLen.W)) 14 | val cmp_out = Output(Bool()) 15 | }) 16 | 17 | val fn = io.uop.alu_op 18 | 19 | val is_sub = fn(3) 20 | val is_cmp = fn >= s"b$ALU_SLT".U 21 | val cmp_unsigned = fn(1) 22 | val cmp_inverted = fn(0) 23 | val cmp_eq = !fn(3) 24 | 25 | // ADD, SUB 26 | val in2_inv = Mux(is_sub, (~io.in2).asUInt, io.in2) 27 | val in1_xor_in2 = io.in1 ^ in2_inv 28 | io.adder_out := io.in1 + in2_inv + is_sub 29 | 30 | // SLT, SLTU 31 | val slt = Mux( 32 | io.in1(xLen - 1) === io.in2(xLen - 1), 33 | io.adder_out(xLen - 1), 34 | Mux(cmp_unsigned, io.in2(xLen - 1), io.in1(xLen - 1)) 35 | ) 36 | io.cmp_out := cmp_inverted ^ Mux(cmp_eq, in1_xor_in2 === 0.U, slt) 37 | 38 | // SLL, SRL, SRA 39 | val (shamt, shin_r) = 40 | if (xLen == 32) (io.in2(4, 0), io.in1) 41 | else { 42 | require(xLen == 64) 43 | val shin_hi_32 = Fill(32, is_sub && io.in1(31)) 44 | val shin_hi = Mux(io.uop.dw, io.in1(63, 32), shin_hi_32) 45 | val shamt = Cat(io.in2(5) & io.uop.dw.asUInt, io.in2(4, 0)) 46 | (shamt, Cat(shin_hi, io.in1(31, 0))) 47 | } 48 | val shin = Mux(fn === s"b$ALU_SRL".U || fn === s"b$ALU_SRA".U, shin_r, Reverse(shin_r)) 49 | val shout_r = (Cat(is_sub & shin(xLen - 1), shin).asSInt >> shamt)(xLen - 1, 0) 50 | val shout_l = Reverse(shout_r) 51 | val shout = Mux(fn === s"b$ALU_SRL".U || fn === s"b$ALU_SRA".U, shout_r, 0.U) | 52 | Mux(fn === s"b$ALU_SLL".U, shout_l, 0.U) 53 | 54 | // AND, OR, XOR 55 | val logic = Mux(fn === s"b$ALU_XOR".U || fn === s"b$ALU_OR".U, in1_xor_in2, 0.U) | 56 | Mux(fn === s"b$ALU_OR".U || fn === s"b$ALU_AND".U, io.in1 & io.in2, 0.U) 57 | val shift_logic = (is_cmp && slt) | logic | shout 58 | val out = Mux(fn === s"b$ALU_ADD".U || fn === s"b$ALU_SUB".U, io.adder_out, shift_logic) 59 | 60 | io.out := out 61 | if (xLen > 32) { 62 | require(xLen == 64) 63 | when(!io.uop.dw) { io.out := Cat(Fill(32, out(31)), out(31, 0)) } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/scala/BPU.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import chisel3.util.random._ 4 | import org.chipsalliance.cde.config._ 5 | 6 | class BTBEntry(implicit p: Parameters) extends CherrySpringsBundle { 7 | val tag = UInt((vaddrLen - 2).W) 8 | val target = UInt((vaddrLen - 2).W) 9 | val valid = Bool() 10 | } 11 | 12 | class BPU(implicit p: Parameters) extends CherrySpringsModule { 13 | val pht_default_state = 1.U(2.W) 14 | 15 | val io = IO(new Bundle { 16 | // from IF stage 17 | val pc = Input(UInt(xLen.W)) 18 | 19 | // from EX stage 20 | val jmp_packet = Input(new JmpPacket) 21 | 22 | // BPU output, generated in the same clock cycle 23 | val out = Output(UInt(xLen.W)) 24 | }) 25 | 26 | val ghr = RegInit(0.U(ghrLen.W)) 27 | val pht = RegInit(VecInit(Seq.fill(phtSize)(pht_default_state))) 28 | val btb = RegInit(VecInit(Seq.fill(btbSize)(0.U.asTypeOf(new BTBEntry)))) 29 | 30 | // read GHR & PHT 31 | val pht_ridx = io.pc(1 + ghrLen, 2) ^ ghr 32 | val pht_rdata = pht(pht_ridx) 33 | val pht_taken = (pht_rdata >= 2.U) 34 | 35 | // read BTB 36 | val btb_rhit = WireDefault(false.B) 37 | val btb_rdata = WireDefault(0.U((vaddrLen - 2).W)) 38 | for (i <- 0 until btbSize) { 39 | when(btb(i).valid && (btb(i).tag === io.pc(vaddrLen - 1, 2))) { 40 | btb_rhit := true.B 41 | btb_rdata := btb(i).target 42 | } 43 | } 44 | 45 | // output 46 | io.out := io.pc + 4.U // default output 47 | when(pht_taken && btb_rhit) { 48 | io.out := SignExt39_64(Cat(btb_rdata, 0.U(2.W))) 49 | } 50 | 51 | // write GHR & PHT 52 | val pht_widx = io.jmp_packet.bp_pc(1 + ghrLen, 2) ^ ghr 53 | when(io.jmp_packet.bp_update) { 54 | ghr := Cat(ghr(ghrLen - 2, 1), io.jmp_packet.bp_taken.asUInt) 55 | pht(pht_widx) := MuxLookup( 56 | pht(pht_widx), 57 | pht_default_state, 58 | Seq( 59 | 0.U -> Mux(io.jmp_packet.bp_taken, 1.U, 0.U), // strongly not taken 60 | 1.U -> Mux(io.jmp_packet.bp_taken, 2.U, 0.U), // weakly not taken 61 | 2.U -> Mux(io.jmp_packet.bp_taken, 3.U, 1.U), // weakly taken 62 | 3.U -> Mux(io.jmp_packet.bp_taken, 3.U, 2.U) // strongly taken 63 | ) 64 | ) 65 | } 66 | 67 | // write BTB 68 | val btb_whit = WireDefault(false.B) 69 | val btb_whit_way = WireDefault(0.U(log2Up(btbSize).W)) 70 | for (i <- 0 until btbSize) { 71 | when(btb(i).valid && (btb(i).tag === io.jmp_packet.bp_pc(vaddrLen - 1, 2))) { 72 | btb_whit := true.B 73 | btb_whit_way := i.U 74 | } 75 | } 76 | val btb_replace_idx = Wire(UInt(log2Up(btbSize).W)) 77 | btb_replace_idx := Mux(btb_whit, btb_whit_way, LFSR(log2Up(btbSize))) 78 | when(io.jmp_packet.bp_update && io.jmp_packet.bp_taken) { 79 | btb(btb_replace_idx).valid := true.B 80 | btb(btb_replace_idx).tag := io.jmp_packet.bp_pc(vaddrLen - 1, 2) 81 | btb(btb_replace_idx).target := io.jmp_packet.target(vaddrLen - 1, 2) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/scala/Bus.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.diplomacy._ 4 | import freechips.rocketchip.tilelink._ 5 | import org.chipsalliance.cde.config._ 6 | 7 | class CachePortReq(implicit p: Parameters) extends CherrySpringsBundle { 8 | val addr = Output(UInt(vaddrLen.W)) 9 | val wdata = Output(UInt(xLen.W)) 10 | val wmask = Output(UInt((xLen / 8).W)) 11 | val wen = Output(Bool()) 12 | val len = Output(UInt(2.W)) // only used for uncache (mmio) and lr/sc/amo for dcache 13 | val lrsc = Output(Bool()) // only used for dcache (lr when wen = 0 and sc when wen = 1) 14 | val amo = Output(UInt(Constant.LSU_X.length.W)) // only used for amo, and wen = 1 15 | 16 | def isAmo(): Bool = Constant.isAmo(amo) 17 | 18 | override def toPrintable: Printable = { 19 | cf"addr=$addr%x wdata=$wdata%x wmask=$wmask%x wen=$wen len=$len" 20 | } 21 | } 22 | 23 | class CachePortResp(implicit p: Parameters) extends CherrySpringsBundle { 24 | val rdata = Output(UInt(xLen.W)) 25 | val page_fault = Output(Bool()) 26 | val access_fault = Output(Bool()) 27 | val mmio = Output(Bool()) 28 | val paddr = Output(UInt(paddrLen.W)) // only for difftest & debug 29 | val wdata = Output(UInt(xLen.W)) // only for difftest & debug 30 | 31 | override def toPrintable: Printable = { 32 | cf"rdata=$rdata%x pf=$page_fault af=$access_fault mmio=$mmio" 33 | } 34 | } 35 | 36 | class CachePortIO(implicit p: Parameters) extends CherrySpringsBundle { 37 | val req = Decoupled(new CachePortReq) 38 | val resp = Flipped(Decoupled(new CachePortResp)) 39 | } 40 | 41 | class CachePortXBarNto1(n: Int)(implicit p: Parameters) extends CherrySpringsModule { 42 | val io = IO(new Bundle { 43 | val in = Flipped(Vec(n, new CachePortIO)) 44 | val out = new CachePortIO 45 | }) 46 | 47 | val arbiter = Module(new RRArbiter(new CachePortReq, n)) 48 | 49 | for (i <- 0 until n) { 50 | arbiter.io.in(i) <> io.in(i).req 51 | } 52 | 53 | // record in flight source (suppose responses are returned in-order) 54 | val id_queue = Module(new Queue(UInt(log2Ceil(n).W), entries = 4)) 55 | id_queue.io.enq.valid := io.out.req.fire 56 | id_queue.io.enq.bits := arbiter.io.chosen 57 | id_queue.io.deq.ready := io.out.resp.fire 58 | 59 | // req logic 60 | for (i <- 0 until n) { 61 | io.in(i).req.ready := (arbiter.io.chosen === i.U) && io.out.req.ready && id_queue.io.enq.ready 62 | } 63 | (io.out.req, arbiter.io.out) match { 64 | case (l, r) => { 65 | l.bits := r.bits 66 | l.valid := r.valid 67 | r.ready := l.ready 68 | } 69 | } 70 | 71 | // resp logic 72 | for (i <- 0 until n) { 73 | io.in(i).resp.bits := io.out.resp.bits 74 | io.in(i).resp.valid := false.B 75 | io.out.resp.ready := false.B 76 | } 77 | for (i <- 0 until n) { 78 | when(id_queue.io.deq.valid && (id_queue.io.deq.bits === i.U)) { 79 | io.out.resp.ready := io.in(i).resp.ready 80 | io.in(i).resp.valid := io.out.resp.valid 81 | } 82 | } 83 | } 84 | 85 | class CachePortXBar1to2(implicit p: Parameters) extends CherrySpringsModule { 86 | val io = IO(new Bundle { 87 | val in = Flipped(new CachePortIO) 88 | val out = Vec(2, new CachePortIO) 89 | val to_1 = Input(Bool()) 90 | }) 91 | 92 | // assume only one in-flight request 93 | val to_1_r = RegEnable(io.to_1, false.B, io.in.req.fire) 94 | 95 | // req logic 96 | io.out(0).req.bits := io.in.req.bits 97 | io.out(1).req.bits := io.in.req.bits 98 | io.out(0).req.valid := io.in.req.valid && !io.to_1 99 | io.out(1).req.valid := io.in.req.valid && io.to_1 100 | io.in.req.ready := Mux(io.to_1, io.out(1).req.ready, io.out(0).req.ready) 101 | 102 | // resp logic 103 | io.in.resp.bits := Mux(to_1_r, io.out(1).resp.bits, io.out(0).resp.bits) 104 | io.in.resp.valid := Mux(to_1_r, io.out(1).resp.valid, io.out(0).resp.valid) 105 | io.in.resp.bits.mmio := to_1_r 106 | io.out(0).resp.ready := io.in.resp.ready 107 | io.out(1).resp.ready := io.in.resp.ready 108 | } 109 | 110 | class Uncache(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 111 | require(xLen == 64) 112 | 113 | val node = TLClientNode( 114 | Seq( 115 | TLMasterPortParameters.v1( 116 | clients = Seq( 117 | TLMasterParameters.v1( 118 | name = s"uncache-${hartID}", 119 | sourceId = IdRange(0, sourceRange) 120 | ) 121 | ) 122 | ) 123 | ) 124 | ) 125 | 126 | lazy val module = new UncacheModule(this) 127 | } 128 | 129 | class UncacheModule(outer: Uncache) extends LazyModuleImp(outer) with HasCherrySpringsParameters { 130 | val io = IO(new Bundle { 131 | val in = Flipped(new CachePortIO) 132 | }) 133 | 134 | // TL-UL for uncache data port 135 | val (tl, edge) = outer.node.out.head 136 | 137 | val req = io.in.req 138 | val resp = io.in.resp 139 | 140 | tl.a.valid := req.valid 141 | req.ready := tl.a.ready 142 | resp.valid := tl.d.valid 143 | tl.d.ready := resp.ready 144 | 145 | val source = Counter(tl.a.fire, sourceRange)._1 146 | 147 | val (_, get_bits) = edge.Get(source, req.bits.addr, req.bits.len) 148 | val (_, put_bits) = edge.Put(source, req.bits.addr, req.bits.len, req.bits.wdata, req.bits.wmask) 149 | 150 | tl.a.bits := Mux(req.bits.wen, put_bits, get_bits) 151 | resp.bits := 0.U.asTypeOf(new CachePortResp) 152 | resp.bits.rdata := tl.d.bits.data 153 | 154 | if (debugUncache) { 155 | when(req.fire) { 156 | printf(cf"${DebugTimer()} [ MMIO ] [in -req ] ${req.bits}\n") 157 | } 158 | when(resp.fire) { 159 | printf(cf"${DebugTimer()} [ MMIO ] [in -resp] ${resp.bits}\n") 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/scala/CSR.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.rocket._ 5 | import org.chipsalliance.cde.config._ 6 | import Constant._ 7 | 8 | object PRV { 9 | val U = 0 10 | val S = 1 11 | val M = 3 12 | } 13 | 14 | // Based on RISC-V ISA Volume II: Privileged Architecture, Version 20211203 15 | class CSR(implicit p: Parameters) extends CherrySpringsModule { 16 | require(xLen == 64) 17 | 18 | val io = IO(new Bundle { 19 | val uop = Input(new MicroOp) 20 | val rw = new Bundle { 21 | val addr = Input(UInt(12.W)) 22 | val cmd = Input(UInt(CSR_X.length.W)) 23 | val wdata = Input(UInt(xLen.W)) 24 | val rdata = Output(UInt(xLen.W)) 25 | val valid = Output(Bool()) 26 | } 27 | val prv = Output(UInt(2.W)) 28 | val mprv = Output(Bool()) 29 | val mpp = Output(UInt(2.W)) 30 | val sv39_en = Output(Bool()) 31 | val satp_asid = Output(UInt(16.W)) 32 | val satp_ppn = Output(UInt(44.W)) 33 | val sfence_vma = Output(Bool()) 34 | val fence_i = Output(Bool()) 35 | val jmp_packet = Output(new JmpPacket) 36 | val lsu_addr = Input(UInt(xLen.W)) 37 | val lsu_exc_code = Input(UInt(4.W)) 38 | val interrupt = Input(new ExternalInterrupt) 39 | val is_int = Output(Bool()) 40 | 41 | val cycle = Output(UInt(xLen.W)) 42 | val instret = Output(UInt(xLen.W)) 43 | val commit = Input(UInt(1.W)) 44 | 45 | val hartid = Output(UInt(8.W)) // only for difftest 46 | }) 47 | 48 | // privilege mode 49 | val prv = RegInit(PRV.M.U) 50 | val prv_is_m = (prv === PRV.M.U) 51 | val prv_is_s = (prv === PRV.S.U) 52 | val prv_is_ms = prv_is_m || prv_is_s 53 | val prv_is_u = (prv === PRV.U.U) 54 | 55 | val csr_legal = WireDefault(false.B) 56 | val rdata = WireDefault(0.U(xLen.W)) 57 | val wdata = Wire(UInt(xLen.W)) 58 | val wen = (io.rw.cmd =/= s"b$CSR_N".U) && (io.uop.exc === s"b$EXC_N".U) && csr_legal 59 | wdata := MuxLookup( 60 | io.rw.cmd, 61 | 0.U, 62 | Seq( 63 | s"b$CSR_RW".U -> io.rw.wdata, 64 | s"b$CSR_RS".U -> (rdata | io.rw.wdata), 65 | s"b$CSR_RC".U -> (rdata & ~io.rw.wdata) 66 | ) 67 | ) 68 | 69 | // constant masks 70 | val s_exc_mask = "h0f7ff".U // exclude env call from M-mode 71 | val s_int_mask = "h222".U 72 | val m_int_mask = "haaa".U 73 | 74 | /* 75 | * Number: 0x300 / 0x100 76 | * Privilege: MRW / SRW 77 | * Name: mstatus / sstatus 78 | * Description: Machine / Supervisor status register 79 | */ 80 | val sstatus = WireDefault(0.U(xLen.W)) 81 | val status_sie = RegInit(0.U(1.W)) 82 | val status_spie = RegInit(0.U(1.W)) 83 | val status_ube = 0.U(1.W) 84 | val status_spp = RegInit(0.U(1.W)) 85 | val status_vs = 0.U(2.W) 86 | val status_fs = RegInit(0.U(2.W)) 87 | val status_xs = 0.U(2.W) // RegInit(0.U(2.W)) 88 | val status_sum = RegInit(0.U(1.W)) 89 | val status_mxr = RegInit(0.U(1.W)) 90 | val status_uxl = log2Up(xLen / 16).U(2.W) 91 | val status_sd = status_fs.orR | status_xs.orR 92 | sstatus := Cat( 93 | status_sd, 94 | 0.U(29.W), 95 | status_uxl, 96 | 0.U(12.W), 97 | status_mxr, 98 | status_sum, 99 | 0.U(1.W), 100 | status_xs, 101 | status_fs, 102 | 0.U(2.W), 103 | status_vs, 104 | status_spp, 105 | 0.U(1.W), 106 | status_ube, 107 | status_spie, 108 | 0.U(3.W), 109 | status_sie, 110 | 0.U(1.W) 111 | ) 112 | val mstatus = WireDefault(0.U(xLen.W)) 113 | val mstatus_mie = RegInit(0.U(1.W)) 114 | val mstatus_mpie = RegInit(0.U(1.W)) 115 | val mstatus_mpp = RegInit(0.U(2.W)) 116 | val mstatus_mprv = RegInit(0.U(1.W)) 117 | val mstatus_tvm = RegInit(0.U(1.W)) 118 | val mstatus_tw = RegInit(0.U(1.W)) 119 | val mstatus_tsr = RegInit(0.U(1.W)) 120 | val mstatus_sxl = log2Up(xLen / 16).U(2.W) 121 | val mstatus_sbe = 0.U(1.W) 122 | val mstatus_mbe = 0.U(1.W) 123 | mstatus := Cat( 124 | status_sd, 125 | 0.U(25.W), 126 | mstatus_mbe, 127 | mstatus_sbe, 128 | mstatus_sxl, 129 | status_uxl, 130 | 0.U(9.W), 131 | mstatus_tsr, 132 | mstatus_tw, 133 | mstatus_tvm, 134 | status_mxr, 135 | status_sum, 136 | mstatus_mprv, 137 | status_xs, 138 | status_fs, 139 | mstatus_mpp, 140 | status_vs, 141 | status_spp, 142 | mstatus_mpie, 143 | status_ube, 144 | status_spie, 145 | 0.U(1.W), 146 | mstatus_mie, 147 | 0.U(1.W), 148 | status_sie, 149 | 0.U(1.W) 150 | ) 151 | when(io.rw.addr === CSRs.sstatus.U) { 152 | rdata := sstatus 153 | when(wen) { 154 | status_sie := wdata(1) 155 | status_spie := wdata(5) 156 | status_spp := wdata(8) 157 | status_fs := wdata(14, 13) 158 | // status_xs := wdata(16, 15) 159 | status_sum := wdata(18) 160 | status_mxr := wdata(19) 161 | } 162 | csr_legal := prv_is_ms 163 | } 164 | when(io.rw.addr === CSRs.mstatus.U) { 165 | rdata := mstatus 166 | when(wen) { 167 | status_sie := wdata(1) 168 | status_spie := wdata(5) 169 | status_spp := wdata(8) 170 | status_fs := wdata(14, 13) 171 | // status_xs := wdata(16, 15) 172 | status_sum := wdata(18) 173 | status_mxr := wdata(19) 174 | mstatus_mie := wdata(3) 175 | mstatus_mpie := wdata(7) 176 | mstatus_mpp := wdata(12, 11) 177 | mstatus_mprv := wdata(17) 178 | mstatus_tvm := wdata(20) 179 | mstatus_tw := wdata(21) 180 | mstatus_tsr := wdata(22) 181 | } 182 | csr_legal := prv_is_m 183 | } 184 | io.mprv := mstatus_mprv 185 | io.mpp := mstatus_mpp 186 | 187 | /* 188 | * Number: 0x105 189 | * Privilege: SRW 190 | * Name: stvec 191 | * Description: Supervisor trap-handler base address 192 | */ 193 | val stvec = RegInit(0.U(xLen.W)) 194 | when(io.rw.addr === CSRs.stvec.U) { 195 | rdata := stvec 196 | when(wen) { 197 | stvec := wdata 198 | } 199 | csr_legal := prv_is_ms 200 | } 201 | 202 | /* 203 | * Number: 0x106 204 | * Privilege: SRW 205 | * Name: scounteren 206 | * Description: Supervisor counter enable 207 | */ 208 | val scounteren = RegInit(0.U(xLen.W)) 209 | when(io.rw.addr === CSRs.scounteren.U) { 210 | rdata := scounteren 211 | when(wen) { 212 | scounteren := wdata 213 | } 214 | csr_legal := prv_is_ms 215 | } 216 | 217 | /* 218 | * Number: 0x140 219 | * Privilege: SRW 220 | * Name: sscratch 221 | * Description: Scratch register for supervisor trap handlers 222 | */ 223 | val sscratch = RegInit(0.U(xLen.W)) 224 | when(io.rw.addr === CSRs.sscratch.U) { 225 | rdata := sscratch 226 | when(wen) { 227 | sscratch := wdata 228 | } 229 | csr_legal := prv_is_ms 230 | } 231 | 232 | /* 233 | * Number: 0x141 234 | * Privilege: SRW 235 | * Name: sepc 236 | * Description: Machine exception program counter 237 | */ 238 | val sepc = RegInit(0.U(xLen.W)) 239 | when(io.rw.addr === CSRs.sepc.U) { 240 | rdata := sepc 241 | when(wen) { 242 | sepc := wdata 243 | } 244 | csr_legal := prv_is_ms 245 | } 246 | 247 | /* 248 | * Number: 0x142 249 | * Privilege: SRW 250 | * Name: scause 251 | * Description: Supervisor trap cause 252 | */ 253 | val scause = RegInit(0.U(xLen.W)) 254 | when(io.rw.addr === CSRs.scause.U) { 255 | rdata := scause 256 | when(wen) { 257 | scause := wdata 258 | } 259 | csr_legal := prv_is_ms 260 | } 261 | 262 | /* 263 | * Number: 0x143 264 | * Privilege: SRW 265 | * Name: stval 266 | * Description: Supervisor bad address or instruction 267 | */ 268 | val stval = RegInit(0.U(xLen.W)) 269 | when(io.rw.addr === CSRs.stval.U) { 270 | rdata := stval 271 | when(wen) { 272 | stval := wdata 273 | } 274 | csr_legal := prv_is_ms 275 | } 276 | 277 | /* 278 | * Number: 0x180 279 | * Privilege: SRW 280 | * Name: satp 281 | * Description: Supervisor address translation and protection 282 | */ 283 | val satp = RegInit(0.U(xLen.W)) 284 | val satp_updated = WireDefault(false.B) 285 | val tvm_en = prv_is_s && mstatus_tvm.asBool 286 | // if satp is written with an unsupported MODE, the entire write has no effect 287 | val satp_wen = (wdata(62, 60) === 0.U) 288 | io.sv39_en := satp(63).asBool 289 | io.satp_asid := satp(59, 44) 290 | io.satp_ppn := satp(43, 0) 291 | when(io.rw.addr === CSRs.satp.U) { 292 | rdata := satp 293 | when(wen && satp_wen) { 294 | satp := wdata 295 | io.sv39_en := wdata(63).asBool // bypass 296 | io.satp_asid := wdata(59, 44) // bypass 297 | io.satp_ppn := wdata(43, 0) // bypass 298 | satp_updated := prv_is_s // flush pipeline after satp updated if in S mode 299 | } 300 | csr_legal := prv_is_ms && !tvm_en 301 | } 302 | 303 | /* 304 | * Number: 0xF11 305 | * Privilege: MRO 306 | * Name: mvendorid 307 | * Description: Vendor ID 308 | */ 309 | when(io.rw.addr === CSRs.mvendorid.U) { 310 | rdata := 0.U 311 | csr_legal := prv_is_m 312 | } 313 | 314 | /* 315 | * Number: 0xF12 316 | * Privilege: MRO 317 | * Name: marchid 318 | * Description: Architecture ID 319 | */ 320 | when(io.rw.addr === CSRs.marchid.U) { 321 | rdata := 0.U 322 | csr_legal := prv_is_m 323 | } 324 | 325 | /* 326 | * Number: 0xF13 327 | * Privilege: MRO 328 | * Name: mimpid 329 | * Description: Implementation ID 330 | */ 331 | when(io.rw.addr === CSRs.mimpid.U) { 332 | rdata := 0.U 333 | csr_legal := prv_is_m 334 | } 335 | 336 | /* 337 | * Number: 0xF14 338 | * Privilege: MRO (Writable in CherrySprings BootROM) 339 | * Name: mhartid 340 | * Description: Hardware thread ID 341 | */ 342 | val mhartid = RegInit(hartID.U(xLen.W)) 343 | val mhartid_writable = RegInit(true.B) 344 | when(io.rw.addr === CSRs.mhartid.U) { 345 | rdata := mhartid 346 | when(wen && mhartid_writable) { 347 | mhartid := wdata 348 | mhartid_writable := false.B 349 | } 350 | csr_legal := prv_is_m 351 | } 352 | io.hartid := mhartid(7, 0) 353 | 354 | /* 355 | * Number: 0x301 356 | * Privilege: MRW 357 | * Name: misa 358 | * Description: ISA and extensions 359 | */ 360 | val misa = "h8000000000141101".U(64.W) 361 | when(io.rw.addr === CSRs.misa.U) { 362 | rdata := misa 363 | csr_legal := prv_is_m 364 | } 365 | 366 | /* 367 | * Number: 0x302 368 | * Privilege: MRW 369 | * Name: medeleg 370 | * Description: Machine exception delegation register 371 | */ 372 | val medeleg = RegInit(0.U(xLen.W)) 373 | when(io.rw.addr === CSRs.medeleg.U) { 374 | rdata := medeleg 375 | when(wen) { 376 | medeleg := wdata & s_exc_mask 377 | } 378 | csr_legal := prv_is_m 379 | } 380 | 381 | /* 382 | * Number: 0x303 383 | * Privilege: MRW 384 | * Name: mideleg 385 | * Description: Machine interrupt delegation register 386 | */ 387 | val mideleg = RegInit(0.U(xLen.W)) 388 | when(io.rw.addr === CSRs.mideleg.U) { 389 | rdata := mideleg 390 | when(wen) { 391 | mideleg := wdata & s_int_mask 392 | } 393 | csr_legal := prv_is_m 394 | } 395 | 396 | /* 397 | * Number: 0x304 / 0x104 398 | * Privilege: MRW / SRW 399 | * Name: mie / sie 400 | * Description: Machine / Supervisor interrupt-enable register 401 | */ 402 | val mie = WireDefault(0.U(xLen.W)) 403 | val sie = WireDefault(0.U(xLen.W)) 404 | val ie_ssie = RegInit(0.U(1.W)) 405 | val ie_msie = RegInit(0.U(1.W)) 406 | val ie_stie = RegInit(0.U(1.W)) 407 | val ie_mtie = RegInit(0.U(1.W)) 408 | val ie_seie = RegInit(0.U(1.W)) 409 | val ie_meie = RegInit(0.U(1.W)) 410 | mie := Cat( 411 | 0.U(52.W), 412 | ie_meie, 413 | 0.U(1.W), 414 | ie_seie, 415 | 0.U(1.W), 416 | ie_mtie, 417 | 0.U(1.W), 418 | ie_stie, 419 | 0.U(1.W), 420 | ie_msie, 421 | 0.U(1.W), 422 | ie_ssie, 423 | 0.U(1.W) 424 | ) 425 | sie := Cat( 426 | 0.U(54.W), 427 | ie_seie, 428 | 0.U(3.W), 429 | ie_stie, 430 | 0.U(3.W), 431 | ie_ssie, 432 | 0.U(1.W) 433 | ) 434 | when(io.rw.addr === CSRs.mie.U) { 435 | rdata := mie 436 | when(wen) { 437 | ie_ssie := wdata(1) 438 | ie_msie := wdata(3) 439 | ie_stie := wdata(5) 440 | ie_mtie := wdata(7) 441 | ie_seie := wdata(9) 442 | ie_meie := wdata(11) 443 | } 444 | csr_legal := prv_is_m 445 | } 446 | when(io.rw.addr === CSRs.sie.U) { 447 | rdata := sie 448 | when(wen) { 449 | ie_ssie := wdata(1) 450 | ie_stie := wdata(5) 451 | ie_seie := wdata(9) 452 | } 453 | csr_legal := prv_is_ms 454 | } 455 | 456 | /* 457 | * Number: 0x305 458 | * Privilege: MRW 459 | * Name: mtvec 460 | * Description: Machine trap-handler base address 461 | */ 462 | val mtvec = RegInit(0.U(xLen.W)) 463 | when(io.rw.addr === CSRs.mtvec.U) { 464 | rdata := mtvec 465 | when(wen) { 466 | mtvec := wdata 467 | } 468 | csr_legal := prv_is_m 469 | } 470 | 471 | /* 472 | * Number: 0x306 473 | * Privilege: MRW 474 | * Name: mcounteren 475 | * Description: Machine counter enable 476 | */ 477 | val mcounteren = RegInit(0.U(xLen.W)) 478 | when(io.rw.addr === CSRs.mcounteren.U) { 479 | rdata := mcounteren 480 | when(wen) { 481 | mcounteren := wdata 482 | } 483 | csr_legal := prv_is_m 484 | } 485 | 486 | /* 487 | * Number: 0x340 488 | * Privilege: MRW 489 | * Name: mscratch 490 | * Description: Scratch register for machine trap handlers 491 | */ 492 | val mscratch = RegInit(0.U(xLen.W)) 493 | when(io.rw.addr === CSRs.mscratch.U) { 494 | rdata := mscratch 495 | when(wen) { 496 | mscratch := wdata 497 | } 498 | csr_legal := prv_is_m 499 | } 500 | 501 | /* 502 | * Number: 0x341 503 | * Privilege: MRW 504 | * Name: mepc 505 | * Description: Machine exception program counter 506 | */ 507 | val mepc = RegInit(0.U(xLen.W)) 508 | when(io.rw.addr === CSRs.mepc.U) { 509 | rdata := mepc 510 | when(wen) { 511 | mepc := wdata 512 | } 513 | csr_legal := prv_is_m 514 | } 515 | 516 | /* 517 | * Number: 0x342 518 | * Privilege: MRW 519 | * Name: mcause 520 | * Description: Machine trap cause 521 | */ 522 | val mcause = RegInit(0.U(xLen.W)) 523 | when(io.rw.addr === CSRs.mcause.U) { 524 | rdata := mcause 525 | when(wen) { 526 | mcause := wdata 527 | } 528 | csr_legal := prv_is_m 529 | } 530 | 531 | /* 532 | * Number: 0x343 533 | * Privilege: MRW 534 | * Name: mtval 535 | * Description: Machine bad address or instruction 536 | */ 537 | val mtval = RegInit(0.U(xLen.W)) 538 | when(io.rw.addr === CSRs.mtval.U) { 539 | rdata := mtval 540 | when(wen) { 541 | mtval := wdata 542 | } 543 | csr_legal := prv_is_m 544 | } 545 | 546 | /* 547 | * Number: 0x344 / 0x144 548 | * Privilege: MRW / SRW 549 | * Name: mip / sip 550 | * Description: Machine / Supervisor interrupt pending 551 | */ 552 | val mip = WireDefault(0.U(xLen.W)) 553 | val sip = WireDefault(0.U(xLen.W)) 554 | val ip_ssip = RegInit(0.U(1.W)) 555 | val ip_msip = io.interrupt.msip.asUInt 556 | val ip_stip = RegInit(0.U(1.W)) 557 | val ip_mtip = io.interrupt.mtip.asUInt 558 | val ip_seip_r = RegInit(0.U(1.W)) 559 | val ip_seip = (io.interrupt.seip || ip_seip_r.asBool).asUInt 560 | val ip_meip = io.interrupt.meip.asUInt 561 | mip := Cat( 562 | 0.U(52.W), 563 | ip_meip, 564 | 0.U(1.W), 565 | ip_seip, 566 | 0.U(1.W), 567 | ip_mtip, 568 | 0.U(1.W), 569 | ip_stip, 570 | 0.U(1.W), 571 | ip_msip, 572 | 0.U(1.W), 573 | ip_ssip, 574 | 0.U(1.W) 575 | ) 576 | sip := Cat( 577 | 0.U(54.W), 578 | ip_seip, 579 | 0.U(3.W), 580 | ip_stip, 581 | 0.U(3.W), 582 | ip_ssip, 583 | 0.U(1.W) 584 | ) 585 | when(io.rw.addr === CSRs.mip.U) { 586 | rdata := mip 587 | when(wen) { 588 | ip_ssip := wdata(1) 589 | ip_stip := wdata(5) 590 | ip_seip_r := wdata(9) 591 | } 592 | csr_legal := prv_is_m 593 | } 594 | when(io.rw.addr === CSRs.sip.U) { 595 | rdata := sip 596 | when(wen) { 597 | ip_ssip := wdata(1) 598 | } 599 | csr_legal := prv_is_ms 600 | } 601 | 602 | /* 603 | * Number: 0xB00 / 0xC00 604 | * Privilege: MRW / URO 605 | * Name: mcycle / cycle 606 | * Description: Machine cycle counter / Cycle counter for RDCYCLE instruction 607 | */ 608 | val cycle = RegInit(UInt(64.W), 0.U) 609 | cycle := cycle + 1.U 610 | when(io.rw.addr === CSRs.mcycle.U) { 611 | rdata := cycle 612 | when(wen) { 613 | cycle := wdata 614 | } 615 | csr_legal := prv_is_m 616 | } 617 | when(io.rw.addr === CSRs.cycle.U) { 618 | rdata := cycle 619 | csr_legal := prv_is_m || (prv_is_s && mcounteren(0)) || (prv_is_u && mcounteren(0) && scounteren(0)) 620 | } 621 | io.cycle := cycle 622 | 623 | /* 624 | * Number: 0xB02 / 0xC02 625 | * Privilege: MRW / URO 626 | * Name: minstret / instret 627 | * Description: Machine instructions-retired counter / 628 | * Instructions-retired counter for RDINSTRET instruction 629 | */ 630 | val instret = RegInit(UInt(64.W), 0.U) 631 | instret := instret + io.commit 632 | when(io.rw.addr === CSRs.minstret.U) { 633 | rdata := instret 634 | when(wen) { 635 | instret := wdata 636 | } 637 | csr_legal := prv_is_m 638 | } 639 | when(io.rw.addr === CSRs.instret.U) { 640 | rdata := instret 641 | csr_legal := prv_is_m || (prv_is_s && mcounteren(2)) || (prv_is_u && mcounteren(2) && scounteren(2)) 642 | } 643 | io.instret := instret 644 | 645 | // CSR module output 646 | io.rw.rdata := rdata 647 | io.rw.valid := csr_legal 648 | io.prv := prv 649 | 650 | /* 651 | * An MRET or SRET instruction is used to return from a trap in M-mode or S-mode respectively. 652 | * When executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the 653 | * privilege mode is changed to y; xPIE is set to 1; and xPP is set to the least-privileged 654 | * supported mode (U if U-mode is implemented, else M). If xPP != M, xRET also sets MPRV = 0. 655 | */ 656 | val is_mret = io.uop.sys_op === s"b$SYS_MRET".U 657 | val mret_legal = prv_is_m 658 | when(is_mret && mret_legal) { 659 | prv := mstatus_mpp 660 | io.prv := mstatus_mpp // bypass 661 | mstatus_mie := mstatus_mpie 662 | mstatus_mpie := 1.U 663 | mstatus_mpp := PRV.U.U 664 | when(mstatus_mpp =/= PRV.M.U) { 665 | mstatus_mprv := 0.U 666 | } 667 | } 668 | 669 | val is_sret = io.uop.sys_op === s"b$SYS_SRET".U 670 | val sret_legal = prv_is_ms && !mstatus_tsr.asBool 671 | when(is_sret && sret_legal) { 672 | prv := status_spp 673 | io.prv := status_spp // bypass 674 | status_sie := status_spie 675 | status_spie := 1.U 676 | status_spp := PRV.U.U 677 | when(status_spp =/= PRV.M.U) { 678 | mstatus_mprv := 0.U 679 | } 680 | } 681 | 682 | /* 683 | * System instructions 684 | */ 685 | val is_sfv = (io.uop.sys_op === s"b$SYS_SFV".U) && io.uop.valid 686 | val sfv_legal = prv_is_ms && !(prv_is_s && mstatus_tvm.asBool) 687 | val is_fence_i = (io.uop.sys_op === s"b$SYS_FENCEI".U) && io.uop.valid 688 | val is_sys = io.sfence_vma || is_fence_i 689 | io.sfence_vma := is_sfv && sfv_legal 690 | io.fence_i := is_fence_i 691 | 692 | /* 693 | * Exception & Interrupt 694 | */ 695 | val is_exc_from_prev = (io.uop.exc =/= s"b$EXC_N".U) 696 | val is_exc_from_lsu = (io.lsu_exc_code =/= 0.U) 697 | val is_exc_from_csr = !csr_legal && (io.rw.cmd =/= s"b$CSR_N".U) 698 | val is_exc_from_sys = (is_mret && !mret_legal) || (is_sret && !sret_legal) || (is_sfv && !sfv_legal) 699 | val is_exc = is_exc_from_prev || is_exc_from_lsu || is_exc_from_csr 700 | 701 | def int2index(x: UInt): UInt = { 702 | val y = WireDefault(0.U(4.W)) 703 | when(x(11)) { 704 | y := 11.U // MEI 705 | }.elsewhen(x(3)) { 706 | y := 3.U // MSI 707 | }.elsewhen(x(7)) { 708 | y := 7.U // MTI 709 | }.elsewhen(x(9)) { 710 | y := 9.U // SEI 711 | }.elsewhen(x(1)) { 712 | y := 1.U // SSI 713 | }.elsewhen(x(5)) { 714 | y := 5.U // STI 715 | } 716 | y 717 | } 718 | 719 | val int_attach = io.uop.valid && (io.uop.fu === s"b$FU_ALU".U || io.uop.fu === s"b$FU_JMP".U) 720 | val int_bits = mip & mie 721 | val int_bits_mmode = int_bits & (~mideleg) 722 | 723 | val int_index = WireDefault(0.U(4.W)) 724 | val int_index_tmp = int2index(int_bits) 725 | when(prv === PRV.M.U) { 726 | when(mstatus_mie.asBool) { 727 | int_index := int2index(int_bits_mmode) 728 | } 729 | }.otherwise { 730 | when(((1.U << int_index_tmp) & mideleg) =/= 0.U) { // delegate to S mode 731 | when(status_sie.asBool || prv === PRV.U.U) { 732 | int_index := int_index_tmp 733 | } 734 | }.otherwise { 735 | when(mstatus_mie.asBool || prv === PRV.U.U || prv === PRV.S.U) { 736 | int_index := int_index_tmp 737 | } 738 | } 739 | } 740 | val is_int = int_attach && (int_index =/= 0.U) 741 | 742 | val cause_exc = Wire(UInt(4.W)) 743 | val cause_exc_onehot = Wire(UInt(16.W)) 744 | val cause_int = Wire(UInt(4.W)) 745 | val cause_int_onehot = Wire(UInt(16.W)) 746 | cause_exc := MuxLookup( 747 | io.uop.exc, // check exception from early stage first 748 | Mux( 749 | io.lsu_exc_code =/= 0.U, 750 | io.lsu_exc_code, 751 | Mux(is_exc_from_csr || is_exc_from_sys, Causes.illegal_instruction.U, 0.U) 752 | ), 753 | Seq( 754 | s"b$EXC_IAM".U -> Causes.misaligned_fetch.U, 755 | s"b$EXC_IAF".U -> Causes.fetch_access.U, 756 | s"b$EXC_II".U -> Causes.illegal_instruction.U, 757 | s"b$EXC_EB".U -> Causes.breakpoint.U, 758 | s"b$EXC_EC".U -> Cat("b10".U, prv), 759 | s"b$EXC_IPF".U -> Causes.fetch_page_fault.U 760 | ) 761 | ) 762 | 763 | cause_exc_onehot := UIntToOH(cause_exc) 764 | cause_int := int_index 765 | cause_int_onehot := UIntToOH(cause_int) 766 | io.is_int := is_int 767 | 768 | if (debugInterrupt) { 769 | when(is_int) { 770 | printf(cf"${DebugTimer()} [Hart ${hartID}] [Interrupt] ") 771 | when(int_index === 11.U) { 772 | printf("MEI") 773 | }.elsewhen(int_index === 3.U) { 774 | printf("MSI") 775 | }.elsewhen(int_index === 7.U) { 776 | printf("MTI") 777 | }.elsewhen(int_index === 9.U) { 778 | printf("SEI") 779 | }.elsewhen(int_index === 1.U) { 780 | printf("SSI") 781 | }.elsewhen(int_index === 5.U) { 782 | printf("STI") 783 | } 784 | printf(cf" (at PC = ${io.uop.pc}%x)\n") 785 | } 786 | } 787 | 788 | val tval = WireDefault(0.U(xLen.W)) 789 | when(is_exc_from_lsu) { 790 | tval := io.lsu_addr 791 | } 792 | when(io.uop.exc === s"b$EXC_IPF".U) { 793 | tval := io.uop.pc 794 | } 795 | 796 | val trap = is_exc || is_int 797 | val trap_to_s = WireDefault(false.B) 798 | val trap_pc = WireDefault(0.U(xLen.W)) 799 | when(!prv_is_m) { 800 | trap_to_s := 801 | (is_exc && ((cause_exc_onehot & medeleg) =/= 0.U)) || 802 | (is_int && ((cause_int_onehot & mideleg) =/= 0.U)) 803 | } 804 | when(trap_to_s && trap) { 805 | stval := tval 806 | scause := Mux(is_exc, cause_exc, Cat(1.U(1.W), 0.U(59.W), cause_int)) 807 | sepc := io.uop.pc 808 | status_spie := status_sie 809 | status_sie := 0.U 810 | status_spp := prv 811 | prv := PRV.S.U 812 | io.prv := PRV.S.U // bypass 813 | trap_pc := Cat(stvec(xLen - 1, 2) + Mux(is_int && (stvec(1, 0) === 1.U), cause_int, 0.U), 0.U(2.W)) 814 | } 815 | when(!trap_to_s && trap) { 816 | mtval := tval 817 | mcause := Mux(is_exc, cause_exc, Cat(1.U(1.W), 0.U(59.W), cause_int)) 818 | mepc := io.uop.pc 819 | mstatus_mpie := mstatus_mie 820 | mstatus_mie := 0.U 821 | mstatus_mpp := prv 822 | prv := PRV.M.U 823 | io.prv := PRV.M.U // bypass 824 | trap_pc := Cat(mtvec(xLen - 1, 2) + Mux(is_int && (mtvec(1, 0) === 1.U), cause_int, 0.U), 0.U(2.W)) 825 | } 826 | 827 | // CSR / SYS control flow 828 | io.jmp_packet := 0.U.asTypeOf(new JmpPacket) 829 | io.jmp_packet.valid := trap || is_sys || satp_updated || (is_mret && mret_legal) || (is_sret && sret_legal) 830 | io.jmp_packet.target := Mux(trap, trap_pc, Mux(is_sys || satp_updated, io.uop.npc, Mux(is_mret, mepc, sepc))) 831 | 832 | if (enableDifftest) { 833 | val diff_cs = Module(new DifftestCSRState) 834 | diff_cs.io.clock := clock 835 | diff_cs.io.coreid := io.hartid 836 | diff_cs.io.priviledgeMode := prv 837 | diff_cs.io.mstatus := mstatus 838 | diff_cs.io.sstatus := sstatus 839 | diff_cs.io.mepc := mepc 840 | diff_cs.io.sepc := sepc 841 | diff_cs.io.mtval := mtval 842 | diff_cs.io.stval := stval 843 | diff_cs.io.mtvec := mtvec 844 | diff_cs.io.stvec := stvec 845 | diff_cs.io.mcause := mcause 846 | diff_cs.io.scause := scause 847 | diff_cs.io.satp := satp 848 | diff_cs.io.mip := mip 849 | diff_cs.io.mie := mie 850 | diff_cs.io.mscratch := mscratch 851 | diff_cs.io.sscratch := sscratch 852 | diff_cs.io.mideleg := mideleg 853 | diff_cs.io.medeleg := medeleg 854 | 855 | val diff_ae = Module(new DifftestArchEvent) 856 | diff_ae.io.clock := clock 857 | diff_ae.io.coreid := io.hartid 858 | diff_ae.io.intrNO := RegNext(Mux(is_int, cause_int, 0.U)) 859 | diff_ae.io.cause := RegNext(Mux(is_exc, cause_exc, 0.U)) 860 | diff_ae.io.exceptionPC := RegNext(io.uop.pc) 861 | diff_ae.io.exceptionInst := RegNext(io.uop.instr) 862 | } 863 | } 864 | -------------------------------------------------------------------------------- /src/main/scala/CachePortProxy.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import org.chipsalliance.cde.config._ 4 | import Constant._ 5 | 6 | class CachePortProxy(implicit p: Parameters) extends CherrySpringsModule with Sv39Parameters { 7 | def isLeaf(p: Sv39PTE) = p.flag.r || p.flag.x 8 | 9 | val io = IO(new Bundle { 10 | val prv = Input(UInt(2.W)) 11 | val sv39_en = Input(Bool()) 12 | val satp_asid = Input(UInt(16.W)) 13 | val satp_ppn = Input(UInt(44.W)) 14 | val sfence_vma = Input(Bool()) 15 | val in = Flipped(new CachePortIO) 16 | val out = new CachePortIO 17 | val ptw = new CachePortIO 18 | }) 19 | 20 | val s_idle :: s_ptw_req :: s_ptw_resp :: s_ptw_complete :: s_access_fault :: Nil = Enum(5) 21 | val state = RegInit(s_idle) 22 | 23 | // input register 24 | val in_req_bits = HoldUnless(io.in.req.bits, state === s_idle) 25 | val in_vaddr = in_req_bits.addr.asTypeOf(new Sv39VirtAddr) 26 | 27 | // access TLB 28 | val tlb = Module(new TLB) 29 | tlb.io.prv := io.prv 30 | tlb.io.satp_asid := io.satp_asid 31 | tlb.io.sfence_vma := io.sfence_vma 32 | tlb.io.vaddr := io.in.req.bits.addr.asTypeOf(new Sv39VirtAddr) 33 | val tlb_pte = tlb.io.rpte 34 | val tlb_level = tlb.io.rlevel 35 | 36 | // address translation & protection enable 37 | // need to record state when input fires, in case satp changes when accessing page table 38 | val atp_en = HoldUnless((io.prv =/= PRV.M.U) && io.sv39_en, state === s_idle) 39 | 40 | // check in port request address range (only check paddr) 41 | val in_addr = io.in.req.bits.addr 42 | val in_addr_invalid = (in_addr < "h00010000".U) 43 | val access_fault = (in_addr(31) === 0.U) && (io.prv === PRV.M.U || !atp_en) && in_addr_invalid 44 | 45 | // page table walk 46 | val ptw_level = RegInit(0.U(2.W)) 47 | val ptw_pte = io.ptw.resp.bits.rdata.asTypeOf(new Sv39PTE) 48 | val ptw_pte_reg = RegEnable(ptw_pte, 0.U.asTypeOf(new Sv39PTE), io.ptw.resp.fire) 49 | val ptw_complete = !ptw_pte.flag.v || (!ptw_pte.flag.r && ptw_pte.flag.w) || isLeaf(ptw_pte) || (ptw_level === 0.U) 50 | val page_fault = WireDefault(false.B) 51 | 52 | switch(state) { 53 | is(s_idle) { 54 | when(io.in.req.fire) { 55 | when(atp_en && !tlb.io.hit) { 56 | state := s_ptw_req 57 | } 58 | } 59 | when(io.in.req.valid) { 60 | when(!atp_en && access_fault) { 61 | state := s_access_fault 62 | } 63 | } 64 | ptw_level := (pageTableLevels - 1).U 65 | } 66 | is(s_ptw_req) { 67 | when(io.ptw.req.fire) { 68 | state := s_ptw_resp 69 | } 70 | } 71 | is(s_ptw_resp) { 72 | when(io.ptw.resp.fire) { 73 | when(ptw_complete) { 74 | state := s_ptw_complete 75 | }.otherwise { 76 | state := s_ptw_req 77 | ptw_level := ptw_level - 1.U 78 | } 79 | } 80 | } 81 | is(s_ptw_complete) { // need one more cycle to avoid comb loop 82 | when(io.out.req.fire || page_fault) { 83 | state := s_idle 84 | } 85 | } 86 | is(s_access_fault) { 87 | when(io.in.resp.fire) { 88 | state := s_idle 89 | } 90 | } 91 | } 92 | 93 | // ptw address to access page table 94 | val l2_addr = Wire(UInt(paddrLen.W)) 95 | val l1_addr = Wire(UInt(paddrLen.W)) 96 | val l0_addr = Wire(UInt(paddrLen.W)) 97 | 98 | l2_addr := Cat(io.satp_ppn, in_vaddr.vpn2, 0.U(3.W)) 99 | l1_addr := Cat(ptw_pte_reg.ppn(), in_vaddr.vpn1, 0.U(3.W)) 100 | l0_addr := Cat(ptw_pte_reg.ppn(), in_vaddr.vpn0, 0.U(3.W)) 101 | 102 | // ptw memory access port 103 | io.ptw.req.bits := 0.U.asTypeOf(new CachePortReq) 104 | io.ptw.req.bits.addr := MuxLookup( 105 | ptw_level, 106 | 0.U, 107 | Seq( 108 | 2.U -> l2_addr, 109 | 1.U -> l1_addr, 110 | 0.U -> l0_addr 111 | ) 112 | ) 113 | io.ptw.req.valid := (state === s_ptw_req) 114 | io.ptw.resp.ready := (state === s_ptw_resp) 115 | 116 | val prv = HoldUnless(io.prv, state === s_idle) 117 | val pte = Mux(state === s_idle, tlb_pte, ptw_pte_reg) 118 | val level = Mux(state === s_idle, tlb_level, ptw_level) 119 | 120 | // TLB refill 121 | tlb.io.wen := (state === s_ptw_complete) && !page_fault 122 | tlb.io.wvaddr := in_vaddr 123 | tlb.io.wpte := ptw_pte_reg 124 | tlb.io.wlevel := ptw_level 125 | 126 | // check page fault for pte 127 | val pf1 = WireDefault(false.B) 128 | val pf2 = WireDefault(false.B) 129 | val pf3 = WireDefault(false.B) 130 | val pf4 = WireDefault(false.B) 131 | val pf5 = WireDefault(false.B) 132 | when(!pte.flag.v || (!pte.flag.r && pte.flag.w)) { 133 | pf1 := true.B 134 | } 135 | when(isLeaf(pte)) { 136 | when(!pte.flag.a) { 137 | pf2 := true.B 138 | } 139 | when(prv === PRV.U.U && !pte.flag.u) { 140 | pf3 := true.B 141 | } 142 | if (p(IsITLB)) { 143 | when(!pte.flag.x) { 144 | pf4 := true.B 145 | } 146 | } 147 | if (p(IsDTLB)) { 148 | when(in_req_bits.wen && (!pte.flag.w || !pte.flag.r || !pte.flag.d)) { 149 | pf4 := true.B 150 | } 151 | } 152 | when(state === s_ptw_complete) { 153 | when((ptw_level === 2.U && Cat(pte.ppn1, pte.ppn0) =/= 0.U) || (ptw_level === 1.U && pte.ppn0 =/= 0.U)) { 154 | pf5 := true.B // misaligned superpage, can only occur during PTW 155 | } 156 | } 157 | } 158 | page_fault := pf1 || pf2 || pf3 || pf4 || pf5 159 | 160 | // physical address for out req port 161 | val paddr = Wire(new Sv39PhysAddr) 162 | paddr.offset := in_vaddr.offset 163 | paddr.ppn0 := Mux(level > 0.U, in_vaddr.vpn0, pte.ppn0) 164 | paddr.ppn1 := Mux(level > 1.U, in_vaddr.vpn1, pte.ppn1) 165 | paddr.ppn2 := pte.ppn2 166 | 167 | // forward in req port to out req port 168 | io.in.req.ready := (state === s_idle) && (io.out.req.ready || access_fault || (atp_en && (!tlb.io.hit || page_fault))) 169 | io.out.req.valid := ((state === s_idle) && ((tlb.io.hit && !page_fault) || (!atp_en && !access_fault)) && io.in.req.valid) || 170 | (state === s_ptw_complete && !page_fault) 171 | io.out.req.bits := in_req_bits 172 | when(atp_en) { 173 | io.out.req.bits.addr := paddr.asTypeOf(UInt(vaddrLen.W)) 174 | } 175 | 176 | // forward out resp port to in resp port 177 | val page_fault_reg = BoolStopWatch( 178 | page_fault && atp_en && ((state === s_idle && tlb.io.hit && io.in.req.fire) || state === s_ptw_complete), 179 | io.in.resp.fire 180 | ) 181 | io.in.resp.bits := io.out.resp.bits 182 | io.in.resp.bits.page_fault := page_fault_reg 183 | io.in.resp.bits.access_fault := (state === s_access_fault) 184 | io.in.resp.bits.paddr := HoldUnless(io.out.req.bits.addr, io.out.req.fire) 185 | io.in.resp.valid := io.out.resp.valid || io.in.resp.bits.page_fault || io.in.resp.bits.access_fault 186 | io.out.resp.ready := io.in.resp.ready 187 | 188 | val debug_name = if (p(IsITLB)) "IProxy" else "DProxy" 189 | if (debugPortProxy) { 190 | when(io.in.req.fire) { 191 | printf(cf"${DebugTimer()} [$debug_name] [in -req ] ${io.in.req.bits}\n") 192 | } 193 | when(io.ptw.req.fire) { 194 | printf(cf"${DebugTimer()} [$debug_name] [ptw-req ] ${io.ptw.req.bits}\n") 195 | } 196 | when(io.out.req.fire) { 197 | printf(cf"${DebugTimer()} [$debug_name] [out-req ] ${io.out.req.bits}\n") 198 | } 199 | when(io.in.resp.fire) { 200 | printf(cf"${DebugTimer()} [$debug_name] [in -resp] ${io.in.resp.bits}\n") 201 | } 202 | when(io.ptw.resp.fire) { 203 | printf(cf"${DebugTimer()} [$debug_name] [ptw-resp] ${io.ptw.resp.bits}\n") 204 | } 205 | when(io.out.resp.fire) { 206 | printf(cf"${DebugTimer()} [$debug_name] [out-resp] ${io.out.resp.bits}\n") 207 | } 208 | } 209 | 210 | if (debugTLB) { 211 | when(tlb.io.wen) { 212 | printf(cf"${DebugTimer()} [$debug_name] [tlb-w] vaddr=$in_vaddr pte=$ptw_pte_reg level=$ptw_level\n") 213 | } 214 | when(io.in.req.fire && atp_en) { 215 | printf( 216 | cf"${DebugTimer()} [$debug_name] [tlb-r] vaddr=${tlb.io.vaddr} pte=$tlb_pte level=$tlb_level hit=${tlb.io.hit} pf=$page_fault\n" 217 | ) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/main/scala/CherrySpringsConfig.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.subsystem._ 4 | import org.chipsalliance.cde.config._ 5 | 6 | case object HartID extends Field[Int](0) 7 | case object ResetPC extends Field[BigInt] 8 | case object BootROMImage extends Field[String] 9 | case object CacheNumSets extends Field[Int] 10 | case object EnableBPU extends Field[Boolean] 11 | case object PHTSize extends Field[Int] 12 | case object BTBSize extends Field[Int] 13 | 14 | class CoreConfig 15 | extends Config((site, here, up) => { 16 | case ResetPC => BigInt("00010000", radix = 16) 17 | case BootROMImage => "./bootrom/bootrom.img" 18 | case CacheNumSets => 512 // 16 KB L1 cache 19 | case EnableBPU => true 20 | case PHTSize => 512 21 | case BTBSize => 16 22 | }) 23 | 24 | case object NumHarts extends Field[Int] 25 | case object FpgaTimerFreq extends Field[Int] 26 | case object L2CacheNumSets extends Field[Int] 27 | case object L2CacheNumWays extends Field[Int] 28 | 29 | class SystemConfig 30 | extends Config((site, here, up) => { 31 | case FpgaTimerFreq => 1 // suppose 100 MHz FPGA frequency => 100 / 1 = 100 MHz timer frequency 32 | case L2CacheNumSets => 1024 33 | case L2CacheNumWays => 4 34 | }) 35 | 36 | case object EnableDifftest extends Field[Boolean](false) 37 | 38 | class EnableDifftestConfig 39 | extends Config((site, here, up) => { 40 | case EnableDifftest => true 41 | }) 42 | 43 | case object EnableSerdes extends Field[Boolean](false) 44 | 45 | class EnableSerdesConfig 46 | extends Config((site, here, up) => { 47 | case EnableSerdes => true 48 | }) 49 | 50 | class FastSimulationConfig extends Config(new CoreConfig ++ new SystemConfig ++ new EnableDifftestConfig) 51 | 52 | class SlowSimulationConfig 53 | extends Config(new CoreConfig ++ new SystemConfig ++ new EnableDifftestConfig ++ new EnableSerdesConfig) 54 | 55 | class SynthesisConfig 56 | extends Config(new CoreConfig ++ new SystemConfig ++ new EnableSerdesConfig ++ new WithoutTLMonitors) 57 | 58 | trait HasCherrySpringsParameters { 59 | implicit val p: Parameters 60 | def numHarts: Int = p(NumHarts) 61 | def hartID: Int = p(HartID) 62 | def resetPC: BigInt = p(ResetPC) 63 | def bootROMImage: String = p(BootROMImage) 64 | def xLen: Int = 64 65 | def paddrLen: Int = 32 66 | def vaddrLen: Int = 39 67 | def sourceRange: Int = 4 68 | def cacheNumSets: Int = p(CacheNumSets) 69 | def phtSize: Int = p(PHTSize) 70 | def enableBPU: Boolean = p(EnableBPU) 71 | def btbSize: Int = p(BTBSize) 72 | def enableDifftest: Boolean = p(EnableDifftest) 73 | def ghrLen: Int = log2Up(phtSize) 74 | def fpgaTimerFreq: Int = p(FpgaTimerFreq) 75 | def l2cacheNumSets: Int = p(L2CacheNumSets) 76 | def l2cacheNumWays: Int = p(L2CacheNumWays) 77 | def tlSourceBits: Int = log2Up(3 * sourceRange) 78 | def tlSerWidth: Int = 8 79 | def enableSerdes: Boolean = p(EnableSerdes) 80 | def debugPCTrace: Boolean = false 81 | def debugInstrFetch: Boolean = false 82 | def debugInstrCommit: Boolean = false 83 | def debugICache: Boolean = false 84 | def debugDCache: Boolean = false 85 | def debugUncache: Boolean = false 86 | def debugPortProxy: Boolean = false 87 | def debugTLB: Boolean = false 88 | def debugInterrupt: Boolean = false 89 | def debugBus: Boolean = false 90 | } 91 | 92 | abstract class CherrySpringsModule(implicit val p: Parameters) extends Module with HasCherrySpringsParameters 93 | 94 | class ParameterizedBundle(implicit p: Parameters) extends Bundle 95 | 96 | abstract class CherrySpringsBundle(implicit val p: Parameters) 97 | extends ParameterizedBundle()(p) 98 | with HasCherrySpringsParameters 99 | -------------------------------------------------------------------------------- /src/main/scala/Constant.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | 3 | trait Constant { 4 | val Y = "1" 5 | val N = "0" 6 | 7 | /* 8 | * Note: 9 | * - Some exceptions are generated in LSU (4, 5, 6, 7, 13, 15) 10 | * - Breakpoint (3) not implemented yet 11 | */ 12 | val EXC_X = "???" 13 | val EXC_N = "000" 14 | val EXC_IAM = "001" // Instruction address misaligned (0) 15 | val EXC_IAF = "010" // Instruction access fault (1) 16 | val EXC_IPF = "011" // Instruction page fault (12) 17 | val EXC_II = "100" // Illegal instruction (2) 18 | val EXC_EC = "101" // Environment call from U/S/M-mode (8, 9, 11) 19 | val EXC_EB = "111" // Breakpoint (3) 20 | 21 | val FU_X = "???" 22 | val FU_ALU = "000" 23 | val FU_JMP = "001" 24 | val FU_MDU = "010" 25 | val FU_LSU = "011" 26 | val FU_CSR = "100" 27 | val FU_SYS = "101" 28 | 29 | val ALU_X = "????" 30 | val ALU_ADD = "0000" 31 | val ALU_SLL = "0001" 32 | val ALU_SEQ = "0010" 33 | val ALU_SNE = "0011" 34 | val ALU_XOR = "0100" 35 | val ALU_SRL = "0101" 36 | val ALU_OR = "0110" 37 | val ALU_AND = "0111" 38 | val ALU_SUB = "1010" 39 | val ALU_SRA = "1011" 40 | val ALU_SLT = "1100" 41 | val ALU_SGE = "1101" 42 | val ALU_SLTU = "1110" 43 | val ALU_SGEU = "1111" 44 | 45 | val JMP_X = "??" 46 | val JMP_N = "00" 47 | val JMP_BR = "01" 48 | val JMP_JAL = "10" 49 | val JMP_JALR = "11" 50 | 51 | def isJalJalr(x: UInt): Bool = x(1).asBool 52 | def isBr(x: UInt): Bool = !x(1).asBool && x(0).asBool 53 | 54 | val MDU_X = "????" 55 | val MDU_MUL = "0000" 56 | val MDU_MULH = "0001" 57 | val MDU_MULHSU = "0010" 58 | val MDU_MULHU = "0011" 59 | val MDU_DIV = "0100" 60 | val MDU_DIVU = "0101" 61 | val MDU_REM = "0110" 62 | val MDU_REMU = "0111" 63 | 64 | val LSU_X = "?????" 65 | val LSU_N = "00000" 66 | val LSU_ST = "00001" 67 | val LSU_LD = "00010" 68 | val LSU_LDU = "00110" 69 | val LSU_LR = "01010" 70 | val LSU_SC = "01001" 71 | val LSU_AMOMIN = "10000" 72 | val LSU_AMOMAX = "10001" 73 | val LSU_AMOMINU = "10010" 74 | val LSU_AMOMAXU = "10011" 75 | val LSU_AMOADD = "10100" 76 | val LSU_AMOXOR = "11000" 77 | val LSU_AMOOR = "11001" 78 | val LSU_AMOAND = "11010" 79 | val LSU_AMOSWAP = "11011" 80 | 81 | def isAmo(x: UInt): Bool = x(4).asBool 82 | def isStore(x: UInt): Bool = !x(4).asBool && x(0).asBool 83 | def isLoad(x: UInt): Bool = !x(4).asBool && x(1).asBool 84 | def isLdu(x: UInt): Bool = isLoad(x) && x(2).asBool 85 | def isLrSc(x: UInt): Bool = !x(4).asBool && x(3).asBool 86 | 87 | val MEM_X = "??" 88 | val MEM_BYTE = "00" 89 | val MEM_HALF = "01" 90 | val MEM_WORD = "10" 91 | val MEM_DWORD = "11" 92 | 93 | val CSR_X = "??" 94 | val CSR_N = "00" 95 | val CSR_RW = "01" 96 | val CSR_RS = "10" 97 | val CSR_RC = "11" 98 | 99 | val SYS_X = "???" 100 | val SYS_N = "000" 101 | val SYS_MRET = "001" 102 | val SYS_SRET = "010" 103 | val SYS_FENCE = "100" 104 | val SYS_FENCEI = "101" 105 | val SYS_SFV = "110" // sfence.vma 106 | 107 | val RS_X = "??" 108 | val RS_ZERO = "00" 109 | val RS_PC = "01" 110 | val RS_RF = "10" 111 | val RS_IMM = "11" 112 | 113 | val IMM_X = "???" 114 | val IMM_I = "000" 115 | val IMM_S = "001" 116 | val IMM_B = "010" 117 | val IMM_U = "011" 118 | val IMM_J = "100" 119 | val IMM_Z = "101" 120 | } 121 | 122 | object Constant extends Constant {} 123 | -------------------------------------------------------------------------------- /src/main/scala/Core.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.rocket.CSRs 5 | import org.chipsalliance.cde.config._ 6 | import Constant._ 7 | 8 | class Core(implicit p: Parameters) extends CherrySpringsModule { 9 | val io = IO(new Bundle { 10 | val imem = new CachePortIO 11 | val dmem = new CachePortIO 12 | val iptw = new CachePortIO 13 | val dptw = new CachePortIO 14 | val uncache = new CachePortIO 15 | val fence_i = Output(Bool()) 16 | val intr = Input(new ExternalInterrupt) 17 | }) 18 | 19 | val prv = Wire(UInt(2.W)) 20 | val sv39_en = Wire(Bool()) 21 | val satp_asid = Wire(UInt(16.W)) 22 | val satp_ppn = Wire(UInt(44.W)) 23 | val sfence_vma = Wire(Bool()) 24 | 25 | /* ----- Stage 1 - Instruction Fetch (IF) -------- */ 26 | 27 | val ifu = Module(new IFU) 28 | val imem_proxy = Module(new CachePortProxy()(p.alterPartial({ 29 | case IsITLB => true 30 | case IsDTLB => false 31 | }))) 32 | imem_proxy.io.in <> ifu.io.imem 33 | imem_proxy.io.out <> io.imem 34 | imem_proxy.io.ptw <> io.iptw 35 | imem_proxy.io.prv := prv 36 | imem_proxy.io.sv39_en := sv39_en 37 | imem_proxy.io.satp_asid := satp_asid 38 | imem_proxy.io.satp_ppn := satp_ppn 39 | imem_proxy.io.sfence_vma := sfence_vma 40 | 41 | val alu_jmp_packet = Wire(new JmpPacket) // from EX stage 42 | val sys_jmp_packet = Wire(new JmpPacket) // from MEM stage 43 | ifu.io.jmp_packet.valid := alu_jmp_packet.valid || sys_jmp_packet.valid 44 | ifu.io.jmp_packet.target := Mux(sys_jmp_packet.valid, sys_jmp_packet.target, alu_jmp_packet.target) 45 | ifu.io.jmp_packet.bp_update := alu_jmp_packet.bp_update 46 | ifu.io.jmp_packet.bp_taken := alu_jmp_packet.bp_taken 47 | ifu.io.jmp_packet.bp_pc := alu_jmp_packet.bp_pc 48 | 49 | /* ----- Stage 2 - Instruction Buffer (IB) ------- */ 50 | 51 | val stall_b = Wire(Bool()) 52 | val flush = Wire(Bool()) 53 | 54 | val instr_buffer = Module(new Queue(new FDPacket, entries = 4, pipe = true, hasFlush = true)) 55 | ifu.io.stall_b := instr_buffer.io.enq.ready 56 | instr_buffer.io.enq.bits := ifu.io.out 57 | instr_buffer.io.enq.valid := ifu.io.out.valid 58 | instr_buffer.io.deq.ready := stall_b 59 | instr_buffer.io.flush.get := flush 60 | 61 | /* ----- Stage 3 - Instruction Decode (ID) ------- */ 62 | 63 | val decode = Module(new Decode) 64 | decode.io.in := instr_buffer.io.deq.bits 65 | decode.io.in.valid := instr_buffer.io.deq.fire 66 | 67 | val rf = Module(new RegFile) 68 | rf.io.rs1_index := decode.io.out.rs1_index 69 | rf.io.rs2_index := decode.io.out.rs2_index 70 | 71 | val id_rs1_data = Wire(UInt(xLen.W)) 72 | val id_rs2_data = Wire(UInt(xLen.W)) 73 | val id_rs2_data_from_rf = Wire(UInt(xLen.W)) 74 | val id_ex = Module(new PipelineReg(new DXPacket)) 75 | id_ex.io.in.uop := decode.io.out 76 | id_ex.io.in.rs1_data := id_rs1_data 77 | id_ex.io.in.rs2_data := id_rs2_data 78 | id_ex.io.in.rs2_data_from_rf := id_rs2_data_from_rf 79 | id_ex.io.in.bp_npc := instr_buffer.io.deq.bits.bp_npc 80 | id_ex.io.en := stall_b 81 | id_ex.io.flush := flush 82 | 83 | /* ----- Stage 4 - Execution (EX) ---------------- */ 84 | 85 | val alu = Module(new ALU) 86 | alu.io.uop := id_ex.io.out.uop 87 | alu.io.in1 := id_ex.io.out.rs1_data 88 | alu.io.in2 := id_ex.io.out.rs2_data 89 | 90 | alu_jmp_packet.valid := alu_jmp_packet.bp_update && (alu_jmp_packet.target =/= id_ex.io.out.bp_npc) 91 | alu_jmp_packet.target := Mux( 92 | isBr(alu.io.uop.jmp_op), 93 | Mux(alu.io.cmp_out, id_ex.io.out.uop.pc + SignExt32_64(id_ex.io.out.uop.imm), id_ex.io.out.uop.npc), 94 | alu.io.adder_out 95 | ) 96 | alu_jmp_packet.bp_update := id_ex.io.out.uop.valid && (alu.io.uop.jmp_op =/= s"b$JMP_N".U) 97 | alu_jmp_packet.bp_taken := Mux(isBr(alu.io.uop.jmp_op), alu.io.cmp_out, isJalJalr(alu.io.uop.jmp_op)) 98 | alu_jmp_packet.bp_pc := id_ex.io.out.uop.pc 99 | 100 | val alu_br_out = Mux(isJalJalr(id_ex.io.out.uop.jmp_op), id_ex.io.out.uop.npc, alu.io.out) 101 | 102 | val is_mem = (id_ex.io.out.uop.fu === s"b$FU_LSU".U) && id_ex.io.out.uop.valid 103 | val is_mdu = (id_ex.io.out.uop.fu === s"b$FU_MDU".U) && id_ex.io.out.uop.valid 104 | val is_csr = (id_ex.io.out.uop.fu === s"b$FU_CSR".U) && id_ex.io.out.uop.valid 105 | val is_store = isStore(id_ex.io.out.uop.lsu_op) 106 | val is_amo = isAmo(id_ex.io.out.uop.lsu_op) 107 | 108 | val lsu = Module(new LSU) 109 | lsu.io.uop := id_ex.io.out.uop 110 | lsu.io.is_mem := is_mem 111 | lsu.io.is_store := is_store 112 | lsu.io.is_amo := is_amo 113 | lsu.io.addr := alu_br_out 114 | lsu.io.wdata := id_ex.io.out.rs2_data_from_rf 115 | 116 | val mdu = Module(new MDU) 117 | mdu.io.uop := id_ex.io.out.uop 118 | mdu.io.is_mdu := is_mdu 119 | mdu.io.in1 := id_ex.io.out.rs1_data 120 | mdu.io.in2 := id_ex.io.out.rs2_data_from_rf 121 | 122 | val csr = Module(new CSR) 123 | csr.io.uop := id_ex.io.out.uop 124 | csr.io.rw.addr := id_ex.io.out.uop.instr(31, 20) 125 | csr.io.rw.cmd := id_ex.io.out.uop.csr_op 126 | csr.io.rw.wdata := id_ex.io.out.rs1_data 127 | sys_jmp_packet := csr.io.jmp_packet 128 | prv := csr.io.prv 129 | sv39_en := csr.io.sv39_en 130 | satp_asid := csr.io.satp_asid 131 | satp_ppn := csr.io.satp_ppn 132 | sfence_vma := csr.io.sfence_vma 133 | csr.io.lsu_addr := lsu.io.addr 134 | csr.io.lsu_exc_code := lsu.io.exc_code 135 | csr.io.interrupt := io.intr 136 | io.fence_i := csr.io.fence_i 137 | 138 | // difftest hartid 139 | rf.io.hartid := csr.io.hartid 140 | lsu.io.hartid := csr.io.hartid 141 | 142 | val dmem_proxy = Module(new CachePortProxy()(p.alterPartial({ 143 | case IsITLB => false 144 | case IsDTLB => true 145 | }))) 146 | dmem_proxy.io.prv := Mux(csr.io.mprv, csr.io.mpp, prv) 147 | dmem_proxy.io.sv39_en := sv39_en 148 | dmem_proxy.io.satp_asid := satp_asid 149 | dmem_proxy.io.satp_ppn := satp_ppn 150 | dmem_proxy.io.sfence_vma := sfence_vma 151 | 152 | /* 153 | * Data bus layout 154 | * 155 | * +------------+ +---------+ 156 | * +-----+ | | | | <--> dmem 157 | * | | | | <--> | c2_xbar | 158 | * | lsu | <--> | dmem_proxy | | | <--> uncache 159 | * | | | | +---------+ 160 | * +-----+ | | <-------------------> dptw 161 | * +------------+ 162 | */ 163 | 164 | val c2_xbar = Module(new CachePortXBar1to2) 165 | dmem_proxy.io.in <> lsu.io.dmem 166 | io.dptw <> dmem_proxy.io.ptw 167 | c2_xbar.io.in <> dmem_proxy.io.out 168 | c2_xbar.io.to_1 := !dmem_proxy.io.out.req.bits.addr(paddrLen - 1).asBool 169 | io.dmem <> c2_xbar.io.out(0) // to data cache 170 | io.uncache <> c2_xbar.io.out(1) // to uncache 171 | 172 | val ex_wb = Module(new PipelineReg(new XWPacket)) 173 | ex_wb.io.in.uop := id_ex.io.out.uop 174 | ex_wb.io.in.uop.valid := ( 175 | (is_mem && lsu.io.valid && (lsu.io.exc_code === 0.U)) || 176 | (is_mdu && mdu.io.valid) || 177 | (is_csr && csr.io.rw.valid) || 178 | (!is_mem && !is_mdu && !is_csr && id_ex.io.out.uop.valid) 179 | ) && !csr.io.is_int 180 | ex_wb.io.in.rd_data := MuxLookup( 181 | id_ex.io.out.uop.fu, 182 | alu_br_out, 183 | Seq( 184 | s"b$FU_LSU".U -> lsu.io.rdata, 185 | s"b$FU_MDU".U -> mdu.io.out, 186 | s"b$FU_CSR".U -> csr.io.rw.rdata 187 | ) 188 | ) 189 | ex_wb.io.in.is_mmio := lsu.io.is_mmio 190 | ex_wb.io.en := true.B 191 | ex_wb.io.flush := false.B 192 | 193 | /* ----- Stage 5 - Write Back (WB) --------------- */ 194 | 195 | val commit_uop = ex_wb.io.out.uop 196 | rf.io.rd_wen := commit_uop.valid && commit_uop.rd_wen 197 | rf.io.rd_index := commit_uop.rd_index 198 | rf.io.rd_data := ex_wb.io.out.rd_data 199 | 200 | /* ----- Forwarding Unit ------------------------- */ 201 | 202 | val need_rs1 = decode.io.out.rs1_src === s"b$RS_RF".U 203 | val need_rs2 = decode.io.out.rs2_src === s"b$RS_RF".U 204 | val need_rs2_from_rf = 205 | isStore(decode.io.out.lsu_op) || isAmo(decode.io.out.lsu_op) || decode.io.out.fu === s"b$FU_MDU".U 206 | 207 | when( 208 | need_rs1 && id_ex.io.out.uop.rd_wen 209 | && decode.io.out.rs1_index === id_ex.io.out.uop.rd_index 210 | && decode.io.out.rs1_index =/= 0.U 211 | ) { 212 | id_rs1_data := ex_wb.io.in.rd_data 213 | }.otherwise { 214 | id_rs1_data := MuxLookup( 215 | decode.io.out.rs1_src, 216 | 0.U, 217 | Seq( 218 | s"b$RS_PC".U -> ZeroExt32_64(instr_buffer.io.deq.bits.pc), 219 | s"b$RS_RF".U -> rf.io.rs1_data, 220 | s"b$RS_IMM".U -> SignExt32_64(decode.io.out.imm) 221 | ) 222 | ) 223 | } 224 | 225 | when( 226 | need_rs2 && id_ex.io.out.uop.rd_wen 227 | && decode.io.out.rs2_index === id_ex.io.out.uop.rd_index 228 | && decode.io.out.rs2_index =/= 0.U 229 | ) { 230 | id_rs2_data := ex_wb.io.in.rd_data 231 | }.otherwise { 232 | id_rs2_data := MuxLookup( 233 | decode.io.out.rs2_src, 234 | 0.U, 235 | Seq( 236 | s"b$RS_PC".U -> ZeroExt32_64(instr_buffer.io.deq.bits.pc), 237 | s"b$RS_RF".U -> rf.io.rs2_data, 238 | s"b$RS_IMM".U -> SignExt32_64(decode.io.out.imm) 239 | ) 240 | ) 241 | } 242 | 243 | when( 244 | need_rs2_from_rf && id_ex.io.out.uop.rd_wen 245 | && decode.io.out.rs2_index === id_ex.io.out.uop.rd_index 246 | && decode.io.out.rs2_index =/= 0.U 247 | ) { 248 | id_rs2_data_from_rf := ex_wb.io.in.rd_data 249 | }.otherwise { 250 | id_rs2_data_from_rf := rf.io.rs2_data 251 | } 252 | 253 | /* ----- Pipeline Control Signals -------------- */ 254 | 255 | stall_b := lsu.io.ready && mdu.io.ready 256 | flush := alu_jmp_packet.valid || sys_jmp_packet.valid 257 | 258 | /* ----- Performance Counters ------------------ */ 259 | 260 | val mcycle = csr.io.cycle // machine cycle counter 261 | val minstret = csr.io.instret // machine instructions-retired counter 262 | csr.io.commit := commit_uop.valid.asUInt 263 | 264 | /* ----- Processor Difftest -------------------- */ 265 | 266 | if (enableDifftest) { 267 | val commit_is_csr = commit_uop.valid && (commit_uop.fu === s"b$FU_CSR".U) 268 | val commit_csr = commit_uop.instr(31, 20) 269 | val commit_cycle = commit_is_csr && (commit_csr === CSRs.mcycle.U || commit_csr === CSRs.cycle.U) 270 | val commit_time = commit_is_csr && (commit_csr === CSRs.time.U) 271 | val commit_tset = commit_is_csr && (commit_csr === "h350".U || commit_csr === "h351".U) 272 | val commit_instret = commit_is_csr && (commit_csr === CSRs.minstret.U || commit_csr === CSRs.instret.U) 273 | val commit_skip = ex_wb.io.out.is_mmio || commit_cycle || commit_time || commit_tset || commit_instret 274 | 275 | val diff_ic = Module(new DifftestInstrCommit) 276 | diff_ic.io.clock := clock 277 | diff_ic.io.coreid := csr.io.hartid 278 | diff_ic.io.index := 0.U 279 | diff_ic.io.pc := commit_uop.pc 280 | diff_ic.io.instr := commit_uop.instr 281 | diff_ic.io.valid := commit_uop.valid 282 | diff_ic.io.special := false.B 283 | diff_ic.io.skip := commit_skip 284 | diff_ic.io.isRVC := false.B 285 | diff_ic.io.rfwen := commit_uop.rd_wen 286 | diff_ic.io.fpwen := false.B 287 | diff_ic.io.wpdest := commit_uop.rd_index 288 | diff_ic.io.wdest := commit_uop.rd_index 289 | diff_ic.io.robIdx := 0.U 290 | diff_ic.io.lqIdx := 0.U 291 | diff_ic.io.sqIdx := 0.U 292 | diff_ic.io.isLoad := (commit_uop.lsu_op === s"b$LSU_LD".U) || (commit_uop.lsu_op === s"b$LSU_LDU".U) 293 | diff_ic.io.isStore := (commit_uop.lsu_op === s"b$LSU_ST".U) 294 | if (debugInstrCommit) { 295 | when(commit_uop.valid) { 296 | printf(cf"${DebugTimer()} [COMMIT] pc=${commit_uop.pc}%x instr=${commit_uop.instr}%x prv=$prv\n") 297 | } 298 | } 299 | if (debugPCTrace) { 300 | when(commit_uop.valid) { 301 | printf(cf"${commit_uop.pc}%x\n") 302 | } 303 | } 304 | 305 | val diff_wb = Module(new DifftestIntWriteback) 306 | diff_wb.io.clock := clock 307 | diff_wb.io.coreid := csr.io.hartid 308 | diff_wb.io.valid := commit_uop.valid && commit_uop.rd_wen 309 | diff_wb.io.dest := commit_uop.rd_index 310 | diff_wb.io.data := ex_wb.io.out.rd_data 311 | 312 | val trap = (commit_uop.instr === HALT()) && commit_uop.valid 313 | val rf_a0 = rf.io.rf_a0 314 | 315 | val diff_te = Module(new DifftestTrapEvent) 316 | diff_te.io.clock := clock 317 | diff_te.io.coreid := csr.io.hartid 318 | diff_te.io.valid := trap 319 | diff_te.io.cycleCnt := mcycle 320 | diff_te.io.instrCnt := minstret 321 | diff_te.io.hasWFI := false.B 322 | diff_te.io.code := rf_a0(2, 0) 323 | diff_te.io.pc := commit_uop.pc 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/main/scala/DCache.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.diplomacy._ 4 | import freechips.rocketchip.tilelink._ 5 | import org.chipsalliance.cde.config._ 6 | import Constant._ 7 | 8 | class DCache(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 9 | require(xLen == 64) 10 | 11 | val node = TLClientNode( 12 | Seq( 13 | TLMasterPortParameters.v1( 14 | clients = Seq( 15 | TLMasterParameters.v1( 16 | name = s"dcache-${hartID}", 17 | sourceId = IdRange(0, sourceRange), 18 | supportsProbe = TransferSizes(32) 19 | ) 20 | ) 21 | ) 22 | ) 23 | ) 24 | 25 | lazy val module = new DCacheModule(this) 26 | } 27 | 28 | class DCacheModule(outer: DCache) extends LazyModuleImp(outer) with HasCherrySpringsParameters { 29 | val io = IO(new Bundle { 30 | val cache = Flipped(new CachePortIO) 31 | }) 32 | 33 | val (tl, edge) = outer.node.out.head 34 | val req = io.cache.req 35 | val resp = io.cache.resp 36 | 37 | // latch input channels 38 | val tl_b_bits_r = RegEnable(tl.b.bits, 0.U.asTypeOf(tl.b.bits.cloneType), tl.b.fire) 39 | val tl_d_bits_r = RegEnable(tl.d.bits, 0.U.asTypeOf(tl.d.bits.cloneType), tl.d.fire) 40 | val req_r = RegEnable(req.bits, 0.U.asTypeOf(req.bits.cloneType), req.fire) 41 | 42 | /* 43 | * Data cache (PIPT) organization 44 | * #Sets = cacheNumSets 45 | * Associativity = 1 46 | * Cacheline = 4 x 64 bits 47 | * Write policy = write allocate + write back 48 | */ 49 | 50 | val tagWidth = paddrLen - (5 + log2Up(cacheNumSets)) 51 | def getOffset(x: UInt) = x(4, 3) 52 | def getIndex(x: UInt) = x(4 + log2Up(cacheNumSets), 5) 53 | def getTag(x: UInt) = x(paddrLen - 1, 5 + log2Up(cacheNumSets)) 54 | 55 | val array = Module(new SRAM(depth = cacheNumSets, dw = (new CacheEntry).len())) 56 | val valid = RegInit(VecInit(Seq.fill(cacheNumSets)(false.B))) 57 | 58 | // FSM 59 | val s_init :: s_check :: s_release :: s_release_ack :: s_acquire :: s_grant :: s_grant_ack :: s_ok :: Nil = Enum(8) 60 | val state = RegInit(s_init) 61 | 62 | // array access address (requested by LSU) 63 | val array_addr = Wire(UInt(paddrLen.W)) 64 | array_addr := Mux(req.fire, req.bits.addr, req_r.addr) 65 | 66 | // default input 67 | val array_wdata = Wire(new CacheEntry) 68 | array_wdata.tag := getTag(array_addr) 69 | array_wdata.data := 0.U 70 | array.io.en := req.fire || resp.fire || tl.b.fire 71 | array.io.addr := getIndex(array_addr) 72 | array.io.wdata := array_wdata.asUInt 73 | array.io.wen := false.B 74 | 75 | // array output 76 | val array_out = HoldUnless(array.io.rdata, RegNext(req.fire)).asTypeOf(new CacheEntry) 77 | val array_valid = valid(getIndex(array_addr)) 78 | val array_hit = array_valid && (getTag(array_addr) === array_out.tag) 79 | 80 | // cache read 81 | val rdata_256 = Mux(state === s_ok, tl_d_bits_r.data, array_out.data) 82 | val rdata_64 = MuxLookup( 83 | getOffset(array_addr), 84 | 0.U(64.W), 85 | Seq( 86 | 0.U -> rdata_256(63, 0), 87 | 1.U -> rdata_256(127, 64), 88 | 2.U -> rdata_256(191, 128), 89 | 3.U -> rdata_256(255, 192) 90 | ) 91 | ) 92 | 93 | // amo data 94 | val amo_rdata_raw_64 = Wire(UInt(64.W)) 95 | val amo_wdata_raw_64 = Wire(UInt(64.W)) 96 | val amo_result_64 = Wire(UInt(64.W)) 97 | val amo_wdata_64 = Wire(UInt(64.W)) 98 | val amo_w = (req_r.len === s"b$MEM_WORD".U) 99 | amo_rdata_raw_64 := Mux( 100 | amo_w, 101 | SignExt32_64(Mux(req_r.addr(2).asBool, rdata_64(63, 32), rdata_64(31, 0))), 102 | rdata_64 103 | ) 104 | amo_wdata_raw_64 := Mux( 105 | amo_w, 106 | SignExt32_64(Mux(req_r.addr(2).asBool, req_r.wdata(63, 32), req_r.wdata(31, 0))), 107 | req_r.wdata 108 | ) 109 | amo_result_64 := MuxLookup( 110 | req_r.amo, 111 | 0.U, 112 | Seq( 113 | s"b$LSU_AMOSWAP".U -> amo_wdata_raw_64, 114 | s"b$LSU_AMOADD".U -> (amo_wdata_raw_64 + amo_rdata_raw_64), 115 | s"b$LSU_AMOAND".U -> (amo_wdata_raw_64 & amo_rdata_raw_64), 116 | s"b$LSU_AMOOR".U -> (amo_wdata_raw_64 | amo_rdata_raw_64), 117 | s"b$LSU_AMOXOR".U -> (amo_wdata_raw_64 ^ amo_rdata_raw_64), 118 | s"b$LSU_AMOMAX".U -> Mux(amo_wdata_raw_64.asSInt > amo_rdata_raw_64.asSInt, amo_wdata_raw_64, amo_rdata_raw_64), 119 | s"b$LSU_AMOMAXU".U -> Mux(amo_wdata_raw_64.asUInt > amo_rdata_raw_64.asUInt, amo_wdata_raw_64, amo_rdata_raw_64), 120 | s"b$LSU_AMOMIN".U -> Mux(amo_wdata_raw_64.asSInt < amo_rdata_raw_64.asSInt, amo_wdata_raw_64, amo_rdata_raw_64), 121 | s"b$LSU_AMOMINU".U -> Mux(amo_wdata_raw_64.asUInt < amo_rdata_raw_64.asUInt, amo_wdata_raw_64, amo_rdata_raw_64) 122 | ) 123 | ) 124 | amo_wdata_64 := Mux(amo_w && req_r.addr(2).asBool, Cat(amo_result_64(31, 0), 0.U(32.W)), amo_result_64) 125 | 126 | // lr / sc 127 | val lrsc_reserved = RegInit(false.B) 128 | val lrsc_addr = RegInit(0.U((paddrLen - 5).W)) // reservation set is a 32-byte cacheline 129 | val lrsc_counter = RegInit(0.U(5.W)) 130 | val lrsc_backoff = lrsc_counter(4).asBool 131 | val is_lr_r = req_r.lrsc && !req_r.wen 132 | when(resp.fire && is_lr_r) { 133 | lrsc_reserved := true.B 134 | lrsc_addr := req_r.addr(paddrLen - 1, 5) 135 | } 136 | when(lrsc_reserved && !lrsc_backoff) { 137 | lrsc_counter := lrsc_counter + 1.U 138 | } 139 | when(tl.b.fire && (tl.b.bits.address(paddrLen - 1, 5) === lrsc_addr)) { 140 | lrsc_reserved := false.B 141 | lrsc_counter := 0.U 142 | } 143 | val is_sc = req.bits.lrsc && req.bits.wen 144 | val sc_fail = is_sc && (!lrsc_reserved || (req.bits.addr(paddrLen - 1, 5) =/= lrsc_addr)) 145 | val is_sc_r = req_r.lrsc && req_r.wen 146 | val sc_fail_r = RegEnable(sc_fail, false.B, req.fire) 147 | when(resp.fire && req_r.wen) { 148 | lrsc_reserved := false.B 149 | lrsc_counter := 0.U 150 | } 151 | val sc_rdata_64 = WireDefault(0.U(64.W)) 152 | when(sc_fail_r) { 153 | sc_rdata_64 := 1.U << (req_r.addr(2) << 5) 154 | } 155 | 156 | // write data & mask expanded to 256 bits from input request 157 | val wdata_64 = Mux(req_r.isAmo(), amo_wdata_64, req_r.wdata) 158 | val wdata_256 = Wire(UInt(256.W)) 159 | val wmask_256 = Wire(UInt(256.W)) 160 | wdata_256 := wdata_64 << (getOffset(req_r.addr) << 6) 161 | wmask_256 := MaskExpand(req_r.wmask) << (getOffset(req_r.addr) << 6) 162 | 163 | // cache write 164 | when(resp.fire) { 165 | when(state === s_ok) { // miss & refill (read / write) in last cycle 166 | array_wdata.data := Mux( 167 | req_r.wen, 168 | MaskData(tl_d_bits_r.data, wdata_256, wmask_256), 169 | tl_d_bits_r.data 170 | ) 171 | when(!sc_fail_r) { 172 | array.io.wen := true.B 173 | valid(getIndex(array_addr)) := true.B 174 | } 175 | }.otherwise { // hit & write 176 | array_wdata.data := MaskData(array_out.data, wdata_256, wmask_256) 177 | array.io.wen := req_r.wen && array_hit 178 | } 179 | } 180 | 181 | // probing mode 182 | val probing = BoolStopWatch(tl.b.fire, tl.c.fire, start_high_priority = true) 183 | 184 | switch(state) { 185 | is(s_init) { 186 | when(req.fire) { 187 | state := Mux(sc_fail, s_ok, s_check) 188 | } 189 | } 190 | is(s_check) { 191 | when(resp.fire) { 192 | state := s_init 193 | }.elsewhen(!array_hit) { 194 | state := Mux(array_valid, s_release, s_acquire) 195 | } 196 | } 197 | is(s_release) { 198 | when(!array_valid) { 199 | state := s_acquire 200 | }.elsewhen(tl.c.fire && !probing) { 201 | state := s_release_ack 202 | } 203 | } 204 | is(s_release_ack) { 205 | when(tl.d.fire) { 206 | state := s_acquire 207 | } 208 | } 209 | is(s_acquire) { 210 | when(tl.a.fire) { 211 | state := s_grant 212 | } 213 | } 214 | is(s_grant) { 215 | when(tl.d.fire) { 216 | state := s_grant_ack 217 | } 218 | } 219 | is(s_grant_ack) { 220 | when(tl.e.fire) { 221 | state := s_ok 222 | } 223 | } 224 | is(s_ok) { 225 | when(resp.fire) { 226 | state := s_init 227 | } 228 | } 229 | } 230 | 231 | // probe output 232 | val probe_addr = Mux(tl.b.fire, tl.b.bits.address, tl_b_bits_r.address) 233 | val probe_out = HoldUnless(array.io.rdata, RegNext(tl.b.fire)).asTypeOf(new CacheEntry) 234 | val probe_hit = valid(getIndex(probe_addr)) && (getTag(probe_addr) === probe_out.tag) 235 | when(tl.b.fire) { 236 | array.io.addr := getIndex(probe_addr) 237 | } 238 | 239 | // clear cacheline when being probed and hit 240 | when(probing && tl.c.fire && probe_hit) { 241 | valid(getIndex(probe_addr)) := false.B 242 | } 243 | 244 | // aligned address 245 | val release_addr_aligned = Cat(array_out.tag, RegNext(array.io.addr), 0.U(5.W)) 246 | val req_addr_aligned = Cat(req_r.addr(paddrLen - 1, 5), 0.U(5.W)) 247 | 248 | // send TL burst read request 249 | val source = Counter(tl.a.fire || tl.c.fire, sourceRange)._1 250 | val (_, acquire_bits) = edge.AcquireBlock(source, req_addr_aligned, 5.U, TLPermissions.NtoT) 251 | val probe_ack_bits = edge.ProbeAck(tl_b_bits_r, TLPermissions.NtoN) 252 | val probe_ack_data_bits = edge.ProbeAck(tl_b_bits_r, TLPermissions.TtoN, probe_out.data) 253 | val (_, release_bits) = edge.Release(source, release_addr_aligned, 5.U, TLPermissions.TtoN, array_out.data) 254 | val grant_ack_bits = edge.GrantAck(tl_d_bits_r) 255 | 256 | tl.a.bits := acquire_bits 257 | tl.a.valid := (state === s_acquire) 258 | tl.b.ready := !probing && (!lrsc_reserved || lrsc_backoff) 259 | tl.c.bits := Mux(probing, Mux(probe_hit, probe_ack_data_bits, probe_ack_bits), release_bits) 260 | tl.c.valid := probing || ((state === s_release) && array_valid) 261 | tl.d.ready := (state === s_release_ack) || (state === s_grant) 262 | tl.e.bits := grant_ack_bits 263 | tl.e.valid := (state === s_grant_ack) 264 | 265 | // cache port handshake 266 | req.ready := (state === s_init) && !(probing || tl.b.fire) 267 | resp.valid := ((state === s_check && array_hit) || (state === s_ok)) && !(probing || tl.b.fire) 268 | resp.bits := 0.U.asTypeOf(new CachePortResp) 269 | resp.bits.rdata := Mux(is_sc_r, sc_rdata_64, rdata_64) 270 | resp.bits.wdata := wdata_64 271 | 272 | if (debugDCache) { 273 | when(req.fire) { 274 | printf(cf"${DebugTimer()} [DCache] [in -req ] ${req.bits}\n") 275 | } 276 | when(resp.fire) { 277 | printf(cf"${DebugTimer()} [DCache] [in -resp] ${resp.bits}\n") 278 | } 279 | when(tl.a.fire) { 280 | printf( 281 | cf"${DebugTimer()} [DCache] [out-req ] addr=${tl.a.bits.address}%x " + 282 | cf"wdata=${tl.a.bits.data}%x wmask=${tl.a.bits.mask}%x wen=${!(state === s_acquire)}\n" 283 | ) 284 | } 285 | when(tl.d.fire) { 286 | printf(cf"${DebugTimer()} [DCache] [out-resp] rdata=${tl.d.bits.data}\n") 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/main/scala/DataType.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import org.chipsalliance.cde.config._ 4 | 5 | class FDPacket(implicit p: Parameters) extends CherrySpringsBundle { 6 | val pc = UInt(xLen.W) 7 | val instr = UInt(32.W) 8 | val valid = Bool() 9 | val page_fault = Bool() 10 | val access_fault = Bool() 11 | val bp_npc = UInt(xLen.W) 12 | 13 | override def toPrintable: Printable = { 14 | cf"v=$valid pc=$pc%x instr=$instr%x pf=$page_fault af=$access_fault bp_npc=$bp_npc%x" 15 | } 16 | } 17 | 18 | class DXPacket(implicit p: Parameters) extends CherrySpringsBundle { 19 | val uop = new MicroOp 20 | val rs1_data = UInt(xLen.W) 21 | val rs2_data = UInt(xLen.W) 22 | val rs2_data_from_rf = UInt(xLen.W) 23 | val bp_npc = UInt(xLen.W) 24 | } 25 | 26 | class XWPacket(implicit p: Parameters) extends CherrySpringsBundle { 27 | val uop = new MicroOp 28 | val rd_data = UInt(xLen.W) 29 | val is_mmio = Bool() 30 | } 31 | 32 | class PipelineReg[T <: Bundle](packet: T)(implicit p: Parameters) extends CherrySpringsModule { 33 | val io = IO(new Bundle { 34 | val in = Input(packet) 35 | val out = Output(packet) 36 | val en = Input(Bool()) 37 | val flush = Input(Bool()) 38 | }) 39 | 40 | val reg = RegEnable(Mux(io.flush, 0.U.asTypeOf(packet), io.in), 0.U.asTypeOf(packet), io.en) 41 | io.out := reg 42 | } 43 | 44 | class JmpPacket(implicit p: Parameters) extends CherrySpringsBundle { 45 | val valid = Bool() // need to redirect to target address 46 | val target = UInt(xLen.W) 47 | 48 | // only valid for branch, jal, jalr 49 | val bp_update = Bool() 50 | val bp_taken = Bool() 51 | val bp_pc = UInt(xLen.W) 52 | } 53 | 54 | class ExternalInterrupt(implicit p: Parameters) extends CherrySpringsBundle { 55 | val mtip = Bool() 56 | val msip = Bool() 57 | val meip = Bool() 58 | val seip = Bool() 59 | } 60 | 61 | class Sv39VirtAddr(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 62 | val vpn2 = UInt(vpn2Len.W) 63 | val vpn1 = UInt(vpn1Len.W) 64 | val vpn0 = UInt(vpn0Len.W) 65 | val offset = UInt(offsetLen.W) 66 | 67 | def vpn() = Cat(vpn2, vpn1, vpn0) 68 | def vpn2mb() = Cat(vpn2, vpn1) 69 | def vpn1gb() = vpn2 70 | 71 | override def toPrintable: Printable = { 72 | cf"{vpn2=$vpn2%x vpn1=$vpn1%x vpn0=$vpn0%x offset=$offset%x}" 73 | } 74 | } 75 | 76 | class Sv39PhysAddr(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 77 | val ppn2 = UInt(ppn2Len.W) 78 | val ppn1 = UInt(ppn1Len.W) 79 | val ppn0 = UInt(ppn0Len.W) 80 | val offset = UInt(offsetLen.W) 81 | 82 | def ppn() = Cat(ppn2, ppn1, ppn0) 83 | 84 | override def toPrintable: Printable = { 85 | cf"{ppn2=$ppn2%x ppn1=$ppn1%x ppn0=$ppn0%x offset=$offset%x}" 86 | } 87 | } 88 | 89 | class Sv39PTEFlag(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 90 | val rsw = UInt(2.W) 91 | val d = Bool() 92 | val a = Bool() 93 | val g = Bool() 94 | val u = Bool() 95 | val x = Bool() 96 | val w = Bool() 97 | val r = Bool() 98 | val v = Bool() 99 | 100 | override def toPrintable: Printable = { 101 | cf"$rsw%b$d$a$g$u$x$w$r$v" 102 | } 103 | } 104 | 105 | class Sv39PTE(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 106 | val ppn2 = UInt(ppn2Len.W) 107 | val ppn1 = UInt(ppn1Len.W) 108 | val ppn0 = UInt(ppn0Len.W) 109 | val flag = new Sv39PTEFlag 110 | 111 | def ppn() = Cat(ppn2, ppn1, ppn0) 112 | 113 | override def toPrintable: Printable = { 114 | cf"{ppn2=$ppn2%x ppn1=$ppn1%x ppn0=$ppn0%x flag=$flag}" 115 | } 116 | } 117 | 118 | class TLB4KBEntry(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 119 | val flag = new Sv39PTEFlag 120 | val vpn2 = UInt(vpn2Len.W) 121 | val vpn1 = UInt(vpn1Len.W) 122 | val vpn0 = UInt(vpn0Len.W) 123 | val ppn2 = UInt(ppn2Len.W) 124 | val ppn1 = UInt(ppn1Len.W) 125 | val ppn0 = UInt(ppn0Len.W) 126 | val asid = UInt(16.W) 127 | 128 | def vpn() = Cat(vpn2, vpn1, vpn0) 129 | } 130 | 131 | class TLB2MBEntry(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 132 | val flag = new Sv39PTEFlag 133 | val vpn2 = UInt(vpn2Len.W) 134 | val vpn1 = UInt(vpn1Len.W) 135 | val ppn2 = UInt(ppn2Len.W) 136 | val ppn1 = UInt(ppn1Len.W) 137 | val asid = UInt(16.W) 138 | 139 | def vpn2mb() = Cat(vpn2, vpn1) 140 | } 141 | 142 | class TLB1GBEntry(implicit p: Parameters) extends CherrySpringsBundle with Sv39Parameters { 143 | val flag = new Sv39PTEFlag 144 | val vpn2 = UInt(vpn2Len.W) 145 | val ppn2 = UInt(ppn2Len.W) 146 | val asid = UInt(16.W) 147 | 148 | def vpn1gb() = vpn2 149 | } 150 | 151 | class CacheEntry(implicit p: Parameters) extends CherrySpringsBundle { 152 | val tag = UInt((paddrLen - (5 + log2Up(cacheNumSets))).W) 153 | val data = UInt(256.W) 154 | 155 | def len() = this.getWidth 156 | } 157 | -------------------------------------------------------------------------------- /src/main/scala/Decode.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util.experimental.decode._ 3 | import org.chipsalliance.cde.config._ 4 | import Constant._ 5 | 6 | class Decode(implicit p: Parameters) extends CherrySpringsModule { 7 | val io = IO(new Bundle { 8 | val in = Input(new FDPacket) 9 | val out = Output(new MicroOp) 10 | }) 11 | 12 | val instr = io.in.instr 13 | val uop = WireInit(0.U.asTypeOf(new MicroOp)) 14 | 15 | uop.pc := io.in.pc 16 | uop.npc := io.in.pc + 4.U 17 | uop.instr := instr 18 | uop.rs1_index := instr(19, 15) 19 | uop.rs2_index := instr(24, 20) 20 | uop.rd_index := instr(11, 7) 21 | 22 | val decode_result = decoder(minimizer = EspressoMinimizer, input = instr, truthTable = DecodeTable.decode_table) 23 | 24 | uop.from_decoder(decode_result) 25 | 26 | when(io.in.valid) { 27 | when(io.in.page_fault) { 28 | uop.exc := s"b$EXC_IPF".U 29 | uop.valid := false.B 30 | }.elsewhen(io.in.access_fault) { 31 | uop.exc := s"b$EXC_IAF".U 32 | uop.valid := false.B 33 | } 34 | } 35 | 36 | io.out := Mux(io.in.valid, uop, 0.U.asTypeOf(new MicroOp)) 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/DecodeTable.scala: -------------------------------------------------------------------------------- 1 | import chisel3.util._ 2 | import chisel3.util.experimental.decode._ 3 | import freechips.rocketchip.rocket.Instructions._ 4 | import Constant._ 5 | 6 | object HALT { 7 | def apply() = BitPat("b00000000000000000000000001101011") 8 | } 9 | 10 | object DecodeTable { 11 | val decode_default: String = Seq( 12 | // v exc fu alu_op jmp_op mdu_op lsu_op mem_len csr_op sys_op rs1/2_src rd_wen imm_type dw64 13 | N, EXC_II, FU_ALU, ALU_X, JMP_X, MDU_X, LSU_X, MEM_X, CSR_X, SYS_X, RS_X, RS_X, N, IMM_X, Y 14 | ).reduce(_ + _) 15 | 16 | val decode_table: TruthTable = TruthTable(Map( 17 | // RV64I 18 | LUI -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_ZERO, RS_IMM, Y, IMM_U, Y), 19 | AUIPC -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_PC, RS_IMM, Y, IMM_U, Y), 20 | JAL -> Seq(Y, EXC_N, FU_JMP, ALU_ADD, JMP_JAL, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_PC, RS_IMM, Y, IMM_J, Y), 21 | JALR -> Seq(Y, EXC_N, FU_JMP, ALU_ADD, JMP_JALR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 22 | BEQ -> Seq(Y, EXC_N, FU_JMP, ALU_SEQ, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 23 | BNE -> Seq(Y, EXC_N, FU_JMP, ALU_SNE, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 24 | BLT -> Seq(Y, EXC_N, FU_JMP, ALU_SLT, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 25 | BGE -> Seq(Y, EXC_N, FU_JMP, ALU_SGE, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 26 | BLTU -> Seq(Y, EXC_N, FU_JMP, ALU_SLTU, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 27 | BGEU -> Seq(Y, EXC_N, FU_JMP, ALU_SGEU, JMP_BR, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_B, Y), 28 | LB -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LD, MEM_BYTE, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 29 | LH -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LD, MEM_HALF, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 30 | LW -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LD, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 31 | LD -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LD, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 32 | LBU -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LDU, MEM_BYTE, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 33 | LHU -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LDU, MEM_HALF, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 34 | LWU -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LDU, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 35 | SB -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_ST, MEM_BYTE, CSR_N, SYS_N, RS_RF, RS_IMM, N, IMM_S, Y), 36 | SH -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_ST, MEM_HALF, CSR_N, SYS_N, RS_RF, RS_IMM, N, IMM_S, Y), 37 | SW -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_ST, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_IMM, N, IMM_S, Y), 38 | SD -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_ST, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_IMM, N, IMM_S, Y), 39 | ADDI -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 40 | SLTI -> Seq(Y, EXC_N, FU_ALU, ALU_SLT, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 41 | SLTIU -> Seq(Y, EXC_N, FU_ALU, ALU_SLTU, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 42 | XORI -> Seq(Y, EXC_N, FU_ALU, ALU_XOR, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 43 | ORI -> Seq(Y, EXC_N, FU_ALU, ALU_OR, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 44 | ANDI -> Seq(Y, EXC_N, FU_ALU, ALU_AND, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 45 | SLLI -> Seq(Y, EXC_N, FU_ALU, ALU_SLL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 46 | SRLI -> Seq(Y, EXC_N, FU_ALU, ALU_SRL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 47 | SRAI -> Seq(Y, EXC_N, FU_ALU, ALU_SRA, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, Y), 48 | ADD -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 49 | SUB -> Seq(Y, EXC_N, FU_ALU, ALU_SUB, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 50 | SLL -> Seq(Y, EXC_N, FU_ALU, ALU_SLL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 51 | SLT -> Seq(Y, EXC_N, FU_ALU, ALU_SLT, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 52 | SLTU -> Seq(Y, EXC_N, FU_ALU, ALU_SLTU, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 53 | XOR -> Seq(Y, EXC_N, FU_ALU, ALU_XOR, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 54 | SRL -> Seq(Y, EXC_N, FU_ALU, ALU_SRL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 55 | SRA -> Seq(Y, EXC_N, FU_ALU, ALU_SRA, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 56 | OR -> Seq(Y, EXC_N, FU_ALU, ALU_OR, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 57 | AND -> Seq(Y, EXC_N, FU_ALU, ALU_AND, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 58 | ADDIW -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, N), 59 | SLLIW -> Seq(Y, EXC_N, FU_ALU, ALU_SLL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, N), 60 | SRLIW -> Seq(Y, EXC_N, FU_ALU, ALU_SRL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, N), 61 | SRAIW -> Seq(Y, EXC_N, FU_ALU, ALU_SRA, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_IMM, Y, IMM_I, N), 62 | ADDW -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 63 | SUBW -> Seq(Y, EXC_N, FU_ALU, ALU_SUB, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 64 | SLLW -> Seq(Y, EXC_N, FU_ALU, ALU_SLL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 65 | SRLW -> Seq(Y, EXC_N, FU_ALU, ALU_SRL, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 66 | SRAW -> Seq(Y, EXC_N, FU_ALU, ALU_SRA, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 67 | // SYS 68 | ECALL -> Seq(Y, EXC_EC, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_ZERO, RS_ZERO, N, IMM_X, Y), 69 | EBREAK -> Seq(Y, EXC_EB, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_ZERO, RS_ZERO, N, IMM_X, Y), 70 | FENCE -> Seq(Y, EXC_N, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_FENCE, RS_ZERO, RS_ZERO, N, IMM_X, Y), 71 | FENCE_I -> Seq(Y, EXC_N, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_FENCEI, RS_ZERO, RS_ZERO, N, IMM_X, Y), 72 | SFENCE_VMA -> Seq(Y, EXC_N, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_SFV, RS_ZERO, RS_ZERO, N, IMM_X, Y), 73 | MRET -> Seq(Y, EXC_N, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_MRET, RS_ZERO, RS_ZERO, N, IMM_X, Y), 74 | SRET -> Seq(Y, EXC_N, FU_SYS, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_SRET, RS_ZERO, RS_ZERO, N, IMM_X, Y), 75 | WFI -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_ZERO, RS_ZERO, N, IMM_X, Y), 76 | // CSR 77 | CSRRW -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RW, SYS_N, RS_RF, RS_X, Y, IMM_X, Y), 78 | CSRRS -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RS, SYS_N, RS_RF, RS_X, Y, IMM_X, Y), 79 | CSRRC -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RC, SYS_N, RS_RF, RS_X, Y, IMM_X, Y), 80 | CSRRWI -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RW, SYS_N, RS_IMM, RS_X, Y, IMM_Z, Y), 81 | CSRRSI -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RS, SYS_N, RS_IMM, RS_X, Y, IMM_Z, Y), 82 | CSRRCI -> Seq(Y, EXC_N, FU_CSR, ALU_X, JMP_N, MDU_X, LSU_N, MEM_X, CSR_RC, SYS_N, RS_IMM, RS_X, Y, IMM_Z, Y), 83 | // RV64M 84 | MUL -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_MUL, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 85 | MULH -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_MULH, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 86 | MULHSU -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_MULHSU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 87 | MULHU -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_MULHU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 88 | DIV -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_DIV, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 89 | DIVU -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_DIVU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 90 | REM -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_REM, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 91 | REMU -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_REMU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, Y), 92 | MULW -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_MUL, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 93 | DIVW -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_DIV, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 94 | DIVUW -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_DIVU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 95 | REMW -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_REM, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 96 | REMUW -> Seq(Y, EXC_N, FU_MDU, ALU_X, JMP_N, MDU_REMU, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, Y, IMM_X, N), 97 | // RV64A 98 | LR_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LR, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 99 | LR_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_LR, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 100 | SC_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_SC, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 101 | SC_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_SC, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 102 | AMOSWAP_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOSWAP, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 103 | AMOSWAP_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOSWAP, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 104 | AMOADD_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOADD, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 105 | AMOADD_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOADD, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 106 | AMOAND_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOAND, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 107 | AMOAND_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOAND, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 108 | AMOOR_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOOR, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 109 | AMOOR_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOOR, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 110 | AMOXOR_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOXOR, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 111 | AMOXOR_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOXOR, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 112 | AMOMAX_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMAX, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 113 | AMOMAX_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMAX, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 114 | AMOMAXU_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMAXU, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 115 | AMOMAXU_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMAXU, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 116 | AMOMIN_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMIN, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 117 | AMOMIN_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMIN, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 118 | AMOMINU_W -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMINU, MEM_WORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 119 | AMOMINU_D -> Seq(Y, EXC_N, FU_LSU, ALU_ADD, JMP_N, MDU_X, LSU_AMOMINU, MEM_DWORD, CSR_N, SYS_N, RS_RF, RS_ZERO, Y, IMM_X, Y), 120 | // CUSTOM - DEBUG ONLY 121 | HALT() -> Seq(Y, EXC_N, FU_ALU, ALU_ADD, JMP_N, MDU_X, LSU_N, MEM_X, CSR_N, SYS_N, RS_RF, RS_RF, N, IMM_X, Y) 122 | ).map({ case (k, v) => k -> BitPat(s"b${v.reduce(_ + _)}") }), BitPat(s"b$decode_default")) 123 | } 124 | -------------------------------------------------------------------------------- /src/main/scala/Elaborate.scala: -------------------------------------------------------------------------------- 1 | import freechips.rocketchip.diplomacy.LazyModule 2 | import freechips.rocketchip.util.HasRocketChipStageUtils 3 | import org.chipsalliance.cde.config._ 4 | 5 | case class CLIConfig(numHarts: Int = 1, target: String = "fast-sim") 6 | 7 | object Elaborate extends App with HasRocketChipStageUtils { 8 | val parser = new scopt.OptionParser[CLIConfig]("Elaborate") { 9 | override def errorOnUnknownArgument: Boolean = false 10 | override def reportWarning(msg: String): Unit = {} 11 | opt[Int]("numHarts") 12 | .abbr("n") 13 | .valueName("") 14 | .validate { x => 15 | if (x > 0) success else failure("numHarts must be positive") 16 | } 17 | .action { (x, c) => 18 | c.copy(numHarts = x) 19 | } 20 | .text("Number of harts") 21 | opt[String]("target") 22 | .valueName("") 23 | .validate { x => 24 | if (x == "fast-sim" || x == "slow-sim" || x == "syn") success 25 | else failure("Elaborate target must be either fast-sim, slow-sim, or syn") 26 | } 27 | .action { (x, c) => 28 | c.copy(target = x) 29 | } 30 | .text("Elaborate target (must be either fast-sim, slow-sim, or syn)") 31 | } 32 | 33 | parser.parse(args, CLIConfig()) match { 34 | case Some(config) => 35 | implicit val p = (config.target match { 36 | case "fast-sim" => new FastSimulationConfig 37 | case "slow-sim" => new SlowSimulationConfig 38 | case "syn" => new SynthesisConfig 39 | }).toInstance.alterPartial({ 40 | case NumHarts => config.numHarts 41 | }) 42 | if (config.target == "syn") { 43 | val soc = LazyModule(new SoC()) 44 | val verilog = chisel3.stage.ChiselStage.emitVerilog(soc.module) 45 | writeOutputFile(".", "SoC.v", verilog) 46 | } else { 47 | val top = LazyModule(new SimTop()) 48 | val verilog = chisel3.stage.ChiselStage.emitVerilog(top.module) 49 | writeOutputFile(".", "SimTop.v", verilog) 50 | writeOutputFile(".", "cs.graphml", top.graphML) 51 | } 52 | case None => 53 | sys.exit(1) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/scala/FPGA.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.diplomacy._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.devices.tilelink._ 7 | import freechips.rocketchip.interrupts._ 8 | import freechips.rocketchip.util._ 9 | import java.nio._ 10 | import java.nio.file._ 11 | import org.chipsalliance.cde.config._ 12 | import sifive.blocks.inclusivecache._ 13 | import testchipip._ 14 | 15 | abstract class FPGAAbstract(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 16 | val node: Option[Seq[TLIdentityNode]] 17 | override lazy val module = new FPGAAbstractImp(this) 18 | } 19 | 20 | class FPGAAbstractImp[+L <: FPGAAbstract](l: L) extends LazyModuleImp(l) with HasCherrySpringsParameters { 21 | val io = IO(new Bundle { 22 | val uart = new UARTIO 23 | val in = if (enableSerdes) Some(Vec(numHarts, Flipped(Decoupled(UInt(tlSerWidth.W))))) else None 24 | val out = if (enableSerdes) Some(Vec(numHarts, Decoupled(UInt(tlSerWidth.W)))) else None 25 | val io_clock = if (enableSerdes) Some(Input(Clock())) else None 26 | val io_reset = if (enableSerdes) Some(Input(Bool())) else None 27 | val intr = Vec(numHarts, Output(new ExternalInterrupt)) 28 | }) 29 | } 30 | 31 | class FPGAImp(implicit p: Parameters) extends FPGAAbstract { 32 | val xbar = LazyModule(new TLXbar(policy = TLArbiter.highestIndexFirst)) 33 | val pbar = LazyModule(new TLXbar) 34 | val hart_id_allocator = LazyModule(new HartIDAllocator) 35 | val clint = LazyModule(new CLINT(CLINTParams(), 8)) 36 | val plic = LazyModule(new TLPLIC(PLICParams(), 8)) 37 | val uart = LazyModule(new UART) 38 | val mem = LazyModule(new TLVirtualRam256) 39 | 40 | val node = Some(for (i <- 0 until numHarts) yield { 41 | val node = TLIdentityNode() 42 | node 43 | }) 44 | 45 | // BootROM 46 | lazy val boot_rom_contents = { 47 | val data = Files.readAllBytes(Paths.get(bootROMImage)) 48 | val rom = ByteBuffer.wrap(data) 49 | rom.array() 50 | } 51 | val rom = LazyModule( 52 | new TLROM( 53 | base = 0x10000, 54 | size = 0x10000, 55 | contentsDelayed = boot_rom_contents.toIndexedSeq, 56 | beatBytes = 8 57 | ) 58 | ) 59 | 60 | // L2 shared cache and coherence controller 61 | val l2cache = LazyModule( 62 | new InclusiveCache( 63 | CacheParameters( 64 | level = 2, 65 | ways = l2cacheNumWays, 66 | sets = l2cacheNumSets, 67 | blockBytes = 32, 68 | beatBytes = 32, 69 | hintsSkipProbe = false 70 | ), 71 | InclusiveCacheMicroParameters(writeBytes = 32) 72 | ) 73 | ) 74 | 75 | // don't modify order of following nodes 76 | mem.node := TLCacheCork() := l2cache.node := xbar.node 77 | pbar.node := TLFIFOFixer() := TLFragmenter(8, 32) := TLWidthWidget(32) := xbar.node // MMIO has higher priority 78 | 79 | rom.node := pbar.node 80 | hart_id_allocator.node := pbar.node 81 | clint.node := pbar.node 82 | plic.node := pbar.node 83 | uart.node := TLFragmenter(4, 8) := TLWidthWidget(8) := pbar.node 84 | 85 | for (i <- 0 until numHarts) { 86 | xbar.node := TLBuffer() := node.get(i) 87 | } 88 | xbar.node := TLSilentClient() // in order to generate correct config for L2 cache 89 | 90 | class IntSourceBridge(val num: Int)(implicit p: Parameters) extends LazyModule { 91 | val sourceNode = IntSourceNode(IntSourcePortSimple(num, ports = 1, sources = 1)) 92 | lazy val module = new IntSourceBridgeModule(this) 93 | } 94 | 95 | class IntSourceBridgeModule(outer: IntSourceBridge) extends LazyModuleImp(outer) { 96 | val in = IO(Input(Vec(outer.num, Bool()))) 97 | in.zip(outer.sourceNode.out.head._1).foreach { case (i, s) => s := i } 98 | } 99 | 100 | def numIntr = 1 101 | 102 | val plicSource = LazyModule(new IntSourceBridge(numIntr)) 103 | plic.intnode := plicSource.sourceNode 104 | 105 | // interrupt sinks 106 | val clint_int = for (i <- 0 until numHarts) yield { val n = IntSinkNode(IntSinkPortSimple(1, 2)); n } 107 | val plic_int = for (i <- 0 until numHarts) yield { val n = IntSinkNode(IntSinkPortSimple(2, 1)); n } 108 | for (i <- 0 until numHarts) { 109 | clint_int(i) := clint.intnode 110 | plic_int(i) :*= plic.intnode 111 | } 112 | 113 | override lazy val module = new FPGAAbstractImp(this) { 114 | // sync external interrupts 115 | val ext_intrs = Wire(UInt(numIntr.W)) 116 | ext_intrs := Cat(uart.module.io.intr.asUInt) 117 | require(plicSource.module.in.length == ext_intrs.getWidth) 118 | for ((plic_in, interrupt) <- plicSource.module.in.zip(ext_intrs.asBools)) { 119 | val ext_intr_sync = RegInit(0.U(3.W)) 120 | ext_intr_sync := Cat(ext_intr_sync(1, 0), interrupt) 121 | plic_in := ext_intr_sync(2) 122 | } 123 | 124 | // interrupt output 125 | for (i <- 0 until numHarts) { 126 | io.intr(i).msip := clint_int(i).in.head._1(0) 127 | io.intr(i).mtip := clint_int(i).in.head._1(1) 128 | io.intr(i).meip := plic_int(i).in.head._1(0) 129 | io.intr(i).seip := plic_int(i).in.last._1(0) 130 | } 131 | 132 | // UART 133 | io.uart <> uart.module.io.uart 134 | 135 | // CLINT 136 | val cnt = RegInit(fpgaTimerFreq.U) 137 | val tick = (cnt === 0.U) 138 | cnt := Mux(tick, fpgaTimerFreq.U, cnt - 1.U) 139 | clint.module.io.rtcTick := tick 140 | } 141 | } 142 | 143 | class FPGA(implicit p: Parameters) extends FPGAAbstract { 144 | val fpga_imp = LazyModule(new FPGAImp) 145 | 146 | val node = None 147 | 148 | // desser 149 | val desser = for (i <- 0 until numHarts) yield { 150 | val desser = LazyModule( 151 | new TLDesser( 152 | w = tlSerWidth, 153 | params = Seq( 154 | TLMasterParameters.v1( 155 | name = s"tl-desser-${i}", 156 | sourceId = IdRange(0, 1 << tlSourceBits), 157 | supportsProbe = TransferSizes(32) 158 | ) 159 | ) 160 | ) 161 | ) 162 | desser 163 | } 164 | for (i <- 0 until numHarts) { 165 | fpga_imp.node.get(i) := desser(i).node 166 | } 167 | 168 | override lazy val module = new FPGAAbstractImp(this) { 169 | // desser 170 | val mergeType = desser(0).module.mergeTypes(0) 171 | val wordsPerBeat = (mergeType.getWidth - 1) / tlSerWidth + 1 172 | val beatsPerBlock = 1 173 | val qDepth = (wordsPerBeat * beatsPerBlock) << tlSourceBits 174 | 175 | for (i <- 0 until numHarts) { 176 | val in_fifo = Module(new AsyncQueue(UInt(tlSerWidth.W))) 177 | val out_fifo = Module(new AsyncQueue(UInt(tlSerWidth.W))) 178 | in_fifo.io.enq <> io.in.get(i) 179 | in_fifo.io.enq_clock := io.io_clock.get 180 | in_fifo.io.enq_reset := io.io_reset.get 181 | desser(i).module.io.ser.head.in <> Queue(in_fifo.io.deq, qDepth) 182 | in_fifo.io.deq_clock := clock 183 | in_fifo.io.deq_reset := reset 184 | out_fifo.io.enq <> Queue(desser(i).module.io.ser.head.out, qDepth) 185 | out_fifo.io.enq_clock := clock 186 | out_fifo.io.enq_reset := reset 187 | out_fifo.io.deq <> io.out.get(i) 188 | out_fifo.io.deq_clock := io.io_clock.get 189 | out_fifo.io.deq_reset := io.io_reset.get 190 | } 191 | 192 | // UART 193 | io.uart <> fpga_imp.module.io.uart 194 | 195 | // interrupt output (sync_dff triggered by IO clock & reset) 196 | withClockAndReset(io.io_clock.get, io.io_reset.get) { 197 | for (i <- 0 until numHarts) { 198 | val sync_dff = RegInit(VecInit(Seq.fill(3)(0.U.asTypeOf(new ExternalInterrupt)))) 199 | sync_dff(0) := fpga_imp.module.io.intr(i) 200 | sync_dff(1) := sync_dff(0) 201 | sync_dff(2) := sync_dff(1) 202 | io.intr(i) := sync_dff(2) 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/scala/HartIDAllocator.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.diplomacy._ 4 | import freechips.rocketchip.tilelink._ 5 | import freechips.rocketchip.regmapper._ 6 | import org.chipsalliance.cde.config._ 7 | 8 | class HartIDAllocator(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 9 | val device = new SimpleDevice("hart-id-allocator", Seq("hart-id-allocator-0")) 10 | val node = TLRegisterNode( 11 | address = Seq(AddressSet(BigInt("00020000", 16), BigInt("ffff", 16))), 12 | device = device, 13 | beatBytes = 8 14 | ) 15 | 16 | lazy val module = new LazyModuleImp(this) { 17 | val hart_id = RegInit(1.U(1.W)) 18 | 19 | def hartIDAllocate(ready: Bool): (Bool, UInt) = { 20 | when(ready) { hart_id := hart_id - 1.U } 21 | // (ready, bits) 22 | (true.B, hart_id) 23 | } 24 | 25 | node.regmap( 26 | 0x0 -> Seq(RegField.r(64, hartIDAllocate(_))) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/ICache.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import chisel3.util.random._ 4 | import freechips.rocketchip.diplomacy._ 5 | import freechips.rocketchip.tilelink._ 6 | import org.chipsalliance.cde.config._ 7 | 8 | class ICache(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 9 | require(xLen == 64) 10 | 11 | val node = TLClientNode( 12 | Seq( 13 | TLMasterPortParameters.v1( 14 | clients = Seq( 15 | TLMasterParameters.v1( 16 | name = s"icache-${hartID}", 17 | sourceId = IdRange(0, sourceRange) 18 | ) 19 | ) 20 | ) 21 | ) 22 | ) 23 | 24 | lazy val module = new ICacheModule(this) 25 | } 26 | 27 | class ICacheModule(outer: ICache) extends LazyModuleImp(outer) with HasCherrySpringsParameters { 28 | val io = IO(new Bundle { 29 | val cache = Flipped(new CachePortIO) 30 | val fence_i = Input(Bool()) 31 | }) 32 | val (tl, edge) = outer.node.out.head 33 | 34 | /* 35 | * Instruction cache (PIPT) organization 36 | * #Sets = cacheNumSets 37 | * Associativity = 1 38 | * Cacheline = 4 x 64 bits 39 | */ 40 | 41 | val tagWidth = paddrLen - (5 + log2Up(cacheNumSets)) 42 | def getOffset(x: UInt) = x(4, 3) 43 | def getIndex(x: UInt) = x(4 + log2Up(cacheNumSets), 5) 44 | def getTag(x: UInt) = x(paddrLen - 1, 5 + log2Up(cacheNumSets)) 45 | 46 | val req = io.cache.req 47 | val resp = io.cache.resp 48 | val req_r = RegInit(0.U.asTypeOf(new CachePortReq)) 49 | 50 | val array = Module(new SRAM(depth = cacheNumSets, dw = (new CacheEntry).len())) 51 | val valid = RegInit(VecInit(Seq.fill(cacheNumSets)(false.B))) 52 | 53 | // pipeline control signals 54 | val s1_valid = Wire(Bool()) 55 | val s2_ready = Wire(Bool()) 56 | val fire = s1_valid & s2_ready 57 | 58 | // default input 59 | array.io.en := fire || tl.d.fire 60 | array.io.addr := 0.U 61 | array.io.wdata := 0.U 62 | array.io.wen := false.B 63 | 64 | // array output in stage 2 65 | val array_out = Wire(new CacheEntry) 66 | val array_hit = Wire(Bool()) 67 | array_out := HoldUnless(array.io.rdata, RegNext(fire)).asTypeOf(new CacheEntry) 68 | array_hit := valid(getIndex(req_r.addr)) && (getTag(req_r.addr) === array_out.tag) 69 | 70 | // when pipeline fire, read data and tag in array 71 | when(fire) { 72 | array.io.addr := getIndex(req.bits.addr) 73 | req_r := req.bits 74 | } 75 | 76 | // stage 2 FSM 77 | val s_check :: s_req :: s_resp :: s_ok :: s_init :: Nil = Enum(5) 78 | val state = RegInit(s_init) 79 | 80 | switch(state) { 81 | is(s_check) { 82 | state := Mux(resp.fire && !req.fire, s_init, Mux(array_hit, s_check, s_req)) 83 | } 84 | is(s_req) { 85 | when(tl.a.fire) { 86 | state := s_resp 87 | } 88 | } 89 | is(s_resp) { 90 | when(tl.d.fire) { 91 | state := s_ok 92 | } 93 | } 94 | is(s_ok) { 95 | when(resp.fire) { 96 | state := Mux(!req.fire, s_init, s_check) 97 | } 98 | } 99 | is(s_init) { 100 | when(req.fire) { 101 | state := s_check 102 | } 103 | } 104 | } 105 | 106 | // cache read 107 | val array_data = HoldUnless( 108 | MuxLookup( 109 | getOffset(req_r.addr), 110 | 0.U(64.W), 111 | Seq( 112 | 0.U -> array_out.data(63, 0), 113 | 1.U -> array_out.data(127, 64), 114 | 2.U -> array_out.data(191, 128), 115 | 3.U -> array_out.data(255, 192) 116 | ) 117 | ), 118 | RegNext(req.fire) 119 | ) 120 | 121 | // cache write 122 | val wdata = RegEnable(tl.d.bits.data, 0.U(256.W), tl.d.fire) 123 | 124 | when(tl.d.fire) { 125 | array.io.addr := getIndex(req_r.addr) 126 | array.io.wdata := Cat(getTag(req_r.addr), tl.d.bits.data) 127 | array.io.wen := true.B 128 | valid(getIndex(req_r.addr)) := true.B 129 | } 130 | 131 | val refill_data = MuxLookup( 132 | getOffset(req_r.addr), 133 | 0.U(64.W), 134 | Seq( 135 | 0.U -> wdata(63, 0), 136 | 1.U -> wdata(127, 64), 137 | 2.U -> wdata(191, 128), 138 | 3.U -> wdata(255, 192) 139 | ) 140 | ) 141 | 142 | // pipeline control 143 | s2_ready := ((state === s_check && array_hit) || (state === s_ok) || (state === s_init)) && resp.ready 144 | s1_valid := req.valid 145 | 146 | // send TL read request 147 | val source = Counter(tl.a.fire, sourceRange)._1 // source id 148 | val (_, get_bits) = edge.Get(source, Cat(req_r.addr(paddrLen - 1, 5), Fill(5, 0.U)), 5.U) // 4x64 bits 149 | 150 | tl.a.valid := (state === s_req) 151 | tl.a.bits := get_bits 152 | tl.d.ready := (state === s_resp) 153 | 154 | // cache port handshake 155 | req.ready := s2_ready 156 | resp.valid := (state === s_check && array_hit) || (state === s_ok) 157 | resp.bits := 0.U.asTypeOf(new CachePortResp) 158 | resp.bits.rdata := Mux(state === s_ok, refill_data, array_data) 159 | 160 | // clear I cache for fence.i instruction 161 | when(io.fence_i) { 162 | for (i <- 0 until cacheNumSets) { 163 | valid(i) := false.B 164 | } 165 | } 166 | 167 | if (debugICache) { 168 | when(req.fire) { 169 | printf(cf"${DebugTimer()} [ICache] [req ] ${req.bits}\n") 170 | } 171 | when(resp.fire) { 172 | printf(cf"${DebugTimer()} [ICache] [resp] ${resp.bits}\n") 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/scala/IFU.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import org.chipsalliance.cde.config._ 4 | 5 | class IF0(implicit p: Parameters) extends CherrySpringsModule { 6 | val io = IO(new Bundle { 7 | val jmp_packet = Input(new JmpPacket) 8 | val req = Decoupled(new CachePortReq) 9 | val req_addr = Output(UInt(vaddrLen.W)) 10 | val bp_npc = Output(UInt(xLen.W)) 11 | val stall_b = Input(Bool()) 12 | }) 13 | 14 | val pc_next = Wire(UInt(xLen.W)) 15 | val pc_update = (io.jmp_packet.valid || io.req.fire) 16 | val pc = RegEnable(pc_next, resetPC.U, pc_update) 17 | val jmp_target = Wire(UInt(xLen.W)) 18 | 19 | val pc_next_raw = Wire(UInt(xLen.W)) 20 | if (enableBPU) { 21 | val bpu = Module(new BPU) 22 | bpu.io.pc := Mux(io.jmp_packet.valid && io.req.fire, jmp_target, pc) 23 | bpu.io.jmp_packet := io.jmp_packet 24 | pc_next_raw := bpu.io.out 25 | } else { 26 | pc_next_raw := pc + 4.U 27 | } 28 | io.bp_npc := pc_next_raw 29 | 30 | jmp_target := Cat(io.jmp_packet.target(xLen - 1, 2), 0.U(2.W)) 31 | pc_next := Mux(io.jmp_packet.valid && !io.req.fire, jmp_target, pc_next_raw) 32 | 33 | io.req_addr := Mux(io.jmp_packet.valid, jmp_target, pc) 34 | io.req.bits := 0.U.asTypeOf(new CachePortReq) 35 | io.req.bits.addr := Cat(io.req_addr(vaddrLen - 1, 3), 0.U(3.W)) 36 | io.req.valid := io.stall_b 37 | } 38 | 39 | class IF1(implicit p: Parameters) extends CherrySpringsModule { 40 | val io = IO(new Bundle { 41 | val jmp_packet = Input(new JmpPacket) 42 | val resp = Flipped(Decoupled(new CachePortResp)) 43 | val stall_b = Input(Bool()) 44 | val out = Output(new FDPacket) 45 | val ready = Output(Bool()) 46 | val req_fire = Input(Bool()) 47 | val pc_queue_empty = Input(Bool()) 48 | }) 49 | 50 | val s_init :: s_resp :: s_wait :: Nil = Enum(3) 51 | val state = RegInit(s_init) 52 | 53 | val jmp_r = BoolStopWatch( 54 | io.jmp_packet.valid && !io.resp.fire && (state =/= s_wait) && !io.pc_queue_empty, 55 | io.resp.fire 56 | ) 57 | 58 | val state_to_wait = (io.resp.bits.page_fault || io.resp.bits.access_fault) && !jmp_r && !io.jmp_packet.valid 59 | 60 | switch(state) { 61 | is(s_init) { 62 | when(io.req_fire) { 63 | state := s_resp 64 | } 65 | } 66 | is(s_resp) { 67 | when(io.resp.fire) { 68 | state := Mux(io.jmp_packet.valid, s_init, s_resp) 69 | } 70 | } 71 | is(s_wait) { 72 | when(io.jmp_packet.valid) { // jump to page fault handler 73 | state := s_init 74 | } 75 | } 76 | } 77 | 78 | io.resp.ready := ((io.stall_b || io.jmp_packet.valid) && (state === s_resp)) || (state === s_init) 79 | 80 | io.out := 0.U.asTypeOf(new FDPacket) 81 | io.out.valid := io.resp.fire && !io.jmp_packet.valid && !jmp_r 82 | 83 | io.ready := (io.resp.fire && !state_to_wait && !io.jmp_packet.valid) || (state === s_init) 84 | } 85 | 86 | class IFU(implicit p: Parameters) extends CherrySpringsModule { 87 | require(xLen == 64) 88 | 89 | val io = IO(new Bundle { 90 | val jmp_packet = Input(new JmpPacket) 91 | val imem = new CachePortIO 92 | val out = Output(new FDPacket) 93 | val stall_b = Input(Bool()) 94 | }) 95 | 96 | val if0 = Module(new IF0) 97 | val if1 = Module(new IF1) 98 | 99 | val pc_queue = Module(new Queue(UInt(vaddrLen.W), 1, pipe = true)) 100 | val bp_npc_queue = Module(new Queue(UInt(xLen.W), 1, pipe = true)) 101 | 102 | if0.io.jmp_packet := io.jmp_packet 103 | if0.io.req <> io.imem.req 104 | if0.io.stall_b := pc_queue.io.enq.ready && io.stall_b 105 | 106 | if1.io.jmp_packet := io.jmp_packet 107 | if1.io.resp <> io.imem.resp 108 | if1.io.stall_b := io.stall_b 109 | if1.io.req_fire := io.imem.req.fire 110 | if1.io.pc_queue_empty := (pc_queue.io.count === 0.U) 111 | 112 | pc_queue.io.enq.bits := if0.io.req_addr 113 | pc_queue.io.enq.valid := io.imem.req.fire 114 | pc_queue.io.deq.ready := io.imem.resp.fire 115 | 116 | bp_npc_queue.io.enq.bits := if0.io.bp_npc 117 | bp_npc_queue.io.enq.valid := io.imem.req.fire 118 | bp_npc_queue.io.deq.ready := io.imem.resp.fire 119 | 120 | io.out := if1.io.out 121 | io.out.pc := SignExt39_64(pc_queue.io.deq.bits) 122 | io.out.instr := Mux(io.out.pc(2), io.imem.resp.bits.rdata(63, 32), io.imem.resp.bits.rdata(31, 0)) 123 | io.out.page_fault := io.imem.resp.bits.page_fault 124 | io.out.access_fault := io.imem.resp.bits.access_fault 125 | io.out.bp_npc := bp_npc_queue.io.deq.bits 126 | 127 | if (debugInstrFetch) { 128 | when(io.out.valid) { 129 | printf(cf"${DebugTimer()} [IFU] ${io.out}\n") 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/scala/LSU.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.rocket._ 5 | import org.chipsalliance.cde.config._ 6 | import Constant._ 7 | 8 | class LSU(implicit p: Parameters) extends CherrySpringsModule { 9 | require(xLen == 64) 10 | 11 | val io = IO(new Bundle { 12 | val uop = Input(new MicroOp) 13 | val is_mem = Input(Bool()) 14 | val is_store = Input(Bool()) 15 | val is_amo = Input(Bool()) 16 | val addr = Input(UInt(xLen.W)) 17 | val wdata = Input(UInt(xLen.W)) 18 | val rdata = Output(UInt(xLen.W)) 19 | val valid = Output(Bool()) 20 | val exc_code = Output(UInt(4.W)) 21 | val dmem = new CachePortIO 22 | val ready = Output(Bool()) 23 | val is_mmio = Output(Bool()) 24 | val hartid = Input(UInt(8.W)) // only for difftest 25 | }) 26 | 27 | val req = io.dmem.req 28 | val resp = io.dmem.resp 29 | 30 | val s_idle :: s_req :: s_resp :: s_exc :: Nil = Enum(4) 31 | val state = RegInit(s_idle) 32 | 33 | val addr_offset = io.addr(2, 0) 34 | 35 | val wdata = (io.wdata << (addr_offset << 3))(xLen - 1, 0) 36 | val wmask = (MuxLookup( 37 | io.uop.mem_len, 38 | 0.U, 39 | Seq( 40 | s"b$MEM_BYTE".U -> "b00000001".U(8.W), 41 | s"b$MEM_HALF".U -> "b00000011".U(8.W), 42 | s"b$MEM_WORD".U -> "b00001111".U(8.W), 43 | s"b$MEM_DWORD".U -> "b11111111".U(8.W) 44 | ) 45 | ) << addr_offset)(xLen / 8 - 1, 0) 46 | 47 | req.bits.addr := io.addr 48 | req.bits.wdata := wdata 49 | req.bits.wmask := wmask 50 | req.bits.len := io.uop.mem_len 51 | req.bits.wen := io.is_store || io.is_amo 52 | req.bits.lrsc := isLrSc(io.uop.lsu_op) 53 | req.bits.amo := io.uop.lsu_op 54 | req.valid := (state === s_req) 55 | resp.ready := (state === s_resp) 56 | 57 | val misaligned = Wire(Bool()) 58 | misaligned := MuxLookup( 59 | io.uop.mem_len, 60 | false.B, 61 | Seq( 62 | s"b$MEM_HALF".U -> (io.addr(0) =/= 0.U), 63 | s"b$MEM_WORD".U -> (io.addr(1, 0) =/= 0.U), 64 | s"b$MEM_DWORD".U -> (io.addr(2, 0) =/= 0.U) 65 | ) 66 | ) 67 | 68 | switch(state) { 69 | is(s_idle) { 70 | when(io.is_mem) { 71 | state := Mux(misaligned, s_exc, s_req) 72 | } 73 | } 74 | is(s_req) { 75 | when(req.fire) { 76 | state := s_resp 77 | } 78 | } 79 | is(s_resp) { 80 | when(resp.fire) { 81 | state := Mux(resp.bits.page_fault || resp.bits.access_fault, s_exc, s_idle) 82 | } 83 | } 84 | is(s_exc) { 85 | state := s_idle 86 | } 87 | } 88 | 89 | val resp_data = resp.bits.rdata >> (addr_offset << 3) 90 | val sign = (!isLdu(io.uop.lsu_op) && MuxLookup( 91 | io.uop.mem_len, 92 | false.B, 93 | Seq( 94 | s"b$MEM_BYTE".U -> resp_data(7), 95 | s"b$MEM_HALF".U -> resp_data(15), 96 | s"b$MEM_WORD".U -> resp_data(31) 97 | ) 98 | ).asBool).asUInt 99 | val rdata = MuxLookup( 100 | io.uop.mem_len, 101 | 0.U, 102 | Seq( 103 | s"b$MEM_BYTE".U -> Cat(Fill(56, sign), resp_data(7, 0)), 104 | s"b$MEM_HALF".U -> Cat(Fill(48, sign), resp_data(15, 0)), 105 | s"b$MEM_WORD".U -> Cat(Fill(32, sign), resp_data(31, 0)), 106 | s"b$MEM_DWORD".U -> resp_data 107 | ) 108 | ) 109 | 110 | io.rdata := rdata 111 | io.valid := ((state === s_resp) && (resp.fire && !resp.bits.page_fault && !resp.bits.access_fault)) || (state === s_exc) // assert for only 1 cycle 112 | io.is_mmio := io.valid && resp.bits.mmio 113 | io.ready := ((state === s_idle) && !io.is_mem) || io.valid 114 | 115 | /* 116 | * exc_code Description 117 | * 0 Load/store/AMO instruction is executed without exception (different from ISA, 0 for Instruction address misaligned) 118 | * 4 Load address misaligned 119 | * 5 Load access fault 120 | * 6 Store/AMO address misaligned 121 | * 7 Store/AMO access fault 122 | * 13 Load page fault 123 | * 15 Store/AMO page fault 124 | */ 125 | val exc_code = 126 | Mux( 127 | io.is_store || io.is_amo, 128 | Mux( 129 | misaligned, 130 | Causes.misaligned_store.U, 131 | Mux(RegNext(resp.bits.access_fault), Causes.store_access.U, Causes.store_page_fault.U) 132 | ), 133 | Mux( 134 | misaligned, 135 | Causes.misaligned_load.U, 136 | Mux(RegNext(resp.bits.access_fault), Causes.load_access.U, Causes.load_page_fault.U) 137 | ) 138 | ) 139 | io.exc_code := Mux(state === s_exc, exc_code, 0.U) 140 | 141 | if (enableDifftest) { 142 | val lsu_ok = (state === s_resp) && resp.fire && !resp.bits.page_fault && !resp.bits.access_fault && !resp.bits.mmio 143 | val paddr_aligned = Cat(resp.bits.paddr(paddrLen - 1, 3), 0.U(3.W)) 144 | 145 | val dt_sb = Module(new DifftestSbufferEvent) 146 | dt_sb.io.clock := clock 147 | dt_sb.io.coreid := io.hartid 148 | dt_sb.io.index := 0.U 149 | dt_sb.io.sbufferResp := RegNext(RegNext(lsu_ok && (io.is_store || io.is_amo))) 150 | dt_sb.io.sbufferAddr := RegNext(RegNext(paddr_aligned)) 151 | dt_sb.io.sbufferMask := RegNext(RegNext(wmask)) 152 | for (i <- 0 until 64) { 153 | if (i < 8) { 154 | dt_sb.io.sbufferData(i) := RegNext(RegNext(resp.bits.wdata(i * 8 + 7, i * 8))) 155 | } else { 156 | dt_sb.io.sbufferData(i) := 0.U 157 | } 158 | } 159 | 160 | val dt_ld = Module(new DifftestLoadEvent) 161 | dt_ld.io.clock := clock 162 | dt_ld.io.coreid := io.hartid 163 | dt_ld.io.index := 0.U 164 | dt_ld.io.valid := RegNext(lsu_ok && (isLoad(io.uop.lsu_op) || io.is_amo)) 165 | dt_ld.io.paddr := RegNext(resp.bits.paddr) 166 | dt_ld.io.opType := RegNext(io.uop.mem_len) 167 | dt_ld.io.fuType := Mux(io.is_amo, 0xf.U /* amo */, 0xc.U /* load */ ) 168 | 169 | val dt_st = Module(new DifftestStoreEvent) 170 | dt_st.io.clock := clock 171 | dt_st.io.coreid := io.hartid 172 | dt_st.io.index := 0.U 173 | dt_st.io.storeAddr := RegNext(RegNext(paddr_aligned)) 174 | dt_st.io.storeData := RegNext(RegNext(MaskData(0.U(64.W), resp.bits.wdata, MaskExpand(wmask)))) 175 | dt_st.io.storeMask := RegNext(RegNext(wmask)) 176 | dt_st.io.valid := RegNext( 177 | RegNext(lsu_ok && ((io.is_store && (!isLrSc(io.uop.lsu_op) || !io.rdata(0).asBool)) || io.is_amo)) 178 | ) 179 | 180 | val dt_am = Module(new DifftestAtomicEvent) 181 | dt_am.io.clock := clock 182 | dt_am.io.coreid := io.hartid 183 | dt_am.io.atomicResp := RegNext(lsu_ok && io.is_amo) 184 | dt_am.io.atomicAddr := RegNext(paddr_aligned) 185 | dt_am.io.atomicData := RegNext(wdata) 186 | dt_am.io.atomicMask := RegNext(wmask) 187 | dt_am.io.atomicOut := RegNext(io.rdata) 188 | dt_am.io.atomicFuop := RegNext( 189 | Cat( 190 | MuxLookup( 191 | io.uop.lsu_op, 192 | 0.U, 193 | Seq( 194 | s"b$LSU_AMOSWAP".U -> "b00101".U, 195 | s"b$LSU_AMOADD".U -> "b00111".U, 196 | s"b$LSU_AMOXOR".U -> "b01001".U, 197 | s"b$LSU_AMOAND".U -> "b01011".U, 198 | s"b$LSU_AMOOR".U -> "b01101".U, 199 | s"b$LSU_AMOMIN".U -> "b01111".U, 200 | s"b$LSU_AMOMAX".U -> "b10001".U, 201 | s"b$LSU_AMOMINU".U -> "b10011".U, 202 | s"b$LSU_AMOMAXU".U -> "b10101".U 203 | ) 204 | ), 205 | (io.uop.mem_len === s"b$MEM_DWORD".U).asUInt 206 | ) 207 | ) 208 | 209 | val diff_ls = Module(new DifftestLrScEvent) 210 | diff_ls.io.clock := clock 211 | diff_ls.io.coreid := io.hartid 212 | diff_ls.io.valid := RegNext(lsu_ok && isLrSc(io.uop.lsu_op) && io.is_store) 213 | diff_ls.io.success := RegNext(!io.rdata(0).asBool) 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/scala/MDU.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.rocket._ 4 | import org.chipsalliance.cde.config._ 5 | import Constant._ 6 | 7 | class MDU(implicit p: Parameters) extends CherrySpringsModule { 8 | val io = IO(new Bundle { 9 | val uop = Input(new MicroOp) 10 | val is_mdu = Input(Bool()) 11 | val in1 = Input(UInt(xLen.W)) 12 | val in2 = Input(UInt(xLen.W)) 13 | val out = Output(UInt(xLen.W)) 14 | val valid = Output(Bool()) 15 | val ready = Output(Bool()) 16 | }) 17 | 18 | val uop = io.uop 19 | val in1 = io.in1 20 | val in2 = io.in2 21 | val is_mdu = io.is_mdu 22 | 23 | val s_idle :: s_req :: s_resp :: Nil = Enum(3) 24 | val state = RegInit(s_idle) 25 | 26 | val rocket_mdu = Module(new MulDiv(MulDivParams(), xLen)) 27 | rocket_mdu.io.req.valid := (state === s_req) && uop.valid && is_mdu 28 | rocket_mdu.io.req.bits.fn := uop.mdu_op 29 | rocket_mdu.io.req.bits.dw := uop.dw.asUInt 30 | rocket_mdu.io.req.bits.in1 := in1 31 | rocket_mdu.io.req.bits.in2 := in2 32 | rocket_mdu.io.req.bits.tag := 0.U 33 | rocket_mdu.io.kill := false.B 34 | rocket_mdu.io.resp.ready := true.B 35 | 36 | switch(state) { 37 | is(s_idle) { 38 | when(is_mdu) { 39 | state := s_req 40 | } 41 | } 42 | is(s_req) { 43 | when(rocket_mdu.io.req.fire) { 44 | state := s_resp 45 | } 46 | } 47 | is(s_resp) { 48 | when(rocket_mdu.io.resp.fire) { 49 | state := s_idle 50 | } 51 | } 52 | } 53 | 54 | io.out := rocket_mdu.io.resp.bits.data 55 | io.valid := (state === s_resp) && rocket_mdu.io.resp.fire // assert for only 1 cycle 56 | io.ready := ((state === s_idle) && !is_mdu) || io.valid 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala/MicroOp.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import org.chipsalliance.cde.config._ 4 | import Constant._ 5 | 6 | class MicroOp(implicit p: Parameters) extends CherrySpringsBundle { 7 | val valid = Bool() 8 | val exc = UInt(EXC_X.length.W) 9 | 10 | val pc = UInt(xLen.W) 11 | val npc = UInt(xLen.W) 12 | val instr = UInt(32.W) 13 | 14 | val fu = UInt(FU_X.length.W) 15 | val alu_op = UInt(ALU_X.length.W) 16 | val jmp_op = UInt(JMP_X.length.W) 17 | val mdu_op = UInt(MDU_X.length.W) 18 | val lsu_op = UInt(LSU_X.length.W) 19 | val mem_len = UInt(MEM_X.length.W) 20 | val csr_op = UInt(CSR_X.length.W) 21 | val sys_op = UInt(SYS_X.length.W) 22 | 23 | val rs1_src = UInt(RS_X.length.W) 24 | val rs2_src = UInt(RS_X.length.W) 25 | 26 | val rs1_index = UInt(5.W) 27 | val rs2_index = UInt(5.W) 28 | val rd_index = UInt(5.W) 29 | val rd_wen = Bool() 30 | val imm = UInt(32.W) 31 | val dw = Bool() 32 | 33 | def from_decoder(in: UInt): Unit = { 34 | val imm_type = WireInit(0.U(IMM_X.length.W)) 35 | val entries = Seq( 36 | valid, 37 | exc, 38 | fu, 39 | alu_op, 40 | jmp_op, 41 | mdu_op, 42 | lsu_op, 43 | mem_len, 44 | csr_op, 45 | sys_op, 46 | rs1_src, 47 | rs2_src, 48 | rd_wen, 49 | imm_type, 50 | dw 51 | ) 52 | var i = 0 53 | for (entry <- entries.reverse) { 54 | val entry_width = entry.getWidth 55 | if (entry_width == 1) { 56 | entry := in(i) 57 | } else { 58 | entry := in(i + entry_width - 1, i) 59 | } 60 | i += entry_width 61 | } 62 | 63 | val imm_i = Cat(Fill(21, instr(31)), instr(30, 20)) 64 | val imm_s = Cat(Fill(21, instr(31)), instr(30, 25), instr(11, 7)) 65 | val imm_b = Cat(Fill(20, instr(31)), instr(7), instr(30, 25), instr(11, 8), 0.U) 66 | val imm_u = Cat(instr(31, 12), Fill(12, 0.U)) 67 | val imm_j = Cat(Fill(12, instr(31)), instr(19, 12), instr(20), instr(30, 21), 0.U) 68 | val imm_csr = Cat(Fill(27, 0.U), instr(19, 15)) 69 | 70 | imm := MuxLookup( 71 | imm_type, 72 | 0.U(32.W), 73 | Seq( 74 | s"b$IMM_I".U -> imm_i, 75 | s"b$IMM_S".U -> imm_s, 76 | s"b$IMM_B".U -> imm_b, 77 | s"b$IMM_U".U -> imm_u, 78 | s"b$IMM_J".U -> imm_j, 79 | s"b$IMM_Z".U -> imm_csr 80 | ) 81 | ) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/scala/RegFile.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import difftest._ 3 | import org.chipsalliance.cde.config._ 4 | 5 | class RegFile(implicit p: Parameters) extends CherrySpringsModule { 6 | val io = IO(new Bundle { 7 | val rs1_index = Input(UInt(5.W)) 8 | val rs2_index = Input(UInt(5.W)) 9 | val rs1_data = Output(UInt(xLen.W)) 10 | val rs2_data = Output(UInt(xLen.W)) 11 | val rd_index = Input(UInt(5.W)) 12 | val rd_data = Input(UInt(xLen.W)) 13 | val rd_wen = Input(Bool()) 14 | val rf_a0 = Output(UInt(8.W)) // only for difftest 15 | val hartid = Input(UInt(8.W)) // only for difftest 16 | }) 17 | 18 | def access(x: UInt) = rf(~x) 19 | def access(i: Int) = rf(31 - i) 20 | 21 | val rf = RegInit(VecInit(Seq.fill(31)(0.U(xLen.W)))) 22 | 23 | when(io.rd_wen && (io.rd_index =/= 0.U)) { 24 | access(io.rd_index) := io.rd_data; 25 | } 26 | 27 | io.rs1_data := Mux(io.rs1_index =/= 0.U, access(io.rs1_index), 0.U) 28 | io.rs2_data := Mux(io.rs2_index =/= 0.U, access(io.rs2_index), 0.U) 29 | 30 | // bypass 31 | when(io.rd_wen && (io.rd_index =/= 0.U)) { 32 | when(io.rd_index === io.rs1_index) { 33 | io.rs1_data := io.rd_data 34 | } 35 | when(io.rd_index === io.rs2_index) { 36 | io.rs2_data := io.rd_data 37 | } 38 | } 39 | 40 | io.rf_a0 := 0.U 41 | if (enableDifftest) { 42 | val dt_ar = Module(new DifftestArchIntRegState) 43 | dt_ar.io.clock := clock 44 | dt_ar.io.coreid := io.hartid 45 | dt_ar.io.gpr(0) := 0.U 46 | for (i <- 1 until 32) { 47 | dt_ar.io.gpr(i) := Mux(io.rd_wen && (io.rd_index === i.U) && (io.rd_index =/= 0.U), io.rd_data, access(i)) 48 | } 49 | io.rf_a0 := access(10)(7, 0) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/SRAM.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | 4 | class SRAM(depth: Int, dw: Int) extends Module { 5 | val io = IO(new Bundle { 6 | val en = Input(Bool()) 7 | val addr = Input(UInt(log2Up(depth).W)) 8 | val wdata = Input(UInt(dw.W)) 9 | val wen = Input(Bool()) 10 | val rdata = Output(UInt(dw.W)) 11 | }) 12 | 13 | val array = SyncReadMem(depth, UInt(dw.W)) 14 | 15 | io.rdata := Mux(RegNext(io.en), array.read(io.addr), 0.U) 16 | when(io.en && io.wen) { 17 | array.write(io.addr, io.wdata) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/scala/SimTop.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.diplomacy._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | import org.chipsalliance.cde.config._ 8 | 9 | class SimTop(implicit p: Parameters) extends LazyModule with BindingScope with HasCherrySpringsParameters { 10 | lazy val dts = DTS(bindingTree) 11 | 12 | val soc = for (i <- 0 until numHarts) yield { 13 | val p_ = p.alterPartial({ 14 | case HartID => i 15 | }) 16 | val soc = LazyModule(if (enableSerdes) (new SoC()(p_)) else (new SoCImp()(p_))) 17 | soc 18 | } 19 | val fpga = LazyModule(if (enableSerdes) (new FPGA) else (new FPGAImp)) 20 | 21 | for (i <- 0 until numHarts) { 22 | if (!enableSerdes) { 23 | fpga.node.get(i) := soc(i).node.get 24 | } 25 | } 26 | 27 | lazy val module = new LazyModuleImp(this) { 28 | ElaborationArtefacts.add("dts", dts) 29 | 30 | val io = IO(new Bundle { 31 | val logCtrl = new LogCtrlIO 32 | val perfInfo = new PerfInfoIO 33 | val uart = new UARTIO 34 | }) 35 | 36 | // uart 37 | io.uart <> fpga.module.io.uart 38 | 39 | // interrupt 40 | for (i <- 0 until numHarts) { 41 | soc(i).module.io.intr := fpga.module.io.intr(i) 42 | } 43 | 44 | // data 45 | if (enableSerdes) { 46 | val clock_divider = Module(new ClockDivider2) 47 | val io_clock = clock_divider.io.clk_out 48 | clock_divider.io.clk_in := clock 49 | 50 | fpga.module.io.io_clock.get := io_clock 51 | fpga.module.io.io_reset.get := reset 52 | for (i <- 0 until numHarts) { 53 | soc(i).module.io.io_clock.get := io_clock 54 | soc(i).module.io.io_reset.get := reset 55 | 56 | fpga.module.io.in.get(i) <> soc(i).module.io.out.get 57 | fpga.module.io.out.get(i) <> soc(i).module.io.in.get 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala/SoC.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.diplomacy._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.interrupts._ 7 | import freechips.rocketchip.util._ 8 | import org.chipsalliance.cde.config._ 9 | import testchipip._ 10 | 11 | abstract class SoCAbstract(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 12 | val node: Option[TLIdentityNode] 13 | override lazy val module = new SoCAbstractImp(this) 14 | } 15 | 16 | class SoCAbstractImp[+L <: SoCAbstract](l: L) extends LazyModuleImp(l) with HasCherrySpringsParameters { 17 | val io = IO(new Bundle { 18 | val in = if (enableSerdes) Some(Flipped(Decoupled(UInt(tlSerWidth.W)))) else None 19 | val out = if (enableSerdes) Some(Decoupled(UInt(tlSerWidth.W))) else None 20 | val io_clock = if (enableSerdes) Some(Input(Clock())) else None 21 | val io_reset = if (enableSerdes) Some(Input(Bool())) else None 22 | val intr = Input(new ExternalInterrupt) 23 | }) 24 | } 25 | 26 | class SoCImp(implicit p: Parameters) extends SoCAbstract { 27 | val icache = LazyModule(new ICache) 28 | val dcache = LazyModule(new DCache) 29 | val uncache = LazyModule(new Uncache) 30 | val xbar = LazyModule(new TLXbar(policy = TLArbiter.highestIndexFirst)) 31 | val node = Some(TLIdentityNode()) 32 | 33 | // don't modify order of following nodes 34 | xbar.node := icache.node // 0 (must be the lowest to avoid deadlock) 35 | xbar.node := TLWidthWidget(8) := uncache.node // 1 36 | xbar.node := dcache.node // 2 (must be the highest to ensure correct b channel routing for Sesdes) 37 | node.get := xbar.node 38 | 39 | override lazy val module = new SoCAbstractImp(this) { 40 | val core = Module(new Core) 41 | 42 | // interrupt input 43 | core.io.intr := io.intr 44 | 45 | // instruction cache 46 | icache.module.io.cache <> core.io.imem 47 | icache.module.io.fence_i := core.io.fence_i 48 | 49 | // connect ptw port to data cache 50 | val xbar = Module(new CachePortXBarNto1(3)) 51 | xbar.io.in(0) <> core.io.dmem 52 | xbar.io.in(1) <> core.io.iptw 53 | xbar.io.in(2) <> core.io.dptw 54 | dcache.module.io.cache <> xbar.io.out 55 | 56 | // uncache 57 | uncache.module.io.in <> core.io.uncache 58 | 59 | if (debugBus) { 60 | val (tl, edge) = node.get.out.head 61 | when(tl.a.fire) { 62 | printf(cf"${DebugTimer()} [TL] [Hart ${hartID} - a] ${tl.a.bits}\n") 63 | } 64 | when(tl.b.fire) { 65 | printf(cf"${DebugTimer()} [TL] [Hart ${hartID} - b] ${tl.b.bits}\n") 66 | } 67 | when(tl.c.fire) { 68 | printf(cf"${DebugTimer()} [TL] [Hart ${hartID} - c] ${tl.c.bits}\n") 69 | } 70 | when(tl.d.fire) { 71 | printf(cf"${DebugTimer()} [TL] [Hart ${hartID} - d] ${tl.d.bits}\n") 72 | } 73 | when(tl.e.fire) { 74 | printf(cf"${DebugTimer()} [TL] [Hart ${hartID} - e] ${tl.e.bits}\n") 75 | } 76 | } 77 | } 78 | } 79 | 80 | class SoC(implicit p: Parameters) extends SoCAbstract { 81 | val soc_imp = LazyModule(new SoCImp) 82 | 83 | val node = None 84 | 85 | // serdes 86 | val beatBytes = 32 87 | val serdes = LazyModule( 88 | new TLSerdes( 89 | w = tlSerWidth, 90 | params = Seq( 91 | TLSlaveParameters.v1( 92 | address = Seq(AddressSet(BigInt("00000000", 16), BigInt("ffffffff", 16))), 93 | regionType = RegionType.CACHED, 94 | supportsAcquireT = TransferSizes(1, beatBytes), 95 | supportsAcquireB = TransferSizes(1, beatBytes), 96 | supportsArithmetic = TransferSizes(1, beatBytes), 97 | supportsLogical = TransferSizes(1, beatBytes), 98 | supportsGet = TransferSizes(1, beatBytes), 99 | supportsPutFull = TransferSizes(1, beatBytes), 100 | supportsPutPartial = TransferSizes(1, beatBytes), 101 | supportsHint = TransferSizes(1, beatBytes) 102 | ) 103 | ), 104 | beatBytes = beatBytes, 105 | endSinkId = 42 // magic number 106 | ) 107 | ) 108 | serdes.node := TLTraceBuffer() := soc_imp.node.get 109 | 110 | override lazy val module = new SoCAbstractImp(this) { 111 | val in_fifo = Module(new AsyncQueue(UInt(tlSerWidth.W))) 112 | val out_fifo = Module(new AsyncQueue(UInt(tlSerWidth.W))) 113 | in_fifo.io.enq <> io.in.get 114 | in_fifo.io.enq_clock := io.io_clock.get 115 | in_fifo.io.enq_reset := io.io_reset.get 116 | in_fifo.io.deq <> serdes.module.io.ser.head.in 117 | in_fifo.io.deq_clock := clock 118 | in_fifo.io.deq_reset := reset 119 | out_fifo.io.enq <> serdes.module.io.ser.head.out 120 | out_fifo.io.enq_clock := clock 121 | out_fifo.io.enq_reset := reset 122 | out_fifo.io.deq <> io.out.get 123 | out_fifo.io.deq_clock := io.io_clock.get 124 | out_fifo.io.deq_reset := io.io_reset.get 125 | 126 | // interrupt input (sync_dff triggered by on-chip clock & reset) 127 | val sync_dff = RegInit(VecInit(Seq.fill(3)(0.U.asTypeOf(new ExternalInterrupt)))) 128 | sync_dff(0) := io.intr 129 | sync_dff(1) := sync_dff(0) 130 | sync_dff(2) := sync_dff(1) 131 | soc_imp.module.io.intr := sync_dff(2) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/scala/TLB.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import chisel3.util.random._ 4 | import org.chipsalliance.cde.config._ 5 | import Constant._ 6 | 7 | case object IsITLB extends Field[Boolean] 8 | case object IsDTLB extends Field[Boolean] 9 | 10 | trait Sv39Parameters extends HasCherrySpringsParameters { 11 | val pageTableLevels = 3 12 | val offsetLen = 12 13 | val ppn0Len = 9 14 | val ppn1Len = 9 15 | val ppn2Len = paddrLen - offsetLen - ppn0Len - ppn1Len // 2 16 | val ppnLen = paddrLen - offsetLen // 20 17 | val vpn2Len = 9 18 | val vpn1Len = 9 19 | val vpn0Len = 9 20 | val vpnLen = vpn2Len + vpn1Len + vpn0Len 21 | val asidLen = 16 // not used now 22 | val flagLen = 10 23 | 24 | val tlb4kbEntryLen = flagLen + vpnLen + ppnLen 25 | val tlb2mbEntryLen = tlb4kbEntryLen - vpn0Len - ppn0Len 26 | val tlb1gbEntryLen = tlb2mbEntryLen - vpn1Len - ppn1Len 27 | } 28 | 29 | class TLB(implicit p: Parameters) extends CherrySpringsModule with Sv39Parameters { 30 | val io = IO(new Bundle { 31 | val prv = Input(UInt(2.W)) 32 | val sfence_vma = Input(Bool()) 33 | // read TLB 34 | val vaddr = Input(new Sv39VirtAddr) 35 | val rpte = Output(new Sv39PTE) 36 | val rlevel = Output(UInt(2.W)) 37 | val hit = Output(Bool()) 38 | // write TLB 39 | val wen = Input(Bool()) 40 | val wvaddr = Input(new Sv39VirtAddr) 41 | val wpte = Input(new Sv39PTE) 42 | val wlevel = Input(UInt(2.W)) 43 | // satp 44 | val satp_asid = Input(UInt(16.W)) 45 | }) 46 | 47 | // 0: 4KB, 1: 2MB, 2: 1GB, 3: invalid 48 | assert(io.rlevel =/= 3.U) 49 | assert(io.wlevel =/= 3.U) 50 | 51 | // TLB sizes 52 | val tlb4kb_size = 32 53 | val tlb2mb_size = 8 54 | val tlb1gb_size = 4 55 | assert(tlb4kb_size >= tlb2mb_size && tlb2mb_size >= tlb1gb_size) 56 | 57 | // random replacement 58 | val replace_idx = Wire(UInt(log2Up(tlb4kb_size).W)) 59 | replace_idx := LFSR(log2Up(tlb4kb_size)) 60 | 61 | /* 62 | * TLB - 4 KB page 63 | */ 64 | val array4kb = RegInit(VecInit(Seq.fill(tlb4kb_size)(0.U.asTypeOf(new TLB4KBEntry)))) 65 | val array4kb_valid = RegInit(VecInit(Seq.fill(tlb4kb_size)(false.B))) // not "valid" in PTE 66 | val array4kb_rdata = WireDefault(0.U.asTypeOf(new TLB4KBEntry)) 67 | val array4kb_wdata = Wire(new TLB4KBEntry) 68 | val hit4kb = WireDefault(false.B) 69 | // read 70 | for (i <- 0 until tlb4kb_size) { 71 | when(array4kb_valid(i) && (array4kb(i).vpn() === io.vaddr.vpn())) { 72 | hit4kb := (array4kb(i).asid === io.satp_asid) || array4kb(i).flag.g 73 | array4kb_rdata := array4kb(i) 74 | } 75 | } 76 | // set wdata 77 | array4kb_wdata.flag := io.wpte.flag 78 | array4kb_wdata.vpn2 := io.wvaddr.vpn2 79 | array4kb_wdata.vpn1 := io.wvaddr.vpn1 80 | array4kb_wdata.vpn0 := io.wvaddr.vpn0 81 | array4kb_wdata.ppn2 := io.wpte.ppn2 82 | array4kb_wdata.ppn1 := io.wpte.ppn1 83 | array4kb_wdata.ppn0 := io.wpte.ppn0 84 | array4kb_wdata.asid := io.satp_asid 85 | when(io.wen && (io.wlevel === 0.U)) { 86 | array4kb(replace_idx) := array4kb_wdata 87 | array4kb_valid(replace_idx) := true.B 88 | } 89 | when(io.sfence_vma) { 90 | for (i <- 0 until tlb4kb_size) { 91 | array4kb_valid(i) := false.B 92 | } 93 | } 94 | 95 | /* 96 | * TLB - 2 MB page 97 | */ 98 | val array2mb = RegInit(VecInit(Seq.fill(tlb2mb_size)(0.U.asTypeOf(new TLB2MBEntry)))) 99 | val array2mb_valid = RegInit(VecInit(Seq.fill(tlb2mb_size)(false.B))) 100 | val array2mb_rdata = WireDefault(0.U.asTypeOf(new TLB2MBEntry)) 101 | val array2mb_wdata = Wire(new TLB2MBEntry) 102 | val hit2mb = WireDefault(false.B) 103 | // read 104 | for (i <- 0 until tlb2mb_size) { 105 | when(array2mb_valid(i) && (array2mb(i).vpn2mb() === io.vaddr.vpn2mb())) { 106 | hit2mb := (array2mb(i).asid === io.satp_asid) || array2mb(i).flag.g 107 | array2mb_rdata := array2mb(i) 108 | } 109 | } 110 | // set wdata 111 | array2mb_wdata.flag := io.wpte.flag 112 | array2mb_wdata.vpn2 := io.wvaddr.vpn2 113 | array2mb_wdata.vpn1 := io.wvaddr.vpn1 114 | array2mb_wdata.ppn2 := io.wpte.ppn2 115 | array2mb_wdata.ppn1 := io.wpte.ppn1 116 | array2mb_wdata.asid := io.satp_asid 117 | when(io.wen && (io.wlevel === 1.U)) { 118 | array2mb(replace_idx) := array2mb_wdata 119 | array2mb_valid(replace_idx) := true.B 120 | } 121 | when(io.sfence_vma) { 122 | for (i <- 0 until tlb2mb_size) { 123 | array2mb_valid(i) := false.B 124 | } 125 | } 126 | 127 | /* 128 | * TLB - 1 GB page 129 | */ 130 | val array1gb = RegInit(VecInit(Seq.fill(tlb1gb_size)(0.U.asTypeOf(new TLB1GBEntry)))) 131 | val array1gb_valid = RegInit(VecInit(Seq.fill(tlb1gb_size)(false.B))) 132 | val array1gb_rdata = WireDefault(0.U.asTypeOf(new TLB1GBEntry)) 133 | val array1gb_wdata = Wire(new TLB1GBEntry) 134 | val hit1gb = WireDefault(false.B) 135 | // read 136 | for (i <- 0 until tlb1gb_size) { 137 | when(array1gb_valid(i) && (array1gb(i).vpn1gb() === io.vaddr.vpn1gb())) { 138 | hit1gb := (array1gb(i).asid === io.satp_asid) || array1gb(i).flag.g 139 | array1gb_rdata := array1gb(i) 140 | } 141 | } 142 | // set wdata 143 | array1gb_wdata.flag := io.wpte.flag 144 | array1gb_wdata.vpn2 := io.wvaddr.vpn2 145 | array1gb_wdata.ppn2 := io.wpte.ppn2 146 | array1gb_wdata.asid := io.satp_asid 147 | when(io.wen && (io.wlevel === 2.U)) { 148 | array1gb(replace_idx) := array1gb_wdata 149 | array1gb_valid(replace_idx) := true.B 150 | } 151 | when(io.sfence_vma) { 152 | for (i <- 0 until tlb1gb_size) { 153 | array1gb_valid(i) := false.B 154 | } 155 | } 156 | 157 | // TLB read 158 | io.rpte := 0.U.asTypeOf(new Sv39PTE) 159 | io.rlevel := 0.U 160 | io.hit := hit4kb || hit2mb || hit1gb 161 | when(hit4kb) { 162 | io.rpte.flag := array4kb_rdata.flag 163 | io.rpte.ppn0 := array4kb_rdata.ppn0 164 | io.rpte.ppn1 := array4kb_rdata.ppn1 165 | io.rpte.ppn2 := array4kb_rdata.ppn2 166 | }.elsewhen(hit2mb) { 167 | io.rpte.flag := array2mb_rdata.flag 168 | io.rpte.ppn0 := 0.U 169 | io.rpte.ppn1 := array2mb_rdata.ppn1 170 | io.rpte.ppn2 := array2mb_rdata.ppn2 171 | io.rlevel := 1.U 172 | }.elsewhen(hit1gb) { 173 | io.rpte.flag := array1gb_rdata.flag 174 | io.rpte.ppn0 := 0.U 175 | io.rpte.ppn1 := 0.U 176 | io.rpte.ppn2 := array1gb_rdata.ppn2 177 | io.rlevel := 2.U 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/scala/TLSilentClient.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import freechips.rocketchip.diplomacy._ 3 | import freechips.rocketchip.tilelink._ 4 | import org.chipsalliance.cde.config._ 5 | 6 | class TLSilentClient(implicit p: Parameters) extends LazyModule { 7 | val node = TLClientNode( 8 | Seq( 9 | TLMasterPortParameters.v1( 10 | clients = Seq( 11 | TLMasterParameters.v1( 12 | name = s"tl-silent-client", 13 | sourceId = IdRange(0, 1) 14 | ) 15 | ) 16 | ) 17 | ) 18 | ) 19 | 20 | lazy val module = new TLSilentClientModule(this) 21 | } 22 | 23 | class TLSilentClientModule(outer: TLSilentClient) extends LazyModuleImp(outer) { 24 | val (tl, edge) = outer.node.out.head 25 | 26 | tl.a.valid := false.B 27 | tl.b.ready := false.B 28 | tl.c.valid := false.B 29 | tl.d.ready := false.B 30 | tl.e.valid := false.B 31 | } 32 | 33 | object TLSilentClient { 34 | def apply()(implicit p: Parameters): TLNode = { 35 | val slient_client = LazyModule(new TLSilentClient) 36 | slient_client.node 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/scala/TLTraceBuffer.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.diplomacy._ 4 | import freechips.rocketchip.tilelink._ 5 | import org.chipsalliance.cde.config._ 6 | 7 | class TLTraceBuffer(implicit p: Parameters) extends LazyModule { 8 | val node = TLIdentityNode() 9 | 10 | lazy val module = new TLTraceBufferModule(this) 11 | } 12 | 13 | class TLTraceBufferModule(outer: TLTraceBuffer) extends LazyModuleImp(outer) { 14 | val (tl_in, _) = outer.node.in.head 15 | val (tl_out, _) = outer.node.out.head 16 | 17 | val in_a_scan_bits = RegEnable(tl_in.a.bits, 0.U.asTypeOf(tl_in.a.bits.cloneType), tl_in.a.fire) 18 | val in_a_scan_valid = BoolStopWatch(tl_in.a.fire, tl_out.a.fire, start_high_priority = true) 19 | val out_b_scan_bits = RegEnable(tl_out.b.bits, 0.U.asTypeOf(tl_out.b.bits.cloneType), tl_out.b.fire) 20 | val out_b_scan_valid = BoolStopWatch(tl_out.b.fire, tl_in.b.fire, start_high_priority = true) 21 | val in_c_scan_bits = RegEnable(tl_in.c.bits, 0.U.asTypeOf(tl_in.c.bits.cloneType), tl_in.c.fire) 22 | val in_c_scan_valid = BoolStopWatch(tl_in.c.fire, tl_out.c.fire, start_high_priority = true) 23 | val out_d_scan_bits = RegEnable(tl_out.d.bits, 0.U.asTypeOf(tl_out.d.bits.cloneType), tl_out.d.fire) 24 | val out_d_scan_valid = BoolStopWatch(tl_out.d.fire, tl_in.d.fire, start_high_priority = true) 25 | val in_e_scan_bits = RegEnable(tl_in.e.bits, 0.U.asTypeOf(tl_in.e.bits.cloneType), tl_in.e.fire) 26 | val in_e_scan_valid = BoolStopWatch(tl_in.e.fire, tl_out.e.fire, start_high_priority = true) 27 | 28 | tl_out.a.valid := in_a_scan_valid 29 | tl_out.a.bits := in_a_scan_bits 30 | tl_in.a.ready := !in_a_scan_valid || tl_out.a.fire 31 | tl_in.b.valid := out_b_scan_valid 32 | tl_in.b.bits := out_b_scan_bits 33 | tl_out.b.ready := !out_b_scan_valid || tl_in.b.fire 34 | tl_out.c.valid := in_c_scan_valid 35 | tl_out.c.bits := in_c_scan_bits 36 | tl_in.c.ready := !in_c_scan_valid || tl_out.c.fire 37 | tl_in.d.valid := out_d_scan_valid 38 | tl_in.d.bits := out_d_scan_bits 39 | tl_out.d.ready := !out_d_scan_valid || tl_in.d.fire 40 | tl_out.e.valid := in_e_scan_valid 41 | tl_out.e.bits := in_e_scan_bits 42 | tl_in.e.ready := !in_e_scan_valid || tl_out.e.fire 43 | } 44 | 45 | object TLTraceBuffer { 46 | def apply()(implicit p: Parameters): TLNode = { 47 | val trace_buffer = LazyModule(new TLTraceBuffer) 48 | trace_buffer.node 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala/UART.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import difftest._ 4 | import freechips.rocketchip.diplomacy._ 5 | import freechips.rocketchip.tilelink._ 6 | import org.chipsalliance.cde.config._ 7 | 8 | class UART(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 9 | val device = new SimpleDevice("uartlite", Seq("uartlite-0")) 10 | val beatBytes = 4 11 | val node = TLManagerNode( 12 | Seq( 13 | TLSlavePortParameters.v1( 14 | managers = Seq( 15 | TLSlaveParameters.v1( 16 | address = Seq(AddressSet(BigInt("10000000", 16), BigInt("ffff", 16))), 17 | resources = device.reg, 18 | regionType = RegionType.UNCACHED, 19 | supportsGet = TransferSizes(1, beatBytes), 20 | supportsPutFull = TransferSizes(1, beatBytes), 21 | supportsPutPartial = TransferSizes(1, beatBytes), 22 | fifoId = Some(0) 23 | ) 24 | ), 25 | beatBytes = beatBytes 26 | ) 27 | ) 28 | ) 29 | 30 | lazy val module = new UARTModule(this) 31 | } 32 | 33 | class UARTModule(outer: UART) extends LazyModuleImp(outer) with HasCherrySpringsParameters { 34 | val RxFifoDepth = 4 35 | val TxFifoDepth = 4 36 | 37 | val io = IO(new Bundle { 38 | val uart = new UARTIO 39 | val intr = Output(Bool()) 40 | }) 41 | val (tl, edge) = outer.node.in.head 42 | 43 | val s_req :: s_resp :: Nil = Enum(2) 44 | val state = RegInit(s_req) 45 | 46 | switch(state) { 47 | is(s_req) { 48 | when(tl.a.fire) { 49 | state := s_resp 50 | } 51 | } 52 | is(s_resp) { 53 | when(tl.d.fire) { 54 | state := s_req 55 | } 56 | } 57 | } 58 | 59 | val req = tl.a.bits 60 | val is_get = (req.opcode === TLMessages.Get) 61 | val is_put = (req.opcode === TLMessages.PutFullData) || (req.opcode === TLMessages.PutPartialData) 62 | val req_r = RegInit(0.U.asTypeOf(tl.a.bits)) 63 | val is_get_r = RegInit(false.B) 64 | val is_put_r = RegInit(false.B) 65 | val index_r = req_r.address(3, 2) 66 | 67 | when(tl.a.fire) { 68 | req_r := tl.a.bits 69 | is_get_r := is_get 70 | is_put_r := is_put 71 | } 72 | 73 | val rx = Module(new Queue(UInt(8.W), entries = RxFifoDepth, hasFlush = true)) 74 | val tx = Module(new Queue(UInt(8.W), entries = TxFifoDepth, hasFlush = true)) 75 | 76 | val intr_en = RegInit(false.B) 77 | val tx_just_empty = RegInit(false.B) 78 | 79 | when(tl.a.fire && is_get) { 80 | tx_just_empty := false.B 81 | }.elsewhen(!tx.io.deq.valid && RegNext(tx.io.deq.valid)) { 82 | tx_just_empty := true.B 83 | } 84 | 85 | val rdata = WireDefault(0.U(32.W)) 86 | 87 | // 0x0, read-only 88 | val rx_fifo = Wire(UInt(32.W)) 89 | rx.io.deq.ready := false.B 90 | rx_fifo := rx.io.deq.bits 91 | when(index_r === 0.U) { 92 | rdata := rx_fifo 93 | when(tl.d.fire) { 94 | rx.io.deq.ready := true.B 95 | } 96 | } 97 | 98 | // 0x4, write-only 99 | val tx_fifo = WireDefault(0.U(32.W)) 100 | tx.io.enq.bits := tx_fifo 101 | tx.io.enq.valid := false.B 102 | when(index_r === 1.U) { 103 | tx_fifo := req_r.data 104 | when(tl.d.fire && is_put_r) { 105 | tx.io.enq.valid := true.B 106 | } 107 | } 108 | 109 | // 0x8, read-only 110 | val status = Wire(UInt(32.W)) 111 | val status_rx_fifo_valid_data = Wire(Bool()) 112 | val status_rx_fifo_full = Wire(Bool()) 113 | val status_tx_fifo_empty = Wire(Bool()) 114 | val status_tx_fifo_full = Wire(Bool()) 115 | status_rx_fifo_valid_data := rx.io.deq.valid 116 | status_rx_fifo_full := (rx.io.count === RxFifoDepth.U) 117 | status_tx_fifo_empty := (tx.io.count === 0.U) 118 | status_tx_fifo_full := (tx.io.count === TxFifoDepth.U) 119 | status := Cat( 120 | 0.U(27.W), 121 | intr_en.asUInt, 122 | status_tx_fifo_full, 123 | status_tx_fifo_empty, 124 | status_rx_fifo_full, 125 | status_rx_fifo_valid_data 126 | ) 127 | when(index_r === 2.U) { 128 | rdata := status 129 | } 130 | 131 | // 0xC, write-only 132 | rx.io.flush.get := false.B 133 | tx.io.flush.get := false.B 134 | when(index_r === 3.U) { 135 | when(tl.d.fire && is_put_r) { 136 | // reset tx fifo 137 | when(req_r.data(0)) { 138 | tx.io.flush.get := true.B 139 | } 140 | // reset rx fifo 141 | when(req_r.data(1)) { 142 | rx.io.flush.get := true.B 143 | } 144 | // enable interrupt 145 | when(req_r.data(4)) { 146 | intr_en := true.B 147 | } 148 | } 149 | } 150 | 151 | // interrupt 152 | io.intr := false.B 153 | when(intr_en) { 154 | io.intr := status_rx_fifo_valid_data || tx_just_empty 155 | } 156 | 157 | // output 158 | tx.io.deq.ready := true.B 159 | io.uart.out.valid := tx.io.deq.valid 160 | io.uart.out.ch := tx.io.deq.bits 161 | when(io.uart.out.valid) { 162 | printf("%c", io.uart.out.ch) 163 | } 164 | 165 | // input 166 | rx.io.enq.valid := (io.uart.in.ch =/= "hff".U) 167 | rx.io.enq.bits := io.uart.in.ch 168 | io.uart.in.valid := rx.io.enq.ready 169 | 170 | tl.a.ready := (state === s_req) 171 | tl.d.valid := (state === s_resp) 172 | tl.d.bits := Mux(is_get_r, edge.AccessAck(req_r, rdata), edge.AccessAck(req_r)) 173 | } 174 | -------------------------------------------------------------------------------- /src/main/scala/Utils.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | 4 | object DebugTimer { 5 | def apply() = { 6 | val c = RegInit(0.U(64.W)) 7 | c := c + 1.U 8 | c 9 | } 10 | } 11 | 12 | object MaskExpand { 13 | def apply(x: UInt) = Cat(x.asBools.map(Fill(8, _)).reverse) 14 | } 15 | 16 | object MaskData { 17 | def apply(old_data: UInt, new_data: UInt, mask: UInt) = { 18 | (new_data & mask) | (old_data & (~mask).asUInt) 19 | } 20 | } 21 | 22 | object SignExt32_64 { 23 | def apply(x: UInt): UInt = Cat(Fill(32, x(31)), x) 24 | } 25 | 26 | object ZeroExt32_64 { 27 | def apply(x: UInt): UInt = Cat(Fill(32, 0.U), x) 28 | } 29 | 30 | object SignExt39_64 { 31 | def apply(x: UInt): UInt = Cat(Fill(25, x(38)), x) 32 | } 33 | 34 | object BoolStopWatch { 35 | def apply(start: Bool, stop: Bool, start_high_priority: Boolean = false) = { 36 | val r = RegInit(false.B) 37 | if (start_high_priority) { 38 | when(stop) { r := false.B } 39 | when(start) { r := true.B } 40 | } else { 41 | when(start) { r := true.B } 42 | when(stop) { r := false.B } 43 | } 44 | r 45 | } 46 | } 47 | 48 | object HoldUnless { 49 | def apply[T <: Data](x: T, en: Bool): T = { 50 | Mux(en, x, RegEnable(x, 0.U.asTypeOf(x), en)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/VirtualRam.scala: -------------------------------------------------------------------------------- 1 | import chisel3._ 2 | import chisel3.util._ 3 | import freechips.rocketchip.diplomacy._ 4 | import freechips.rocketchip.tilelink._ 5 | import org.chipsalliance.cde.config._ 6 | import Constant._ 7 | 8 | class VirtualRam256(implicit p: Parameters) extends BlackBox with HasBlackBoxResource { 9 | val io = IO(new Bundle { 10 | val clk = Input(Clock()) 11 | val en = Input(Bool()) 12 | val addr = Input(UInt(64.W)) 13 | val rdata = Output(UInt(256.W)) 14 | val wdata = Input(UInt(256.W)) 15 | val wmask = Input(UInt(32.W)) 16 | val wen = Input(Bool()) 17 | }) 18 | 19 | addResource("/vsrc/VirtualRam256.v") 20 | } 21 | 22 | class TLVirtualRam256(implicit p: Parameters) extends LazyModule with HasCherrySpringsParameters { 23 | val device = new SimpleDevice("virtual-ram-256", Seq()) 24 | val beatBytes = 32 25 | val node = TLManagerNode( 26 | Seq( 27 | TLSlavePortParameters.v1( 28 | managers = Seq( 29 | TLSlaveParameters.v1( 30 | address = Seq(AddressSet(BigInt("080000000", 16), BigInt("7fffffff", 16))), 31 | resources = device.reg, 32 | regionType = RegionType.UNCACHED, 33 | executable = true, 34 | supportsGet = TransferSizes(beatBytes), 35 | supportsPutFull = TransferSizes(beatBytes), 36 | supportsPutPartial = TransferSizes(beatBytes), 37 | fifoId = Some(0) 38 | ) 39 | ), 40 | beatBytes = beatBytes 41 | ) 42 | ) 43 | ) 44 | 45 | lazy val module = new LazyModuleImp(this) { 46 | val (tl, edge) = node.in.head 47 | 48 | val s_idle :: s_access_ack :: Nil = Enum(2) 49 | val state = RegInit(s_idle) 50 | 51 | val req = HoldUnless(tl.a.bits, tl.a.fire) 52 | val is_get = (req.opcode === TLMessages.Get) 53 | val is_put = (req.opcode === TLMessages.PutFullData) || (req.opcode === TLMessages.PutPartialData) 54 | 55 | switch(state) { 56 | is(s_idle) { 57 | when(tl.a.fire) { 58 | state := s_access_ack 59 | } 60 | } 61 | is(s_access_ack) { 62 | when(tl.d.fire) { 63 | state := s_idle 64 | } 65 | } 66 | } 67 | 68 | val vr = Module(new VirtualRam256) 69 | vr.io.clk := clock 70 | vr.io.en := (is_get && tl.d.fire) || (is_put && tl.a.fire) 71 | vr.io.addr := req.address(30, 5) 72 | vr.io.wdata := req.data 73 | vr.io.wmask := req.mask 74 | vr.io.wen := is_put && tl.a.fire 75 | 76 | tl.a.ready := (state === s_idle) 77 | tl.d.valid := (state === s_access_ack) 78 | tl.d.bits := Mux(is_put, edge.AccessAck(req), edge.AccessAck(req, vr.io.rdata)) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/scala/testchipip/Serdes.scala: -------------------------------------------------------------------------------- 1 | package testchipip 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.diplomacy._ 6 | import freechips.rocketchip.util.HellaPeekingArbiter 7 | import freechips.rocketchip.tilelink._ 8 | import org.chipsalliance.cde.config._ 9 | 10 | class SerialIO(val w: Int) extends Bundle { 11 | val in = Flipped(Decoupled(UInt(w.W))) 12 | val out = Decoupled(UInt(w.W)) 13 | 14 | def flipConnect(other: SerialIO) { 15 | in <> other.out 16 | other.in <> out 17 | } 18 | } 19 | 20 | class ValidSerialIO(val w: Int) extends Bundle { 21 | val in = Flipped(Valid(UInt(w.W))) 22 | val out = Valid(UInt(w.W)) 23 | 24 | def flipConnect(other: ValidSerialIO) { 25 | in <> other.out 26 | other.in <> out 27 | } 28 | } 29 | 30 | class StreamChannel(val w: Int) extends Bundle { 31 | val data = UInt(w.W) 32 | val keep = UInt((w / 8).W) 33 | val last = Bool() 34 | } 35 | 36 | class StreamIO(val w: Int) extends Bundle { 37 | val in = Flipped(Decoupled(new StreamChannel(w))) 38 | val out = Decoupled(new StreamChannel(w)) 39 | 40 | def flipConnect(other: StreamIO) { 41 | in <> other.out 42 | other.in <> out 43 | } 44 | } 45 | 46 | class StreamNarrower(inW: Int, outW: Int) extends Module { 47 | require(inW > outW) 48 | require(inW % outW == 0) 49 | 50 | val io = IO(new Bundle { 51 | val in = Flipped(Decoupled(new StreamChannel(inW))) 52 | val out = Decoupled(new StreamChannel(outW)) 53 | }) 54 | 55 | val outBytes = outW / 8 56 | val outBeats = inW / outW 57 | 58 | val bits = Reg(new StreamChannel(inW)) 59 | val count = Reg(UInt(log2Ceil(outBeats).W)) 60 | 61 | val s_recv :: s_send :: Nil = Enum(2) 62 | val state = RegInit(s_recv) 63 | 64 | val nextData = bits.data >> outW.U 65 | val nextKeep = bits.keep >> outBytes.U 66 | 67 | io.in.ready := state === s_recv 68 | io.out.valid := state === s_send 69 | io.out.bits.data := bits.data(outW - 1, 0) 70 | io.out.bits.keep := bits.keep(outBytes - 1, 0) 71 | io.out.bits.last := bits.last && !nextKeep.orR 72 | 73 | when(io.in.fire) { 74 | count := (outBeats - 1).U 75 | bits := io.in.bits 76 | state := s_send 77 | } 78 | 79 | when(io.out.fire) { 80 | count := count - 1.U 81 | bits.data := nextData 82 | bits.keep := nextKeep 83 | when(io.out.bits.last || count === 0.U) { 84 | state := s_recv 85 | } 86 | } 87 | } 88 | 89 | class StreamWidener(inW: Int, outW: Int) extends Module { 90 | require(outW > inW) 91 | require(outW % inW == 0) 92 | 93 | val io = IO(new Bundle { 94 | val in = Flipped(Decoupled(new StreamChannel(inW))) 95 | val out = Decoupled(new StreamChannel(outW)) 96 | }) 97 | 98 | val inBytes = inW / 8 99 | val inBeats = outW / inW 100 | 101 | val data = Reg(Vec(inBeats, UInt(inW.W))) 102 | val keep = RegInit(VecInit(Seq.fill(inBeats)(0.U(inBytes.W)))) 103 | val last = Reg(Bool()) 104 | 105 | val idx = RegInit(0.U(log2Ceil(inBeats).W)) 106 | 107 | val s_recv :: s_send :: Nil = Enum(2) 108 | val state = RegInit(s_recv) 109 | 110 | io.in.ready := state === s_recv 111 | io.out.valid := state === s_send 112 | io.out.bits.data := data.asUInt 113 | io.out.bits.keep := keep.asUInt 114 | io.out.bits.last := last 115 | 116 | when(io.in.fire) { 117 | idx := idx + 1.U 118 | data(idx) := io.in.bits.data 119 | keep(idx) := io.in.bits.keep 120 | when(io.in.bits.last || idx === (inBeats - 1).U) { 121 | last := io.in.bits.last 122 | state := s_send 123 | } 124 | } 125 | 126 | when(io.out.fire) { 127 | idx := 0.U 128 | keep.foreach(_ := 0.U) 129 | state := s_recv 130 | } 131 | } 132 | 133 | object StreamWidthAdapter { 134 | def apply(out: DecoupledIO[StreamChannel], in: DecoupledIO[StreamChannel]) { 135 | if (out.bits.w > in.bits.w) { 136 | val widener = Module(new StreamWidener(in.bits.w, out.bits.w)) 137 | widener.io.in <> in 138 | out <> widener.io.out 139 | } else if (out.bits.w < in.bits.w) { 140 | val narrower = Module(new StreamNarrower(in.bits.w, out.bits.w)) 141 | narrower.io.in <> in 142 | out <> narrower.io.out 143 | } else { 144 | out <> in 145 | } 146 | } 147 | 148 | def apply(a: StreamIO, b: StreamIO) { 149 | apply(a.out, b.out) 150 | apply(b.in, a.in) 151 | } 152 | } 153 | 154 | class ValidStreamIO(w: Int) extends Bundle { 155 | val in = Flipped(Valid(new StreamChannel(w))) 156 | val out = Valid(new StreamChannel(w)) 157 | 158 | def flipConnect(other: ValidStreamIO) { 159 | in <> other.out 160 | other.in <> out 161 | } 162 | 163 | } 164 | 165 | class GenericSerializer[T <: Data](t: T, w: Int) extends Module { 166 | val io = IO(new Bundle { 167 | val in = Flipped(Decoupled(t)) 168 | val out = Decoupled(UInt(w.W)) 169 | }) 170 | 171 | val dataBits = t.getWidth 172 | val dataBeats = (dataBits - 1) / w + 1 173 | val data = Reg(UInt(dataBits.W)) 174 | 175 | val sending = RegInit(false.B) 176 | val (sendCount, sendDone) = Counter(io.out.fire, dataBeats) 177 | 178 | io.in.ready := !sending 179 | io.out.valid := sending 180 | io.out.bits := data(w - 1, 0) 181 | 182 | when(io.in.fire) { 183 | data := io.in.bits.asUInt 184 | sending := true.B 185 | } 186 | 187 | when(io.out.fire) { data := data >> w.U } 188 | 189 | when(sendDone) { sending := false.B } 190 | } 191 | 192 | class GenericDeserializer[T <: Data](t: T, w: Int) extends Module { 193 | val io = IO(new Bundle { 194 | val in = Flipped(Decoupled(UInt(w.W))) 195 | val out = Decoupled(t) 196 | }) 197 | 198 | val dataBits = t.getWidth 199 | val dataBeats = (dataBits - 1) / w + 1 200 | val data = Reg(Vec(dataBeats, UInt(w.W))) 201 | 202 | val receiving = RegInit(true.B) 203 | val (recvCount, recvDone) = Counter(io.in.fire, dataBeats) 204 | 205 | io.in.ready := receiving 206 | io.out.valid := !receiving 207 | io.out.bits := data.asUInt.asTypeOf(t) 208 | 209 | when(io.in.fire) { 210 | data(recvCount) := io.in.bits 211 | } 212 | 213 | when(recvDone) { receiving := false.B } 214 | 215 | when(io.out.fire) { receiving := true.B } 216 | } 217 | 218 | // If hasCorruptDenied is false we revert to earlier TL2 bundles which have an error signal on C and D in the same position as denied in D 219 | class TLMergedBundle(params: TLBundleParameters, hasCorruptDenied: Boolean = true) extends TLBundleBase(params) { 220 | val chanId = UInt(3.W) 221 | val opcode = UInt(3.W) 222 | val param = UInt( 223 | Seq(TLAtomics.width, TLHints.width, TLPermissions.aWidth, TLPermissions.bdWidth, TLPermissions.cWidth).max.W 224 | ) 225 | val size = UInt(params.sizeBits.W) 226 | val source = UInt(params.sourceBits.W) 227 | val address = UInt(params.addressBits.W) 228 | val data = UInt(params.dataBits.W) 229 | val corrupt = if (hasCorruptDenied) Some(Bool()) else None 230 | // either mask or sink+denied (or sink+error if !hasCorruptDenied) 231 | val union = UInt(Seq(params.dataBits / 8, params.sinkBits + 1).max.W) 232 | val last = Bool() 233 | 234 | def isA(dummy: Int = 0) = (chanId === TLMergedBundle.TL_CHAN_ID_A) 235 | def isB(dummy: Int = 0) = (chanId === TLMergedBundle.TL_CHAN_ID_B) 236 | def isC(dummy: Int = 0) = (chanId === TLMergedBundle.TL_CHAN_ID_C) 237 | def isD(dummy: Int = 0) = (chanId === TLMergedBundle.TL_CHAN_ID_D) 238 | def isE(dummy: Int = 0) = (chanId === TLMergedBundle.TL_CHAN_ID_E) 239 | 240 | } 241 | 242 | object TLMergedBundle { 243 | val TL_CHAN_ID_A = 0.U 244 | val TL_CHAN_ID_B = 1.U 245 | val TL_CHAN_ID_C = 2.U 246 | val TL_CHAN_ID_D = 3.U 247 | val TL_CHAN_ID_E = 4.U 248 | 249 | def apply(a: TLBundleA, hasCorruptDenied: Boolean): TLMergedBundle = apply(a, a.params, hasCorruptDenied) 250 | 251 | def apply(a: TLBundleA, params: TLBundleParameters, hasCorruptDenied: Boolean): TLMergedBundle = { 252 | val merged = Wire(new TLMergedBundle(params, hasCorruptDenied)) 253 | merged.chanId := TL_CHAN_ID_A 254 | merged.opcode := a.opcode 255 | merged.param := a.param 256 | merged.size := a.size 257 | merged.source := a.source 258 | merged.address := a.address 259 | merged.data := a.data 260 | if (hasCorruptDenied) 261 | merged.corrupt.get := a.corrupt 262 | merged.union := a.mask 263 | merged.last := true.B 264 | merged 265 | } 266 | 267 | def apply(b: TLBundleB, hasCorruptDenied: Boolean): TLMergedBundle = apply(b, b.params, hasCorruptDenied) 268 | 269 | def apply(b: TLBundleB, params: TLBundleParameters, hasCorruptDenied: Boolean): TLMergedBundle = { 270 | val merged = Wire(new TLMergedBundle(params, hasCorruptDenied)) 271 | merged.chanId := TL_CHAN_ID_B 272 | merged.opcode := b.opcode 273 | merged.param := b.param 274 | merged.size := b.size 275 | merged.source := b.source 276 | merged.address := b.address 277 | merged.data := b.data 278 | if (hasCorruptDenied) 279 | merged.corrupt.get := b.corrupt 280 | merged.union := b.mask 281 | merged.last := true.B 282 | merged 283 | } 284 | 285 | def apply(c: TLBundleC, hasCorruptDenied: Boolean): TLMergedBundle = apply(c, c.params, hasCorruptDenied) 286 | 287 | def apply(c: TLBundleC, params: TLBundleParameters, hasCorruptDenied: Boolean): TLMergedBundle = { 288 | val merged = Wire(new TLMergedBundle(params, hasCorruptDenied)) 289 | merged.chanId := TL_CHAN_ID_C 290 | merged.opcode := c.opcode 291 | merged.param := c.param 292 | merged.size := c.size 293 | merged.source := c.source 294 | merged.address := c.address 295 | merged.data := c.data 296 | if (hasCorruptDenied) { 297 | merged.corrupt.get := c.corrupt 298 | merged.union := DontCare 299 | } else { 300 | merged.union := 0.U //error 301 | } 302 | merged.last := true.B 303 | merged 304 | } 305 | 306 | def apply(d: TLBundleD, hasCorruptDenied: Boolean): TLMergedBundle = apply(d, d.params, hasCorruptDenied) 307 | 308 | def apply(d: TLBundleD, params: TLBundleParameters, hasCorruptDenied: Boolean): TLMergedBundle = { 309 | val merged = Wire(new TLMergedBundle(params, hasCorruptDenied)) 310 | merged.chanId := TL_CHAN_ID_D 311 | merged.opcode := d.opcode 312 | merged.param := d.param 313 | merged.size := d.size 314 | merged.source := d.source 315 | merged.address := DontCare 316 | merged.data := d.data 317 | if (hasCorruptDenied) { 318 | merged.corrupt.get := d.corrupt 319 | merged.union := Cat(d.sink, d.denied) 320 | } else { 321 | merged.union := Cat(d.sink, 0.U) //error 322 | } 323 | merged.last := true.B 324 | merged 325 | } 326 | 327 | def apply(e: TLBundleE, hasCorruptDenied: Boolean): TLMergedBundle = apply(e, e.params, hasCorruptDenied) 328 | 329 | def apply(e: TLBundleE, params: TLBundleParameters, hasCorruptDenied: Boolean): TLMergedBundle = { 330 | val merged = Wire(new TLMergedBundle(params, hasCorruptDenied)) 331 | merged.chanId := TL_CHAN_ID_E 332 | merged.opcode := 0.U 333 | merged.param := 0.U 334 | merged.size := 0.U 335 | merged.source := 0.U 336 | merged.address := 0.U 337 | merged.data := 0.U 338 | if (hasCorruptDenied) { 339 | merged.corrupt.get := DontCare 340 | merged.union := Cat(e.sink, false.B) 341 | } else { 342 | merged.union := Cat(e.sink) 343 | } 344 | merged.last := true.B 345 | merged 346 | } 347 | 348 | def apply( 349 | chan: DecoupledIO[TLChannel], 350 | hasCorruptDenied: Boolean 351 | )( 352 | implicit edge: TLEdge 353 | ): DecoupledIO[TLMergedBundle] = 354 | apply(chan, chan.bits.params, hasCorruptDenied) 355 | 356 | def apply( 357 | chan: DecoupledIO[TLChannel], 358 | params: TLBundleParameters, 359 | hasCorruptDenied: Boolean 360 | )( 361 | implicit edge: TLEdge 362 | ): DecoupledIO[TLMergedBundle] = { 363 | val merged = Wire(Decoupled(new TLMergedBundle(params))) 364 | merged.valid := chan.valid 365 | merged.bits := (chan.bits match { 366 | case (a: TLBundleA) => apply(a, params, hasCorruptDenied) 367 | case (b: TLBundleB) => apply(b, params, hasCorruptDenied) 368 | case (c: TLBundleC) => apply(c, params, hasCorruptDenied) 369 | case (d: TLBundleD) => apply(d, params, hasCorruptDenied) 370 | case (e: TLBundleE) => apply(e, params, hasCorruptDenied) 371 | }) 372 | merged.bits.last := edge.last(chan) 373 | chan.ready := merged.ready 374 | merged 375 | } 376 | 377 | def toA(chan: TLMergedBundle, hasCorruptDenied: Boolean): TLBundleA = toA(chan, chan.params, hasCorruptDenied) 378 | 379 | def toA(chan: TLMergedBundle, params: TLBundleParameters, hasCorruptDenied: Boolean): TLBundleA = { 380 | val a = Wire(new TLBundleA(params)) 381 | a.opcode := chan.opcode 382 | a.param := chan.param 383 | a.size := chan.size 384 | a.source := chan.source 385 | a.address := chan.address 386 | a.data := chan.data 387 | if (hasCorruptDenied) 388 | a.corrupt := chan.corrupt.get 389 | else 390 | a.corrupt := false.B 391 | a.mask := chan.union 392 | a 393 | } 394 | 395 | def toA(chan: DecoupledIO[TLMergedBundle], hasCorruptDenied: Boolean): DecoupledIO[TLBundleA] = 396 | toA(chan, chan.bits.params, hasCorruptDenied) 397 | 398 | def toA( 399 | chan: DecoupledIO[TLMergedBundle], 400 | params: TLBundleParameters, 401 | hasCorruptDenied: Boolean 402 | ): DecoupledIO[TLBundleA] = { 403 | val a = Wire(Decoupled(new TLBundleA(params))) 404 | a.valid := chan.valid 405 | a.bits := apply(a.bits, params, hasCorruptDenied) 406 | chan.ready := a.ready 407 | a 408 | } 409 | 410 | def toB(chan: TLMergedBundle, hasCorruptDenied: Boolean): TLBundleB = toB(chan, chan.params, hasCorruptDenied) 411 | 412 | def toB(chan: TLMergedBundle, params: TLBundleParameters, hasCorruptDenied: Boolean): TLBundleB = { 413 | val b = Wire(new TLBundleB(params)) 414 | b.opcode := chan.opcode 415 | b.param := chan.param 416 | b.size := chan.size 417 | b.source := chan.source 418 | b.address := chan.address 419 | b.data := chan.data 420 | if (hasCorruptDenied) 421 | b.corrupt := chan.corrupt.get 422 | else 423 | b.corrupt := false.B 424 | b.mask := chan.union 425 | b 426 | } 427 | 428 | def toB(chan: DecoupledIO[TLMergedBundle], hasCorruptDenied: Boolean): DecoupledIO[TLBundleB] = 429 | toB(chan, chan.bits.params, hasCorruptDenied) 430 | 431 | def toB( 432 | chan: DecoupledIO[TLMergedBundle], 433 | params: TLBundleParameters, 434 | hasCorruptDenied: Boolean 435 | ): DecoupledIO[TLBundleB] = { 436 | val b = Wire(Decoupled(new TLBundleB(params))) 437 | b.valid := chan.valid 438 | b.bits := apply(b.bits, hasCorruptDenied) 439 | chan.ready := b.ready 440 | b 441 | } 442 | 443 | def toC(chan: TLMergedBundle, hasCorruptDenied: Boolean): TLBundleC = toC(chan, chan.params, hasCorruptDenied) 444 | 445 | def toC(chan: TLMergedBundle, params: TLBundleParameters, hasCorruptDenied: Boolean): TLBundleC = { 446 | val c = Wire(new TLBundleC(params)) 447 | c.opcode := chan.opcode 448 | c.param := chan.param 449 | c.size := chan.size 450 | c.source := chan.source 451 | c.address := chan.address 452 | c.data := chan.data 453 | if (hasCorruptDenied) 454 | c.corrupt := chan.corrupt.get 455 | else 456 | c.corrupt := false.B 457 | c 458 | } 459 | 460 | def toC(chan: DecoupledIO[TLMergedBundle], hasCorruptDenied: Boolean): DecoupledIO[TLBundleC] = 461 | toC(chan, chan.bits.params, hasCorruptDenied) 462 | 463 | def toC( 464 | chan: DecoupledIO[TLMergedBundle], 465 | params: TLBundleParameters, 466 | hasCorruptDenied: Boolean 467 | ): DecoupledIO[TLBundleC] = { 468 | val c = Wire(Decoupled(new TLBundleC(params))) 469 | c.valid := chan.valid 470 | c.bits := apply(c.bits, hasCorruptDenied) 471 | chan.ready := c.ready 472 | c 473 | } 474 | 475 | def toD(chan: TLMergedBundle, hasCorruptDenied: Boolean): TLBundleD = toD(chan, chan.params, hasCorruptDenied) 476 | 477 | def toD(chan: TLMergedBundle, params: TLBundleParameters, hasCorruptDenied: Boolean): TLBundleD = { 478 | val d = Wire(new TLBundleD(params)) 479 | d.opcode := chan.opcode 480 | d.param := chan.param 481 | d.size := chan.size 482 | d.source := chan.source 483 | d.data := chan.data 484 | if (hasCorruptDenied) { 485 | d.corrupt := chan.corrupt.get 486 | d.sink := chan.union >> 1.U 487 | d.denied := chan.union(0) 488 | } else { 489 | d.corrupt := false.B 490 | d.sink := chan.union >> 1.U // error 491 | d.denied := false.B 492 | } 493 | d 494 | } 495 | 496 | def toD(chan: DecoupledIO[TLMergedBundle], hasCorruptDenied: Boolean): DecoupledIO[TLBundleD] = 497 | toD(chan, chan.bits.params, hasCorruptDenied) 498 | 499 | def toD( 500 | chan: DecoupledIO[TLMergedBundle], 501 | params: TLBundleParameters, 502 | hasCorruptDenied: Boolean 503 | ): DecoupledIO[TLBundleD] = { 504 | val d = Wire(Decoupled(new TLBundleD(params))) 505 | d.valid := chan.valid 506 | d.bits := apply(d.bits, hasCorruptDenied) 507 | chan.ready := d.ready 508 | d 509 | } 510 | 511 | def toE(chan: TLMergedBundle, hasCorruptDenied: Boolean): TLBundleE = toE(chan, chan.params, hasCorruptDenied) 512 | 513 | def toE(chan: TLMergedBundle, params: TLBundleParameters, hasCorruptDenied: Boolean): TLBundleE = { 514 | val e = Wire(new TLBundleE(params)) 515 | if (hasCorruptDenied) 516 | e.sink := chan.union >> 1.U 517 | else 518 | e.sink := chan.union 519 | e 520 | } 521 | 522 | def toE(chan: DecoupledIO[TLMergedBundle], hasCorruptDenied: Boolean): DecoupledIO[TLBundleE] = 523 | toE(chan, chan.bits.params, hasCorruptDenied) 524 | 525 | def toE( 526 | chan: DecoupledIO[TLMergedBundle], 527 | params: TLBundleParameters, 528 | hasCorruptDenied: Boolean 529 | ): DecoupledIO[TLBundleE] = { 530 | val e = Wire(Decoupled(new TLBundleE(params))) 531 | e.valid := chan.valid 532 | e.bits := apply(e.bits, hasCorruptDenied) 533 | chan.ready := e.ready 534 | e 535 | } 536 | } 537 | 538 | class TLSerdes( 539 | w: Int, 540 | params: Seq[TLManagerParameters], 541 | beatBytes: Int = 8, 542 | endSinkId: Int = 0, 543 | hasCorruptDenied: Boolean = true 544 | )( 545 | implicit p: Parameters) 546 | extends LazyModule { 547 | 548 | val node = TLManagerNode( 549 | params.map(manager => 550 | TLSlavePortParameters.v1(managers = Seq(manager), beatBytes = beatBytes, endSinkId = endSinkId) 551 | ) 552 | ) 553 | 554 | lazy val module = new Impl 555 | class Impl extends LazyModuleImp(this) { 556 | val nChannels = params.size 557 | val io = IO(new Bundle { 558 | val ser = Vec(nChannels, new SerialIO(w)) 559 | }) 560 | 561 | val mergeTypes = new Array[TLMergedBundle](nChannels) 562 | 563 | node.in.zip(io.ser).zipWithIndex.foreach { 564 | case (((tl, edge), ser), i) => 565 | val mergeType = new TLMergedBundle(tl.params, hasCorruptDenied) 566 | 567 | val outChannels = Seq(tl.e, tl.c, tl.a).map(TLMergedBundle(_, hasCorruptDenied)(edge)) 568 | val outArb = Module(new HellaPeekingArbiter(mergeType, outChannels.size, (b: TLMergedBundle) => b.last)) 569 | val outSer = Module(new GenericSerializer(mergeType, w)) 570 | outArb.io.in <> outChannels 571 | outSer.io.in <> outArb.io.out 572 | ser.out <> outSer.io.out 573 | 574 | val inDes = Module(new GenericDeserializer(mergeType, w)) 575 | inDes.io.in <> ser.in 576 | tl.b.valid := inDes.io.out.valid && inDes.io.out.bits.isB() 577 | tl.b.bits := TLMergedBundle.toB(inDes.io.out.bits, hasCorruptDenied) 578 | tl.d.valid := inDes.io.out.valid && inDes.io.out.bits.isD() 579 | tl.d.bits := TLMergedBundle.toD(inDes.io.out.bits, hasCorruptDenied) 580 | inDes.io.out.ready := MuxLookup( 581 | inDes.io.out.bits.chanId, 582 | false.B, 583 | Seq(TLMergedBundle.TL_CHAN_ID_B -> tl.b.ready, TLMergedBundle.TL_CHAN_ID_D -> tl.d.ready) 584 | ) 585 | 586 | mergeTypes(i) = mergeType 587 | } 588 | } 589 | } 590 | 591 | class TLDesser(w: Int, params: Seq[TLClientParameters], hasCorruptDenied: Boolean = true)(implicit p: Parameters) 592 | extends LazyModule { 593 | 594 | val node = TLClientNode(params.map(client => TLMasterPortParameters.v1(Seq(client)))) 595 | 596 | lazy val module = new Impl 597 | class Impl extends LazyModuleImp(this) { 598 | val nChannels = params.size 599 | val io = IO(new Bundle { 600 | val ser = Vec(nChannels, new SerialIO(w)) 601 | }) 602 | 603 | val mergeTypes = new Array[TLMergedBundle](nChannels) 604 | 605 | node.out.zip(io.ser).zipWithIndex.foreach { 606 | case (((tl, edge), ser), i) => 607 | val mergeType = new TLMergedBundle(tl.params, hasCorruptDenied) 608 | 609 | val outChannels = Seq(tl.d, tl.b).map(TLMergedBundle(_, hasCorruptDenied)(edge)) 610 | val outArb = Module(new HellaPeekingArbiter(mergeType, outChannels.size, (b: TLMergedBundle) => b.last)) 611 | val outSer = Module(new GenericSerializer(mergeType, w)) 612 | outArb.io.in <> outChannels 613 | outSer.io.in <> outArb.io.out 614 | ser.out <> outSer.io.out 615 | 616 | val inDes = Module(new GenericDeserializer(mergeType, w)) 617 | inDes.io.in <> ser.in 618 | tl.a.valid := inDes.io.out.valid && inDes.io.out.bits.isA() 619 | tl.a.bits := TLMergedBundle.toA(inDes.io.out.bits, hasCorruptDenied) 620 | tl.c.valid := inDes.io.out.valid && inDes.io.out.bits.isC() 621 | tl.c.bits := TLMergedBundle.toC(inDes.io.out.bits, hasCorruptDenied) 622 | tl.e.valid := inDes.io.out.valid && inDes.io.out.bits.isE() 623 | tl.e.bits := TLMergedBundle.toE(inDes.io.out.bits, hasCorruptDenied) 624 | inDes.io.out.ready := MuxLookup( 625 | inDes.io.out.bits.chanId, 626 | false.B, 627 | Seq( 628 | TLMergedBundle.TL_CHAN_ID_A -> tl.a.ready, 629 | TLMergedBundle.TL_CHAN_ID_C -> tl.c.ready, 630 | TLMergedBundle.TL_CHAN_ID_E -> tl.e.ready 631 | ) 632 | ) 633 | 634 | mergeTypes(i) = mergeType 635 | } 636 | } 637 | } 638 | 639 | class TLSerdesser( 640 | w: Int, 641 | clientPortParams: TLMasterPortParameters, 642 | managerPortParams: TLSlavePortParameters, 643 | hasCorruptDenied: Boolean = true 644 | )( 645 | implicit p: Parameters) 646 | extends LazyModule { 647 | val clientNode = TLClientNode(Seq(clientPortParams)) 648 | val managerNode = TLManagerNode(Seq(managerPortParams)) 649 | 650 | lazy val module = new Impl 651 | class Impl extends LazyModuleImp(this) { 652 | val io = IO(new Bundle { 653 | val ser = new SerialIO(w) 654 | }) 655 | 656 | val (client_tl, client_edge) = clientNode.out(0) 657 | val (manager_tl, manager_edge) = managerNode.in(0) 658 | 659 | val clientParams = client_edge.bundle 660 | val managerParams = manager_edge.bundle 661 | val mergedParams = clientParams.union(managerParams) 662 | val mergeType = new TLMergedBundle(mergedParams, hasCorruptDenied) 663 | 664 | val outChannels = Seq(manager_tl.e, client_tl.d, manager_tl.c, client_tl.b, manager_tl.a) 665 | val outArb = Module(new HellaPeekingArbiter(mergeType, outChannels.size, (b: TLMergedBundle) => b.last)) 666 | val outSer = Module(new GenericSerializer(mergeType, w)) 667 | outArb.io.in <> outChannels.map(TLMergedBundle(_, mergedParams, hasCorruptDenied)(client_edge)) 668 | outSer.io.in <> outArb.io.out 669 | io.ser.out <> outSer.io.out 670 | 671 | val inDes = Module(new GenericDeserializer(mergeType, w)) 672 | inDes.io.in <> io.ser.in 673 | client_tl.a.valid := inDes.io.out.valid && inDes.io.out.bits.isA() 674 | client_tl.a.bits := TLMergedBundle.toA(inDes.io.out.bits, clientParams, hasCorruptDenied) 675 | manager_tl.b.valid := inDes.io.out.valid && inDes.io.out.bits.isB() 676 | manager_tl.b.bits := TLMergedBundle.toB(inDes.io.out.bits, managerParams, hasCorruptDenied) 677 | client_tl.c.valid := inDes.io.out.valid && inDes.io.out.bits.isC() 678 | client_tl.c.bits := TLMergedBundle.toC(inDes.io.out.bits, clientParams, hasCorruptDenied) 679 | manager_tl.d.valid := inDes.io.out.valid && inDes.io.out.bits.isD() 680 | manager_tl.d.bits := TLMergedBundle.toD(inDes.io.out.bits, managerParams, hasCorruptDenied) 681 | client_tl.e.valid := inDes.io.out.valid && inDes.io.out.bits.isE() 682 | client_tl.e.bits := TLMergedBundle.toE(inDes.io.out.bits, clientParams, hasCorruptDenied) 683 | inDes.io.out.ready := MuxLookup( 684 | inDes.io.out.bits.chanId, 685 | false.B, 686 | Seq( 687 | TLMergedBundle.TL_CHAN_ID_A -> client_tl.a.ready, 688 | TLMergedBundle.TL_CHAN_ID_B -> manager_tl.b.ready, 689 | TLMergedBundle.TL_CHAN_ID_C -> client_tl.c.ready, 690 | TLMergedBundle.TL_CHAN_ID_D -> manager_tl.d.ready, 691 | TLMergedBundle.TL_CHAN_ID_E -> client_tl.e.ready 692 | ) 693 | ) 694 | } 695 | } 696 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function tests { 4 | bin_files=`eval "find $1 -mindepth 1 -maxdepth 1 -regex \".*\.\(bin\)\""` 5 | for bin_file in $bin_files; do 6 | file_name=`basename ${bin_file%.*}` 7 | printf "[%30s] " $file_name 8 | log_file=./build/$file_name.log 9 | ./build/emu --diff=../NEMU/build/riscv64-nemu-interpreter-so -i $bin_file &> $log_file 10 | if (grep 'HIT GOOD TRAP' $log_file > /dev/null) then 11 | echo -e "\033[1;32mPASS!\033[0m" 12 | rm $log_file 13 | else 14 | echo -e "\033[1;31mFAIL!\033[0m see $log_file for more information" 15 | fi 16 | done 17 | wait 18 | } 19 | 20 | while getopts 'r:t:' OPT; do 21 | case $OPT in 22 | r) 23 | dir="$OPTARG" 24 | tests ${dir};; 25 | t) 26 | test="$OPTARG" 27 | ./build/emu -i ${test} --dump-wave --wave-path=./build/wave.vcd;; 28 | ?) 29 | echo "Error: unknown arguments" 30 | esac 31 | done 32 | --------------------------------------------------------------------------------