{"id":13643310,"url":"https://github.com/firesim/firechip","last_synced_at":"2026-05-30T21:30:19.429Z","repository":{"id":96083921,"uuid":"103165424","full_name":"firesim/firechip","owner":"firesim","description":"Top-Level Project for Firebox SoC, consisting of Rocket, BOOM, and peripherals (e.g. Ethernet NIC). This is the default target generator used in FireSim.","archived":false,"fork":false,"pushed_at":"2019-11-24T22:24:15.000Z","size":1260,"stargazers_count":56,"open_issues_count":2,"forks_count":31,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-04-21T01:44:25.296Z","etag":null,"topics":["risc-v","rocket-chip","soc"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/firesim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-09-11T17:17:22.000Z","updated_at":"2024-08-27T14:46:31.000Z","dependencies_parsed_at":"2023-04-23T11:47:13.422Z","dependency_job_id":null,"html_url":"https://github.com/firesim/firechip","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/firesim/firechip","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firesim%2Ffirechip","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firesim%2Ffirechip/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firesim%2Ffirechip/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firesim%2Ffirechip/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/firesim","download_url":"https://codeload.github.com/firesim/firechip/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firesim%2Ffirechip/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33711018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["risc-v","rocket-chip","soc"],"created_at":"2024-08-02T01:01:45.654Z","updated_at":"2026-05-30T21:30:19.413Z","avatar_url":"https://github.com/firesim.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"# RISC-V Project Template\n\nThis is a starter template for your custom RISC-V project. It will allow you\nto leverage the Chisel HDL and RocketChip SoC generator to produce a\nRISC-V SoC with MMIO-mapped peripherals, DMA, and custom accelerators.\n\n## Getting started\n\n### Checking out the sources\n\nAfter cloning this repo, you will need to initialize all of the submodules\n\n    git clone https://github.com/ucb-bar/project-template.git\n    cd project-template\n    git submodule update --init --recursive\n\n### Building the tools\n\nThe tools repo contains the cross-compiler toolchain, frontend server, and\nproxy kernel, which you will need in order to compile code to RISC-V\ninstructions and run them on your design. There are detailed instructions at\nhttps://github.com/riscv/riscv-tools. But to get a basic installation, just\nthe following steps are necessary.\n\n    # You may want to add the following two lines to your shell profile\n    export RISCV=/path/to/install/dir\n    export PATH=$RISCV/bin:$PATH\n\n    cd riscv-tools\n    ./build.sh\n\n### Compiling and running the Verilator simulation\n\nTo compile the example design, run make in the \"verisim\" directory.\nThis will elaborate the DefaultExampleConfig in the example project.\n\nAn executable called simulator-example-DefaultExampleConfig will be produced.\nYou can then use this executable to run any compatible RV64 code. For instance,\nto run one of the riscv-tools assembly tests.\n\n    ./simulator-example-DefaultExampleConfig $RISCV/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-simple\n\nIf you later create your own project, you can use environment variables to\nbuild an alternate configuration.\n\n    make PROJECT=yourproject CONFIG=YourConfig\n    ./simulator-yourproject-YourConfig ...\n\n## Submodules and Subdirectories\n\nThe submodules and subdirectories for the project template are organized as\nfollows.\n\n * rocket-chip - contains code for the RocketChip generator and Chisel HDL\n * testchipip - contains the serial adapter, block device, and associated verilog and C++ code\n * verisim - directory in which Verilator simulations are compiled and run\n * vsim - directory in which Synopsys VCS simulations are compiled and run\n * bootrom - sources for the first-stage bootloader included in the Boot ROM\n * src/main/scala - scala source files for your project go here\n\n## Using the block device\n\nThe default example project just provides the Rocket subsystem, memory, and\nserial line. But testchipip also provides a simulated block device that can\nbe used for non-volatile storage. You can build a simulator including the\nblock device using the blkdev package.\n\n    make PROJECT=blkdev CONFIG=BlockDeviceConfig\n    ./simulator-blkdev-BlockDeviceConfig +blkdev=block-device.img ...\n\nBy passing the +blkdev argument on the simulator command line, you can allow\nthe RTL simulation to read and write from a file. Take a look at tests/blkdev.c\nfor an example of how Rocket can program the block device controller.\n\n## Using the network device\n\nTestchipip also includes a basic ethernet controller (SimpleNIC). The simulator\nprovides a way to connect this up to a tap interface and thus interact with\nrocketchip as if it was a regular network node.\n\nFirst set up the tap interface. If you want to run the simulation as a regular\nuser (recommended), use the following commands.\n\n    sudo ip tuntap add mode tap dev tap0 user $USER\n    sudo ip link set tap0 up\n    sudo ip addr add 192.168.1.1/24 dev tap0\n\nThen build the SimNetworkConfig and pass it the name of the tap interface\n\n    make CONFIG=SimNetworkConfig\n    ./simulator-example-SimNetworkConfig +netdev=tap0 ../tests/pingd.riscv\n\nThen run ping in a separate terminal.\n\n    ping 192.168.1.2\n\nYou should now see the ping responses come back. The `pingd.riscv` program\nwill also log each packet it receives.\n\n## Adding an MMIO peripheral\n\nYou can RocketChip to create your own memory-mapped IO device and add it into\nthe SoC design. The easiest way to create a TileLink peripheral is to use the\nTLRegisterRouter, which abstracts away the details of handling the TileLink\nprotocol and provides a convenient interface for specifying memory-mapped\nregisters. To create a RegisterRouter-based peripheral, you will need to\nspecify a parameter case class for the configuration settings, a bundle trait\nwith the extra top-level ports, and a module implementation containing the\nactual RTL.\n\n```scala\n    case class PWMParams(address: BigInt, beatBytes: Int)\n\n    trait PWMTLBundle extends Bundle {\n      val pwmout = Output(Bool())\n    }\n\n    trait PWMTLModule {\n      val io: PWMTLBundle\n      implicit val p: Parameters\n      def params: PWMParams\n\n      val w = params.beatBytes * 8\n      val period = Reg(UInt(w.W))\n      val duty = Reg(UInt(w.W))\n      val enable = RegInit(false.B)\n\n      // ... Use the registers to drive io.pwmout ...\n\n      regmap(\n        0x00 -\u003e Seq(\n          RegField(w, period)),\n        0x04 -\u003e Seq(\n          RegField(w, duty)),\n        0x08 -\u003e Seq(\n          RegField(1, enable)))\n    }\n```\n\nOnce you have these classes, you can construct the final peripheral by\nextending the TLRegisterRouter and passing the proper arguments. The first\nset of arguments determines where the register router will be placed in the\nglobal address map and what information will be put in its device tree entry.\nThe second set of arguments is the IO bundle constructor, which we create\nby extending TLRegBundle with our bundle trait. The final set of arguments\nis the module constructor, which we create by extends TLRegModule with our\nmodule trait.\n\n```scala\n    class PWMTL(c: PWMParams)(implicit p: Parameters)\n      extends TLRegisterRouter(\n        c.address, \"pwm\", Seq(\"ucbbar,pwm\"),\n        beatBytes = c.beatBytes)(\n          new TLRegBundle(c, _) with PWMTLBundle)(\n          new TLRegModule(c, _, _) with PWMTLModule)\n```\n\nThe full module code with comments can be found in src/main/scala/example/PWM.scala.\n\nAfter creating the module, we need to hook it up to our SoC. Rocketchip\naccomplishes this using the [cake pattern](http://www.cakesolutions.net/teamblogs/2011/12/19/cake-pattern-in-depth).\nThis basically involves placing code inside traits. In the RocketChip cake,\nthere are two kinds of traits: a LazyModule trait and a module implementation\ntrait.\n\nThe LazyModule trait runs setup code that must execute before all the hardware\ngets elaborated. For a simple memory-mapped peripheral, this just involves\nconnecting the peripheral's TileLink node to the MMIO crossbar.\n\n```scala\n    trait HasPeripheryPWM extends HasSystemNetworks {\n      implicit val p: Parameters\n\n      private val address = 0x2000\n\n      val pwm = LazyModule(new PWMTL(\n        PWMParams(address, peripheryBusConfig.beatBytes))(p))\n\n      pwm.node := TLFragmenter(\n        peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)\n    }\n```\n\nNote that the PWMTL class we created from the register router is itself a\nLazyModule. Register routers have a TileLike node simply named \"node\", which\nwe can hook up to the RocketChip peripheryBus. This will automatically add\naddress map and device tree entries for the peripheral.\n\nThe module implementation trait is where we instantiate our PWM module and\nconnect it to the rest of the SoC. Since this module has an extra `pwmout`\noutput, we declare that in this trait, using Chisel's multi-IO\nfunctionality. We then connect the PWMTL's pwmout to the pwmout we declared.\n\n```scala\n    trait HasPeripheryPWMModuleImp extends LazyMultiIOModuleImp {\n      implicit val p: Parameters\n      val outer: HasPeripheryPWM\n\n      val pwmout = IO(Output(Bool()))\n\n      pwmout := outer.pwm.module.io.pwmout\n    }\n```\n\nNow we want to mix our traits into the system as a whole. This code is from\nsrc/main/scala/example/Top.scala.\n\n```scala\n    class ExampleTopWithPWM(q: Parameters) extends ExampleTop(q)\n        with PeripheryPWM {\n      override lazy val module = Module(\n        new ExampleTopWithPWMModule(p, this))\n    }\n\n    class ExampleTopWithPWMModule(l: ExampleTopWithPWM)\n      extends ExampleTopModule(l) with HasPeripheryPWMModuleImp\n```\n\nJust as we need separate traits for LazyModule and module implementation, we\nneed two classes to build the system. The ExampleTop classes already have the\nbasic peripherals included for us, so we will just extend those.\n\nThe ExampleTop class includes the pre-elaboration code and also a lazy val to\nproduce the module implementation (hence LazyModule). The ExampleTopModule\nclass is the actual RTL that gets synthesized.\n\nFinally, we need to add a configuration class in\nsrc/main/scala/example/Configs.scala that tells the TestHarness to instantiate\nExampleTopWithPWM instead of the default ExampleTop.\n\n```scala\n    class WithPWM extends Config((site, here, up) =\u003e {\n      case BuildTop =\u003e (p: Parameters) =\u003e\n        Module(LazyModule(new ExampleTopWithPWM()(p)).module)\n    })\n\n    class PWMConfig extends Config(new WithPWM ++ new BaseExampleConfig)\n```\n\nNow we can test that the PWM is working. The test program is in tests/pwm.c\n\n```c\n    #define PWM_PERIOD 0x2000\n    #define PWM_DUTY 0x2008\n    #define PWM_ENABLE 0x2010\n\n    static inline void write_reg(unsigned long addr, unsigned long data)\n    {\n            volatile unsigned long *ptr = (volatile unsigned long *) addr;\n            *ptr = data;\n    }\n\n    static inline unsigned long read_reg(unsigned long addr)\n    {\n            volatile unsigned long *ptr = (volatile unsigned long *) addr;\n            return *ptr;\n    }\n\n    int main(void)\n    {\n            write_reg(PWM_PERIOD, 20);\n            write_reg(PWM_DUTY, 5);\n            write_reg(PWM_ENABLE, 1);\n    }\n```\n\nThis just writes out to the registers we defined earlier. The base of the\nmodule's MMIO region is at 0x2000. This will be printed out in the address\nmap portion when you generated the verilog code.\n\nCompiling this program with make produces a `pwm.riscv` executable.\n\nNow with all of that done, we can go ahead and run our simulation.\n\n    cd verisim\n    make PROJECT=pwm CONFIG=PWMConfig\n    ./simulator-pwm-PWMConfig ../tests/pwm.riscv\n\n## Adding a DMA port\n\nIn the example above, we gave allowed the processor to communicate with the\nperipheral through MMIO. However, for IO devices (like a disk or network\ndriver), we may want to have the device write directly to the coherent\nmemory system instead. To add a device like that, you would do the following.\n\n```scala\n    class DMADevice(implicit p: Parameters) extends LazyModule {\n      val node = TLClientNode(TLClientParameters(\n        name = \"dma-device\", sourceId = IdRange(0, 1)))\n\n      lazy val module = new DMADeviceModule(this)\n    }\n\n    class DMADeviceModule(outer: DMADevice) extends LazyModuleImp(outer) {\n      val io = IO(new Bundle {\n        val mem = outer.node.bundleOut\n        val ext = new ExtBundle\n      })\n\n      // ... rest of the code ...\n    }\n\n    trait HasPeripheryDMA extends HasSystemNetworks {\n      implicit val p: Parameters\n\n      val dma = LazyModule(new DMADevice)\n\n      fsb.node := dma.node\n    }\n\n    trait HasPeripheryDMAModuleImp extends LazyMultiIOModuleImp {\n      val ext = IO(new ExtBundle)\n      ext \u003c\u003e outer.dma.module.io.ext\n    }\n```\n\nThe `ExtBundle` contains the signals we connect off-chip that we get data from.\nThe DMADevice also has a Tilelink client port that we connect into the L1-L2\ncrossbar through the front-side buffer (fsb). The sourceId variable given in\nthe TLClientNode instantiation determines the range of ids that can be used\nin acquire messages from this device. Since we specified [0, 1) as our range,\nonly the ID 0 can be used.\n\n## Adding a RoCC accelerator\n\nBesides peripheral devices, a RocketChip-based SoC can also be customized with\ncoprocessor accelerators. Each core can have up to four accelerators that\nare controlled by custom instructions and share resources with the CPU.\n\n### A RoCC instruction\n\nCoprocessor instructions have the following form.\n\n    customX rd, rs1, rs2, funct\n\nThe X will be a number 0-3, and determines the opcode of the instruction,\nwhich controls which accelerator an instruction will be routed to.\nThe `rd`, `rs1`, and `rs2` fields are the register numbers of the destination\nregister and two source registers. The `funct` field is a 7-bit integer that\nthe accelerator can use to distinguish different instructions from each other.\n\n### Creating an accelerator\n\nRoCC accelerators are lazy modules that extend the LazyRoCC class.\nTheir implementation should extends the LazyRoCCModule class.\n\n```scala\n    class CustomAccelerator(implicit p: Parameters) extends LazyRoCC {\n      override lazy val module = new CustomAcceleratorModule(this)\n    }\n\n    class CustomAcceleratorModule(outer: CustomAccelerator) extends LazyRoCCModule(outer) {\n      val cmd = Queue(io.cmd)\n      // The parts of the command are as follows\n      // inst - the parts of the instruction itself\n      //   opcode\n      //   rd - destination register number\n      //   rs1 - first source register number\n      //   rs2 - second source register number\n      //   funct\n      //   xd - is the destination register being used?\n      //   xs1 - is the first source register being used?\n      //   xs2 - is the second source register being used?\n      // rs1 - the value of source register 1\n      // rs2 - the value of source register 2\n      ...\n    }\n```\n\nThe LazyRoCC class contains two TLOutputNode instances, `atlNode` and `tlNode`.\nThe former connects into a tile-local arbiter along with the backside of the\nL1 instruction cache. The latter connects directly to the L1-L2 crossbar.\nThe corresponding Tilelink ports in the module implementation's IO bundle\nare `atl` and `tl`, respectively.\n\nThe other interfaces available to the accelerator are `mem`, which provides\naccess to the L1 cache; `ptw` which provides access to the page-table walker;\nthe `busy` signal, which indicates when the accelerator is still handling an\ninstruction; and the `interrupt` signal, which can be used to interrupt the CPU.\n\nLook at the examples in rocket-chip/src/main/scala/tile/LazyRocc.scala for\ndetailed information on the different IOs.\n\n### Adding RoCC accelerator to Config\n\nRoCC accelerators can be added to a core by overriding the BuildRoCC parameter\nin the configuration. This takes a sequence of RoccParameters objects, one\nfor each accelerator you wish to add. The two required fields for this\nobject are `opcodes` which determines which custom opcodes get routed to the\naccelerator, and `generator` which specifies how to build the accelerator itself.\nFor instance, if we wanted to add the previously defined accelerator and\nroute custom0 and custom1 instructions to it, we could do the following.\n\n```scala\n    class WithCustomAccelerator extends Config((site, here, up) =\u003e {\n      case RocketTilesKey =\u003e up(RocketTilesKey, site).map { r =\u003e\n        r.copy(rocc = Seq(\n          RoCCParams(\n            opcodes = OpcodeSet.custom0 | OpcodeSet.custom1,\n            generator = (p: Parameters) =\u003e LazyModule(new CustomAccelerator()(p)))))\n      }\n    })\n\n    class CustomAcceleratorConfig extends Config(\n      new WithCustomAccelerator ++ new BaseConfig)\n```\n\n## Adding a submodule\n\nWhile developing, you want to include Chisel code in a submodule so that it\ncan be shared by different projects. To add a submodule to the project\ntemplate, make sure that your project is organized as follows.\n\n    yourproject/\n        build.sbt\n        src/main/scala/\n            YourFile.scala\n\nPut this in a git repository and make it accessible. Then add it as a submodule\nto the project template.\n\n    git submodule add https://git-repository.com/yourproject.git\n\nThen add `yourproject` to the `EXTRA_PACKAGES` variable in the Makefrag.\nNow your project will be bundled into a jar file alongside the rocket-chip\nand testchipip libraries. You can then import the classes defined in the\nsubmodule in a new project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiresim%2Ffirechip","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiresim%2Ffirechip","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiresim%2Ffirechip/lists"}