├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile.in ├── README.md ├── README2 ├── build.sbt ├── configure ├── configure.ac ├── doc ├── 1stage.pdf ├── 2stage.pdf ├── lab1.pdf ├── sodor.pdf ├── sodor.png ├── zscale.pdf └── zscale.png ├── prefix.mk.in ├── project ├── build.properties └── check ├── riscv-bmarks ├── dhrystone.riscv ├── dhrystone.riscv.dump ├── median.riscv ├── median.riscv.dump ├── multiply.riscv ├── multiply.riscv.dump ├── qsort.riscv ├── qsort.riscv.dump ├── rsort.riscv ├── rsort.riscv.dump ├── towers.riscv ├── towers.riscv.dump ├── vvadd.riscv └── vvadd.riscv.dump ├── scripts ├── instructions.py └── tracer.py ├── src └── main │ ├── resources │ └── sodor │ │ └── vsrc │ │ └── SimDTM.v │ └── scala │ └── sodor │ ├── common │ ├── abstract_commands.scala │ ├── consts.scala │ ├── debug.scala │ ├── dm_registers.scala │ ├── instructions.scala │ ├── master_adapter.scala │ ├── memory.scala │ ├── package.scala │ ├── scratchpad_adapter.scala │ ├── sodor_internal_tile.scala │ ├── sodor_tile.scala │ └── util.scala │ ├── rv32_1stage │ ├── consts.scala │ ├── core.scala │ ├── cpath.scala │ ├── dpath.scala │ └── package.scala │ ├── rv32_2stage │ ├── consts.scala │ ├── core.scala │ ├── cpath.scala │ ├── dpath.scala │ └── package.scala │ ├── rv32_3stage │ ├── alu.scala │ ├── arbiter.scala │ ├── consts.scala │ ├── core.scala │ ├── cpath.scala │ ├── dpath.scala │ ├── frontend.scala │ └── package.scala │ ├── rv32_5stage │ ├── consts.scala │ ├── core.scala │ ├── cpath.scala │ ├── dpath.scala │ ├── package.scala │ └── regfile.scala │ └── rv32_ucode │ ├── consts.scala │ ├── core.scala │ ├── cpath.scala │ ├── dpath.scala │ ├── microcode.scala │ ├── microcodecompiler.scala │ └── package.scala └── test ├── .gitignore ├── custom-bmarks ├── Makefile ├── crt.S └── mix.c ├── custom-tests ├── Makefile ├── movn.S └── yourinst.S └── env ├── encoding.h ├── p ├── link.ld └── riscv_test.h ├── test.ld └── test_macros.h /.gitignore: -------------------------------------------------------------------------------- 1 | config.status 2 | Makefile 3 | prefix.mk 4 | *.tar.gz 5 | *.pyc 6 | *.swp 7 | *.vcd 8 | *.vpd 9 | .DS_Store 10 | */target 11 | *.log 12 | target 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/.gitmodules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Sodor licence terms 2 | 3 | Copyright (c); 2011, 2012, 2013 The Regents of the University of 4 | California (Regents). All Rights Reserved. Redistribution and use in 5 | source and binary forms, with or without modification, are permitted 6 | provided that the following conditions are met: 7 | 8 | o Redistributions of source code must retain the above 9 | copyright notice, this list of conditions and the following 10 | two paragraphs of disclaimer. 11 | 12 | o Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | two paragraphs of disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | o Neither the name of the Regents nor the names of its contributors 18 | may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 22 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, 23 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 24 | REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 27 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF 29 | ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION 30 | TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 31 | MODIFICATIONS. 32 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | #======================================================================= 2 | # UCB Chisel C++ Simulator Generator: Makefile 3 | #----------------------------------------------------------------------- 4 | # Christopher Celio (celio@eecs.berkeley.edu) 5 | # 6 | # This makefile will generate a processor emulator from chisel code. 7 | # 8 | # Many different processors are provided. To switch between which processor(s) 9 | # you would like to build, simply set the $(targets) variable as appropriate. 10 | 11 | all_targets := rv32_1stage rv32_2stage rv32_3stage rv32_5stage rv32_ucode 12 | targets := $(all_targets) 13 | 14 | # To switch between which processor the Makefile would like to build, it will 15 | # change the bash environment variable $MK_TARGET_PROC as follows: 16 | # "export MK_TARGET_PROC=rv32_1stage" 17 | # "export MK_TARGET_PROC=rv32_2stage" 18 | # "export MK_TARGET_PROC=rv32_5stage" 19 | # "export MK_TARGET_PROC=rv32_ucode" 20 | 21 | include @top_builddir@/prefix.mk 22 | 23 | MK_TARGET_PROC?=rv32_1stage 24 | 25 | prerequisites := $(if $(chiseldir),chisel-timestamp) 26 | 27 | all: $(prerequisites) $(patsubst %,emulator/%/emulator,$(targets)) 28 | 29 | install: all 30 | install -d $(RISCV)/bin 31 | $(if $(findstring rv32_1stage,$(targets)),install -s -p -m 755 emulator/rv32_1stage/emulator $(RISCV)/bin/rv32_1stage-emulator) 32 | $(if $(findstring rv32_2stage,$(targets)),install -s -p -m 755 emulator/rv32_2stage/emulator $(RISCV)/bin/rv32_2stage-emulator) 33 | $(if $(findstring rv32_3stage,$(targets)),install -s -p -m 755 emulator/rv32_3stage/emulator $(RISCV)/bin/rv32_3stage-emulator) 34 | $(if $(findstring rv32_5stage,$(targets)),install -s -p -m 755 emulator/rv32_5stage/emulator $(RISCV)/bin/rv32_5stage-emulator) 35 | $(if $(findstring rv32_ucode,$(targets)),install -s -p -m 755 emulator/rv32_ucode/emulator $(RISCV)/bin/rv32_ucode-emulator) 36 | 37 | dist-src: 38 | cd $(srcDir) && git archive --prefix=sodor/ -o $(buildDir)/sodor-`git rev-parse --short HEAD`.tar.gz HEAD 39 | 40 | 41 | compile: 42 | $(SBT) "project ${MK_TARGET_PROC}" compile 43 | 44 | shell: 45 | $(SBT) "project ${MK_TARGET_PROC}" shell 46 | 47 | debug: 48 | $(SBT) "project ${MK_TARGET_PROC}" "last run" 49 | 50 | console: 51 | $(SBT) "project ${MK_TARGET_PROC}" console 52 | 53 | timestamps := $(foreach x,$(targets),emulator/$(x)/generated-src/timestamp) 54 | timestamps_debug := $(foreach x,$(targets),emulator/$(x)/generated-src-debug/timestamp) 55 | 56 | run-emulator: $(timestamps) 57 | 58 | run-emulator-debug: $(timestamps_debug) 59 | 60 | clean-tests: 61 | for d in $(addprefix emulator/,$(all_targets)) ; do \ 62 | $(MAKE) -C "$${d}" clean-tests ; \ 63 | done 64 | $(RM) $(timestamps) $(timestamps_debug) 65 | 66 | clean: 67 | -find sbt -type d -name target -exec rm -rf {} \+ 68 | for d in $(addprefix emulator/,$(all_targets)) ; do \ 69 | $(MAKE) -C "$${d}" clean ; \ 70 | done 71 | $(RM) $(timestamps) $(timestamps_debug) 72 | $(RM) test-results.xml 73 | 74 | reports: test-results.xml report-cpi report-bp report-stats 75 | 76 | report-cpi: $(patsubst %,%-report-cpi,$(targets)) 77 | 78 | report-bp: $(patsubst %,%-report-bp,$(targets)) 79 | 80 | report-stats: $(patsubst %,%-report-stats,$(targets)) 81 | 82 | chisel-timestamp: $(wildcard $(chiseldir)/src/main/scala/*.scala) 83 | cd $(chiseldir) && $(SBT) publish-local 84 | date > $@ 85 | 86 | test-results.xml: $(wildcard $(patsubst %,emulator/%/output/*.out,$(targets))) 87 | $(srcDir)/project/check $^ > test-results.xml 88 | 89 | %-report-cpi: 90 | -grep CPI emulator/$(patsubst %-report-cpi,%,$@)/output/*.out 91 | 92 | %-report-bp: 93 | -grep Acc emulator/$(patsubst %-report-bp,%,$@)/output/*.out 94 | 95 | %-report-stats: 96 | -grep "#" emulator/$(patsubst %-report-stats,%,$@)/output/*.out 97 | 98 | emulator/%/generated-src/timestamp: emulator/%/emulator 99 | @echo 100 | @echo running basedir/Makefile: make run-emulator 101 | @echo 102 | $(MAKE) -C $(dir $<) run 103 | install -d $(dir $@) 104 | date > $@ 105 | 106 | emulator/%/generated-src-debug/timestamp: emulator/%/emulator-debug 107 | @echo 108 | @echo running basedir/Makefile: make run-emulator-debug 109 | @echo 110 | $(MAKE) -C $(dir $<) run-debug 111 | install -d $(dir $@) 112 | date > $@ 113 | 114 | emulator/%/emulator: $(prerequisites) 115 | $(MAKE) -C $(dir $@) 116 | 117 | emulator/%/emulator-debug: $(prerequisites) 118 | $(MAKE) -C $(dir $@) emulator-debug 119 | 120 | .PHONY: all install dist-src compile shell debug console 121 | .PHONY: run-emulator run-emulator-debug target clean clean-tests 122 | .PHONY: reports report-cpi report-bp report-stats 123 | 124 | # Because we are using recursive makefiles and emulator is an actual file. 125 | emulator/rv32_1stage/emulator: $(wildcard $(srcDir)/src/common/*.scala) \ 126 | $(wildcard $(srcDir)/src/rv32_1stage/*.scala) 127 | 128 | emulator/rv32_2stage/emulator: $(wildcard $(srcDir)/src/common/*.scala) \ 129 | $(wildcard $(srcDir)/src/rv32_2stage/*.scala) 130 | 131 | emulator/rv32_3stage/emulator: $(wildcard $(srcDir)/src/common/*.scala) \ 132 | $(wildcard $(srcDir)/src/rv32_3stage/*.scala) 133 | 134 | emulator/rv32_5stage/emulator: $(wildcard $(srcDir)/src/common/*.scala) \ 135 | $(wildcard $(srcDir)/src/rv32_5stage/*.scala) 136 | 137 | emulator/rv32_ucode/emulator: $(wildcard $(srcDir)/src/common/*.scala) \ 138 | $(wildcard $(srcDir)/src/rv32_ucode/*.scala) 139 | 140 | emulator/rv32_1stage/emulator-debug: $(wildcard $(srcDir)/src/common/*.scala) \ 141 | $(wildcard $(srcDir)/src/rv32_1stage/*.scala) 142 | 143 | emulator/rv32_2stage/emulator-debug: $(wildcard $(srcDir)/src/common/*.scala) \ 144 | $(wildcard $(srcDir)/src/rv32_2stage/*.scala) 145 | 146 | emulator/rv32_3stage/emulator-debug: $(wildcard $(srcDir)/src/common/*.scala) \ 147 | $(wildcard $(srcDir)/src/rv32_3stage/*.scala) 148 | 149 | emulator/rv32_5stage/emulator-debug: $(wildcard $(srcDir)/src/common/*.scala) \ 150 | $(wildcard $(srcDir)/src/rv32_5stage/*.scala) 151 | 152 | emulator/rv32_ucode/emulator-debug: $(wildcard $(srcDir)/src/common/*.scala) \ 153 | $(wildcard $(srcDir)/src/rv32_ucode/*.scala) 154 | 155 | .PHONY: jenkins-build 156 | 157 | jenkins-build: 158 | $(MAKE) clean run-emulator 159 | $(MAKE) reports 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About The Sodor Processor Collection 2 | ==================================== 3 | 4 | **Note: This repo has been updated to be used with the [Chipyard](https://github.com/ucb-bar/chipyard) SoC Generator.** 5 | **For the old self-contained version of Sodor (which is no longer maintained), see https://github.com/ucb-bar/riscv-sodor/tree/sodor-old.** 6 | 7 | Diagrams: [Sodor Github wiki](https://github.com/ucb-bar/riscv-sodor/wiki) 8 | 9 | More documentation: [Librecores Sodor wiki](https://github.com/librecores/riscv-sodor/wiki) 10 | 11 | Downstream development: [Librecores Sodor](https://github.com/librecores/riscv-sodor) 12 | 13 | 14 | This repo has been put together to demonstrate a number of simple [RISC-V](http://riscv.org) 15 | integer pipelines written in [Chisel](http://chisel.eecs.berkeley.edu): 16 | 17 | * 1-stage (essentially an ISA simulator) 18 | * 2-stage (demonstrates pipelining in Chisel) 19 | * 3-stage (uses sequential memory; supports both Harvard and Princeton versions) 20 | * 5-stage (can toggle between fully bypassed or fully interlocked) 21 | * "bus"-based micro-coded implementation 22 | 23 | All of the cores implement the RISC-V 32b integer base user-level ISA (RV32I) 24 | version 2.0. None of the cores support virtual memory, and thus only implement 25 | the Machine-level (M-mode) of the Privileged ISA v1.10 . 26 | 27 | All processors talk to a simple scratchpad memory (asynchronous, 28 | single-cycle), with no backing outer memory (the 3-stage is the exception 29 | \- its scratchpad is synchronous). Programs are loaded in via JTAG or TSI, 30 | scratchpads 3-port memories (instruction, data, debug). 31 | 32 | This repository is set up to use the Verilog file generated by Chisel3 which is fed 33 | to Verilator along with a test harness in C++ to generate and run the Sodor emulators. 34 | 35 | This repo works great as an undergraduate lab (and has been used by Berkeley's 36 | CS152 class for 3 semesters and counting). See doc/ for an example, as well as 37 | for some processor diagrams. Be careful though - admittedly some of those 38 | documents may become dated as things like the Privileged ISA evolve. 39 | 40 | 41 | 42 | Getting the repo and Building the processor emulators 43 | ===================================================== 44 | 45 | This repo is **NOT** a self-running repository. Please follow the instruction in 46 | https://chipyard.readthedocs.io/en/latest/ to set up Chipyard and simulate Sodor cores. 47 | 48 | FAQ 49 | === 50 | 51 | *What is the goal of these cores?* 52 | 53 | First and foremost, to provide a set of easy to understand cores that users can 54 | easily modify and play with. Sodor is useful both as a quick introduction to 55 | the [RISC-V ISA](http://riscv.org) and to the hardware construction language 56 | [Chisel3](http://chisel.eecs.berkeley.edu). 57 | 58 | *Are there any diagrams of these cores?* 59 | 60 | Diagrams of some of the processors can be found either in the 61 | [Sodor Github wiki](https://github.com/ucb-bar/riscv-sodor/wiki), in doc/, 62 | or in doc/lab1.pdf. A more comprehensive write-up on the micro-code implementation can 63 | be found at the [CS152 website](http://inst.eecs.berkeley.edu/~cs152/sp12/handouts/microcode.pdf). 64 | 65 | 66 | *How do I generate Verilog code for use on a FPGA?* 67 | 68 | Chisel3 outputs verilog by default which can be generated by 69 | ```bash 70 | cd emulator/rv32_1stage 71 | make generated-src/Top.v 72 | ``` 73 | 74 | *I want to help! Where do I go?* 75 | 76 | You can participate in the Sodor conversation on [gitter](https://gitter.im/librecores/riscv-sodor). Downstream development is also taking place at [Librecores](https://github.com/librecores/riscv-sodor). Major milestones will be pulled back here. Check it out! We also accept pull requests here! 77 | 78 | TODO 79 | ---- 80 | 81 | Here is an informal list of things that would be nice to get done. Feel free to 82 | contribute! 83 | 84 | * Reduce the port count on the scratchpad memory by having the HTIF port 85 | share one of the cpu ports. 86 | * Provide a Verilog test harness, and put the 3-stage on a FPGA. 87 | * Add support for the ma_addr, ma_fetch ISA tests. This requires detecting 88 | misaligned address exceptions. 89 | * Greatly cleanup the common/csr.scala file, to make it clearer and more 90 | understandable. 91 | * Refactor the stall, kill, fencei, and exception logic of the 5-stage to be 92 | more understandable. 93 | * Update the u-code to properly handle illegal instructions (rv32mi-p-illegal) 94 | and to properly handle exceptions generated by the CSR file (rv32mi-p-csr). 95 | -------------------------------------------------------------------------------- /README2: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | CS152 Lab1 Infrastructure : Quick & Dirty Instructions! 3 | =============================================================================== 4 | # Author : Christopher Celio 5 | # : 6 | # Date : 2012 Jan 8 7 | 8 | # About : The following readme is written to target CS152 lab users, and may 9 | # be slightly out of date. 10 | 11 | ------------------------------------------------------------------------------- 12 | ------------------------------------------------------------------------------- 13 | First, make sure you have included the RISC-V toolchain in your path. For 14 | students using Instructional machines, this can be accomplished as follows: 15 | 16 | $ source ~cs152/tools/cs152.bashrc 17 | 18 | 19 | To switch between the provided processors (rv32_1stage, rv32_2stage, 20 | rv32_5stage, rv32_ucode), change the bash variable $MK_TARGET_PROC: 21 | 22 | $ export MK_TARGET_PROC=rv32_5stage 23 | $ make run-emulator 24 | 25 | See docs/ for more information. Read the makefiles to see what's going on. 26 | 27 | ------------------------------------------------------------------------------- 28 | BUILDING LAB 1 29 | ------------------------------------------------------------------------------- 30 | 31 | from this directory: 32 | 33 | make run-emulator - to generate C++ simulator code from Chisel and run all 34 | riscv-bmarks and riscv-test on the resulting processor (by 35 | default builds rv32_1stage). 36 | make clean - remove all generated files for the current target 37 | processor. 38 | 39 | export MK_TARGET_PROC=rv32_5stage - to change to a new target processor. 40 | Other options are {rv32_1stage, 41 | rv32_2stage, rv32_5stage, rv32_ucode}. 42 | 43 | the generated code is written to the emulator/${MK_TARGET_PROC}/generated-src 44 | directories. 45 | 46 | to build the C simulator: 47 | 48 | cd emulator/${MK_TARGET_PROC} 49 | make 50 | 51 | You can run a set of assembly tests or simple benchmarks: 52 | 53 | make run-asm-tests 54 | make run-bmarks 55 | 56 | Local assembly tests and benchmarks are in the ../test directory. 57 | to build them: 58 | 59 | cd ../test/riscv-tests 60 | make 61 | cd ../riscv-bmarks 62 | make 63 | 64 | Most assembly tests and benchmarks are globally installed in 65 | ~cs152/install/. 66 | 67 | You must have the riscv toolchain (gcc, frontend server, proxy kernel) 68 | installed and in your path. 69 | 70 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // // See LICENSE for license details. 2 | 3 | organization := "edu.berkeley.cs" 4 | 5 | version := "2.0" 6 | 7 | name := "sodor" 8 | 9 | scalaVersion := "2.13.10" 10 | 11 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(sodor, 1.0) 2 | 3 | AC_PROG_CXX 4 | 5 | # Chisel/Scala sources 6 | AC_ARG_WITH(chisel, [ --with-chisel path to chisel sources]) 7 | 8 | AS_IF([test "x$with_chisel" != "x"], 9 | [AC_SUBST(chisel_version, latest.SNAPSHOT)], 10 | [AC_SUBST(chisel_version, latest.release)]) 11 | 12 | AC_SUBST(chiseldir, $with_chisel) 13 | 14 | AC_OUTPUT( 15 | prefix.mk 16 | Makefile 17 | ) 18 | -------------------------------------------------------------------------------- /doc/1stage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/1stage.pdf -------------------------------------------------------------------------------- /doc/2stage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/2stage.pdf -------------------------------------------------------------------------------- /doc/lab1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/lab1.pdf -------------------------------------------------------------------------------- /doc/sodor.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/sodor.pdf -------------------------------------------------------------------------------- /doc/sodor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/sodor.png -------------------------------------------------------------------------------- /doc/zscale.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/zscale.pdf -------------------------------------------------------------------------------- /doc/zscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/doc/zscale.png -------------------------------------------------------------------------------- /prefix.mk.in: -------------------------------------------------------------------------------- 1 | # Declaration of common variables 2 | 3 | srcDir := @abs_top_srcdir@ 4 | buildDir := @abs_top_builddir@ 5 | 6 | # Paths to different source trees 7 | chiseldir := @chiseldir@ 8 | 9 | CXX := @CXX@ 10 | SBT := java -Xmx4096M -Xss8M -XX:MaxPermSize=128M -jar $(srcDir)/sbt/sbt-launch.jar $(SBT_FLAGS) 11 | 12 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.3.7 2 | -------------------------------------------------------------------------------- /project/check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import datetime, re, socket, sys 4 | 5 | def main(args, junit_file): 6 | testsuitename = 'solutions' 7 | hostname = socket.gethostname() 8 | timestamp = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S") 9 | testsuite_time = 0 10 | nb_errors = 0 11 | nb_tests = len(args) 12 | nb_expected_failures = 0 13 | test_results = {} 14 | for path in args: 15 | with open(path) as outfile: 16 | test_status = 'FAILED' 17 | test_time = 0 18 | for line in outfile.readlines(): 19 | look = re.match('.*(PASSED|FAILED)', line) 20 | if look: 21 | test_status = look.group(1) 22 | look = re.match('.*Total time: (\S+) s,', line) 23 | if look: 24 | test_time = int(look.group(1)) 25 | if test_status != 'PASSED': 26 | nb_errors = nb_errors + 1 27 | testsuite_time = testsuite_time + test_time 28 | test_results[path] = (test_status, test_time) 29 | 30 | # Output the XML file compatible with JUnit format. 31 | junit_file.write('\n' 32 | % (testsuitename, timestamp, testsuite_time, hostname, 33 | nb_tests, nb_expected_failures, nb_errors)) 34 | junit_file.write('\n') 35 | for path in args: 36 | junit_file.write( 37 | '\n' 38 | % (path, 'emulator', test_results[path][1])) 39 | if test_results[path][0] != 'PASSED': 40 | junit_file.write('\n' 41 | % test_results[path][0]) 42 | junit_file.write('\n') 49 | junit_file.write( 50 | '\n') 51 | junit_file.write('\n') 52 | 53 | 54 | if __name__ == '__main__': 55 | main(sys.argv[1:], sys.stdout) 56 | -------------------------------------------------------------------------------- /riscv-bmarks/dhrystone.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/dhrystone.riscv -------------------------------------------------------------------------------- /riscv-bmarks/median.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/median.riscv -------------------------------------------------------------------------------- /riscv-bmarks/multiply.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/multiply.riscv -------------------------------------------------------------------------------- /riscv-bmarks/qsort.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/qsort.riscv -------------------------------------------------------------------------------- /riscv-bmarks/rsort.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/rsort.riscv -------------------------------------------------------------------------------- /riscv-bmarks/towers.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/towers.riscv -------------------------------------------------------------------------------- /riscv-bmarks/vvadd.riscv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ucb-bar/riscv-sodor/32d49f96dd9ff37ff7fb2eab875a285f6e4dc295/riscv-bmarks/vvadd.riscv -------------------------------------------------------------------------------- /scripts/tracer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import sys 5 | import re 6 | import instructions 7 | from instructions import * 8 | 9 | # Parse arguments. 10 | parser = argparse.ArgumentParser(description="SODOR instruction trace analyzer") 11 | parser.add_argument('file', nargs='?', type=argparse.FileType('r'), 12 | default=sys.stdin, 13 | help="workload.out file") 14 | parser.add_argument('-u', '--ucode', action='store_true', 15 | help='Process the tracefile from the UCode machine') 16 | 17 | args = parser.parse_args() 18 | 19 | # Variables for collecting stats 20 | # We don't collect stats until the core has completed executing the bootrom, 21 | # so collecting_stats is initialized as False 22 | collecting_stats = False 23 | start_cycle = 0 24 | 25 | n_instructions = 0 # Total instructions retired while collecting_stats == True 26 | n_arith_instructions = 0 # Total arithmetic instructions retired while collecting_stats == True 27 | n_ldst_instructions = 0 # Total load/store instructions retired while collecting_stats == True 28 | n_brjmp_instructions = 0 # Total branch/jump instructions retired while collecting_stats == True 29 | n_misc_instructions = 0 # All other instructions retired while collecting_stats == True 30 | n_bubbles = 0 # Total cycles where no instruction was committed while collecting_stats == True 31 | n_cycles = 0 # Total cycles where collecting_stats == True 32 | 33 | # Use Regex to decode and read each line of the trace 34 | if args.ucode: 35 | regex = "Cyc=([ 0-9]+) \[([01])\] PCReg=\[([0-9a-f]+)\] uPC=\[([0-9a-f]+)\] Bus=\[([0-9a-f]+)\] RegSel=\[([0-9a-f]+)\] RegAddr=\[([ 0-9a-f]+)\] A=\[([0-9a-f]+)\] B=\[([0-9a-f]+)\] MA=\[([0-9a-f]+)\] InstReg=\[([0-9a-f]+)\] ([F ])([M ])([X ]) ([ a-z0-9-,.()]+)" 36 | groupmap = { 37 | "cycle" : 1, 38 | "retire" : 2, 39 | "pc" : 3, 40 | "inst" : 11, 41 | "dasm" : 15, 42 | 43 | "upc" : 4, 44 | "bus" : 5, 45 | "regsel" : 6, 46 | "regaddr" : 7, 47 | "A" : 8, 48 | "B" : 9, 49 | "MA" : 10, 50 | } 51 | 52 | else: 53 | regex = "Cyc=([ 0-9]+) \[([01])\] pc=\[([0-9a-f]+)] W\[r([ 0-9]+)=([0-9a-f]+)\]\[([01])\] Op1=\[r([ 0-9]+)\]\[([0-9a-f]+)\] Op2=\[r([ 0-9]+)\]\[([0-9a-f]+)\] inst=\[([0-9a-f]+)\] ([SKFH ])([BJREM ])([X ]) ([ a-z0-9-,.()]+)" 54 | 55 | groupmap = { 56 | "cycle" : 1, 57 | "retire" : 2, 58 | "pc" : 3, 59 | "inst" : 11, 60 | "dasm" : 15, 61 | 62 | "rd" : 4, 63 | "wdata" : 5, 64 | "wen" : 6, 65 | 66 | "rs1" : 7, 67 | "rs1_data" : 8, 68 | "rs2" : 9, 69 | "rs2_data" : 10, 70 | 71 | "pc_sel" : 13, 72 | "exception" : 14, 73 | } 74 | 75 | def extract(match, key): 76 | return match.group(groupmap[key]) 77 | 78 | 79 | 80 | h = re.compile(regex) 81 | 82 | 83 | 84 | for line in args.file: 85 | p = h.match(line) 86 | 87 | if p: 88 | # Extract useful information from a line of the trace 89 | cycle = int(extract(p, "cycle")) # Cycle timestamp of this line 90 | retire = extract(p, "retire") == '1' # True if instruction was retired here 91 | pc = int(extract(p, "pc"), 16) # PC of retired instruction 92 | inst = Instruction(int(extract(p, "inst"), 16)) # Raw instruction bits as uint 93 | dasm = extract(p, "dasm") # Diassembled instruction 94 | 95 | if args.ucode: 96 | upc = int(extract(p, "upc"), 16) # Address of microcoded instruction 97 | bus = int(extract(p, "bus"), 16) # Value on bus 98 | regsel = int(extract(p, "regsel"), 16) # Which value to get the reg addr from? 99 | regaddr = int(extract(p, "regaddr"), 16) # Register address 100 | A = int(extract(p, "A"), 16) # A operand register 101 | B = int(extract(p, "B"), 16) # B operand register 102 | MA = int(extract(p, "MA"), 16) # MA memory address register 103 | else: 104 | rd = int(extract(p, "rd")) # Destination register 105 | wdata = int(extract(p, "wdata"), 16) # Data to be written to destination register 106 | wen = extract(p, "wen") == '1' # True of this instruction writes to a destination register 107 | 108 | rs1 = int(extract(p, "rs1")) # Register operand 1 address 109 | rs1_data = int(extract(p, "rs1_data"), 16) # Register operand 1 data 110 | rs2 = int(extract(p, "rs2")) # Register operand 2 address 111 | rs2_data = int(extract(p, "rs2_data"), 16) # Register operand 2 data 112 | 113 | pc_sel = extract(p, "pc_sel") # How the next PC after this instruction is generated 114 | exception = extract(p, "exception") # True if this instruction generates an exception 115 | 116 | 117 | 118 | # Start recording stats after we jump to the target binary at 0x8000_0000 119 | if retire and pc == 0x80000000: 120 | collecting_stats = True 121 | start_cycle = cycle 122 | 123 | if collecting_stats: 124 | if retire: 125 | n_instructions += 1 126 | if inst.opcode in BRJAL_OPCODES: 127 | n_brjmp_instructions += 1 128 | elif inst.opcode in LDST_OPCODES: 129 | n_ldst_instructions += 1 130 | elif inst.opcode in ARITH_OPCODES: 131 | n_arith_instructions += 1 132 | else: 133 | n_misc_instructions += 1 134 | 135 | # TODO: Track more types of instructions here? 136 | 137 | # Example code showing how to slice some bits out of each Instruction object 138 | # Bits 0-6 correspond to the opcode of instructions. Notice that inst[0:6] is 7 139 | # bits, as we follow verilog-style bit indexing 140 | 141 | bits0to6 = inst[0:6] 142 | # print("{0:07b}".format(bits0to6)) 143 | else: 144 | n_bubbles += 1 145 | 146 | n_cycles = cycle - start_cycle 147 | 148 | 149 | if (n_instructions == 0): 150 | sys.exit("Trace analyzer found no instructions. Are you passing in the correct trace file?") 151 | 152 | print(""" 153 | Stats: 154 | 155 | CPI : {cpi:.3f} 156 | IPC : {ipc:.3f} 157 | Cycles : {cycles} 158 | Instructions : {instructions} 159 | Bubbles : {bubbles} 160 | 161 | Instruction Breakdown: 162 | % Arithmetic : {arith:.3f} % 163 | % Ld/St : {ldst:.3f} % 164 | % Branch/Jump : {brjmp:.3f} % 165 | % Misc. : {misc:.3f} % 166 | """.format(cpi=n_cycles / n_instructions, 167 | ipc=n_instructions / n_cycles, 168 | cycles=n_cycles, 169 | instructions=n_instructions, 170 | bubbles=n_bubbles, 171 | arith=100 * n_arith_instructions / n_instructions, 172 | ldst=100 * n_ldst_instructions / n_instructions, 173 | brjmp=100 * n_brjmp_instructions / n_instructions, 174 | misc=100 * n_misc_instructions / n_instructions)) 175 | -------------------------------------------------------------------------------- /src/main/resources/sodor/vsrc/SimDTM.v: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | 3 | import "DPI-C" function int debug_tick 4 | ( 5 | output bit debug_req_valid, 6 | input bit debug_req_ready, 7 | output int debug_req_bits_addr, 8 | output int debug_req_bits_op, 9 | output int debug_req_bits_data, 10 | 11 | input bit debug_resp_valid, 12 | output bit debug_resp_ready, 13 | input int debug_resp_bits_resp, 14 | input int debug_resp_bits_data 15 | ); 16 | 17 | module SimDTM( 18 | input clk, 19 | input reset, 20 | 21 | output debug_req_valid, 22 | input debug_req_ready, 23 | output [ 6:0] debug_req_bits_addr, 24 | output [ 1:0] debug_req_bits_op, 25 | output [31:0] debug_req_bits_data, 26 | 27 | input debug_resp_valid, 28 | output debug_resp_ready, 29 | input [ 1:0] debug_resp_bits_resp, 30 | input [31:0] debug_resp_bits_data, 31 | 32 | output [31:0] exit 33 | ); 34 | 35 | bit r_reset; 36 | 37 | wire #0.1 __debug_req_ready = debug_req_ready; 38 | wire #0.1 __debug_resp_valid = debug_resp_valid; 39 | wire [31:0] #0.1 __debug_resp_bits_resp = {30'b0, debug_resp_bits_resp}; 40 | wire [31:0] #0.1 __debug_resp_bits_data = debug_resp_bits_data; 41 | 42 | bit __debug_req_valid; 43 | int __debug_req_bits_addr; 44 | int __debug_req_bits_op; 45 | int __debug_req_bits_data; 46 | bit __debug_resp_ready; 47 | int __exit; 48 | 49 | assign #0.1 debug_req_valid = __debug_req_valid; 50 | assign #0.1 debug_req_bits_addr = __debug_req_bits_addr[6:0]; 51 | assign #0.1 debug_req_bits_op = __debug_req_bits_op[1:0]; 52 | assign #0.1 debug_req_bits_data = __debug_req_bits_data[31:0]; 53 | assign #0.1 debug_resp_ready = __debug_resp_ready; 54 | assign #0.1 exit = __exit; 55 | 56 | always @(posedge clk) 57 | begin 58 | r_reset <= reset; 59 | if (reset || r_reset) 60 | begin 61 | __debug_req_valid = 0; 62 | __debug_resp_ready = 0; 63 | __exit = 0; 64 | end 65 | else 66 | begin 67 | __exit = debug_tick( 68 | __debug_req_valid, 69 | __debug_req_ready, 70 | __debug_req_bits_addr, 71 | __debug_req_bits_op, 72 | __debug_req_bits_data, 73 | __debug_resp_valid, 74 | __debug_resp_ready, 75 | __debug_resp_bits_resp, 76 | __debug_resp_bits_data 77 | ); 78 | end 79 | end 80 | endmodule 81 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/abstract_commands.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | import chisel3._ 3 | import chisel3.util._ 4 | 5 | // This file was auto-generated from the repository at https://github.com/sifive/riscv-debug-spec.git, 6 | // 'make chisel' 7 | 8 | object AC_RegAddrs { 9 | } 10 | 11 | class ACCESS_REGISTERFields extends Bundle { 12 | 13 | /* This is 0 to indicate Access Register Command. 14 | */ 15 | val cmdtype = UInt(8.W) 16 | 17 | val reserved0 = UInt(1.W) 18 | 19 | /* 2: Access the lowest 32 bits of the register. 20 | 21 | 3: Access the lowest 64 bits of the register. 22 | 23 | 4: Access the lowest 128 bits of the register. 24 | 25 | If \Fsize specifies a size larger than the register's actual size, 26 | then the access must fail. If a register is accessible, then reads of \Fsize 27 | less than or equal to the register's actual size must be supported. 28 | */ 29 | val size = UInt(3.W) 30 | 31 | val reserved1 = UInt(1.W) 32 | 33 | /* When 1, execute the program in the Program Buffer exactly once 34 | after performing the transfer, if any. 35 | */ 36 | val postexec = Bool() 37 | 38 | /* 0: Don't do the operation specified by \Fwrite. 39 | 40 | 1: Do the operation specified by \Fwrite. 41 | */ 42 | val transfer = Bool() 43 | 44 | /* When \Ftransfer is set: 45 | 0: Copy data from the specified register into {\tt arg0} portion 46 | of {\tt data}. 47 | 48 | 1: Copy data from {\tt arg0} portion of {\tt data} into the 49 | specified register. 50 | */ 51 | val write = Bool() 52 | 53 | /* Number of the register to access, as described in Table~\ref{tab:regno}. 54 | */ 55 | val regno = UInt(16.W) 56 | 57 | } 58 | 59 | class QUICK_ACCESSFields extends Bundle { 60 | 61 | /* This is 1 to indicate Quick Access command. 62 | */ 63 | val cmdtype = UInt(8.W) 64 | 65 | val reserved0 = UInt(24.W) 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/consts.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | package constants 3 | { 4 | 5 | import chisel3._ 6 | import chisel3.util._ 7 | import scala.math._ 8 | 9 | trait RISCVConstants 10 | { 11 | // abstract out instruction decode magic numbers 12 | val RD_MSB = 11 13 | val RD_LSB = 7 14 | val RS1_MSB = 19 15 | val RS1_LSB = 15 16 | val RS2_MSB = 24 17 | val RS2_LSB = 20 18 | 19 | val CSR_ADDR_MSB = 31 20 | val CSR_ADDR_LSB = 20 21 | 22 | val X0 = 0.U 23 | 24 | // The Bubble Instruction (Machine generated NOP) 25 | // Insert (XOR x0,x0,x0) which is different from software compiler 26 | // generated NOPs which are (ADDI x0, x0, 0). 27 | // Reasoning for this is to let visualizers and stat-trackers differentiate 28 | // between software NOPs and machine-generated Bubbles in the pipeline. 29 | val BUBBLE = 0x4033.U(32.W) 30 | } 31 | 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/debug.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import sodor.common.Util._ 6 | import Util._ 7 | import Constants._ 8 | 9 | 10 | object DMConsts{ 11 | 12 | def dmiDataSize = 32 13 | def nDMIAddrSize = 7 14 | def dmiOpSize = 2 15 | def dmi_OP_NONE = "b00".U 16 | def dmi_OP_READ = "b01".U 17 | def dmi_OP_WRITE = "b10".U 18 | 19 | def dmiRespSize = 2 20 | def dmi_RESP_SUCCESS = "b00".U 21 | def dmi_RESP_FAILURE = "b01".U 22 | def dmi_RESP_HW_FAILURE = "b10".U 23 | // This is used outside this block 24 | // to indicate 'busy'. 25 | def dmi_RESP_RESERVED = "b11".U 26 | 27 | def dmi_haltStatusAddr = 0x40 28 | def nProgBuf = 4 29 | def nDataCount = 1 30 | def hartInfo = "h111bc0".U 31 | } 32 | 33 | 34 | class DMIReq(addrBits : Int) extends Bundle { 35 | val op = Output(UInt(DMConsts.dmiOpSize.W)) 36 | val addr = Output(UInt(addrBits.W)) 37 | val data = Output(UInt(DMConsts.dmiDataSize.W)) 38 | } 39 | 40 | /** Structure to define the contents of a Debug Bus Response 41 | */ 42 | class DMIResp() extends Bundle { 43 | val data = Output(UInt(DMConsts.dmiDataSize.W)) 44 | val resp = Output(UInt(DMConsts.dmiRespSize.W)) 45 | } 46 | 47 | /** Structure to define the top-level DMI interface 48 | * of DebugModule. 49 | * DebugModule is the consumer of this interface. 50 | * Therefore it has the 'flipped' version of this. 51 | */ 52 | class DMIIO(implicit val conf: SodorCoreParams) extends Bundle { 53 | val req = new DecoupledIO(new DMIReq(DMConsts.nDMIAddrSize)) 54 | val resp = Flipped(new DecoupledIO(new DMIResp)) 55 | } 56 | 57 | /** This includes the clock and reset as these are passed through the 58 | * hierarchy until the Debug Module is actually instantiated. 59 | * 60 | */ 61 | 62 | class SimDTM(implicit val conf: SodorCoreParams) extends BlackBox { 63 | val io = IO(new Bundle { 64 | val clk = Input(Clock()) 65 | val reset = Input(Bool()) 66 | val debug = new DMIIO() 67 | val exit = Output(UInt(32.W)) 68 | }) 69 | 70 | def connect(tbclk: Clock, tbreset: Bool, dutio: DMIIO, tbsuccess: Bool) = { 71 | io.clk := tbclk 72 | io.reset := tbreset 73 | dutio <> io.debug 74 | 75 | tbsuccess := io.exit === 1.U 76 | when (io.exit >= 2.U) { 77 | printf("*** FAILED *** (exit code = %d)\n", io.exit >> 1.U) 78 | //stop(1) 79 | } 80 | } 81 | } 82 | 83 | class DebugDPath(implicit val conf: SodorCoreParams) extends Bundle 84 | { 85 | // REG access 86 | val addr = Output(UInt(5.W)) 87 | val wdata = Output(UInt(32.W)) 88 | val validreq = Output(Bool()) 89 | val rdata = Input(UInt(32.W)) 90 | val resetpc = Output(Bool()) 91 | } 92 | 93 | class DebugCPath(implicit val conf: SodorCoreParams) extends Bundle 94 | { 95 | val halt = Output(Bool()) 96 | } 97 | 98 | class DebugIo(implicit val conf: SodorCoreParams) extends Bundle 99 | { 100 | val dmi = Flipped(new DMIIO()) 101 | val ddpath = new DebugDPath() 102 | val dcpath = new DebugCPath() 103 | val debugmem = new MemPortIo(data_width = 32) 104 | val resetcore = Output(Bool()) 105 | } 106 | 107 | class DebugModule(implicit val conf: SodorCoreParams) extends Module { 108 | val io = IO(new DebugIo()) 109 | io := DontCare 110 | 111 | io.dmi.req.ready := io.dmi.req.valid 112 | val dmireq = io.dmi.req.valid 113 | io.dmi.resp.bits.resp := DMConsts.dmi_RESP_SUCCESS 114 | val dmstatusReset = Wire(new DMSTATUSFields()) 115 | dmstatusReset := DontCare 116 | dmstatusReset.authenticated := true.B 117 | dmstatusReset.versionlo := "b10".U 118 | val dmstatus = RegInit(dmstatusReset) 119 | val sbcsreset = Wire(new SBCSFields()) 120 | sbcsreset := DontCare 121 | sbcsreset.sbaccess := 2.U 122 | sbcsreset.sbasize := 32.U 123 | sbcsreset.sbaccess32 := true.B 124 | sbcsreset.sbaccess16 := false.B 125 | sbcsreset.sbaccess8 := false.B 126 | val sbcs = RegInit(sbcsreset) 127 | val abstractcsReset = Wire(new ABSTRACTCSFields()) 128 | abstractcsReset := DontCare 129 | abstractcsReset.datacount := DMConsts.nDataCount.U 130 | abstractcsReset.progsize := DMConsts.nProgBuf.U 131 | val abstractcs = RegInit(abstractcsReset) 132 | val command = Reg(new ACCESS_REGISTERFields()) 133 | val dmcontrol = Reg(new DMCONTROLFields()) 134 | val progbuf = Reg(Vec(DMConsts.nProgBuf, UInt(conf.xprlen.W))) 135 | val data0 = Reg(UInt(conf.xprlen.W)) //arg0 136 | val data1 = Reg(UInt(conf.xprlen.W)) //arg1 137 | val data2 = Reg(UInt(conf.xprlen.W)) //arg2 138 | val sbaddr = Reg(UInt(conf.xprlen.W)) 139 | val sbdata = Reg(UInt(conf.xprlen.W)) 140 | val memreadfire = RegInit(false.B) 141 | val coreresetval = RegInit(true.B) 142 | 143 | val read_map = collection.mutable.LinkedHashMap[Int,UInt]( 144 | DMI_RegAddrs.DMI_ABSTRACTCS -> abstractcs.asUInt, 145 | DMI_RegAddrs.DMI_DMCONTROL -> dmcontrol.asUInt, 146 | DMI_RegAddrs.DMI_DMSTATUS -> dmstatus.asUInt, 147 | DMI_RegAddrs.DMI_COMMAND -> command.asUInt, 148 | DMI_RegAddrs.DMI_HARTINFO -> DMConsts.hartInfo, 149 | DMI_RegAddrs.DMI_ABSTRACTAUTO -> 0.U, 150 | DMI_RegAddrs.DMI_CFGSTRADDR0 -> 0.U, 151 | DMI_RegAddrs.DMI_DATA0 -> data0, 152 | (DMI_RegAddrs.DMI_DATA0 + 1) -> data1, 153 | (DMI_RegAddrs.DMI_DATA0 + 2) -> data2, 154 | DMI_RegAddrs.DMI_PROGBUF0 -> progbuf(0), 155 | DMI_RegAddrs.DMI_PROGBUF1 -> progbuf(1), 156 | DMI_RegAddrs.DMI_PROGBUF2 -> progbuf(2), 157 | DMI_RegAddrs.DMI_PROGBUF3 -> progbuf(3), 158 | DMI_RegAddrs.DMI_AUTHDATA -> 0.U, 159 | DMI_RegAddrs.DMI_SERCS -> 0.U, 160 | DMI_RegAddrs.DMI_SBCS -> sbcs.asUInt, 161 | DMI_RegAddrs.DMI_SBADDRESS0 -> sbaddr, 162 | DMI_RegAddrs.DMI_SBDATA0 -> sbdata) 163 | val decoded_addr = read_map map { case (k, v) => k -> (io.dmi.req.bits.addr === k) } 164 | io.dmi.resp.bits.data := Mux1H(for ((k, v) <- read_map) yield decoded_addr(k) -> v) 165 | val wdata = io.dmi.req.bits.data 166 | dmstatus.allhalted := dmcontrol.haltreq 167 | dmstatus.allrunning := dmcontrol.resumereq 168 | io.dcpath.halt := dmstatus.allhalted && !dmstatus.allrunning 169 | when (io.dmi.req.bits.op === DMConsts.dmi_OP_WRITE){ 170 | when((decoded_addr(DMI_RegAddrs.DMI_ABSTRACTCS)) && io.dmi.req.valid) { 171 | val tempabstractcs = wdata.asTypeOf(new ABSTRACTCSFields()) 172 | abstractcs.cmderr := tempabstractcs.cmderr 173 | } 174 | when(decoded_addr(DMI_RegAddrs.DMI_COMMAND)) { 175 | val tempcommand = wdata.asTypeOf(new ACCESS_REGISTERFields()) 176 | when(tempcommand.size === 2.U){ 177 | command.postexec := tempcommand.postexec 178 | command.regno := tempcommand.regno 179 | command.transfer := tempcommand.transfer 180 | command.write := tempcommand.write 181 | abstractcs.cmderr := 1.U 182 | } .otherwise { 183 | abstractcs.cmderr := 2.U 184 | } 185 | } 186 | when(decoded_addr(DMI_RegAddrs.DMI_DMCONTROL)) { 187 | val tempcontrol = wdata.asTypeOf(new DMCONTROLFields()) 188 | dmcontrol.haltreq := tempcontrol.haltreq 189 | dmcontrol.resumereq := tempcontrol.resumereq 190 | dmcontrol.hartreset := tempcontrol.hartreset 191 | dmcontrol.ndmreset := tempcontrol.ndmreset 192 | dmcontrol.dmactive := tempcontrol.dmactive 193 | } 194 | when(decoded_addr(DMI_RegAddrs.DMI_SBCS)){ 195 | val tempsbcs = wdata.asTypeOf(new SBCSFields()) 196 | sbcs.sbsingleread := tempsbcs.sbsingleread 197 | sbcs.sbaccess := tempsbcs.sbaccess 198 | sbcs.sbautoincrement := tempsbcs.sbautoincrement 199 | sbcs.sbautoread := tempsbcs.sbautoread 200 | sbcs.sberror := tempsbcs.sberror 201 | } 202 | when(decoded_addr(DMI_RegAddrs.DMI_SBADDRESS0)) { sbaddr := wdata} 203 | when(decoded_addr(DMI_RegAddrs.DMI_SBDATA0)) { 204 | sbdata := wdata 205 | io.debugmem.req.bits.addr := sbaddr 206 | io.debugmem.req.bits.data := sbdata 207 | io.debugmem.req.bits.fcn := M_XWR 208 | io.debugmem.req.valid := io.dmi.req.valid 209 | when(sbcs.sbautoincrement && io.dmi.req.valid) 210 | { 211 | sbaddr := sbaddr + 4.U 212 | } 213 | } 214 | when(decoded_addr(DMI_RegAddrs.DMI_DATA0)) ( data0 := wdata ) 215 | when(decoded_addr(DMI_RegAddrs.DMI_DATA0+1)) ( data1 := wdata ) 216 | when(decoded_addr(DMI_RegAddrs.DMI_DATA0+2)) ( data2 := wdata ) 217 | } 218 | 219 | /// abstract cs command regfile access 220 | io.ddpath.addr := command.regno & "hfff".U 221 | when(command.transfer && abstractcs.cmderr =/= 0.U){ 222 | when(command.write){ 223 | io.ddpath.wdata := data0 224 | io.ddpath.validreq := true.B 225 | } .otherwise { 226 | data0 := io.ddpath.rdata 227 | } 228 | abstractcs.cmderr := 0.U 229 | } 230 | 231 | when(!(decoded_addr(DMI_RegAddrs.DMI_SBDATA0) && io.dmi.req.bits.op === DMConsts.dmi_OP_WRITE)){ 232 | io.debugmem.req.bits.fcn := false.B 233 | } 234 | 235 | 236 | val firstreaddone = Reg(Bool()) 237 | 238 | io.dmi.resp.valid := Mux(firstreaddone, RegNext(io.debugmem.resp.valid), io.dmi.req.valid) 239 | 240 | when ((decoded_addr(DMI_RegAddrs.DMI_SBDATA0) && (io.dmi.req.bits.op === DMConsts.dmi_OP_READ)) || (sbcs.sbautoread && firstreaddone)){ 241 | io.debugmem.req.bits.addr := sbaddr 242 | io.debugmem.req.bits.fcn := M_XRD 243 | io.debugmem.req.valid := io.dmi.req.valid 244 | // for async data readily available 245 | // so capture it in reg 246 | when(io.debugmem.resp.valid){ 247 | sbdata := io.debugmem.resp.bits.data 248 | } 249 | memreadfire := true.B 250 | firstreaddone := true.B 251 | } 252 | 253 | when(memreadfire && io.debugmem.resp.valid) 254 | { // following is for sync data available in 255 | // next cycle memreadfire a reg allows 256 | // entering this reg only in next 257 | sbdata := io.debugmem.resp.bits.data 258 | memreadfire := false.B 259 | when(sbcs.sbautoincrement) 260 | { 261 | sbaddr := sbaddr + 4.U 262 | } 263 | } 264 | 265 | when(!decoded_addr(DMI_RegAddrs.DMI_SBDATA0)){ 266 | firstreaddone := false.B 267 | } 268 | 269 | io.resetcore := coreresetval 270 | 271 | when((io.dmi.req.bits.addr === "h44".U) && io.dmi.req.valid){ 272 | coreresetval := false.B 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/master_adapter.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import org.chipsalliance.cde.config._ 7 | import freechips.rocketchip.subsystem._ 8 | import freechips.rocketchip.devices.tilelink._ 9 | import freechips.rocketchip.diplomacy._ 10 | 11 | import freechips.rocketchip.rocket._ 12 | import freechips.rocketchip.subsystem.{RocketCrossingParams} 13 | import freechips.rocketchip.tilelink._ 14 | import freechips.rocketchip.interrupts._ 15 | import freechips.rocketchip.util._ 16 | import freechips.rocketchip.tile._ 17 | import freechips.rocketchip.amba.axi4._ 18 | 19 | class SodorMasterAdapter(implicit p: Parameters, val conf: SodorCoreParams) extends LazyModule { 20 | // The node exposed to the crossbar 21 | val node = TLIdentityNode() 22 | 23 | // The client node (only one inflight request supported for Sodor) 24 | // This node handles core requests to addresses not managed in the tile-local scratchpad 25 | val masterNode = TLClientNode(Seq(TLMasterPortParameters.v1( 26 | clients = Seq(TLMasterParameters.v1( 27 | name = "sodor-mmio-master", 28 | sourceId = IdRange(0, 1) 29 | )) 30 | ))) 31 | 32 | // Connect nodes 33 | (node := TLBuffer() := masterNode) 34 | 35 | lazy val module = new SodorMasterAdapterImp(this) 36 | } 37 | 38 | class SodorMasterAdapterImp(outer: SodorMasterAdapter) extends LazyModuleImp(outer) { 39 | implicit val conf = outer.conf 40 | 41 | val io = IO(new Bundle() { 42 | val dport = Flipped(new MemPortIo(data_width = conf.xprlen)) 43 | }) 44 | 45 | val (tl_out, edge) = outer.masterNode.out(0) 46 | 47 | // Register 48 | // State 49 | val s_ready :: s_active :: s_inflight :: Nil = Enum(3) 50 | val state = RegInit(s_ready) 51 | // Address and signedness of the request to be used by LoadGen 52 | val a_address_reg = Reg(UInt(io.dport.req.bits.addr.getWidth.W)) 53 | val a_signed_reg = Reg(Bool()) 54 | // Request register - store the request when the input port fires to avoid value changes when sending the TileLink request 55 | val req_address_reg = Reg(UInt(io.dport.req.bits.addr.getWidth.W)) 56 | val req_size_reg = Reg(UInt(2.W)) 57 | val req_data_reg = Reg(UInt(io.dport.req.bits.data.getWidth.W)) 58 | // Sign and size 59 | val a_signed = io.dport.req.bits.getTLSigned 60 | val a_size = io.dport.req.bits.getTLSize 61 | 62 | // State logic 63 | when (state === s_ready && io.dport.req.valid) { 64 | state := s_active 65 | req_address_reg := io.dport.req.bits.addr 66 | req_size_reg := a_size 67 | req_data_reg := io.dport.req.bits.data 68 | } 69 | when (state === s_active && tl_out.a.fire) { 70 | state := s_inflight 71 | } 72 | when (state === s_inflight && tl_out.d.fire) { 73 | state := s_ready 74 | } 75 | tl_out.a.valid := state === s_active 76 | tl_out.d.ready := true.B 77 | io.dport.req.ready := state === s_ready 78 | io.dport.resp.valid := tl_out.d.valid 79 | 80 | // Bookkeeping 81 | when (tl_out.a.fire) { 82 | a_address_reg := io.dport.req.bits.addr 83 | a_signed_reg := a_size 84 | } 85 | 86 | // Build "Get" message 87 | val (legal_get, get_bundle) = edge.Get(0.U, req_address_reg, req_size_reg) 88 | // Build "Put" message 89 | val (legal_put, put_bundle) = edge.Put(0.U, req_address_reg, req_size_reg, req_data_reg) 90 | 91 | // Connect Channel A bundle 92 | tl_out.a.bits := Mux(io.dport.req.bits.fcn === M_XRD, get_bundle, put_bundle) 93 | 94 | // Connect Channel D bundle (read result) 95 | io.dport.resp.bits.data := new LoadGen(tl_out.d.bits.size, a_signed_reg, a_address_reg, tl_out.d.bits.data, false.B, conf.xprlen / 8).data 96 | 97 | // Handle error 98 | val legal_op = Mux(io.dport.req.bits.fcn === M_XRD, legal_get, legal_put) 99 | val resp_xp = tl_out.d.bits.corrupt | tl_out.d.bits.denied 100 | // Since the core doesn't have an external exception port, we have to kill it 101 | assert(legal_op | !tl_out.a.valid, "Illegal operation") 102 | assert(!resp_xp | !tl_out.d.valid, "Responds exception") 103 | 104 | // Tie off unused channels 105 | tl_out.b.valid := false.B 106 | tl_out.c.ready := true.B 107 | tl_out.e.ready := true.B 108 | } 109 | 110 | // This class allows the next memory request to be sent when the response of the previous request come back. 111 | class SameCycleRequestBuffer(implicit val conf: SodorCoreParams) extends Module { 112 | val io = IO(new Bundle() { 113 | val in = Flipped(new MemPortIo(data_width = conf.xprlen)) 114 | val out = new MemPortIo(data_width = conf.xprlen) 115 | }) 116 | 117 | io.out.req <> io.in.req 118 | io.in.resp := Pipe(io.out.resp) 119 | } 120 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/memory.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // Scratchpad Memory (asynchronous) 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2013 Jun 12 7 | // 8 | // Provides a variable number of ports to the core, and one port to the HTIF 9 | // (host-target interface). 10 | // 11 | // Assumes that if the port is ready, it will be performed immediately. 12 | // For now, don't detect write collisions. 13 | // 14 | // Optionally uses synchronous read (default is async). For example, a 1-stage 15 | // processor can only ever work using asynchronous memory! 16 | 17 | package sodor.common 18 | 19 | import chisel3._ 20 | import chisel3.util._ 21 | import chisel3.experimental._ 22 | 23 | import Constants._ 24 | import sodor.common.Util._ 25 | 26 | trait MemoryOpConstants 27 | { 28 | val MT_X = 0.asUInt(3.W) 29 | val MT_B = 1.asUInt(3.W) 30 | val MT_H = 2.asUInt(3.W) 31 | val MT_W = 3.asUInt(3.W) 32 | val MT_D = 4.asUInt(3.W) 33 | val MT_BU = 5.asUInt(3.W) 34 | val MT_HU = 6.asUInt(3.W) 35 | val MT_WU = 7.asUInt(3.W) 36 | 37 | val M_X = "b0".asUInt(1.W) 38 | val M_XRD = "b0".asUInt(1.W) // int load 39 | val M_XWR = "b1".asUInt(1.W) // int store 40 | 41 | val DPORT = 0 42 | val IPORT = 1 43 | } 44 | 45 | // from the pov of the datapath 46 | class MemPortIo(data_width: Int)(implicit val conf: SodorCoreParams) extends Bundle 47 | { 48 | val req = new DecoupledIO(new MemReq(data_width)) 49 | val resp = Flipped(new ValidIO(new MemResp(data_width))) 50 | } 51 | 52 | class MemReq(data_width: Int)(implicit val conf: SodorCoreParams) extends Bundle 53 | { 54 | val addr = Output(UInt(conf.xprlen.W)) 55 | val data = Output(UInt(data_width.W)) 56 | val fcn = Output(UInt(M_X.getWidth.W)) // memory function code 57 | val typ = Output(UInt(MT_X.getWidth.W)) // memory type 58 | // To convert MemPortIO type to sign and size in TileLink format: subtract 1 from type, then take inversed MSB as signedness 59 | // and the remaining two bits as TileLink size 60 | def getTLSize = (typ - 1.U)(1, 0) 61 | def getTLSigned = ~(typ - 1.U)(2) 62 | def setType(tlSigned: Bool, tlSize: UInt) = { typ := Cat(~tlSigned, tlSize + 1.U) } 63 | } 64 | 65 | class MemResp(data_width: Int) extends Bundle 66 | { 67 | val data = Output(UInt(data_width.W)) 68 | } 69 | 70 | // Note: All `size` field in this class are base 2 logarithm 71 | class MemoryModule(numBytes: Int, useAsync: Boolean) { 72 | val addrWidth = log2Ceil(numBytes) 73 | val mem = if (useAsync) Mem(numBytes / 4, Vec(4, UInt(8.W))) else SyncReadMem(numBytes / 4, Vec(4, UInt(8.W))) 74 | 75 | // Convert size exponent to actual number of bytes - 1 76 | private def sizeToBytes(size: UInt) = MuxLookup(size, 3.U)(List(0.U -> 0.U, 1.U -> 1.U, 2.U -> 3.U)) 77 | 78 | private def getMask(bytes: UInt, storeOffset: UInt = 0.U) = { 79 | val mask = ("b00011111".U(8.W) << bytes).apply(7, 4) 80 | val maskWithOffset = (mask << storeOffset).apply(3, 0) 81 | maskWithOffset.asBools.reverse 82 | } 83 | 84 | private def splitWord(data: UInt) = VecInit(((data(31, 0).asBools.reverse grouped 8) map (bools => Cat(bools))).toSeq) 85 | 86 | // Read function 87 | def read(addr: UInt, size: UInt, signed: Bool) = { 88 | // Create a module to show signal inside 89 | class MemReader extends Module { 90 | val io = IO(new Bundle { 91 | val addr = Input(UInt(addrWidth.W)) 92 | val size = Input(UInt(2.W)) 93 | val signed = Input(Bool()) 94 | val data = Output(UInt(32.W)) 95 | 96 | val mem_addr = Output(UInt((addrWidth - 2).W)) 97 | val mem_data = Input(Vec(4, UInt(8.W))) 98 | }) 99 | // Sync argument if needed 100 | val s_offset = if (useAsync) io.addr(1, 0) else RegNext(io.addr(1, 0)) 101 | val s_size = if (useAsync) io.size else RegNext(io.size) 102 | val s_signed = if (useAsync) io.signed else RegNext(io.signed) 103 | 104 | // Read data from the banks and align 105 | io.mem_addr := io.addr(addrWidth - 1, 2) 106 | val readVec = io.mem_data 107 | val shiftedVec = splitWord(Cat(readVec) >> (s_offset << 3)) 108 | 109 | // Mask data according to the size 110 | val bytes = sizeToBytes(s_size) 111 | val sign = shiftedVec(3.U - bytes).apply(7) 112 | val masks = getMask(bytes) 113 | val maskedVec = (shiftedVec zip masks) map ({ case (byte, mask) => 114 | Mux(sign && s_signed, byte | ~Fill(8, mask), byte & Fill(8, mask)) 115 | }) 116 | 117 | io.data := Cat(maskedVec) 118 | } 119 | 120 | val memreader = Module(new MemReader) 121 | memreader.io.addr := addr 122 | memreader.io.size := size 123 | memreader.io.signed := signed 124 | memreader.io.mem_data := mem.read(memreader.io.mem_addr) 125 | 126 | memreader.io.data 127 | } 128 | def apply(addr: UInt, size: UInt, signed: Bool) = read(addr, size, signed) 129 | 130 | // Write function 131 | def write(addr: UInt, data: UInt, size: UInt, en: Bool) = { 132 | // Create a module to show signal inside 133 | class MemWriter extends Module { 134 | val io = IO(new Bundle { 135 | val addr = Input(UInt(addrWidth.W)) 136 | val data = Input(UInt(32.W)) 137 | val size = Input(UInt(2.W)) 138 | val en = Input(Bool()) 139 | 140 | val mem_addr = Output(UInt((addrWidth - 2).W)) 141 | val mem_data = Output(Vec(4, UInt(8.W))) 142 | val mem_masks = Output(Vec(4, Bool())) 143 | }) 144 | 145 | // Align data and mask 146 | val offset = io.addr(1, 0) 147 | val shiftedVec = splitWord(io.data << (offset << 3)) 148 | val masks = getMask(sizeToBytes(io.size), offset) 149 | 150 | // Write 151 | io.mem_addr := io.addr(addrWidth - 1, 2) 152 | io.mem_data := shiftedVec 153 | io.mem_masks := VecInit(masks map (mask => mask && io.en)) 154 | } 155 | 156 | val memwriter = Module(new MemWriter) 157 | memwriter.io.addr := addr 158 | memwriter.io.data := data 159 | memwriter.io.size := size 160 | memwriter.io.en := en 161 | 162 | when (en) { 163 | mem.write(memwriter.io.mem_addr, memwriter.io.mem_data, memwriter.io.mem_masks) 164 | } 165 | } 166 | } 167 | 168 | // NOTE: the default is enormous (and may crash your computer), but is bound by 169 | // what the fesvr expects the smallest memory size to be. A proper fix would 170 | // be to modify the fesvr to expect smaller sizes. 171 | //for 1,2 and 5 stage need for combinational reads 172 | class ScratchPadMemoryBase(num_core_ports: Int, num_bytes: Int = (1 << 21), useAsync: Boolean = true)(implicit val conf: SodorCoreParams) extends Module 173 | { 174 | val io = IO(new Bundle 175 | { 176 | val core_ports = Vec(num_core_ports, Flipped(new MemPortIo(data_width = conf.xprlen)) ) 177 | val debug_port = Flipped(new MemPortIo(data_width = 32)) 178 | }) 179 | val num_bytes_per_line = 8 180 | val num_lines = num_bytes / num_bytes_per_line 181 | println("\n Sodor Tile: creating Asynchronous Scratchpad Memory of size " + num_lines*num_bytes_per_line/1024 + " kB\n") 182 | val async_data = new MemoryModule(num_bytes, useAsync) 183 | for (i <- 0 until num_core_ports) 184 | { 185 | io.core_ports(i).resp.valid := (if (useAsync) io.core_ports(i).req.valid else RegNext(io.core_ports(i).req.valid, false.B)) 186 | io.core_ports(i).req.ready := true.B // for now, no back pressure 187 | } 188 | 189 | /////////// DPORT 190 | val req_addri = io.core_ports(DPORT).req.bits.addr 191 | 192 | val dport_req = io.core_ports(DPORT).req.bits 193 | val dport_wen = io.core_ports(DPORT).req.valid && dport_req.fcn === M_XWR 194 | io.core_ports(DPORT).resp.bits.data := async_data.read(dport_req.addr, dport_req.getTLSize, dport_req.getTLSigned) 195 | async_data.write(dport_req.addr, dport_req.data, dport_req.getTLSize, dport_wen) 196 | ///////////////// 197 | 198 | ///////////// IPORT 199 | if (num_core_ports == 2){ 200 | val iport_req = io.core_ports(IPORT).req.bits 201 | io.core_ports(IPORT).resp.bits.data := async_data.read(iport_req.addr, iport_req.getTLSize, iport_req.getTLSigned) 202 | } 203 | //////////// 204 | 205 | // DEBUG PORT------- 206 | io.debug_port.req.ready := true.B // for now, no back pressure 207 | io.debug_port.resp.valid := (if (useAsync) io.debug_port.req.valid else RegNext(io.debug_port.req.valid, false.B)) 208 | // asynchronous read 209 | val debug_port_req = io.debug_port.req.bits 210 | val debug_port_wen = io.debug_port.req.valid && debug_port_req.fcn === M_XWR 211 | io.debug_port.resp.bits.data := async_data.read(debug_port_req.addr, debug_port_req.getTLSize, debug_port_req.getTLSigned) 212 | async_data.write(debug_port_req.addr, debug_port_req.data, debug_port_req.getTLSize, debug_port_wen) 213 | } 214 | 215 | class AsyncScratchPadMemory(num_core_ports: Int, num_bytes: Int = (1 << 21))(implicit conf: SodorCoreParams) 216 | extends ScratchPadMemoryBase(num_core_ports, num_bytes, true)(conf) 217 | 218 | class SyncScratchPadMemory(num_core_ports: Int, num_bytes: Int = (1 << 21))(implicit conf: SodorCoreParams) 219 | extends ScratchPadMemoryBase(num_core_ports, num_bytes, false)(conf) 220 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import scala.math._ 6 | 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends MemoryOpConstants 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/scratchpad_adapter.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import chisel3.experimental._ 6 | 7 | import freechips.rocketchip.rocket._ 8 | import org.chipsalliance.cde.config.{Parameters, Field} 9 | import freechips.rocketchip.subsystem._ 10 | import freechips.rocketchip.diplomacy._ 11 | 12 | import freechips.rocketchip.tile._ 13 | import freechips.rocketchip.tilelink._ 14 | import freechips.rocketchip.util._ 15 | 16 | class SodorScratchpadAdapter(implicit p: Parameters, implicit val sodorConf: SodorCoreParams) extends CoreModule { 17 | val io = IO(new Bundle() { 18 | val slavePort = Flipped(new HellaCacheIO()) 19 | val memPort = new MemPortIo(data_width = coreDataBits) 20 | }) 21 | 22 | // =================== 23 | // Slave port signals 24 | val slave_req_ready = io.slavePort.req.ready 25 | val s2_slave_resp_valid = io.slavePort.resp.valid 26 | val slave_req_valid = io.slavePort.req.valid 27 | 28 | val slave_cmd = io.slavePort.req.bits.cmd 29 | val slave_req = io.slavePort.req.bits 30 | 31 | // All request are delayed for one cycle to avoid being killed 32 | val s1_slave_write_kill = io.slavePort.s1_kill 33 | val s1_slave_write_data = io.slavePort.s1_data.data 34 | val s1_slave_write_mask = io.slavePort.s1_data.mask 35 | 36 | val s1_slave_req_valid = RegNext(slave_req_valid, false.B) 37 | val s1_slave_cmd = RegNext(slave_cmd) 38 | val s1_slave_req = RegNext(slave_req) 39 | 40 | // Note that ScratchpadSlavePort requires 2-cycle delay, or it won't even send the response 41 | val s2_slave_read_data = io.slavePort.resp.bits.data_raw 42 | val s2_slave_read_mask = io.slavePort.resp.bits.mask 43 | 44 | val s2_nack = io.slavePort.s2_nack 45 | 46 | // Tie anything not defined below to DontCare 47 | io.slavePort := DontCare 48 | 49 | // =================== 50 | // HellaCacheIO to MemPortIo logic 51 | // Connect valid & ready bits 52 | slave_req_ready := io.memPort.req.ready 53 | io.memPort.req.valid := s1_slave_req_valid & (s1_slave_cmd === M_XRD || !s1_slave_write_kill) 54 | s2_slave_resp_valid := RegNext(io.memPort.resp.valid, false.B) 55 | 56 | // Connect read info 57 | s2_slave_read_mask := RegNext(new StoreGen(s1_slave_req.size, s1_slave_req.addr, 0.U, coreDataBytes).mask) 58 | s2_slave_read_data := RegNext(io.memPort.resp.bits.data) 59 | 60 | // Connect write info 61 | io.memPort.req.bits.addr := s1_slave_req.addr 62 | io.memPort.req.bits.data := s1_slave_write_data 63 | 64 | // Other connections 65 | s2_nack := false.B 66 | io.memPort.req.bits.fcn := Mux(s1_slave_cmd === M_XRD, M_XRD, M_XWR) 67 | // Since we don't have dword here (the bus only has 32 bits), s1_slave_req.size <= 2. 68 | // The expression below convert TileLink size and signedness to Sodor type. 69 | require(io.slavePort.req.bits.addr.getWidth == 32, "Slave port only support 32 bit address") 70 | assert (s1_slave_req.size <= 2.U, "Slave port received a bus request with unsupported size: %d", s1_slave_req.size) 71 | io.memPort.req.bits.setType(s1_slave_req.signed, s1_slave_req.size) 72 | } 73 | 74 | // This class simply route all memory request that doesn't belong to the scratchpad 75 | class SodorRequestRouter(cacheAddress: AddressSet)(implicit val conf: SodorCoreParams) extends Module { 76 | val io = IO(new Bundle() { 77 | val masterPort = new MemPortIo(data_width = conf.xprlen) 78 | val scratchPort = new MemPortIo(data_width = conf.xprlen) 79 | val corePort = Flipped(new MemPortIo(data_width = conf.xprlen)) 80 | val respAddress = Input(UInt(conf.xprlen.W)) 81 | }) 82 | 83 | val in_range = cacheAddress.contains(io.corePort.req.bits.addr) 84 | 85 | // Connect other signals 86 | io.masterPort.req.bits <> io.corePort.req.bits 87 | io.scratchPort.req.bits <> io.corePort.req.bits 88 | 89 | // Connect valid signal 90 | io.masterPort.req.valid := io.corePort.req.valid & !in_range 91 | io.scratchPort.req.valid := io.corePort.req.valid & in_range 92 | 93 | // Mux ready and request signal 94 | io.corePort.req.ready := Mux(in_range, io.scratchPort.req.ready, io.masterPort.req.ready) 95 | // Use respAddress to route response 96 | val resp_in_range = cacheAddress.contains(io.respAddress) 97 | io.corePort.resp.bits := Mux(resp_in_range, io.scratchPort.resp.bits, io.masterPort.resp.bits) 98 | io.corePort.resp.valid := Mux(resp_in_range, io.scratchPort.resp.valid, io.masterPort.resp.valid) 99 | } 100 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/sodor_internal_tile.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import org.chipsalliance.cde.config._ 7 | import freechips.rocketchip.subsystem._ 8 | import freechips.rocketchip.devices.tilelink._ 9 | import freechips.rocketchip.diplomacy._ 10 | 11 | import freechips.rocketchip.rocket._ 12 | import freechips.rocketchip.subsystem.{RocketCrossingParams} 13 | import freechips.rocketchip.tilelink._ 14 | import freechips.rocketchip.interrupts._ 15 | import freechips.rocketchip.util._ 16 | import freechips.rocketchip.tile._ 17 | import freechips.rocketchip.amba.axi4._ 18 | 19 | // Event sets for core CSR 20 | // This object simply disable all performance counters 21 | object CSREvents { 22 | val events = new EventSets(Seq(new EventSet((mask, hit) => false.B, Seq(("placeholder", () => false.B))))) 23 | } 24 | 25 | // Abstract core and tile base class for all cores 26 | abstract class AbstractCore extends Module { 27 | val mem_ports: Seq[MemPortIo] 28 | val interrupt: CoreInterrupts 29 | val hartid: UInt 30 | val reset_vector: UInt 31 | val io: Data 32 | } 33 | abstract class AbstractInternalTile(ports: Int)(implicit val p: Parameters, val conf: SodorCoreParams) extends Module { 34 | val io = IO(new Bundle { 35 | val debug_port = Flipped(new MemPortIo(data_width = conf.xprlen)) 36 | val master_port = Vec(ports, new MemPortIo(data_width = conf.xprlen)) 37 | val interrupt = Input(new CoreInterrupts(false)) 38 | val hartid = Input(UInt()) 39 | val reset_vector = Input(UInt()) 40 | }) 41 | } 42 | 43 | // Cores and internal tiles constructors 44 | trait SodorCoreFactory { 45 | def nMemPorts: Int 46 | def instantiate(implicit p: Parameters, conf: SodorCoreParams): AbstractCore 47 | } 48 | 49 | trait SodorInternalTileFactory { 50 | def nMemPorts: Int 51 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams): AbstractInternalTile 52 | } 53 | 54 | // Original sodor tile in this repo. 55 | // This tile is only for 3-stage core since it has a special structure (SyncMem and possibly one memory port). 56 | class SodorInternalTileStage3(range: AddressSet, ports: Int)(implicit p: Parameters, conf: SodorCoreParams) 57 | extends AbstractInternalTile(ports) 58 | { 59 | // Core memory port 60 | val core = Module(new sodor.stage3.Core()) 61 | core.io := DontCare 62 | val core_ports = Wire(Vec(2, new MemPortIo(data_width = conf.xprlen))) 63 | core.io.imem <> core_ports(1) 64 | core.io.dmem <> core_ports(0) 65 | 66 | // scratchpad memory port 67 | val memory = Module(new SyncScratchPadMemory(num_core_ports = ports)) 68 | val mem_ports = Wire(Vec(2, new MemPortIo(data_width = conf.xprlen))) 69 | // master memory port 70 | val master_ports = Wire(Vec(2, new MemPortIo(data_width = conf.xprlen))) 71 | 72 | // Connect ports 73 | ((mem_ports zip core_ports) zip master_ports).foreach({ case ((mem_port, core_port), master_port) => { 74 | val router = Module(new SodorRequestRouter(range)) 75 | router.io.corePort <> core_port 76 | router.io.scratchPort <> mem_port 77 | router.io.masterPort <> master_port 78 | // For sync memory, use the request address from the previous cycle 79 | val reg_resp_address = Reg(UInt(conf.xprlen.W)) 80 | when (core_port.req.fire) { reg_resp_address := core_port.req.bits.addr } 81 | router.io.respAddress := reg_resp_address 82 | }}) 83 | 84 | // If we only use one port, arbitrate 85 | if (ports == 1) 86 | { 87 | // Arbitrate scratchpad 88 | val scratchpad_arbiter = Module(new sodor.stage3.SodorMemArbiter) 89 | mem_ports(1) <> scratchpad_arbiter.io.imem 90 | mem_ports(0) <> scratchpad_arbiter.io.dmem 91 | scratchpad_arbiter.io.mem <> memory.io.core_ports(0) 92 | 93 | // Arbitrate master 94 | val master_buffer = Module(new SameCycleRequestBuffer) 95 | val master_arbiter = Module(new sodor.stage3.SodorMemArbiter) 96 | master_ports(1) <> master_arbiter.io.imem 97 | master_ports(0) <> master_arbiter.io.dmem 98 | master_arbiter.io.mem <> master_buffer.io.in 99 | master_buffer.io.out <> io.master_port(0) 100 | } 101 | else 102 | { 103 | mem_ports(1) <> memory.io.core_ports(1) 104 | mem_ports(0) <> memory.io.core_ports(0) 105 | master_ports(1) <> io.master_port(1) 106 | master_ports(0) <> io.master_port(0) 107 | } 108 | 109 | memory.io.debug_port <> io.debug_port 110 | 111 | core.interrupt <> io.interrupt 112 | core.hartid := io.hartid 113 | core.reset_vector := io.reset_vector 114 | } 115 | 116 | // The general Sodor tile for all cores other than 3-stage 117 | class SodorInternalTile(range: AddressSet, coreCtor: SodorCoreFactory)(implicit p: Parameters, conf: SodorCoreParams) 118 | extends AbstractInternalTile(coreCtor.nMemPorts) 119 | { 120 | val core = Module(coreCtor.instantiate) 121 | core.io := DontCare 122 | val memory = Module(new AsyncScratchPadMemory(num_core_ports = coreCtor.nMemPorts)) 123 | 124 | val nMemPorts = coreCtor.nMemPorts 125 | ((memory.io.core_ports zip core.mem_ports) zip io.master_port).foreach({ case ((mem_port, core_port), master_port) => { 126 | val router = Module(new SodorRequestRouter(range)) 127 | router.io.corePort <> core_port 128 | router.io.scratchPort <> mem_port 129 | router.io.masterPort <> master_port 130 | // For async memory, simply use the current request address 131 | router.io.respAddress := core_port.req.bits.addr 132 | }}) 133 | 134 | io.debug_port <> memory.io.debug_port 135 | 136 | core.interrupt <> io.interrupt 137 | core.hartid := io.hartid 138 | core.reset_vector := io.reset_vector 139 | } 140 | 141 | // Tile constructor 142 | case object Stage1Factory extends SodorInternalTileFactory { 143 | case object Stage1CoreFactory extends SodorCoreFactory { 144 | val nMemPorts = 2 145 | def instantiate(implicit p: Parameters, conf: SodorCoreParams) = new sodor.stage1.Core() 146 | } 147 | def nMemPorts = Stage1CoreFactory.nMemPorts 148 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams) = new SodorInternalTile(range, Stage1CoreFactory) 149 | } 150 | 151 | case object Stage2Factory extends SodorInternalTileFactory { 152 | case object Stage2CoreFactory extends SodorCoreFactory { 153 | val nMemPorts = 2 154 | def instantiate(implicit p: Parameters, conf: SodorCoreParams) = new sodor.stage2.Core() 155 | } 156 | def nMemPorts = Stage2CoreFactory.nMemPorts 157 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams) = new SodorInternalTile(range, Stage2CoreFactory) 158 | } 159 | 160 | case class Stage3Factory(ports: Int = 2) extends SodorInternalTileFactory { 161 | def nMemPorts = ports 162 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams) = new SodorInternalTileStage3(range, ports) 163 | } 164 | 165 | case object Stage5Factory extends SodorInternalTileFactory { 166 | case object Stage5CoreFactory extends SodorCoreFactory { 167 | val nMemPorts = 2 168 | def instantiate(implicit p: Parameters, conf: SodorCoreParams) = new sodor.stage5.Core() 169 | } 170 | def nMemPorts = Stage5CoreFactory.nMemPorts 171 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams) = new SodorInternalTile(range, Stage5CoreFactory) 172 | } 173 | 174 | case object UCodeFactory extends SodorInternalTileFactory { 175 | case object UCodeCoreFactory extends SodorCoreFactory { 176 | val nMemPorts = 1 177 | def instantiate(implicit p: Parameters, conf: SodorCoreParams) = new sodor.ucode.Core() 178 | } 179 | def nMemPorts = UCodeCoreFactory.nMemPorts 180 | def instantiate(range: AddressSet)(implicit p: Parameters, conf: SodorCoreParams) = new SodorInternalTile(range, UCodeCoreFactory) 181 | } 182 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/sodor_tile.scala: -------------------------------------------------------------------------------- 1 | package sodor.common 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import org.chipsalliance.cde.config._ 7 | import freechips.rocketchip.subsystem._ 8 | import freechips.rocketchip.devices.tilelink._ 9 | import freechips.rocketchip.diplomacy._ 10 | import freechips.rocketchip.rocket._ 11 | import freechips.rocketchip.subsystem.{RocketCrossingParams} 12 | import freechips.rocketchip.tilelink._ 13 | import freechips.rocketchip.interrupts._ 14 | import freechips.rocketchip.util._ 15 | import freechips.rocketchip.tile._ 16 | import freechips.rocketchip.amba.axi4._ 17 | import freechips.rocketchip.prci._ 18 | 19 | // Example parameter class copied from Ariane, not included in documentation but for compile check only 20 | // If you are here for documentation, DO NOT copy MyCoreParams and MyTileParams directly - always figure 21 | // out what parameters you need before you write the parameter class 22 | case class SodorCoreParams( 23 | bootFreqHz: BigInt = BigInt(1700000000), 24 | ports: Int = 2, 25 | xprlen: Int = 32, 26 | internalTile: SodorInternalTileFactory = Stage5Factory 27 | ) extends CoreParams { 28 | val xLen = xprlen 29 | val pgLevels = 2 30 | val useVM: Boolean = false 31 | val useHypervisor: Boolean = false 32 | val useUser: Boolean = false 33 | val useSupervisor: Boolean = false 34 | val useDebug: Boolean = true 35 | val useAtomics: Boolean = false 36 | val useAtomicsOnlyForIO: Boolean = false // copied from Rocket 37 | val useCompressed: Boolean = false 38 | override val useVector: Boolean = false 39 | val useSCIE: Boolean = false 40 | val useRVE: Boolean = false 41 | val mulDiv: Option[MulDivParams] = None 42 | val fpu: Option[FPUParams] = None 43 | val nLocalInterrupts: Int = 0 44 | val useNMI: Boolean = false 45 | val nPMPs: Int = 0 // TODO: Check 46 | val pmpGranularity: Int = 4 // copied from Rocket 47 | val nBreakpoints: Int = 0 // TODO: Check 48 | val useBPWatch: Boolean = false 49 | val mcontextWidth: Int = 0 // TODO: Check 50 | val scontextWidth: Int = 0 // TODO: Check 51 | val nPerfCounters: Int = 0 52 | val haveBasicCounters: Boolean = true 53 | val haveFSDirty: Boolean = false 54 | val misaWritable: Boolean = false 55 | val haveCFlush: Boolean = false 56 | val nL2TLBEntries: Int = 0 // copied from Rocket 57 | val nL2TLBWays: Int = 0 // copied from Rocket 58 | val mtvecInit: Option[BigInt] = Some(BigInt(0)) // copied from Rocket 59 | val mtvecWritable: Boolean = true // copied from Rocket 60 | val instBits: Int = if (useCompressed) 16 else 32 61 | val lrscCycles: Int = 80 // copied from Rocket 62 | val decodeWidth: Int = 1 // TODO: Check 63 | val fetchWidth: Int = 1 // TODO: Check 64 | val retireWidth: Int = 1 65 | val nPTECacheEntries: Int = 0 66 | val traceHasWdata: Boolean = false 67 | val useConditionalZero: Boolean = false 68 | val useZba: Boolean = false 69 | val useZbb: Boolean = false 70 | val useZbs: Boolean = false 71 | } 72 | 73 | // DOC include start: CanAttachTile 74 | case class SodorTileAttachParams( 75 | tileParams: SodorTileParams, 76 | crossingParams: RocketCrossingParams 77 | ) extends CanAttachTile { 78 | type TileType = SodorTile 79 | val lookup = PriorityMuxHartIdFromSeq(Seq(tileParams)) 80 | } 81 | // DOC include end: CanAttachTile 82 | 83 | case class SodorTileParams( 84 | name: Option[String] = Some("sodor_tile"), 85 | tileId: Int = 0, 86 | trace: Boolean = false, 87 | val core: SodorCoreParams = SodorCoreParams(), 88 | val scratchpad: DCacheParams = DCacheParams() 89 | ) extends InstantiableTileParams[SodorTile] 90 | { 91 | val beuAddr: Option[BigInt] = None 92 | val blockerCtrlAddr: Option[BigInt] = None 93 | val btb: Option[BTBParams] = None 94 | val boundaryBuffers: Boolean = false 95 | val dcache: Option[DCacheParams] = Some(scratchpad) 96 | val icache: Option[ICacheParams] = None 97 | val clockSinkParams: ClockSinkParameters = ClockSinkParameters() 98 | def instantiate(crossing: HierarchicalElementCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters): SodorTile = { 99 | new SodorTile(this, crossing, lookup) 100 | } 101 | val baseName = name.getOrElse("sodor_tile") 102 | val uniqueName = s"${baseName}_$tileId" 103 | } 104 | 105 | class SodorTile( 106 | val sodorParams: SodorTileParams, 107 | crossing: ClockCrossingType, 108 | lookup: LookupByHartIdImpl, 109 | q: Parameters) 110 | extends BaseTile(sodorParams, crossing, lookup, q) 111 | with SinksExternalInterrupts 112 | with SourcesExternalNotifications 113 | { 114 | // Private constructor ensures altered LazyModule.p is used implicitly 115 | def this(params: SodorTileParams, crossing: HierarchicalElementCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters) = 116 | this(params, crossing.crossingType, lookup, p) 117 | 118 | // Require TileLink nodes 119 | val intOutwardNode = None 120 | val masterNode = visibilityNode 121 | val slaveNode = TLIdentityNode() 122 | 123 | // Connect node to crossbar switches (bus) 124 | tlOtherMastersNode := tlMasterXbar.node 125 | masterNode :=* tlOtherMastersNode 126 | DisableMonitors { implicit p => tlSlaveXbar.node :*= slaveNode } 127 | 128 | // Slave port adapter 129 | val coreParams = { 130 | class C(implicit val p: Parameters) extends HasCoreParameters 131 | new C 132 | } 133 | val dtim_address = tileParams.dcache.flatMap { d => d.scratch.map { s => 134 | AddressSet.misaligned(s, d.dataScratchpadBytes) 135 | }} 136 | val dtim_adapter = dtim_address.map { addr => 137 | LazyModule(new ScratchpadSlavePort(addr, coreParams.coreDataBytes, false)) 138 | } 139 | dtim_adapter.foreach(lm => connectTLSlave(lm.node, lm.node.portParams.head.beatBytes)) 140 | 141 | val dtimProperty = dtim_adapter.map(d => Map( 142 | "ucb-bar,dtim" -> d.device.asProperty)).getOrElse(Nil) 143 | 144 | // Sodor master port adapter 145 | val imaster_adapter = if (sodorParams.core.ports == 2) Some(LazyModule(new SodorMasterAdapter()(p, sodorParams.core))) else None 146 | if (sodorParams.core.ports == 2) tlMasterXbar.node := imaster_adapter.get.node 147 | val dmaster_adapter = LazyModule(new SodorMasterAdapter()(p, sodorParams.core)) 148 | tlMasterXbar.node := dmaster_adapter.node 149 | 150 | // Implementation class (See below) 151 | override lazy val module = new SodorTileModuleImp(this) 152 | 153 | // Required entry of CPU device in the device tree for interrupt purpose 154 | val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("ucb-bar,sodor", "riscv")) { 155 | override def parent = Some(ResourceAnchors.cpus) 156 | override def describe(resources: ResourceBindings): Description = { 157 | val Description(name, mapping) = super.describe(resources) 158 | Description(name, mapping ++ 159 | cpuProperties ++ 160 | nextLevelCacheProperty ++ 161 | tileProperties ++ 162 | dtimProperty) 163 | } 164 | } 165 | 166 | ResourceBinding { 167 | Resource(cpuDevice, "reg").bind(ResourceAddress(tileId)) 168 | } 169 | 170 | override def makeMasterBoundaryBuffers(crossing: ClockCrossingType)(implicit p: Parameters) = { 171 | if (!sodorParams.boundaryBuffers) super.makeMasterBoundaryBuffers(crossing) 172 | else TLBuffer(BufferParams.none, BufferParams.flow, BufferParams.none, BufferParams.flow, BufferParams(1)) 173 | } 174 | 175 | override def makeSlaveBoundaryBuffers(crossing: ClockCrossingType)(implicit p: Parameters) = { 176 | if (!sodorParams.boundaryBuffers) super.makeSlaveBoundaryBuffers(crossing) 177 | else TLBuffer(BufferParams.flow, BufferParams.none, BufferParams.none, BufferParams.none, BufferParams.none) 178 | } 179 | 180 | } 181 | 182 | class SodorTileModuleImp(outer: SodorTile) extends BaseTileModuleImp(outer){ 183 | // Sodor core parameters 184 | implicit val conf = outer.sodorParams.core 185 | 186 | // Scratchpad checking 187 | require(outer.dtim_adapter.isDefined, "Sodor core must have a scratchpad: make sure that tileParams.dcache.scratch is defined.") 188 | require(outer.dtim_address.get.length == 1, "Sodor core can only have one scratchpad.") 189 | 190 | // Tile 191 | val tile = Module(outer.sodorParams.core.internalTile.instantiate(outer.dtim_address.get.apply(0))) 192 | 193 | // Add scratchpad adapter 194 | val scratchpadAdapter = Module(new SodorScratchpadAdapter()(outer.p, conf)) 195 | scratchpadAdapter.io.slavePort <> outer.dtim_adapter.get.module.io.dmem 196 | 197 | // Connect tile 198 | tile.io.debug_port <> scratchpadAdapter.io.memPort 199 | tile.io.master_port(0) <> outer.dmaster_adapter.module.io.dport 200 | if (outer.sodorParams.core.ports == 2) tile.io.master_port(1) <> outer.imaster_adapter.get.module.io.dport 201 | 202 | // Connect interrupts 203 | outer.decodeCoreInterrupts(tile.io.interrupt) 204 | 205 | // Connect constants 206 | tile.io.hartid := outer.hartIdSinkNode.bundle 207 | tile.io.reset_vector := outer.resetVectorSinkNode.bundle 208 | } 209 | 210 | class WithNSodorCores( 211 | n: Int = 1, 212 | internalTile: SodorInternalTileFactory = Stage3Factory() 213 | ) extends Config((site, here, up) => { 214 | case TilesLocated(InSubsystem) => { 215 | // Calculate the next available hart ID (since hart ID cannot be duplicated) 216 | val prev = up(TilesLocated(InSubsystem), site) 217 | require(prev.length == 0, "Sodor doesn't support multiple core.") 218 | val idOffset = up(NumTiles) 219 | // Create TileAttachParams for every core to be instantiated 220 | (0 until n).map { i => 221 | SodorTileAttachParams( 222 | tileParams = SodorTileParams( 223 | tileId = i + idOffset, 224 | scratchpad = DCacheParams( 225 | nSets = 4096, // Very large so we have enough SPAD for bmark tests 226 | nWays = 1, 227 | nMSHRs = 0, 228 | scratch = Some(0x80000000L) 229 | ), 230 | core = SodorCoreParams( 231 | ports = internalTile.nMemPorts, 232 | internalTile = internalTile 233 | ) 234 | ), 235 | crossingParams = RocketCrossingParams() 236 | ) 237 | } ++ prev 238 | } 239 | // Configurate # of bytes in one memory / IO transaction. For RV64, one load/store instruction can transfer 8 bytes at most. 240 | case SystemBusKey => up(SystemBusKey, site).copy(beatBytes = 4) 241 | case NumTiles => up(NumTiles) + n 242 | }) { 243 | require(n == 1, "Sodor doesn't support multiple core.") 244 | } 245 | -------------------------------------------------------------------------------- /src/main/scala/sodor/common/util.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Utility Functions 3 | //-------------------------------------------------------------------------- 4 | 5 | package sodor.common 6 | 7 | import chisel3._ 8 | import chisel3.util._ 9 | import scala.math._ 10 | import scala.collection.mutable.ArrayBuffer 11 | 12 | object Util 13 | { 14 | implicit def intToUInt(x: Int): UInt = x.U 15 | implicit def intToBoolean(x: Int): Boolean = if (x != 0) true else false 16 | implicit def booleanToInt(x: Boolean): Int = if (x) 1 else 0 17 | implicit def booleanToBool(x: Boolean): Bool = x.B 18 | implicit def sextToConv(x: UInt) = new AnyRef { 19 | def sextTo(n: Int): UInt = Cat(Fill(n - x.getWidth, x(x.getWidth-1)), x) 20 | } 21 | 22 | implicit def wcToUInt(c: WideCounter): UInt = c.value 23 | implicit class UIntIsOneOf(val x: UInt) extends AnyVal { 24 | def isOneOf(s: Seq[UInt]): Bool = s.map(x === _).reduce(_||_) 25 | 26 | def isOneOf(u1: UInt, u2: UInt*): Bool = isOneOf(u1 +: u2.toSeq) 27 | } 28 | 29 | implicit class UIntToAugmentedUInt(val x: UInt) extends AnyVal { 30 | def sextTo(n: Int): UInt = { 31 | require(x.getWidth <= n) 32 | if (x.getWidth == n) x 33 | else Cat(Fill(n - x.getWidth, x(x.getWidth-1)), x) 34 | } 35 | 36 | def padTo(n: Int): UInt = { 37 | require(x.getWidth <= n) 38 | if (x.getWidth == n) x 39 | else Cat(0.U((n - x.getWidth).W), x) 40 | } 41 | 42 | def extract(hi: Int, lo: Int): UInt = { 43 | if (hi == lo-1) 0.U 44 | else x(hi, lo) 45 | } 46 | 47 | def inRange(base: UInt, bounds: UInt) = x >= base && x < bounds 48 | } 49 | } 50 | 51 | 52 | //do two masks have at least 1 bit match? 53 | object maskMatch 54 | { 55 | def apply(msk1: UInt, msk2: UInt): Bool = 56 | { 57 | val br_match = (msk1 & msk2) =/= 0.U 58 | return br_match 59 | } 60 | } 61 | 62 | //clear one-bit in the Mask as specified by the idx 63 | object clearMaskBit 64 | { 65 | def apply(msk: UInt, idx: UInt): UInt = 66 | { 67 | return (msk & ~(1.U << idx))(msk.getWidth-1, 0) 68 | } 69 | } 70 | 71 | //shift a register over by one bit 72 | object PerformShiftRegister 73 | { 74 | def apply(reg_val: Bits, new_bit: Bool): Bits = 75 | { 76 | reg_val := Cat(reg_val(reg_val.getWidth-1, 0).asUInt, new_bit.asUInt).asUInt 77 | reg_val 78 | } 79 | } 80 | 81 | object Split 82 | { 83 | // is there a better way to do do this? 84 | def apply(x: Bits, n0: Int) = { 85 | val w = checkWidth(x, n0) 86 | (x(w-1,n0), x(n0-1,0)) 87 | } 88 | def apply(x: Bits, n1: Int, n0: Int) = { 89 | val w = checkWidth(x, n1, n0) 90 | (x(w-1,n1), x(n1-1,n0), x(n0-1,0)) 91 | } 92 | def apply(x: Bits, n2: Int, n1: Int, n0: Int) = { 93 | val w = checkWidth(x, n2, n1, n0) 94 | (x(w-1,n2), x(n2-1,n1), x(n1-1,n0), x(n0-1,0)) 95 | } 96 | 97 | private def checkWidth(x: Bits, n: Int*) = { 98 | val w = x.getWidth 99 | def decreasing(x: Seq[Int]): Boolean = 100 | if (x.tail.isEmpty) true 101 | else x.head > x.tail.head && decreasing(x.tail) 102 | require(decreasing(w :: n.toList)) 103 | w 104 | } 105 | } 106 | 107 | 108 | // a counter that clock gates most of its MSBs using the LSB carry-out 109 | case class WideCounter(width: Int, inc: UInt = 1.U, reset: Boolean = true) 110 | { 111 | private val isWide = width > 2*inc.getWidth 112 | private val smallWidth = if (isWide) inc.getWidth max log2Ceil(width) else width 113 | private val small = if (reset) RegInit(0.asUInt(smallWidth.W)) else Reg(UInt(smallWidth.W)) 114 | private val nextSmall = small +& inc 115 | small := nextSmall 116 | 117 | private val large = if (isWide) { 118 | val r = if (reset) RegInit(0.asUInt((width - smallWidth).W)) else Reg(UInt((width - smallWidth).W)) 119 | when (nextSmall(smallWidth)) { r := r + 1.U } 120 | r 121 | } else null 122 | 123 | val value = if (isWide) Cat(large, small) else small 124 | lazy val carryOut = { 125 | val lo = (small ^ nextSmall) >> 1 126 | if (!isWide) lo else { 127 | val hi = Mux(nextSmall(smallWidth), large ^ (large +& 1.U), 0.U) >> 1 128 | Cat(hi, lo) 129 | } 130 | } 131 | 132 | def := (x: UInt) = { 133 | small := x 134 | if (isWide) large := x >> smallWidth 135 | } 136 | } 137 | 138 | // taken from rocket FPU 139 | object RegEn 140 | { 141 | def apply[T <: Data](data: T, en: Bool) = 142 | { 143 | val r = Reg(data) 144 | when (en) { r := data } 145 | r 146 | } 147 | def apply[T <: Bits](data: T, en: Bool, resetVal: T) = 148 | { 149 | val r = RegInit(resetVal) 150 | when (en) { r := data } 151 | r 152 | } 153 | } 154 | 155 | object Str 156 | { 157 | def apply(s: String): UInt = { 158 | var i = BigInt(0) 159 | require(s.forall(validChar _)) 160 | for (c <- s) 161 | i = (i << 8) | c 162 | i.asUInt((s.length*8).W) 163 | } 164 | def apply(x: Char): Bits = { 165 | require(validChar(x)) 166 | val lit = x.asUInt(8.W) 167 | lit 168 | } 169 | def apply(x: UInt): Bits = apply(x, 10) 170 | def apply(x: UInt, radix: Int): Bits = { 171 | val rad = radix.U 172 | val digs = digits(radix) 173 | val w = x.getWidth 174 | require(w > 0) 175 | 176 | var q = x 177 | var s = digs(q % rad) 178 | for (i <- 1 until ceil(log(2)/log(radix)*w).toInt) { 179 | q = q / rad 180 | s = Cat(Mux((radix == 10).B && q === 0.U, Str(' '), digs(q % rad)), s) 181 | } 182 | s 183 | } 184 | def apply(x: SInt): Bits = apply(x, 10) 185 | def apply(x: SInt, radix: Int): Bits = { 186 | val neg = x < 0.S 187 | val abs = Mux(neg, -x, x).asUInt 188 | if (radix != 10) { 189 | Cat(Mux(neg, Str('-'), Str(' ')), Str(abs, radix)) 190 | } else { 191 | val rad = radix.U 192 | val digs = digits(radix) 193 | val w = abs.getWidth 194 | require(w > 0) 195 | 196 | var q = abs 197 | var s = digs(q % rad) 198 | var needSign = neg 199 | for (i <- 1 until ceil(log(2)/log(radix)*w).toInt) { 200 | q = q / rad 201 | val placeSpace = q === 0.U 202 | val space = Mux(needSign, Str('-'), Str(' ')) 203 | needSign = needSign && !placeSpace 204 | s = Cat(Mux(placeSpace, space, digs(q % rad)), s) 205 | } 206 | Cat(Mux(needSign, Str('-'), Str(' ')), s) 207 | } 208 | } 209 | 210 | def bigIntToString(x: BigInt): String = { 211 | val s = new StringBuilder 212 | var b = x 213 | while (b != 0) { 214 | s += (x & 0xFF).toChar 215 | b = b >> 8 216 | } 217 | s.toString 218 | } 219 | 220 | private def digit(d: Int): Char = (if (d < 10) '0'+d else 'a'-10+d).toChar 221 | private def digits(radix: Int): Vec[Bits] = 222 | VecInit((0 until radix).map(i => Str(digit(i)))) 223 | 224 | private def validChar(x: Char) = x == (x & 0xFF) 225 | } 226 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_1stage/consts.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Constants 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | package sodor.stage1 9 | package constants 10 | { 11 | 12 | import chisel3._ 13 | import chisel3.util._ 14 | 15 | 16 | trait SodorProcConstants 17 | { 18 | // for debugging, print out the commit information. 19 | // can be compared against the riscv-isa-run Spike ISA simulator's commit logger. 20 | val PRINT_COMMIT_LOG = false 21 | } 22 | 23 | trait ScalarOpConstants 24 | { 25 | //************************************ 26 | // Control Signals 27 | 28 | val Y = true.B 29 | val N = false.B 30 | 31 | // PC Select Signal 32 | val PC_4 = 0.asUInt(3.W) // PC + 4 33 | val PC_BR = 1.asUInt(3.W) // branch_target 34 | val PC_J = 2.asUInt(3.W) // jump_target 35 | val PC_JR = 3.asUInt(3.W) // jump_reg_target 36 | val PC_EXC = 4.asUInt(3.W) // exception 37 | 38 | // Branch Type 39 | val BR_N = 0.asUInt(4.W) // Next 40 | val BR_NE = 1.asUInt(4.W) // Branch on NotEqual 41 | val BR_EQ = 2.asUInt(4.W) // Branch on Equal 42 | val BR_GE = 3.asUInt(4.W) // Branch on Greater/Equal 43 | val BR_GEU = 4.asUInt(4.W) // Branch on Greater/Equal Unsigned 44 | val BR_LT = 5.asUInt(4.W) // Branch on Less Than 45 | val BR_LTU = 6.asUInt(4.W) // Branch on Less Than Unsigned 46 | val BR_J = 7.asUInt(4.W) // Jump 47 | val BR_JR = 8.asUInt(4.W) // Jump Register 48 | 49 | // RS1 Operand Select Signal 50 | val OP1_RS1 = 0.asUInt(2.W) // Register Source #1 51 | val OP1_IMU = 1.asUInt(2.W) // immediate, U-type 52 | val OP1_IMZ = 2.asUInt(2.W) // Zero-extended rs1 field of inst, for CSRI instructions 53 | val OP1_X = 0.asUInt(2.W) 54 | 55 | // RS2 Operand Select Signal 56 | val OP2_RS2 = 0.asUInt(2.W) // Register Source #2 57 | val OP2_IMI = 1.asUInt(2.W) // immediate, I-type 58 | val OP2_IMS = 2.asUInt(2.W) // immediate, S-type 59 | val OP2_PC = 3.asUInt(2.W) // PC 60 | val OP2_X = 0.asUInt(2.W) 61 | 62 | // Register File Write Enable Signal 63 | val REN_0 = false.B 64 | val REN_1 = true.B 65 | val REN_X = false.B 66 | 67 | // ALU Operation Signal 68 | val ALU_ADD = 1.asUInt(4.W) 69 | val ALU_SUB = 2.asUInt(4.W) 70 | val ALU_SLL = 3.asUInt(4.W) 71 | val ALU_SRL = 4.asUInt(4.W) 72 | val ALU_SRA = 5.asUInt(4.W) 73 | val ALU_AND = 6.asUInt(4.W) 74 | val ALU_OR = 7.asUInt(4.W) 75 | val ALU_XOR = 8.asUInt(4.W) 76 | val ALU_SLT = 9.asUInt(4.W) 77 | val ALU_SLTU= 10.asUInt(4.W) 78 | val ALU_COPY1= 11.asUInt(4.W) 79 | val ALU_X = 0.asUInt(4.W) 80 | 81 | // Writeback Select Signal 82 | val WB_ALU = 0.asUInt(2.W) 83 | val WB_MEM = 1.asUInt(2.W) 84 | val WB_PC4 = 2.asUInt(2.W) 85 | val WB_CSR = 3.asUInt(2.W) 86 | val WB_X = 0.asUInt(2.W) 87 | 88 | // Memory Function Type (Read,Write,Fence) Signal 89 | val MWR_R = 0.asUInt(2.W) 90 | val MWR_W = 1.asUInt(2.W) 91 | val MWR_F = 2.asUInt(2.W) 92 | val MWR_X = 0.asUInt(2.W) 93 | 94 | // Memory Enable Signal 95 | val MEN_0 = false.B 96 | val MEN_1 = true.B 97 | val MEN_X = false.B 98 | 99 | // Memory Mask Type Signal 100 | val MSK_B = 0.asUInt(3.W) 101 | val MSK_BU = 1.asUInt(3.W) 102 | val MSK_H = 2.asUInt(3.W) 103 | val MSK_HU = 3.asUInt(3.W) 104 | val MSK_W = 4.asUInt(3.W) 105 | val MSK_X = 4.asUInt(3.W) 106 | 107 | 108 | // Cache Flushes & Sync Primitives 109 | val M_N = 0.asUInt(3.W) 110 | val M_SI = 1.asUInt(3.W) // synch instruction stream 111 | val M_SD = 2.asUInt(3.W) // synch data stream 112 | val M_FA = 3.asUInt(3.W) // flush all caches 113 | val M_FD = 4.asUInt(3.W) // flush data cache 114 | 115 | // Memory Functions (read, write, fence) 116 | val MT_READ = 0.asUInt(2.W) 117 | val MT_WRITE = 1.asUInt(2.W) 118 | val MT_FENCE = 2.asUInt(2.W) 119 | 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_1stage/core.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 Jul 30 7 | // 8 | // Describes a simple RISCV 1-stage processor 9 | // - No div/mul/rem 10 | // - No FPU 11 | // - implements a minimal supervisor mode (can trap to handle the 12 | // above instructions) 13 | // 14 | // The goal of the 1-stage is to provide the simpliest, easiest-to-read code to 15 | // demonstrate the RISC-V ISA. 16 | 17 | package sodor.stage1 18 | 19 | import chisel3._ 20 | import chisel3.util._ 21 | import sodor.common._ 22 | 23 | import org.chipsalliance.cde.config.Parameters 24 | import freechips.rocketchip.rocket.CoreInterrupts 25 | 26 | class CoreIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle 27 | { 28 | val imem = new MemPortIo(conf.xprlen) 29 | val dmem = new MemPortIo(conf.xprlen) 30 | val ddpath = Flipped(new DebugDPath()) 31 | val dcpath = Flipped(new DebugCPath()) 32 | val interrupt = Input(new CoreInterrupts(false)) 33 | val hartid = Input(UInt()) 34 | val reset_vector = Input(UInt()) 35 | } 36 | 37 | class Core(implicit val p: Parameters, val conf: SodorCoreParams) extends AbstractCore 38 | { 39 | val io = IO(new CoreIo()) 40 | io := DontCare 41 | val c = Module(new CtlPath()) 42 | val d = Module(new DatPath()) 43 | c.io.ctl <> d.io.ctl 44 | c.io.dat <> d.io.dat 45 | 46 | io.imem <> c.io.imem 47 | io.imem <> d.io.imem 48 | 49 | io.dmem <> c.io.dmem 50 | io.dmem <> d.io.dmem 51 | io.dmem.req.valid := c.io.dmem.req.valid 52 | io.dmem.req.bits.typ := c.io.dmem.req.bits.typ 53 | io.dmem.req.bits.fcn := c.io.dmem.req.bits.fcn 54 | 55 | d.io.ddpath <> io.ddpath 56 | c.io.dcpath <> io.dcpath 57 | 58 | d.io.interrupt := io.interrupt 59 | d.io.hartid := io.hartid 60 | d.io.reset_vector := io.reset_vector 61 | 62 | val mem_ports = List(io.dmem, io.imem) 63 | val interrupt = io.interrupt 64 | val hartid = io.hartid 65 | val reset_vector = io.reset_vector 66 | } 67 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_1stage/cpath.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Control Path 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | 7 | package sodor.stage1 8 | 9 | import chisel3._ 10 | import chisel3.util._ 11 | 12 | import freechips.rocketchip.rocket.{CSR, Causes} 13 | 14 | import sodor.common._ 15 | import sodor.common.Instructions._ 16 | import sodor.stage1.Constants._ 17 | 18 | class CtlToDatIo extends Bundle() 19 | { 20 | val stall = Output(Bool()) 21 | val dmiss = Output(Bool()) 22 | val pc_sel = Output(UInt(PC_4.getWidth.W)) 23 | val op1_sel = Output(UInt(OP1_X.getWidth.W)) 24 | val op2_sel = Output(UInt(OP2_X.getWidth.W)) 25 | val alu_fun = Output(UInt(ALU_X.getWidth.W)) 26 | val wb_sel = Output(UInt(WB_X.getWidth.W)) 27 | val rf_wen = Output(Bool()) 28 | val csr_cmd = Output(UInt(CSR.SZ.W)) 29 | val exception = Output(Bool()) 30 | val exception_cause = Output(UInt(32.W)) 31 | val pc_sel_no_xept = Output(UInt(PC_4.getWidth.W)) // Use only for instuction misalignment detection 32 | } 33 | 34 | class CpathIo(implicit val conf: SodorCoreParams) extends Bundle() 35 | { 36 | val dcpath = Flipped(new DebugCPath()) 37 | val imem = new MemPortIo(conf.xprlen) 38 | val dmem = new MemPortIo(conf.xprlen) 39 | val dat = Flipped(new DatToCtlIo()) 40 | val ctl = new CtlToDatIo() 41 | } 42 | 43 | 44 | class CtlPath(implicit val conf: SodorCoreParams) extends Module 45 | { 46 | val io = IO(new CpathIo()) 47 | io := DontCare 48 | 49 | val csignals = 50 | ListLookup(io.dat.inst, 51 | List(N, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 52 | Array( /* val | BR | op1 | op2 | ALU | wb | rf | mem | mem | mask | csr */ 53 | /* inst | type | sel | sel | fcn | sel | wen | en | wr | type | cmd */ 54 | LW -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_W, CSR.N), 55 | LB -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_B, CSR.N), 56 | LBU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_BU, CSR.N), 57 | LH -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_H, CSR.N), 58 | LHU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_HU, CSR.N), 59 | SW -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_W, CSR.N), 60 | SB -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_B, CSR.N), 61 | SH -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_H, CSR.N), 62 | 63 | AUIPC -> List(Y, BR_N , OP1_IMU, OP2_PC , ALU_ADD , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 64 | LUI -> List(Y, BR_N , OP1_IMU, OP2_X , ALU_COPY1, WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 65 | 66 | ADDI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 67 | ANDI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_AND , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 68 | ORI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_OR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 69 | XORI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_XOR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 70 | SLTI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLT , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 71 | SLTIU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLTU, WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 72 | SLLI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 73 | SRAI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SRA , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 74 | SRLI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SRL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 75 | 76 | SLL -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 77 | ADD -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_ADD , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 78 | SUB -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SUB , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 79 | SLT -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLT , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 80 | SLTU -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLTU, WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 81 | AND -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_AND , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 82 | OR -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_OR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 83 | XOR -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_XOR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 84 | SRA -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SRA , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 85 | SRL -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SRL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 86 | 87 | JAL -> List(Y, BR_J , OP1_X , OP2_X , ALU_X , WB_PC4, REN_1, MEN_0, M_X , MT_X, CSR.N), 88 | JALR -> List(Y, BR_JR , OP1_RS1, OP2_IMI , ALU_X , WB_PC4, REN_1, MEN_0, M_X , MT_X, CSR.N), 89 | BEQ -> List(Y, BR_EQ , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 90 | BNE -> List(Y, BR_NE , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 91 | BGE -> List(Y, BR_GE , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 92 | BGEU -> List(Y, BR_GEU, OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 93 | BLT -> List(Y, BR_LT , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 94 | BLTU -> List(Y, BR_LTU, OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 95 | 96 | CSRRWI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.W), 97 | CSRRSI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.S), 98 | CSRRCI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.C), 99 | CSRRW -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.W), 100 | CSRRS -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.S), 101 | CSRRC -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1, WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.C), 102 | 103 | ECALL -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 104 | MRET -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 105 | DRET -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 106 | EBREAK -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 107 | WFI -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), // implemented as a NOP 108 | 109 | FENCE_I -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 110 | FENCE -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N) 111 | // we are already sequentially consistent, so no need to honor the fence instruction 112 | )) 113 | 114 | // Put these control signals into variables 115 | val (cs_val_inst: Bool) :: cs_br_type :: cs_op1_sel :: cs_op2_sel :: cs0 = csignals 116 | val cs_alu_fun :: cs_wb_sel :: (cs_rf_wen: Bool) :: cs1 = cs0 117 | val (cs_mem_en: Bool) :: cs_mem_fcn :: cs_msk_sel :: cs_csr_cmd :: Nil = cs1 118 | 119 | // Branch Logic 120 | val ctrl_pc_sel_no_xept = Mux(io.dat.csr_interrupt , PC_EXC, 121 | Mux(cs_br_type === BR_N , PC_4, 122 | Mux(cs_br_type === BR_NE , Mux(!io.dat.br_eq, PC_BR, PC_4), 123 | Mux(cs_br_type === BR_EQ , Mux( io.dat.br_eq, PC_BR, PC_4), 124 | Mux(cs_br_type === BR_GE , Mux(!io.dat.br_lt, PC_BR, PC_4), 125 | Mux(cs_br_type === BR_GEU, Mux(!io.dat.br_ltu, PC_BR, PC_4), 126 | Mux(cs_br_type === BR_LT , Mux( io.dat.br_lt, PC_BR, PC_4), 127 | Mux(cs_br_type === BR_LTU, Mux( io.dat.br_ltu, PC_BR, PC_4), 128 | Mux(cs_br_type === BR_J , PC_J, 129 | Mux(cs_br_type === BR_JR , PC_JR, 130 | PC_4)))))))))) 131 | val ctrl_pc_sel = Mux(io.ctl.exception || io.dat.csr_eret, PC_EXC, ctrl_pc_sel_no_xept) 132 | 133 | // mem_en suppression: no new memory request shall be issued after the memory operation of the current instruction is done. 134 | // Once we get a new instruction, we reset this flag. 135 | val reg_mem_en = RegInit(false.B) 136 | when (io.dmem.resp.valid) { 137 | reg_mem_en := false.B 138 | } .elsewhen (io.imem.resp.valid) { 139 | reg_mem_en := cs_mem_en 140 | } 141 | val mem_en = Mux(io.imem.resp.valid, cs_mem_en, reg_mem_en) 142 | 143 | val data_misaligned = Wire(Bool()) 144 | io.ctl.dmiss := !((mem_en && (io.dmem.resp.valid || data_misaligned)) || !mem_en) 145 | val stall = io.dat.imiss || io.ctl.dmiss 146 | 147 | 148 | // Set the data-path control signals 149 | io.ctl.stall := stall 150 | io.ctl.pc_sel := ctrl_pc_sel 151 | io.ctl.op1_sel := cs_op1_sel 152 | io.ctl.op2_sel := cs_op2_sel 153 | io.ctl.alu_fun := cs_alu_fun 154 | io.ctl.wb_sel := cs_wb_sel 155 | io.ctl.rf_wen := Mux(stall || io.ctl.exception, false.B, cs_rf_wen) 156 | 157 | // convert CSR instructions with raddr1 == 0 to read-only CSR commands 158 | val rs1_addr = io.dat.inst(RS1_MSB, RS1_LSB) 159 | val csr_ren = (cs_csr_cmd === CSR.S || cs_csr_cmd === CSR.C) && rs1_addr === 0.U 160 | val csr_cmd = Mux(csr_ren, CSR.R, cs_csr_cmd) 161 | 162 | io.ctl.csr_cmd := Mux(stall, CSR.N, csr_cmd) 163 | 164 | // Memory Requests 165 | io.dmem.req.valid := mem_en && !io.ctl.exception 166 | io.dmem.req.bits.fcn := cs_mem_fcn 167 | io.dmem.req.bits.typ := cs_msk_sel 168 | 169 | // Exception Handling --------------------- 170 | io.ctl.pc_sel_no_xept := ctrl_pc_sel_no_xept 171 | val illegal = (!cs_val_inst && io.imem.resp.valid) 172 | 173 | // Data misalignment detection 174 | // For example, if type is 3 (word), the mask is ~(0b111 << (3 - 1)) = ~0b100 = 0b011. 175 | val misaligned_mask = Wire(UInt(3.W)) 176 | misaligned_mask := ~(7.U(3.W) << (cs_msk_sel - 1.U)(1, 0)) 177 | data_misaligned := (misaligned_mask & io.dat.mem_address_low).orR && mem_en 178 | val mem_store = cs_mem_fcn === M_XWR 179 | 180 | // Set exception flag and cause 181 | // Exception priority matters! 182 | io.ctl.exception := illegal || io.dat.inst_misaligned || data_misaligned 183 | io.ctl.exception_cause := Mux(illegal, Causes.illegal_instruction.U, 184 | Mux(io.dat.inst_misaligned, Causes.misaligned_fetch.U, 185 | Mux(mem_store, Causes.misaligned_store.U, 186 | Causes.misaligned_load.U 187 | ))) 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_1stage/dpath.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor 1-Stage Datapath 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2012 Jan 11 7 | 8 | package sodor.stage1 9 | 10 | import chisel3._ 11 | import chisel3.util._ 12 | 13 | import org.chipsalliance.cde.config.Parameters 14 | import freechips.rocketchip.rocket.{CSRFile, Causes} 15 | import freechips.rocketchip.rocket.CoreInterrupts 16 | 17 | import sodor.stage1.Constants._ 18 | import sodor.common._ 19 | 20 | class DatToCtlIo(implicit val conf: SodorCoreParams) extends Bundle() 21 | { 22 | val inst = Output(UInt(32.W)) 23 | val imiss = Output(Bool()) 24 | val br_eq = Output(Bool()) 25 | val br_lt = Output(Bool()) 26 | val br_ltu = Output(Bool()) 27 | val csr_eret = Output(Bool()) 28 | val csr_interrupt = Output(Bool()) 29 | val inst_misaligned = Output(Bool()) 30 | val mem_address_low = Output(UInt(3.W)) 31 | } 32 | 33 | class DpathIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle() 34 | { 35 | val ddpath = Flipped(new DebugDPath()) 36 | val imem = new MemPortIo(conf.xprlen) 37 | val dmem = new MemPortIo(conf.xprlen) 38 | val ctl = Flipped(new CtlToDatIo()) 39 | val dat = new DatToCtlIo() 40 | val interrupt = Input(new CoreInterrupts(false)) 41 | val hartid = Input(UInt()) 42 | val reset_vector = Input(UInt()) 43 | } 44 | 45 | class DatPath(implicit val p: Parameters, val conf: SodorCoreParams) extends Module 46 | { 47 | val io = IO(new DpathIo()) 48 | io := DontCare 49 | 50 | // Exception handling values 51 | val tval_data_ma = Wire(UInt(conf.xprlen.W)) 52 | val tval_inst_ma = Wire(UInt(conf.xprlen.W)) 53 | 54 | // Interrupt kill 55 | val interrupt_edge = Wire(Bool()) 56 | 57 | // Instruction Fetch 58 | val pc_next = Wire(UInt(32.W)) 59 | val pc_plus4 = Wire(UInt(32.W)) 60 | val br_target = Wire(UInt(32.W)) 61 | val jmp_target = Wire(UInt(32.W)) 62 | val jump_reg_target = Wire(UInt(32.W)) 63 | val exception_target = Wire(UInt(32.W)) 64 | 65 | // PC Register 66 | pc_next := MuxCase(pc_plus4, Array( 67 | (io.ctl.pc_sel === PC_4) -> pc_plus4, 68 | (io.ctl.pc_sel === PC_BR) -> br_target, 69 | (io.ctl.pc_sel === PC_J ) -> jmp_target, 70 | (io.ctl.pc_sel === PC_JR) -> jump_reg_target, 71 | (io.ctl.pc_sel === PC_EXC) -> exception_target 72 | )) 73 | 74 | val pc_reg = RegInit(io.reset_vector) 75 | 76 | when (!io.ctl.stall) 77 | { 78 | pc_reg := pc_next 79 | } 80 | 81 | pc_plus4 := (pc_reg + 4.asUInt(conf.xprlen.W)) 82 | 83 | 84 | // Instruction memory buffer to store instruction during multicycle data request 85 | io.dat.imiss := (io.imem.req.valid && !io.imem.resp.valid) 86 | val reg_dmiss = RegNext(io.ctl.dmiss, false.B) 87 | val if_inst_buffer = RegInit(0.U(32.W)) 88 | when (io.imem.resp.valid) { 89 | assert(!reg_dmiss, "instruction arrived during data miss") 90 | if_inst_buffer := io.imem.resp.bits.data 91 | } 92 | 93 | io.imem.req.bits.fcn := M_XRD 94 | io.imem.req.bits.typ := MT_WU 95 | io.imem.req.bits.addr := pc_reg 96 | io.imem.req.valid := !reg_dmiss 97 | val inst = Mux(reg_dmiss, if_inst_buffer, io.imem.resp.bits.data) 98 | 99 | // Instruction misalign detection 100 | // In control path, instruction misalignment exception is always raised in the next cycle once the misaligned instruction reaches 101 | // execution stage, regardless whether the pipeline stalls or not 102 | io.dat.inst_misaligned := (br_target(1, 0).orR && io.ctl.pc_sel_no_xept === PC_BR) || 103 | (jmp_target(1, 0).orR && io.ctl.pc_sel_no_xept === PC_J) || 104 | (jump_reg_target(1, 0).orR && io.ctl.pc_sel_no_xept === PC_JR) 105 | tval_inst_ma := MuxCase(0.U, Array( 106 | (io.ctl.pc_sel_no_xept === PC_BR) -> br_target, 107 | (io.ctl.pc_sel_no_xept === PC_J) -> jmp_target, 108 | (io.ctl.pc_sel_no_xept === PC_JR) -> jump_reg_target 109 | )) 110 | 111 | // Decode 112 | val rs1_addr = inst(RS1_MSB, RS1_LSB) 113 | val rs2_addr = inst(RS2_MSB, RS2_LSB) 114 | val wb_addr = inst(RD_MSB, RD_LSB) 115 | 116 | val wb_data = Wire(UInt(conf.xprlen.W)) 117 | val wb_wen = io.ctl.rf_wen && !io.ctl.exception && !interrupt_edge 118 | 119 | // Register File 120 | val regfile = Mem(32, UInt(conf.xprlen.W)) 121 | 122 | when (wb_wen && (wb_addr =/= 0.U)) 123 | { 124 | regfile(wb_addr) := wb_data 125 | } 126 | 127 | //// DebugModule 128 | io.ddpath.rdata := regfile(io.ddpath.addr) 129 | when(io.ddpath.validreq){ 130 | regfile(io.ddpath.addr) := io.ddpath.wdata 131 | } 132 | /// 133 | 134 | val rs1_data = Mux((rs1_addr =/= 0.U), regfile(rs1_addr), 0.asUInt(conf.xprlen.W)) 135 | val rs2_data = Mux((rs2_addr =/= 0.U), regfile(rs2_addr), 0.asUInt(conf.xprlen.W)) 136 | 137 | 138 | // immediates 139 | val imm_i = inst(31, 20) 140 | val imm_s = Cat(inst(31, 25), inst(11,7)) 141 | val imm_b = Cat(inst(31), inst(7), inst(30,25), inst(11,8)) 142 | val imm_u = inst(31, 12) 143 | val imm_j = Cat(inst(31), inst(19,12), inst(20), inst(30,21)) 144 | val imm_z = Cat(Fill(27,0.U), inst(19,15)) 145 | 146 | // sign-extend immediates 147 | val imm_i_sext = Cat(Fill(20,imm_i(11)), imm_i) 148 | val imm_s_sext = Cat(Fill(20,imm_s(11)), imm_s) 149 | val imm_b_sext = Cat(Fill(19,imm_b(11)), imm_b, 0.U) 150 | val imm_u_sext = Cat(imm_u, Fill(12,0.U)) 151 | val imm_j_sext = Cat(Fill(11,imm_j(19)), imm_j, 0.U) 152 | 153 | 154 | val alu_op1 = MuxCase(0.U, Array( 155 | (io.ctl.op1_sel === OP1_RS1) -> rs1_data, 156 | (io.ctl.op1_sel === OP1_IMU) -> imm_u_sext, 157 | (io.ctl.op1_sel === OP1_IMZ) -> imm_z 158 | )).asUInt 159 | 160 | val alu_op2 = MuxCase(0.U, Array( 161 | (io.ctl.op2_sel === OP2_RS2) -> rs2_data, 162 | (io.ctl.op2_sel === OP2_PC) -> pc_reg, 163 | (io.ctl.op2_sel === OP2_IMI) -> imm_i_sext, 164 | (io.ctl.op2_sel === OP2_IMS) -> imm_s_sext 165 | )).asUInt 166 | 167 | 168 | 169 | // ALU 170 | val alu_out = Wire(UInt(conf.xprlen.W)) 171 | 172 | val alu_shamt = alu_op2(4,0).asUInt 173 | 174 | alu_out := MuxCase(0.U, Array( 175 | (io.ctl.alu_fun === ALU_ADD) -> (alu_op1 + alu_op2).asUInt, 176 | (io.ctl.alu_fun === ALU_SUB) -> (alu_op1 - alu_op2).asUInt, 177 | (io.ctl.alu_fun === ALU_AND) -> (alu_op1 & alu_op2).asUInt, 178 | (io.ctl.alu_fun === ALU_OR) -> (alu_op1 | alu_op2).asUInt, 179 | (io.ctl.alu_fun === ALU_XOR) -> (alu_op1 ^ alu_op2).asUInt, 180 | (io.ctl.alu_fun === ALU_SLT) -> (alu_op1.asSInt < alu_op2.asSInt).asUInt, 181 | (io.ctl.alu_fun === ALU_SLTU) -> (alu_op1 < alu_op2).asUInt, 182 | (io.ctl.alu_fun === ALU_SLL) -> ((alu_op1 << alu_shamt)(conf.xprlen-1, 0)).asUInt, 183 | (io.ctl.alu_fun === ALU_SRA) -> (alu_op1.asSInt >> alu_shamt).asUInt, 184 | (io.ctl.alu_fun === ALU_SRL) -> (alu_op1 >> alu_shamt).asUInt, 185 | (io.ctl.alu_fun === ALU_COPY1)-> alu_op1 186 | )) 187 | 188 | // Branch/Jump Target Calculation 189 | br_target := pc_reg + imm_b_sext 190 | jmp_target := pc_reg + imm_j_sext 191 | jump_reg_target := (rs1_data.asUInt + imm_i_sext.asUInt) & ~1.U(conf.xprlen.W) 192 | 193 | // Control Status Registers 194 | val csr = Module(new CSRFile(perfEventSets=CSREvents.events)) 195 | csr.io := DontCare 196 | csr.io.decode(0).inst := inst 197 | csr.io.rw.addr := inst(CSR_ADDR_MSB,CSR_ADDR_LSB) 198 | csr.io.rw.cmd := io.ctl.csr_cmd 199 | csr.io.rw.wdata := alu_out 200 | 201 | csr.io.retire := !(io.ctl.stall || io.ctl.exception) 202 | csr.io.exception := io.ctl.exception 203 | csr.io.pc := pc_reg 204 | exception_target := csr.io.evec 205 | 206 | csr.io.tval := MuxCase(0.U, Array( 207 | (io.ctl.exception_cause === Causes.illegal_instruction.U) -> inst, 208 | (io.ctl.exception_cause === Causes.misaligned_fetch.U) -> tval_inst_ma, 209 | (io.ctl.exception_cause === Causes.misaligned_store.U) -> tval_data_ma, 210 | (io.ctl.exception_cause === Causes.misaligned_load.U) -> tval_data_ma, 211 | )) 212 | 213 | // Interrupt rising edge detector (output trap signal for one cycle on rising edge) 214 | val reg_interrupt_edge = RegInit(false.B) 215 | when (!io.ctl.stall) { 216 | reg_interrupt_edge := csr.io.interrupt 217 | } 218 | interrupt_edge := csr.io.interrupt && !reg_interrupt_edge 219 | 220 | io.dat.csr_eret := csr.io.eret 221 | 222 | csr.io.interrupts := io.interrupt 223 | csr.io.hartid := io.hartid 224 | io.dat.csr_interrupt := interrupt_edge 225 | csr.io.cause := Mux(io.ctl.exception, io.ctl.exception_cause, csr.io.interrupt_cause) 226 | csr.io.ungated_clock := clock 227 | 228 | // Add your own uarch counters here! 229 | csr.io.counters.foreach(_.inc := false.B) 230 | 231 | // WB Mux 232 | wb_data := MuxCase(alu_out, Array( 233 | (io.ctl.wb_sel === WB_ALU) -> alu_out, 234 | (io.ctl.wb_sel === WB_MEM) -> io.dmem.resp.bits.data, 235 | (io.ctl.wb_sel === WB_PC4) -> pc_plus4, 236 | (io.ctl.wb_sel === WB_CSR) -> csr.io.rw.rdata 237 | )) 238 | 239 | 240 | // datapath to controlpath outputs 241 | io.dat.inst := inst 242 | io.dat.br_eq := (rs1_data === rs2_data) 243 | io.dat.br_lt := (rs1_data.asSInt < rs2_data.asSInt) 244 | io.dat.br_ltu := (rs1_data.asUInt < rs2_data.asUInt) 245 | 246 | 247 | // datapath to data memory outputs 248 | io.dmem.req.bits.addr := alu_out 249 | io.dmem.req.bits.data := rs2_data.asUInt 250 | 251 | io.dat.mem_address_low := alu_out(2, 0) 252 | tval_data_ma := alu_out 253 | 254 | // Printout 255 | // pass output through the spike-dasm binary (found in riscv-tools) to turn 256 | // the DASM(%x) into a disassembly string. 257 | printf("Cyc= %d [%d] pc=[%x] W[r%d=%x][%d] Op1=[r%d][%x] Op2=[r%d][%x] inst=[%x] %c%c%c DASM(%x)\n", 258 | csr.io.time(31,0), 259 | csr.io.retire, 260 | pc_reg, 261 | wb_addr, 262 | wb_data, 263 | wb_wen, 264 | rs1_addr, 265 | alu_op1, 266 | rs2_addr, 267 | alu_op2, 268 | inst, 269 | Mux(io.ctl.stall, Str("S"), Str(" ")), 270 | MuxLookup(io.ctl.pc_sel, Str("?"))(Seq( 271 | PC_BR -> Str("B"), 272 | PC_J -> Str("J"), 273 | PC_JR -> Str("R"), 274 | PC_EXC -> Str("E"), 275 | PC_4 -> Str(" "))), 276 | Mux(csr.io.exception, Str("X"), Str(" ")), 277 | inst) 278 | 279 | 280 | if (PRINT_COMMIT_LOG) 281 | { 282 | when (!io.ctl.stall) 283 | { 284 | // use "sed" to parse out "@@@" from the other printf code above. 285 | val rd = inst(RD_MSB,RD_LSB) 286 | when (io.ctl.rf_wen && rd =/= 0.U) 287 | { 288 | printf("@@@ 0x%x (0x%x) x%d 0x%x\n", pc_reg, inst, rd, Cat(Fill(32,wb_data(31)),wb_data)) 289 | } 290 | .otherwise 291 | { 292 | printf("@@@ 0x%x (0x%x)\n", pc_reg, inst) 293 | } 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_1stage/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.stage1 2 | import sodor.stage1.constants._ 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import scala.math._ 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends 11 | SodorProcConstants with 12 | ScalarOpConstants with 13 | sodor.common.constants.RISCVConstants with 14 | sodor.common.MemoryOpConstants 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_2stage/consts.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Constants 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | package sodor.stage2 9 | package constants 10 | { 11 | 12 | import chisel3._ 13 | import chisel3.util._ 14 | 15 | 16 | trait SodorProcConstants 17 | { 18 | 19 | //************************************ 20 | // Machine Parameters 21 | } 22 | 23 | trait ScalarOpConstants 24 | { 25 | //************************************ 26 | // Control Signals 27 | 28 | val Y = true.B 29 | val N = false.B 30 | 31 | // PC Select Signal 32 | val PC_4 = 0.asUInt(3.W) // PC + 4 33 | val PC_BR = 1.asUInt(3.W) // branch_target 34 | val PC_J = 2.asUInt(3.W) // jump_target 35 | val PC_JR = 3.asUInt(3.W) // jump_reg_target 36 | val PC_EXC = 4.asUInt(3.W) // exception 37 | 38 | // Branch Type 39 | val BR_N = 0.asUInt(4.W) // Next 40 | val BR_NE = 1.asUInt(4.W) // Branch on NotEqual 41 | val BR_EQ = 2.asUInt(4.W) // Branch on Equal 42 | val BR_GE = 3.asUInt(4.W) // Branch on Greater/Equal 43 | val BR_GEU = 4.asUInt(4.W) // Branch on Greater/Equal Unsigned 44 | val BR_LT = 5.asUInt(4.W) // Branch on Less Than 45 | val BR_LTU = 6.asUInt(4.W) // Branch on Less Than Unsigned 46 | val BR_J = 7.asUInt(4.W) // Jump 47 | val BR_JR = 8.asUInt(4.W) // Jump Register 48 | 49 | // RS1 Operand Select Signal 50 | val OP1_RS1 = 0.asUInt(2.W) // Register Source #1 51 | val OP1_IMU = 1.asUInt(2.W) // immediate, U-type 52 | val OP1_IMZ = 2.asUInt(2.W) // zero-extended immediate for CSRI instructions 53 | val OP1_X = 0.asUInt(2.W) 54 | 55 | // RS2 Operand Select Signal 56 | val OP2_RS2 = 0.asUInt(3.W) // Register Source #2 57 | val OP2_PC = 1.asUInt(3.W) // PC 58 | val OP2_IMI = 2.asUInt(3.W) // immediate, I-type 59 | val OP2_IMS = 3.asUInt(3.W) // immediate, S-type 60 | val OP2_X = 0.asUInt(3.W) 61 | 62 | 63 | // Register File Write Enable Signal 64 | val REN_0 = false.B 65 | val REN_1 = true.B 66 | val REN_X = false.B 67 | 68 | // ALU Operation Signal 69 | val ALU_ADD = 1.asUInt(4.W) 70 | val ALU_SUB = 2.asUInt(4.W) 71 | val ALU_SLL = 3.asUInt(4.W) 72 | val ALU_SRL = 4.asUInt(4.W) 73 | val ALU_SRA = 5.asUInt(4.W) 74 | val ALU_AND = 6.asUInt(4.W) 75 | val ALU_OR = 7.asUInt(4.W) 76 | val ALU_XOR = 8.asUInt(4.W) 77 | val ALU_SLT = 9.asUInt(4.W) 78 | val ALU_SLTU= 10.asUInt(4.W) 79 | val ALU_COPY1 = 11.asUInt(4.W) 80 | val ALU_X = 0.asUInt(4.W) 81 | 82 | // Writeback Address Select Signal 83 | val WA_RD = true.B // write to register rd 84 | val WA_RA = false.B // write to register x1 (return address) 85 | val WA_X = true.B 86 | 87 | // Writeback Select Signal 88 | val WB_ALU = 0.asUInt(2.W) 89 | val WB_MEM = 1.asUInt(2.W) 90 | val WB_PC4 = 2.asUInt(2.W) 91 | val WB_CSR = 3.asUInt(2.W) 92 | val WB_X = 0.asUInt(2.W) 93 | 94 | // Memory Function Type (Read,Write,Fence) Signal 95 | val MWR_R = 0.asUInt(2.W) 96 | val MWR_W = 1.asUInt(2.W) 97 | val MWR_F = 2.asUInt(2.W) 98 | val MWR_X = 0.asUInt(2.W) 99 | 100 | // Memory Enable Signal 101 | val MEN_0 = false.B 102 | val MEN_1 = true.B 103 | val MEN_X = false.B 104 | 105 | // Memory Mask Type Signal 106 | val MSK_B = 0.asUInt(3.W) 107 | val MSK_BU = 1.asUInt(3.W) 108 | val MSK_H = 2.asUInt(3.W) 109 | val MSK_HU = 3.asUInt(3.W) 110 | val MSK_W = 4.asUInt(3.W) 111 | val MSK_X = 4.asUInt(3.W) 112 | } 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_2stage/core.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 Jul 30 7 | // 8 | // Describes a simple RISCV 2-stage processor 9 | // - Statically predict pc+4, kill instruction fetch 10 | // - Single-cycle memory 11 | // - No div/mul/rem 12 | // - No FPU 13 | // - No double-word nor sub-word memory access support 14 | 15 | package sodor.stage2 16 | 17 | import chisel3._ 18 | import chisel3.util._ 19 | 20 | import sodor.common._ 21 | 22 | import org.chipsalliance.cde.config.Parameters 23 | import freechips.rocketchip.rocket.CoreInterrupts 24 | 25 | class CoreIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle 26 | { 27 | val imem = new MemPortIo(conf.xprlen) 28 | val dmem = new MemPortIo(conf.xprlen) 29 | val ddpath = Flipped(new DebugDPath()) 30 | val dcpath = Flipped(new DebugCPath()) 31 | val interrupt = Input(new CoreInterrupts(false)) 32 | val hartid = Input(UInt()) 33 | val reset_vector = Input(UInt()) 34 | } 35 | 36 | class Core(implicit val p: Parameters, val conf: SodorCoreParams) extends AbstractCore 37 | { 38 | val io = IO(new CoreIo()) 39 | val c = Module(new CtlPath()) 40 | val d = Module(new DatPath()) 41 | 42 | c.io.ctl <> d.io.ctl 43 | c.io.dat <> d.io.dat 44 | 45 | io.imem <> c.io.imem 46 | io.imem <> d.io.imem 47 | 48 | io.dmem <> c.io.dmem 49 | io.dmem <> d.io.dmem 50 | io.dmem.req.valid := c.io.dmem.req.valid 51 | io.dmem.req.bits.typ := c.io.dmem.req.bits.typ 52 | io.dmem.req.bits.fcn := c.io.dmem.req.bits.fcn 53 | 54 | d.io.ddpath <> io.ddpath 55 | c.io.dcpath <> io.dcpath 56 | 57 | d.io.interrupt := io.interrupt 58 | d.io.hartid := io.hartid 59 | d.io.reset_vector := io.reset_vector 60 | 61 | val mem_ports = List(io.dmem, io.imem) 62 | val interrupt = io.interrupt 63 | val hartid = io.hartid 64 | val reset_vector = io.reset_vector 65 | } 66 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_2stage/cpath.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Control Path 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | 7 | package sodor.stage2 8 | 9 | import chisel3._ 10 | import chisel3.util._ 11 | 12 | import freechips.rocketchip.rocket.{CSR, Causes} 13 | 14 | import sodor.common._ 15 | import sodor.common.Instructions._ 16 | import sodor.stage2.Constants._ 17 | 18 | class CtlToDatIo extends Bundle() 19 | { 20 | val stall = Output(Bool()) 21 | val if_kill = Output(Bool()) 22 | val pc_sel = Output(UInt(3.W)) 23 | val op1_sel = Output(UInt(2.W)) 24 | val op2_sel = Output(UInt(3.W)) 25 | val alu_fun = Output(UInt(5.W)) 26 | val wb_sel = Output(UInt(2.W)) 27 | val rf_wen = Output(Bool()) 28 | val csr_cmd = Output(UInt(CSR.SZ.W)) 29 | val mem_val = Output(Bool()) 30 | val mem_fcn = Output(UInt(2.W)) 31 | val mem_typ = Output(UInt(3.W)) 32 | val exception = Output(Bool()) 33 | val exception_cause = Output(UInt(32.W)) 34 | val pc_sel_no_xept = Output(UInt(PC_4.getWidth.W)) // Use only for instuction misalignment detection 35 | } 36 | 37 | class CpathIo(implicit val conf: SodorCoreParams) extends Bundle() 38 | { 39 | val dcpath = Flipped(new DebugCPath()) 40 | val imem = new MemPortIo(conf.xprlen) 41 | val dmem = new MemPortIo(conf.xprlen) 42 | val dat = Flipped(new DatToCtlIo()) 43 | val ctl = new CtlToDatIo() 44 | } 45 | 46 | 47 | class CtlPath(implicit val conf: SodorCoreParams) extends Module 48 | { 49 | val io = IO(new CpathIo()) 50 | io := DontCare 51 | 52 | val csignals = 53 | ListLookup(io.dat.inst, 54 | List(N, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X ,MT_X, CSR.N), 55 | Array( /* val | BR | op1 | op2 | ALU | wb | rf | mem | mem | mask | csr */ 56 | /* inst | type | sel | sel | fcn | sel | wen | en | wr | type | cmd */ 57 | LW -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_W, CSR.N), 58 | LB -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_B, CSR.N), 59 | LBU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_BU, CSR.N), 60 | LH -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_H, CSR.N), 61 | LHU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_MEM, REN_1, MEN_1, M_XRD, MT_HU, CSR.N), 62 | SW -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_W, CSR.N), 63 | SB -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_B, CSR.N), 64 | SH -> List(Y, BR_N , OP1_RS1, OP2_IMS , ALU_ADD , WB_X , REN_0, MEN_1, M_XWR, MT_H, CSR.N), 65 | 66 | AUIPC -> List(Y, BR_N , OP1_IMU, OP2_PC , ALU_ADD ,WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 67 | LUI -> List(Y, BR_N , OP1_IMU, OP2_X , ALU_COPY1,WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 68 | 69 | ADDI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_ADD , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 70 | ANDI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_AND , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 71 | ORI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_OR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 72 | XORI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_XOR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 73 | SLTI -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLT , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 74 | SLTIU -> List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLTU, WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 75 | SLLI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SLL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 76 | SRAI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SRA , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 77 | SRLI_RV32->List(Y, BR_N , OP1_RS1, OP2_IMI , ALU_SRL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 78 | 79 | SLL -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 80 | ADD -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_ADD , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 81 | SUB -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SUB , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 82 | SLT -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLT , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 83 | SLTU -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SLTU, WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 84 | AND -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_AND , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 85 | OR -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_OR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 86 | XOR -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_XOR , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 87 | SRA -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SRA , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 88 | SRL -> List(Y, BR_N , OP1_RS1, OP2_RS2 , ALU_SRL , WB_ALU, REN_1, MEN_0, M_X , MT_X, CSR.N), 89 | 90 | JAL -> List(Y, BR_J , OP1_X , OP2_X , ALU_X , WB_PC4, REN_1, MEN_0, M_X , MT_X, CSR.N), 91 | JALR -> List(Y, BR_JR , OP1_RS1, OP2_IMI , ALU_X , WB_PC4, REN_1, MEN_0, M_X , MT_X, CSR.N), 92 | BEQ -> List(Y, BR_EQ , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 93 | BNE -> List(Y, BR_NE , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 94 | BGE -> List(Y, BR_GE , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 95 | BGEU -> List(Y, BR_GEU, OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 96 | BLT -> List(Y, BR_LT , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 97 | BLTU -> List(Y, BR_LTU, OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 98 | 99 | CSRRWI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.W), 100 | CSRRSI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.S), 101 | CSRRW -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.W), 102 | CSRRS -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.S), 103 | CSRRC -> List(Y, BR_N , OP1_RS1, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.C), 104 | CSRRCI -> List(Y, BR_N , OP1_IMZ, OP2_X , ALU_COPY1,WB_CSR, REN_1, MEN_0, M_X , MT_X, CSR.C), 105 | 106 | ECALL -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 107 | MRET -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 108 | DRET -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 109 | EBREAK -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.I), 110 | WFI -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), // implemented as a NOP 111 | 112 | FENCE_I-> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N), 113 | FENCE -> List(Y, BR_N , OP1_X , OP2_X , ALU_X , WB_X , REN_0, MEN_0, M_X , MT_X, CSR.N) 114 | // we are already sequentially consistent, so no need to honor the fence instruction 115 | 116 | )) 117 | 118 | // Put these control signals in variables 119 | val (cs_val_inst: Bool) :: cs_br_type :: cs_op1_sel :: cs_op2_sel :: cs_alu_fun :: cs_wb_sel :: cs0 = csignals 120 | val (cs_rf_wen: Bool) :: (cs_mem_en: Bool) :: cs_mem_fcn :: cs_msk_sel :: cs_csr_cmd :: Nil = cs0 121 | 122 | 123 | // Branch Logic 124 | val ctrl_pc_sel_no_xept = Mux(io.dat.csr_interrupt , PC_EXC, 125 | Mux(cs_br_type === BR_N , PC_4, 126 | Mux(cs_br_type === BR_NE , Mux(!io.dat.br_eq, PC_BR, PC_4), 127 | Mux(cs_br_type === BR_EQ , Mux( io.dat.br_eq, PC_BR, PC_4), 128 | Mux(cs_br_type === BR_GE , Mux(!io.dat.br_lt, PC_BR, PC_4), 129 | Mux(cs_br_type === BR_GEU, Mux(!io.dat.br_ltu, PC_BR, PC_4), 130 | Mux(cs_br_type === BR_LT , Mux( io.dat.br_lt, PC_BR, PC_4), 131 | Mux(cs_br_type === BR_LTU, Mux( io.dat.br_ltu, PC_BR, PC_4), 132 | Mux(cs_br_type === BR_J , PC_J, 133 | Mux(cs_br_type === BR_JR , PC_JR, 134 | PC_4)))))))))) 135 | val ctrl_pc_sel = Mux(io.ctl.exception || io.dat.csr_eret, PC_EXC, ctrl_pc_sel_no_xept) 136 | 137 | // stall entire pipeline on I$ or D$ miss 138 | val stall = !io.dat.if_valid_resp || !((cs_mem_en && (io.dmem.resp.valid || io.dat.data_misaligned)) || !cs_mem_en) 139 | 140 | val ifkill = !(ctrl_pc_sel === PC_4) 141 | 142 | io.ctl.stall := stall 143 | io.ctl.if_kill := ifkill 144 | io.ctl.pc_sel := ctrl_pc_sel 145 | io.ctl.op1_sel := cs_op1_sel 146 | io.ctl.op2_sel := cs_op2_sel 147 | io.ctl.alu_fun := cs_alu_fun 148 | io.ctl.wb_sel := cs_wb_sel 149 | io.ctl.rf_wen := Mux(stall, false.B, cs_rf_wen) 150 | 151 | 152 | // convert CSR instructions with raddr1 == 0 to read-only CSR commands 153 | val rs1_addr = io.dat.inst(RS1_MSB, RS1_LSB) 154 | val csr_ren = (cs_csr_cmd === CSR.S || cs_csr_cmd === CSR.C) && rs1_addr === 0.U 155 | val csr_cmd = Mux(csr_ren, CSR.R, cs_csr_cmd) 156 | 157 | io.ctl.csr_cmd := Mux(stall, CSR.N, csr_cmd) 158 | 159 | io.dmem.req.valid := cs_mem_en && !io.dat.data_misaligned 160 | io.dmem.req.bits.fcn := cs_mem_fcn 161 | io.dmem.req.bits.typ := cs_msk_sel 162 | 163 | io.ctl.mem_val := cs_mem_en 164 | io.ctl.mem_fcn := cs_mem_fcn 165 | io.ctl.mem_typ := cs_msk_sel 166 | 167 | // Exception Handling --------------------- 168 | io.ctl.pc_sel_no_xept := ctrl_pc_sel_no_xept 169 | val illegal = (!cs_val_inst && io.imem.resp.valid) 170 | // Exception priority matters! 171 | io.ctl.exception := (illegal || io.dat.inst_misaligned || io.dat.data_misaligned) && !io.dat.csr_eret 172 | io.ctl.exception_cause := Mux(illegal, Causes.illegal_instruction.U, 173 | Mux(io.dat.inst_misaligned, Causes.misaligned_fetch.U, 174 | Mux(io.dat.mem_store, Causes.misaligned_store.U, 175 | Causes.misaligned_load.U 176 | ))) 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_2stage/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.stage2 2 | import sodor.stage2.constants._ 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import scala.math._ 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends 11 | SodorProcConstants with 12 | ScalarOpConstants with 13 | sodor.common.constants.RISCVConstants with 14 | sodor.common.MemoryOpConstants 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/alu.scala: -------------------------------------------------------------------------------- 1 | // This is borrowed from rocket, and modified to be hardcoded to 32b. 2 | // I will leave it as an excercise to the reader to make a parameterizable ALU 3 | // that doesn't generate extra hardware for 32b. I also didn't carefully 4 | // consider the function encodings. - Chris 5 | package sodor.stage3 6 | 7 | import chisel3._ 8 | import chisel3.util._ 9 | 10 | import sodor.common._ 11 | import sodor.stage3.Constants._ 12 | 13 | object ALU 14 | { 15 | // TODO is this the optimal encoding? 16 | val SZ_ALU_FN = 4 17 | val ALU_X = 0.U // TODO use a more optimal decode table, which uses "???" format 18 | val ALU_ADD = 0.U 19 | val ALU_SLL = 1.U 20 | val ALU_XOR = 4.U 21 | val ALU_OR = 6.U 22 | val ALU_AND = 7.U 23 | val ALU_SRL = 5.U 24 | val ALU_SUB = 10.U 25 | val ALU_SRA = 11.U 26 | val ALU_SLT = 12.U 27 | val ALU_SLTU = 14.U 28 | val ALU_COPY1= 8.U 29 | 30 | def isSub(cmd: UInt) = cmd(3) 31 | def isSLTU(cmd: UInt) = cmd(1) 32 | } 33 | import ALU._ 34 | 35 | class ALUIO(implicit val conf: SodorCoreParams) extends Bundle { 36 | val fn = Input(UInt(SZ_ALU_FN.W)) 37 | val in2 = Input(UInt(conf.xprlen.W)) 38 | val in1 = Input(UInt(conf.xprlen.W)) 39 | val out = Output(UInt(conf.xprlen.W)) 40 | val adder_out = Output(UInt(conf.xprlen.W)) 41 | } 42 | 43 | class ALU(implicit val conf: SodorCoreParams) extends Module 44 | { 45 | val io = IO(new ALUIO) 46 | 47 | val msb = conf.xprlen-1 48 | 49 | // ADD, SUB 50 | val sum = io.in1 + Mux(isSub(io.fn), -io.in2, io.in2) 51 | 52 | // SLT, SLTU 53 | val less = Mux(io.in1(msb) === io.in2(msb), sum(msb), 54 | Mux(isSLTU(io.fn), io.in2(msb), io.in1(msb))) 55 | 56 | require(conf.xprlen == 32) 57 | // SLL, SRL, SRA 58 | val shamt = io.in2(4,0).asUInt 59 | val shin_r = io.in1(31,0) 60 | val shin = Mux(io.fn === ALU_SRL || io.fn === ALU_SRA, shin_r, Reverse(shin_r)) 61 | val shout_r = (Cat(isSub(io.fn) & shin(msb), shin).asSInt >> shamt)(msb,0) 62 | val shout_l = Reverse(shout_r) 63 | 64 | val bitwise_logic = 65 | Mux(io.fn === ALU_AND, io.in1 & io.in2, 66 | Mux(io.fn === ALU_OR, io.in1 | io.in2, 67 | Mux(io.fn === ALU_XOR, io.in1 ^ io.in2, 68 | io.in1))) // ALU_COPY1 69 | 70 | val out_xpr_length = 71 | Mux(io.fn === ALU_ADD || io.fn === ALU_SUB, sum, 72 | Mux(io.fn === ALU_SLT || io.fn === ALU_SLTU, less, 73 | Mux(io.fn === ALU_SRL || io.fn === ALU_SRA, shout_r, 74 | Mux(io.fn === ALU_SLL, shout_l, 75 | bitwise_logic)))) 76 | 77 | io.out := out_xpr_length(31,0).asUInt 78 | io.adder_out := sum 79 | } 80 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/arbiter.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // Arbiter for Princeton Architectures 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Arbitrates instruction and data accesses to a single port memory. 6 | 7 | package sodor.stage3 8 | 9 | import chisel3._ 10 | import chisel3.util._ 11 | 12 | import sodor.common._ 13 | 14 | // arbitrates memory access 15 | class SodorMemArbiter(implicit val conf: SodorCoreParams) extends Module 16 | { 17 | val io = IO(new Bundle 18 | { 19 | // TODO I need to come up with better names... this is too confusing 20 | // from the point of view of the other modules 21 | val imem = Flipped(new MemPortIo(conf.xprlen)) // instruction fetch 22 | val dmem = Flipped(new MemPortIo(conf.xprlen)) // load/store 23 | val mem = new MemPortIo(conf.xprlen) // the single-ported memory 24 | }) 25 | 26 | val d_resp = RegInit(false.B) 27 | 28 | // hook up requests 29 | // d_resp ensures that data req gets access to bus only 30 | // for one cycle 31 | // alternate between data and instr to avoid starvation 32 | when (d_resp) 33 | { 34 | // Last request is a data request - do not allow data request this cycle 35 | io.dmem.req.ready := false.B 36 | io.imem.req.ready := io.mem.req.ready 37 | 38 | // We only clear the d_resp flag when the next request fired since it also indicates the allowed type of the next request 39 | when (io.mem.req.fire) 40 | { 41 | d_resp := false.B 42 | } 43 | } 44 | .otherwise 45 | { 46 | // Last request is not a data request - if this cycle has a new data request, dispatch it 47 | io.dmem.req.ready := io.mem.req.ready 48 | io.imem.req.ready := io.mem.req.ready && !io.dmem.req.valid 49 | 50 | when (io.dmem.req.fire) 51 | { 52 | d_resp := true.B 53 | } 54 | } 55 | // SWITCH BET DATA AND INST REQ FOR SINGLE PORT 56 | when (io.dmem.req.fire) 57 | { 58 | io.mem.req.valid := io.dmem.req.valid 59 | io.mem.req.bits.addr := io.dmem.req.bits.addr 60 | io.mem.req.bits.fcn := io.dmem.req.bits.fcn 61 | io.mem.req.bits.typ := io.dmem.req.bits.typ 62 | } 63 | .otherwise 64 | { 65 | io.mem.req.valid := io.imem.req.valid 66 | io.mem.req.bits.addr := io.imem.req.bits.addr 67 | io.mem.req.bits.fcn := io.imem.req.bits.fcn 68 | io.mem.req.bits.typ := io.imem.req.bits.typ 69 | } 70 | // Control valid signal 71 | when (d_resp) 72 | { 73 | io.imem.resp.valid := false.B 74 | io.dmem.resp.valid := io.mem.resp.valid 75 | } 76 | .otherwise { 77 | io.imem.resp.valid := io.mem.resp.valid 78 | io.dmem.resp.valid := false.B 79 | } 80 | 81 | // No need to switch data since instruction port doesn't write 82 | io.mem.req.bits.data := io.dmem.req.bits.data 83 | 84 | // Simply connect response data to both ports since we only have one inflight request 85 | // the validity of the data is controlled above 86 | io.imem.resp.bits.data := io.mem.resp.bits.data 87 | io.dmem.resp.bits.data := io.mem.resp.bits.data 88 | } 89 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/consts.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Constants 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | package sodor.stage3 9 | package constants 10 | { 11 | 12 | import chisel3._ 13 | import chisel3.util._ 14 | 15 | 16 | trait SodorProcConstants 17 | { 18 | //************************************ 19 | // Machine Parameters 20 | 21 | val NUM_MEMORY_PORTS = 2 22 | 23 | // if the front-end ONLY predicts PC+4, this simplifies quite a bit of logic. 24 | // First, the PC select mux never needs to compute ExePC + 4 on a branch 25 | // redirect (since PC+4 is always predicted). 26 | // Second, JAL can write-back to rd the ExePC, since it will already be PC+4 27 | // relative to the JAL. 28 | val PREDICT_PCP4 = true; require(PREDICT_PCP4==true) // no BTB, etc, added yet 29 | 30 | //************************************ 31 | // Debugging 32 | val PRINT_COMMIT_LOG = false 33 | } 34 | 35 | trait ScalarOpConstants 36 | { 37 | //************************************ 38 | // Control Signals 39 | 40 | val Y = true.B 41 | val N = false.B 42 | 43 | // PC Select Signal 44 | val PC_4 = 0.asUInt(3.W) // PC + 4 45 | val PC_BR = 1.asUInt(3.W) // branch_target 46 | val PC_J = 2.asUInt(3.W) // jump_target 47 | val PC_JR = 3.asUInt(3.W) // jump_reg_target 48 | val PC_EXC = 4.asUInt(3.W) // exception 49 | 50 | // Branch Type 51 | val BR_N = 0.asUInt(4.W) // Next 52 | val BR_NE = 1.asUInt(4.W) // Branch on NotEqual 53 | val BR_EQ = 2.asUInt(4.W) // Branch on Equal 54 | val BR_GE = 3.asUInt(4.W) // Branch on Greater/Equal 55 | val BR_GEU = 4.asUInt(4.W) // Branch on Greater/Equal Unsigned 56 | val BR_LT = 5.asUInt(4.W) // Branch on Less Than 57 | val BR_LTU = 6.asUInt(4.W) // Branch on Less Than Unsigned 58 | val BR_J = 7.asUInt(4.W) // Jump 59 | val BR_JR = 8.asUInt(4.W) // Jump Register 60 | 61 | // RS1 Operand Select Signal 62 | val OP1_RS1 = 0.asUInt(2.W) // Register Source #1 63 | val OP1_IMU = 1.asUInt(2.W) // immediate, U-type 64 | val OP1_IMZ = 2.asUInt(2.W) // zero-extended 5-bit immediate; used by CSRI instructions 65 | val OP1_X = 0.asUInt(2.W) 66 | 67 | // RS2 Operand Select Signal 68 | val OP2_RS2 = 0.asUInt(2.W) // Register Source #2 69 | val OP2_IMI = 1.asUInt(2.W) // immediate, I-type 70 | val OP2_IMS = 2.asUInt(2.W) // immediate, S-type 71 | val OP2_PC = 3.asUInt(2.W) // PC 72 | val OP2_X = 0.asUInt(2.W) 73 | 74 | // Register File Write Enable Signal 75 | val REN_0 = false.B 76 | val REN_1 = true.B 77 | val REN_X = false.B 78 | 79 | // Writeback Select Signal 80 | val WB_ALU = 0.asUInt(2.W) 81 | val WB_MEM = 1.asUInt(2.W) 82 | val WB_PC4 = 2.asUInt(2.W) 83 | val WB_CSR = 3.asUInt(2.W) 84 | val WB_X = 0.asUInt(2.W) 85 | 86 | // Memory Function Type (Read,Write,Fence) Signal 87 | val MWR_R = 0.asUInt(2.W) 88 | val MWR_W = 1.asUInt(2.W) 89 | val MWR_F = 2.asUInt(2.W) 90 | val MWR_X = 0.asUInt(2.W) 91 | 92 | // Memory Enable Signal 93 | val MEN_0 = false.B 94 | val MEN_1 = true.B 95 | val MEN_X = false.B 96 | 97 | // Memory Mask Type Signal 98 | val MSK_B = 0.asUInt(3.W) 99 | val MSK_BU = 1.asUInt(3.W) 100 | val MSK_H = 2.asUInt(3.W) 101 | val MSK_HU = 3.asUInt(3.W) 102 | val MSK_W = 4.asUInt(3.W) 103 | val MSK_X = 4.asUInt(3.W) 104 | 105 | // Cache Flushes & Sync Primitives 106 | val M_N = 0.asUInt(3.W) 107 | val M_SI = 1.asUInt(3.W) // synch instruction stream 108 | val M_SD = 2.asUInt(3.W) // synch data stream 109 | val M_FA = 3.asUInt(3.W) // flush all caches 110 | val M_FD = 4.asUInt(3.W) // flush data cache 111 | 112 | // Memory Functions (read, write, fence) 113 | val MT_READ = 0.asUInt(2.W) 114 | val MT_WRITE = 1.asUInt(2.W) 115 | val MT_FENCE = 2.asUInt(2.W) 116 | 117 | 118 | } 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/core.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor 3 | //-------------------------------------------------------------------------- 4 | 5 | package sodor.stage3 6 | 7 | import chisel3._ 8 | import chisel3.util._ 9 | 10 | import sodor.common._ 11 | 12 | import org.chipsalliance.cde.config.Parameters 13 | import freechips.rocketchip.rocket.CoreInterrupts 14 | 15 | class CoreIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle 16 | { 17 | val ddpath = Flipped(new DebugDPath()) 18 | val dcpath = Flipped(new DebugCPath()) 19 | val imem = new MemPortIo(conf.xprlen) 20 | val dmem = new MemPortIo(conf.xprlen) 21 | val interrupt = Input(new CoreInterrupts(false)) 22 | val hartid = Input(UInt()) 23 | val reset_vector = Input(UInt()) 24 | } 25 | 26 | class Core(implicit val p: Parameters, val conf: SodorCoreParams) extends AbstractCore 27 | { 28 | val io = IO(new CoreIo()) 29 | 30 | val frontend = Module(new FrontEnd()) 31 | val cpath = Module(new CtlPath()) 32 | val dpath = Module(new DatPath()) 33 | 34 | frontend.io.reset_vector := io.reset_vector 35 | frontend.io.imem <> io.imem 36 | frontend.io.cpu <> cpath.io.imem 37 | frontend.io.cpu <> dpath.io.imem 38 | frontend.io.cpu.req.valid := cpath.io.imem.req.valid 39 | frontend.io.cpu.exe_kill := cpath.io.imem.exe_kill 40 | 41 | cpath.io.ctl <> dpath.io.ctl 42 | cpath.io.dat <> dpath.io.dat 43 | 44 | cpath.io.dmem <> io.dmem 45 | dpath.io.dmem <> io.dmem 46 | 47 | dpath.io.ddpath <> io.ddpath 48 | cpath.io.dcpath <> io.dcpath 49 | 50 | dpath.io.interrupt := io.interrupt 51 | dpath.io.hartid := io.hartid 52 | 53 | val mem_ports = List(io.dmem, io.imem) 54 | val interrupt = io.interrupt 55 | val hartid = io.hartid 56 | val reset_vector = io.reset_vector 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/frontend.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Front-end 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2013 Jn 29 7 | // 8 | // Handles the fetching of instructions. 9 | // 10 | // The front-end will go into cruise-control and fetch whichever instrutions it 11 | // feels like (probably just PC+4 for this simple pipeline), and it's the job 12 | // of the datapath to assert "valid" on the "io.req" bundle when it wants to 13 | // redirect the fetch PC. Otherwise, io.req.valid is disasserted. 14 | 15 | // There are a number of games we can play with the front-end. 16 | // - It can fetch doublewords, thus only using the memory port on every 17 | // other cycle. 18 | // - It can have a fetch buffer (combined with dw fetch) to hide memory port 19 | // hazards. 20 | // - It can use a stall cache (Krste Asanovic paper), which holds a cache 21 | // of previously stalled instructions. 22 | // 23 | // None of the above are implemented - I leave them as an excercise to the 24 | // reader for now... 25 | 26 | 27 | package sodor.stage3 28 | 29 | import chisel3._ 30 | import chisel3.util._ 31 | 32 | 33 | import sodor.stage3.Constants._ 34 | import sodor.common._ 35 | 36 | 37 | class FrontEndIO(implicit val conf: SodorCoreParams) extends Bundle 38 | { 39 | val cpu = new FrontEndCpuIO 40 | val imem = new MemPortIo(conf.xprlen) 41 | 42 | val reset_vector = Input(UInt()) 43 | 44 | } 45 | 46 | 47 | class FrontEndReq(xprlen: Int) extends Bundle 48 | { 49 | val pc = UInt(xprlen.W) 50 | 51 | } 52 | 53 | 54 | class FrontEndResp(xprlen: Int) extends Bundle 55 | { 56 | val pc = UInt(xprlen.W) 57 | val inst = UInt(xprlen.W) // only support 32b insts for now 58 | 59 | } 60 | 61 | class FrontEndDebug(xprlen: Int) extends Bundle 62 | { 63 | val if_pc = Output(UInt(xprlen.W)) 64 | val if_inst = Output(UInt(xprlen.W)) 65 | } 66 | 67 | class FrontEndCpuIO(implicit val conf: SodorCoreParams) extends Bundle 68 | { 69 | val req = Flipped(new ValidIO(new FrontEndReq(conf.xprlen))) 70 | val resp = new DecoupledIO(new FrontEndResp(conf.xprlen)) 71 | 72 | val debug = new FrontEndDebug(conf.xprlen) 73 | 74 | // Inst miss 75 | val imiss = Output(Bool()) 76 | // Flush the entire pipeline upon exception, including exe stage 77 | val exe_kill = Input(Bool()) 78 | 79 | } 80 | 81 | 82 | class FrontEnd(implicit val conf: SodorCoreParams) extends Module 83 | { 84 | val io = IO(new FrontEndIO) 85 | io := DontCare 86 | 87 | //********************************** 88 | // Pipeline State Registers 89 | val if_reg_pc = RegInit(io.reset_vector - 4.U) 90 | 91 | val exe_reg_valid = RegInit(false.B) 92 | val exe_reg_pc = Reg(UInt(conf.xprlen.W)) 93 | val exe_reg_inst = Reg(UInt(conf.xprlen.W)) 94 | 95 | //********************************** 96 | // Next PC Stage (if we can call it that) 97 | val if_pc_next = Wire(UInt(conf.xprlen.W)) 98 | val if_val_next = Wire(Bool()) // for now, always true. But instruction 99 | // buffers, etc., could make that not the case. 100 | 101 | val if_pc_plus4 = (if_reg_pc + 4.asUInt(conf.xprlen.W)) 102 | 103 | // Redirect handling 104 | val if_redirected = RegInit(false.B) 105 | val if_redirected_pc = Reg(UInt(conf.xprlen.W)) 106 | 107 | // Instruction buffer 108 | val if_buffer_in = Wire(new DecoupledIO(new MemResp(conf.xprlen))) 109 | if_buffer_in.valid := io.imem.resp.valid 110 | if_buffer_in.bits := io.imem.resp.bits 111 | if_val_next := io.cpu.resp.ready || (if_buffer_in.ready && !io.imem.resp.valid) // If the incoming inst goes to buffer, don't send the next req 112 | assert(if_buffer_in.ready || !if_buffer_in.valid, "Inst buffer overflow") 113 | val if_buffer_out = Queue(if_buffer_in, entries = 1, pipe = false, flow = true) 114 | 115 | // stall IF/EXE if backend not ready 116 | if_pc_next := if_pc_plus4 117 | when (io.cpu.req.valid) 118 | { 119 | // datapath is redirecting the PC stream (misspeculation) 120 | if_redirected := true.B 121 | if_redirected_pc := io.cpu.req.bits.pc 122 | } 123 | when (if_redirected) 124 | { 125 | if_pc_next := if_redirected_pc 126 | } 127 | 128 | // Go to next PC if both CPU and imem are ready, and the memory response for the current PC already presents 129 | val if_reg_pc_responded = RegInit(false.B) 130 | val if_pc_responsed = if_reg_pc_responded || io.imem.resp.valid 131 | when (io.cpu.resp.ready && io.imem.req.ready && if_pc_responsed) 132 | { 133 | if_reg_pc_responded := false.B 134 | if_reg_pc := if_pc_next 135 | when (!io.cpu.req.valid) 136 | { 137 | if_redirected := false.B 138 | } 139 | } .elsewhen (io.imem.resp.valid) 140 | { 141 | if_reg_pc_responded := true.B 142 | } 143 | 144 | // set up outputs to the instruction memory 145 | io.imem.req.bits.addr := if_pc_next 146 | io.imem.req.valid := if_val_next && !io.cpu.req.valid 147 | io.imem.req.bits.fcn := M_XRD 148 | io.imem.req.bits.typ := MT_WU 149 | 150 | //********************************** 151 | // Inst Fetch/Return Stage 152 | if_buffer_out.ready := io.cpu.resp.ready 153 | when (io.cpu.exe_kill) 154 | { 155 | exe_reg_valid := false.B 156 | } 157 | .elsewhen (io.cpu.resp.ready) 158 | { 159 | exe_reg_valid := if_buffer_out.valid && !io.cpu.req.valid && !if_redirected 160 | exe_reg_pc := if_reg_pc 161 | exe_reg_inst := if_buffer_out.bits.data 162 | } 163 | 164 | //********************************** 165 | // Execute Stage 166 | // (pass the instruction to the backend) 167 | io.cpu.resp.valid := exe_reg_valid 168 | io.cpu.resp.bits.inst := exe_reg_inst 169 | io.cpu.resp.bits.pc := exe_reg_pc 170 | 171 | //********************************** 172 | // only used for debugging 173 | io.cpu.debug.if_pc := if_reg_pc 174 | io.cpu.debug.if_inst := io.imem.resp.bits.data 175 | } 176 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_3stage/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.stage3 2 | import sodor.stage3.constants._ 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import scala.math._ 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends 11 | SodorProcConstants with 12 | ScalarOpConstants with 13 | sodor.common.constants.RISCVConstants with 14 | sodor.common.MemoryOpConstants 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_5stage/consts.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Constants 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 Feb 1 7 | 8 | package sodor.stage5 9 | package constants 10 | { 11 | 12 | import chisel3._ 13 | import chisel3.util._ 14 | 15 | 16 | trait SodorProcConstants 17 | { 18 | 19 | //************************************ 20 | // Machine Parameters 21 | val USE_FULL_BYPASSING = true // turn on full bypassing (only stalls 22 | // on load-use). Otherwise rely 23 | // entirely on interlocking to handle 24 | // pipeline hazards. 25 | } 26 | 27 | trait ScalarOpConstants 28 | { 29 | 30 | //************************************ 31 | // Control Signals 32 | val Y = true.B 33 | val N = false.B 34 | 35 | // PC Select Signal 36 | val PC_4 = 0.asUInt(2.W) // PC + 4 37 | val PC_BRJMP = 1.asUInt(2.W) // brjmp_target 38 | val PC_JALR = 2.asUInt(2.W) // jump_reg_target 39 | val PC_EXC = 3.asUInt(2.W) // exception 40 | 41 | // Branch Type 42 | val BR_N = 0.asUInt(4.W) // Next 43 | val BR_NE = 1.asUInt(4.W) // Branch on NotEqual 44 | val BR_EQ = 2.asUInt(4.W) // Branch on Equal 45 | val BR_GE = 3.asUInt(4.W) // Branch on Greater/Equal 46 | val BR_GEU = 4.asUInt(4.W) // Branch on Greater/Equal Unsigned 47 | val BR_LT = 5.asUInt(4.W) // Branch on Less Than 48 | val BR_LTU = 6.asUInt(4.W) // Branch on Less Than Unsigned 49 | val BR_J = 7.asUInt(4.W) // Jump 50 | val BR_JR = 8.asUInt(4.W) // Jump Register 51 | 52 | // RS1 Operand Select Signal 53 | val OP1_RS1 = 0.asUInt(2.W) // Register Source #1 54 | val OP1_PC = 1.asUInt(2.W) // PC 55 | val OP1_IMZ = 2.asUInt(2.W) // Zero-extended Immediate from RS1 field, for use by CSRI instructions 56 | val OP1_X = 0.asUInt(2.W) 57 | 58 | // RS2 Operand Select Signal 59 | val OP2_RS2 = 0.asUInt(3.W) // Register Source #2 60 | val OP2_ITYPE = 1.asUInt(3.W) // immediate, I-type 61 | val OP2_STYPE = 2.asUInt(3.W) // immediate, S-type 62 | val OP2_SBTYPE = 3.asUInt(3.W) // immediate, B 63 | val OP2_UTYPE = 4.asUInt(3.W) // immediate, U-type 64 | val OP2_UJTYPE = 5.asUInt(3.W) // immediate, J-type 65 | val OP2_X = 0.asUInt(3.W) 66 | 67 | // Register Operand Output Enable Signal 68 | val OEN_0 = false.B 69 | val OEN_1 = true.B 70 | 71 | // Register File Write Enable Signal 72 | val REN_0 = false.B 73 | val REN_1 = true.B 74 | 75 | // ALU Operation Signal 76 | val ALU_ADD = 0.asUInt(4.W) 77 | val ALU_SUB = 1.asUInt(4.W) 78 | val ALU_SLL = 2.asUInt(4.W) 79 | val ALU_SRL = 3.asUInt(4.W) 80 | val ALU_SRA = 4.asUInt(4.W) 81 | val ALU_AND = 5.asUInt(4.W) 82 | val ALU_OR = 6.asUInt(4.W) 83 | val ALU_XOR = 7.asUInt(4.W) 84 | val ALU_SLT = 8.asUInt(4.W) 85 | val ALU_SLTU = 9.asUInt(4.W) 86 | val ALU_COPY_1 = 10.asUInt(4.W) 87 | val ALU_COPY_2 = 11.asUInt(4.W) 88 | val ALU_X = 0.asUInt(4.W) 89 | 90 | // Writeback Select Signal 91 | val WB_ALU = 0.asUInt(2.W) 92 | val WB_MEM = 1.asUInt(2.W) 93 | val WB_PC4 = 2.asUInt(2.W) 94 | val WB_CSR = 3.asUInt(2.W) 95 | val WB_X = 0.asUInt(2.W) 96 | 97 | // Memory Write Signal 98 | val MWR_0 = false.B 99 | val MWR_1 = true.B 100 | val MWR_X = false.B 101 | 102 | // Memory Enable Signal 103 | val MEN_0 = false.B 104 | val MEN_1 = true.B 105 | val MEN_X = false.B 106 | 107 | // Memory Mask Type Signal 108 | val MSK_B = 0.asUInt(3.W) 109 | val MSK_BU = 1.asUInt(3.W) 110 | val MSK_H = 2.asUInt(3.W) 111 | val MSK_HU = 3.asUInt(3.W) 112 | val MSK_W = 4.asUInt(3.W) 113 | val MSK_X = 4.asUInt(3.W) 114 | 115 | } 116 | 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_5stage/core.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor 3 | //-------------------------------------------------------------------------- 4 | 5 | package sodor.stage5 6 | 7 | import chisel3._ 8 | import sodor.common._ 9 | 10 | import org.chipsalliance.cde.config.Parameters 11 | import freechips.rocketchip.rocket.CoreInterrupts 12 | 13 | class CoreIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle 14 | { 15 | val ddpath = Flipped(new DebugDPath()) 16 | val dcpath = Flipped(new DebugCPath()) 17 | val imem = new MemPortIo(conf.xprlen) 18 | val dmem = new MemPortIo(conf.xprlen) 19 | val interrupt = Input(new CoreInterrupts(false)) 20 | val hartid = Input(UInt()) 21 | val reset_vector = Input(UInt())} 22 | 23 | class Core()(implicit val p: Parameters, val conf: SodorCoreParams) extends AbstractCore 24 | { 25 | val io = IO(new CoreIo()) 26 | val c = Module(new CtlPath()) 27 | val d = Module(new DatPath()) 28 | 29 | c.io.ctl <> d.io.ctl 30 | c.io.dat <> d.io.dat 31 | 32 | io.imem <> c.io.imem 33 | io.imem <> d.io.imem 34 | 35 | io.dmem <> c.io.dmem 36 | io.dmem <> d.io.dmem 37 | 38 | d.io.ddpath <> io.ddpath 39 | c.io.dcpath <> io.dcpath 40 | 41 | d.io.interrupt := io.interrupt 42 | d.io.hartid := io.hartid 43 | d.io.reset_vector := io.reset_vector 44 | 45 | val mem_ports = List(io.dmem, io.imem) 46 | val interrupt = io.interrupt 47 | val hartid = io.hartid 48 | val reset_vector = io.reset_vector 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_5stage/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.stage5 2 | import sodor.stage5.constants._ 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import scala.math._ 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends 11 | SodorProcConstants with 12 | ScalarOpConstants with 13 | sodor.common.constants.RISCVConstants with 14 | sodor.common.MemoryOpConstants 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_5stage/regfile.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Processor Register File 3 | //-------------------------------------------------------------------------- 4 | // 5 | 6 | package sodor.stage5 7 | 8 | import chisel3._ 9 | import chisel3.util._ 10 | 11 | 12 | import sodor.stage5.Constants._ 13 | import sodor.common._ 14 | 15 | class RFileIo(implicit val conf: SodorCoreParams) extends Bundle() 16 | { 17 | val rs1_addr = Input(UInt(5.W)) 18 | val rs1_data = Output(UInt(conf.xprlen.W)) 19 | val rs2_addr = Input(UInt(5.W)) 20 | val rs2_data = Output(UInt(conf.xprlen.W)) 21 | val dm_addr = Input(UInt(5.W)) 22 | val dm_rdata = Output(UInt(conf.xprlen.W)) 23 | val dm_wdata = Input(UInt(conf.xprlen.W)) 24 | val dm_en = Input(Bool()) 25 | 26 | val waddr = Input(UInt(5.W)) 27 | val wdata = Input(UInt(conf.xprlen.W)) 28 | val wen = Input(Bool()) 29 | } 30 | 31 | class RegisterFile(implicit val conf: SodorCoreParams) extends Module 32 | { 33 | val io = IO(new RFileIo()) 34 | 35 | val regfile = Mem(32, UInt(conf.xprlen.W)) 36 | 37 | when (io.wen && (io.waddr =/= 0.U)) 38 | { 39 | regfile(io.waddr) := io.wdata 40 | } 41 | 42 | when (io.dm_en && (io.dm_addr =/= 0.U)) 43 | { 44 | regfile(io.dm_addr) := io.dm_wdata 45 | } 46 | 47 | io.rs1_data := Mux((io.rs1_addr =/= 0.U), regfile(io.rs1_addr), 0.U) 48 | io.rs2_data := Mux((io.rs2_addr =/= 0.U), regfile(io.rs2_addr), 0.U) 49 | io.dm_rdata := Mux((io.dm_addr =/= 0.U), regfile(io.dm_addr), 0.U) 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/consts.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV U-Coded Processor Constants 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | 9 | package sodor.ucode 10 | package constants 11 | { 12 | 13 | import chisel3._ 14 | import chisel3.util._ 15 | 16 | import sodor.common._ 17 | 18 | trait SodorProcConstants 19 | { 20 | 21 | //************************************ 22 | // Machine Parameters 23 | } 24 | 25 | trait ScalarOpConstants extends MemoryOpConstants 26 | { 27 | //************************************ 28 | // Micro-code Generated Control Signals 29 | 30 | // Load IR Register Signal 31 | val LDIR_0 = 0.asUInt(1.W) 32 | val LDIR_1 = 1.asUInt(1.W) 33 | val LDIR_X = 0.asUInt(1.W) 34 | 35 | // Register File Address Select Signal 36 | val RS_PC = 0.asUInt(3.W) 37 | val RS_RD = 1.asUInt(3.W) 38 | val RS_RS1 = 2.asUInt(3.W) 39 | val RS_RS2 = 3.asUInt(3.W) 40 | val RS_CA = 5.asUInt(3.W) 41 | val RS_CR = 6.asUInt(3.W) 42 | val RS_X0 = 7.asUInt(3.W) 43 | val RS_X = 7.asUInt(3.W) 44 | 45 | // Register File Write Signal 46 | val RWR_0 = 0.asUInt(1.W) 47 | val RWR_1 = 1.asUInt(1.W) 48 | val RWR_X = 0.asUInt(1.W) 49 | 50 | // Register File Enable Signal 51 | val REN_0 = 0.asUInt(1.W) 52 | val REN_1 = 1.asUInt(1.W) 53 | val REN_X = 0.asUInt(1.W) 54 | 55 | // Load A Register Signal 56 | val LDA_0 = 0.asUInt(1.W) 57 | val LDA_1 = 1.asUInt(1.W) 58 | val LDA_X = 0.asUInt(1.W) 59 | 60 | // Load B Register Signal 61 | val LDB_0 = 0.asUInt(1.W) 62 | val LDB_1 = 1.asUInt(1.W) 63 | val LDB_X = 0.asUInt(1.W) 64 | 65 | // ALU Operation Signal 66 | val ALU_COPY_A = 0.asUInt(5.W) 67 | val ALU_COPY_B = 1.asUInt(5.W) 68 | val ALU_INC_A_1 = 2.asUInt(5.W) 69 | val ALU_DEC_A_1 = 3.asUInt(5.W) 70 | val ALU_INC_A_4 = 4.asUInt(5.W) 71 | val ALU_DEC_A_4 = 5.asUInt(5.W) 72 | val ALU_ADD = 6.asUInt(5.W) 73 | val ALU_SUB = 7.asUInt(5.W) 74 | val ALU_SLL = 8.asUInt(5.W) 75 | val ALU_SRL = 9.asUInt(5.W) 76 | val ALU_SRA = 10.asUInt(5.W) 77 | val ALU_AND = 11.asUInt(5.W) 78 | val ALU_OR = 12.asUInt(5.W) 79 | val ALU_XOR = 13.asUInt(5.W) 80 | val ALU_SLT = 14.asUInt(5.W) 81 | val ALU_SLTU = 15.asUInt(5.W) 82 | val ALU_MASK_12 = 16.asUInt(5.W) // output A with lower 12 bits cleared (AUIPC) 83 | val ALU_EVEC = 17.asUInt(5.W) // output evec from CSR file 84 | val ALU_X = 0.asUInt(5.W) 85 | 86 | // ALU Enable Signal 87 | val AEN_0 = 0.asUInt(1.W) 88 | val AEN_1 = 1.asUInt(1.W) 89 | val AEN_X = 0.asUInt(1.W) 90 | 91 | val LDMA_0 = 0.asUInt(1.W) 92 | val LDMA_1 = 1.asUInt(1.W) 93 | val LDMA_X = 0.asUInt(1.W) 94 | 95 | // Memory Write Signal 96 | val MWR_0 = 0.asUInt(1.W) 97 | val MWR_1 = 1.asUInt(1.W) 98 | val MWR_X = 0.asUInt(1.W) 99 | 100 | // Memory Enable Signal 101 | val MEN_0 = 0.asUInt(1.W) 102 | val MEN_1 = 1.asUInt(1.W) 103 | val MEN_X = 0.asUInt(1.W) 104 | 105 | // Immediate Extend Select 106 | val IS_I = 0.asUInt(3.W) //I-Type (LDs,ALU) , sign-extend : ({ 20{inst[31:20] }) 107 | val IS_S = 1.asUInt(3.W) //S-Type (Stores) , sign-extend : ({ 20{inst[31:25]}, inst[11:7] }) 108 | val IS_U = 2.asUInt(3.W) //U-Type (LUI) , sign-extend : ({ {inst[31:12], {12{1'b0}} }) 109 | val IS_J = 3.asUInt(3.W) //J-Type (JAL) , sign-extend and shift 1b: ({ 11{inst[31]}, inst[19: 12], inst[20], inst[30:21], 1'b0 }) 110 | val IS_B = 4.asUInt(3.W) //B-Type (Branches), sign-extend and shift 1b: ({ 19{inst[31]}, inst[7], inst[30:25], inst[11:8], 1'b0 }) 111 | val IS_Z = 5.asUInt(3.W) //Z-Type (CSRR*I) , zero-extended rs1 field : ({ 27{1'b0}, inst[19:15] }) 112 | val IS_X = 0.asUInt(3.W) 113 | 114 | // Immediate Enable Signal 115 | val IEN_0 = 0.asUInt(1.W) 116 | val IEN_1 = 1.asUInt(1.W) 117 | val IEN_X = 0.asUInt(1.W) 118 | 119 | // Enable ToHost Signal 120 | val TEN_0 = 0.asUInt(1.W) 121 | val TEN_1 = 1.asUInt(1.W) 122 | val TEN_X = 0.asUInt(1.W) 123 | 124 | // uBranch Type 125 | val UBR_N = 0.asUInt(3.W) // Next 126 | val UBR_D = 1.asUInt(3.W) // Dispatch on Opcode 127 | val UBR_J = 2.asUInt(3.W) // Jump 128 | val UBR_EZ = 3.asUInt(3.W) // Jump on ALU-Zero 129 | val UBR_NZ = 4.asUInt(3.W) // Jump on Not ALU-Zero 130 | val UBR_S = 5.asUInt(3.W) // Spin if Mem-Busy 131 | 132 | 133 | // Micro-PC State Logic 134 | val UPC_NEXT = 0.asUInt(2.W) 135 | val UPC_ABSOLUTE = 1.asUInt(2.W) 136 | val UPC_CURRENT = 2.asUInt(2.W) 137 | val UPC_DISPATCH = 3.asUInt(2.W) 138 | 139 | // Registers 140 | val PC_IDX = 32.U //pc register 141 | 142 | // Memory Mask Type Signal 143 | val MSK_SZ = 3.W 144 | } 145 | 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/core.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV Micro-Coded Processor 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | // 8 | // A rough design spec can be found at: 9 | // http://inst.eecs.berkeley.edu/~cs152/sp11/assignments/ps1/handout1.pdf 10 | // 11 | 12 | package sodor.ucode 13 | 14 | import chisel3._ 15 | import chisel3.util._ 16 | 17 | import sodor.common._ 18 | 19 | import org.chipsalliance.cde.config.Parameters 20 | import freechips.rocketchip.rocket.CoreInterrupts 21 | 22 | class CoreIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle 23 | { 24 | val ddpath = Flipped(new DebugDPath()) 25 | val dcpath = Flipped(new DebugCPath()) 26 | val mem = new MemPortIo(conf.xprlen) 27 | val interrupt = Input(new CoreInterrupts(false)) 28 | val hartid = Input(UInt()) 29 | val reset_vector = Input(UInt()) 30 | } 31 | 32 | class Core(implicit val p: Parameters, val conf: SodorCoreParams) extends AbstractCore 33 | { 34 | val io = IO(new CoreIo()) 35 | val c = Module(new CtlPath()) 36 | val d = Module(new DatPath()) 37 | 38 | c.io.ctl <> d.io.ctl 39 | c.io.dat <> d.io.dat 40 | 41 | c.io.mem <> io.mem 42 | d.io.mem <> io.mem 43 | io.mem.req.valid := c.io.mem.req.valid 44 | io.mem.req.bits.fcn := c.io.mem.req.bits.fcn 45 | io.mem.req.bits.typ := c.io.mem.req.bits.typ 46 | 47 | d.io.ddpath <> io.ddpath 48 | c.io.dcpath <> io.dcpath 49 | 50 | d.io.interrupt := io.interrupt 51 | d.io.hartid := io.hartid 52 | d.io.reset_vector := io.reset_vector 53 | 54 | val mem_ports = List(io.mem) 55 | val interrupt = io.interrupt 56 | val hartid = io.hartid 57 | val reset_vector = io.reset_vector 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/cpath.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV U-Coded Processor Control Path 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | package sodor.ucode 9 | 10 | import chisel3._ 11 | import chisel3.util._ 12 | 13 | import freechips.rocketchip.rocket.CSR 14 | 15 | import sodor.common._ 16 | import sodor.common.Instructions._ 17 | import sodor.ucode.Constants._ 18 | import scala.collection.mutable.ArrayBuffer 19 | 20 | 21 | class CtlToDatIo extends Bundle() 22 | { 23 | val csr_cmd = Output(UInt(CSR.SZ.W)) 24 | val ld_ir = Output(Bool()) 25 | val reg_sel = Output(UInt(RS_X.getWidth.W)) 26 | val reg_wr = Output(Bool()) 27 | val en_reg = Output(Bool()) 28 | val ld_a = Output(Bool()) 29 | val ld_b = Output(Bool()) 30 | val alu_op = Output(UInt(ALU_X.getWidth.W)) 31 | val en_alu = Output(Bool()) 32 | val ld_ma = Output(Bool()) 33 | val mem_wr = Output(Bool()) 34 | val en_mem = Output(Bool()) 35 | val msk_sel = Output(UInt(MSK_SZ)) 36 | val is_sel = Output(UInt(IS_X.getWidth.W)) 37 | val en_imm = Output(Bool()) 38 | val upc = Output(UInt()) // for debugging purposes 39 | val upc_is_fetch = Output(Bool()) // for debugging purposes 40 | val illegal_exception = Output(Bool()) 41 | val exception = Output(Bool()) 42 | val retire = Output(Bool()) 43 | } 44 | 45 | class CpathIo(implicit val conf: SodorCoreParams) extends Bundle() 46 | { 47 | val dcpath = Flipped(new DebugCPath()) 48 | val mem = new MemPortIo(conf.xprlen) 49 | val dat = Flipped(new DatToCtlIo()) 50 | val ctl = new CtlToDatIo() 51 | } 52 | 53 | class CtlPath(implicit val conf: SodorCoreParams) extends Module 54 | { 55 | val io = IO(new CpathIo()) 56 | io := DontCare 57 | 58 | // Compile the Micro-code down into a ROM 59 | val (label_target_map, label_sz) = MicrocodeCompiler.constructLabelTargetMap(Microcode.codes) 60 | val rombits = MicrocodeCompiler.emitRomBits(Microcode.codes, label_target_map, label_sz) 61 | val opcode_dispatch_table = MicrocodeCompiler.generateDispatchTable(label_target_map) 62 | 63 | 64 | // Macro Instruction Opcode Dispatch Table 65 | val upc_opgroup_target = Lookup (io.dat.inst, label_target_map("ILLEGAL").asUInt(label_sz.W), 66 | opcode_dispatch_table) 67 | 68 | // Micro-PC State Register 69 | val upc_state_next = Wire(UInt()) 70 | val upc_state = RegNext(upc_state_next, init = label_target_map("FETCH").asUInt(label_sz.W)) 71 | 72 | // Micro-code ROM 73 | val micro_code = VecInit(rombits) 74 | val uop = micro_code(upc_state) 75 | 76 | // Extract Control Signals from UOP 77 | val cs = uop.asTypeOf(new Bundle() 78 | { 79 | val csr_cmd = UInt(CSR.SZ.W) 80 | val ld_ir = Bool() 81 | val reg_sel = UInt(RS_X.getWidth.W) 82 | val reg_wr = Bool() 83 | val en_reg = Bool() 84 | val ld_a = Bool() 85 | val ld_b = Bool() 86 | val alu_op = UInt(ALU_X.getWidth.W) 87 | val en_alu = Bool() 88 | val ld_ma = Bool() 89 | val mem_wr = Bool() 90 | val en_mem = Bool() 91 | val msk_sel = UInt(MSK_SZ) 92 | val is_sel = UInt(IS_X.getWidth.W) 93 | val en_imm = Bool() 94 | val ubr = UInt(UBR_N.getWidth.W) 95 | val upc_rom_target = UInt(label_sz.W) 96 | // override def clone = this.asInstanceOf[this.type] 97 | }) 98 | require(label_sz == 8, "Label size must be 8") 99 | 100 | val mem_is_busy = !io.mem.resp.valid && (cs.en_mem || cs.mem_wr) 101 | 102 | val interrupt_trigger = io.dat.interrupt && io.ctl.upc_is_fetch 103 | val non_illegal_trap = interrupt_trigger || io.dat.addr_exception 104 | 105 | // Micro-PC State Logic 106 | val upc_sel = MuxCase(UPC_CURRENT, Array( 107 | (cs.ubr === UBR_N) -> UPC_NEXT, 108 | (cs.ubr === UBR_D) -> UPC_DISPATCH, 109 | (cs.ubr === UBR_J) -> UPC_ABSOLUTE, 110 | (cs.ubr === UBR_EZ)-> Mux ( io.dat.alu_zero, UPC_ABSOLUTE , UPC_NEXT), 111 | (cs.ubr === UBR_NZ)-> Mux (~io.dat.alu_zero, UPC_ABSOLUTE , UPC_NEXT), 112 | (cs.ubr === UBR_S) -> Mux (mem_is_busy , UPC_CURRENT , UPC_NEXT) 113 | )) 114 | 115 | 116 | upc_state_next := MuxCase(upc_state, Array( 117 | (non_illegal_trap) -> label_target_map("ILLEGAL").asUInt(label_sz.W), 118 | (upc_sel === UPC_DISPATCH) -> upc_opgroup_target, 119 | (upc_sel === UPC_ABSOLUTE) -> cs.upc_rom_target, 120 | (upc_sel === UPC_NEXT) -> (upc_state + 1.U), 121 | (upc_sel === UPC_CURRENT) -> upc_state 122 | )) 123 | 124 | 125 | // Exception Handling --------------------- 126 | io.ctl.illegal_exception := label_target_map("ILLEGAL").U === upc_state && RegNext(cs.ubr) === UBR_D 127 | io.ctl.exception := io.ctl.illegal_exception || io.dat.addr_exception 128 | 129 | // Cpath Control Interface 130 | io.ctl.msk_sel := cs.msk_sel 131 | io.ctl.ld_ir := cs.ld_ir 132 | io.ctl.reg_sel := cs.reg_sel 133 | io.ctl.reg_wr := cs.reg_wr 134 | io.ctl.en_reg := cs.en_reg 135 | io.ctl.ld_a := cs.ld_a 136 | io.ctl.ld_b := cs.ld_b 137 | io.ctl.alu_op := cs.alu_op 138 | io.ctl.en_alu := cs.en_alu 139 | io.ctl.ld_ma := cs.ld_ma 140 | io.ctl.mem_wr := cs.mem_wr 141 | io.ctl.en_mem := cs.en_mem 142 | io.ctl.is_sel := cs.is_sel 143 | io.ctl.en_imm := cs.en_imm 144 | 145 | // convert CSR instructions with raddr1 == 0 to read-only CSR commands 146 | val rs1_addr = io.dat.inst(RS1_MSB, RS1_LSB) 147 | val csr_ren = (cs.csr_cmd === CSR.S || cs.csr_cmd === CSR.C) && rs1_addr === 0.U 148 | val csr_cmd1 = Mux(csr_ren, CSR.R, cs.csr_cmd) 149 | io.ctl.csr_cmd := csr_cmd1 150 | 151 | io.ctl.upc := upc_state 152 | io.ctl.upc_is_fetch := (upc_state === label_target_map("FETCH").U) 153 | 154 | // track whether current instruction caused an exception 155 | val en_retire = RegInit(false.B) 156 | when (io.ctl.upc_is_fetch) { 157 | en_retire := true.B 158 | } 159 | when (io.ctl.exception) { 160 | en_retire := false.B 161 | } 162 | io.ctl.retire := io.ctl.upc_is_fetch && en_retire 163 | 164 | // Memory Interface 165 | io.mem.req.bits.fcn := Mux(cs.mem_wr , M_XWR, M_XRD) 166 | io.mem.req.bits.typ := cs.msk_sel 167 | io.mem.req.valid := (cs.en_mem || cs.mem_wr) && !io.dat.addr_exception 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/dpath.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV U-Coded Processor Data Path 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Christopher Celio 6 | // 2011 May 28 7 | 8 | package sodor.ucode 9 | 10 | import chisel3._ 11 | import chisel3.util._ 12 | 13 | import org.chipsalliance.cde.config.Parameters 14 | import freechips.rocketchip.rocket.CSRFile 15 | import freechips.rocketchip.rocket.CoreInterrupts 16 | 17 | import sodor.ucode.Constants._ 18 | import sodor.common._ 19 | 20 | class DatToCtlIo extends Bundle() 21 | { 22 | val inst = Output(UInt(32.W)) 23 | val alu_zero = Output(Bool()) 24 | val csr_eret = Output(Bool()) 25 | val interrupt = Output(Bool()) 26 | val addr_exception = Output(Bool()) 27 | } 28 | 29 | 30 | class DpathIo(implicit val p: Parameters, val conf: SodorCoreParams) extends Bundle() 31 | { 32 | val ddpath = Flipped(new DebugDPath()) 33 | val mem = new MemPortIo(conf.xprlen) 34 | val ctl = Flipped(new CtlToDatIo()) 35 | val dat = new DatToCtlIo() 36 | val interrupt = Input(new CoreInterrupts(false)) 37 | val hartid = Input(UInt()) 38 | val reset_vector = Input(UInt()) 39 | } 40 | 41 | 42 | class DatPath(implicit val p: Parameters, val conf: SodorCoreParams) extends Module 43 | { 44 | val io = IO(new DpathIo()) 45 | io := DontCare 46 | 47 | 48 | // forward declarations 49 | val imm = Wire(UInt(conf.xprlen.W)) 50 | val alu = Wire(UInt(conf.xprlen.W)) 51 | val reg_rdata = Wire(UInt(conf.xprlen.W)) 52 | val csr_rdata = Wire(UInt(conf.xprlen.W)) 53 | val exception_target = Wire(UInt(conf.xprlen.W)) 54 | 55 | // The Bus 56 | // (this is a bus-based RISCV implementation, so all data movement goes 57 | // across this wire) 58 | val bus = MuxCase(0.U, Array( 59 | (io.ctl.en_imm) -> imm(conf.xprlen-1,0), 60 | (io.ctl.en_alu) -> alu(conf.xprlen-1,0), 61 | (io.ctl.en_reg & 62 | (io.ctl.reg_sel =/= RS_CR)) -> reg_rdata(conf.xprlen-1,0), 63 | (io.ctl.en_mem) -> io.mem.resp.bits.data(conf.xprlen-1,0), 64 | (io.ctl.en_reg & 65 | (io.ctl.reg_sel === RS_CR)) -> csr_rdata 66 | )) 67 | 68 | assert(PopCount(Seq(io.ctl.en_imm, io.ctl.en_alu, io.ctl.en_reg, io.ctl.en_mem)) <= 1.U, 69 | "Error. Multiple components attempting to write to bus simultaneously") 70 | 71 | 72 | // IR Register 73 | val ir = RegInit(0.asUInt(conf.xprlen.W)) 74 | when (io.ctl.ld_ir) { ir := bus } 75 | io.dat.inst := ir 76 | 77 | // A Register 78 | val reg_a = RegInit("haaaa".asUInt(conf.xprlen.W)) 79 | when (io.ctl.ld_a) { reg_a := bus } 80 | 81 | // B Register 82 | val reg_b = RegInit("hbbbb".asUInt(conf.xprlen.W)) 83 | when (io.ctl.ld_b) { reg_b := bus } 84 | 85 | // MA Register 86 | val reg_ma = RegInit("heeee".asUInt(conf.xprlen.W)) 87 | when (io.ctl.ld_ma) { reg_ma := bus } 88 | 89 | // IR Immediate 90 | imm := MuxCase(0.U, Array( 91 | (io.ctl.is_sel === IS_I) -> Cat(Fill(20,ir(31)),ir(31,20)), 92 | (io.ctl.is_sel === IS_S) -> Cat(Fill(20,ir(31)),ir(31,25),ir(11,7)), 93 | (io.ctl.is_sel === IS_U) -> Cat(ir(31,12),0.S(12.W)), 94 | (io.ctl.is_sel === IS_B) -> Cat(Fill(20,ir(31)),ir(7),ir(30,25),ir(11,8),0.asUInt(1.W)), 95 | (io.ctl.is_sel === IS_J) -> Cat(Fill(20,ir(31)),ir(19,12),ir(20),ir(30,21),0.asUInt(1.W)), 96 | (io.ctl.is_sel === IS_Z) -> Cat(0.asUInt(27.W), ir(19,15)) 97 | )) 98 | 99 | 100 | 101 | 102 | // Exception 103 | val tval_data_ma = Wire(UInt(conf.xprlen.W)) 104 | val tval_inst_ma = Wire(UInt(conf.xprlen.W)) 105 | val inst_misaligned = Wire(Bool()) 106 | val data_misaligned = Wire(Bool()) 107 | val mem_store = Wire(Bool()) 108 | io.dat.addr_exception := inst_misaligned || data_misaligned 109 | 110 | // Register File (Single Port) 111 | // also holds the PC register 112 | val rs1 = ir(RS1_MSB, RS1_LSB) 113 | val rs2 = ir(RS2_MSB, RS2_LSB) 114 | val rd = ir(RD_MSB, RD_LSB) 115 | 116 | val reg_addr = MuxCase(0.U, Array( 117 | (io.ctl.reg_sel === RS_PC) -> PC_IDX, 118 | (io.ctl.reg_sel === RS_RD) -> rd, 119 | (io.ctl.reg_sel === RS_RS1) -> rs1, 120 | (io.ctl.reg_sel === RS_RS2) -> rs2, 121 | (io.ctl.reg_sel === RS_X0) -> X0, 122 | (io.ctl.reg_sel === RS_CA) -> X0, 123 | (io.ctl.reg_sel === RS_CR) -> X0 124 | )) 125 | 126 | //note: I could be far more clever and save myself on wasted registers here... 127 | //32 x-registers, 1 pc-register 128 | val regfile = Reg(Vec(33, UInt(32.W))) 129 | when (reset.asBool) { 130 | regfile(PC_IDX) := io.reset_vector 131 | } 132 | 133 | 134 | 135 | inst_misaligned := false.B 136 | tval_inst_ma := RegNext(bus & ~1.U(conf.xprlen.W)) 137 | when (io.ctl.reg_wr & reg_addr =/= 0.U) 138 | { 139 | when (reg_addr === PC_IDX) 140 | { 141 | // Check bit 1 of the address for misalignment 142 | inst_misaligned := bus(1) 143 | // Clear LSB of the write data if we are writing to PC (required by JALR, but doesn't hurt doing this for all req) 144 | regfile(reg_addr) := bus & ~1.U(conf.xprlen.W) 145 | } 146 | .elsewhen (!io.ctl.exception) 147 | { 148 | regfile(reg_addr) := bus 149 | } 150 | } 151 | 152 | // This is a hack to make it look like the CSRFile is part of the regfile 153 | reg_rdata := MuxCase(regfile(reg_addr), Array( 154 | (io.ctl.reg_sel === RS_CR) -> csr_rdata, 155 | (reg_addr === 0.U) -> 0.asUInt(conf.xprlen.W))) 156 | 157 | // CSR addr Register 158 | val csr_addr = RegInit(0.asUInt(12.W)) 159 | when(io.ctl.reg_wr & (io.ctl.reg_sel === RS_CA)) { 160 | csr_addr := bus 161 | } 162 | 163 | val csr_wdata = RegInit(0.asUInt(conf.xprlen.W)) 164 | when(io.ctl.reg_wr & (io.ctl.reg_sel === RS_CR)) { 165 | csr_wdata := bus 166 | } 167 | 168 | // Control Status Registers 169 | val csr = Module(new CSRFile(perfEventSets=CSREvents.events)) 170 | csr.io := DontCare 171 | csr.io.decode(0).inst := csr_addr << 20 172 | csr.io.rw.addr := csr_addr 173 | csr.io.rw.wdata := csr_wdata 174 | csr.io.rw.cmd := io.ctl.csr_cmd 175 | csr_rdata := csr.io.rw.rdata 176 | csr.io.retire := io.ctl.retire 177 | csr.io.pc := regfile(PC_IDX) - 4.U 178 | exception_target := csr.io.evec 179 | 180 | csr.io.interrupts := io.interrupt 181 | csr.io.hartid := io.hartid 182 | 183 | io.dat.csr_eret := csr.io.eret 184 | 185 | val interrupt_handled = RegInit(false.B) 186 | when (io.ctl.retire) { interrupt_handled := csr.io.interrupt } 187 | val interrupt_edge = csr.io.interrupt && ! interrupt_handled 188 | io.dat.interrupt := interrupt_edge 189 | 190 | // Delay exception for CSR to avoid combinational loop 191 | // If there is an exception, we will enter ILLEGAL state in the next cycle 192 | val delayed_exception = io.ctl.illegal_exception || RegNext(io.dat.addr_exception) 193 | val exception_cause = Mux(io.ctl.illegal_exception, Causes.illegal_instruction.U, 194 | Mux(RegNext(inst_misaligned), Causes.misaligned_fetch.U, 195 | Mux(RegNext(mem_store), Causes.misaligned_store.U, 196 | Causes.misaligned_load.U 197 | ))) 198 | csr.io.exception := delayed_exception 199 | csr.io.cause := Mux(delayed_exception, exception_cause, csr.io.interrupt_cause) 200 | csr.io.tval := MuxCase(0.U, Array( 201 | (exception_cause === Causes.illegal_instruction.U) -> io.dat.inst, 202 | (exception_cause === Causes.misaligned_fetch.U) -> tval_inst_ma, 203 | (exception_cause === Causes.misaligned_store.U) -> tval_data_ma, 204 | (exception_cause === Causes.misaligned_load.U) -> tval_data_ma, 205 | )) 206 | csr.io.ungated_clock := clock 207 | 208 | // Add your own uarch counters here! 209 | csr.io.counters.foreach(_.inc := false.B) 210 | 211 | // ALU 212 | val alu_shamt = reg_b(4,0).asUInt 213 | 214 | alu := MuxCase(0.U, Array[(Bool, UInt)]( 215 | (io.ctl.alu_op === ALU_COPY_A) -> reg_a, 216 | (io.ctl.alu_op === ALU_COPY_B) -> reg_b, 217 | (io.ctl.alu_op === ALU_INC_A_1) -> (reg_a + 1.U), 218 | (io.ctl.alu_op === ALU_DEC_A_1) -> (reg_a - 1.U), 219 | (io.ctl.alu_op === ALU_INC_A_4) -> (reg_a + 4.U), 220 | (io.ctl.alu_op === ALU_DEC_A_4) -> (reg_a - 4.U), 221 | (io.ctl.alu_op === ALU_ADD) -> (reg_a + reg_b), 222 | (io.ctl.alu_op === ALU_SUB) -> (reg_a - reg_b), 223 | (io.ctl.alu_op === ALU_SLL) -> ((reg_a << alu_shamt)(conf.xprlen-1,0)), 224 | (io.ctl.alu_op === ALU_SRL) -> (reg_a >> alu_shamt), 225 | (io.ctl.alu_op === ALU_SRA) -> (reg_a.asSInt >> alu_shamt).asUInt, 226 | (io.ctl.alu_op === ALU_AND) -> (reg_a & reg_b), 227 | (io.ctl.alu_op === ALU_OR) -> (reg_a | reg_b), 228 | (io.ctl.alu_op === ALU_XOR) -> (reg_a ^ reg_b), 229 | (io.ctl.alu_op === ALU_SLT) -> (reg_a.asSInt < reg_b.asSInt).asUInt, 230 | (io.ctl.alu_op === ALU_SLTU) -> (reg_a < reg_b), 231 | (io.ctl.alu_op === ALU_MASK_12) -> (reg_a & ~((1<<12)-1).asUInt(conf.xprlen.W)), 232 | (io.ctl.alu_op === ALU_EVEC) -> exception_target 233 | )) 234 | 235 | // Output Signals to the Control Path 236 | io.dat.alu_zero := (alu === 0.U) 237 | 238 | // Output Signals to the Memory 239 | io.mem.req.bits.addr := reg_ma.asUInt 240 | io.mem.req.bits.data := bus 241 | 242 | // Data misalignment detection 243 | // For example, if type is 3 (word), the mask is ~(0b111 << (3 - 1)) = ~0b100 = 0b011. 244 | val misaligned_mask = Wire(UInt(3.W)) 245 | misaligned_mask := ~(7.U(3.W) << (io.ctl.msk_sel - 1.U)(1, 0)) 246 | data_misaligned := (misaligned_mask & reg_ma.asUInt.apply(2, 0)).orR && (io.ctl.en_mem || io.ctl.mem_wr) 247 | mem_store := io.ctl.mem_wr 248 | tval_data_ma := RegNext(reg_ma.asUInt) 249 | 250 | // Printout 251 | printf("Cyc= %d [%d] PCReg=[%x] uPC=[%x] Bus=[%x] RegSel=[%d] RegAddr=[%d] A=[%x] B=[%x] MA=[%x] InstReg=[%x] %c%c%c DASM(%x)\n", 252 | csr.io.time(31,0), 253 | csr.io.retire, 254 | regfile(PC_IDX), 255 | io.ctl.upc, 256 | bus, 257 | io.ctl.reg_sel, 258 | reg_addr, 259 | reg_a, 260 | reg_b, 261 | reg_ma, 262 | ir, 263 | Mux(io.ctl.upc_is_fetch, Str("F"), Str(" ")), 264 | Mux(io.ctl.en_mem, Str("M"), Str(" ")), 265 | Mux(io.ctl.exception, Str("X"), Str(" ")), 266 | ir) 267 | 268 | } 269 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/microcodecompiler.scala: -------------------------------------------------------------------------------- 1 | //************************************************************************** 2 | // RISCV U-Code Compiler 3 | //-------------------------------------------------------------------------- 4 | // 5 | // Jonathan Bachrach 6 | // Christopher Celio 7 | // 2012 Feb 4 8 | // 9 | // 10 | // 11 | // This file contains the methods required for compiling the micro-code found in 12 | // "microcode.scala" down to a ROM. The micro-code programmer needs only specify 13 | // the micro-code and labels at the beginning of the start of a macro-instruction 14 | // sequence, and the micro-code compiler will generate the micro-code ROM bits, 15 | // and patch in the label addresses (much like writing assembly, labels get 16 | // turned into addresses). 17 | // 18 | // The compiler also generates the dispatch table, which requires analyzing the 19 | // micro-code table and comparing which instruction labels were used that match 20 | // up the instructions enumerated in "instructions.scala". 21 | // 22 | 23 | 24 | package sodor.ucode 25 | 26 | import chisel3._ 27 | import chisel3.util._ 28 | 29 | 30 | import sodor.common.Instructions._ 31 | import scala.collection.mutable.ArrayBuffer 32 | 33 | 34 | abstract class MicroOp() 35 | case class Label(name: String) extends MicroOp 36 | case class Signals(ctrl_bits: Bits, label: String = "X") extends MicroOp 37 | 38 | object MicrocodeCompiler 39 | { 40 | 41 | // run through instructions.scala, build mapping bewteen 42 | // Instruction Name (String) -> Bit Pattern 43 | // used for building opcodeDispatchTable 44 | def generateInstructionList (): Map[String, BitPat] = 45 | { 46 | var inst_list = Map[String, BitPat]() 47 | val instClass = sodor.common.Instructions.getClass() 48 | val b = BitPat("b?????????????????000?????1100011") 49 | val bitsClass = b.getClass() 50 | 51 | for (m <- instClass.getMethods()) 52 | { 53 | val name = m.getName() 54 | val rtype = m.getReturnType() 55 | if (rtype == bitsClass) 56 | { 57 | val i = m.invoke(sodor.common.Instructions) 58 | inst_list += ((name, i.asInstanceOf[BitPat])) 59 | } 60 | } 61 | 62 | return inst_list 63 | } 64 | 65 | def generateDispatchTable (labelTargets: Map[String,Int]): Array[(BitPat, UInt)]= 66 | { 67 | println("Generating Opcode Dispatch Table...") 68 | var dispatch_targets = ArrayBuffer[(BitPat, UInt)]() 69 | val inst_list = generateInstructionList() 70 | 71 | for ((inst_str, inst_bits) <- inst_list) 72 | { 73 | if (labelTargets.contains(inst_str)) 74 | { 75 | dispatch_targets += ((inst_bits -> labelTargets(inst_str).U)) 76 | } 77 | } 78 | 79 | //for debugging purposes, print out unused labels to verify we didn't 80 | //fail at matching an instructure 81 | var unused_targets = ArrayBuffer[String]() 82 | for ((label_str, addr) <- labelTargets) 83 | { 84 | if (!inst_list.contains(label_str)) 85 | unused_targets += label_str 86 | } 87 | 88 | println("") 89 | println("Unused Labels for Dispatching:") 90 | println(" (Verify no instruction labels made it here by accident)") 91 | println("") 92 | println(" " + unused_targets) 93 | println("") 94 | 95 | return dispatch_targets.toArray 96 | } 97 | 98 | // returns a tuple 99 | // Mapping from "LabelString" to micro-address 100 | // and LabelSz (log of largest micro-address) 101 | def constructLabelTargetMap(uop_insts: Array[MicroOp]): (Map[String,Int], Int) = 102 | { 103 | println("Building Microcode labelTargetMap...") 104 | 105 | var label_map = Map[String,Int]() 106 | var uaddr = 0 107 | for (uop_inst <- uop_insts) 108 | { 109 | uop_inst match 110 | { 111 | case Label(name) => label_map += ((name, uaddr)) 112 | case Signals(code,str) => uaddr += 1 113 | } 114 | } 115 | val label_sz = log2Ceil(uaddr) 116 | println("Label Map " + label_map) 117 | println(" MicroROM size : " + uaddr + " lines") 118 | println(" Bitwidth of uaddr: " + label_sz + " bits") 119 | println("") 120 | return (label_map, label_sz) 121 | } 122 | 123 | 124 | def emitRomBits(uop_lines: Array[MicroOp], labelTargets: Map[String,Int], label_sz: Int): Array[Bits] = 125 | { 126 | //printf("Building Microcode ROM...\n") 127 | 128 | var buf = ArrayBuffer[Bits]() 129 | 130 | for (uop_line <- uop_lines) 131 | { 132 | uop_line match 133 | { 134 | case Label(name) => 135 | case Signals(ctrl_bits, "X") => 136 | val line = Cat(ctrl_bits, 0.U(label_sz.W)) 137 | buf += (line) 138 | 139 | 140 | case Signals(ctrl_bits, label) => 141 | val line = Cat(ctrl_bits, labelTargets(label).U(label_sz.W)) 142 | buf += (line) 143 | } 144 | } 145 | 146 | println("") 147 | return buf.toArray 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/scala/sodor/rv32_ucode/package.scala: -------------------------------------------------------------------------------- 1 | package sodor.ucode 2 | import sodor.ucode.constants._ 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import scala.math._ 7 | 8 | //TODO: When compiler bug SI-5604 is fixed in 2.10, change object Constants to 9 | // package object rocket and remove import Constants._'s from other files 10 | object Constants extends 11 | SodorProcConstants with 12 | ScalarOpConstants with 13 | sodor.common.constants.RISCVConstants with 14 | sodor.common.MemoryOpConstants 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.riscv 3 | *.out 4 | *.dump 5 | rv32ui-p-* 6 | -------------------------------------------------------------------------------- /test/custom-bmarks/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/sh 2 | 3 | CC := riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 4 | OBJDUMP := riscv32-unknown-elf-objdump 5 | 6 | CFLAGS := -O0 -Wall -g -std=gnu99 -mcmodel=medany -fno-common -fno-builtin-printf -I ../env 7 | LDFLAGS := -static -nostdlib -nostartfiles -T ../env/test.ld 8 | 9 | programs := mix 10 | bins := $(addsuffix .riscv,$(programs)) 11 | dumps := $(addsuffix .dump,$(programs)) 12 | logs := $(addsuffix .out,$(programs)) 13 | 14 | all: $(bins) $(dumps) 15 | dump: $(dumps) 16 | run: $(logs) 17 | 18 | %.riscv: %.c crt.S 19 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ 20 | 21 | %.dump: %.riscv 22 | $(OBJDUMP) -D $< > $@ 23 | 24 | %.out: %.riscv 25 | spike --isa=rv32i -l $< > $@ 2>&1 26 | 27 | clean: 28 | rm -f -- $(bins) $(dumps) $(logs) 29 | 30 | .PHONY: all dump run clean 31 | .SUFFIXES: 32 | -------------------------------------------------------------------------------- /test/custom-bmarks/crt.S: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | #include "encoding.h" 4 | 5 | #if __riscv_xlen == 64 6 | # define LREG ld 7 | # define SREG sd 8 | # define REGBYTES 8 9 | #else 10 | # define LREG lw 11 | # define SREG sw 12 | # define REGBYTES 4 13 | #endif 14 | 15 | .section ".text.init" 16 | .globl _start 17 | _start: 18 | li x1, 0 19 | li x2, 0 20 | li x3, 0 21 | li x4, 0 22 | li x5, 0 23 | li x6, 0 24 | li x7, 0 25 | li x8, 0 26 | li x9, 0 27 | li x10,0 28 | li x11,0 29 | li x12,0 30 | li x13,0 31 | li x14,0 32 | li x15,0 33 | li x16,0 34 | li x17,0 35 | li x18,0 36 | li x19,0 37 | li x20,0 38 | li x21,0 39 | li x22,0 40 | li x23,0 41 | li x24,0 42 | li x25,0 43 | li x26,0 44 | li x27,0 45 | li x28,0 46 | li x29,0 47 | li x30,0 48 | li x31,0 49 | 50 | # enable FPU and accelerator if present 51 | li t0, MSTATUS_FS | MSTATUS_XS 52 | csrs mstatus, t0 53 | 54 | # make sure XLEN agrees with compilation choice 55 | li t0, 1 56 | slli t0, t0, 31 57 | #if __riscv_xlen == 64 58 | bgez t0, 1f 59 | #else 60 | bltz t0, 1f 61 | #endif 62 | 2: 63 | li a0, 1 64 | sw a0, tohost, t0 65 | j 2b 66 | 1: 67 | 68 | #ifdef __riscv_flen 69 | # initialize FPU if we have one 70 | la t0, 1f 71 | csrw mtvec, t0 72 | 73 | fssr x0 74 | fmv.s.x f0, x0 75 | fmv.s.x f1, x0 76 | fmv.s.x f2, x0 77 | fmv.s.x f3, x0 78 | fmv.s.x f4, x0 79 | fmv.s.x f5, x0 80 | fmv.s.x f6, x0 81 | fmv.s.x f7, x0 82 | fmv.s.x f8, x0 83 | fmv.s.x f9, x0 84 | fmv.s.x f10,x0 85 | fmv.s.x f11,x0 86 | fmv.s.x f12,x0 87 | fmv.s.x f13,x0 88 | fmv.s.x f14,x0 89 | fmv.s.x f15,x0 90 | fmv.s.x f16,x0 91 | fmv.s.x f17,x0 92 | fmv.s.x f18,x0 93 | fmv.s.x f19,x0 94 | fmv.s.x f20,x0 95 | fmv.s.x f21,x0 96 | fmv.s.x f22,x0 97 | fmv.s.x f23,x0 98 | fmv.s.x f24,x0 99 | fmv.s.x f25,x0 100 | fmv.s.x f26,x0 101 | fmv.s.x f27,x0 102 | fmv.s.x f28,x0 103 | fmv.s.x f29,x0 104 | fmv.s.x f30,x0 105 | fmv.s.x f31,x0 106 | 1: 107 | #endif 108 | 109 | # initialize trap vector 110 | la t0, trap_entry 111 | csrw mtvec, t0 112 | 113 | # initialize global pointer 114 | .option push 115 | .option norelax 116 | la gp, __global_pointer$ 117 | .option pop 118 | 119 | la tp, _end + 63 120 | and tp, tp, -64 121 | 122 | # get core id 123 | csrr a0, mhartid 124 | # for now, assume only 1 core 125 | li a1, 1 126 | 1:bgeu a0, a1, 1b 127 | 128 | # give each core 128KB of stack + TLS 129 | #define STKSHIFT 17 130 | sll a2, a0, STKSHIFT 131 | add tp, tp, a2 132 | add sp, a0, 1 133 | sll sp, sp, STKSHIFT 134 | add sp, sp, tp 135 | 136 | call main 137 | 138 | exit: 139 | slli a0, a0, 1 140 | ori a0, a0, 1 141 | 1: 142 | sw a0, tohost, t0 143 | j 1b 144 | 145 | .align 2 146 | trap_entry: 147 | csrr a0, mcause 148 | tail exit 149 | 150 | .section ".tohost","aw",@progbits 151 | .align 6 152 | .globl tohost 153 | tohost: .dword 0 154 | .align 6 155 | .globl fromhost 156 | fromhost: .dword 0 157 | -------------------------------------------------------------------------------- /test/custom-bmarks/mix.c: -------------------------------------------------------------------------------- 1 | int main(void) 2 | { 3 | /* TODO: Write your code here */ 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/custom-tests/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/sh 2 | 3 | CC := riscv32-unknown-elf-gcc -march=rv32i -mabi=ilp32 4 | OBJDUMP := riscv32-unknown-elf-objdump 5 | 6 | CFLAGS := -O2 -g -Wall -std=gnu99 -mcmodel=medany -fno-common -fno-builtin-printf \ 7 | -I ../env/p -I ../env/ 8 | LDFLAGS := -static -nostdlib -nostartfiles -T ../env/p/link.ld 9 | 10 | programs := movn yourinst 11 | bins := $(addprefix rv32ui-p-,$(programs)) 12 | dumps := $(addsuffix .dump,$(bins)) 13 | 14 | all: $(bins) 15 | dump: $(dumps) 16 | 17 | rv32ui-p-%: %.S 18 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< 19 | 20 | %.dump: % 21 | $(OBJDUMP) -d $< > $@ 22 | 23 | clean: 24 | rm -f -- $(bins) $(dumps) 25 | 26 | .PHONY: all dump clean 27 | .SUFFIXES: 28 | -------------------------------------------------------------------------------- /test/custom-tests/movn.S: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | #***************************************************************************** 4 | # movn.S 5 | #----------------------------------------------------------------------------- 6 | # 7 | # This is the most basic self checking test. If your simulator does not 8 | # pass thiss then there is little chance that it will pass any of the 9 | # more complicated self checking tests. 10 | # 11 | 12 | #include "riscv_test.h" 13 | #include "test_macros.h" 14 | 15 | // The TEST_CASE macro sets up a single assembly test. 16 | // 1. The first argument, testnum, is the number that gets returned if the 17 | // test fails. You should use a unique testnum for each test. 18 | // 2. The second argument is the register that you expect the result to 19 | // to be written to. In this case, it's x1. 20 | // 3. The third argument is the value you expect the result to be. 21 | // The test will fail if the value in the register does not match. 22 | // 4. The final argument is the sequence of assembly instructions for 23 | // the test. The first three instructions set up the destination 24 | // and source registers. The last instruction is the custom instruction 25 | // we actually want to test. Since the assembler won't recognize it, 26 | // we have to manually encode it using ".word". 27 | #define TEST_MOVN( testnum, result, init, a, b ) \ 28 | TEST_CASE( testnum, x1, result, \ 29 | li x1, MASK_XLEN(init); \ 30 | li x2, MASK_XLEN(a); \ 31 | li x3, MASK_XLEN(b); \ 32 | .word 0x003100f7) 33 | 34 | RVTEST_RV64U 35 | RVTEST_CODE_BEGIN 36 | 37 | // Test that rd is left unchanged if rs2 is 0 38 | TEST_MOVN( 2, 0, 0, 1, 0 ); 39 | // Test that rd = rs1 if rs2 is not 0 40 | TEST_MOVN( 3, 1, 0, 1, 1 ); 41 | 42 | TEST_PASSFAIL 43 | 44 | RVTEST_CODE_END 45 | 46 | .data 47 | RVTEST_DATA_BEGIN 48 | 49 | TEST_DATA 50 | 51 | RVTEST_DATA_END 52 | -------------------------------------------------------------------------------- /test/custom-tests/yourinst.S: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | #***************************************************************************** 4 | # yourinst.S 5 | #----------------------------------------------------------------------------- 6 | # 7 | # This is the most basic self checking test. If your simulator does not 8 | # pass thiss then there is little chance that it will pass any of the 9 | # more complicated self checking tests. 10 | # 11 | 12 | #include "riscv_test.h" 13 | #include "test_macros.h" 14 | 15 | RVTEST_RV64U 16 | RVTEST_CODE_BEGIN 17 | 18 | # FIXME: Add your tests here 19 | # See the tests in movn.S for examples 20 | 21 | TEST_PASSFAIL 22 | 23 | RVTEST_CODE_END 24 | 25 | .data 26 | RVTEST_DATA_BEGIN 27 | 28 | TEST_DATA 29 | 30 | RVTEST_DATA_END 31 | -------------------------------------------------------------------------------- /test/env/p/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | ENTRY(_start) 3 | 4 | SECTIONS 5 | { 6 | . = 0x80000000; 7 | .text.init : { *(.text.init) } 8 | . = ALIGN(0x1000); 9 | .tohost : { *(.tohost) } 10 | . = ALIGN(0x1000); 11 | .text : { *(.text) } 12 | . = ALIGN(0x1000); 13 | .data : { *(.data) } 14 | .bss : { *(.bss) } 15 | _end = .; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /test/env/p/riscv_test.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #ifndef _ENV_PHYSICAL_SINGLE_CORE_H 4 | #define _ENV_PHYSICAL_SINGLE_CORE_H 5 | 6 | #include "../encoding.h" 7 | 8 | //----------------------------------------------------------------------- 9 | // Begin Macro 10 | //----------------------------------------------------------------------- 11 | 12 | #define RVTEST_RV64U \ 13 | .macro init; \ 14 | .endm 15 | 16 | #define RVTEST_RV64UF \ 17 | .macro init; \ 18 | RVTEST_FP_ENABLE; \ 19 | .endm 20 | 21 | #define RVTEST_RV32U \ 22 | .macro init; \ 23 | .endm 24 | 25 | #define RVTEST_RV32UF \ 26 | .macro init; \ 27 | RVTEST_FP_ENABLE; \ 28 | .endm 29 | 30 | #define RVTEST_RV64M \ 31 | .macro init; \ 32 | RVTEST_ENABLE_MACHINE; \ 33 | .endm 34 | 35 | #define RVTEST_RV64S \ 36 | .macro init; \ 37 | RVTEST_ENABLE_SUPERVISOR; \ 38 | .endm 39 | 40 | #define RVTEST_RV32M \ 41 | .macro init; \ 42 | RVTEST_ENABLE_MACHINE; \ 43 | .endm 44 | 45 | #define RVTEST_RV32S \ 46 | .macro init; \ 47 | RVTEST_ENABLE_SUPERVISOR; \ 48 | .endm 49 | 50 | #if __riscv_xlen == 64 51 | # define CHECK_XLEN li a0, 1; slli a0, a0, 31; bgez a0, 1f; RVTEST_PASS; 1: 52 | #else 53 | # define CHECK_XLEN li a0, 1; slli a0, a0, 31; bltz a0, 1f; RVTEST_PASS; 1: 54 | #endif 55 | 56 | #define INIT_PMP \ 57 | la t0, 1f; \ 58 | csrw mtvec, t0; \ 59 | li t0, -1; /* Set up a PMP to permit all accesses */ \ 60 | csrw pmpaddr0, t0; \ 61 | li t0, PMP_NAPOT | PMP_R | PMP_W | PMP_X; \ 62 | csrw pmpcfg0, t0; \ 63 | .align 2; \ 64 | 1: 65 | 66 | #define INIT_SATP \ 67 | la t0, 1f; \ 68 | csrw mtvec, t0; \ 69 | csrwi sptbr, 0; \ 70 | .align 2; \ 71 | 1: 72 | 73 | #define DELEGATE_NO_TRAPS \ 74 | la t0, 1f; \ 75 | csrw mtvec, t0; \ 76 | csrwi medeleg, 0; \ 77 | csrwi mideleg, 0; \ 78 | csrwi mie, 0; \ 79 | .align 2; \ 80 | 1: 81 | 82 | #define RVTEST_ENABLE_SUPERVISOR \ 83 | li a0, MSTATUS_MPP & (MSTATUS_MPP >> 1); \ 84 | csrs mstatus, a0; \ 85 | li a0, SIP_SSIP | SIP_STIP; \ 86 | csrs mideleg, a0; \ 87 | 88 | #define RVTEST_ENABLE_MACHINE \ 89 | li a0, MSTATUS_MPP; \ 90 | csrs mstatus, a0; \ 91 | 92 | #define RVTEST_FP_ENABLE \ 93 | li a0, MSTATUS_FS & (MSTATUS_FS >> 1); \ 94 | csrs mstatus, a0; \ 95 | csrwi fcsr, 0 96 | 97 | #define RISCV_MULTICORE_DISABLE \ 98 | csrr a0, mhartid; \ 99 | 1: bnez a0, 1b 100 | 101 | #define EXTRA_TVEC_USER 102 | #define EXTRA_TVEC_MACHINE 103 | #define EXTRA_INIT 104 | #define EXTRA_INIT_TIMER 105 | 106 | #define INTERRUPT_HANDLER j other_exception /* No interrupts should occur */ 107 | 108 | #define RVTEST_CODE_BEGIN \ 109 | .section .text.init; \ 110 | .align 6; \ 111 | .weak stvec_handler; \ 112 | .weak mtvec_handler; \ 113 | .globl _start; \ 114 | _start: \ 115 | /* reset vector */ \ 116 | j reset_vector; \ 117 | .align 2; \ 118 | trap_vector: \ 119 | /* test whether the test came from pass/fail */ \ 120 | csrr t5, mcause; \ 121 | li t6, CAUSE_USER_ECALL; \ 122 | beq t5, t6, write_tohost; \ 123 | li t6, CAUSE_SUPERVISOR_ECALL; \ 124 | beq t5, t6, write_tohost; \ 125 | li t6, CAUSE_MACHINE_ECALL; \ 126 | beq t5, t6, write_tohost; \ 127 | /* if an mtvec_handler is defined, jump to it */ \ 128 | la t5, mtvec_handler; \ 129 | beqz t5, 1f; \ 130 | jr t5; \ 131 | /* was it an interrupt or an exception? */ \ 132 | 1: csrr t5, mcause; \ 133 | bgez t5, handle_exception; \ 134 | INTERRUPT_HANDLER; \ 135 | handle_exception: \ 136 | /* we don't know how to handle whatever the exception was */ \ 137 | other_exception: \ 138 | /* some unhandlable exception occurred */ \ 139 | 1: ori TESTNUM, TESTNUM, 1337; \ 140 | write_tohost: \ 141 | sw TESTNUM, tohost, t5; \ 142 | j write_tohost; \ 143 | reset_vector: \ 144 | RISCV_MULTICORE_DISABLE; \ 145 | INIT_SATP; \ 146 | INIT_PMP; \ 147 | DELEGATE_NO_TRAPS; \ 148 | li TESTNUM, 0; \ 149 | la t0, trap_vector; \ 150 | csrw mtvec, t0; \ 151 | CHECK_XLEN; \ 152 | /* if an stvec_handler is defined, delegate exceptions to it */ \ 153 | la t0, stvec_handler; \ 154 | beqz t0, 1f; \ 155 | csrw stvec, t0; \ 156 | li t0, (1 << CAUSE_LOAD_PAGE_FAULT) | \ 157 | (1 << CAUSE_STORE_PAGE_FAULT) | \ 158 | (1 << CAUSE_FETCH_PAGE_FAULT) | \ 159 | (1 << CAUSE_MISALIGNED_FETCH) | \ 160 | (1 << CAUSE_USER_ECALL) | \ 161 | (1 << CAUSE_BREAKPOINT); \ 162 | csrw medeleg, t0; \ 163 | csrr t1, medeleg; \ 164 | bne t0, t1, other_exception; \ 165 | 1: csrwi mstatus, 0; \ 166 | init; \ 167 | EXTRA_INIT; \ 168 | EXTRA_INIT_TIMER; \ 169 | la t0, 1f; \ 170 | csrw mepc, t0; \ 171 | csrr a0, mhartid; \ 172 | mret; \ 173 | 1: 174 | 175 | //----------------------------------------------------------------------- 176 | // End Macro 177 | //----------------------------------------------------------------------- 178 | 179 | #define RVTEST_CODE_END \ 180 | unimp 181 | 182 | //----------------------------------------------------------------------- 183 | // Pass/Fail Macro 184 | //----------------------------------------------------------------------- 185 | 186 | #define RVTEST_PASS \ 187 | fence; \ 188 | li TESTNUM, 1; \ 189 | ecall 190 | 191 | #define TESTNUM gp 192 | #define RVTEST_FAIL \ 193 | fence; \ 194 | 1: beqz TESTNUM, 1b; \ 195 | sll TESTNUM, TESTNUM, 1; \ 196 | or TESTNUM, TESTNUM, 1; \ 197 | ecall 198 | 199 | //----------------------------------------------------------------------- 200 | // Data Section Macro 201 | //----------------------------------------------------------------------- 202 | 203 | #define EXTRA_DATA 204 | 205 | #define RVTEST_DATA_BEGIN \ 206 | EXTRA_DATA \ 207 | .pushsection .tohost,"aw",@progbits; \ 208 | .align 6; .global tohost; tohost: .dword 0; \ 209 | .align 6; .global fromhost; fromhost: .dword 0; \ 210 | .popsection; \ 211 | .align 4; .global begin_signature; begin_signature: 212 | 213 | #define RVTEST_DATA_END .align 4; .global end_signature; end_signature: 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /test/env/test.ld: -------------------------------------------------------------------------------- 1 | /*======================================================================*/ 2 | /* Proxy kernel linker script */ 3 | /*======================================================================*/ 4 | /* This is the linker script used when building the proxy kernel. */ 5 | 6 | /*----------------------------------------------------------------------*/ 7 | /* Setup */ 8 | /*----------------------------------------------------------------------*/ 9 | 10 | /* The OUTPUT_ARCH command specifies the machine architecture where the 11 | argument is one of the names used in the BFD library. More 12 | specifically one of the entires in bfd/cpu-mips.c */ 13 | 14 | OUTPUT_ARCH( "riscv" ) 15 | ENTRY(_start) 16 | 17 | /*----------------------------------------------------------------------*/ 18 | /* Sections */ 19 | /*----------------------------------------------------------------------*/ 20 | 21 | SECTIONS 22 | { 23 | 24 | /* text: test code section */ 25 | . = 0x80000000; 26 | .text.init : { *(.text.init) } 27 | 28 | . = ALIGN(0x1000); 29 | .tohost : { *(.tohost) } 30 | 31 | .text : { *(.text) } 32 | 33 | /* data segment */ 34 | .data : { *(.data) } 35 | 36 | .sdata : { 37 | __global_pointer$ = . + 0x800; 38 | *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) 39 | *(.sdata .sdata.* .gnu.linkonce.s.*) 40 | } 41 | 42 | /* bss segment */ 43 | .sbss : { 44 | *(.sbss .sbss.* .gnu.linkonce.sb.*) 45 | *(.scommon) 46 | } 47 | .bss : { *(.bss) } 48 | 49 | /* thread-local data segment */ 50 | .tdata : 51 | { 52 | _tdata_begin = .; 53 | *(.tdata) 54 | _tdata_end = .; 55 | } 56 | .tbss : 57 | { 58 | *(.tbss) 59 | _tbss_end = .; 60 | } 61 | 62 | /* End of uninitalized data segement */ 63 | _end = .; 64 | } 65 | 66 | --------------------------------------------------------------------------------