├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile.x300arty100devkit ├── Makefile.x300arty35devkit ├── README.md ├── bootrom └── xip │ ├── Makefile │ └── xip.S ├── build.sbt ├── common.mk ├── fpga-shells ├── .gitignore ├── src │ └── main │ │ └── scala │ │ ├── clocks │ │ ├── Bundles.scala │ │ ├── ClockGroup.scala │ │ ├── Nodes.scala │ │ ├── PLLFactory.scala │ │ ├── Parameters.scala │ │ └── ResetWrangler.scala │ │ ├── devices │ │ └── xilinx │ │ │ ├── xdma │ │ │ ├── XDMA.scala │ │ │ └── package.scala │ │ │ └── xilinxethernetlite │ │ │ ├── XilinxEthernetLite.scala │ │ │ └── XilinxEthernetLitePeriphery.scala │ │ ├── ip │ │ └── xilinx │ │ │ ├── Unisim.scala │ │ │ ├── Xilinx.scala │ │ │ ├── ethernetlite │ │ │ └── ethernetlite.scala │ │ │ ├── ibufds_gte2 │ │ │ └── ibufds_gte2.scala │ │ │ └── xdma │ │ │ └── xdma.scala │ │ └── shell │ │ ├── ChipLinkOverlay.scala │ │ ├── ClockOverlay.scala │ │ ├── DDROverlay.scala │ │ ├── IOShell.scala │ │ ├── JTAGDebugOverlay.scala │ │ ├── LEDOverlay.scala │ │ ├── PCIeOverlay.scala │ │ ├── SDIOOverlay.scala │ │ ├── Shell.scala │ │ ├── SwitchOverlay.scala │ │ ├── UARTOverlay.scala │ │ ├── package.scala │ │ └── xilinx │ │ ├── ArtyShell.scala │ │ ├── ChipLinkOverlay.scala │ │ ├── ClockOverlay.scala │ │ ├── JTAGDebugOverlay.scala │ │ ├── LEDOverlay.scala │ │ ├── SDIOOverlay.scala │ │ ├── SwitchOverlay.scala │ │ ├── UARTOverlay.scala │ │ └── XilinxShell.scala └── xilinx │ ├── Makefile │ ├── arty │ ├── constraints │ │ ├── arty-config.xdc │ │ └── arty-master.xdc │ └── tcl │ │ ├── board.tcl │ │ └── ip.tcl │ ├── arty_a7_100 │ ├── constraints │ │ ├── arty-config.xdc │ │ └── arty-master.xdc │ └── tcl │ │ ├── board.tcl │ │ └── ip.tcl │ └── common │ ├── tcl │ ├── bitstream.tcl │ ├── boards.tcl │ ├── init.tcl │ ├── opt.tcl │ ├── place.tcl │ ├── prologue.tcl │ ├── report.tcl │ ├── route.tcl │ ├── synth.tcl │ ├── util.tcl │ ├── vivado.tcl │ └── write_cfgmem.tcl │ └── vsrc │ └── PowerOnResetFPGAOnly.v ├── sifive-blocks ├── .gitignore ├── LICENSE ├── src │ └── main │ │ ├── resources │ │ └── BlackBoxDelayBuffer.v │ │ └── scala │ │ ├── devices │ │ ├── chiplink │ │ │ ├── Bundles.scala │ │ │ ├── CAM.scala │ │ │ ├── ChipLink.scala │ │ │ ├── Parameters.scala │ │ │ ├── Partial.scala │ │ │ ├── RX.scala │ │ │ ├── SinkA.scala │ │ │ ├── SinkB.scala │ │ │ ├── SinkC.scala │ │ │ ├── SinkD.scala │ │ │ ├── SinkE.scala │ │ │ ├── SourceA.scala │ │ │ ├── SourceB.scala │ │ │ ├── SourceC.scala │ │ │ ├── SourceD.scala │ │ │ ├── SourceE.scala │ │ │ └── TX.scala │ │ ├── gpio │ │ │ ├── GPIO.scala │ │ │ ├── GPIOCtrlRegs.scala │ │ │ ├── GPIOPeriphery.scala │ │ │ ├── GPIOPins.scala │ │ │ └── IOF.scala │ │ ├── i2c │ │ │ ├── I2C.scala │ │ │ ├── I2CCtrlRegs.scala │ │ │ ├── I2CPeriphery.scala │ │ │ └── I2CPins.scala │ │ ├── jtag │ │ │ └── JTAGPins.scala │ │ ├── mockaon │ │ │ ├── MockAON.scala │ │ │ ├── MockAONPeriphery.scala │ │ │ ├── MockAONWrapper.scala │ │ │ ├── PMU.scala │ │ │ ├── RTC.scala │ │ │ └── WatchdogTimer.scala │ │ ├── msi │ │ │ └── MSIMaster.scala │ │ ├── pinctrl │ │ │ └── PinCtrl.scala │ │ ├── pwm │ │ │ ├── PWM.scala │ │ │ ├── PWMPeriphery.scala │ │ │ └── PWMPins.scala │ │ ├── spi │ │ │ ├── BlackBoxDelayBuffer.scala │ │ │ ├── SPI.scala │ │ │ ├── SPIArbiter.scala │ │ │ ├── SPIBundle.scala │ │ │ ├── SPIConsts.scala │ │ │ ├── SPIFIFO.scala │ │ │ ├── SPIFlash.scala │ │ │ ├── SPIMedia.scala │ │ │ ├── SPIPeriphery.scala │ │ │ ├── SPIPhysical.scala │ │ │ ├── SPIPins.scala │ │ │ ├── SPIRegs.scala │ │ │ ├── TLSPI.scala │ │ │ └── TLSPIFlash.scala │ │ ├── stream │ │ │ ├── PseudoStream.scala │ │ │ └── PseudoStreamRegs.scala │ │ └── uart │ │ │ ├── UART.scala │ │ │ ├── UARTCtrlRegs.scala │ │ │ ├── UARTPeriphery.scala │ │ │ ├── UARTPins.scala │ │ │ ├── UARTRx.scala │ │ │ └── UARTTx.scala │ │ ├── ip │ │ └── xilinx │ │ │ └── ibufds_gte2 │ │ │ └── ibufds_gte2.scala │ │ └── util │ │ ├── DeglitchShiftRegister.scala │ │ ├── RegMapFIFO.scala │ │ ├── SRLatch.scala │ │ ├── SlaveRegIF.scala │ │ └── Timer.scala └── vsrc │ └── SRLatch.v └── src └── main └── scala └── x300artydevkit ├── Config.scala ├── FPGAChip.scala ├── Platform.scala └── System.scala /.gitignore: -------------------------------------------------------------------------------- 1 | /builds 2 | 3 | # sbt directories 4 | /target 5 | /project/target 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rocket-chip"] 2 | path = rocket-chip 3 | url = https://github.com/ucb-bar/rocket-chip.git 4 | -------------------------------------------------------------------------------- /Makefile.x300arty100devkit: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # Modifications copyright (C) 2019 Hex-Five 3 | base_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) 4 | BUILD_DIR := $(base_dir)/builds/x300arty100devkit 5 | FPGA_DIR := $(base_dir)/fpga-shells/xilinx 6 | MODEL := X300ArtyDevKitFPGAChip 7 | PROJECT := hexfive.x300artydevkit 8 | export CONFIG_PROJECT := hexfive.x300artydevkit 9 | export CONFIG := X300ArtyDevKitConfig 10 | export BOARD := arty_a7_100 11 | export BOOTROM_DIR := $(base_dir)/bootrom/xip 12 | 13 | rocketchip_dir := $(base_dir)/rocket-chip 14 | sifiveblocks_dir := $(base_dir)/sifive-blocks 15 | VSRCS := \ 16 | $(rocketchip_dir)/src/main/resources/vsrc/AsyncResetReg.v \ 17 | $(rocketchip_dir)/src/main/resources/vsrc/plusarg_reader.v \ 18 | $(sifiveblocks_dir)/vsrc/SRLatch.v \ 19 | $(FPGA_DIR)/common/vsrc/PowerOnResetFPGAOnly.v \ 20 | $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).rom.v \ 21 | $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).v 22 | 23 | include common.mk 24 | -------------------------------------------------------------------------------- /Makefile.x300arty35devkit: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # Modifications copyright (C) 2019 Hex-Five 3 | base_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) 4 | BUILD_DIR := $(base_dir)/builds/x300arty35devkit 5 | FPGA_DIR := $(base_dir)/fpga-shells/xilinx 6 | MODEL := X300ArtyDevKitFPGAChip 7 | PROJECT := hexfive.x300artydevkit 8 | export CONFIG_PROJECT := hexfive.x300artydevkit 9 | export CONFIG := X300ArtyDevKitConfig 10 | export BOARD := arty 11 | export BOOTROM_DIR := $(base_dir)/bootrom/xip 12 | 13 | rocketchip_dir := $(base_dir)/rocket-chip 14 | sifiveblocks_dir := $(base_dir)/sifive-blocks 15 | VSRCS := \ 16 | $(rocketchip_dir)/src/main/resources/vsrc/AsyncResetReg.v \ 17 | $(rocketchip_dir)/src/main/resources/vsrc/plusarg_reader.v \ 18 | $(sifiveblocks_dir)/vsrc/SRLatch.v \ 19 | $(FPGA_DIR)/common/vsrc/PowerOnResetFPGAOnly.v \ 20 | $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).rom.v \ 21 | $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).v 22 | 23 | include common.mk 24 | -------------------------------------------------------------------------------- /bootrom/xip/Makefile: -------------------------------------------------------------------------------- 1 | # RISCV environment variable must be set 2 | 3 | CC=$(RISCV)/bin/riscv64-unknown-elf-gcc 4 | OBJCOPY=$(RISCV)/bin/riscv64-unknown-elf-objcopy 5 | CFLAGS=-march=rv32imac -mabi=ilp32 -O2 -std=gnu11 -Wall -I. -nostartfiles -fno-common -g 6 | LFLAGS=-static -nostdlib 7 | 8 | dtb := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).dtb 9 | $(dtb): $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).dts 10 | dtc -I dts -O dtb -o $@ $< 11 | 12 | .PHONY: dtb 13 | dtb: $(dtb) 14 | 15 | elf := $(BUILD_DIR)/xip.elf 16 | $(elf): xip.S $(dtb) 17 | $(CC) $(CFLAGS) -DXIP_TARGET_ADDR=0x20400000 -DDEVICE_TREE='"$(dtb)"' $(LFLAGS) -o $@ $< 18 | 19 | .PHONY: elf 20 | elf: $(elf) 21 | 22 | bin := $(BUILD_DIR)/xip.bin 23 | $(bin): $(elf) 24 | $(OBJCOPY) -O binary $< $@ 25 | 26 | .PHONY: bin 27 | bin: $(bin) 28 | 29 | hex := $(BUILD_DIR)/xip.hex 30 | $(hex): $(bin) 31 | od -t x4 -An -w4 -v $< > $@ 32 | 33 | .PHONY: hex 34 | hex: $(hex) 35 | 36 | romgen := $(BUILD_DIR)/rom.v 37 | $(romgen): $(hex) 38 | $(rocketchip_dir)/scripts/vlsi_rom_gen $(ROMCONF) $< > $@ 39 | 40 | .PHONY: romgen 41 | romgen: $(romgen) 42 | 43 | .PHONY: clean 44 | clean:: 45 | rm -rf $(hex) $(elf) 46 | -------------------------------------------------------------------------------- /bootrom/xip/xip.S: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Execute in place 3 | // Jump directly to XIP_TARGET_ADDR 4 | 5 | .section .text.init 6 | .option norvc 7 | .globl _start 8 | _start: 9 | csrr a0, mhartid 10 | la a1, dtb 11 | li t0, XIP_TARGET_ADDR 12 | jr t0 13 | 14 | .section .rodata 15 | dtb: 16 | .incbin DEVICE_TREE 17 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Modifications copyright (C) 2019 Hex-Five 3 | organization := "com.hex-five" 4 | name := "x300" 5 | version := "0.1.0" 6 | 7 | lazy val commonSettings = Seq( 8 | scalaVersion := "2.12.4", // This needs to match rocket-chip's scalaVersion 9 | scalacOptions ++= Seq( 10 | "-deprecation", 11 | "-feature", 12 | "-unchecked", 13 | "-Xsource:2.11", 14 | "-language:reflectiveCalls" 15 | ) 16 | ) 17 | 18 | // A RootProject (not well-documented) tells sbt to treat the target directory 19 | // as its own root project, reading its build settings. If we instead used the 20 | // normal `project in file()` declaration, sbt would ignore all of rocket-chip's 21 | // build settings, and therefore not understand that it has its own dependencies 22 | // on chisel, etc. 23 | lazy val rocketChip = RootProject(file("rocket-chip")) 24 | 25 | lazy val sifiveBlocks = (project in file("sifive-blocks")). 26 | dependsOn(rocketChip). 27 | settings(commonSettings: _*) 28 | 29 | lazy val fpgaShells = (project in file("fpga-shells")). 30 | dependsOn(rocketChip, sifiveBlocks). 31 | settings(commonSettings: _*) 32 | 33 | lazy val x300 = (project in file(".")). 34 | dependsOn(rocketChip, sifiveBlocks, fpgaShells). 35 | settings(commonSettings: _*) 36 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Required variables: 4 | # - MODEL 5 | # - PROJECT 6 | # - CONFIG_PROJECT 7 | # - CONFIG 8 | # - BUILD_DIR 9 | # - FPGA_DIR 10 | 11 | # Optional variables: 12 | # - EXTRA_FPGA_VSRCS 13 | 14 | # export to bootloader 15 | export ROMCONF=$(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).rom.conf 16 | 17 | # export to fpga-shells 18 | export FPGA_TOP_SYSTEM=$(MODEL) 19 | export FPGA_BUILD_DIR=$(BUILD_DIR)/$(FPGA_TOP_SYSTEM) 20 | export fpga_common_script_dir=$(FPGA_DIR)/common/tcl 21 | export fpga_board_script_dir=$(FPGA_DIR)/$(BOARD)/tcl 22 | 23 | export BUILD_DIR 24 | 25 | EXTRA_FPGA_VSRCS ?= 26 | PATCHVERILOG ?= "" 27 | BOOTROM_DIR ?= "" 28 | 29 | base_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) 30 | export rocketchip_dir := $(base_dir)/rocket-chip 31 | SBT ?= java -jar $(rocketchip_dir)/sbt-launch.jar ++2.12.4 32 | 33 | # Build firrtl.jar and put it where chisel3 can find it. 34 | FIRRTL_JAR ?= $(rocketchip_dir)/firrtl/utils/bin/firrtl.jar 35 | FIRRTL ?= java -Xmx4G -Xss16M -cp $(FIRRTL_JAR) firrtl.Driver 36 | 37 | $(FIRRTL_JAR): $(shell find $(rocketchip_dir)/firrtl/src/main/scala -iname "*.scala") 38 | $(MAKE) -C $(rocketchip_dir)/firrtl SBT="$(SBT)" root_dir=$(rocketchip_dir)/firrtl build-scala 39 | touch $(FIRRTL_JAR) 40 | mkdir -p $(rocketchip_dir)/lib 41 | cp -p $(FIRRTL_JAR) rocket-chip/lib 42 | mkdir -p $(rocketchip_dir)/chisel3/lib 43 | cp -p $(FIRRTL_JAR) $(rocketchip_dir)/chisel3/lib 44 | 45 | # Build .fir 46 | firrtl := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).fir 47 | $(firrtl): $(shell find $(base_dir)/src/main/scala -name '*.scala') $(FIRRTL_JAR) 48 | mkdir -p $(dir $@) 49 | $(SBT) "runMain freechips.rocketchip.system.Generator $(BUILD_DIR) $(PROJECT) $(MODEL) $(CONFIG_PROJECT) $(CONFIG)" 50 | 51 | .PHONY: firrtl 52 | firrtl: $(firrtl) 53 | 54 | # Build .v 55 | verilog := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).v 56 | $(verilog): $(firrtl) $(FIRRTL_JAR) 57 | $(FIRRTL) -i $(firrtl) -o $@ -X verilog 58 | ifneq ($(PATCHVERILOG),"") 59 | $(PATCHVERILOG) 60 | endif 61 | 62 | .PHONY: verilog 63 | verilog: $(verilog) 64 | 65 | romgen := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).rom.v 66 | $(romgen): $(verilog) 67 | ifneq ($(BOOTROM_DIR),"") 68 | $(MAKE) -C $(BOOTROM_DIR) romgen 69 | mv $(BUILD_DIR)/rom.v $@ 70 | endif 71 | 72 | .PHONY: romgen 73 | romgen: $(romgen) 74 | 75 | f := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).vsrcs.F 76 | $(f): 77 | echo $(VSRCS) > $@ 78 | 79 | bit := $(BUILD_DIR)/obj/$(MODEL).bit 80 | $(bit): $(romgen) $(f) 81 | cd $(BUILD_DIR); vivado \ 82 | -nojournal -mode batch \ 83 | -source $(fpga_common_script_dir)/vivado.tcl \ 84 | -tclargs \ 85 | -top-module "$(MODEL)" \ 86 | -F "$(f)" \ 87 | -ip-vivado-tcls "$(shell find '$(BUILD_DIR)' -name '*.vivado.tcl')" \ 88 | -board "$(BOARD)" 89 | 90 | 91 | # Build .mcs 92 | mcs := $(BUILD_DIR)/obj/$(MODEL).mcs 93 | $(mcs): $(bit) 94 | cd $(BUILD_DIR); vivado -nojournal -mode batch -source $(fpga_common_script_dir)/write_cfgmem.tcl -tclargs $(BOARD) $@ $< 95 | 96 | .PHONY: mcs 97 | mcs: $(mcs) 98 | 99 | # Build Libero project 100 | prjx := $(BUILD_DIR)/libero/$(MODEL).prjx 101 | $(prjx): $(verilog) 102 | cd $(BUILD_DIR); libero SCRIPT:$(fpga_common_script_dir)/libero.tcl SCRIPT_ARGS:"$(BUILD_DIR) $(MODEL) $(PROJECT) $(CONFIG) $(BOARD)" 103 | 104 | .PHONY: prjx 105 | prjx: $(prjx) 106 | 107 | # Clean 108 | .PHONY: clean 109 | clean: 110 | ifneq ($(BOOTROM_DIR),"") 111 | $(MAKE) -C $(BOOTROM_DIR) clean 112 | endif 113 | $(MAKE) -C $(FPGA_DIR) clean 114 | rm -rf $(BUILD_DIR) 115 | -------------------------------------------------------------------------------- /fpga-shells/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/Bundles.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.diplomacy._ 6 | import freechips.rocketchip.util._ 7 | 8 | class ClockBundle(params: ClockBundleParameters) extends GenericParameterizedBundle(params) 9 | { 10 | val clock = Clock() 11 | val reset = Bool() 12 | } 13 | 14 | class ClockGroupBundle(params: ClockGroupBundleParameters) extends GenericParameterizedBundle(params) 15 | { 16 | val member = HeterogeneousBag(params.members.map(p => new ClockBundle(p))) 17 | } 18 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/ClockGroup.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import Chisel._ 5 | import chisel3.internal.sourceinfo.SourceInfo 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.diplomacy._ 8 | 9 | case class ClockGroupNode(groupName: String)(implicit valName: ValName) 10 | extends MixedNexusNode(ClockGroupImp, ClockImp)( 11 | dFn = { _ => ClockSourceParameters() }, 12 | uFn = { seq => ClockGroupSinkParameters(name = groupName, members = seq) }) 13 | 14 | class ClockGroup(groupName: String)(implicit p: Parameters) extends LazyModule 15 | { 16 | val node = ClockGroupNode(groupName) 17 | 18 | lazy val module = new LazyModuleImp(this) { 19 | val (in, _) = node.in(0) 20 | val (out, _) = node.out.unzip 21 | 22 | require (node.in.size == 1) 23 | require (in.member.size == out.size) 24 | 25 | (in.member zip out) foreach { case (i, o) => o := i } 26 | } 27 | } 28 | 29 | object ClockGroup 30 | { 31 | def apply()(implicit p: Parameters, valName: ValName) = 32 | LazyModule(new ClockGroup(valName.name)).node 33 | } 34 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/Nodes.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import Chisel._ 5 | import chisel3.internal.sourceinfo.SourceInfo 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.diplomacy._ 8 | 9 | object ClockImp extends SimpleNodeImp[ClockSourceParameters, ClockSinkParameters, ClockEdgeParameters, ClockBundle] 10 | { 11 | def edge(pd: ClockSourceParameters, pu: ClockSinkParameters, p: Parameters, sourceInfo: SourceInfo) = ClockEdgeParameters(pd, pu, p, sourceInfo) 12 | def bundle(e: ClockEdgeParameters) = new ClockBundle(e.bundle) 13 | def render(e: ClockEdgeParameters) = RenderedEdge(colour = "#00cc00" /* green */) 14 | } 15 | 16 | case class ClockSourceNode(val portParams: Seq[ClockSourceParameters])(implicit valName: ValName) extends SourceNode(ClockImp)(portParams) 17 | case class ClockSinkNode(val portParams: Seq[ClockSinkParameters])(implicit valName: ValName) extends SinkNode(ClockImp)(portParams) 18 | case class ClockAdapterNode( 19 | sourceFn: ClockSourceParameters => ClockSourceParameters = { m => m }, 20 | sinkFn: ClockSinkParameters => ClockSinkParameters = { s => s })( 21 | implicit valName: ValName) 22 | extends AdapterNode(ClockImp)(sourceFn, sinkFn) 23 | case class ClockIdentityNode()(implicit valName: ValName) extends IdentityNode(ClockImp)() 24 | 25 | object ClockSinkNode 26 | { 27 | def apply( 28 | freqMHz: Double, 29 | dutyCycle: Double = 50, 30 | phaseDeg: Double = 0, 31 | // Create SDC/TCL constraints that the clock matches these requirements: 32 | phaseErrorDeg: Double = 5, 33 | freqErrorPPM: Double = 10000, 34 | jitterPS: Double = 300)(implicit valName: ValName): ClockSinkNode = 35 | ClockSinkNode(Seq(ClockSinkParameters( 36 | phaseDeg = phaseDeg, 37 | phaseErrorDeg = phaseErrorDeg, 38 | freqErrorPPM = freqErrorPPM, 39 | jitterPS = jitterPS, 40 | take = Some(ClockParameters( 41 | freqMHz = freqMHz, 42 | dutyCycle = dutyCycle))))) 43 | } 44 | 45 | object ClockSourceNode 46 | { 47 | def apply( 48 | freqMHz: Double, 49 | dutyCycle: Double = 50, 50 | jitterPS: Double = 300)(implicit valName: ValName): ClockSourceNode = 51 | ClockSourceNode(Seq(ClockSourceParameters( 52 | jitterPS = Some(jitterPS), 53 | give = Some(ClockParameters( 54 | freqMHz = freqMHz, 55 | dutyCycle = dutyCycle))))) 56 | } 57 | 58 | object ClockGroupImp extends SimpleNodeImp[ClockGroupSourceParameters, ClockGroupSinkParameters, ClockGroupEdgeParameters, ClockGroupBundle] 59 | { 60 | def edge(pd: ClockGroupSourceParameters, pu: ClockGroupSinkParameters, p: Parameters, sourceInfo: SourceInfo) = ClockGroupEdgeParameters(pd, pu, p, sourceInfo) 61 | def bundle(e: ClockGroupEdgeParameters) = new ClockGroupBundle(e.bundle) 62 | def render(e: ClockGroupEdgeParameters) = RenderedEdge(colour = "#00cc00" /* green */) 63 | } 64 | 65 | case class ClockGroupAdapterNode( 66 | sourceFn: ClockGroupSourceParameters => ClockGroupSourceParameters = { m => m }, 67 | sinkFn: ClockGroupSinkParameters => ClockGroupSinkParameters = { s => s })( 68 | implicit valName: ValName) 69 | extends AdapterNode(ClockGroupImp)(sourceFn, sinkFn) 70 | case class ClockGroupIdentityNode()(implicit valName: ValName) extends IdentityNode(ClockGroupImp)() 71 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/PLLFactory.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import sifive.fpgashells.shell._ 8 | import scala.collection.immutable.ListMap 9 | 10 | case class PLLNode(val feedback: Boolean)(implicit valName: ValName) 11 | extends MixedNexusNode(ClockImp, ClockGroupImp)( 12 | dFn = { _ => ClockGroupSourceParameters() }, 13 | uFn = { _ => ClockSinkParameters() }) 14 | 15 | case class PLLInClockParameters( 16 | freqMHz: Double, 17 | jitter: Double = 50, 18 | feedback: Boolean = false) 19 | 20 | case class PLLOutClockParameters( 21 | freqMHz: Double, 22 | phaseDeg: Double = 0, 23 | dutyCycle: Double = 50, // in percent 24 | // used to create constraints: 25 | jitterPS: Double = 300, 26 | freqErrorPPM: Double = 10000, 27 | phaseErrorDeg: Double = 5) 28 | 29 | case class PLLParameters( 30 | name: String, 31 | input: PLLInClockParameters, 32 | req: Seq[PLLOutClockParameters]) 33 | 34 | trait PLLInstance { 35 | def getInput: Clock 36 | def getReset: Option[Bool] 37 | def getLocked: Bool 38 | def getClocks: Seq[Clock] 39 | def getClockNames: Seq[String] 40 | } 41 | 42 | case object PLLFactoryKey extends Field[PLLFactory] 43 | class PLLFactory(scope: IOShell, maxOutputs: Int, gen: PLLParameters => PLLInstance) 44 | { 45 | private var pllNodes: Seq[PLLNode] = Nil 46 | 47 | def apply(feedback: Boolean = false)(implicit valName: ValName, p: Parameters): PLLNode = { 48 | val node = scope { PLLNode(feedback) } 49 | pllNodes = node +: pllNodes 50 | node 51 | } 52 | 53 | scope { InModuleBody { 54 | // Require all clock group names to be distinct 55 | val sdcGroups = Map() ++ pllNodes.flatMap { case node => 56 | require (node.in.size == 1) 57 | val (in, edgeIn) = node.in(0) 58 | val (out, edgeOut) = node.out.unzip 59 | 60 | val params = PLLParameters( 61 | name = node.valName.name, 62 | input = PLLInClockParameters( 63 | freqMHz = edgeIn.clock.freqMHz, 64 | jitter = edgeIn.source.jitterPS.getOrElse(50), 65 | feedback = node.feedback), 66 | req = edgeOut.flatMap(_.members).map { e => 67 | PLLOutClockParameters( 68 | freqMHz = e.clock.freqMHz, 69 | phaseDeg = e.sink.phaseDeg, 70 | dutyCycle = e.clock.dutyCycle, 71 | jitterPS = e.sink.jitterPS, 72 | freqErrorPPM = e.sink.freqErrorPPM, 73 | phaseErrorDeg = e.sink.phaseErrorDeg)}) 74 | 75 | val pll = gen(params) 76 | pll.getInput := in.clock 77 | pll.getReset.foreach { _ := in.reset } 78 | (out.flatMap(_.member) zip pll.getClocks) foreach { case (o, i) => 79 | o.clock := i 80 | o.reset := !pll.getLocked || in.reset 81 | } 82 | 83 | val groupLabels = edgeOut.flatMap(e => Seq.fill(e.members.size) { e.sink.name }) 84 | groupLabels zip pll.getClocks.map(x => IOPin(x)) 85 | }.groupBy(_._1).mapValues(_.map(_._2)) 86 | 87 | // Ensure there are no clock groups with the same name 88 | require (sdcGroups.size == pllNodes.map(_.edges.out.size).sum) 89 | sdcGroups.foreach { case (_, clockPins) => scope.sdc.addGroup(pins = clockPins) } 90 | } } 91 | } 92 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/Parameters.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import Chisel._ 5 | import chisel3.internal.sourceinfo.SourceInfo 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.diplomacy._ 8 | import scala.math.max 9 | 10 | // All Clock parameters specify only the PLL values required at power-on 11 | // Dynamic control of the PLL from software can take the values out-of-range 12 | 13 | case class ClockParameters( 14 | freqMHz: Double, 15 | dutyCycle: Double = 50) //in percent 16 | { 17 | require (freqMHz > 0) 18 | require (0 < dutyCycle) 19 | require (dutyCycle < 100) 20 | } 21 | 22 | case class ClockSourceParameters( 23 | jitterPS: Option[Double] = None, // if known at chisel elaboration 24 | give: Option[ClockParameters] = None) 25 | 26 | case class ClockSinkParameters( 27 | phaseDeg: Double = 0, 28 | // Create SDC/TCL constraints that the clock matches these requirements: 29 | phaseErrorDeg: Double = 5, 30 | freqErrorPPM: Double = 10000, 31 | jitterPS: Double = 200, 32 | take: Option[ClockParameters] = None) 33 | { 34 | require (phaseErrorDeg >= 0) 35 | require (freqErrorPPM >= 0) 36 | } 37 | 38 | case class ClockBundleParameters() 39 | 40 | case class ClockEdgeParameters( 41 | source: ClockSourceParameters, 42 | sink: ClockSinkParameters, 43 | params: Parameters, 44 | sourceInfo: SourceInfo) 45 | { 46 | // Unify the given+taken ClockParameters 47 | require (!source.give.isEmpty || !sink.take.isEmpty) 48 | val clock = source.give.orElse(sink.take).get 49 | source.give.foreach { x => require (clock == x) } 50 | sink.take.foreach { x => require (clock == x) } 51 | 52 | val bundle = ClockBundleParameters() 53 | } 54 | 55 | // ClockGroups exist as the output of a PLL 56 | 57 | case class ClockGroupSourceParameters() 58 | case class ClockGroupSinkParameters( 59 | name: String, 60 | members: Seq[ClockSinkParameters]) 61 | 62 | case class ClockGroupBundleParameters( 63 | members: Seq[ClockBundleParameters]) 64 | 65 | case class ClockGroupEdgeParameters( 66 | source: ClockGroupSourceParameters, 67 | sink: ClockGroupSinkParameters, 68 | params: Parameters, 69 | sourceInfo: SourceInfo) 70 | { 71 | val sourceParameters = ClockSourceParameters() 72 | val members = sink.members.map { s => 73 | ClockEdgeParameters(sourceParameters, s, params, sourceInfo) 74 | } 75 | 76 | val bundle = ClockGroupBundleParameters(members.map(_.bundle)) 77 | } 78 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/clocks/ResetWrangler.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.clocks 3 | 4 | import chisel3._ 5 | import chisel3.util._ 6 | import chisel3.experimental.{withClockAndReset} 7 | import freechips.rocketchip.config._ 8 | import freechips.rocketchip.diplomacy._ 9 | import freechips.rocketchip.util._ 10 | 11 | class ResetWrangler(debounceNs: Double = 100000)(implicit p: Parameters) extends LazyModule 12 | { 13 | val node = ClockAdapterNode() 14 | 15 | lazy val module = new LazyRawModuleImp(this) { 16 | val (in, _) = node.in.unzip 17 | val (out, _) = node.out.unzip 18 | 19 | val status = IO(Output(UInt(in.size.W))) 20 | status := Cat(in.map(_.reset).reverse) 21 | 22 | val causes = in.map(_.reset).foldLeft(false.B)(_ || _) 23 | val (slowIn, slowEdge) = node.in.minBy(_._2.clock.freqMHz) 24 | val slowPeriodNs = 1000 / slowEdge.clock.freqMHz 25 | val slowTicks = math.ceil(debounceNs/slowPeriodNs).toInt max 7 26 | val slowBits = log2Ceil(slowTicks+1) 27 | 28 | // debounce 29 | val increment = Wire(Bool()) 30 | val incremented = Wire(UInt(slowBits.W)) 31 | val debounced = withClockAndReset(slowIn.clock, causes) { 32 | AsyncResetReg(incremented, 0, increment, Some("debounce")) 33 | } 34 | increment := debounced =/= slowTicks.U 35 | incremented := debounced + 1.U 36 | val deglitched = AsyncResetReg(increment, slowIn.clock, causes, true, Some("deglitch")) 37 | 38 | // catch and sync increment to each domain 39 | (in zip out) foreach { case (i, o) => 40 | o.clock := i.clock 41 | o.reset := ResetCatchAndSync(o.clock, deglitched) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/devices/xilinx/xdma/XDMA.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.devices.xilinx.xdma 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.amba.axi4._ 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.diplomacy._ 8 | import freechips.rocketchip.tilelink._ 9 | import freechips.rocketchip.interrupts._ 10 | import freechips.rocketchip.subsystem.{CrossesToOnlyOneClockDomain, CacheBlockBytes} 11 | import sifive.fpgashells.ip.xilinx.xdma._ 12 | 13 | class XDMA(c: XDMAParams)(implicit p: Parameters, val crossing: ClockCrossingType = AsynchronousCrossing(8)) 14 | extends LazyModule with CrossesToOnlyOneClockDomain 15 | { 16 | val imp = LazyModule(new DiplomaticXDMA(c)) 17 | 18 | val slave: TLInwardNode = 19 | (imp.slave 20 | := AXI4Buffer() 21 | := AXI4UserYanker() 22 | := AXI4Deinterleaver(p(CacheBlockBytes)) 23 | := AXI4IdIndexer(idBits=c.sIDBits) 24 | := TLToAXI4(adapterName = Some("pcie-slave"))) 25 | 26 | val control: TLInwardNode = 27 | (imp.control 28 | := AXI4Buffer() 29 | := AXI4UserYanker(capMaxFlight = Some(2)) 30 | := TLToAXI4() 31 | := TLFragmenter(4, p(CacheBlockBytes), holdFirstDeny = true)) 32 | 33 | val master: TLOutwardNode = 34 | (TLWidthWidget(c.busBytes) 35 | := AXI4ToTL() 36 | := AXI4UserYanker(capMaxFlight=Some(16)) 37 | := AXI4Fragmenter() 38 | := AXI4IdIndexer(idBits=2) 39 | := imp.master) 40 | 41 | val intnode: IntOutwardNode = imp.intnode 42 | 43 | lazy val module = new LazyModuleImp(this) { 44 | val io = IO(new Bundle { 45 | val pads = new XDMAPads(c.lanes) 46 | val clocks = new XDMAClocks 47 | }) 48 | 49 | io.pads <> imp.module.io.pads 50 | io.clocks <> imp.module.io.clocks 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/devices/xilinx/xdma/package.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.devices.xilinx 3 | 4 | package object xdma 5 | { 6 | type XDMAPads = sifive.fpgashells.ip.xilinx.xdma.XDMAPads 7 | type XDMAClocks = sifive.fpgashells.ip.xilinx.xdma.XDMAClocks 8 | type XDMAParams = sifive.fpgashells.ip.xilinx.xdma.XDMAParams 9 | val XDMAParams = sifive.fpgashells.ip.xilinx.xdma.XDMAParams 10 | } 11 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/devices/xilinx/xilinxethernetlite/XilinxEthernetLite.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Copyright (C) 2018-2019 Hex-Five 3 | package sifive.fpgashells.devices.xilinx.xilinxethernetlite 4 | 5 | import Chisel._ 6 | import freechips.rocketchip.amba.axi4._ 7 | import freechips.rocketchip.config.Parameters 8 | import freechips.rocketchip.diplomacy._ 9 | import freechips.rocketchip.tilelink._ 10 | import freechips.rocketchip.interrupts._ 11 | import freechips.rocketchip.subsystem.{CrossesToOnlyOneClockDomain, CacheBlockBytes} 12 | import sifive.fpgashells.ip.xilinx.ethernetlite.{EthernetLite, PhyPort, MdioPort} 13 | 14 | class XilinxEthernetLite(implicit p: Parameters, val crossing: ClockCrossingType = NoCrossing) 15 | extends LazyModule with CrossesToOnlyOneClockDomain 16 | { 17 | val ethernetlite = LazyModule(new EthernetLite) 18 | 19 | val slave: TLInwardNode = 20 | (ethernetlite.slave 21 | := AXI4Buffer() 22 | := AXI4UserYanker() 23 | := AXI4Deinterleaver(p(CacheBlockBytes)) 24 | := AXI4IdIndexer(idBits=1) 25 | := TLToAXI4(adapterName = Some("ethernetlite"))) 26 | 27 | val intnode: IntOutwardNode = ethernetlite.intnode 28 | 29 | lazy val module = new LazyModuleImp(this) { 30 | val io = IO(new Bundle { 31 | val port = new Bundle with PhyPort 32 | with MdioPort { 33 | } 34 | }) 35 | 36 | ethernetlite.module.io.clockreset.s_axi_aclk := clock 37 | ethernetlite.module.io.clockreset.s_axi_aresetn := ~reset 38 | 39 | io.port <> ethernetlite.module.io.port 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/devices/xilinx/xilinxethernetlite/XilinxEthernetLitePeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Copyright (C) 2018-2019 Hex-Five 3 | package sifive.fpgashells.devices.xilinx.xilinxethernetlite 4 | 5 | import Chisel._ 6 | import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, BufferParams} 7 | import freechips.rocketchip.subsystem.BaseSubsystem 8 | import freechips.rocketchip.tilelink._ 9 | import freechips.rocketchip.interrupts.IntSyncCrossingSink 10 | 11 | import sifive.fpgashells.ip.xilinx.ethernetlite.{PhyPort, MdioPort} 12 | 13 | trait HasSystemXilinxEthernetLite { this: BaseSubsystem => 14 | val xilinxethernetlite = LazyModule(new XilinxEthernetLite) 15 | private val cname = "xilinxethernetlite" 16 | sbus.coupleTo(s"slave_named_$cname") { xilinxethernetlite.crossTLIn(xilinxethernetlite.slave) :*= TLWidthWidget(sbus.beatBytes) :*= _ } 17 | ibus.fromSync := xilinxethernetlite.crossIntOut(xilinxethernetlite.intnode) 18 | } 19 | 20 | trait HasSystemXilinxEthernetLiteModuleImp extends LazyModuleImp { 21 | val outer: HasSystemXilinxEthernetLite 22 | val phy = IO(new Bundle with PhyPort with MdioPort {}) 23 | 24 | phy <> outer.xilinxethernetlite.module.io.port 25 | } 26 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.ip.xilinx.ibufds_gte2 3 | 4 | import Chisel._ 5 | 6 | //IP : xilinx unisim IBUFDS_GTE2 7 | //Differential Signaling Input Buffer 8 | //unparameterized 9 | 10 | class IBUFDS_GTE2 extends BlackBox { 11 | val io = new Bundle { 12 | val O = Bool(OUTPUT) 13 | val ODIV2 = Bool(OUTPUT) 14 | val CEB = Bool(INPUT) 15 | val I = Bool(INPUT) 16 | val IB = Bool(INPUT) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/ChipLinkOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.tilelink._ 8 | import sifive.blocks.devices.chiplink._ 9 | import sifive.fpgashells.clocks._ 10 | 11 | case class ChipLinkOverlayParams( 12 | params: ChipLinkParams, 13 | txGroup: ClockGroupNode, 14 | txData: ClockSinkNode, 15 | wrangler: ClockAdapterNode)( 16 | implicit val p: Parameters) 17 | 18 | case object ChipLinkOverlayKey extends Field[Seq[DesignOverlay[ChipLinkOverlayParams, TLNode]]](Nil) 19 | 20 | abstract class ChipLinkOverlay( 21 | val params: ChipLinkOverlayParams, 22 | val rxPhase: Double, 23 | val txPhase: Double) 24 | extends IOOverlay[WideDataLayerPort, TLNode] 25 | { 26 | implicit val p = params.p 27 | val freqMHz = params.txData.portParams.head.take.get.freqMHz 28 | val phaseDeg = params.txData.portParams.head.phaseDeg 29 | 30 | val link = LazyModule(new ChipLink(params.params)) 31 | val rxPLL = p(PLLFactoryKey)(feedback = true) 32 | val ioSink = shell { link.ioNode.makeSink() } 33 | val rxI = shell { ClockSourceNode(freqMHz = freqMHz, jitterPS = 100) } 34 | val rxGroup = shell { ClockGroup() } 35 | val rxO = shell { ClockSinkNode(freqMHz = freqMHz, phaseDeg = rxPhase) } 36 | val txClock = shell { ClockSinkNode(freqMHz = freqMHz, phaseDeg = phaseDeg + txPhase) } 37 | 38 | rxO := params.wrangler := rxGroup := rxPLL := rxI 39 | txClock := params.wrangler := params.txGroup 40 | 41 | def designOutput = link.node 42 | def ioFactory = new WideDataLayerPort(ChipLinkParams(Nil,Nil)) 43 | 44 | shell { InModuleBody { 45 | val (rxOut, _) = rxO.in(0) 46 | val port = ioSink.bundle 47 | io <> port 48 | port.b2c.clk := rxOut.clock 49 | } } 50 | } 51 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/ClockOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import sifive.fpgashells.clocks._ 8 | 9 | case class ClockInputOverlayParams()(implicit val p: Parameters) 10 | case class ClockOutputOverlayParams()(implicit val p: Parameters) 11 | 12 | case object ClockInputOverlayKey extends Field[Seq[DesignOverlay[ClockInputOverlayParams, ClockSourceNode]]](Nil) 13 | case object ClockOutputOverlayKey extends Field[Seq[DesignOverlay[ClockOutputOverlayParams, ClockSinkNode]]](Nil) 14 | 15 | class LVDSClock extends Bundle 16 | { 17 | val p = Clock() 18 | val n = Clock() 19 | } 20 | 21 | abstract class LVDSClockInputOverlay( 22 | val params: ClockInputOverlayParams) 23 | extends IOOverlay[LVDSClock, ClockSourceNode] 24 | { 25 | implicit val p = params.p 26 | def node: ClockSourceNode 27 | 28 | def ioFactory = Input(new LVDSClock) 29 | def designOutput = node 30 | 31 | val clock = shell { InModuleBody { 32 | val (bundle, edge) = node.out.head 33 | shell.sdc.addClock(name, io.p, edge.clock.freqMHz) 34 | bundle.clock 35 | } } 36 | } 37 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/DDROverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.tilelink._ 8 | import sifive.fpgashells.clocks._ 9 | 10 | case class DDROverlayParams( 11 | baseAddress: BigInt, 12 | wrangler: ClockAdapterNode)( 13 | implicit val p: Parameters) 14 | 15 | case object DDROverlayKey extends Field[Seq[DesignOverlay[DDROverlayParams, TLInwardNode]]](Nil) 16 | 17 | abstract class DDROverlay[IO <: Data](val params: DDROverlayParams) 18 | extends IOOverlay[IO, TLInwardNode] 19 | { 20 | implicit val p = params.p 21 | } 22 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/JTAGDebugOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.util._ 7 | import freechips.rocketchip.diplomacy._ 8 | import freechips.rocketchip.devices.debug._ 9 | import freechips.rocketchip.subsystem.{BaseSubsystem, PeripheryBus, PeripheryBusKey} 10 | import sifive.fpgashells.ip.xilinx._ 11 | 12 | case class JTAGDebugOverlayParams()(implicit val p: Parameters) 13 | case object JTAGDebugOverlayKey extends Field[Seq[DesignOverlay[JTAGDebugOverlayParams, ModuleValue[FPGAJTAGIO]]]](Nil) 14 | 15 | class FPGAJTAGIO extends Bundle { 16 | // JTAG 17 | val jtag_TCK = Input(Clock()) 18 | val jtag_TMS = Input(Bool()) 19 | val jtag_TDI = Input(Bool()) 20 | val jtag_TDO = Output(Bool()) 21 | } 22 | 23 | abstract class JTAGDebugOverlay( 24 | val params: JTAGDebugOverlayParams) 25 | extends IOOverlay[FPGAJTAGIO, ModuleValue[FPGAJTAGIO]] 26 | { 27 | implicit val p = params.p 28 | 29 | def ioFactory = new FPGAJTAGIO 30 | 31 | val jtagDebugSource = BundleBridgeSource(() => new FPGAJTAGIO) 32 | val jtagDebugSink = shell { jtagDebugSource.makeSink } 33 | 34 | val designOutput = InModuleBody { jtagDebugSource.bundle} 35 | 36 | shell { InModuleBody { 37 | io <> jtagDebugSink.bundle 38 | } } 39 | } 40 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/LEDOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | 8 | case class LEDOverlayParams()(implicit val p: Parameters) 9 | case object LEDOverlayKey extends Field[Seq[DesignOverlay[LEDOverlayParams, ModuleValue[UInt]]]](Nil) 10 | 11 | abstract class LEDOverlay( 12 | val params: LEDOverlayParams) 13 | extends IOOverlay[UInt, ModuleValue[UInt]] 14 | { 15 | implicit val p = params.p 16 | 17 | def width: Int 18 | def ioFactory = Output(UInt(width.W)) 19 | 20 | val ledSource = BundleBridgeSource(() => UInt(width.W)) 21 | val ledSink = shell { ledSource.makeSink() } 22 | val designOutput = InModuleBody { ledSource.out(0)._1 } 23 | } 24 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/PCIeOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.tilelink._ 8 | import freechips.rocketchip.interrupts._ 9 | import sifive.fpgashells.clocks._ 10 | 11 | case class PCIeOverlayParams( 12 | wrangler: ClockAdapterNode, 13 | bars: Seq[AddressSet] = Seq(AddressSet(0x40000000L, 0x1FFFFFFFL)), 14 | ecam: BigInt = 0x2000000000L)( 15 | implicit val p: Parameters) 16 | 17 | case object PCIeOverlayKey extends Field[Seq[DesignOverlay[PCIeOverlayParams, (TLNode, IntOutwardNode)]]](Nil) 18 | 19 | abstract class PCIeOverlay[IO <: Data](val params: PCIeOverlayParams) 20 | extends IOOverlay[IO, (TLNode, IntOutwardNode)] 21 | { 22 | implicit val p = params.p 23 | } 24 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/SDIOOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import chisel3.experimental.Analog 6 | import freechips.rocketchip.config._ 7 | import freechips.rocketchip.diplomacy._ 8 | import sifive.blocks.devices.spi._ 9 | import freechips.rocketchip.tilelink.TLBusWrapper 10 | import freechips.rocketchip.interrupts.IntInwardNode 11 | 12 | // TODO: Can this be combined with SPIAttachParams? 13 | case class SDIOOverlayParams(spiParam: SPIParams, controlBus: TLBusWrapper, intNode: IntInwardNode)(implicit val p: Parameters) 14 | case object SDIOOverlayKey extends Field[Seq[DesignOverlay[SDIOOverlayParams, TLSPI]]](Nil) 15 | 16 | // SDIO Port. Not sure how generic this is, it might need to move. 17 | class FPGASDIOPortIO extends Bundle { 18 | val sdio_clk = Output(Bool()) 19 | val sdio_cmd = Output(Bool()) 20 | val sdio_dat_0 = Input(Bool()) 21 | val sdio_dat_1 = Analog(1.W) 22 | val sdio_dat_2 = Analog(1.W) 23 | val sdio_dat_3 = Output(Bool()) 24 | } 25 | 26 | abstract class SDIOOverlay( 27 | val params: SDIOOverlayParams) 28 | extends IOOverlay[FPGASDIOPortIO, TLSPI] 29 | { 30 | implicit val p = params.p 31 | 32 | def ioFactory = new FPGASDIOPortIO 33 | val tlspi = SPI.attach(SPIAttachParams(params.spiParam, params.controlBus, params.intNode)) 34 | val tlspiSink = tlspi.ioNode.makeSink 35 | 36 | val spiSource = BundleBridgeSource(() => new SPIPortIO(params.spiParam)) 37 | val spiSink = shell { spiSource.makeSink } 38 | val designOutput = tlspi 39 | 40 | InModuleBody { 41 | val (io, _) = spiSource.out(0) 42 | val tlspiport = tlspiSink.bundle 43 | io <> tlspiport 44 | (0 to 3).foreach { case q => 45 | tlspiport.dq(q).i := RegNext(RegNext(io.dq(q).i)) 46 | } 47 | } 48 | 49 | shell { InModuleBody { 50 | val sd_spi_sck = spiSink.bundle.sck 51 | val sd_spi_cs = spiSink.bundle.cs(0) 52 | 53 | val sd_spi_dq_i = Wire(Vec(4, Bool())) 54 | val sd_spi_dq_o = Wire(Vec(4, Bool())) 55 | 56 | spiSink.bundle.dq.zipWithIndex.foreach { 57 | case(pin, idx) => 58 | sd_spi_dq_o(idx) := pin.o 59 | pin.i := sd_spi_dq_i(idx) 60 | } 61 | 62 | io.sdio_clk := sd_spi_sck 63 | io.sdio_dat_3 := sd_spi_cs 64 | io.sdio_cmd := sd_spi_dq_o(0) 65 | sd_spi_dq_i := Seq(false.B, io.sdio_dat_0, false.B, false.B) 66 | } } 67 | } 68 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/Shell.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | 8 | case object DesignKey extends Field[Parameters => LazyModule] 9 | 10 | // Overlays are declared by the Shell and placed somewhere by the Design 11 | // ... they inject diplomatic code both where they were placed and in the shell 12 | // ... they are instantiated with DesignInput and return DesignOutput 13 | trait Overlay[DesignOutput] 14 | { 15 | def designOutput: DesignOutput 16 | def name: String 17 | def shell: Shell 18 | } 19 | 20 | // DesignOverlays provide the method used to instantiate and place an Overlay 21 | trait DesignOverlay[DesignInput, DesignOutput] { 22 | def isPlaced: Boolean 23 | def name: String 24 | def apply(input: DesignInput): DesignOutput 25 | } 26 | 27 | abstract class Shell()(implicit p: Parameters) extends LazyModule with LazyScope 28 | { 29 | private var overlays = Parameters.empty 30 | def designParameters: Parameters = overlays ++ p 31 | 32 | def Overlay[DesignInput, DesignOutput, T <: Overlay[DesignOutput]]( 33 | key: Field[Seq[DesignOverlay[DesignInput, DesignOutput]]])( 34 | gen: (this.type, String, DesignInput) => T)( 35 | implicit valName: ValName): ModuleValue[Option[T]] = 36 | { 37 | val self = this.asInstanceOf[this.type] 38 | val thunk = new ModuleValue[Option[T]] with DesignOverlay[DesignInput, DesignOutput] { 39 | var placement: Option[T] = None 40 | def getWrappedValue = placement 41 | def isPlaced = !placement.isEmpty 42 | def name = valName.name 43 | def apply(input: DesignInput): DesignOutput = { 44 | require (placement.isEmpty, s"Overlay ${name} has already been placed by the design; cannot place again") 45 | val it = gen(self, valName.name, input) 46 | placement = Some(it) 47 | it.designOutput 48 | } 49 | } 50 | overlays = overlays ++ Parameters((site, here, up) => { 51 | case x: Field[_] if x eq key => { 52 | val tail = up(key) 53 | if (thunk.isPlaced) { tail } else { thunk +: tail } 54 | } 55 | }) 56 | thunk 57 | } 58 | 59 | // feel free to override this if necessary 60 | lazy val module = new LazyRawModuleImp(this) 61 | } 62 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/SwitchOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | 8 | case class SwitchOverlayParams()(implicit val p: Parameters) 9 | case object SwitchOverlayKey extends Field[Seq[DesignOverlay[SwitchOverlayParams, ModuleValue[UInt]]]](Nil) 10 | 11 | abstract class SwitchOverlay( 12 | val params: SwitchOverlayParams) 13 | extends IOOverlay[UInt, ModuleValue[UInt]] 14 | { 15 | implicit val p = params.p 16 | 17 | def width: Int 18 | def ioFactory = Input(UInt(width.W)) 19 | 20 | val switchSource = shell { BundleBridgeSource(() => UInt(width.W)) } 21 | val switchSink = switchSource.makeSink() 22 | val designOutput = InModuleBody { switchSink.bundle } 23 | } 24 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/UARTOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import sifive.blocks.devices.uart._ 8 | import freechips.rocketchip.subsystem.{BaseSubsystem, PeripheryBus, PeripheryBusKey} 9 | import freechips.rocketchip.tilelink.TLBusWrapper 10 | import freechips.rocketchip.interrupts.IntInwardNode 11 | 12 | //TODO: Can this be combined with UARTAttachParams? 13 | case class UARTOverlayParams(uartParams: UARTParams, divInit: Int, controlBus: TLBusWrapper, intNode: IntInwardNode)(implicit val p: Parameters) 14 | case object UARTOverlayKey extends Field[Seq[DesignOverlay[UARTOverlayParams, TLUART]]](Nil) 15 | 16 | // Tack on cts, rts signals available on some FPGAs. They are currently unused 17 | // by our designs. 18 | class FPGAUARTPortIO extends UARTPortIO { 19 | val rtsn = Output(Bool()) 20 | val ctsn = Input(Bool()) 21 | } 22 | 23 | //class UARTReplacementBundle extends Bundle with HasUARTTopBundleContents 24 | 25 | abstract class UARTOverlay( 26 | val params: UARTOverlayParams) 27 | extends IOOverlay[FPGAUARTPortIO, TLUART] 28 | { 29 | implicit val p = params.p 30 | 31 | def ioFactory = new FPGAUARTPortIO 32 | 33 | val tluart = UART.attach(UARTAttachParams(params.uartParams, params.divInit, params.controlBus, params.intNode)) 34 | val tluartSink = tluart.ioNode.makeSink 35 | val uartSource = BundleBridgeSource(() => new UARTPortIO()) 36 | val uartSink = shell { uartSource.makeSink } 37 | 38 | val designOutput = tluart 39 | 40 | InModuleBody { 41 | val (io, _) = uartSource.out(0) 42 | val tluartport = tluartSink.bundle 43 | io <> tluartport 44 | tluartport.rxd := RegNext(RegNext(io.rxd)) 45 | } 46 | 47 | shell { InModuleBody { 48 | io.txd := uartSink.bundle.txd 49 | uartSink.bundle.rxd := io.rxd 50 | 51 | // Some FPGAs have this, we don't use it. 52 | io.rtsn := false.B 53 | } } 54 | } 55 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/package.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells 3 | 4 | import chisel3._ 5 | import scala.language.implicitConversions 6 | 7 | package object shell { 8 | implicit def boolToIOPin(x: Bool): IOPin = IOPin(x, 0) 9 | implicit def clockToIOPin(x: Clock): IOPin = IOPin(x, 0) 10 | } 11 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/ChipLinkOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import freechips.rocketchip.util._ 7 | import sifive.fpgashells.shell._ 8 | import sifive.fpgashells.ip.xilinx._ 9 | 10 | abstract class ChipLinkXilinxOverlay(params: ChipLinkOverlayParams, rxPhase: Double, txPhase: Double, rxMargin: Double, txMargin: Double) 11 | extends ChipLinkOverlay(params.copy(params = params.params.copy(fpgaReset = true))(params.p), rxPhase, txPhase) 12 | { 13 | def shell: XilinxShell 14 | 15 | InModuleBody { 16 | // Provide reset pulse to initialize b2c_reset (before RX PLL locks) 17 | link.module.io.fpga_reset.foreach { _ := PowerOnResetFPGAOnly(Module.clock) } 18 | } 19 | 20 | shell { InModuleBody { 21 | val (tx, _) = txClock.in(0) 22 | val (rx, _) = rxI.out(0) 23 | val rxEdge = rxI.edges.out(0) 24 | 25 | val oddr = Module(new ODDR(DDR_CLK_EDGE = "SAME_EDGE", SRTYPE = "ASYNC")) 26 | oddr.suggestName(s"${name}_tx_oddr") 27 | io.c2b.clk := oddr.io.Q.asClock 28 | oddr.io.C := tx.clock 29 | oddr.io.CE := true.B 30 | oddr.io.D1 := true.B 31 | oddr.io.D2 := false.B 32 | oddr.io.S := false.B 33 | // We can't use tx.reset here as it waits for all PLLs to lock, 34 | // including RX, which depends on this clock being driven. 35 | // tap.reset only waits for the TX PLL to lock. 36 | oddr.io.R := ResetCatchAndSync(tx.clock, PowerOnResetFPGAOnly(tx.clock)) 37 | 38 | val ibufg = Module(new IBUFG) 39 | ibufg.suggestName(s"${name}_rx_ibufg") 40 | ibufg.io.I := io.b2c.clk 41 | rx.clock := ibufg.io.O 42 | rx.reset := shell.pllReset 43 | 44 | IOPin.of(io).foreach { shell.xdc.addIOStandard(_, "LVCMOS18") } 45 | IOPin.of(io).filterNot(_.element eq io.b2c.clk).foreach { shell.xdc.addIOB(_) } 46 | IOPin.of(io).filter(_.isOutput).foreach { shell.xdc.addSlew(_, "FAST") } 47 | 48 | val timing = IOTiming( 49 | /* The data signals coming from Aloe have: clock - 1.2 <= transition <= clock + 0.8 50 | * min = hold = - 1.2 51 | * max = period - setup = 0.8 52 | */ 53 | minInput = -1.2 - rxMargin, 54 | maxInput = 0.8 + rxMargin, 55 | /* The data signals going to Aloe must have: clock - 1.85 <= NO transition <= clock + 0.65 56 | * min = -hold = -0.65 57 | * max = setup = 1.85 58 | */ 59 | minOutput = -0.65 - txMargin, 60 | maxOutput = 1.85 + txMargin) 61 | 62 | shell.sdc.addClock(s"${name}_b2c_clock", io.b2c.clk, rxEdge.clock.freqMHz, 0.3) 63 | shell.sdc.addDerivedClock(s"${name}_c2b_clock", oddr.io.C, io.c2b.clk) 64 | IOPin.of(io).filter(p => p.isInput && !(p.element eq io.b2c.clk)).foreach { e => 65 | shell.sdc.addIOTiming(e, s"${name}_b2c_clock", timing) 66 | } 67 | IOPin.of(io).filter(p => p.isOutput && !(p.element eq io.c2b.clk)).foreach { e => 68 | shell.sdc.addIOTiming(e, s"${name}_c2b_clock", timing) 69 | } 70 | } } 71 | } 72 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/ClockOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class LVDSClockInputXilinxOverlay(params: ClockInputOverlayParams) 10 | extends LVDSClockInputOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | 14 | shell { InModuleBody { 15 | val ibufds = Module(new IBUFDS) 16 | ibufds.suggestName(s"${name}_ibufds") 17 | 18 | val (c, _) = node.out(0) 19 | ibufds.io.I := io.p 20 | ibufds.io.IB := io.n 21 | c.clock := ibufds.io.O 22 | c.reset := shell.pllReset 23 | } } 24 | } 25 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/JTAGDebugOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class JTAGDebugXilinxOverlay(params: JTAGDebugOverlayParams) 10 | extends JTAGDebugOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | } 14 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/LEDOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class LEDXilinxOverlay(params: LEDOverlayParams, boardPins: Seq[String] = Nil, packagePins: Seq[String] = Nil) 10 | extends LEDOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | val width = boardPins.size + packagePins.size 14 | 15 | shell { InModuleBody { 16 | io := ledSink.bundle // could/should put OBUFs here? 17 | 18 | val cutAt = boardPins.size 19 | val ios = IOPin.of(io) 20 | val boardIOs = ios.take(cutAt) 21 | val packageIOs = ios.drop(cutAt) 22 | 23 | (boardPins zip boardIOs) foreach { case (pin, io) => shell.xdc.addBoardPin (io, pin) } 24 | (packagePins zip packageIOs) foreach { case (pin, io) => shell.xdc.addPackagePin(io, pin) } 25 | } } 26 | } 27 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/SDIOOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class SDIOXilinxOverlay(params: SDIOOverlayParams) 10 | extends SDIOOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | } 14 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/SwitchOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class SwitchXilinxOverlay(params: SwitchOverlayParams, boardPins: Seq[String] = Nil, packagePins: Seq[String] = Nil) 10 | extends SwitchOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | val width = boardPins.size + packagePins.size 14 | 15 | shell { InModuleBody { 16 | val vec = Wire(Vec(width, Bool())) 17 | switchSource.out(0)._1 := vec.asUInt 18 | (vec zip io.toBools).zipWithIndex.foreach { case ((o, i), idx) => 19 | val ibuf = Module(new IBUF) 20 | ibuf.suggestName(s"switch_ibuf_${idx}") 21 | ibuf.io.I := i 22 | o := ibuf.io.O 23 | } 24 | 25 | val cutAt = boardPins.size 26 | val ios = IOPin.of(io) 27 | val boardIOs = ios.take(cutAt) 28 | val packageIOs = ios.drop(cutAt) 29 | 30 | (boardPins zip boardIOs) foreach { case (pin, io) => shell.xdc.addBoardPin (io, pin) } 31 | (packagePins zip packageIOs) foreach { case (pin, io) => shell.xdc.addPackagePin(io, pin) } 32 | } } 33 | } 34 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/UARTOverlay.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.diplomacy._ 6 | import sifive.fpgashells.shell._ 7 | import sifive.fpgashells.ip.xilinx._ 8 | 9 | abstract class UARTXilinxOverlay(params: UARTOverlayParams) 10 | extends UARTOverlay(params) 11 | { 12 | def shell: XilinxShell 13 | } 14 | -------------------------------------------------------------------------------- /fpga-shells/src/main/scala/shell/xilinx/XilinxShell.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.fpgashells.shell.xilinx 3 | 4 | import chisel3._ 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.util._ 8 | import sifive.fpgashells.clocks._ 9 | import sifive.fpgashells.ip.xilinx._ 10 | import sifive.fpgashells.shell._ 11 | 12 | class XDC(val name: String) 13 | { 14 | private var constraints: Seq[() => String] = Nil 15 | protected def addConstraint(command: => String) { constraints = (() => command) +: constraints } 16 | ElaborationArtefacts.add(name, constraints.map(_()).reverse.mkString("\n") + "\n") 17 | 18 | def addBoardPin(io: IOPin, pin: String) { 19 | addConstraint(s"set_property BOARD_PIN {${pin}} ${io.sdcPin}") 20 | } 21 | def addPackagePin(io: IOPin, pin: String) { 22 | addConstraint(s"set_property PACKAGE_PIN {${pin}} ${io.sdcPin}") 23 | } 24 | def addIOStandard(io: IOPin, standard: String) { 25 | addConstraint(s"set_property IOSTANDARD {${standard}} ${io.sdcPin}") 26 | } 27 | def addPullup(io: IOPin) { 28 | addConstraint(s"set_property PULLUP {TRUE} ${io.sdcPin}") 29 | } 30 | def addIOB(io: IOPin) { 31 | if (io.isOutput) { 32 | addConstraint(s"set_property IOB {TRUE} [ get_cells -of_objects [ all_fanin -flat -startpoints_only ${io.sdcPin}]]") 33 | } else { 34 | addConstraint(s"set_property IOB {TRUE} [ get_cells -of_objects [ all_fanout -flat -endpoints_only ${io.sdcPin}]]") 35 | } 36 | } 37 | def addSlew(io: IOPin, speed: String) { 38 | addConstraint(s"set_property SLEW {${speed}} ${io.sdcPin}") 39 | } 40 | def addTermination(io: IOPin, kind: String) { 41 | addConstraint(s"set_property OFFCHIP_TERM {${kind}} ${io.sdcPin}") 42 | } 43 | def clockDedicatedRouteFalse(io: IOPin) { 44 | addConstraint(s"set_property CLOCK_DEDICATED_ROUTE {FALSE} [get_nets ${io.sdcPin}]") 45 | } 46 | } 47 | 48 | abstract class XilinxShell()(implicit p: Parameters) extends IOShell 49 | { 50 | val sdc = new SDC("shell.sdc") 51 | val xdc = new XDC("shell.xdc") 52 | def pllReset: ModuleValue[Bool] 53 | 54 | ElaborationArtefacts.add("shell.vivado.tcl", 55 | """set shell_vivado_tcl [file normalize [info script]] 56 | |set shell_vivado_idx [string last ".shell.vivado.tcl" $shell_vivado_tcl] 57 | |add_files -fileset [current_fileset -constrset] [string replace $shell_vivado_tcl $shell_vivado_idx 999 ".shell.sdc"] 58 | |add_files -fileset [current_fileset -constrset] [string replace $shell_vivado_tcl $shell_vivado_idx 999 ".shell.xdc"] 59 | |""".stripMargin) 60 | } 61 | 62 | abstract class Series7Shell()(implicit p: Parameters) extends XilinxShell 63 | { 64 | val pllFactory = new PLLFactory(this, 7, p => Module(new Series7MMCM(p))) 65 | override def designParameters = super.designParameters.alterPartial { 66 | case PLLFactoryKey => pllFactory 67 | } 68 | } 69 | 70 | abstract class UltraScaleShell()(implicit p: Parameters) extends XilinxShell 71 | { 72 | val pllFactory = new PLLFactory(this, 7, p => Module(new Series7MMCM(p))) 73 | override def designParameters = super.designParameters.alterPartial { 74 | case PLLFactoryKey => pllFactory 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/Makefile: -------------------------------------------------------------------------------- 1 | VIVADO ?= vivado 2 | VIVADOFLAGS := \ 3 | -nojournal -mode batch \ 4 | -source $(fpga_board_script_dir)/board.tcl \ 5 | -source $(fpga_common_script_dir)/prologue.tcl 6 | 7 | # Path to a program in raw binary format to be flashed into the address that the 8 | # bootrom jumps to. 9 | # FIXME: This variable should probably be communicated by a higher-level Makefile 10 | FLASHED_PROGRAM ?= 11 | 12 | # Init project 13 | init = $(FPGA_BUILD_DIR)/.init 14 | $(init): $(fpga_common_script_dir)/init.tcl 15 | mkdir -p $(FPGA_BUILD_DIR) && \ 16 | cd $(FPGA_BUILD_DIR) && \ 17 | VSRCS="$(VSRCS)" IPVIVADOTCLS="$(IPVIVADOTCLS)" $(VIVADO) $(VIVADOFLAGS) -source $< 18 | 19 | .PHONY: init 20 | init: $(init) 21 | 22 | # Generate bitstream 23 | bit := $(FPGA_BUILD_DIR)/obj/$(FPGA_TOP_SYSTEM).bit 24 | $(bit): $(fpga_common_script_dir)/vivado.tcl $(init) 25 | cd $(FPGA_BUILD_DIR) && \ 26 | VSRCS="$(VSRCS)" $(VIVADO) $(VIVADOFLAGS) -source $< 27 | 28 | .PHONY: bit 29 | bit: $(bit) 30 | 31 | # Generate mcs 32 | mcs := $(FPGA_BUILD_DIR)/obj/system.mcs 33 | $(mcs): $(bit) 34 | cd $(FPGA_BUILD_DIR) && \ 35 | $(VIVADO) $(VIVADOFLAGS) $(fpga_common_script_dir)/write_cfgmem.tcl -tclargs $(BOARD) $@ $^ $(FLASHED_PROGRAM) 36 | 37 | .PHONY: mcs 38 | mcs: $(mcs) 39 | 40 | .PHONY: clean 41 | clean:: 42 | rm -rf $(FPGA_BUILD_DIR) 43 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty/constraints/arty-config.xdc: -------------------------------------------------------------------------------- 1 | set_property -dict [list \ 2 | CONFIG_VOLTAGE {3.3} \ 3 | CFGBVS {VCCO} \ 4 | BITSTREAM.CONFIG.SPI_BUSWIDTH {4} \ 5 | ] [current_design] 6 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty/tcl/board.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | set name {arty} 3 | set part_fpga {xc7a35ticsg324-1L} 4 | set part_board {digilentinc.com:arty:part0:1.1} 5 | set bootrom_inst {rom} 6 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty/tcl/ip.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # Modifications copyright (C) 2018-2019 Hex-Five 3 | 4 | create_ip -vendor xilinx.com -library ip -name clk_wiz -module_name mmcm -dir $ipdir -force 5 | set_property -dict [list \ 6 | CONFIG.PRIMITIVE {MMCM} \ 7 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 8 | CONFIG.CLKOUT1_USED {true} \ 9 | CONFIG.CLKOUT2_USED {true} \ 10 | CONFIG.CLKOUT3_USED {true} \ 11 | CONFIG.CLKOUT4_USED {true} \ 12 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {25.00} \ 13 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {8.388} \ 14 | CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {65.000} \ 15 | CONFIG.CLKOUT4_REQUESTED_OUT_FREQ {32.500} \ 16 | ] [get_ips mmcm] 17 | 18 | create_ip -vendor xilinx.com -library ip -name proc_sys_reset -module_name reset_sys -dir $ipdir -force 19 | set_property -dict [list \ 20 | CONFIG.C_EXT_RESET_HIGH {false} \ 21 | CONFIG.C_AUX_RESET_HIGH {false} \ 22 | CONFIG.C_NUM_BUS_RST {1} \ 23 | CONFIG.C_NUM_PERP_RST {1} \ 24 | CONFIG.C_NUM_INTERCONNECT_ARESETN {1} \ 25 | CONFIG.C_NUM_PERP_ARESETN {1} \ 26 | ] [get_ips reset_sys] 27 | 28 | create_ip -vendor xilinx.com -library ip -name ila -module_name ila -dir $ipdir -force 29 | set_property -dict [list \ 30 | CONFIG.C_NUM_OF_PROBES {1} \ 31 | CONFIG.C_TRIGOUT_EN {false} \ 32 | CONFIG.C_TRIGIN_EN {false} \ 33 | CONFIG.C_MONITOR_TYPE {Native} \ 34 | CONFIG.C_ENABLE_ILA_AXI_MON {false} \ 35 | CONFIG.C_PROBE0_WIDTH {4} \ 36 | CONFIG.C_PROBE10_TYPE {1} \ 37 | CONFIG.C_PROBE10_WIDTH {32} \ 38 | CONFIG.C_PROBE11_TYPE {1} \ 39 | CONFIG.C_PROBE11_WIDTH {32} \ 40 | CONFIG.C_PROBE12_TYPE {1} \ 41 | CONFIG.C_PROBE12_WIDTH {64} \ 42 | CONFIG.C_PROBE13_TYPE {1} \ 43 | CONFIG.C_PROBE13_WIDTH {64} \ 44 | CONFIG.C_PROBE14_TYPE {1} \ 45 | CONFIG.C_PROBE14_WIDTH {97} \ 46 | ] [get_ips ila] 47 | 48 | create_ip -vendor xilinx.com -library ip -name axi_ethernetlite -module_name axiethernetlite -dir $ipdir -force 49 | set_property -dict [list \ 50 | CONFIG.C_S_AXI_PROTOCOL {AXI4} \ 51 | CONFIG.AXI_ACLK_FREQ_MHZ {65} \ 52 | CONFIG.C_DUPLEX {1} \ 53 | CONFIG.C_TX_PING_PONG {1} \ 54 | CONFIG.C_RX_PING_PONG {1} \ 55 | CONFIG.C_INCLUDE_INTERNAL_LOOPBACK {0} \ 56 | CONFIG.C_INCLUDE_GLOBAL_BUFFERS {0} \ 57 | CONFIG.C_INCLUDE_MDIO {1} \ 58 | CONFIG.C_S_AXI_ID_WIDTH {1} \ 59 | ] [get_ips axiethernetlite] 60 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty_a7_100/constraints/arty-config.xdc: -------------------------------------------------------------------------------- 1 | set_property -dict [list \ 2 | CONFIG_VOLTAGE {3.3} \ 3 | CFGBVS {VCCO} \ 4 | BITSTREAM.CONFIG.SPI_BUSWIDTH {4} \ 5 | ] [current_design] 6 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty_a7_100/tcl/board.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | set name {arty-a7-100} 3 | set part_fpga {xc7a100ticsg324-1L} 4 | set part_board {digilentinc.com:arty-a7-100:part0:1.0} 5 | set bootrom_inst {rom} 6 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/arty_a7_100/tcl/ip.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # Modifications copyright (C) 2021 Hex Five Security, Inc. 3 | 4 | create_ip -vendor xilinx.com -library ip -name clk_wiz -module_name mmcm -dir $ipdir -force 5 | set_property -dict [list \ 6 | CONFIG.PRIMITIVE {MMCM} \ 7 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 8 | CONFIG.CLKOUT1_USED {true} \ 9 | CONFIG.CLKOUT2_USED {true} \ 10 | CONFIG.CLKOUT3_USED {true} \ 11 | CONFIG.CLKOUT4_USED {true} \ 12 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {25.00} \ 13 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {8.388} \ 14 | CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {65.000} \ 15 | CONFIG.CLKOUT4_REQUESTED_OUT_FREQ {32.500} \ 16 | ] [get_ips mmcm] 17 | 18 | create_ip -vendor xilinx.com -library ip -name proc_sys_reset -module_name reset_sys -dir $ipdir -force 19 | set_property -dict [list \ 20 | CONFIG.C_EXT_RESET_HIGH {false} \ 21 | CONFIG.C_AUX_RESET_HIGH {false} \ 22 | CONFIG.C_NUM_BUS_RST {1} \ 23 | CONFIG.C_NUM_PERP_RST {1} \ 24 | CONFIG.C_NUM_INTERCONNECT_ARESETN {1} \ 25 | CONFIG.C_NUM_PERP_ARESETN {1} \ 26 | ] [get_ips reset_sys] 27 | 28 | create_ip -vendor xilinx.com -library ip -name ila -module_name ila -dir $ipdir -force 29 | set_property -dict [list \ 30 | CONFIG.C_NUM_OF_PROBES {1} \ 31 | CONFIG.C_TRIGOUT_EN {false} \ 32 | CONFIG.C_TRIGIN_EN {false} \ 33 | CONFIG.C_MONITOR_TYPE {Native} \ 34 | CONFIG.C_ENABLE_ILA_AXI_MON {false} \ 35 | CONFIG.C_PROBE0_WIDTH {4} \ 36 | CONFIG.C_PROBE10_TYPE {1} \ 37 | CONFIG.C_PROBE10_WIDTH {32} \ 38 | CONFIG.C_PROBE11_TYPE {1} \ 39 | CONFIG.C_PROBE11_WIDTH {32} \ 40 | CONFIG.C_PROBE12_TYPE {1} \ 41 | CONFIG.C_PROBE12_WIDTH {64} \ 42 | CONFIG.C_PROBE13_TYPE {1} \ 43 | CONFIG.C_PROBE13_WIDTH {64} \ 44 | CONFIG.C_PROBE14_TYPE {1} \ 45 | CONFIG.C_PROBE14_WIDTH {97} \ 46 | ] [get_ips ila] 47 | 48 | create_ip -vendor xilinx.com -library ip -name axi_ethernetlite -module_name axiethernetlite -dir $ipdir -force 49 | set_property -dict [list \ 50 | CONFIG.C_S_AXI_PROTOCOL {AXI4} \ 51 | CONFIG.AXI_ACLK_FREQ_MHZ {65} \ 52 | CONFIG.C_DUPLEX {1} \ 53 | CONFIG.C_TX_PING_PONG {1} \ 54 | CONFIG.C_RX_PING_PONG {1} \ 55 | CONFIG.C_INCLUDE_INTERNAL_LOOPBACK {0} \ 56 | CONFIG.C_INCLUDE_GLOBAL_BUFFERS {0} \ 57 | CONFIG.C_INCLUDE_MDIO {1} \ 58 | CONFIG.C_S_AXI_ID_WIDTH {1} \ 59 | ] [get_ips axiethernetlite] 60 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/bitstream.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Write a bitstream for the current design 4 | write_bitstream -force [file join $wrkdir "${top}.bit"] 5 | 6 | # Save the timing delays for cells in the design in SDF format 7 | write_sdf -force [file join $wrkdir "${top}.sdf"] 8 | 9 | # Export the current netlist in verilog format 10 | write_verilog -mode timesim -force [file join ${wrkdir} "${top}.v"] 11 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/boards.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # Modifications copyright (C) 2021 Hex-Five 3 | 4 | namespace eval ::program::boards {} 5 | 6 | set ::program::boards::spec [dict create \ 7 | arty [dict create iface spix4 size 16 bitaddr 0x0 memdev {s25fl128sxxxxxx0-spi-x1_x2_x4}] \ 8 | arty_a7_100 [dict create iface spix4 size 16 bitaddr 0x0 memdev {s25fl128sxxxxxx0-spi-x1_x2_x4}] \ 9 | vc707 [dict create iface bpix16 size 128 bitaddr 0x3000000 ] \ 10 | vcu118 [dict create iface spix8 size 256 bitaddr 0x0 memdev {mt25qu01g-spi-x1_x2_x4_x8}]] 11 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/init.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Include helper functions 4 | source [file join $scriptdir "util.tcl"] 5 | 6 | # Create the diretory for IPs 7 | file mkdir $ipdir 8 | 9 | # Update the IP catalog 10 | update_ip_catalog -rebuild 11 | 12 | # Generate IP implementations. Vivado TCL emitted from Chisel Blackboxes 13 | foreach ip_vivado_tcl $ip_vivado_tcls { 14 | source $ip_vivado_tcl 15 | } 16 | # Optional board-specific ip script 17 | set boardiptcl [file join $boarddir tcl ip.tcl] 18 | if {[file exists $boardiptcl]} { 19 | source $boardiptcl 20 | } 21 | 22 | # AR 58526 23 | set xci_files [get_files -all {*.xci}] 24 | foreach xci_file $xci_files { 25 | set_property GENERATE_SYNTH_CHECKPOINT {false} -quiet $xci_file 26 | } 27 | 28 | # Get a list of IPs in the current design 29 | set obj [get_ips] 30 | 31 | # Generate target data for the inlcuded IPs in the design 32 | generate_target all $obj 33 | 34 | # Export the IP user files 35 | export_ip_user_files -of_objects $obj -no_script -force 36 | 37 | # Get the list of active source and constraint files 38 | set obj [current_fileset] 39 | 40 | #Xilinx bug workaround 41 | #scrape IP tree for directories containing .vh files 42 | #[get_property include_dirs] misses all IP core subdirectory includes if user has specified -dir flag in create_ip 43 | set property_include_dirs [get_property include_dirs $obj] 44 | 45 | # Include generated files for the IPs in the design 46 | set ip_include_dirs [concat $property_include_dirs [findincludedir $ipdir "*.vh"]] 47 | set ip_include_dirs [concat $ip_include_dirs [findincludedir $srcdir "*.h"]] 48 | set ip_include_dirs [concat $ip_include_dirs [findincludedir $srcdir "*.vh"]] 49 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/opt.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Optimize the netlist 4 | opt_design -directive Explore 5 | 6 | # Checkpoint the current design 7 | write_checkpoint -force [file join $wrkdir post_opt] 8 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/place.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Place the current design 4 | place_design -directive Explore 5 | 6 | # Optimize the current placed netlist 7 | phys_opt_design -directive Explore 8 | 9 | # Optimize dynamic power using intelligent clock gating 10 | power_opt_design 11 | 12 | # Checkpoint the current design 13 | write_checkpoint -force [file join $wrkdir post_place] 14 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/prologue.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Process command line arguments 4 | # http://wiki.tcl.tk/1730 5 | set ip_vivado_tcls {} 6 | 7 | while {[llength $argv]} { 8 | set argv [lassign $argv[set argv {}] flag] 9 | switch -glob $flag { 10 | -top-module { 11 | set argv [lassign $argv[set argv {}] top] 12 | } 13 | -F { 14 | # This should be a simple file format with one filepath per line 15 | set argv [lassign $argv[set argv {}] vsrc_manifest] 16 | } 17 | -board { 18 | set argv [lassign $argv[set argv {}] board] 19 | } 20 | -ip-vivado-tcls { 21 | set argv [lassign $argv[set argv {}] ip_vivado_tcls] 22 | } 23 | -pre-impl-debug-tcl { 24 | set argv [lassign $argv[set argv {}] pre_impl_debug_tcl] 25 | } 26 | -post-impl-debug-tcl { 27 | set argv [lassign $argv[set argv {}] post_impl_debug_tcl] 28 | } 29 | default { 30 | return -code error [list {unknown option} $flag] 31 | } 32 | } 33 | } 34 | 35 | if {![info exists top]} { 36 | return -code error [list {--top-module option is required}] 37 | } 38 | 39 | if {![info exists vsrc_manifest]} { 40 | return -code error [list {-F option is required}] 41 | } 42 | 43 | if {![info exists board]} { 44 | return -code error [list {--board option is required}] 45 | } 46 | 47 | # Set the variable for all the common files 48 | set commondir [file dirname $scriptdir] 49 | 50 | # Set the variable that points to board specific files 51 | set boarddir [file join [file dirname $commondir] $board] 52 | source [file join $boarddir tcl board.tcl] 53 | 54 | # Set the variable that points to board constraint files 55 | set constraintsdir [file join $boarddir constraints] 56 | 57 | # Set the variable that points to common verilog sources 58 | set srcdir [file join $commondir vsrc] 59 | 60 | # Creates a work directory 61 | set wrkdir [file join [pwd] obj] 62 | 63 | # Create the directory for IPs 64 | set ipdir [file join $wrkdir ip] 65 | 66 | # Create an in-memory project 67 | create_project -part $part_fpga -force $top 68 | 69 | # Set the board part, target language, default library, and IP directory 70 | # paths for the current project 71 | set_property -dict [list \ 72 | BOARD_PART $part_board \ 73 | TARGET_LANGUAGE {Verilog} \ 74 | DEFAULT_LIB {xil_defaultlib} \ 75 | IP_REPO_PATHS $ipdir \ 76 | ] [current_project] 77 | 78 | if {[get_filesets -quiet sources_1] eq ""} { 79 | create_fileset -srcset sources_1 80 | } 81 | set obj [current_fileset] 82 | 83 | # Add verilog files from manifest 84 | proc load_vsrc_manifest {obj vsrc_manifest} { 85 | set fp [open $vsrc_manifest r] 86 | set files [lsearch -not -exact -all -inline [split [read $fp] "\n"] {}] 87 | set relative_files {} 88 | foreach path $files { 89 | if {[string match {/*} $path]} { 90 | lappend relative_files $path 91 | } elseif {![string match {#*} $path]} { 92 | lappend relative_files [file join [file dirname $vsrc_manifest] $path] 93 | } 94 | } 95 | add_files -norecurse -fileset $obj {*}$relative_files 96 | close $fp 97 | } 98 | 99 | load_vsrc_manifest $obj $vsrc_manifest 100 | 101 | # Add IP Vivado TCL 102 | if {$ip_vivado_tcls ne {}} { 103 | # Split string into words even with multiple consecutive spaces 104 | # http://wiki.tcl.tk/989 105 | set ip_vivado_tcls [regexp -inline -all -- {\S+} $ip_vivado_tcls] 106 | } 107 | 108 | if {[get_filesets -quiet sim_1] eq ""} { 109 | create_fileset -simset sim_1 110 | } 111 | set obj [current_fileset -simset] 112 | 113 | if {[get_filesets -quiet constrs_1] eq ""} { 114 | create_fileset -constrset constrs_1 115 | } 116 | 117 | set obj [current_fileset -constrset] 118 | add_files -quiet -norecurse -fileset $obj [lsort [glob -directory $constraintsdir -nocomplain {*.xdc}]] 119 | add_files -quiet -norecurse -fileset $obj [lsort [glob -directory $constraintsdir -nocomplain {*.tcl}]] 120 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/report.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Create a report directory 4 | set rptdir [file join $wrkdir report] 5 | file mkdir $rptdir 6 | 7 | # Create a datasheet for the current design 8 | report_datasheet -file [file join $rptdir datasheet.txt] 9 | 10 | # Report utilization of the current device 11 | set rptutil [file join $rptdir utilization.txt] 12 | report_utilization -hierarchical -file $rptutil 13 | 14 | # Report information about clock nets in the design 15 | report_clock_utilization -file $rptutil -append 16 | 17 | # Report the RAM resources utilized in the implemented design 18 | report_ram_utilization -file $rptutil -append -detail 19 | 20 | # Report timing summary for a max of 10 paths per group 21 | report_timing_summary -file [file join $rptdir timing.txt] -max_paths 10 22 | 23 | # Report the highest fanout of nets in the implemented design 24 | report_high_fanout_nets -file [file join $rptdir fanout.txt] -timing -load_types -max_nets 25 25 | 26 | # Run DRC 27 | report_drc -file [file join $rptdir drc.txt] 28 | 29 | # Report details of the IO banks in the design 30 | report_io -file [file join $rptdir io.txt] 31 | 32 | # Report a table of all clocks in the design 33 | report_clocks -file [file join $rptdir clocks.txt] 34 | 35 | # Fail loudly if timing not met 36 | # 37 | # We would ideally elevate critical warning Route 35-39 to an error, but it is 38 | # currently not being emitted with our flow for some reason. 39 | # https://forums.xilinx.com/t5/Implementation/Making-timing-violations-fatal-to-the-Vivado-build/m-p/716957#M15979 40 | set timing_slack [get_property SLACK [get_timing_paths]] 41 | if {$timing_slack < 0} { 42 | puts "Failed to meet timing by $timing_slack, see [file join $rptdir timing.txt]" 43 | exit 1 44 | } 45 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/route.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Route the current design 4 | route_design -directive Explore 5 | 6 | # Optimize the current design post routing 7 | phys_opt_design -directive Explore 8 | 9 | # Checkpoint the current design 10 | write_checkpoint -force [file join $wrkdir post_route] 11 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/synth.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Read the specified list of IP files 4 | read_ip [glob -directory $ipdir [file join * {*.xci}]] 5 | 6 | # Synthesize the design 7 | synth_design -top $top -flatten_hierarchy rebuilt 8 | 9 | # Checkpoint the current design 10 | write_checkpoint -force [file join $wrkdir post_synth] 11 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/util.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Helper function that recursively includes files given a directory and a 4 | # pattern/suffix extensions 5 | proc recglob { basedir pattern } { 6 | set dirlist [glob -nocomplain -directory $basedir -type d *] 7 | set findlist [glob -nocomplain -directory $basedir $pattern] 8 | foreach dir $dirlist { 9 | set reclist [recglob $dir $pattern] 10 | set findlist [concat $findlist $reclist] 11 | } 12 | return $findlist 13 | } 14 | 15 | # Helper function to find all subdirectories containing ".vh" files 16 | proc findincludedir { basedir pattern } { 17 | set vhfiles [recglob $basedir $pattern] 18 | set vhdirs {} 19 | foreach match $vhfiles { 20 | lappend vhdirs [file dir $match] 21 | } 22 | set uniquevhdirs [lsort -unique $vhdirs] 23 | return $uniquevhdirs 24 | } 25 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/vivado.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | # Set the variable for the directory that includes all scripts 4 | set scriptdir [file dirname [info script]] 5 | 6 | # Set up variables and Vivado objects 7 | source [file join $scriptdir "prologue.tcl"] 8 | 9 | # Initialize Vivado project files 10 | source [file join $scriptdir "init.tcl"] 11 | 12 | # Synthesize the design 13 | source [file join $scriptdir "synth.tcl"] 14 | 15 | # Pre-implementation debug 16 | if {[info exists pre_impl_debug_tcl]} { 17 | source [file join $scriptdir $pre_impl_debug_tcl] 18 | } 19 | 20 | # Post synthesis optimization 21 | source [file join $scriptdir "opt.tcl"] 22 | 23 | # Place the design 24 | source [file join $scriptdir "place.tcl"] 25 | 26 | # Route the design 27 | source [file join $scriptdir "route.tcl"] 28 | 29 | # Generate bitstream and save verilog netlist 30 | source [file join $scriptdir "bitstream.tcl"] 31 | 32 | # Post-implementation debug 33 | if {[info exists post_impl_debug_tcl)]} { 34 | source [file join $scriptdir $post_impl_debug_tcl] 35 | } 36 | 37 | # Create reports for the current implementation 38 | source [file join $scriptdir "report.tcl"] 39 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/tcl/write_cfgmem.tcl: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | # 3 | # Create an MCS-format memory configuration file from a bitstream and an 4 | # optional data file. 5 | 6 | set script_program_dir [file dirname [info script]] 7 | source [file join $script_program_dir {boards.tcl}] 8 | 9 | if {$argc < 3 || $argc > 4} { 10 | puts $argc 11 | puts {Error: Invalid number of arguments} 12 | puts {Usage: write_cfgmem.tcl board mcsfile bitfile [datafile]} 13 | exit 1 14 | } 15 | lassign $argv board mcsfile bitfile datafile 16 | 17 | if {![dict exists $::program::boards::spec $board]} { 18 | puts {Unsupported board} 19 | exit 1 20 | } 21 | set board [dict get $::program::boards::spec $board] 22 | 23 | write_cfgmem -format mcs -interface [dict get $board iface] -size [dict get $board size] \ 24 | -loadbit "up [dict get $board bitaddr] $bitfile" \ 25 | -loaddata [expr {$datafile ne "" ? "up 0x400000 $datafile" : ""}] \ 26 | -file $mcsfile -force 27 | -------------------------------------------------------------------------------- /fpga-shells/xilinx/common/vsrc/PowerOnResetFPGAOnly.v: -------------------------------------------------------------------------------- 1 | // See LICENSE file for license details. 2 | (* keep_hierarchy = "yes" *) module PowerOnResetFPGAOnly( 3 | input clock, 4 | (* dont_touch = "true" *) output power_on_reset 5 | ); 6 | reg reset; 7 | assign power_on_reset = reset; 8 | 9 | initial begin 10 | reset <= 1'b1; 11 | end 12 | 13 | always @(posedge clock) begin 14 | reset <= 1'b0; 15 | end 16 | endmodule 17 | -------------------------------------------------------------------------------- /sifive-blocks/.gitignore: -------------------------------------------------------------------------------- 1 | # sbt build files 2 | /target 3 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/resources/BlackBoxDelayBuffer.v: -------------------------------------------------------------------------------- 1 | // This module is added as a reference only. Please add library 2 | // cells for delay buffers and muxes from the foundry that is fabricating your SoC. 3 | 4 | module BlackBoxDelayBuffer ( in, mux_out, out, sel 5 | ); 6 | 7 | input in; 8 | output mux_out; 9 | output out; 10 | input [4:0] sel; 11 | 12 | assign mux_out = in; 13 | assign out = in; 14 | endmodule 15 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/Bundles.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.util.{rightOR,GenericParameterizedBundle} 6 | 7 | class WideDataLayerPortLane(params: ChipLinkParams) extends GenericParameterizedBundle(params) { 8 | val clk = Clock(OUTPUT) 9 | val rst = Bool(OUTPUT) 10 | val send = Bool(OUTPUT) 11 | val data = UInt(OUTPUT, width=params.dataBits) 12 | } 13 | 14 | class WideDataLayerPort(params: ChipLinkParams) extends GenericParameterizedBundle(params) { 15 | val c2b = new WideDataLayerPortLane(params) 16 | val b2c = new WideDataLayerPortLane(params).flip 17 | } 18 | 19 | class DataLayer(params: ChipLinkParams) extends GenericParameterizedBundle(params) { 20 | val data = UInt(OUTPUT, width=params.dataBits) 21 | val last = Bool(OUTPUT) 22 | val beats = UInt(OUTPUT, width=params.xferBits + 1) 23 | } 24 | 25 | class CreditBump(params: ChipLinkParams) extends GenericParameterizedBundle(params) { 26 | val a = UInt(OUTPUT, width = params.creditBits) 27 | val b = UInt(OUTPUT, width = params.creditBits) 28 | val c = UInt(OUTPUT, width = params.creditBits) 29 | val d = UInt(OUTPUT, width = params.creditBits) 30 | val e = UInt(OUTPUT, width = params.creditBits) 31 | def X: Seq[UInt] = Seq(a, b, c, d, e) 32 | 33 | // saturating addition 34 | def +(that: CreditBump): CreditBump = { 35 | val out = Wire(new CreditBump(params)) 36 | (out.X zip (X zip that.X)) foreach { case (o, (x, y)) => 37 | val z = x +& y 38 | o := Mux((z >> params.creditBits).orR, ~UInt(0, width=params.creditBits), z) 39 | } 40 | out 41 | } 42 | 43 | // Send the MSB of the credits 44 | def toHeader: (UInt, CreditBump) = { 45 | def msb(x: UInt) = { 46 | val mask = rightOR(x) >> 1 47 | val msbOH = ~(~x | mask) 48 | val msb = OHToUInt(msbOH << 1, params.creditBits + 1) // 0 = 0, 1 = 1, 2 = 4, 3 = 8, ... 49 | val pad = (msb | UInt(0, width=5))(4,0) 50 | (pad, x & mask) 51 | } 52 | val (a_msb, a_rest) = msb(a) 53 | val (b_msb, b_rest) = msb(b) 54 | val (c_msb, c_rest) = msb(c) 55 | val (d_msb, d_rest) = msb(d) 56 | val (e_msb, e_rest) = msb(e) 57 | val header = Cat( 58 | e_msb, d_msb, c_msb, b_msb, a_msb, 59 | UInt(0, width = 4), // padding 60 | UInt(5, width = 3)) 61 | 62 | val out = Wire(new CreditBump(params)) 63 | out.a := a_rest 64 | out.b := b_rest 65 | out.c := c_rest 66 | out.d := d_rest 67 | out.e := e_rest 68 | (header, out) 69 | } 70 | } 71 | 72 | object CreditBump { 73 | def apply(params: ChipLinkParams, x: Int): CreditBump = { 74 | val v = UInt(x, width = params.creditBits) 75 | val out = Wire(new CreditBump(params)) 76 | out.X.foreach { _ := v } 77 | out 78 | } 79 | 80 | def apply(params: ChipLinkParams, header: UInt): CreditBump = { 81 | def convert(x: UInt) = 82 | Mux(x > UInt(params.creditBits), 83 | ~UInt(0, width = params.creditBits), 84 | UIntToOH(x, params.creditBits + 1) >> 1) 85 | val out = Wire(new CreditBump(params)) 86 | out.a := convert(header(11, 7)) 87 | out.b := convert(header(16, 12)) 88 | out.c := convert(header(21, 17)) 89 | out.d := convert(header(26, 22)) 90 | out.e := convert(header(31, 27)) 91 | out 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/CAM.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class CAM(keys: Int, dataBits: Int) extends Module 9 | { 10 | val io = new Bundle { 11 | // alloc.valid => allocate a key 12 | // alloc.ready => a key is avilable 13 | val alloc = Decoupled(UInt(width = dataBits)).flip 14 | val key = UInt(OUTPUT, width = log2Ceil(keys)) 15 | // free.valid => release the key 16 | val free = Valid(UInt(width = log2Ceil(keys))).flip 17 | val data = UInt(OUTPUT, width = dataBits) 18 | } 19 | 20 | val free = RegInit(UInt((BigInt(1) << keys) - 1, width = keys)) 21 | val data = Mem(keys, UInt(width = dataBits)) 22 | 23 | val free_sel = ~(leftOR(free, keys) << 1) & free 24 | io.key := OHToUInt(free_sel, keys) 25 | 26 | io.alloc.ready := free.orR 27 | when (io.alloc.fire()) { data.write(io.key, io.alloc.bits) } 28 | 29 | // Support free in same cycle as alloc 30 | val bypass = io.alloc.fire() && io.free.bits === io.key 31 | io.data := Mux(bypass, io.alloc.bits, data(io.free.bits)) 32 | 33 | // Update CAM usage 34 | val clr = Mux(io.alloc.fire(), free_sel, UInt(0)) 35 | val set = Mux(io.free.valid, UIntToOH(io.free.bits), UInt(0)) 36 | free := (free & ~clr) | set 37 | } 38 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/Parameters.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.{Field, Parameters} 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.tilelink._ 8 | import freechips.rocketchip.util.AsyncQueueParams 9 | 10 | case class ChipLinkParams(TLUH: Seq[AddressSet], TLC: Seq[AddressSet], sourceBits: Int = 6, sinkBits: Int = 5, syncTX: Boolean = false, fpgaReset: Boolean = false) 11 | { 12 | val domains = 8 // hard-wired into chiplink protocol 13 | require (sourceBits >= log2Ceil(domains)) 14 | require (sinkBits >= 0) 15 | val sources = 1 << sourceBits 16 | val sinks = 1 << sinkBits 17 | val sourcesPerDomain = sources / domains 18 | val latency = 8 // ChipLink has at least 4 cycles of synchronization per side 19 | val dataBytes = 4 20 | val dataBits = dataBytes*8 21 | val clSourceBits = 16 22 | val clSinkBits = 16 23 | val crossing = AsyncQueueParams() 24 | val Qdepth = 8192 / dataBytes 25 | val maxXfer = 4096 26 | val xferBits = log2Ceil(maxXfer) 27 | val creditBits = 20 // use saturating addition => we can exploit at most 1MB of buffers 28 | val addressBits = 64 29 | require (log2Ceil(Qdepth + 1) <= creditBits) 30 | 31 | // Protocol supported operations: 32 | val noXfer = TransferSizes.none 33 | val fullXfer = TransferSizes(1, 64) // !!! 4096) 34 | val acqXfer = TransferSizes(64, 64) 35 | val atomicXfer = TransferSizes(1, 8) 36 | 37 | } 38 | 39 | case object ChipLinkKey extends Field[Seq[ChipLinkParams]] 40 | 41 | case class TXN(domain: Int, source: Int) 42 | case class ChipLinkInfo(params: ChipLinkParams, edgeIn: TLEdge, edgeOut: TLEdge, errorDev: AddressSet) 43 | { 44 | // TL source => CL TXN 45 | val sourceMap: Map[Int, TXN] = { 46 | var alloc = 1 47 | val domains = Array.fill(params.domains) { 0 } 48 | println("ChipLink source mapping CLdomain CLsource <= TLsource:") 49 | val out = Map() ++ edgeIn.client.clients.flatMap { c => 50 | // If the client needs order, pick a domain for it 51 | val domain = if (c.requestFifo) alloc else 0 52 | val offset = domains(domain) 53 | println(s"\t${domain} [${offset}, ${offset + c.sourceId.size}) <= [${c.sourceId.start}, ${c.sourceId.end}):\t${c.name}") 54 | if (c.requestFifo) { 55 | alloc = alloc + 1 56 | if (alloc == params.domains) alloc = 1 57 | } 58 | c.sourceId.range.map { id => 59 | val source = domains(domain) 60 | domains(domain) = source + 1 61 | (id, TXN(domain, source)) 62 | } 63 | } 64 | println("") 65 | out 66 | } 67 | 68 | def mux(m: Map[Int, Int]): Vec[UInt] = { 69 | val maxKey = m.keys.max 70 | val maxVal = m.values.max 71 | val valBits = log2Up(maxVal + 1) 72 | val out = Wire(Vec(maxKey + 1, UInt(width = valBits))) 73 | m.foreach { case (k, v) => out(k) := UInt(v, width = valBits) } 74 | out 75 | } 76 | 77 | // Packet format; little-endian 78 | def encode(format: UInt, opcode: UInt, param: UInt, size: UInt, domain: UInt, source: UInt): UInt = { 79 | def fmt(x: UInt, w: Int) = (x | UInt(0, width=w))(w-1, 0) 80 | Cat( 81 | fmt(source, 16), 82 | fmt(domain, 3), 83 | fmt(size, 4), 84 | fmt(param, 3), 85 | fmt(opcode, 3), 86 | fmt(format, 3)) 87 | } 88 | 89 | def decode(x: UInt): Seq[UInt] = { 90 | val format = x( 2, 0) 91 | val opcode = x( 5, 3) 92 | val param = x( 8, 6) 93 | val size = x(12, 9) 94 | val domain = x(15, 13) 95 | val source = x(31, 16) 96 | Seq(format, opcode, param, size, domain, source) 97 | } 98 | 99 | def size2beats(size: UInt): UInt = { 100 | val shift = log2Ceil(params.dataBytes) 101 | Cat(UIntToOH(size|UInt(0, width=4), params.xferBits + 1) >> (shift + 1), size <= UInt(shift)) 102 | } 103 | 104 | def mask2beats(size: UInt): UInt = { 105 | val shift = log2Ceil(params.dataBytes*8) 106 | Cat(UIntToOH(size|UInt(0, width=4), params.xferBits + 1) >> (shift + 1), size <= UInt(shift)) 107 | } 108 | 109 | def beats1(x: UInt, forceFormat: Option[UInt] = None): UInt = { 110 | val Seq(format, opcode, _, size, _, _) = decode(x) 111 | val beats = size2beats(size) 112 | val masks = mask2beats(size) 113 | val grant = opcode === TLMessages.Grant || opcode === TLMessages.GrantData 114 | val partial = opcode === TLMessages.PutPartialData 115 | val a = Mux(opcode(2), UInt(0), beats) + UInt(2) + Mux(partial, masks, UInt(0)) 116 | val b = Mux(opcode(2), UInt(0), beats) + UInt(2) + Mux(partial, masks, UInt(0)) 117 | val c = Mux(opcode(0), beats, UInt(0)) + UInt(2) 118 | val d = Mux(opcode(0), beats, UInt(0)) + grant.asUInt 119 | val e = UInt(0) 120 | val f = UInt(0) 121 | Vec(a, b, c, d, e, f)(forceFormat.getOrElse(format)) 122 | } 123 | 124 | def firstlast(x: DecoupledIO[UInt], forceFormat: Option[UInt] = None): (Bool, Bool) = { 125 | val count = RegInit(UInt(0)) 126 | val beats = beats1(x.bits, forceFormat) 127 | val first = count === UInt(0) 128 | val last = count === UInt(1) || (first && beats === UInt(0)) 129 | when (x.fire()) { count := Mux(first, beats, count - UInt(1)) } 130 | (first, last) 131 | } 132 | 133 | // You can't just unilaterally use error, because this would misalign the mask 134 | def makeError(legal: Bool, address: UInt): UInt = { 135 | val alignBits = log2Ceil(errorDev.alignment) 136 | Cat( 137 | Mux(legal, address, UInt(errorDev.base))(params.addressBits-1, alignBits), 138 | address(alignBits-1, 0)) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/Partial.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class ParitalExtractor[T <: TLDataChannel](gen: T) extends Module 9 | { 10 | val io = new Bundle { 11 | val last = Bool(INPUT) 12 | val i = Decoupled(gen).flip 13 | val o = Decoupled(gen) 14 | } 15 | 16 | io.o <> io.i 17 | 18 | // Grab references to the fields we care about 19 | val (i_opcode, i_data) = io.i.bits match { 20 | case a: TLBundleA => (a.opcode, a.data) 21 | case b: TLBundleB => (b.opcode, b.data) 22 | } 23 | val (o_data, o_mask) = io.o.bits match { 24 | case a: TLBundleA => (a.data, a.mask) 25 | case b: TLBundleB => (b.data, b.mask) 26 | } 27 | 28 | val state = RegInit(UInt(0, width=4)) // number of nibbles; [0,8] 29 | val shift = Reg(UInt(width=32)) 30 | val enable = i_opcode === TLMessages.PutPartialData 31 | val empty = state === UInt(0) 32 | 33 | when (enable) { 34 | val wide = shift | (i_data << (state << 2)) 35 | o_data := Vec.tabulate(4) { i => wide(9*(i+1)-1, 9*i+1) } .asUInt 36 | o_mask := Vec.tabulate(4) { i => wide(9*i) } .asUInt 37 | 38 | // Swallow beat if we have no nibbles 39 | when (empty) { 40 | io.i.ready := Bool(true) 41 | io.o.valid := Bool(false) 42 | } 43 | 44 | // Update the FSM 45 | when (io.i.fire()) { 46 | shift := Mux(empty, i_data, wide >> 36) 47 | state := state - UInt(1) 48 | when (empty) { state := UInt(8) } 49 | when (io.last) { state := UInt(0) } 50 | } 51 | } 52 | } 53 | 54 | class PartialInjector[T <: TLDataChannel](gen: T) extends Module 55 | { 56 | val io = new Bundle { 57 | val i_last = Bool(INPUT) 58 | val o_last = Bool(OUTPUT) 59 | val i = Decoupled(gen).flip 60 | val o = Decoupled(gen) 61 | } 62 | 63 | io.o <> io.i 64 | 65 | // Grab references to the fields we care about 66 | val (i_opcode, i_data, i_mask) = io.i.bits match { 67 | case a: TLBundleA => (a.opcode, a.data, a.mask) 68 | case b: TLBundleB => (b.opcode, b.data, b.mask) 69 | } 70 | val o_data = io.o.bits match { 71 | case a: TLBundleA => a.data 72 | case b: TLBundleB => b.data 73 | } 74 | 75 | val state = RegInit(UInt(0, width=4)) // number of nibbles; [0,8] 76 | val shift = RegInit(UInt(0, width=32)) 77 | val full = state(3) 78 | val partial = i_opcode === TLMessages.PutPartialData 79 | 80 | val last = RegInit(Bool(false)) 81 | io.o_last := Mux(partial, last, io.i_last) 82 | 83 | when (partial) { 84 | val bytes = Seq.tabulate(4) { i => i_data(8*(i+1)-1, 8*i) } 85 | val bits = i_mask.toBools 86 | val mixed = Cat(Seq(bits, bytes).transpose.flatten.reverse) 87 | val wide = shift | (mixed << (state << 2)) 88 | o_data := wide 89 | 90 | // Inject a beat 91 | when ((io.i_last || full) && !last) { 92 | io.i.ready := Bool(false) 93 | } 94 | 95 | // Update the FSM 96 | when (io.o.fire()) { 97 | shift := wide >> 32 98 | state := state + UInt(1) 99 | when (full || last) { 100 | state := UInt(0) 101 | shift := UInt(0) 102 | } 103 | last := io.i_last && !last 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/RX.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class RX(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val b2c_send = Bool(INPUT) 12 | val b2c_data = UInt(INPUT, info.params.dataBits) 13 | val a = new AsyncBundle(UInt(width = info.params.dataBits), info.params.crossing) 14 | val b = new AsyncBundle(UInt(width = info.params.dataBits), info.params.crossing) 15 | val c = new AsyncBundle(UInt(width = info.params.dataBits), info.params.crossing) 16 | val d = new AsyncBundle(UInt(width = info.params.dataBits), info.params.crossing) 17 | val e = new AsyncBundle(UInt(width = info.params.dataBits), info.params.crossing) 18 | val rxc = new AsyncBundle(new CreditBump(info.params), AsyncQueueParams.singleton()) 19 | val txc = new AsyncBundle(new CreditBump(info.params), AsyncQueueParams.singleton()) 20 | } 21 | 22 | // Immediately register our input data 23 | val b2c_data = RegNext(RegNext(io.b2c_data)) 24 | val b2c_send = RegNext(RegNext(io.b2c_send), Bool(false)) 25 | // b2c_send is NOT cleared on the first RegNext because this module's reset has a flop on it 26 | 27 | // Fit b2c into the firstlast API 28 | val beat = Wire(Decoupled(UInt(width = info.params.dataBits))) 29 | beat.bits := b2c_data 30 | beat.valid := b2c_send 31 | beat.ready := Bool(true) 32 | 33 | // Select the correct HellaQueue for the request 34 | val (first, _) = info.firstlast(beat) 35 | val formatBits = beat.bits(2, 0) 36 | val formatValid = beat.fire() && first 37 | val format = Mux(formatValid, formatBits, RegEnable(formatBits, formatValid)) 38 | val formatOH = UIntToOH(format) 39 | 40 | // Create the receiver buffers 41 | val hqa = Module(new HellaQueue(info.params.Qdepth)(beat.bits)) 42 | val hqb = Module(new HellaQueue(info.params.Qdepth)(beat.bits)) 43 | val hqc = Module(new HellaQueue(info.params.Qdepth)(beat.bits)) 44 | val hqd = Module(new HellaQueue(info.params.Qdepth)(beat.bits)) 45 | val hqe = Module(new HellaQueue(info.params.Qdepth)(beat.bits)) 46 | 47 | // Use these to save some typing; function to prevent renaming 48 | private def hqX = Seq(hqa, hqb, hqc, hqd, hqe) 49 | private def ioX = Seq(io.a, io.b, io.c, io.d, io.e) 50 | 51 | // Enqueue to the HellaQueues 52 | (formatOH.toBools zip hqX) foreach { case (sel, hq) => 53 | hq.io.enq.valid := beat.valid && sel 54 | hq.io.enq.bits := beat.bits 55 | assert (!hq.io.enq.valid || hq.io.enq.ready) // overrun impossible 56 | } 57 | 58 | // Send HellaQueue output to their respective FSMs 59 | (hqX zip ioX) foreach { case (hq, io) => 60 | io <> ToAsyncBundle(hq.io.deq, info.params.crossing) 61 | } 62 | 63 | // Credits we need to hand-off to the TX FSM 64 | val tx = RegInit(CreditBump(info.params, 0)) 65 | val rx = RegInit(CreditBump(info.params, info.params.Qdepth)) 66 | 67 | // Constantly transmit credit updates 68 | val txOut = Wire(Decoupled(new CreditBump(info.params))) 69 | val rxOut = Wire(Decoupled(new CreditBump(info.params))) 70 | txOut.valid := Bool(true) 71 | rxOut.valid := Bool(true) 72 | txOut.bits := tx 73 | rxOut.bits := rx 74 | io.txc <> ToAsyncBundle(txOut, AsyncQueueParams.singleton()) 75 | io.rxc <> ToAsyncBundle(rxOut, AsyncQueueParams.singleton()) 76 | 77 | // Generate new RX credits as the HellaQueues drain 78 | val rxInc = Wire(new CreditBump(info.params)) 79 | (hqX zip rxInc.X) foreach { case (hq, inc) => 80 | inc := hq.io.deq.fire().asUInt 81 | } 82 | 83 | // Generate new TX credits as we receive F-format messages 84 | val txInc = Mux(beat.valid && formatOH(5), CreditBump(info.params, beat.bits), CreditBump(info.params, 0)) 85 | 86 | // As we hand-over credits, reset the counters 87 | tx := tx + txInc 88 | rx := rx + rxInc 89 | when (txOut.fire()) { tx := txInc } 90 | when (rxOut.fire()) { rx := rxInc } 91 | } 92 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SinkA.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | 7 | class SinkA(info: ChipLinkInfo) extends Module 8 | { 9 | val io = new Bundle { 10 | val a = Decoupled(new TLBundleA(info.edgeIn.bundle)).flip 11 | val q = Decoupled(new DataLayer(info.params)) 12 | } 13 | 14 | // Map TileLink sources to ChipLink sources+domain 15 | val tl2cl = info.sourceMap 16 | val source = info.mux(tl2cl.mapValues(_.source)) 17 | val domain = info.mux(tl2cl.mapValues(_.domain)) 18 | 19 | // We need a Q because we stall the channel while serializing it's header 20 | val inject = Module(new PartialInjector(io.a.bits)) 21 | inject.io.i <> Queue(io.a, 1, flow=true) 22 | inject.io.i_last := info.edgeIn.last(inject.io.i) 23 | val a = inject.io.o 24 | val a_last = inject.io.o_last 25 | val a_hasData = info.edgeIn.hasData(a.bits) 26 | val a_partial = a.bits.opcode === TLMessages.PutPartialData 27 | 28 | // A simple FSM to generate the packet components 29 | val state = RegInit(UInt(0, width = 2)) 30 | val s_header = UInt(0, width = 2) 31 | val s_address0 = UInt(1, width = 2) 32 | val s_address1 = UInt(2, width = 2) 33 | val s_data = UInt(3, width = 2) 34 | 35 | when (io.q.fire()) { 36 | switch (state) { 37 | is (s_header) { state := s_address0 } 38 | is (s_address0) { state := s_address1 } 39 | is (s_address1) { state := Mux(a_hasData, s_data, s_header) } 40 | is (s_data) { state := Mux(!a_last, s_data, s_header) } 41 | } 42 | } 43 | 44 | // Construct the header beat 45 | val header = info.encode( 46 | format = UInt(0), 47 | opcode = a.bits.opcode, 48 | param = a.bits.param, 49 | size = a.bits.size, 50 | domain = domain(a.bits.source), 51 | source = source(a.bits.source)) 52 | 53 | // Construct the address beats 54 | val address0 = a.bits.address 55 | val address1 = a.bits.address >> 32 56 | 57 | // Frame the output packet 58 | val isLastState = state === Mux(a_hasData, s_data, s_address1) 59 | a.ready := io.q.ready && isLastState 60 | io.q.valid := a.valid 61 | io.q.bits.last := a_last && isLastState 62 | io.q.bits.data := Vec(header, address0, address1, a.bits.data)(state) 63 | io.q.bits.beats := Mux(a_hasData, info.size2beats(a.bits.size), UInt(0)) + UInt(3) + 64 | Mux(a_partial, info.mask2beats(a.bits.size), UInt(0)) 65 | } 66 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SinkB.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | 7 | class SinkB(info: ChipLinkInfo) extends Module 8 | { 9 | val io = new Bundle { 10 | val b = Decoupled(new TLBundleB(info.edgeOut.bundle)).flip 11 | val q = Decoupled(new DataLayer(info.params)) 12 | } 13 | 14 | // We need a Q because we stall the channel while serializing it's header 15 | val inject = Module(new PartialInjector(io.b.bits)) 16 | inject.io.i <> Queue(io.b, 1, flow=true) 17 | inject.io.i_last := info.edgeOut.last(inject.io.i) 18 | val b = inject.io.o 19 | val b_last = inject.io.o_last 20 | val b_hasData = info.edgeOut.hasData(b.bits) 21 | val b_partial = b.bits.opcode === TLMessages.PutPartialData 22 | 23 | // A simple FSM to generate the packet components 24 | val state = RegInit(UInt(0, width = 2)) 25 | val s_header = UInt(0, width = 2) 26 | val s_address0 = UInt(1, width = 2) 27 | val s_address1 = UInt(2, width = 2) 28 | val s_data = UInt(3, width = 2) 29 | 30 | when (io.q.fire()) { 31 | switch (state) { 32 | is (s_header) { state := s_address0 } 33 | is (s_address0) { state := s_address1 } 34 | is (s_address1) { state := Mux(b_hasData, s_data, s_header) } 35 | is (s_data) { state := Mux(!b_last, s_data, s_header) } 36 | } 37 | } 38 | 39 | // Construct the header beat 40 | val header = info.encode( 41 | format = UInt(1), 42 | opcode = b.bits.opcode, 43 | param = b.bits.param, 44 | size = b.bits.size, 45 | domain = UInt(0), // ChipLink only allows one remote cache, in domain 0 46 | source = UInt(0)) 47 | 48 | assert (!b.valid || b.bits.source === UInt(0)) 49 | 50 | // Construct the address beats 51 | val address0 = b.bits.address 52 | val address1 = b.bits.address >> 32 53 | 54 | // Frame the output packet 55 | val isLastState = state === Mux(b_hasData, s_data, s_address1) 56 | b.ready := io.q.ready && isLastState 57 | io.q.valid := b.valid 58 | io.q.bits.last := b_last && isLastState 59 | io.q.bits.data := Vec(header, address0, address1, b.bits.data)(state) 60 | io.q.bits.beats := Mux(b_hasData, info.size2beats(b.bits.size), UInt(0)) + UInt(3) + 61 | Mux(b_partial, info.mask2beats(b.bits.size), UInt(0)) 62 | } 63 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SinkC.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | 7 | class SinkC(info: ChipLinkInfo) extends Module 8 | { 9 | val io = new Bundle { 10 | val c = Decoupled(new TLBundleC(info.edgeIn.bundle)).flip 11 | val q = Decoupled(new DataLayer(info.params)) 12 | } 13 | 14 | // Map TileLink sources to ChipLink sources+domain 15 | val tl2cl = info.sourceMap 16 | val source = info.mux(tl2cl.mapValues(_.source)) 17 | val domain = info.mux(tl2cl.mapValues(_.domain)) 18 | 19 | // We need a Q because we stall the channel while serializing it's header 20 | val c = Queue(io.c, 1, flow=true) 21 | val c_last = info.edgeIn.last(c) 22 | val c_hasData = info.edgeIn.hasData(c.bits) 23 | val c_release = c.bits.opcode === TLMessages.Release || c.bits.opcode === TLMessages.ReleaseData 24 | 25 | // A simple FSM to generate the packet components 26 | val state = RegInit(UInt(0, width = 2)) 27 | val s_header = UInt(0, width = 2) 28 | val s_address0 = UInt(1, width = 2) 29 | val s_address1 = UInt(2, width = 2) 30 | val s_data = UInt(3, width = 2) 31 | 32 | when (io.q.fire()) { 33 | switch (state) { 34 | is (s_header) { state := s_address0 } 35 | is (s_address0) { state := s_address1 } 36 | is (s_address1) { state := Mux(c_hasData, s_data, s_header) } 37 | is (s_data) { state := Mux(!c_last, s_data, s_header) } 38 | } 39 | } 40 | 41 | // Construct the header beat 42 | val header = info.encode( 43 | format = UInt(2), 44 | opcode = c.bits.opcode, 45 | param = c.bits.param, 46 | size = c.bits.size, 47 | domain = UInt(0), // only caches (unordered) can release 48 | source = Mux(c_release, source(c.bits.source), UInt(0))) 49 | 50 | assert (!c.valid || domain(c.bits.source) === UInt(0)) 51 | 52 | // Construct the address beats 53 | val address0 = c.bits.address 54 | val address1 = c.bits.address >> 32 55 | 56 | // Frame the output packet 57 | val isLastState = state === Mux(c_hasData, s_data, s_address1) 58 | c.ready := io.q.ready && isLastState 59 | io.q.valid := c.valid 60 | io.q.bits.last := c_last && isLastState 61 | io.q.bits.data := Vec(header, address0, address1, c.bits.data)(state) 62 | io.q.bits.beats := Mux(c_hasData, info.size2beats(c.bits.size), UInt(0)) + UInt(3) 63 | } 64 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SinkD.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | 7 | class SinkD(info: ChipLinkInfo) extends Module 8 | { 9 | val io = new Bundle { 10 | val d = Decoupled(new TLBundleD(info.edgeOut.bundle)).flip 11 | val q = Decoupled(new DataLayer(info.params)) 12 | val a_tlSource = Valid(UInt(width = info.params.sourceBits)) 13 | val a_clSource = UInt(INPUT, width = info.params.clSourceBits) 14 | val c_tlSource = Valid(UInt(width = info.params.sourceBits)) 15 | val c_clSource = UInt(INPUT, width = info.params.clSourceBits) 16 | } 17 | 18 | // The FSM states 19 | val state = RegInit(UInt(0, width = 2)) 20 | val s_header = UInt(0, width = 2) 21 | val s_sink = UInt(1, width = 2) 22 | val s_data = UInt(2, width = 2) 23 | 24 | // We need a Q because we stall the channel while serializing it's header 25 | val d = Queue(io.d, 1, flow=true) 26 | val d_last = info.edgeOut.last(d) 27 | val d_hasData = info.edgeOut.hasData(d.bits) 28 | val d_grant = d.bits.opcode === TLMessages.Grant || d.bits.opcode === TLMessages.GrantData 29 | 30 | when (io.q.fire()) { 31 | switch (state) { 32 | is (s_header) { state := Mux(d_grant, s_sink, Mux(d_hasData, s_data, s_header)) } 33 | is (s_sink) { state := Mux(d_hasData, s_data, s_header) } 34 | is (s_data) { state := Mux(d_last, s_header, s_data) } 35 | } 36 | } 37 | 38 | // Release the TL source 39 | val relack = d.bits.opcode === TLMessages.ReleaseAck 40 | io.a_tlSource.valid := io.q.fire() && state === s_header && !relack 41 | io.a_tlSource.bits := d.bits.source 42 | io.c_tlSource.valid := io.q.fire() && state === s_header && relack 43 | io.c_tlSource.bits := d.bits.source 44 | 45 | // Construct the header beat 46 | val header = info.encode( 47 | format = UInt(3), 48 | opcode = d.bits.opcode, 49 | param = Cat(d.bits.denied, d.bits.param), 50 | size = d.bits.size, 51 | domain = d.bits.source >> log2Ceil(info.params.sourcesPerDomain), 52 | source = Mux(relack, io.c_clSource, io.a_clSource)) 53 | 54 | val isLastState = state === Mux(d_hasData, s_data, Mux(d_grant, s_sink, s_header)) 55 | d.ready := io.q.ready && isLastState 56 | io.q.valid := d.valid 57 | io.q.bits.last := d_last && isLastState 58 | io.q.bits.data := Vec(header, d.bits.sink, d.bits.data)(state) 59 | io.q.bits.beats := Mux(d_hasData, info.size2beats(d.bits.size), UInt(0)) + UInt(1) + d_grant.asUInt 60 | } 61 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SinkE.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | 7 | class SinkE(info: ChipLinkInfo) extends Module 8 | { 9 | val io = new Bundle { 10 | val e = Decoupled(new TLBundleE(info.edgeIn.bundle)).flip 11 | val q = Decoupled(new DataLayer(info.params)) 12 | // Find the sink from D 13 | val d_tlSink = Valid(UInt(width = info.params.sinkBits)) 14 | val d_clSink = UInt(INPUT, width = info.params.clSinkBits) 15 | } 16 | 17 | io.d_tlSink.valid := io.e.fire() 18 | io.d_tlSink.bits := io.e.bits.sink 19 | 20 | val header = info.encode( 21 | format = UInt(4), 22 | opcode = UInt(0), 23 | param = UInt(0), 24 | size = UInt(0), 25 | domain = UInt(0), 26 | source = io.d_clSink) 27 | 28 | io.e.ready := io.q.ready 29 | io.q.valid := io.e.valid 30 | io.q.bits.last := Bool(true) 31 | io.q.bits.data := header 32 | io.q.bits.beats := UInt(1) 33 | } 34 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SourceA.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class SourceA(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val a = Decoupled(new TLBundleA(info.edgeOut.bundle)) 12 | val q = Decoupled(UInt(width = info.params.dataBits)).flip 13 | // Used by D to find the txn 14 | val d_tlSource = Valid(UInt(width = info.params.sourceBits)).flip 15 | val d_clSource = UInt(OUTPUT, width = info.params.clSourceBits) 16 | } 17 | 18 | // CAM of sources used for each domain 19 | val cams = Seq.fill(info.params.domains) { 20 | Module(new CAM(info.params.sourcesPerDomain, info.params.clSourceBits)) 21 | } 22 | 23 | // A simple FSM to generate the packet components 24 | val state = RegInit(UInt(0, width = 2)) 25 | val s_header = UInt(0, width = 2) 26 | val s_address0 = UInt(1, width = 2) 27 | val s_address1 = UInt(2, width = 2) 28 | val s_data = UInt(3, width = 2) 29 | 30 | private def hold(key: UInt)(data: UInt) = { 31 | val enable = state === key 32 | Mux(enable, data, RegEnable(data, enable)) 33 | } 34 | 35 | // Extract header fields 36 | val Seq(_, q_opcode, q_param, q_size, q_domain, q_source) = 37 | info.decode(io.q.bits).map(hold(s_header) _) 38 | 39 | // Latch address 40 | val q_address0 = hold(s_address0)(io.q.bits) 41 | val q_address1 = hold(s_address1)(io.q.bits) 42 | 43 | val (_, q_last) = info.firstlast(io.q, Some(UInt(0))) 44 | val q_hasData = !q_opcode(2) 45 | val a_first = RegEnable(state =/= s_data, io.q.fire()) 46 | 47 | when (io.q.fire()) { 48 | switch (state) { 49 | is (s_header) { state := s_address0 } 50 | is (s_address0) { state := s_address1 } 51 | is (s_address1) { state := Mux(q_hasData, s_data, s_header) } 52 | is (s_data) { state := Mux(!q_last, s_data, s_header) } 53 | } 54 | } 55 | 56 | // Determine if the request is legal. If not, route to error device. 57 | val q_address = Cat(q_address1, q_address0) 58 | val q_acq = q_opcode === TLMessages.AcquireBlock || q_opcode === TLMessages.AcquirePerm 59 | val q_write = Mux(q_acq, q_param === TLPermissions.NtoT || q_param === TLPermissions.BtoT, q_hasData) 60 | val exists = info.edgeOut.manager.containsSafe(q_address) 61 | private def writeable(m: TLManagerParameters): Boolean = if (m.supportsAcquireB) m.supportsAcquireT else m.supportsPutFull 62 | private def acquireable(m: TLManagerParameters): Boolean = m.supportsAcquireB || m.supportsAcquireT 63 | private def toBool(x: Boolean) = Bool(x) 64 | val writeOk = info.edgeOut.manager.fastProperty(q_address, writeable, toBool) 65 | val acquireOk = info.edgeOut.manager.fastProperty(q_address, acquireable, toBool) 66 | val q_legal = exists && (!q_write || writeOk) && (!q_acq || acquireOk) 67 | 68 | // Look for an available source in the correct domain 69 | val source_ok = Vec(cams.map(_.io.alloc.ready))(q_domain) 70 | val source = Vec(cams.map(_.io.key))(q_domain) holdUnless a_first 71 | val a_sel = UIntToOH(q_domain) 72 | 73 | // Feed our preliminary A channel via the Partial Extractor FSM 74 | val extract = Module(new ParitalExtractor(io.a.bits)) 75 | io.a <> extract.io.o 76 | val a = extract.io.i 77 | extract.io.last := q_last 78 | 79 | a.bits.opcode := q_opcode 80 | a.bits.param := q_param 81 | a.bits.size := q_size 82 | a.bits.source := Cat(q_domain, source) 83 | a.bits.address := info.makeError(q_legal, q_address) 84 | a.bits.mask := MaskGen(q_address0, q_size, info.params.dataBytes) 85 | a.bits.data := io.q.bits 86 | a.bits.corrupt := Bool(false) 87 | 88 | val stall = a_first && !source_ok 89 | val xmit = q_last || state === s_data 90 | a.valid := (io.q.valid && !stall) && xmit 91 | io.q.ready := (a.ready && !stall) || !xmit 92 | (cams zip a_sel.toBools) foreach { case (cam, sel) => 93 | cam.io.alloc.valid := sel && a_first && xmit && io.q.valid && a.ready 94 | cam.io.alloc.bits := q_source 95 | } 96 | 97 | // Free the CAM entries 98 | val d_clDomain = io.d_tlSource.bits >> log2Ceil(info.params.sourcesPerDomain) 99 | val d_sel = UIntToOH(d_clDomain) 100 | io.d_clSource := Vec(cams.map(_.io.data))(d_clDomain) 101 | (cams zip d_sel.toBools) foreach { case (cam, sel) => 102 | cam.io.free.bits := io.d_tlSource.bits 103 | cam.io.free.valid := io.d_tlSource.valid && sel 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SourceB.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class SourceB(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val b = Decoupled(new TLBundleB(info.edgeIn.bundle)) 12 | val q = Decoupled(UInt(width = info.params.dataBits)).flip 13 | } 14 | 15 | // Find the optional cache (at most one) 16 | val cache = info.edgeIn.client.clients.filter(_.supportsProbe).headOption 17 | 18 | // A simple FSM to generate the packet components 19 | val state = RegInit(UInt(0, width = 2)) 20 | val s_header = UInt(0, width = 2) 21 | val s_address0 = UInt(1, width = 2) 22 | val s_address1 = UInt(2, width = 2) 23 | val s_data = UInt(3, width = 2) 24 | 25 | private def hold(key: UInt)(data: UInt) = { 26 | val enable = state === key 27 | Mux(enable, data, RegEnable(data, enable)) 28 | } 29 | 30 | // Extract header fields 31 | val Seq(_, q_opcode, q_param, q_size, _, _) = 32 | info.decode(io.q.bits).map(hold(s_header) _) 33 | 34 | // Latch address 35 | val q_address0 = hold(s_address0)(io.q.bits) 36 | val q_address1 = hold(s_address1)(io.q.bits) 37 | 38 | val (_, q_last) = info.firstlast(io.q, Some(UInt(1))) 39 | val q_hasData = !q_opcode(2) 40 | val b_first = RegEnable(state =/= s_data, io.q.fire()) 41 | 42 | when (io.q.fire()) { 43 | switch (state) { 44 | is (s_header) { state := s_address0 } 45 | is (s_address0) { state := s_address1 } 46 | is (s_address1) { state := Mux(q_hasData, s_data, s_header) } 47 | is (s_data) { state := Mux(!q_last, s_data, s_header) } 48 | } 49 | } 50 | 51 | // Feed our preliminary B channel via the Partial Extractor FSM 52 | val extract = Module(new ParitalExtractor(io.b.bits)) 53 | io.b <> extract.io.o 54 | val b = extract.io.i 55 | extract.io.last := q_last 56 | 57 | b.bits.opcode := q_opcode 58 | b.bits.param := q_param 59 | b.bits.size := q_size 60 | b.bits.source := UInt(cache.map(_.sourceId.start).getOrElse(0)) 61 | b.bits.address := Cat(q_address1, q_address0) 62 | b.bits.mask := MaskGen(q_address0, q_size, info.params.dataBytes) 63 | b.bits.data := io.q.bits 64 | b.bits.corrupt := Bool(false) 65 | 66 | val xmit = q_last || state === s_data 67 | b.valid := io.q.valid && xmit 68 | io.q.ready := b.ready || !xmit 69 | } 70 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SourceC.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class SourceC(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val c = Decoupled(new TLBundleC(info.edgeOut.bundle)) 12 | val q = Decoupled(UInt(width = info.params.dataBits)).flip 13 | // Used by D to find the txn 14 | val d_tlSource = Valid(UInt(width = info.params.sourceBits)).flip 15 | val d_clSource = UInt(OUTPUT, width = info.params.clSourceBits) 16 | } 17 | 18 | // CAM of sources used for release 19 | val cam = Module(new CAM(info.params.sourcesPerDomain, info.params.clSourceBits)) 20 | 21 | // A simple FSM to generate the packet components 22 | val state = RegInit(UInt(0, width = 2)) 23 | val s_header = UInt(0, width = 2) 24 | val s_address0 = UInt(1, width = 2) 25 | val s_address1 = UInt(2, width = 2) 26 | val s_data = UInt(3, width = 2) 27 | 28 | private def hold(key: UInt)(data: UInt) = { 29 | val enable = state === key 30 | Mux(enable, data, RegEnable(data, enable)) 31 | } 32 | 33 | // Extract header fields 34 | val Seq(_, q_opcode, q_param, q_size, _, q_source) = 35 | info.decode(io.q.bits).map(hold(s_header) _) 36 | 37 | // Latch address 38 | val q_address0 = hold(s_address0)(io.q.bits) 39 | val q_address1 = hold(s_address1)(io.q.bits) 40 | 41 | val (_, q_last) = info.firstlast(io.q, Some(UInt(2))) 42 | val q_hasData = q_opcode(0) 43 | val c_first = RegEnable(state =/= s_data, io.q.fire()) 44 | 45 | when (io.q.fire()) { 46 | switch (state) { 47 | is (s_header) { state := s_address0 } 48 | is (s_address0) { state := s_address1 } 49 | is (s_address1) { state := Mux(q_hasData, s_data, s_header) } 50 | is (s_data) { state := Mux(!q_last, s_data, s_header) } 51 | } 52 | } 53 | 54 | // Determine if the request is legal. If not, route to error device. 55 | val q_address = Cat(q_address1, q_address0) 56 | val exists = info.edgeOut.manager.containsSafe(q_address) 57 | private def writeable(m: TLManagerParameters): Boolean = if (m.supportsAcquireB) m.supportsAcquireT else m.supportsPutFull 58 | private def acquireable(m: TLManagerParameters): Boolean = m.supportsAcquireB || m.supportsAcquireT 59 | private def toBool(x: Boolean) = Bool(x) 60 | val writeOk = info.edgeOut.manager.fastProperty(q_address, writeable, toBool) 61 | val acquireOk = info.edgeOut.manager.fastProperty(q_address, acquireable, toBool) 62 | val q_legal = exists && (!q_hasData || writeOk) && acquireOk 63 | 64 | // Look for an available source in the correct domain 65 | val q_release = q_opcode === TLMessages.Release || q_opcode === TLMessages.ReleaseData 66 | val source_ok = !q_release || cam.io.alloc.ready 67 | val source = cam.io.key holdUnless c_first 68 | 69 | io.c.bits.opcode := q_opcode 70 | io.c.bits.param := q_param 71 | io.c.bits.size := q_size 72 | io.c.bits.source := Mux(q_release, source, UInt(0)) // always domain 0 73 | io.c.bits.address := info.makeError(q_legal, q_address) 74 | io.c.bits.data := io.q.bits 75 | io.c.bits.corrupt := Bool(false) 76 | 77 | val stall = c_first && !source_ok 78 | val xmit = q_last || state === s_data 79 | io.c.valid := (io.q.valid && !stall) && xmit 80 | io.q.ready := (io.c.ready && !stall) || !xmit 81 | cam.io.alloc.valid := q_release && c_first && xmit && io.q.valid && io.c.ready 82 | cam.io.alloc.bits := q_source 83 | 84 | // Free the CAM entries 85 | io.d_clSource := cam.io.data 86 | cam.io.free := io.d_tlSource 87 | } 88 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SourceD.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class SourceD(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val d = Decoupled(new TLBundleD(info.edgeIn.bundle)) 12 | val q = Decoupled(UInt(width = info.params.dataBits)).flip 13 | // Used by E to find the txn 14 | val e_tlSink = Valid(UInt(width = info.params.sinkBits)).flip 15 | val e_clSink = UInt(OUTPUT, width = info.params.clSinkBits) 16 | } 17 | 18 | // We need a sink id CAM 19 | val cam = Module(new CAM(info.params.sinks, info.params.clSinkBits)) 20 | 21 | // Map ChipLink transaction to TileLink source 22 | val cl2tl = info.sourceMap.map(_.swap) 23 | val nestedMap = cl2tl.groupBy(_._1.domain).mapValues(_.map { case (TXN(_, cls), tls) => (cls, tls) }) 24 | val muxes = Seq.tabulate(info.params.domains) { i => 25 | info.mux(nestedMap.lift(i).getOrElse(Map(0 -> 0))) 26 | } 27 | 28 | // The FSM states 29 | val state = RegInit(UInt(0, width = 2)) 30 | val s_header = UInt(0, width = 2) 31 | val s_sink = UInt(1, width = 2) 32 | val s_data = UInt(2, width = 2) 33 | 34 | private def hold(key: UInt)(data: UInt) = { 35 | val enable = state === key 36 | Mux(enable, data, RegEnable(data, enable)) 37 | } 38 | 39 | // Extract header fields from the message 40 | val Seq(_, q_opcode, q_param, q_size, q_domain, q_source) = 41 | info.decode(io.q.bits).map(hold(s_header) _) 42 | 43 | // Extract sink from the optional second beat 44 | val q_sink = hold(s_sink)(io.q.bits(15, 0)) 45 | 46 | val q_grant = q_opcode === TLMessages.Grant || q_opcode === TLMessages.GrantData 47 | val (_, q_last) = info.firstlast(io.q, Some(UInt(3))) 48 | val d_first = RegEnable(state =/= s_data, io.q.fire()) 49 | val s_maybe_data = Mux(q_last, s_header, s_data) 50 | 51 | when (io.q.fire()) { 52 | switch (state) { 53 | is (s_header) { state := Mux(q_grant, s_sink, s_maybe_data) } 54 | is (s_sink) { state := s_maybe_data } 55 | is (s_data) { state := s_maybe_data } 56 | } 57 | } 58 | 59 | // Look for an available sink 60 | val sink_ok = !q_grant || cam.io.alloc.ready 61 | val sink = cam.io.key holdUnless d_first 62 | val stall = d_first && !sink_ok 63 | val xmit = q_last || state === s_data 64 | 65 | io.d.bits.opcode := q_opcode 66 | io.d.bits.param := q_param(1,0) 67 | io.d.bits.size := q_size 68 | io.d.bits.source := Vec(muxes.map { m => m(q_source) })(q_domain) 69 | io.d.bits.sink := Mux(q_grant, sink, UInt(0)) 70 | io.d.bits.denied := q_param >> 2 71 | io.d.bits.data := io.q.bits 72 | io.d.bits.corrupt := io.d.bits.denied && info.edgeIn.hasData(io.d.bits) 73 | 74 | io.d.valid := (io.q.valid && !stall) && xmit 75 | io.q.ready := (io.d.ready && !stall) || !xmit 76 | 77 | cam.io.alloc.valid := q_grant && d_first && xmit && io.q.valid && io.d.ready 78 | cam.io.alloc.bits := q_sink 79 | 80 | // Free the CAM 81 | io.e_clSink := cam.io.data 82 | cam.io.free := io.e_tlSink 83 | } 84 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/SourceE.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class SourceE(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val e = Decoupled(new TLBundleE(info.edgeOut.bundle)) 12 | val q = Decoupled(UInt(width = info.params.dataBits)).flip 13 | } 14 | 15 | // Extract header fields 16 | val Seq(_, _, _, _, _, q_sink) = info.decode(io.q.bits) 17 | 18 | io.q.ready := io.e.ready 19 | io.e.valid := io.q.valid 20 | io.e.bits.sink := q_sink 21 | } 22 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/chiplink/TX.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.chiplink 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.tilelink._ 6 | import freechips.rocketchip.util._ 7 | 8 | class TX(info: ChipLinkInfo) extends Module 9 | { 10 | val io = new Bundle { 11 | val c2b_clk = Clock(OUTPUT) 12 | val c2b_rst = Bool(OUTPUT) 13 | val c2b_send = Bool(OUTPUT) 14 | val c2b_data = UInt(OUTPUT, info.params.dataBits) 15 | val a = new AsyncBundle(new DataLayer(info.params), info.params.crossing).flip 16 | val b = new AsyncBundle(new DataLayer(info.params), info.params.crossing).flip 17 | val c = new AsyncBundle(new DataLayer(info.params), info.params.crossing).flip 18 | val d = new AsyncBundle(new DataLayer(info.params), info.params.crossing).flip 19 | val e = new AsyncBundle(new DataLayer(info.params), info.params.crossing).flip 20 | val sa = DecoupledIO(new DataLayer(info.params)).flip 21 | val sb = DecoupledIO(new DataLayer(info.params)).flip 22 | val sc = DecoupledIO(new DataLayer(info.params)).flip 23 | val sd = DecoupledIO(new DataLayer(info.params)).flip 24 | val se = DecoupledIO(new DataLayer(info.params)).flip 25 | val rxc = new AsyncBundle(new CreditBump(info.params), AsyncQueueParams.singleton()).flip 26 | val txc = new AsyncBundle(new CreditBump(info.params), AsyncQueueParams.singleton()).flip 27 | } 28 | 29 | // Currently available credits 30 | val rx = RegInit(CreditBump(info.params, 0)) 31 | val tx = RegInit(CreditBump(info.params, 0)) 32 | 33 | // Constantly pull credits from RX 34 | val rxInc = FromAsyncBundle(io.rxc) 35 | val txInc = FromAsyncBundle(io.txc) 36 | rxInc.ready := Bool(true) 37 | txInc.ready := Bool(true) 38 | 39 | // Cross the requests (if necessary) 40 | val sync = info.params.syncTX 41 | val qa = if (sync) ShiftQueue(io.sa, 2) else FromAsyncBundle(io.a) 42 | val qb = if (sync) ShiftQueue(io.sb, 2) else FromAsyncBundle(io.b) 43 | val qc = if (sync) ShiftQueue(io.sc, 2) else FromAsyncBundle(io.c) 44 | val qd = if (sync) ShiftQueue(io.sd, 2) else FromAsyncBundle(io.d) 45 | val qe = if (sync) ShiftQueue(io.se, 2) else FromAsyncBundle(io.e) 46 | private def qX = Seq(qa, qb, qc, qd, qe) 47 | 48 | // Consume TX credits and propagate pre-paid requests 49 | val ioX = (qX zip (tx.X zip txInc.bits.X)) map { case (q, (credit, gain)) => 50 | val first = RegEnable(q.bits.last, Bool(true), q.fire()) 51 | val delta = credit -& q.bits.beats 52 | val allow = !first || (delta.asSInt >= SInt(0)) 53 | credit := Mux(q.fire() && first, delta, credit) + Mux(txInc.fire(), gain, UInt(0)) 54 | 55 | val cq = Module(new ShiftQueue(q.bits.cloneType, 2)) // maybe flow? 56 | cq.io.enq.bits := q.bits 57 | cq.io.enq.valid := q.valid && allow 58 | q.ready := cq.io.enq.ready && allow 59 | cq.io.deq 60 | } 61 | 62 | // Prepare RX credit update headers 63 | val rxQ = Module(new ShiftQueue(new DataLayer(info.params), 2)) // maybe flow? 64 | val (rxHeader, rxLeft) = rx.toHeader 65 | rxQ.io.enq.valid := Bool(true) 66 | rxQ.io.enq.bits.data := rxHeader 67 | rxQ.io.enq.bits.last := Bool(true) 68 | rxQ.io.enq.bits.beats := UInt(1) 69 | rx := Mux(rxQ.io.enq.fire(), rxLeft, rx) + Mux(rxInc.fire(), rxInc.bits, CreditBump(info.params, 0)) 70 | 71 | // Include the F credit channel in arbitration 72 | val f = Wire(rxQ.io.deq) 73 | val ioF = ioX :+ f 74 | val requests = Cat(ioF.map(_.valid).reverse) 75 | val lasts = Cat(ioF.map(_.bits.last).reverse) 76 | 77 | // How often should we force transmission of a credit update? sqrt 78 | val xmitBits = log2Ceil(info.params.Qdepth) / 2 79 | val xmit = RegInit(UInt(0, width = xmitBits)) 80 | val forceXmit = xmit === UInt(0) 81 | when (!forceXmit) { xmit := xmit - UInt(1) } 82 | when (f.fire()) { xmit := ~UInt(0, width = xmitBits) } 83 | 84 | // Flow control for returned credits 85 | val allowReturn = !ioX.map(_.valid).reduce(_ || _) || forceXmit 86 | f.bits := rxQ.io.deq.bits 87 | f.valid := rxQ.io.deq.valid && allowReturn 88 | rxQ.io.deq.ready := f.ready && allowReturn 89 | 90 | // Select a channel to transmit from those with data and space 91 | val first = RegInit(Bool(true)) 92 | val state = Reg(UInt(0, width=6)) 93 | val readys = TLArbiter.roundRobin(6, requests, first) 94 | val winner = readys & requests 95 | val grant = Mux(first, winner, state) 96 | val allowed = Mux(first, readys, state) 97 | (ioF zip allowed.toBools) foreach { case (beat, sel) => beat.ready := sel } 98 | 99 | val send = Mux(first, rxQ.io.deq.valid, (state & requests) =/= UInt(0)) 100 | assert (send === ((grant & requests) =/= UInt(0))) 101 | 102 | when (send) { first := (grant & lasts).orR } 103 | when (first) { state := winner } 104 | 105 | // Form the output beat 106 | io.c2b_clk := clock 107 | io.c2b_rst := AsyncResetReg(Bool(false), clock, reset, true, None) 108 | io.c2b_send := RegNext(RegNext(send, Bool(false)), Bool(false)) 109 | io.c2b_data := RegNext(Mux1H(RegNext(grant), RegNext(Vec(ioF.map(_.bits.data))))) 110 | } 111 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/gpio/GPIOCtrlRegs.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.gpio 3 | 4 | object GPIOCtrlRegs { 5 | val value = 0x00 6 | val input_en = 0x04 7 | val output_en = 0x08 8 | val port = 0x0c 9 | val pullup_en = 0x10 10 | val drive = 0x14 11 | val rise_ie = 0x18 12 | val rise_ip = 0x1c 13 | val fall_ie = 0x20 14 | val fall_ip = 0x24 15 | val high_ie = 0x28 16 | val high_ip = 0x2c 17 | val low_ie = 0x30 18 | val low_ip = 0x34 19 | val iof_en = 0x38 20 | val iof_sel = 0x3c 21 | val out_xor = 0x40 22 | } 23 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/gpio/GPIOPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.gpio 3 | 4 | import freechips.rocketchip.config.Field 5 | import freechips.rocketchip.diplomacy._ 6 | import freechips.rocketchip.subsystem.BaseSubsystem 7 | 8 | case object PeripheryGPIOKey extends Field[Seq[GPIOParams]] 9 | 10 | trait HasPeripheryGPIO { this: BaseSubsystem => 11 | val gpioNodes = p(PeripheryGPIOKey).map { ps => GPIO.attach(GPIOAttachParams(ps, pbus, ibus.fromAsync)).ioNode.makeSink } 12 | } 13 | 14 | trait HasPeripheryGPIOBundle { 15 | val gpio: Seq[GPIOPortIO] 16 | } 17 | 18 | trait HasPeripheryGPIOModuleImp extends LazyModuleImp with HasPeripheryGPIOBundle { 19 | val outer: HasPeripheryGPIO 20 | val gpio = outer.gpioNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"gpio_$i")) } 21 | } 22 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/gpio/GPIOPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.gpio 3 | 4 | import Chisel._ 5 | import chisel3.experimental.{withClockAndReset} 6 | import sifive.blocks.devices.pinctrl.{Pin} 7 | 8 | // While this is a bit pendantic, it keeps the GPIO 9 | // device more similar to the other devices. It's not 'special' 10 | // even though it looks like something that more directly talks to 11 | // a pin. It also makes it possible to change the exact 12 | // type of pad this connects to. 13 | class GPIOSignals[T <: Data](private val pingen: () => T, private val c: GPIOParams) extends Bundle { 14 | val pins = Vec(c.width, pingen()) 15 | } 16 | 17 | class GPIOPins[T <: Pin](pingen: () => T, c: GPIOParams) extends GPIOSignals[T](pingen, c) 18 | 19 | object GPIOPinsFromPort { 20 | 21 | def apply[T <: Pin](pins: GPIOSignals[T], port: GPIOPortIO, clock: Clock, reset: Bool){ 22 | 23 | // This will just match up the components of the Bundle that 24 | // exist in both. 25 | withClockAndReset(clock, reset) { 26 | (pins.pins zip port.pins) foreach {case (pin, port) => 27 | pin <> port 28 | } 29 | } 30 | } 31 | 32 | def apply[T <: Pin](pins: GPIOSignals[T], port: GPIOPortIO){ 33 | 34 | // This will just match up the components of the Bundle that 35 | // exist in both. 36 | (pins.pins zip port.pins) foreach {case (pin, port) => 37 | pin <> port 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/gpio/IOF.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.gpio 3 | 4 | import Chisel._ 5 | import sifive.blocks.devices.pinctrl.{PinCtrl, Pin, BasePin, EnhancedPin, EnhancedPinCtrl} 6 | 7 | // This is the actual IOF interface.pa 8 | // Add a valid bit to indicate whether 9 | // there is something actually connected 10 | // to this. 11 | class IOFCtrl extends PinCtrl { 12 | val valid = Bool() 13 | } 14 | 15 | // By default, 16 | object IOFCtrl { 17 | def apply(): IOFCtrl = { 18 | val iof = Wire(new IOFCtrl()) 19 | iof.valid := Bool(false) 20 | iof.oval := Bool(false) 21 | iof.oe := Bool(false) 22 | iof.ie := Bool(false) 23 | iof 24 | } 25 | } 26 | 27 | // Package up the inputs and outputs 28 | // for the IOF 29 | class IOFPin extends Pin { 30 | val o = new IOFCtrl().asOutput 31 | 32 | def default(): Unit = { 33 | this.o.oval := Bool(false) 34 | this.o.oe := Bool(false) 35 | this.o.ie := Bool(false) 36 | this.o.valid := Bool(false) 37 | } 38 | 39 | def inputPin(pue: Bool = Bool(false) /*ignored*/): Bool = { 40 | this.o.oval := Bool(false) 41 | this.o.oe := Bool(false) 42 | this.o.ie := Bool(true) 43 | this.i.ival 44 | } 45 | def outputPin(signal: Bool, 46 | pue: Bool = Bool(false), /*ignored*/ 47 | ds: Bool = Bool(false), /*ignored*/ 48 | ie: Bool = Bool(false) 49 | ): Unit = { 50 | this.o.oval := signal 51 | this.o.oe := Bool(true) 52 | this.o.ie := ie 53 | } 54 | } 55 | 56 | // Connect both the i and o side of the pin, 57 | // and drive the valid signal for the IOF. 58 | object BasePinToIOF { 59 | def apply(pin: BasePin, iof: IOFPin): Unit = { 60 | iof <> pin 61 | iof.o.valid := Bool(true) 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/i2c/I2CCtrlRegs.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.i2c 3 | 4 | // matching Open Cores I2C to re-use Linux driver 5 | // http://lxr.free-electrons.com/source/drivers/i2c/busses/i2c-ocores.c?v=4.6 6 | 7 | object I2CCtrlRegs { 8 | val prescaler_lo = 0x00 // low byte clock prescaler register 9 | val prescaler_hi = 0x04 // high byte clock prescaler register 10 | val control = 0x08 // control register 11 | val data = 0x0c // write: transmit byte, read: receive byte 12 | val cmd_status = 0x10 // write: command, read: status 13 | } 14 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/i2c/I2CPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.i2c 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Field 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.subsystem.{BaseSubsystem} 8 | 9 | case object PeripheryI2CKey extends Field[Seq[I2CParams]] 10 | 11 | trait HasPeripheryI2C { this: BaseSubsystem => 12 | val i2cNodes = p(PeripheryI2CKey).map { ps => 13 | I2C.attach(I2CAttachParams(ps, pbus, ibus.fromAsync)).ioNode.makeSink() 14 | } 15 | } 16 | 17 | trait HasPeripheryI2CBundle { 18 | val i2c: Seq[I2CPort] 19 | } 20 | 21 | trait HasPeripheryI2CModuleImp extends LazyModuleImp with HasPeripheryI2CBundle { 22 | val outer: HasPeripheryI2C 23 | val i2c = outer.i2cNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"i2c_$i")) } 24 | } 25 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/i2c/I2CPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.i2c 3 | 4 | import Chisel._ 5 | import chisel3.experimental.{withClockAndReset} 6 | import freechips.rocketchip.util.SyncResetSynchronizerShiftReg 7 | import sifive.blocks.devices.pinctrl.{Pin, PinCtrl} 8 | 9 | class I2CSignals[T <: Data](private val pingen: () => T) extends Bundle { 10 | val scl: T = pingen() 11 | val sda: T = pingen() 12 | } 13 | 14 | class I2CPins[T <: Pin](pingen: () => T) extends I2CSignals[T](pingen) 15 | 16 | object I2CPinsFromPort { 17 | 18 | def apply[T <: Pin](pins: I2CSignals[T], i2c: I2CPort, clock: Clock, reset: Bool, syncStages: Int = 0) = { 19 | withClockAndReset(clock, reset) { 20 | pins.scl.outputPin(i2c.scl.out, pue=true.B, ie = true.B) 21 | pins.scl.o.oe := i2c.scl.oe 22 | i2c.scl.in := SyncResetSynchronizerShiftReg(pins.scl.i.ival, syncStages, init = Bool(true), 23 | name = Some("i2c_scl_sync")) 24 | 25 | pins.sda.outputPin(i2c.sda.out, pue=true.B, ie = true.B) 26 | pins.sda.o.oe := i2c.sda.oe 27 | i2c.sda.in := SyncResetSynchronizerShiftReg(pins.sda.i.ival, syncStages, init = Bool(true), 28 | name = Some("i2c_sda_sync")) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/jtag/JTAGPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.jtag 3 | 4 | import Chisel._ 5 | 6 | // ------------------------------------------------------------ 7 | // SPI, UART, etc are with their respective packages, 8 | // JTAG doesn't really correspond directly to a device, but it does 9 | // define pins as those devices do. 10 | // ------------------------------------------------------------ 11 | 12 | import freechips.rocketchip.config._ 13 | import freechips.rocketchip.jtag.{JTAGIO} 14 | import sifive.blocks.devices.pinctrl.{Pin, PinCtrl} 15 | 16 | class JTAGSignals[T <: Data](pingen: () => T, hasTRSTn: Boolean = true) extends Bundle { 17 | val TCK = pingen() 18 | val TMS = pingen() 19 | val TDI = pingen() 20 | val TDO = pingen() 21 | val TRSTn = if (hasTRSTn) Option(pingen()) else None 22 | } 23 | 24 | class JTAGPins[T <: Pin](pingen: () => T, hasTRSTn: Boolean = true) extends JTAGSignals[T](pingen, hasTRSTn) 25 | 26 | object JTAGPinsFromPort { 27 | 28 | def apply[T <: Pin] (pins: JTAGSignals[T], jtag: JTAGIO): Unit = { 29 | jtag.TCK := pins.TCK.inputPin (pue = Bool(true)).asClock 30 | jtag.TMS := pins.TMS.inputPin (pue = Bool(true)) 31 | jtag.TDI := pins.TDI.inputPin(pue = Bool(true)) 32 | jtag.TRSTn.foreach{t => t := pins.TRSTn.get.inputPin(pue = Bool(true))} 33 | 34 | pins.TDO.outputPin(jtag.TDO.data) 35 | pins.TDO.o.oe := jtag.TDO.driven 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/mockaon/MockAON.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.mockaon 3 | 4 | import Chisel._ 5 | import chisel3.experimental.MultiIOModule 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.regmapper._ 8 | import freechips.rocketchip.tilelink._ 9 | 10 | import sifive.blocks.util.GenericTimer 11 | 12 | case class MockAONParams( 13 | address: BigInt = BigInt(0x10000000), 14 | nBackupRegs: Int = 16) { 15 | def size: Int = 0x1000 16 | def regBytes: Int = 4 17 | def wdogOffset: Int = 0 18 | def rtcOffset: Int = 0x40 19 | def backupRegOffset: Int = 0x80 20 | def pmuOffset: Int = 0x100 21 | } 22 | 23 | class MockAONPMUIO extends Bundle { 24 | val vddpaden = Bool(OUTPUT) 25 | val dwakeup = Bool(INPUT) 26 | } 27 | 28 | class MockAONMOffRstIO extends Bundle { 29 | val hfclkrst = Bool(OUTPUT) 30 | val corerst = Bool(OUTPUT) 31 | } 32 | 33 | trait HasMockAONBundleContents extends Bundle { 34 | 35 | // Output of the Power Management Sequencer 36 | val moff = new MockAONMOffRstIO 37 | 38 | // This goes out to wrapper 39 | // to be combined to create aon_rst. 40 | val wdog_rst = Bool(OUTPUT) 41 | 42 | // This goes out to wrapper 43 | // and comes back as our clk 44 | val lfclk = Clock(OUTPUT) 45 | 46 | val pmu = new MockAONPMUIO 47 | 48 | val lfextclk = Clock(INPUT) 49 | 50 | val resetCauses = new ResetCauses().asInput 51 | } 52 | 53 | trait HasMockAONModuleContents extends MultiIOModule with HasRegMap { 54 | val io: HasMockAONBundleContents 55 | val params: MockAONParams 56 | val c = params 57 | 58 | // the expectation here is that Chisel's implicit reset is aonrst, 59 | // which is asynchronous, so don't use synchronous-reset registers. 60 | 61 | val rtc = Module(new RTC) 62 | 63 | val pmu = Module(new PMU(new DevKitPMUConfig)) 64 | io.moff <> pmu.io.control 65 | io.pmu.vddpaden := pmu.io.control.vddpaden 66 | pmu.io.wakeup.dwakeup := io.pmu.dwakeup 67 | pmu.io.wakeup.awakeup := Bool(false) 68 | pmu.io.wakeup.rtc := rtc.io.ip(0) 69 | pmu.io.resetCauses := io.resetCauses 70 | val pmuRegMap = { 71 | val regs = pmu.io.regs.wakeupProgram ++ pmu.io.regs.sleepProgram ++ 72 | Seq(pmu.io.regs.ie, pmu.io.regs.cause, pmu.io.regs.sleep, pmu.io.regs.key) 73 | for ((r, i) <- regs.zipWithIndex) 74 | yield (c.pmuOffset + c.regBytes*i) -> Seq(r.toRegField()) 75 | } 76 | interrupts(1) := rtc.io.ip(0) 77 | 78 | val wdog = Module(new WatchdogTimer) 79 | io.wdog_rst := wdog.io.rst 80 | wdog.io.corerst := pmu.io.control.corerst 81 | interrupts(0) := wdog.io.ip(0) 82 | 83 | // If there are multiple lfclks to choose from, we can mux them here. 84 | io.lfclk := io.lfextclk 85 | 86 | val backupRegs = Seq.fill(c.nBackupRegs)(Reg(UInt(width = c.regBytes * 8))) 87 | val backupRegMap = 88 | for ((reg, i) <- backupRegs.zipWithIndex) 89 | yield (c.backupRegOffset + c.regBytes*i) -> Seq(RegField(reg.getWidth, RegReadFn(reg), RegWriteFn(reg))) 90 | 91 | regmap((backupRegMap ++ 92 | GenericTimer.timerRegMap(wdog, c.wdogOffset, c.regBytes) ++ 93 | GenericTimer.timerRegMap(rtc, c.rtcOffset, c.regBytes) ++ 94 | pmuRegMap):_*) 95 | 96 | } 97 | 98 | class TLMockAON(w: Int, c: MockAONParams)(implicit p: Parameters) 99 | extends TLRegisterRouter(c.address, "aon", Seq("sifive,aon0"), interrupts = 2, size = c.size, beatBytes = w, concurrency = 1)( 100 | new TLRegBundle(c, _) with HasMockAONBundleContents)( 101 | new TLRegModule(c, _, _) with HasMockAONModuleContents) 102 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/mockaon/MockAONPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.mockaon 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Field 6 | import freechips.rocketchip.devices.debug.HasPeripheryDebug 7 | import freechips.rocketchip.devices.tilelink.CanHavePeripheryCLINT 8 | import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp} 9 | import freechips.rocketchip.interrupts._ 10 | import freechips.rocketchip.subsystem.BaseSubsystem 11 | import freechips.rocketchip.tilelink.{TLAsyncCrossingSource} 12 | import freechips.rocketchip.util.{ResetCatchAndSync, SynchronizerShiftReg} 13 | 14 | case object PeripheryMockAONKey extends Field[MockAONParams] 15 | 16 | trait HasPeripheryMockAON extends CanHavePeripheryCLINT with HasPeripheryDebug { this: BaseSubsystem => 17 | // We override the clock & Reset here so that all synchronizers, etc 18 | // are in the proper clock domain. 19 | val mockAONParams= p(PeripheryMockAONKey) 20 | val aon = LazyModule(new MockAONWrapper(sbus.control_bus.beatBytes, mockAONParams)) 21 | sbus.control_bus.toVariableWidthSlave(Some("aon")) { aon.node := TLAsyncCrossingSource() } 22 | ibus.fromSync := IntSyncCrossingSink() := aon.intnode 23 | } 24 | 25 | trait HasPeripheryMockAONBundle { 26 | val aon: MockAONWrapperBundle 27 | def coreResetCatchAndSync(core_clock: Clock) = { 28 | ResetCatchAndSync(core_clock, aon.rsts.corerst, 20) 29 | } 30 | } 31 | 32 | trait HasPeripheryMockAONModuleImp extends LazyModuleImp with HasPeripheryMockAONBundle { 33 | val outer: HasPeripheryMockAON 34 | val aon = IO(new MockAONWrapperBundle) 35 | 36 | aon <> outer.aon.module.io 37 | 38 | // Explicit clock & reset are unused in MockAONWrapper. 39 | // Tie to check this assumption. 40 | outer.aon.module.clock := Bool(false).asClock 41 | outer.aon.module.reset := Bool(true) 42 | 43 | // Synchronize the external toggle into the clint 44 | val rtc_sync = SynchronizerShiftReg(outer.aon.module.io.rtc.asUInt.toBool, 3, Some("rtc")) 45 | val rtc_last = Reg(init = Bool(false), next=rtc_sync) 46 | val rtc_tick = Reg(init = Bool(false), next=(rtc_sync & (~rtc_last))) 47 | 48 | outer.clintOpt.foreach { clint => 49 | clint.module.io.rtcTick := rtc_tick 50 | } 51 | 52 | outer.aon.module.io.ndreset := outer.debug.module.io.ctrl.ndreset 53 | } 54 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/mockaon/PMU.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.mockaon 3 | 4 | import Chisel._ 5 | import Chisel.ImplicitConversions._ 6 | import freechips.rocketchip.util._ 7 | import sifive.blocks.util.SRLatch 8 | 9 | import sifive.blocks.util.{SlaveRegIF} 10 | 11 | class WakeupCauses extends Bundle { 12 | val awakeup = Bool() 13 | val dwakeup = Bool() 14 | val rtc = Bool() 15 | val reset = Bool() 16 | } 17 | 18 | class ResetCauses extends Bundle { 19 | val wdogrst = Bool() 20 | val erst = Bool() 21 | val porrst = Bool() 22 | } 23 | 24 | class PMUSignals extends Bundle { 25 | val hfclkrst = Bool() 26 | val corerst = Bool() 27 | val reserved1 = Bool() 28 | val vddpaden = Bool() 29 | val reserved0 = Bool() 30 | } 31 | 32 | class PMUInstruction extends Bundle { 33 | val sigs = new PMUSignals 34 | val dt = UInt(width = 4) 35 | } 36 | 37 | class PMUConfig(wakeupProgramIn: Seq[Int], 38 | sleepProgramIn: Seq[Int]) { 39 | val programLength = 8 40 | val nWakeupCauses = new WakeupCauses().elements.size 41 | val wakeupProgram = wakeupProgramIn.padTo(programLength, wakeupProgramIn.last) 42 | val sleepProgram = sleepProgramIn.padTo(programLength, sleepProgramIn.last) 43 | require(wakeupProgram.length == programLength) 44 | require(sleepProgram.length == programLength) 45 | } 46 | 47 | class DevKitPMUConfig extends PMUConfig( // TODO 48 | Seq(0x1f0, 0x0f8, 0x030), 49 | Seq(0x0f0, 0x1f0, 0x1d0, 0x1c0)) 50 | 51 | class PMURegs(c: PMUConfig) extends Bundle { 52 | val ie = new SlaveRegIF(c.nWakeupCauses) 53 | val cause = new SlaveRegIF(32) 54 | val sleep = new SlaveRegIF(32) 55 | val key = new SlaveRegIF(32) 56 | val wakeupProgram = Vec(c.programLength, new SlaveRegIF(32)) 57 | val sleepProgram = Vec(c.programLength, new SlaveRegIF(32)) 58 | } 59 | 60 | class PMUCore(c: PMUConfig)(resetIn: Bool) extends Module(_reset = resetIn) { 61 | val io = new Bundle { 62 | val wakeup = new WakeupCauses().asInput 63 | val control = Valid(new PMUSignals) 64 | val resetCause = UInt(INPUT, log2Ceil(new ResetCauses().getWidth)) 65 | val regs = new PMURegs(c) 66 | } 67 | 68 | val run = Reg(init = Bool(true)) 69 | val awake = Reg(init = Bool(true)) 70 | val unlocked = { 71 | val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key) 72 | RegEnable(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, Bool(false), io.regs.key.write.valid || writeAny) 73 | } 74 | val wantSleep = RegEnable(Bool(true), Bool(false), io.regs.sleep.write.valid && unlocked) 75 | val pc = Reg(init = UInt(0, log2Ceil(c.programLength))) 76 | val wakeupCause = Reg(init = UInt(0, log2Ceil(c.nWakeupCauses))) 77 | val ie = RegEnable(io.regs.ie.write.bits, io.regs.ie.write.valid && unlocked) | 1 /* POR always enabled */ 78 | 79 | val insnWidth = new PMUInstruction().getWidth 80 | val wakeupProgram = c.wakeupProgram.map(v => Reg(init = UInt(v, insnWidth))) 81 | val sleepProgram = c.sleepProgram.map(v => Reg(init = UInt(v, insnWidth))) 82 | val insnBits = Mux(awake, wakeupProgram(pc), sleepProgram(pc)) 83 | val insn = new PMUInstruction().fromBits(insnBits) 84 | 85 | val count = Reg(init = UInt(0, 1 << insn.dt.getWidth)) 86 | val tick = (count ^ (count + 1))(insn.dt) 87 | val npc = pc +& 1 88 | val last = npc >= c.programLength 89 | io.control.valid := run && !last && tick 90 | io.control.bits := insn.sigs 91 | 92 | when (run) { 93 | count := count + 1 94 | when (tick) { 95 | count := 0 96 | 97 | require(isPow2(c.programLength)) 98 | run := !last 99 | pc := npc 100 | } 101 | }.otherwise { 102 | val maskedWakeupCauses = ie & io.wakeup.asUInt 103 | when (!awake && maskedWakeupCauses.orR) { 104 | run := true 105 | awake := true 106 | wakeupCause := PriorityEncoder(maskedWakeupCauses) 107 | } 108 | when (awake && wantSleep) { 109 | run := true 110 | awake := false 111 | wantSleep := false 112 | } 113 | } 114 | 115 | io.regs.cause.read := wakeupCause | (io.resetCause << 8) 116 | io.regs.ie.read := ie 117 | io.regs.key.read := unlocked 118 | io.regs.sleep.read := 0 119 | 120 | for ((port, reg) <- (io.regs.wakeupProgram ++ io.regs.sleepProgram) zip (wakeupProgram ++ sleepProgram)) { 121 | port.read := reg 122 | when (port.write.valid && unlocked) { reg := port.write.bits } 123 | } 124 | } 125 | 126 | class PMU(val c: PMUConfig) extends Module { 127 | val io = new Bundle { 128 | val wakeup = new WakeupCauses().asInput 129 | val control = new PMUSignals().asOutput 130 | val regs = new PMURegs(c) 131 | val resetCauses = new ResetCauses().asInput 132 | } 133 | 134 | val coreReset = Reg(next = Reg(next = reset)) 135 | val core = Module(new PMUCore(c)(resetIn = coreReset)) 136 | 137 | io <> core.io 138 | core.io.wakeup.reset := false // this is implied by resetting the PMU 139 | 140 | // during aonrst, hold all control signals high 141 | val latch = ~AsyncResetReg(~core.io.control.bits.asUInt, core.io.control.valid) 142 | io.control := io.control.fromBits(latch) 143 | 144 | core.io.resetCause := { 145 | val cause = io.resetCauses.asUInt 146 | val latches = for (i <- 0 until cause.getWidth) yield { 147 | val latch = Module(new SRLatch) 148 | latch.io.set := cause(i) 149 | latch.io.reset := (0 until cause.getWidth).filter(_ != i).map(cause(_)).reduce(_||_) 150 | latch.io.q 151 | } 152 | OHToUInt(latches) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/mockaon/RTC.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.mockaon 3 | 4 | import Chisel._ 5 | import Chisel.ImplicitConversions._ 6 | import chisel3.experimental.MultiIOModule 7 | import freechips.rocketchip.util.AsyncResetReg 8 | import freechips.rocketchip.regmapper.RegFieldDesc 9 | 10 | import sifive.blocks.util.{SlaveRegIF, GenericTimer, GenericTimerIO, DefaultGenericTimerCfgDescs} 11 | 12 | class RTC extends MultiIOModule with GenericTimer { 13 | 14 | protected def prefix = "rtc" 15 | protected def countWidth = 48 16 | protected def cmpWidth = 32 17 | protected def ncmp = 1 18 | protected def countEn = countAlways 19 | override protected lazy val ip = RegNext(elapsed) 20 | override protected lazy val zerocmp = Bool(false) 21 | protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.countAlways, io.regs.cfg.write_countAlways && unlocked)(0) 22 | protected lazy val feed = Bool(false) 23 | 24 | override protected lazy val feed_desc = RegFieldDesc.reserved 25 | override protected lazy val key_desc = RegFieldDesc.reserved 26 | override protected lazy val cfg_desc = DefaultGenericTimerCfgDescs("rtc", ncmp).copy( 27 | sticky = RegFieldDesc.reserved, 28 | zerocmp = RegFieldDesc.reserved, 29 | deglitch = RegFieldDesc.reserved, 30 | running = RegFieldDesc.reserved, 31 | center = Seq.fill(ncmp){ RegFieldDesc.reserved }, 32 | extra = Seq.fill(ncmp){ RegFieldDesc.reserved }, 33 | gang = Seq.fill(ncmp){ RegFieldDesc.reserved } 34 | ) 35 | 36 | lazy val io = IO(new GenericTimerIO(regWidth, ncmp, maxcmp, scaleWidth, countWidth, cmpWidth)) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/mockaon/WatchdogTimer.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.mockaon 3 | 4 | import Chisel._ 5 | import Chisel.ImplicitConversions._ 6 | import chisel3.experimental.MultiIOModule 7 | import freechips.rocketchip.util.AsyncResetReg 8 | import freechips.rocketchip.regmapper.{RegFieldDesc} 9 | 10 | import sifive.blocks.util.{SlaveRegIF, GenericTimer, GenericTimerIO, GenericTimerCfgRegIFC, DefaultGenericTimerCfgDescs} 11 | 12 | object WatchdogTimer { 13 | def writeAnyExceptKey(regs: Bundle, keyReg: SlaveRegIF): Bool = { 14 | regs.elements.values.filter(_ ne keyReg).map({ 15 | case c: GenericTimerCfgRegIFC => c.anyWriteValid 16 | case v: Vec[SlaveRegIF] @unchecked => v.map(_.write.valid).reduce(_||_) 17 | case s: SlaveRegIF => s.write.valid 18 | }).reduce(_||_) 19 | } 20 | 21 | val key = 0x51F15E 22 | } 23 | 24 | class WatchdogTimer extends MultiIOModule with GenericTimer { 25 | protected def prefix = "wdog" 26 | protected def countWidth = 31 27 | protected def cmpWidth = 16 28 | protected def ncmp = 1 29 | protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.countAlways, io.regs.cfg.write_countAlways && unlocked)(0) 30 | override protected lazy val countAwake = AsyncResetReg(io.regs.cfg.write.running, io.regs.cfg.write_running && unlocked)(0) 31 | protected lazy val countEn = { 32 | val corerstSynchronized = Reg(next = Reg(next = io.corerst)) 33 | countAlways || (countAwake && !corerstSynchronized) 34 | } 35 | override protected lazy val rsten = AsyncResetReg(io.regs.cfg.write.sticky, io.regs.cfg.write_sticky && unlocked)(0) 36 | protected lazy val ip = RegEnable(Vec(Seq(io.regs.cfg.write.ip(0) || elapsed(0))), (io.regs.cfg.write_ip(0) && unlocked) || elapsed(0)) 37 | override protected lazy val unlocked = { 38 | val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key) 39 | AsyncResetReg(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, io.regs.key.write.valid || writeAny)(0) 40 | } 41 | protected lazy val feed = { 42 | val food = 0xD09F00D 43 | unlocked && io.regs.feed.write.valid && io.regs.feed.write.bits === food 44 | } 45 | 46 | // The Scala Type-Chekcher seems to have a bug and I get a null pointer during the Scala compilation 47 | // if I don't do this temporary assignment. 48 | val tmpStickyDesc = RegFieldDesc("wdogrsten", "Controls whether the comparator output can set the wdogrst bit and hence cause a full reset.", 49 | reset = Some(0)) 50 | 51 | override protected lazy val cfg_desc = DefaultGenericTimerCfgDescs("wdog", ncmp).copy( 52 | sticky = tmpStickyDesc, 53 | deglitch = RegFieldDesc.reserved, 54 | running = RegFieldDesc("wdogcoreawake", "Increment the watchdog counter if the processor is not asleep", reset=Some(0)), 55 | center = Seq.fill(ncmp){RegFieldDesc.reserved}, 56 | extra = Seq.fill(ncmp){RegFieldDesc.reserved}, 57 | gang = Seq.fill(ncmp){RegFieldDesc.reserved} 58 | ) 59 | 60 | lazy val io = IO(new GenericTimerIO(regWidth, ncmp, maxcmp, scaleWidth, countWidth, cmpWidth) { 61 | val corerst = Bool(INPUT) 62 | val rst = Bool(OUTPUT) 63 | } 64 | ) 65 | io.rst := AsyncResetReg(Bool(true), rsten && elapsed(0)) 66 | } 67 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/msi/MSIMaster.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | package sifive.blocks.devices.msi 4 | 5 | import Chisel._ 6 | import freechips.rocketchip.config.Parameters 7 | import freechips.rocketchip.diplomacy._ 8 | import freechips.rocketchip.interrupts._ 9 | import freechips.rocketchip.tilelink._ 10 | import freechips.rocketchip.util.leftOR 11 | 12 | case class MSITarget(address: BigInt, spacing: Int, number: Int) 13 | { 14 | require (number >= 0) 15 | require (address >= 0) 16 | } 17 | 18 | class MSIMaster(targets: Seq[MSITarget])(implicit p: Parameters) extends LazyModule 19 | { 20 | val masterNode = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters("MSI Master", sourceId = IdRange(0,2)))))) 21 | 22 | // A terminal interrupt node of flexible number 23 | val intNode = IntNexusNode( 24 | sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Nil))) }, 25 | sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, 26 | inputRequiresOutput = false) 27 | 28 | lazy val module = new LazyModuleImp(this) { 29 | val (io, masterEdge) = masterNode.out(0) 30 | val interrupts = intNode.in.flatMap { case (i, e) => i.take(e.source.num) } 31 | 32 | // Construct a map of the addresses to update for interrupts 33 | val targetMap = targets.flatMap { case MSITarget(address, spacing, number) => 34 | address until (address+spacing*number) by spacing 35 | } .map { addr => 36 | val m = masterEdge.manager.find(addr) 37 | require (m.isDefined, s"MSIMaster ${name} was pointed at address 0x${addr}%x which does not exist") 38 | require (m.get.supportsPutFull.contains(1), s"MSIMaster ${name} requires device ${m.get.name} supportPutFull of 1 byte (${m.get.supportsPutFull})") 39 | UInt(addr) 40 | }.take(interrupts.size max 1) 41 | 42 | require (interrupts.size <= targetMap.size, s"MSIMaster ${name} has more interrupts (${interrupts.size}) than addresses to use (${targetMap.size})") 43 | require (intNode.out.isEmpty, s"MSIMaster ${name} intNode is not a source!") 44 | 45 | val busy = RegInit(Bool(false)) 46 | val remote = RegInit(UInt(0, width=interrupts.size max 1)) 47 | val local = if (interrupts.isEmpty) UInt(0) else Cat(interrupts.reverse) 48 | val pending = remote ^ local 49 | val select = ~(leftOR(pending) << 1) & pending 50 | val address = Mux1H(select, targetMap) 51 | val lowBits = log2Ceil(masterEdge.manager.beatBytes) 52 | val shift = if (lowBits > 0) address(lowBits-1, 0) else UInt(0) 53 | val data = (select & local).orR 54 | 55 | io.a.valid := pending.orR && !busy 56 | io.a.bits := masterEdge.Put( 57 | fromSource = UInt(0), 58 | toAddress = address, 59 | lgSize = UInt(0), 60 | data = data << (shift << 3))._2 61 | 62 | // When A is sent, toggle our model of the remote state 63 | when (io.a.fire()) { 64 | remote := remote ^ select 65 | busy := Bool(true) 66 | } 67 | 68 | // Sink D messages to clear busy 69 | io.d.ready := Bool(true) 70 | when (io.d.fire()) { 71 | busy := Bool(false) 72 | } 73 | 74 | // Tie off unused channels 75 | io.b.ready := Bool(false) 76 | io.c.valid := Bool(false) 77 | io.e.valid := Bool(false) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/pinctrl/PinCtrl.scala: -------------------------------------------------------------------------------- 1 | //See LICENSE for license details 2 | 3 | package sifive.blocks.devices.pinctrl 4 | 5 | import Chisel._ 6 | 7 | // This is the base class of things you "always" 8 | // want to control from a HW block. 9 | class PinCtrl extends Bundle { 10 | val oval = Bool() 11 | val oe = Bool() 12 | val ie = Bool() 13 | } 14 | 15 | // Package up the inputs and outputs 16 | // for the Pin 17 | abstract class Pin extends Bundle { 18 | val i = new Bundle { 19 | val ival = Bool(INPUT) 20 | } 21 | val o: PinCtrl 22 | 23 | // Must be defined by the subclasses 24 | def default(): Unit 25 | def inputPin(pue: Bool = Bool(false)): Bool 26 | def outputPin(signal: Bool, 27 | pue: Bool = Bool(false), 28 | ds: Bool = Bool(false), 29 | ie: Bool = Bool(false) 30 | ): Unit 31 | 32 | } 33 | 34 | 35 | //////////////////////////////////////////////////////////////////////////////////// 36 | 37 | class BasePin extends Pin() { 38 | val o = new PinCtrl().asOutput 39 | 40 | def default(): Unit = { 41 | this.o.oval := Bool(false) 42 | this.o.oe := Bool(false) 43 | this.o.ie := Bool(false) 44 | } 45 | 46 | def inputPin(pue: Bool = Bool(false) /*ignored*/): Bool = { 47 | this.o.oval := Bool(false) 48 | this.o.oe := Bool(false) 49 | this.o.ie := Bool(true) 50 | this.i.ival 51 | } 52 | 53 | def outputPin(signal: Bool, 54 | pue: Bool = Bool(false), /*ignored*/ 55 | ds: Bool = Bool(false), /*ignored*/ 56 | ie: Bool = Bool(false) 57 | ): Unit = { 58 | this.o.oval := signal 59 | this.o.oe := Bool(true) 60 | this.o.ie := ie 61 | } 62 | } 63 | 64 | ///////////////////////////////////////////////////////////////////////// 65 | class EnhancedPinCtrl extends PinCtrl { 66 | val pue = Bool() 67 | val ds = Bool() 68 | } 69 | 70 | class EnhancedPin extends Pin() { 71 | 72 | val o = new EnhancedPinCtrl().asOutput 73 | 74 | def default(): Unit = { 75 | this.o.oval := Bool(false) 76 | this.o.oe := Bool(false) 77 | this.o.ie := Bool(false) 78 | this.o.ds := Bool(false) 79 | this.o.pue := Bool(false) 80 | } 81 | 82 | def inputPin(pue: Bool = Bool(false)): Bool = { 83 | this.o.oval := Bool(false) 84 | this.o.oe := Bool(false) 85 | this.o.pue := pue 86 | this.o.ds := Bool(false) 87 | this.o.ie := Bool(true) 88 | 89 | this.i.ival 90 | } 91 | 92 | def outputPin(signal: Bool, 93 | pue: Bool = Bool(false), 94 | ds: Bool = Bool(false), 95 | ie: Bool = Bool(false) 96 | ): Unit = { 97 | this.o.oval := signal 98 | this.o.oe := Bool(true) 99 | this.o.pue := pue 100 | this.o.ds := ds 101 | this.o.ie := ie 102 | } 103 | 104 | def toBasePin(): BasePin = { 105 | 106 | val base_pin = Wire(new BasePin()) 107 | base_pin <> this 108 | base_pin 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/pwm/PWM.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.pwm 3 | 4 | import Chisel._ 5 | import Chisel.ImplicitConversions._ 6 | import chisel3.experimental.MultiIOModule 7 | import freechips.rocketchip.config.Parameters 8 | import freechips.rocketchip.diplomacy._ 9 | import freechips.rocketchip.interrupts._ 10 | import freechips.rocketchip.regmapper._ 11 | import freechips.rocketchip.tilelink._ 12 | import freechips.rocketchip.util._ 13 | import sifive.blocks.util.{GenericTimer, GenericTimerIO, DefaultGenericTimerCfgDescs} 14 | 15 | // Core PWM Functionality & Register Interface 16 | class PWMTimer(val ncmp: Int = 4, val cmpWidth: Int = 16) extends MultiIOModule with GenericTimer { 17 | 18 | def orR(v: Vec[Bool]): Bool = v.foldLeft(Bool(false))( _||_ ) 19 | 20 | protected def prefix = "pwm" 21 | protected def countWidth = ((1 << scaleWidth) - 1) + cmpWidth 22 | protected lazy val countAlways = RegEnable(io.regs.cfg.write.countAlways, Bool(false), io.regs.cfg.write_countAlways && unlocked) 23 | protected lazy val feed = count.carryOut(scale + UInt(cmpWidth)) 24 | protected lazy val countEn = Wire(Bool()) 25 | override protected lazy val oneShot = RegEnable(io.regs.cfg.write.running && !countReset, Bool(false), (io.regs.cfg.write_running && unlocked) || countReset) 26 | override protected lazy val extra: Vec[Bool] = RegEnable(io.regs.cfg.write.extra, init = Vec.fill(maxcmp){false.B}, orR(io.regs.cfg.write_extra) && unlocked) 27 | override protected lazy val center: Vec[Bool] = RegEnable(io.regs.cfg.write.center, orR(io.regs.cfg.write_center) && unlocked) 28 | override protected lazy val gang: Vec[Bool] = RegEnable(io.regs.cfg.write.gang, orR(io.regs.cfg.write_gang) && unlocked) 29 | override protected lazy val deglitch = RegEnable(io.regs.cfg.write.deglitch, io.regs.cfg.write_deglitch && unlocked)(0) 30 | override protected lazy val sticky = RegEnable(io.regs.cfg.write.sticky, io.regs.cfg.write_sticky && unlocked)(0) 31 | override protected lazy val ip = { 32 | val doSticky = Reg(next = (deglitch && !countReset) || sticky) 33 | val sel = (0 until ncmp).map(i => s(cmpWidth-1) && center(i)) 34 | val reg = Reg(Vec(ncmp, Bool())) 35 | reg := (sel & elapsed) | (~sel & (elapsed | (Vec.fill(ncmp){doSticky} & reg))) 36 | when (orR(io.regs.cfg.write_ip) && unlocked) { reg := io.regs.cfg.write_ip } 37 | reg 38 | } 39 | 40 | override protected lazy val feed_desc = RegFieldDesc.reserved 41 | override protected lazy val key_desc = RegFieldDesc.reserved 42 | override protected lazy val cfg_desc = DefaultGenericTimerCfgDescs("pwm", ncmp).copy( 43 | extra = Seq.tabulate(ncmp){ i => RegFieldDesc(s"pwminvert${i}", s"Invert Comparator ${i} Output", reset = Some(0))} 44 | ) 45 | 46 | lazy val io = IO(new GenericTimerIO(regWidth, ncmp, maxcmp, scaleWidth, countWidth, cmpWidth) { 47 | val gpio = Vec(ncmp, Bool()).asOutput 48 | }) 49 | 50 | val invert = extra.asUInt 51 | 52 | val ipU = ip.asUInt 53 | val gangU = gang.asUInt 54 | 55 | io.gpio := io.gpio.fromBits((ipU & ~(gangU & Cat(ipU(0), ipU >> 1))) ^ invert) 56 | countEn := countAlways || oneShot 57 | } 58 | 59 | case class PWMParams( 60 | address: BigInt, 61 | size: Int = 0x1000, 62 | regBytes: Int = 4, 63 | ncmp: Int = 4, 64 | cmpWidth: Int = 16) 65 | 66 | class PWMPortIO(val c: PWMParams) extends Bundle { 67 | val gpio = Vec(c.ncmp, Bool()).asOutput 68 | } 69 | 70 | abstract class PWM(busWidthBytes: Int, val params: PWMParams)(implicit p: Parameters) 71 | extends IORegisterRouter( 72 | RegisterRouterParams( 73 | name = "pwm", 74 | compat = Seq("sifive,pwm0"), 75 | base = params.address, 76 | size = params.size, 77 | beatBytes = busWidthBytes), 78 | new PWMPortIO(params)) 79 | with HasInterruptSources { 80 | 81 | def nInterrupts = params.ncmp 82 | 83 | lazy val module = new LazyModuleImp(this) { 84 | val pwm = Module(new PWMTimer(params.ncmp, params.cmpWidth)) 85 | interrupts := pwm.io.ip 86 | port.gpio := pwm.io.gpio 87 | regmap((GenericTimer.timerRegMap(pwm, 0, params.regBytes)):_*) 88 | } 89 | } 90 | 91 | class TLPWM(busWidthBytes: Int, params: PWMParams)(implicit p: Parameters) 92 | extends PWM(busWidthBytes, params) with HasTLControlRegMap 93 | 94 | case class PWMAttachParams( 95 | pwm: PWMParams, 96 | controlBus: TLBusWrapper, 97 | intNode: IntInwardNode, 98 | mclock: Option[ModuleValue[Clock]] = None, 99 | mreset: Option[ModuleValue[Bool]] = None, 100 | controlXType: ClockCrossingType = NoCrossing, 101 | intXType: ClockCrossingType = NoCrossing) 102 | (implicit val p: Parameters) 103 | 104 | object PWM { 105 | val nextId = { var i = -1; () => { i += 1; i} } 106 | 107 | def attach(params: PWMAttachParams): TLPWM = { 108 | implicit val p = params.p 109 | val name = s"pwm_${nextId()}" 110 | val cbus = params.controlBus 111 | val pwm = LazyModule(new TLPWM(cbus.beatBytes, params.pwm)) 112 | pwm.suggestName(name) 113 | cbus.coupleTo(s"device_named_$name") { 114 | pwm.controlXing(params.controlXType) := TLFragmenter(cbus.beatBytes, cbus.blockBytes) := _ 115 | } 116 | params.intNode := pwm.intXing(params.intXType) 117 | InModuleBody { pwm.module.clock := params.mclock.map(_.getWrappedValue).getOrElse(cbus.module.clock) } 118 | InModuleBody { pwm.module.reset := params.mreset.map(_.getWrappedValue).getOrElse(cbus.module.reset) } 119 | 120 | pwm 121 | } 122 | 123 | def attachAndMakePort(params: PWMAttachParams): ModuleValue[PWMPortIO] = { 124 | val pwm = attach(params) 125 | val pwmNode = pwm.ioNode.makeSink()(params.p) 126 | InModuleBody { pwmNode.makeIO()(ValName(pwm.name)) } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/pwm/PWMPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.pwm 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Field 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.subsystem.BaseSubsystem 8 | 9 | case object PeripheryPWMKey extends Field[Seq[PWMParams]] 10 | 11 | trait HasPeripheryPWM { this: BaseSubsystem => 12 | val pwmNodes = p(PeripheryPWMKey).map { ps => PWM.attach(PWMAttachParams(ps, pbus, ibus.fromAsync)).ioNode.makeSink } 13 | } 14 | 15 | trait HasPeripheryPWMBundle { 16 | val pwm: Seq[PWMPortIO] 17 | } 18 | 19 | trait HasPeripheryPWMModuleImp extends LazyModuleImp with HasPeripheryPWMBundle { 20 | val outer: HasPeripheryPWM 21 | val pwm = outer.pwmNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"pwm_$i")) } 22 | } 23 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/pwm/PWMPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.pwm 3 | 4 | import Chisel._ 5 | import chisel3.experimental.{withClockAndReset} 6 | import sifive.blocks.devices.pinctrl.{Pin} 7 | 8 | class PWMSignals[T <: Data](private val pingen: () => T, val c: PWMParams) extends Bundle { 9 | val pwm: Vec[T] = Vec(c.ncmp, pingen()) 10 | } 11 | 12 | class PWMPins[T <: Pin](pingen: () => T, c: PWMParams) extends PWMSignals[T](pingen, c) 13 | 14 | object PWMPinsFromPort { 15 | def apply[T <: Pin] (pins: PWMSignals[T], port: PWMPortIO, clock: Clock, reset: Bool): Unit = { 16 | withClockAndReset(clock, reset){ 17 | (pins.pwm zip port.gpio) foreach { case (pin, port) => 18 | pin.outputPin(port) 19 | } 20 | } 21 | } 22 | 23 | def apply[T <: Pin] (pins: PWMSignals[T], port: PWMPortIO): Unit = { 24 | (pins.pwm zip port.gpio) foreach { case (pin, port) => 25 | pin.outputPin(port) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/BlackBoxDelayBuffer.scala: -------------------------------------------------------------------------------- 1 | package sifive.blocks.devices.spi 2 | 3 | import Chisel._ 4 | import freechips.rocketchip.util.ShiftRegInit 5 | import chisel3.experimental._ 6 | 7 | class BlackBoxDelayBuffer extends BlackBox { 8 | val io = IO(new Bundle() { 9 | val in = UInt(INPUT,1.W) 10 | val sel = UInt(INPUT,5.W) 11 | val out = UInt(OUTPUT, 1.W) 12 | val mux_out = UInt(OUTPUT, 1.W) 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPI.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Parameters 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.interrupts._ 8 | import freechips.rocketchip.regmapper._ 9 | import freechips.rocketchip.tilelink._ 10 | 11 | case class SPIAttachParams( 12 | spi: SPIParams, 13 | controlBus: TLBusWrapper, 14 | intNode: IntInwardNode, 15 | controlXType: ClockCrossingType = NoCrossing, 16 | intXType: ClockCrossingType = NoCrossing, 17 | mclock: Option[ModuleValue[Clock]] = None, 18 | mreset: Option[ModuleValue[Bool]] = None) 19 | (implicit val p: Parameters) 20 | 21 | case class SPIFlashAttachParams( 22 | spi: SPIFlashParams, 23 | controlBus: TLBusWrapper, 24 | memBus: TLBusWrapper, 25 | intNode: IntInwardNode, 26 | fBufferDepth: Int = 0, 27 | controlXType: ClockCrossingType = NoCrossing, 28 | intXType: ClockCrossingType = NoCrossing, 29 | memXType: ClockCrossingType = NoCrossing, 30 | mclock: Option[ModuleValue[Clock]] = None, 31 | mreset: Option[ModuleValue[Bool]] = None) 32 | (implicit val p: Parameters) 33 | 34 | object SPI { 35 | val nextId = { var i = -1; () => { i += 1; i} } 36 | def attach(params: SPIAttachParams): TLSPI = { 37 | implicit val p = params.p 38 | val name = s"spi_${nextId()}" 39 | val cbus = params.controlBus 40 | val spi = LazyModule(new TLSPI(cbus.beatBytes, params.spi)) 41 | spi.suggestName(name) 42 | 43 | cbus.coupleTo(s"device_named_$name") { 44 | spi.controlXing(params.controlXType) := TLFragmenter(cbus.beatBytes, cbus.blockBytes) := _ 45 | } 46 | 47 | params.intNode := spi.intXing(params.intXType) 48 | 49 | InModuleBody { spi.module.clock := params.mclock.map(_.getWrappedValue).getOrElse(cbus.module.clock) } 50 | InModuleBody { spi.module.reset := params.mreset.map(_.getWrappedValue).getOrElse(cbus.module.reset) } 51 | 52 | spi 53 | } 54 | 55 | def attachAndMakePort(params: SPIAttachParams): ModuleValue[SPIPortIO] = { 56 | val spi = attach(params) 57 | val spiNode = spi.ioNode.makeSink()(params.p) 58 | InModuleBody { spiNode.makeIO()(ValName(spi.name)) } 59 | } 60 | 61 | val nextFlashId = { var i = -1; () => { i += 1; i} } 62 | def attachFlash(params: SPIFlashAttachParams): TLSPIFlash = { 63 | implicit val p = params.p 64 | val name = s"qspi_${nextFlashId()}" // TODO should these be shared with regular SPIs? 65 | val cbus = params.controlBus 66 | val mbus = params.memBus 67 | val qspi = LazyModule(new TLSPIFlash(cbus.beatBytes, params.spi)) 68 | qspi.suggestName(name) 69 | 70 | cbus.coupleTo(s"device_named_$name") { 71 | qspi.controlXing(params.controlXType) := TLFragmenter(cbus.beatBytes, cbus.blockBytes) := _ 72 | } 73 | 74 | mbus.coupleTo(s"mem_named_$name") { 75 | (qspi.memXing(params.memXType) 76 | := TLFragmenter(1, mbus.blockBytes) 77 | := TLBuffer(BufferParams(params.fBufferDepth), BufferParams.none) 78 | := TLWidthWidget(mbus.beatBytes) 79 | := _) 80 | } 81 | 82 | params.intNode := qspi.intXing(params.intXType) 83 | 84 | InModuleBody { qspi.module.clock := params.mclock.map(_.getWrappedValue).getOrElse(cbus.module.clock) } 85 | InModuleBody { qspi.module.reset := params.mreset.map(_.getWrappedValue).getOrElse(cbus.module.reset) } 86 | 87 | qspi 88 | } 89 | 90 | def attachAndMakePort(params: SPIFlashAttachParams): ModuleValue[SPIPortIO] = { 91 | val qspi = attachFlash(params) 92 | val qspiNode = qspi.ioNode.makeSink()(params.p) 93 | InModuleBody { qspiNode.makeIO()(ValName(qspi.name)) } 94 | } 95 | 96 | def connectPort(q: SPIPortIO): SPIPortIO = { 97 | val x = Wire(new SPIPortIO(q.c)) 98 | x <> q 99 | x 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIArbiter.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | class SPIInnerIO(c: SPIParamsBase) extends SPILinkIO(c) { 7 | val lock = Bool(OUTPUT) 8 | } 9 | 10 | class SPIArbiter(c: SPIParamsBase, n: Int) extends Module { 11 | val io = new Bundle { 12 | val inner = Vec(n, new SPIInnerIO(c)).flip 13 | val outer = new SPILinkIO(c) 14 | val sel = UInt(INPUT, log2Up(n)) 15 | } 16 | 17 | val sel = Reg(init = Vec(Bool(true) +: Seq.fill(n-1)(Bool(false)))) 18 | 19 | io.outer.tx.valid := Mux1H(sel, io.inner.map(_.tx.valid)) 20 | io.outer.tx.bits := Mux1H(sel, io.inner.map(_.tx.bits)) 21 | io.outer.cnt := Mux1H(sel, io.inner.map(_.cnt)) 22 | io.outer.fmt := Mux1H(sel, io.inner.map(_.fmt)) 23 | // Workaround for overzealous combinational loop detection 24 | io.outer.cs := Mux(sel(0), io.inner(0).cs, io.inner(1).cs) 25 | require(n == 2, "SPIArbiter currently only supports 2 clients") 26 | 27 | (io.inner zip sel).foreach { case (inner, s) => 28 | inner.tx.ready := io.outer.tx.ready && s 29 | inner.rx.valid := io.outer.rx.valid && s 30 | inner.rx.bits := io.outer.rx.bits 31 | inner.active := io.outer.active && s 32 | } 33 | 34 | val nsel = Vec.tabulate(n)(io.sel === UInt(_)) 35 | val lock = Mux1H(sel, io.inner.map(_.lock)) 36 | when (!lock) { 37 | sel := nsel 38 | when (sel.asUInt =/= nsel.asUInt) { 39 | io.outer.cs.clear := Bool(true) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIBundle.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | abstract class SPIBundle(val c: SPIParamsBase) extends Bundle 7 | 8 | class SPIDataIO extends Bundle { 9 | val i = Bool(INPUT) 10 | val o = Bool(OUTPUT) 11 | val oe = Bool(OUTPUT) 12 | } 13 | 14 | class SPIPortIO(c: SPIParamsBase) extends SPIBundle(c) { 15 | val sck = Bool(OUTPUT) 16 | val dq = Vec(4, new SPIDataIO) 17 | val cs = Vec(c.csWidth, Bool(OUTPUT)) 18 | } 19 | 20 | trait HasSPIProtocol { 21 | val proto = Bits(width = SPIProtocol.width) 22 | } 23 | trait HasSPIEndian { 24 | val endian = Bits(width = SPIEndian.width) 25 | } 26 | class SPIFormat(c: SPIParamsBase) extends SPIBundle(c) 27 | with HasSPIProtocol 28 | with HasSPIEndian { 29 | val iodir = Bits(width = SPIDirection.width) 30 | } 31 | 32 | trait HasSPILength extends SPIBundle { 33 | val len = UInt(width = c.lengthBits) 34 | } 35 | 36 | class SPIClocking(c: SPIParamsBase) extends SPIBundle(c) { 37 | val div = UInt(width = c.divisorBits) 38 | val pol = Bool() 39 | val pha = Bool() 40 | } 41 | 42 | class SPIChipSelect(c: SPIParamsBase) extends SPIBundle(c) { 43 | val id = UInt(width = c.csIdBits) 44 | val dflt = Vec(c.csWidth, Bool()) 45 | 46 | def toggle(en: Bool): Vec[Bool] = { 47 | val mask = en << id 48 | val out = Cat(dflt.reverse) ^ mask 49 | Vec.tabulate(c.csWidth)(out(_)) 50 | } 51 | } 52 | 53 | trait HasSPICSMode { 54 | val mode = Bits(width = SPICSMode.width) 55 | } 56 | 57 | class SPIDelay(c: SPIParamsBase) extends SPIBundle(c) { 58 | val cssck = UInt(width = c.delayBits) 59 | val sckcs = UInt(width = c.delayBits) 60 | val intercs = UInt(width = c.delayBits) 61 | val interxfr = UInt(width = c.delayBits) 62 | } 63 | 64 | class SPIWatermark(c: SPIParamsBase) extends SPIBundle(c) { 65 | val tx = UInt(width = c.txDepthBits) 66 | val rx = UInt(width = c.rxDepthBits) 67 | } 68 | 69 | class SPIControl(c: SPIParamsBase) extends SPIBundle(c) { 70 | val fmt = new SPIFormat(c) with HasSPILength 71 | val sck = new SPIClocking(c) 72 | val cs = new SPIChipSelect(c) with HasSPICSMode 73 | val dla = new SPIDelay(c) 74 | val wm = new SPIWatermark(c) 75 | val extradel = new SPIExtraDelay(c) 76 | val sampledel = new SPISampleDelay(c) 77 | } 78 | 79 | object SPIControl { 80 | def init(c: SPIParamsBase): SPIControl = { 81 | val ctrl = Wire(new SPIControl(c)) 82 | ctrl.fmt.proto := SPIProtocol.Single 83 | ctrl.fmt.iodir := SPIDirection.Rx 84 | ctrl.fmt.endian := SPIEndian.MSB 85 | ctrl.fmt.len := UInt(math.min(c.frameBits, 8)) 86 | ctrl.sck.div := UInt(3) 87 | ctrl.sck.pol := Bool(false) 88 | ctrl.sck.pha := Bool(false) 89 | ctrl.cs.id := UInt(0) 90 | ctrl.cs.dflt.foreach { _ := Bool(true) } 91 | ctrl.cs.mode := SPICSMode.Auto 92 | ctrl.dla.cssck := UInt(1) 93 | ctrl.dla.sckcs := UInt(1) 94 | ctrl.dla.intercs := UInt(1) 95 | ctrl.dla.interxfr := UInt(0) 96 | ctrl.wm.tx := UInt(0) 97 | ctrl.wm.rx := UInt(0) 98 | ctrl.extradel.coarse := UInt(0) 99 | ctrl.extradel.fine := UInt(0) 100 | ctrl.sampledel.sd := UInt(c.defaultSampleDel) 101 | ctrl 102 | } 103 | } 104 | 105 | class SPIInterrupts extends Bundle { 106 | val txwm = Bool() 107 | val rxwm = Bool() 108 | } 109 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIConsts.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | object SPIProtocol { 7 | val width = 2 8 | def Single = UInt(0, width) 9 | def Dual = UInt(1, width) 10 | def Quad = UInt(2, width) 11 | 12 | def cases = Seq(Single, Dual, Quad) 13 | def decode(x: UInt): Seq[Bool] = cases.map(_ === x) 14 | } 15 | 16 | object SPIDirection { 17 | val width = 1 18 | def Rx = UInt(0, width) 19 | def Tx = UInt(1, width) 20 | } 21 | 22 | object SPIEndian { 23 | val width = 1 24 | def MSB = UInt(0, width) 25 | def LSB = UInt(1, width) 26 | } 27 | 28 | object SPICSMode { 29 | val width = 2 30 | def Auto = UInt(0, width) 31 | def Hold = UInt(2, width) 32 | def Off = UInt(3, width) 33 | } 34 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIFIFO.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | class SPIFIFOControl(c: SPIParamsBase) extends SPIBundle(c) { 7 | val fmt = new SPIFormat(c) with HasSPILength 8 | val cs = new Bundle with HasSPICSMode 9 | val wm = new SPIWatermark(c) 10 | } 11 | 12 | class SPIFIFO(c: SPIParamsBase) extends Module { 13 | val io = new Bundle { 14 | val ctrl = new SPIFIFOControl(c).asInput 15 | val link = new SPIInnerIO(c) 16 | val tx = Decoupled(Bits(width = c.frameBits)).flip 17 | val rx = Decoupled(Bits(width = c.frameBits)) 18 | val ip = new SPIInterrupts().asOutput 19 | } 20 | 21 | val txq = Module(new Queue(io.tx.bits, c.txDepth)) 22 | val rxq = Module(new Queue(io.rx.bits, c.rxDepth)) 23 | 24 | txq.io.enq <> io.tx 25 | io.link.tx <> txq.io.deq 26 | 27 | val fire_tx = io.link.tx.fire() 28 | val fire_rx = io.link.rx.fire() 29 | val rxen = Reg(init = Bool(false)) 30 | 31 | rxq.io.enq.valid := io.link.rx.valid && rxen 32 | rxq.io.enq.bits := io.link.rx.bits 33 | io.rx <> rxq.io.deq 34 | 35 | when (fire_rx) { 36 | rxen := Bool(false) 37 | } 38 | when (fire_tx) { 39 | rxen := (io.link.fmt.iodir === SPIDirection.Rx) 40 | } 41 | 42 | val proto = SPIProtocol.decode(io.link.fmt.proto).zipWithIndex 43 | val cnt_quot = Mux1H(proto.map { case (s, i) => s -> (io.ctrl.fmt.len >> i) }) 44 | val cnt_rmdr = Mux1H(proto.map { case (s, i) => s -> (if (i > 0) io.ctrl.fmt.len(i-1, 0).orR else UInt(0)) }) 45 | io.link.fmt <> io.ctrl.fmt 46 | io.link.cnt := cnt_quot + cnt_rmdr 47 | 48 | val cs_mode = RegNext(io.ctrl.cs.mode, SPICSMode.Auto) 49 | val cs_mode_hold = (cs_mode === SPICSMode.Hold) 50 | val cs_mode_off = (cs_mode === SPICSMode.Off) 51 | val cs_update = (cs_mode =/= io.ctrl.cs.mode) 52 | val cs_clear = !(cs_mode_hold || cs_mode_off) 53 | 54 | io.link.cs.set := !cs_mode_off 55 | io.link.cs.clear := cs_update || (fire_tx && cs_clear) 56 | io.link.cs.hold := Bool(false) 57 | 58 | io.link.lock := io.link.tx.valid || rxen 59 | 60 | io.ip.txwm := (txq.io.count < io.ctrl.wm.tx) 61 | io.ip.rxwm := (rxq.io.count > io.ctrl.wm.rx) 62 | } 63 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIFlash.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | class SPIFlashInsn(c: SPIFlashParamsBase) extends SPIBundle(c) { 7 | val cmd = new Bundle with HasSPIProtocol { 8 | val code = Bits(width = c.insnCmdBits) 9 | val en = Bool() 10 | } 11 | val addr = new Bundle with HasSPIProtocol { 12 | val len = UInt(width = c.insnAddrLenBits) 13 | } 14 | val pad = new Bundle { 15 | val code = Bits(width = c.frameBits) 16 | val cnt = Bits(width = c.insnPadLenBits) 17 | } 18 | val data = new Bundle with HasSPIProtocol 19 | } 20 | 21 | class SPIFlashControl(c: SPIFlashParamsBase) extends SPIBundle(c) { 22 | val insn = new SPIFlashInsn(c) 23 | val fmt = new Bundle with HasSPIEndian 24 | } 25 | 26 | object SPIFlashInsn { 27 | def init(c: SPIFlashParamsBase): SPIFlashInsn = { 28 | val insn = Wire(new SPIFlashInsn(c)) 29 | insn.cmd.en := Bool(true) 30 | insn.cmd.code := Bits(0x03) 31 | insn.cmd.proto := SPIProtocol.Single 32 | insn.addr.len := UInt(3) 33 | insn.addr.proto := SPIProtocol.Single 34 | insn.pad.cnt := UInt(0) 35 | insn.pad.code := Bits(0) 36 | insn.data.proto := SPIProtocol.Single 37 | insn 38 | } 39 | } 40 | 41 | class SPIFlashAddr(c: SPIFlashParamsBase) extends SPIBundle(c) { 42 | val next = UInt(width = c.insnAddrBits) 43 | val hold = UInt(width = c.insnAddrBits) 44 | } 45 | 46 | class SPIFlashMap(c: SPIFlashParamsBase) extends Module { 47 | val io = new Bundle { 48 | val en = Bool(INPUT) 49 | val ctrl = new SPIFlashControl(c).asInput 50 | val addr = Decoupled(new SPIFlashAddr(c)).flip 51 | val data = Decoupled(UInt(width = c.frameBits)) 52 | val link = new SPIInnerIO(c) 53 | } 54 | 55 | val addr = io.addr.bits.hold + UInt(1) 56 | val merge = io.link.active && (io.addr.bits.next === addr) 57 | 58 | private val insn = io.ctrl.insn 59 | io.link.tx.valid := Bool(true) 60 | io.link.fmt.proto := insn.addr.proto 61 | io.link.fmt.iodir := SPIDirection.Tx 62 | io.link.fmt.endian := io.ctrl.fmt.endian 63 | io.link.cnt := Mux1H( 64 | SPIProtocol.decode(io.link.fmt.proto).zipWithIndex.map { 65 | case (s, i) => (s -> UInt(c.frameBits >> i)) 66 | }) 67 | io.link.cs.set := Bool(true) 68 | io.link.cs.clear := Bool(false) 69 | io.link.cs.hold := Bool(true) 70 | io.link.lock := Bool(true) 71 | 72 | io.addr.ready := Bool(false) 73 | io.data.valid := Bool(false) 74 | io.data.bits := io.link.rx.bits 75 | 76 | val cnt = Reg(UInt(width = math.max(c.insnPadLenBits, c.insnAddrLenBits))) 77 | val cnt_en = Wire(init = Bool(false)) 78 | val cnt_cmp = (0 to c.insnAddrBytes).map(cnt === UInt(_)) 79 | val cnt_zero = cnt_cmp(0) 80 | val cnt_last = cnt_cmp(1) && io.link.tx.ready 81 | val cnt_done = cnt_last || cnt_zero 82 | when (cnt_en) { 83 | io.link.tx.valid := !cnt_zero 84 | when (io.link.tx.fire()) { 85 | cnt := cnt - UInt(1) 86 | } 87 | } 88 | 89 | val (s_idle :: s_cmd :: s_addr :: s_pad :: s_data_pre :: s_data_post :: s_off :: Nil) = Enum(UInt(), 7) 90 | val state = Reg(init = s_idle) 91 | 92 | switch (state) { 93 | is (s_idle) { 94 | io.link.tx.valid := Bool(false) 95 | when (io.en) { 96 | io.addr.ready := Bool(true) 97 | when (io.addr.valid) { 98 | when (merge) { 99 | state := s_data_pre 100 | } .otherwise { 101 | state := Mux(insn.cmd.en, s_cmd, s_addr) 102 | io.link.cs.clear := Bool(true) 103 | } 104 | } .otherwise { 105 | io.link.lock := Bool(false) 106 | } 107 | } .otherwise { 108 | io.addr.ready := Bool(true) 109 | io.link.lock := Bool(false) 110 | when (io.addr.valid) { 111 | state := s_off 112 | } 113 | } 114 | } 115 | 116 | is (s_cmd) { 117 | io.link.fmt.proto := insn.cmd.proto 118 | io.link.tx.bits := insn.cmd.code 119 | when (io.link.tx.ready) { 120 | state := s_addr 121 | cnt := insn.addr.len 122 | } 123 | } 124 | 125 | is (s_addr) { 126 | io.link.tx.bits := Mux1H(cnt_cmp.tail.zipWithIndex.map { 127 | case (s, i) => 128 | val n = i * c.frameBits 129 | val m = n + (c.frameBits - 1) 130 | s -> io.addr.bits.hold(m, n) 131 | }) 132 | 133 | cnt_en := Bool(true) 134 | when (cnt_done) { 135 | state := s_pad 136 | } 137 | } 138 | 139 | is (s_pad) { 140 | io.link.cnt := insn.pad.cnt 141 | io.link.tx.bits := insn.pad.code 142 | when (io.link.tx.ready) { 143 | state := s_data_pre 144 | } 145 | } 146 | 147 | is (s_data_pre) { 148 | io.link.fmt.proto := insn.data.proto 149 | io.link.fmt.iodir := SPIDirection.Rx 150 | when (io.link.tx.ready) { 151 | state := s_data_post 152 | } 153 | } 154 | 155 | is (s_data_post) { 156 | io.link.tx.valid := Bool(false) 157 | io.data.valid := io.link.rx.valid 158 | when (io.data.fire()) { 159 | state := s_idle 160 | } 161 | } 162 | 163 | is (s_off) { 164 | io.data.valid := Bool(true) 165 | io.data.bits := UInt(0) 166 | when (io.data.ready) { 167 | state := s_idle 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIMedia.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | 6 | class SPILinkIO(c: SPIParamsBase) extends SPIBundle(c) { 7 | val tx = Decoupled(Bits(width = c.frameBits)) 8 | val rx = Valid(Bits(width = c.frameBits)).flip 9 | 10 | val cnt = UInt(OUTPUT, c.countBits) 11 | val fmt = new SPIFormat(c).asOutput 12 | val cs = new Bundle { 13 | val set = Bool(OUTPUT) 14 | val clear = Bool(OUTPUT) // Deactivate CS 15 | val hold = Bool(OUTPUT) // Supress automatic CS deactivation 16 | } 17 | val active = Bool(INPUT) 18 | } 19 | 20 | class SPIMedia(c: SPIParamsBase) extends Module { 21 | val io = new Bundle { 22 | val port = new SPIPortIO(c) 23 | val ctrl = new Bundle { 24 | val sck = new SPIClocking(c).asInput 25 | val dla = new SPIDelay(c).asInput 26 | val cs = new SPIChipSelect(c).asInput 27 | val extradel = new SPIExtraDelay(c).asInput 28 | val sampledel = new SPISampleDelay(c).asInput 29 | } 30 | val link = new SPILinkIO(c).flip 31 | } 32 | 33 | val phy = Module(new SPIPhysical(c)) 34 | phy.io.ctrl.sck := io.ctrl.sck 35 | phy.io.ctrl.fmt := io.link.fmt 36 | phy.io.ctrl.extradel := io.ctrl.extradel 37 | phy.io.ctrl.sampledel := io.ctrl.sampledel 38 | 39 | private val op = phy.io.op 40 | op.valid := Bool(true) 41 | op.bits.fn := SPIMicroOp.Delay 42 | op.bits.stb := Bool(false) 43 | op.bits.cnt := io.link.cnt 44 | op.bits.data := io.link.tx.bits 45 | 46 | val cs = Reg(io.ctrl.cs) 47 | val cs_set = Reg(Bool()) 48 | val cs_active = io.ctrl.cs.toggle(io.link.cs.set) 49 | val cs_update = (cs_active.asUInt =/= cs.dflt.asUInt) 50 | 51 | val clear = Reg(init = Bool(false)) 52 | val cs_assert = Reg(init = Bool(false)) 53 | val cs_deassert = clear || (cs_update && !io.link.cs.hold) 54 | 55 | clear := clear || (io.link.cs.clear && cs_assert) 56 | 57 | val continuous = (io.ctrl.dla.interxfr === UInt(0)) 58 | 59 | io.port.sck := phy.io.port.sck 60 | io.port.dq <> phy.io.port.dq 61 | io.port.cs := cs.dflt 62 | 63 | io.link.rx := phy.io.rx 64 | io.link.tx.ready := Bool(false) 65 | io.link.active := cs_assert 66 | 67 | val (s_main :: s_interxfr :: s_intercs :: Nil) = Enum(UInt(), 3) 68 | val state = Reg(init = s_main) 69 | 70 | switch (state) { 71 | is (s_main) { 72 | when (cs_assert) { 73 | when (cs_deassert) { 74 | op.bits.cnt := io.ctrl.dla.sckcs 75 | when (op.ready) { 76 | state := s_intercs 77 | } 78 | } .otherwise { 79 | op.bits.fn := SPIMicroOp.Transfer 80 | op.bits.stb := Bool(true) 81 | 82 | op.valid := io.link.tx.valid 83 | io.link.tx.ready := op.ready 84 | when (op.fire()) { 85 | state := s_interxfr 86 | } 87 | } 88 | } .elsewhen (io.link.tx.valid) { 89 | // Assert CS 90 | op.bits.cnt := io.ctrl.dla.cssck 91 | when (op.ready) { 92 | cs_assert := Bool(true) 93 | cs_set := io.link.cs.set 94 | cs.dflt := cs_active 95 | } 96 | } .otherwise { 97 | // Idle 98 | op.bits.cnt := UInt(0) 99 | op.bits.stb := Bool(true) 100 | cs := io.ctrl.cs 101 | } 102 | } 103 | 104 | is (s_interxfr) { 105 | // Skip if interxfr delay is zero 106 | op.valid := !continuous 107 | op.bits.cnt := io.ctrl.dla.interxfr 108 | when (op.ready || continuous) { 109 | state := s_main 110 | } 111 | } 112 | 113 | is (s_intercs) { 114 | // Deassert CS 115 | op.bits.cnt := io.ctrl.dla.intercs 116 | op.bits.stb := Bool(true) 117 | cs_assert := Bool(false) 118 | clear := Bool(false) 119 | when (op.ready) { 120 | cs.dflt := cs.toggle(cs_set) 121 | state := s_main 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Field 6 | import freechips.rocketchip.subsystem.{BaseSubsystem} 7 | import freechips.rocketchip.diplomacy._ 8 | 9 | case object PeripherySPIKey extends Field[Seq[SPIParams]] 10 | 11 | trait HasPeripherySPI { this: BaseSubsystem => 12 | val spiNodes = p(PeripherySPIKey).map { ps => SPI.attach(SPIAttachParams(ps, pbus, ibus.fromAsync)).ioNode.makeSink() } 13 | } 14 | 15 | trait HasPeripherySPIBundle { 16 | val spi: Seq[SPIPortIO] 17 | } 18 | 19 | trait HasPeripherySPIModuleImp extends LazyModuleImp with HasPeripherySPIBundle { 20 | val outer: HasPeripherySPI 21 | val spi = outer.spiNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"spi_$i")) } 22 | } 23 | 24 | case object PeripherySPIFlashKey extends Field[Seq[SPIFlashParams]] 25 | 26 | trait HasPeripherySPIFlash { this: BaseSubsystem => 27 | val qspiNodes = p(PeripherySPIFlashKey).map { ps => 28 | SPI.attachFlash(SPIFlashAttachParams(ps, pbus, pbus, ibus.fromAsync, fBufferDepth = 8)).ioNode.makeSink() 29 | } 30 | } 31 | 32 | trait HasPeripherySPIFlashBundle { 33 | val qspi: Seq[SPIPortIO] 34 | } 35 | 36 | trait HasPeripherySPIFlashModuleImp extends LazyModuleImp with HasPeripherySPIFlashBundle { 37 | val outer: HasPeripherySPIFlash 38 | val qspi = outer.qspiNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"qspi_$i")) } 39 | } 40 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | import chisel3.experimental.{withClockAndReset} 6 | import freechips.rocketchip.util.{SynchronizerShiftReg} 7 | import sifive.blocks.devices.pinctrl.{PinCtrl, Pin} 8 | 9 | class SPISignals[T <: Data](private val pingen: () => T, c: SPIParamsBase) extends SPIBundle(c) { 10 | 11 | val sck = pingen() 12 | val dq = Vec(4, pingen()) 13 | val cs = Vec(c.csWidth, pingen()) 14 | } 15 | 16 | class SPIPins[T <: Pin] (pingen: ()=> T, c: SPIParamsBase) extends SPISignals(pingen, c) 17 | 18 | object SPIPinsFromPort { 19 | 20 | def apply[T <: Pin](pins: SPISignals[T], spi: SPIPortIO, clock: Clock, reset: Bool, 21 | syncStages: Int = 0, driveStrength: Bool = Bool(false)) { 22 | 23 | withClockAndReset(clock, reset) { 24 | pins.sck.outputPin(spi.sck, ds = driveStrength) 25 | 26 | (pins.dq zip spi.dq).zipWithIndex.foreach {case ((p, s), i) => 27 | p.outputPin(s.o, pue = Bool(true), ds = driveStrength) 28 | p.o.oe := s.oe 29 | p.o.ie := ~s.oe 30 | s.i := SynchronizerShiftReg(p.i.ival, syncStages, name = Some(s"spi_dq_${i}_sync")) 31 | } 32 | 33 | (pins.cs zip spi.cs) foreach { case (c, s) => 34 | c.outputPin(s, ds = driveStrength) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/SPIRegs.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | object SPICRs { 5 | val sckdiv = 0x00 6 | val sckmode = 0x04 7 | val csid = 0x10 8 | val csdef = 0x14 9 | val csmode = 0x18 10 | val dcssck = 0x28 11 | val dsckcs = 0x2a 12 | val dintercs = 0x2c 13 | val dinterxfr = 0x2e 14 | val extradel = 0x38 15 | val sampledel = 0x3c 16 | 17 | val fmt = 0x40 18 | val len = 0x42 19 | val txfifo = 0x48 20 | val rxfifo = 0x4c 21 | val txmark = 0x50 22 | val rxmark = 0x54 23 | 24 | val insnmode = 0x60 25 | val insnfmt = 0x64 26 | val insnproto = 0x65 27 | val insncmd = 0x66 28 | val insnpad = 0x67 29 | 30 | val ie = 0x70 31 | val ip = 0x74 32 | } 33 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/spi/TLSPIFlash.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.spi 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Parameters 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.regmapper._ 8 | import freechips.rocketchip.tilelink._ 9 | import freechips.rocketchip.subsystem._ 10 | import freechips.rocketchip.util.HeterogeneousBag 11 | 12 | trait SPIFlashParamsBase extends SPIParamsBase { 13 | val fAddress: BigInt 14 | val fSize: BigInt 15 | 16 | val insnAddrBytes: Int 17 | val insnPadLenBits: Int 18 | lazy val insnCmdBits = frameBits 19 | lazy val insnAddrBits = insnAddrBytes * frameBits 20 | lazy val insnAddrLenBits = log2Floor(insnAddrBytes) + 1 21 | } 22 | 23 | case class SPIFlashParams( 24 | rAddress: BigInt, 25 | fAddress: BigInt, 26 | rSize: BigInt = 0x1000, 27 | fSize: BigInt = 0x20000000, 28 | rxDepth: Int = 8, 29 | txDepth: Int = 8, 30 | csWidth: Int = 1, 31 | delayBits: Int = 8, 32 | divisorBits: Int = 12, 33 | fineDelayBits: Int = 0, 34 | sampleDelayBits: Int = 5, 35 | defaultSampleDel: Int = 3 36 | ) 37 | extends SPIFlashParamsBase { 38 | val frameBits = 8 39 | val insnAddrBytes = 4 40 | val insnPadLenBits = 4 41 | 42 | require(insnPadLenBits <= delayBits) 43 | require((fineDelayBits == 0) | (fineDelayBits == 5), s"Require fine delay bits to be 0 or 5 and not $fineDelayBits") 44 | require(sampleDelayBits >= 0) 45 | require(defaultSampleDel >= 0) 46 | } 47 | 48 | class SPIFlashTopModule(c: SPIFlashParamsBase, outer: TLSPIFlashBase) 49 | extends SPITopModule(c, outer) { 50 | 51 | val flash = Module(new SPIFlashMap(c)) 52 | val arb = Module(new SPIArbiter(c, 2)) 53 | 54 | private val (f, _) = outer.fnode.in(0) 55 | // Tie unused channels 56 | f.b.valid := Bool(false) 57 | f.c.ready := Bool(true) 58 | f.e.ready := Bool(true) 59 | 60 | val a = Reg(f.a.bits) 61 | val a_msb = log2Ceil(c.fSize) - 1 62 | 63 | when (f.a.fire()) { 64 | a := f.a.bits 65 | } 66 | 67 | flash.io.addr.bits.next := f.a.bits.address(a_msb, 0) 68 | flash.io.addr.bits.hold := a.address(a_msb, 0) 69 | flash.io.addr.valid := f.a.valid 70 | f.a.ready := flash.io.addr.ready 71 | 72 | f.d.bits := outer.fnode.edges.in.head.AccessAck(a, flash.io.data.bits) 73 | f.d.valid := flash.io.data.valid 74 | flash.io.data.ready := f.d.ready 75 | 76 | val insn = Reg(init = SPIFlashInsn.init(c)) 77 | val flash_en = Reg(init = Bool(true)) 78 | 79 | flash.io.ctrl.insn := insn 80 | flash.io.ctrl.fmt <> ctrl.fmt 81 | flash.io.en := flash_en 82 | arb.io.sel := !flash_en 83 | 84 | protected val regmapFlash = Seq( 85 | SPICRs.insnmode -> Seq(RegField(1, flash_en, 86 | RegFieldDesc("flash_en","SPIFlash mode select", reset=Some(1)))), 87 | SPICRs.insnfmt -> RegFieldGroup("ffmtlen",Some("SPIFlash instruction length"),Seq( 88 | RegField(1, insn.cmd.en, 89 | RegFieldDesc("cmd_en","Enable sending of command", reset=Some(1))), 90 | RegField(c.insnAddrLenBits, insn.addr.len, 91 | RegFieldDesc("cmd_en","Number of address bytes", reset=Some(3))), 92 | RegField(c.insnPadLenBits, insn.pad.cnt, 93 | RegFieldDesc("cmd_en","Number of dummy cycles", reset=Some(0))))), 94 | SPICRs.insnproto -> RegFieldGroup("ffmtproto",Some("SPIFlash instruction format"),Seq( 95 | RegField(SPIProtocol.width, insn.cmd.proto, 96 | RegFieldDesc("cmd_proto","Protocol for transmitting command", reset=Some(0))), 97 | RegField(SPIProtocol.width, insn.addr.proto, 98 | RegFieldDesc("addr_proto","Protocol for transmitting address and padding", reset=Some(0))), 99 | RegField(SPIProtocol.width, insn.data.proto, 100 | RegFieldDesc("data_proto","Protocol for transmitting receiving data", reset=Some(0))))), 101 | SPICRs.insncmd -> Seq(RegField(c.insnCmdBits, insn.cmd.code, 102 | RegFieldDesc("cmd_code","Value of command byte", reset=Some(3)))), 103 | SPICRs.insnpad -> Seq(RegField(c.frameBits, insn.pad.code, 104 | RegFieldDesc("pad_code","First 8 bits to transmit during dummy cycles", reset=Some(0))))) 105 | } 106 | 107 | abstract class TLSPIFlashBase(w: Int, c: SPIFlashParamsBase)(implicit p: Parameters) extends TLSPIBase(w,c)(p) { 108 | require(isPow2(c.fSize)) 109 | val fnode = TLManagerNode(Seq(TLManagerPortParameters( 110 | managers = Seq(TLManagerParameters( 111 | address = Seq(AddressSet(c.fAddress, c.fSize-1)), 112 | resources = device.reg("mem"), 113 | regionType = RegionType.UNCACHED, 114 | executable = true, 115 | supportsGet = TransferSizes(1, 1), 116 | fifoId = Some(0))), 117 | beatBytes = 1))) 118 | val memXing = this.crossIn(fnode) 119 | } 120 | 121 | class TLSPIFlash(w: Int, c: SPIFlashParams)(implicit p: Parameters) 122 | extends TLSPIFlashBase(w,c)(p) 123 | with HasTLControlRegMap { 124 | lazy val module = new SPIFlashTopModule(c, this) { 125 | 126 | arb.io.inner(0) <> flash.io.link 127 | arb.io.inner(1) <> fifo.io.link 128 | mac.io.link <> arb.io.outer 129 | 130 | regmap(regmapBase ++ regmapFlash:_*) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/stream/PseudoStream.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.stream 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.config.Parameters 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.regmapper._ 8 | import freechips.rocketchip.tilelink._ 9 | import sifive.blocks.util.{NonBlockingEnqueue, NonBlockingDequeue} 10 | 11 | case class PseudoStreamParams( 12 | address: BigInt, 13 | nChannels: Int = 1, 14 | dataBits: Int = 32) { 15 | require(dataBits <= 63) 16 | } 17 | 18 | class PseudoStreamChannelIO(val params: PseudoStreamParams) extends Bundle { 19 | val txq = Decoupled(UInt(width = params.dataBits)) 20 | val rxq = Decoupled(UInt(width = params.dataBits)).flip 21 | } 22 | 23 | class PseudoStreamPortIO(val params: PseudoStreamParams) extends Bundle { 24 | val channel = Vec(params.nChannels, new PseudoStreamChannelIO(params)) 25 | } 26 | 27 | abstract class PseudoStream(busWidthBytes: Int, val params: PseudoStreamParams)(implicit p: Parameters) 28 | extends IORegisterRouter( 29 | RegisterRouterParams( 30 | name = "stream", 31 | compat = Seq("sifive,stream0"), 32 | base = params.address, 33 | size = 1 << log2Up(4096 * params.nChannels), 34 | beatBytes = busWidthBytes), 35 | new PseudoStreamPortIO(params)) { 36 | lazy val module = new LazyModuleImp(this) { 37 | 38 | val nbports = Wire(Vec(params.nChannels, new PseudoStreamChannelIO(params))) 39 | val bports = Wire(Vec(params.nChannels, new PseudoStreamChannelIO(params))) 40 | 41 | regmap( 42 | List.tabulate(params.nChannels)(idx => List( 43 | PseudoStreamCtrlRegs.txfifo + 4096 * idx -> NonBlockingEnqueue(nbports(idx).txq, 64), 44 | PseudoStreamCtrlRegs.rxfifo + 4096 * idx -> NonBlockingDequeue(nbports(idx).rxq, 64), 45 | PseudoStreamCtrlRegs.txfifob + 4096 * idx -> Seq( 46 | RegField.w(params.dataBits, bports(idx).txq, RegFieldDesc("txfifob", "blocking txfifo interface"))), 47 | PseudoStreamCtrlRegs.rxfifob + 4096 * idx -> Seq( 48 | RegField.r(params.dataBits, bports(idx).rxq, RegFieldDesc("rxfifob", "blocking rxfifo interface"))) 49 | )).flatten:_*) 50 | 51 | (nbports zip bports).zipWithIndex.map { case ((nb, b), idx) => 52 | val txq_arb = Module(new Arbiter(UInt(width = params.dataBits), 2)) 53 | txq_arb.io.in(0) <> nb.txq 54 | txq_arb.io.in(1) <> b.txq 55 | port.channel(idx).txq <> txq_arb.io.out 56 | 57 | nb.rxq.valid := port.channel(idx).rxq.valid 58 | nb.rxq.bits := port.channel(idx).rxq.bits 59 | b.rxq.valid := port.channel(idx).rxq.valid 60 | b.rxq.bits := port.channel(idx).rxq.bits 61 | port.channel(idx).rxq.ready := nb.rxq.ready || b.rxq.ready 62 | } 63 | }} 64 | 65 | class TLPseudoStream(busWidthBytes: Int, params: PseudoStreamParams)(implicit p: Parameters) 66 | extends PseudoStream(busWidthBytes, params) with HasTLControlRegMap 67 | 68 | case class PseudoStreamAttachParams( 69 | stream: PseudoStreamParams, 70 | controlBus: TLBusWrapper, 71 | controlXType: ClockCrossingType = NoCrossing, 72 | mclock: Option[ModuleValue[Clock]] = None, 73 | mreset: Option[ModuleValue[Bool]] = None) 74 | (implicit val p: Parameters) 75 | 76 | object PseudoStream { 77 | val nextId = { var i = -1; () => { i += 1; i} } 78 | 79 | def attach(params: PseudoStreamAttachParams): TLPseudoStream = { 80 | implicit val p = params.p 81 | val name = s"stream_${nextId()}" 82 | val cbus = params.controlBus 83 | val stream = LazyModule(new TLPseudoStream(cbus.beatBytes, params.stream)) 84 | stream.suggestName(name) 85 | 86 | cbus.coupleTo(s"slave_named_$name") { 87 | (stream.controlXing(params.controlXType) 88 | := TLFragmenter(cbus.beatBytes, cbus.blockBytes) 89 | := TLBuffer(BufferParams.flow) := _) 90 | } 91 | InModuleBody { stream.module.clock := params.mclock.map(_.getWrappedValue).getOrElse(cbus.module.clock) } 92 | InModuleBody { stream.module.reset := params.mreset.map(_.getWrappedValue).getOrElse(cbus.module.reset) } 93 | 94 | stream 95 | } 96 | 97 | def attachAndMakePort(params: PseudoStreamAttachParams): ModuleValue[PseudoStreamPortIO] = { 98 | val stream = attach(params) 99 | val streamNode = stream.ioNode.makeSink()(params.p) 100 | InModuleBody { streamNode.makeIO()(ValName(stream.name)) } 101 | } 102 | 103 | def tieoff(port: PseudoStreamPortIO) { 104 | port.channel.foreach { s => 105 | s.txq.ready := true.B 106 | s.rxq.valid := false.B 107 | } 108 | } 109 | 110 | def loopback(port: PseudoStreamPortIO, clock: Clock) { 111 | port.channel.foreach { s => 112 | val q = Module(new Queue(s.txq.bits, 2)) 113 | q.clock := clock 114 | q.io.enq <> s.txq 115 | s.rxq <> q.io.deq 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/stream/PseudoStreamRegs.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.stream 3 | 4 | object PseudoStreamCtrlRegs { 5 | val txfifo = 0x00 6 | val rxfifo = 0x08 7 | val txfifob = 0x10 8 | val rxfifob = 0x18 9 | } 10 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/uart/UARTCtrlRegs.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.uart 3 | 4 | object UARTCtrlRegs { 5 | val txfifo = 0x00 6 | val rxfifo = 0x04 7 | val txctrl = 0x08 8 | val txmark = 0x0a 9 | val rxctrl = 0x0c 10 | val rxmark = 0x0e 11 | 12 | val ie = 0x10 13 | val ip = 0x14 14 | val div = 0x18 15 | } 16 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/uart/UARTPeriphery.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.uart 3 | 4 | import freechips.rocketchip.config.Field 5 | import freechips.rocketchip.diplomacy._ 6 | import freechips.rocketchip.subsystem.{BaseSubsystem, PeripheryBusKey} 7 | 8 | case object PeripheryUARTKey extends Field[Seq[UARTParams]] 9 | 10 | trait HasPeripheryUART { this: BaseSubsystem => 11 | val uartNodes = p(PeripheryUARTKey).map { ps => 12 | val divinit = (p(PeripheryBusKey).frequency / 115200).toInt 13 | UART.attach(UARTAttachParams(ps, divinit, pbus, ibus.fromAsync)).ioNode.makeSink 14 | } 15 | } 16 | 17 | trait HasPeripheryUARTBundle { 18 | val uart: Seq[UARTPortIO] 19 | } 20 | 21 | 22 | trait HasPeripheryUARTModuleImp extends LazyModuleImp with HasPeripheryUARTBundle { 23 | val outer: HasPeripheryUART 24 | val uart = outer.uartNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"uart_$i")) } 25 | } 26 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/uart/UARTPins.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.uart 3 | 4 | import Chisel._ 5 | import chisel3.experimental.{withClockAndReset} 6 | import freechips.rocketchip.util.SyncResetSynchronizerShiftReg 7 | import sifive.blocks.devices.pinctrl.{Pin} 8 | 9 | class UARTSignals[T <: Data](private val pingen: () => T) extends Bundle { 10 | val rxd = pingen() 11 | val txd = pingen() 12 | } 13 | 14 | class UARTPins[T <: Pin](pingen: () => T) extends UARTSignals[T](pingen) 15 | 16 | object UARTPinsFromPort { 17 | def apply[T <: Pin](pins: UARTSignals[T], uart: UARTPortIO, clock: Clock, reset: Bool, syncStages: Int = 0) { 18 | withClockAndReset(clock, reset) { 19 | pins.txd.outputPin(uart.txd) 20 | val rxd_t = pins.rxd.inputPin() 21 | uart.rxd := SyncResetSynchronizerShiftReg(rxd_t, syncStages, init = Bool(true), name = Some("uart_rxd_sync")) 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/uart/UARTRx.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.uart 3 | 4 | import Chisel._ 5 | 6 | import freechips.rocketchip.util.Majority 7 | 8 | class UARTRx(c: UARTParams) extends Module { 9 | val io = new Bundle { 10 | val en = Bool(INPUT) 11 | val in = Bits(INPUT, 1) 12 | val out = Valid(Bits(width = c.dataBits)) 13 | val div = UInt(INPUT, c.divisorBits) 14 | } 15 | 16 | val debounce = Reg(init = UInt(0, 2)) 17 | val debounce_max = (debounce === UInt(3)) 18 | val debounce_min = (debounce === UInt(0)) 19 | 20 | val prescaler = Reg(UInt(width = c.divisorBits - c.oversample + 1)) 21 | val start = Wire(init = Bool(false)) 22 | val pulse = (prescaler === UInt(0)) 23 | 24 | private val dataCountBits = log2Floor(c.dataBits) + 1 25 | 26 | val data_count = Reg(UInt(width = dataCountBits)) 27 | val data_last = (data_count === UInt(0)) 28 | val sample_count = Reg(UInt(width = c.oversample)) 29 | val sample_mid = (sample_count === UInt((c.oversampleFactor - c.nSamples + 1) >> 1)) 30 | val sample_last = (sample_count === UInt(0)) 31 | val countdown = Cat(data_count, sample_count) - UInt(1) 32 | 33 | // Compensate for the divisor not being a multiple of the oversampling period. 34 | // Let remainder k = (io.div % c.oversampleFactor). 35 | // For the last k samples, extend the sampling delay by 1 cycle. 36 | val remainder = io.div(c.oversample-1, 0) 37 | val extend = (sample_count < remainder) // Pad head: (sample_count > ~remainder) 38 | val restore = start || pulse 39 | val prescaler_in = Mux(restore, io.div >> c.oversample, prescaler) 40 | val prescaler_next = prescaler_in - Mux(restore && extend, UInt(0), UInt(1)) 41 | 42 | val sample = Reg(Bits(width = c.nSamples)) 43 | val voter = Majority(sample.toBools.toSet) 44 | val shifter = Reg(Bits(width = c.dataBits)) 45 | 46 | val valid = Reg(init = Bool(false)) 47 | valid := Bool(false) 48 | io.out.valid := valid 49 | io.out.bits := shifter 50 | 51 | val (s_idle :: s_data :: Nil) = Enum(UInt(), 2) 52 | val state = Reg(init = s_idle) 53 | 54 | switch (state) { 55 | is (s_idle) { 56 | when (!(!io.in) && !debounce_min) { 57 | debounce := debounce - UInt(1) 58 | } 59 | when (!io.in) { 60 | debounce := debounce + UInt(1) 61 | when (debounce_max) { 62 | state := s_data 63 | start := Bool(true) 64 | prescaler := prescaler_next 65 | data_count := UInt(c.dataBits+1) 66 | sample_count := UInt(c.oversampleFactor - 1) 67 | } 68 | } 69 | } 70 | 71 | is (s_data) { 72 | prescaler := prescaler_next 73 | when (pulse) { 74 | sample := Cat(sample, io.in) 75 | data_count := countdown >> c.oversample 76 | sample_count := countdown(c.oversample-1, 0) 77 | 78 | when (sample_mid) { 79 | when (data_last) { 80 | state := s_idle 81 | valid := Bool(true) 82 | } .otherwise { 83 | shifter := Cat(voter, shifter >> 1) 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | when (!io.en) { 91 | debounce := UInt(0) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/devices/uart/UARTTx.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.devices.uart 3 | 4 | import Chisel._ 5 | 6 | import freechips.rocketchip.util.PlusArg 7 | 8 | class UARTTx(c: UARTParams) extends Module { 9 | val io = new Bundle { 10 | val en = Bool(INPUT) 11 | val in = Decoupled(Bits(width = c.dataBits)).flip 12 | val out = Bits(OUTPUT, 1) 13 | val div = UInt(INPUT, c.divisorBits) 14 | val nstop = UInt(INPUT, log2Up(c.stopBits)) 15 | } 16 | 17 | val prescaler = Reg(init = UInt(0, c.divisorBits)) 18 | val pulse = (prescaler === UInt(0)) 19 | 20 | private val n = c.dataBits + 1 21 | val counter = Reg(init = UInt(0, log2Floor(n + c.stopBits) + 1)) 22 | val shifter = Reg(Bits(width = n)) 23 | val out = Reg(init = Bits(1, 1)) 24 | io.out := out 25 | 26 | val plusarg_tx = PlusArg("uart_tx", 1, "Enable/disable the TX to speed up simulation").orR 27 | 28 | val busy = (counter =/= UInt(0)) 29 | io.in.ready := io.en && !busy 30 | when (io.in.fire()) { 31 | printf("UART TX (%x): %c\n", io.in.bits, io.in.bits) 32 | } 33 | when (io.in.fire() && plusarg_tx) { 34 | shifter := Cat(io.in.bits, Bits(0, 1)) 35 | counter := Mux1H((0 until c.stopBits).map(i => 36 | (io.nstop === UInt(i)) -> UInt(n + i + 1))) 37 | } 38 | when (busy) { 39 | prescaler := Mux(pulse, io.div, prescaler - UInt(1)) 40 | } 41 | when (pulse && busy) { 42 | counter := counter - UInt(1) 43 | shifter := Cat(Bits(1, 1), shifter >> 1) 44 | out := shifter(0) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.ip.xilinx.ibufds_gte2 3 | 4 | import Chisel._ 5 | 6 | //IP : xilinx unisim IBUFDS_GTE2 7 | //Differential Signaling Input Buffer 8 | //unparameterized 9 | 10 | class IBUFDS_GTE2 extends BlackBox { 11 | val io = new Bundle { 12 | val O = Bool(OUTPUT) 13 | val ODIV2 = Bool(OUTPUT) 14 | val CEB = Bool(INPUT) 15 | val I = Bool(INPUT) 16 | val IB = Bool(INPUT) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/util/DeglitchShiftRegister.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.util 3 | 4 | import Chisel._ 5 | 6 | //Allows us to specify a different clock for a shift register 7 | // and to force input to be high for > 1 cycle. 8 | class DeglitchShiftRegister(shift: Int) extends Module { 9 | val io = new Bundle { 10 | val d = Bool(INPUT) 11 | val q = Bool(OUTPUT) 12 | } 13 | val sync = ShiftRegister(io.d, shift) 14 | val last = ShiftRegister(sync, 1) 15 | io.q := sync & last 16 | } 17 | 18 | object DeglitchShiftRegister { 19 | def apply (shift: Int, d: Bool, clock: Clock, 20 | name: Option[String] = None): Bool = { 21 | val deglitch = Module (new DeglitchShiftRegister(shift)) 22 | name.foreach(deglitch.suggestName(_)) 23 | deglitch.clock := clock 24 | deglitch.reset := Bool(false) 25 | deglitch.io.d := d 26 | deglitch.io.q 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/util/RegMapFIFO.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.util 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.regmapper._ 6 | 7 | // MSB indicates full status 8 | object NonBlockingEnqueue { 9 | def apply(enq: DecoupledIO[UInt], regWidth: Int = 32): Seq[RegField] = { 10 | val enqWidth = enq.bits.getWidth 11 | val quash = Wire(Bool()) 12 | require(enqWidth > 0) 13 | require(regWidth > enqWidth) 14 | Seq( 15 | RegField(enqWidth, 16 | RegReadFn(UInt(0)), 17 | RegWriteFn((valid, data) => { 18 | enq.valid := valid && !quash 19 | enq.bits := data 20 | Bool(true) 21 | }), RegFieldDesc("data","Transmit data")), 22 | RegField(regWidth - enqWidth - 1), 23 | RegField(1, 24 | !enq.ready, 25 | RegWriteFn((valid, data) => { 26 | quash := valid && data(0) 27 | Bool(true) 28 | }), RegFieldDesc("full","Transmit FIFO full", volatile=true))) 29 | } 30 | } 31 | 32 | // MSB indicates empty status 33 | object NonBlockingDequeue { 34 | def apply(deq: DecoupledIO[UInt], regWidth: Int = 32): Seq[RegField] = { 35 | val deqWidth = deq.bits.getWidth 36 | require(deqWidth > 0) 37 | require(regWidth > deqWidth) 38 | Seq( 39 | RegField.r(deqWidth, 40 | RegReadFn(ready => { 41 | deq.ready := ready 42 | (Bool(true), deq.bits) 43 | }), RegFieldDesc("data","Receive data", volatile=true)), 44 | RegField(regWidth - deqWidth - 1), 45 | RegField.r(1, !deq.valid, 46 | RegFieldDesc("empty","Receive FIFO empty", volatile=true))) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/util/SRLatch.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.util 3 | 4 | import Chisel._ 5 | 6 | class SRLatch extends BlackBox { 7 | val io = new Bundle { 8 | val set = Bool(INPUT) 9 | val reset = Bool(INPUT) 10 | val q = Bool(OUTPUT) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sifive-blocks/src/main/scala/util/SlaveRegIF.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | package sifive.blocks.util 3 | 4 | import Chisel._ 5 | import freechips.rocketchip.regmapper._ 6 | 7 | class SlaveRegIF(private val w: Int) extends Bundle { 8 | val write = Valid(UInt(width = w)).flip 9 | val read = UInt(OUTPUT, w) 10 | 11 | def toRegField(desc: Option[RegFieldDesc] = None): RegField = { 12 | def writeFn(valid: Bool, data: UInt): Bool = { 13 | write.valid := valid 14 | write.bits := data 15 | Bool(true) 16 | } 17 | RegField(w, RegReadFn(read), RegWriteFn((v, d) => writeFn(v, d)), desc) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sifive-blocks/vsrc/SRLatch.v: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | module SRLatch ( 3 | input set, 4 | input reset, 5 | output q 6 | ); 7 | 8 | reg latch; 9 | 10 | // synopsys async_set_reset "set" 11 | // synopsys one_hot "set, reset" 12 | always @(set or reset) 13 | begin 14 | if (set) 15 | latch <= 1'b1; 16 | else if (reset) 17 | latch <= 1'b0; 18 | end 19 | 20 | assign q = latch; 21 | 22 | endmodule 23 | -------------------------------------------------------------------------------- /src/main/scala/x300artydevkit/Config.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Modifications copyright (C) 2018-2019 Hex-Five 3 | package hexfive.x300artydevkit 4 | 5 | import freechips.rocketchip.config._ 6 | import freechips.rocketchip.subsystem._ 7 | import freechips.rocketchip.devices.debug._ 8 | import freechips.rocketchip.devices.tilelink._ 9 | import freechips.rocketchip.diplomacy.{DTSModel, DTSTimebase} 10 | import freechips.rocketchip.system._ 11 | import freechips.rocketchip.tile._ 12 | 13 | import sifive.blocks.devices.mockaon._ 14 | import sifive.blocks.devices.gpio._ 15 | import sifive.blocks.devices.pwm._ 16 | import sifive.blocks.devices.spi._ 17 | import sifive.blocks.devices.uart._ 18 | import sifive.blocks.devices.i2c._ 19 | 20 | class WithITIMAddr(addr: BigInt) extends Config((site, here, up) => 21 | { 22 | case RocketTilesKey => up(RocketTilesKey, site) map { r => 23 | r.copy(icache = r.icache.map(_.copy(itimAddr = Some(addr)))) } 24 | }) 25 | 26 | class WithUserMode() extends Config((site, here, up) => 27 | { 28 | case RocketTilesKey => up(RocketTilesKey, site) map { r => 29 | r.copy(core = r.core.copy(useUser = true)) } 30 | }) 31 | 32 | class WithLocalInterrupts(nLocalInterrupts: Int) extends Config((site, here, up) => 33 | { 34 | case RocketTilesKey => up(RocketTilesKey, site) map { r => 35 | r.copy(core = r.core.copy(nLocalInterrupts = nLocalInterrupts)) } 36 | }) 37 | 38 | class WithPerfCounters(nPerfCounters: Int) extends Config((site, here, up) => 39 | { 40 | case RocketTilesKey => up(RocketTilesKey, site) map { r => 41 | r.copy(core = r.core.copy(nPerfCounters = nPerfCounters)) } 42 | }) 43 | 44 | class WithDTSTimebase(timebase: Int) extends Config((site, here, up) => 45 | { 46 | case DTSTimebase => BigInt(timebase) 47 | }) 48 | 49 | class WithMVendorID(mvendorid: Int) extends Config((site, here, up) => 50 | { 51 | case RocketTilesKey => up(RocketTilesKey, site) map { r => 52 | r.copy(core = r.core.copy(mvendorid = mvendorid)) } 53 | }) 54 | 55 | class WithJtagDTMConfig(idcodeVersion: Int, idcodePartNum: Int, idcodeManufId: Int, debugIdleCycles: Int) extends Config((site, here, up) => 56 | { 57 | case JtagDTMKey => new JtagDTMConfig ( 58 | idcodeVersion = idcodeVersion, 59 | idcodePartNum = idcodePartNum, 60 | idcodeManufId = idcodeManufId, 61 | debugIdleCycles = debugIdleCycles) 62 | }) 63 | 64 | // X300ArtyDevKit Peripherals 65 | class X300ArtyDevKitPeripherals extends Config((site, here, up) => { 66 | case PeripheryGPIOKey => List( 67 | GPIOParams(address = 0x10012000, width = 32, includeIOF = true)) 68 | case PeripheryPWMKey => List( 69 | PWMParams(address = 0x10015000, cmpWidth = 8), 70 | PWMParams(address = 0x10025000, cmpWidth = 16), 71 | PWMParams(address = 0x10035000, cmpWidth = 16)) 72 | case PeripherySPIKey => List( 73 | SPIParams(csWidth = 4, rAddress = 0x10024000, defaultSampleDel = 3), 74 | SPIParams(csWidth = 1, rAddress = 0x10034000, defaultSampleDel = 3)) 75 | case PeripherySPIFlashKey => List( 76 | SPIFlashParams( 77 | fAddress = 0x20000000, 78 | rAddress = 0x10014000, 79 | defaultSampleDel = 3)) 80 | case PeripheryUARTKey => List( 81 | UARTParams(address = 0x10013000), 82 | UARTParams(address = 0x10023000)) 83 | case PeripheryI2CKey => List( 84 | I2CParams(address = 0x10016000)) 85 | case PeripheryMockAONKey => 86 | MockAONParams(address = 0x10000000) 87 | case PeripheryMaskROMKey => List( 88 | MaskROMParams(address = 0x10000, name = "BootROM")) 89 | }) 90 | 91 | // X300ArtyDevKit Config 92 | class X300ArtyDevKitConfig extends Config( 93 | new X300ArtyDevKitPeripherals ++ 94 | new WithDTSTimebase(32768) ++ 95 | new WithJtagDTMConfig( 96 | idcodeVersion = 1, 97 | idcodePartNum = 0x300, 98 | idcodeManufId = 0x57C, 99 | debugIdleCycles = 5) ++ 100 | new WithMVendorID(0x57C) ++ 101 | new WithNBreakpoints(8) ++ 102 | new WithNExtTopInterrupts(0) ++ 103 | new WithJtagDTM ++ 104 | new WithNoMemPort ++ 105 | new WithNoMMIOPort ++ 106 | new WithNMemoryChannels(0) ++ 107 | new WithNBanks(0) ++ 108 | new WithL1ICacheWays(4) ++ 109 | new WithL1DCacheSets(1024) ++ 110 | new WithUserMode ++ 111 | new WithITIMAddr(0x08000000) ++ 112 | new WithLocalInterrupts(3) ++ 113 | new WithPerfCounters(2) ++ 114 | new With1TinyCore ++ 115 | new BaseConfig 116 | ) 117 | -------------------------------------------------------------------------------- /src/main/scala/x300artydevkit/System.scala: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | // Modifications copyright (C) 2018-2019 Hex-Five 3 | package hexfive.x300artydevkit 4 | 5 | import Chisel._ 6 | 7 | import freechips.rocketchip.config._ 8 | import freechips.rocketchip.subsystem._ 9 | import freechips.rocketchip.devices.debug._ 10 | import freechips.rocketchip.devices.tilelink._ 11 | import freechips.rocketchip.diplomacy._ 12 | import freechips.rocketchip.interrupts._ 13 | import freechips.rocketchip.system._ 14 | 15 | import sifive.blocks.devices.mockaon._ 16 | import sifive.blocks.devices.gpio._ 17 | import sifive.blocks.devices.pwm._ 18 | import sifive.blocks.devices.spi._ 19 | import sifive.blocks.devices.uart._ 20 | import sifive.blocks.devices.i2c._ 21 | 22 | import sifive.fpgashells.devices.xilinx.xilinxethernetlite._ 23 | 24 | //------------------------------------------------------------------------- 25 | // X300ArtyDevKitSystem 26 | //------------------------------------------------------------------------- 27 | 28 | class X300ArtyDevKitSystem(implicit p: Parameters) extends RocketSubsystem 29 | with HasPeripheryMaskROMSlave 30 | with HasPeripheryDebug 31 | with HasPeripheryMockAON 32 | with HasPeripheryUART 33 | with HasPeripherySPIFlash 34 | with HasPeripherySPI 35 | with HasPeripheryPWM 36 | with HasPeripheryI2C 37 | with HasSystemXilinxEthernetLite { 38 | override lazy val module = new X300ArtyDevKitSystemModule(this) 39 | 40 | val gpio = p(PeripheryGPIOKey).map { ps => 41 | GPIO.attach(GPIOAttachParams(ps, pbus, ibus.fromAsync)) 42 | } 43 | 44 | val gpioNodes = gpio.map { g => 45 | g.ioNode.makeSink 46 | } 47 | 48 | private val device = new Device with DeviceInterrupts { 49 | def describe(resources: ResourceBindings): Description = { 50 | Description("soc/local-interrupts", describeInterrupts(resources)) 51 | } 52 | } 53 | 54 | val clicInterrupts = IntSourceNode(IntSourcePortSimple(num = 3, resources = device.int)) 55 | 56 | val asyncXing = LazyModule(new IntXing(3)) 57 | asyncXing.intnode := clicInterrupts 58 | tiles(0).crossIntIn() := asyncXing.intnode 59 | } 60 | 61 | class X300ArtyDevKitSystemModule[+L <: X300ArtyDevKitSystem](_outer: L) 62 | extends RocketSubsystemModuleImp(_outer) 63 | with HasPeripheryDebugModuleImp 64 | with HasPeripheryUARTModuleImp 65 | with HasPeripherySPIModuleImp 66 | with HasPeripherySPIFlashModuleImp 67 | with HasPeripheryMockAONModuleImp 68 | with HasPeripheryPWMModuleImp 69 | with HasPeripheryI2CModuleImp 70 | with HasSystemXilinxEthernetLiteModuleImp { 71 | // Reset vector is set to the location of the mask rom 72 | val maskROMParams = p(PeripheryMaskROMKey) 73 | global_reset_vector := maskROMParams(0).address.U 74 | 75 | val gpio = outer.gpioNodes.zipWithIndex.map { case(n,i) => n.makeIO()(ValName(s"gpio_$i")) } 76 | 77 | outer.clicInterrupts.out.map(_._1).flatten.zipWithIndex.foreach { 78 | case(o, 0) => o := outer.gpio(0).module.lip(15) 79 | case(o, 1) => o := outer.gpio(0).module.lip(30) 80 | case(o, 2) => o := outer.gpio(0).module.lip(31) 81 | } 82 | } 83 | --------------------------------------------------------------------------------