From c5c272b90dbc3532de5429056cd43c30dc28c25c Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Fri, 19 Jun 2020 13:51:57 -0700 Subject: [PATCH 01/16] Custom core doc first draft --- docs/Customization/Custom-Core.rst | 286 ++++++++++++++++++++++ docs/Customization/Heterogeneous-SoCs.rst | 2 + docs/Customization/index.rst | 2 + 3 files changed, 290 insertions(+) create mode 100644 docs/Customization/Custom-Core.rst diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst new file mode 100644 index 00000000..bffdc236 --- /dev/null +++ b/docs/Customization/Custom-Core.rst @@ -0,0 +1,286 @@ +.. _custom_core: + +Adding a custom core +==================== + +You may want to add a custom RISC-V core to Chipyard generator. If the top module of your core is not in Chisel, +you will first need to create a Verilog blackbox for it. See ::ref:`_incorporating-verilog-blocks` for instructions. +Once you have a top module in Chisel, you are ready to create integrate it with Chipyard. + +.. note:: + + RoCC is not supported by custom core currently. Please use Rocket or Boom if you need to use RoCC. + +.. note:: + + Custom core doesn't support FireSim at this time. + +Parameter Case Classes +---------------------- + +Chipyard will generate a core for every ``TileParams`` object it discovered in the current config. +``TileParams`` is a trait containing the information needed to create a tile, and every custom core must implement +their own version of ``TileParams``, as well as ``CoreParams`` which is passed as a field in ``TileParams``. + +``TileParams`` holds the parameters that are the same for every generated core, while ``CoreParams`` contains those +that can vary from cores to cores. They must be implemented as case classes with fields that can be overridden by +other config fragments as the constructor parameters. See the appendix at the bottom of the page for a list of +variable to be implemented. You can also add custom fields to them, but standard fields should always be preferred. + +Now you have your parameter classes, you will need config keys to hold them. There are two required keys: + +.. code-block:: scala + + case object MyTilesKey extends Field[Seq[MyTileParams]](Nil) + case object MyCrossingKey extends Field[Seq[RocketCrossingParams]](List(RocketCrossingParams())) + +``MyCrossingKey`` here is used to store information about the clock-crossing behavior of the core, and it is normally +set to its default values. + +``TileParams`` and ``CoreParams`` contains the following fields: + +.. code-block:: scala + + trait TileParams { + val core: CoreParams // Core parameters (see below) + val icache: Option[ICacheParams] // Not used if you use your own I1 cache + val dcache: Option[DCacheParams] // Not used if you use your own D1 cache + val btb: Option[BTBParams] // Not used if you use your own BTB / branch predictor + val hartId: Int // Hart ID: Must be unique within a design config + val beuAddr: Option[BigInt] + val blockerCtrlAddr: Option[BigInt] + val name: Option[String] // Name of the core + } + + trait CoreParams { + val bootFreqHz: BigInt // Frequency + val useVM: Boolean // Support virtual memory + val useUser: Boolean // Support user mode + val useSupervisor: Boolean // Support supervisor mode + val useDebug: Boolean // Support RISC-V debug specs + val useAtomics: Boolean // Support A extension + val useAtomicsOnlyForIO: Boolean // Support A extension for memory-mapped IO (may be true even if useAtomics is false) + val useCompressed: Boolean // Support C extension + val useVector: Boolean = false // Support V extension + val useSCIE: Boolean + val useRVE: Boolean + val mulDiv: Option[MulDivParams] // M extension and related setting (Only used by Rocket core, simply use its default value) + val fpu: Option[FPUParams] // F and D extensions and related setting (see below) + val fetchWidth: Int // Max # of insts fetched every cycle + val decodeWidth: Int // Max # of insts decoded every cycle + val retireWidth: Int // Max # of insts retired every cycle + val instBits: Int // Instruction bits (if 32 bit and 64 bit are both supported, use 64) + val nLocalInterrupts: Int // # of local interrupts (see SiFive interrupt cookbook) + val nPMPs: Int // # of Physical Memory Protection units + val pmpGranularity: Int // Size of the smallest unit of region for PMP unit (must be power of 2) + val nBreakpoints: Int // # of breakpoints supported (in RISC-V debug specs) + val useBPWatch: Boolean + val nPerfCounters: Int // # of supported performance counters + val haveBasicCounters: Boolean // Support basic counters defined in the RISC-V counter extension + val haveFSDirty: Boolean + val misaWritable: Boolean // Support writable misa CSR (like variable instruction bits) + val haveCFlush: Boolean + val nL2TLBEntries: Int // # of L2 TLB entries + val mtvecInit: Option[BigInt] // mtvec CSR (of V extension) initial value + val mtvecWritable: Boolean // If mtvec CSR is writable + + // Normally, you don't need to change these values (except lrscCycles) + def customCSRs(implicit p: Parameters): CustomCSRs = new CustomCSRs + + def hasSupervisorMode: Boolean = useSupervisor || useVM + def instBytes: Int = instBits / 8 + def fetchBytes: Int = fetchWidth * instBytes + // This field is used only with the D1 cache of Rocket chip. Simply set it to the default value 80. + def lrscCycles: Int + + def dcacheReqTagBits: Int = 6 + + def minFLen: Int = 32 + def vLen: Int = 0 + def sLen: Int = 0 + def eLen(xLen: Int, fLen: Int): Int = xLen max fLen + def vMemDataBits: Int = 0 + } + + case class FPUParams( + minFLen: Int = 32, // Minimum floating point length (no need to change) + fLen: Int = 64, // Maximum floating point length, use 32 if only single precision is supported + divSqrt: Boolean = true, // Div/Sqrt operation supported + sfmaLatency: Int = 3, // + dfmaLatency: Int = 4 + ) + +Most of the fields here are originally designed for Rocket core and contains some architecture-specific details, but +many of them are general enough to be useful for other cores. It is strongly recommended to use these fields instead +of creating your own custom fields when applicable. + +Tile Class +---------- + +In Chipyard, all connections with other components on SoC are defined a core's `Tile` class, while the implementation +of the actual hardware are in the implementation class. This structure allows Chipyard to use the Diplomacy framework +to resolve paramters and connections before elaboration. + +All tile classes implement ``BaseTile`` and will normally implement ``SinksExternalInterrupts`` and ``SourcesExternalNotifications``, +which allow the tile to accept external interrupt. A typical tile has the following form: + +.. code-block:: scala + + class MyTile( + val myParams: MyTileParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters, + logicalTreeNode: LogicalTreeNode) + extends BaseTile(myParams, crossing, lookup, q) + with SinksExternalInterrupts + with SourcesExternalNotifications + { + + // Private constructor ensures altered LazyModule.p is used implicitly + def this(params: MyTileParams, crossing: RocketCrossingParams, lookup: LookupByHartIdImpl, logicalTreeNode: LogicalTreeNode)(implicit p: Parameters) = + this(params, crossing.crossingType, lookup, p, logicalTreeNode) + + // Require TileLink nodes + val intOutwardNode = IntIdentityNode() + val masterNode = visibilityNode + val slaveNode = TLIdentityNode() + + // Implementation class (See below) + override lazy val module = new MyTileModuleImp(this) + + // Required entry of CPU device in the device tree for interrupt purpose + val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("my-organization,my-cpu", "riscv")) { + override def parent = Some(ResourceAnchors.cpus) + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping ++ + cpuProperties ++ + nextLevelCacheProperty ++ + tileProperties) + } + } + + ResourceBinding { + Resource(cpuDevice, "reg").bind(ResourceAddress(hartId)) + } + + // (Connection to bus, interrupt, etc.) + } + +TileLink Connection +------------------- + +Chipyard use TileLink as its onboard bus protocol, and if your core doesn't use TileLink, you will need to convert them +in the tile class. Below is an example of how to connect a core using AXI4 to the TileLink bus: + +.. code-block:: scala + + val memoryTap = TLIdentityNode() // Every bus connection should have their own tap node + (tlMasterXbar.node // tlMasterXbar is the bus crossbar to be used when this core / tile is acting as a master; otherwise, use tlSlaveXBar + := memoryTap + := TLBuffer() + := TLFIFOFixer(TLFIFOFixer.all) // fix FIFO ordering + := TLWidthWidget(beatBytes) // reduce size of TL + := AXI4ToTL() // convert to TL + := AXI4UserYanker(Some(2)) // remove user field on AXI interface. need but in reality user intf. not needed + := AXI4Fragmenter() // deal with multi-beat xacts + := memAXI4Node) // The custom node, see below + +Remember, you may not need all of these intermediate widgets. See :::ref:`Diplomatic-Widgets` for the meaning of each intermediate +widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Also, Chipyard +support AHB, APB and AXIS, and most of the AXI4 widgets has equivalent widget for these bus protocol. See the reference page for +more info. + +``memAXI4Node`` is an AXI4 master node and is defined as following in our example: + +.. code-block:: scala + + val memAXI4Node = AXI4MasterNode( + Seq(AXI4MasterPortParameters( + masters = Seq(AXI4MasterParameters( + name = portName, + id = IdRange(0, 1 << idBits)))))) + +where ``portName`` and ``idBits`` are the parameter provides by the tile. Make sure to read :::ref:`node-tyoes` to check out what +type of nodes Chipyard supports and their parameters! + +Also, by default, there are boundary buffers for both master and slave connections to the bus when they are leaving the tile, and you +can override the following two functions to control how to buffer the bus requests/responses: + +.. code-block:: scala + + protected def makeMasterBoundaryBuffers(implicit p: Parameters): TLBuffer + protected def makeSlaveBoundaryBuffers(implicit p: Parameters): TLBuffer + +Interrupt +--------- + +Chipyard allows a tile to either receive interrupts from other devices or initiate interrupts to notify other cores/devices. +In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and +call ``decodeCoreInterrupts`` with the object as the argument. You can then read the interrupt bits from the object. +The definition of ``TileInterrupts`` is + +.. code-block:: scala + + class TileInterrupts(implicit p: Parameters) extends CoreBundle()(p) { + val debug = Bool() // debug interrupt + val mtip = Bool() // Machine level timer interrupt + val msip = Bool() // Machine level software interrupt + val meip = Bool() // Machine level external interrupt + val seip = usingSupervisor.option(Bool()) // Valid only if supervisor mode is supported + val lip = Vec(coreParams.nLocalInterrupts, Bool()) // Local interrupts + } + +This function should be in the implementation class since it involves hardware generation. +Also, the tile can also notify other cores or devices for some events by calling following functions (in implementation class): + +.. code-block:: scala + + def reportHalt(could_halt: Option[Bool]) // Triggered when there is an unrecoverable hardware error (halt the machine) + def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (used only by cache when there's an ECC error) + reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) + reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruciton is executed + +Implementation Class +-------------------- + +The implementation class is of the following form: + +.. code-block:: scala + + class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ + // annotate the parameters + Annotated.params(this, outer.tileParams) + + // TODO: Create the top module of the core and connect it with the ports in "outer" + } + +In the body of this class, you can look up any parameters by calling ``p({key})``, where ``{key}`` is the config key of +the value you want to look up. For a list of available keys, see the appendix below. + +If you create an AXI4 node (or equivalents), you will need to connect them to your core. + +Integrate the Core +------------------ + +To use your core in a set of config, you would need a config fragment that would create a ``TileParams`` object of your core in +the current config. An example of such config will be like this: + +.. code-block:: scala + + class WithNMyCores(n: Int) extends Config( + new RegisterCore(new CoreEntry[MyTileParams, MyTile]("MyCore", MyTilesKey, MyCrossingKey)) ++ + new Config((site, here, up) => { + case MyTilesKey => { + List.tabulate(n)(i => MyTileParams(hartId = i)) + } + }) + ) + +Where ``RegisterCore`` will register the core with chipyard so that it can be recognized by generic config. This is required for +all custom cores. You can also create other config fragments to change other parameters. + +Now you have finished all the steps to prepare your cores for Chipyard! To generate the custom core, simply follow the instructions +in :::ref:`_custom_chisel` to add your project to the build system, then create a config by following the steps in :::ref:`_hetero_socs_`. +You can now run any desired workflow for the new config just as you do for the built-in cores. diff --git a/docs/Customization/Heterogeneous-SoCs.rst b/docs/Customization/Heterogeneous-SoCs.rst index c640e31c..204b3159 100644 --- a/docs/Customization/Heterogeneous-SoCs.rst +++ b/docs/Customization/Heterogeneous-SoCs.rst @@ -1,3 +1,5 @@ +.. _hetero_socs_: + Heterogeneous SoCs =============================== diff --git a/docs/Customization/index.rst b/docs/Customization/index.rst index 9421b79a..38fdf622 100644 --- a/docs/Customization/index.rst +++ b/docs/Customization/index.rst @@ -7,6 +7,8 @@ These guides will walk you through customization of your system-on-chip: - How to include your custom Chisel sources in the Chipyard build system +- Adding custom core + - Adding custom RoCC accelerators to an existing Chipyard core (BOOM or Rocket) - Adding custom MMIO widgets to the Chipyard memory system by Tilelink or AXI4, with custom Top-level IOs From c407e39cd8c9a4a8dd2e2f19138174cc4cd6dffb Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sat, 20 Jun 2020 01:04:56 -0700 Subject: [PATCH 02/16] Completed most documentation (without AXI4 bus) --- docs/Customization/Custom-Core.rst | 78 ++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index bffdc236..5a5cc627 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -11,10 +11,6 @@ Once you have a top module in Chisel, you are ready to create integrate it with RoCC is not supported by custom core currently. Please use Rocket or Boom if you need to use RoCC. -.. note:: - - Custom core doesn't support FireSim at this time. - Parameter Case Classes ---------------------- @@ -37,18 +33,19 @@ Now you have your parameter classes, you will need config keys to hold them. The ``MyCrossingKey`` here is used to store information about the clock-crossing behavior of the core, and it is normally set to its default values. -``TileParams`` and ``CoreParams`` contains the following fields: +``TileParams`` and ``CoreParams`` contains the following fields (you may ignore any fields marked "Rocket specific" and +use their default values, although it is recommended to use them if you need a custom field with similar purposes) : .. code-block:: scala trait TileParams { val core: CoreParams // Core parameters (see below) - val icache: Option[ICacheParams] // Not used if you use your own I1 cache - val dcache: Option[DCacheParams] // Not used if you use your own D1 cache - val btb: Option[BTBParams] // Not used if you use your own BTB / branch predictor + val icache: Option[ICacheParams] // Rocket specific: I1 cache option + val dcache: Option[DCacheParams] // Rocket specific: D1 cache option + val btb: Option[BTBParams] // Rocket specific: BTB / branch predictor option val hartId: Int // Hart ID: Must be unique within a design config - val beuAddr: Option[BigInt] - val blockerCtrlAddr: Option[BigInt] + val beuAddr: Option[BigInt] // Rocket specific: Bus Error Unit for Rocket Core + val blockerCtrlAddr: Option[BigInt] // Rocket specific: Bus Blocker for Rocket Core val name: Option[String] // Name of the core } @@ -62,9 +59,9 @@ set to its default values. val useAtomicsOnlyForIO: Boolean // Support A extension for memory-mapped IO (may be true even if useAtomics is false) val useCompressed: Boolean // Support C extension val useVector: Boolean = false // Support V extension - val useSCIE: Boolean - val useRVE: Boolean - val mulDiv: Option[MulDivParams] // M extension and related setting (Only used by Rocket core, simply use its default value) + val useSCIE: Boolean // Support custom instructions (in custom-0 and custom-1) + val useRVE: Boolean // Use E base ISA + val mulDiv: Option[MulDivParams] // *Rocket specific: M extension related setting (Use Some(MulDivParams()) to indicate M extension supported) val fpu: Option[FPUParams] // F and D extensions and related setting (see below) val fetchWidth: Int // Max # of insts fetched every cycle val decodeWidth: Int // Max # of insts decoded every cycle @@ -73,13 +70,13 @@ set to its default values. val nLocalInterrupts: Int // # of local interrupts (see SiFive interrupt cookbook) val nPMPs: Int // # of Physical Memory Protection units val pmpGranularity: Int // Size of the smallest unit of region for PMP unit (must be power of 2) - val nBreakpoints: Int // # of breakpoints supported (in RISC-V debug specs) - val useBPWatch: Boolean + val nBreakpoints: Int // # of hardware breakpoints supported (in RISC-V debug specs) + val useBPWatch: Boolean // Support hardware breakpoints val nPerfCounters: Int // # of supported performance counters val haveBasicCounters: Boolean // Support basic counters defined in the RISC-V counter extension - val haveFSDirty: Boolean + val haveFSDirty: Boolean // If true, the core will set FS field in mstatus CSR to dirty when appropriate val misaWritable: Boolean // Support writable misa CSR (like variable instruction bits) - val haveCFlush: Boolean + val haveCFlush: Boolean // Rocket specific: enables Rocket's custom instruction extension to flush the cache val nL2TLBEntries: Int // # of L2 TLB entries val mtvecInit: Option[BigInt] // mtvec CSR (of V extension) initial value val mtvecWritable: Boolean // If mtvec CSR is writable @@ -90,7 +87,7 @@ set to its default values. def hasSupervisorMode: Boolean = useSupervisor || useVM def instBytes: Int = instBits / 8 def fetchBytes: Int = fetchWidth * instBytes - // This field is used only with the D1 cache of Rocket chip. Simply set it to the default value 80. + // Rocket specific: Longest possible latency of Rocket core D1 cache. Simply set it to the default value 80. def lrscCycles: Int def dcacheReqTagBits: Int = 6 @@ -106,8 +103,8 @@ set to its default values. minFLen: Int = 32, // Minimum floating point length (no need to change) fLen: Int = 64, // Maximum floating point length, use 32 if only single precision is supported divSqrt: Boolean = true, // Div/Sqrt operation supported - sfmaLatency: Int = 3, // - dfmaLatency: Int = 4 + sfmaLatency: Int = 3, // Rocket specific: Fused multiply-add pipeline latency (single precision) + dfmaLatency: Int = 4 // Rocket specific: Fused multiply-add pipeline latency (double precision) ) Most of the fields here are originally designed for Rocket core and contains some architecture-specific details, but @@ -213,6 +210,8 @@ can override the following two functions to control how to buffer the bus reques protected def makeMasterBoundaryBuffers(implicit p: Parameters): TLBuffer protected def makeSlaveBoundaryBuffers(implicit p: Parameters): TLBuffer +You can find more information on ``TLBuffer`` in :::ref:`Diplomatic-Widgets`. + Interrupt --------- @@ -240,7 +239,40 @@ Also, the tile can also notify other cores or devices for some events by calling def reportHalt(could_halt: Option[Bool]) // Triggered when there is an unrecoverable hardware error (halt the machine) def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (used only by cache when there's an ECC error) reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) - reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruciton is executed + reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed + +Trace (Optional) +---------------- + +Chipyard provides a set of ports for instruction trace that conforms with related RISC-V standard. +If you are using FireSim, it is recommended to implement these trace ports to enable FireSim to read trace. + +There are one inbound node ``traceAuxSinkNode.bundle: TraceAux`` and two outbound nodes ``traceCoreSourceNode.bundle: TraceCoreInterface`` +and ``bpwatchSourceNode.bundle: Vec[BPWatch]``. Note that the length of ``bpwatchSourceNode`` is equal to the max number of +breakpoints (set by ``nBreakpoints`` in ``CoreParams``). Below is the definition of these types: + +.. code-block:: scala + + // Control signal from the external tracer + class TraceAux extends Bundle { + val enable = Bool() // Enable trace output + val stall = Bool() // If true, the core should stall + } + // Check RISC-V Processor Trace spec V1.0 for more information of this interface + class TraceCoreInterface (val params: TraceCoreParams) extends Bundle { + val group = Vec(params.nGroups, new TraceCoreGroup(params)) + val priv = UInt(4.W) + val tval = UInt(params.xlen.W) + val cause = UInt(params.xlen.W) + } + // Address Breakpoint and watchpoint info (n is the retire width) + class BPWatch (val n: Int) extends Bundle() { + val valid = Vec(n, Bool()) // Valid bit of the output + val rvalid = Vec(n, Bool()) // Break on read + val wvalid = Vec(n, Bool()) // Break on write + val ivalid = Vec(n, Bool()) // Break on execute + val action = UInt(3.W) // Exception code (3 usually) + } Implementation Class -------------------- @@ -261,6 +293,10 @@ the value you want to look up. For a list of available keys, see the appendix be If you create an AXI4 node (or equivalents), you will need to connect them to your core. +.. warning:: + + TODO: Documenting bus connection + Integrate the Core ------------------ From 34bc8da0024df1957d7a760d0ac23ed0d8a31874 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Mon, 22 Jun 2020 17:57:17 -0700 Subject: [PATCH 03/16] Add a list of common config keys --- docs/Customization/Custom-Core.rst | 98 ++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 5a5cc627..6d0a9956 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -199,8 +199,8 @@ more info. name = portName, id = IdRange(0, 1 << idBits)))))) -where ``portName`` and ``idBits`` are the parameter provides by the tile. Make sure to read :::ref:`node-tyoes` to check out what -type of nodes Chipyard supports and their parameters! +where ``portName`` and ``idBits`` (number of bits to represent a port ID) are the parameter provides by the tile. +Make sure to read :::ref:`node-tyoes` to check out what type of nodes Chipyard supports and their parameters! Also, by default, there are boundary buffers for both master and slave connections to the bus when they are leaving the tile, and you can override the following two functions to control how to buffer the bus requests/responses: @@ -291,11 +291,19 @@ The implementation class is of the following form: In the body of this class, you can look up any parameters by calling ``p({key})``, where ``{key}`` is the config key of the value you want to look up. For a list of available keys, see the appendix below. -If you create an AXI4 node (or equivalents), you will need to connect them to your core. +If you create an AXI4 node (or equivalents), you will need to connect them to your core. You can connect a port like this: -.. warning:: +.. code-block:: scala - TODO: Documenting bus connection + outer.myAXI4Node.out foreach { case (out, edgeOut) => + // Connect your module IO port to "out" + // The type of "out" here is AXI4Bundle, which is defined in generators/rocket-chip/src/main/scala/amba/axi4/Bundles.scala + // Please refer to this file for the definition of the ports. + // If you are using APB, check APBBundle in generators/rocket-chip/src/main/scala/amba/apb/Bundles.scala + // If you are using AHB, check AHBSlaveBundle or AHBMasterBundle in generators/rocket-chip/src/main/scala/amba/ahb/Bundles.scala + // (choose one depends on the type of AHB node you create) + // If you are using AXIS, check AXISBundle and AXISBundleBits in generators/rocket-chip/src/main/scala/amba/axis/Bundles.scala + } Integrate the Core ------------------ @@ -320,3 +328,83 @@ all custom cores. You can also create other config fragments to change other par Now you have finished all the steps to prepare your cores for Chipyard! To generate the custom core, simply follow the instructions in :::ref:`_custom_chisel` to add your project to the build system, then create a config by following the steps in :::ref:`_hetero_socs_`. You can now run any desired workflow for the new config just as you do for the built-in cores. + +Appendix: Common Config Keys +---------------------------- + +Chipyard provide a set of keys to store standard parameters. Below are some of the most common key used in core integration. +(Note that internal fields are hidden) + +.. code-block:: scala + + // keys + // Parameters exposed to the top-level design, set based on external requirements, etc. See RISC-V debug specs for more info. + case object DebugModuleKey extends Field[Option[DebugModuleParams]](Some(DebugModuleParams())) + case object BootROMParams extends Field[BootROMParams] // See chipyard boot process tutorial + case object CLINTKey extends Field[Option[CLINTParams]](None) // Core Local Interrupter setting (See SiFive Interrupt Cookbook) + case object PLICKey extends Field[Option[PLICParams]](None) // Platform Level Interrupt Controller setting (See SiFive Interrupt Cookbook) + case object CacheBlockBytes extends Field[Int](64) // # of bytes in a cache block + case object BroadcastKey extends Field(BroadcastParams()) // L2 Cache broadcast setting + case object BankedL2Key extends Field(BankedL2Params()) // L2 Cache memory setting + case object PgLevels extends Field[Int](2) // Page Level of virtual memory + case object ASIdBits extends Field[Int](0) // Max # of bits for Address Space Identifer (See specs) + case object ExtMem extends Field[Option[MemoryPortParams]](None) // External DRAM setting + case object ExtBus extends Field[Option[MasterPortParams]](None) // External (off-chip) output bus setting + case object ExtIn extends Field[Option[SlavePortParams]](None) // External (off-chip) input bus setting + case object MaxHartIdBits extends Field[Int] // Max # of bits used to represent a Hart ID + case object XLen extends Field[Int] // Instruction bits (32 or 64) + case object BuildRoCC extends Field[Seq[Parameters => LazyRoCC]](Nil) // See custom ROCC tutorial + + // Values + case class DebugModuleParams ( + nDMIAddrSize : Int = 7, // Size of the Debug Bus Address + nProgramBufferWords: Int = 16, // Number of 32-bit words for Program Buffer + nAbstractDataWords : Int = 4, // Number of 32-bit words for Abstract Commands + nScratch : Int = 1, // Number of scratch memories used + hasBusMaster : Boolean = false, // Whether or not a bus master should be included + clockGate : Boolean = true, // Use clock gating + maxSupportedSBAccess : Int = 32, // Maximum transaction size supported by System Bus Access logic. + supportQuickAccess : Boolean = false, // Whether or not to support the quick access command. + supportHartArray : Boolean = true, // Whether or not to implement the hart array register (if >1 hart). + nHaltGroups : Int = 1, // Number of halt groups (group of harts that are halted together) + nExtTriggers : Int = 0, // Number of extra triggers + hasHartResets : Boolean = false, // Whether harts can be reseted with debugging system + hasImplicitEbreak : Boolean = false, // There is an additional RO program buffer word containing an ebreak + hasAuthentication : Boolean = false, // Has authentication (to prevent unauthorized users to use debugging system) + crossingHasSafeReset : Boolean = true // Include "safe" logic in Async Crossings so that only one side needs to be reset. + ) + case class CLINTParams( + baseAddress: BigInt = 0x02000000, // Default interrupt handler base address for CLINT + intStages: Int = 0 // # of cycles (stages) interrupts are delayed + ) + case class PLICParams( + baseAddress: BigInt = 0xC000000, // Default interrupt handler base address for PLIC + maxPriorities: Int = 7, // Maximum allowed interrupt priority (cannot be over 7) + intStages: Int = 0, // # of cycles (stages) interrupts are delayed + maxHarts: Int = PLICConsts.maxMaxHarts // Maximum number or hart / core connected to it + ) + case class BroadcastParams( + nTrackers: Int = 4, // # of broadcast tracker + bufferless: Boolean = false // Bufferless broadcast + ) + case class BankedL2Params( + nBanks: Int = 1 // Number of banks in L2 cache + ) + case class MasterPortParams( + base: BigInt, // Base memory address for this port + size: BigInt, // Size of this external memory + beatBytes: Int, // Interface width in bytes + idBits: Int, // # of bits in the port ID + maxXferBytes: Int = 256, // Maximum bytes in one transfer transaction + executable: Boolean = true // If the data from this port can be executed as instruciton + ) + /** Specifies the width of external slave ports */ + case class SlavePortParams( + beatBytes: Int, // Interface width in bytes + idBits: Int, // # of bits in the port ID + sourceBits: Int // # of bits in the source address + ) + case class MemoryPortParams( + master: MasterPortParams, // The memory port setting + nMemoryChannels: Int // Number of memory channel + ) From 7b5f474b041a30ff06eb29ee8dbf6914277ba171 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 28 Jun 2020 21:26:50 -0700 Subject: [PATCH 04/16] Finished Custom Core Docs --- docs/Customization/Custom-Core.rst | 26 +++++++------------ docs/Customization/index.rst | 1 + .../NodeTypes.rst | 2 ++ docs/TileLink-Diplomacy-Reference/Widgets.rst | 2 ++ 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 6d0a9956..e2723336 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -4,7 +4,7 @@ Adding a custom core ==================== You may want to add a custom RISC-V core to Chipyard generator. If the top module of your core is not in Chisel, -you will first need to create a Verilog blackbox for it. See ::ref:`_incorporating-verilog-blocks` for instructions. +you will first need to create a Verilog blackbox for it. See :ref:`incorporating-verilog-blocks` for instructions. Once you have a top module in Chisel, you are ready to create integrate it with Chipyard. .. note:: @@ -184,7 +184,7 @@ in the tile class. Below is an example of how to connect a core using AXI4 to th := AXI4Fragmenter() // deal with multi-beat xacts := memAXI4Node) // The custom node, see below -Remember, you may not need all of these intermediate widgets. See :::ref:`Diplomatic-Widgets` for the meaning of each intermediate +Remember, you may not need all of these intermediate widgets. See :ref:`diplomatic_widgets` for the meaning of each intermediate widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Also, Chipyard support AHB, APB and AXIS, and most of the AXI4 widgets has equivalent widget for these bus protocol. See the reference page for more info. @@ -200,7 +200,7 @@ more info. id = IdRange(0, 1 << idBits)))))) where ``portName`` and ``idBits`` (number of bits to represent a port ID) are the parameter provides by the tile. -Make sure to read :::ref:`node-tyoes` to check out what type of nodes Chipyard supports and their parameters! +Make sure to read :ref:`node_types` to check out what type of nodes Chipyard supports and their parameters! Also, by default, there are boundary buffers for both master and slave connections to the bus when they are leaving the tile, and you can override the following two functions to control how to buffer the bus requests/responses: @@ -210,7 +210,7 @@ can override the following two functions to control how to buffer the bus reques protected def makeMasterBoundaryBuffers(implicit p: Parameters): TLBuffer protected def makeSlaveBoundaryBuffers(implicit p: Parameters): TLBuffer -You can find more information on ``TLBuffer`` in :::ref:`Diplomatic-Widgets`. +You can find more information on ``TLBuffer`` in :ref:`diplomatic_widgets`. Interrupt --------- @@ -313,20 +313,14 @@ the current config. An example of such config will be like this: .. code-block:: scala - class WithNMyCores(n: Int) extends Config( - new RegisterCore(new CoreEntry[MyTileParams, MyTile]("MyCore", MyTilesKey, MyCrossingKey)) ++ - new Config((site, here, up) => { - case MyTilesKey => { - List.tabulate(n)(i => MyTileParams(hartId = i)) - } - }) - ) - -Where ``RegisterCore`` will register the core with chipyard so that it can be recognized by generic config. This is required for -all custom cores. You can also create other config fragments to change other parameters. + class WithNMyCores(n: Int) extends Config((site, here, up) => { + case MyTilesKey => { + List.tabulate(n)(i => MyTileParams(hartId = i)) + } + }) Now you have finished all the steps to prepare your cores for Chipyard! To generate the custom core, simply follow the instructions -in :::ref:`_custom_chisel` to add your project to the build system, then create a config by following the steps in :::ref:`_hetero_socs_`. +in :ref:`custom_chisel` to add your project to the build system, then create a config by following the steps in :ref:`hetero_socs_`. You can now run any desired workflow for the new config just as you do for the built-in cores. Appendix: Common Config Keys diff --git a/docs/Customization/index.rst b/docs/Customization/index.rst index 38fdf622..a7b571b6 100644 --- a/docs/Customization/index.rst +++ b/docs/Customization/index.rst @@ -37,6 +37,7 @@ We recommend reading all these pages in order. Hit next to get started! Heterogeneous-SoCs Custom-Chisel + Custom-Core RoCC-or-MMIO RoCC-Accelerators MMIO-Peripherals diff --git a/docs/TileLink-Diplomacy-Reference/NodeTypes.rst b/docs/TileLink-Diplomacy-Reference/NodeTypes.rst index ddb53c9f..32953944 100644 --- a/docs/TileLink-Diplomacy-Reference/NodeTypes.rst +++ b/docs/TileLink-Diplomacy-Reference/NodeTypes.rst @@ -1,3 +1,5 @@ +.. _node_types: + TileLink Node Types =================== diff --git a/docs/TileLink-Diplomacy-Reference/Widgets.rst b/docs/TileLink-Diplomacy-Reference/Widgets.rst index 7eba871b..791c1b9b 100644 --- a/docs/TileLink-Diplomacy-Reference/Widgets.rst +++ b/docs/TileLink-Diplomacy-Reference/Widgets.rst @@ -1,3 +1,5 @@ +.. _diplomatic_widgets: + Diplomatic Widgets ================== From 104c350a59af995d0d36affc5f1203582be838ca Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Thu, 2 Jul 2020 15:56:15 -0700 Subject: [PATCH 05/16] Custom Core Integration Doc, 1st Revision --- docs/Customization/Custom-Core.rst | 113 +++++++++----------- docs/TileLink-Diplomacy-Reference/index.rst | 2 + 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index e2723336..dcf2c257 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -7,34 +7,32 @@ You may want to add a custom RISC-V core to Chipyard generator. If the top modul you will first need to create a Verilog blackbox for it. See :ref:`incorporating-verilog-blocks` for instructions. Once you have a top module in Chisel, you are ready to create integrate it with Chipyard. +``generators/ariane/src/main/scala/ArianeTile.scala`` and ``generators/boom/src/main/scala/common/tile.scala`` +provide two examples of how to integrate a core. + .. note:: - RoCC is not supported by custom core currently. Please use Rocket or Boom if you need to use RoCC. + RoCC is not supported by custom core currently. Please use Rocket or Boom as the RoCC base core if you need to use RoCC. Parameter Case Classes ---------------------- -Chipyard will generate a core for every ``TileParams`` object it discovered in the current config. -``TileParams`` is a trait containing the information needed to create a tile, and every custom core must implement -their own version of ``TileParams``, as well as ``CoreParams`` which is passed as a field in ``TileParams``. +Chipyard will generate a core for every ``InstantiableTileParams`` object it discovered in the current config. +This object is derived from``TileParams``, a trait containing the information needed to create a tile. All cores must have +their own implementation of ``InstantiableTileParams``, as well as ``CoreParams`` which is passed as a field in ``TileParams``. -``TileParams`` holds the parameters that are the same for every generated core, while ``CoreParams`` contains those -that can vary from cores to cores. They must be implemented as case classes with fields that can be overridden by +``TileParams`` holds the parameters for the tile, which are the same for every generated core, while ``CoreParams`` +contains the parameters for individual cores. They must be implemented as case classes with fields that can be overridden by other config fragments as the constructor parameters. See the appendix at the bottom of the page for a list of variable to be implemented. You can also add custom fields to them, but standard fields should always be preferred. -Now you have your parameter classes, you will need config keys to hold them. There are two required keys: +``InstantiableTileParams[TileType]`` holds the constructor of ``TileType`` on top of the fields of ``TileParams``. +All custom cores will also need to implement ``instantiate()`` in their tile parameter class to return a new instance +of the tile class ``TileType``. -.. code-block:: scala - - case object MyTilesKey extends Field[Seq[MyTileParams]](Nil) - case object MyCrossingKey extends Field[Seq[RocketCrossingParams]](List(RocketCrossingParams())) - -``MyCrossingKey`` here is used to store information about the clock-crossing behavior of the core, and it is normally -set to its default values. - -``TileParams`` and ``CoreParams`` contains the following fields (you may ignore any fields marked "Rocket specific" and -use their default values, although it is recommended to use them if you need a custom field with similar purposes) : +``TileParams``, ``InstantiableTileParams[TileType]`` and ``CoreParams`` contains the following fields (you may ignore +any fields marked "Rocket specific" and use their default values, although it is recommended to use them if you +need a custom field with similar purposes): .. code-block:: scala @@ -49,6 +47,11 @@ use their default values, although it is recommended to use them if you need a c val name: Option[String] // Name of the core } + abstract class InstantiableTileParams[TileType <: BaseTile] extends TileParams { + def instantiate(crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl) + (implicit p: Parameters): TileType + } + trait CoreParams { val bootFreqHz: BigInt // Frequency val useVM: Boolean // Support virtual memory @@ -87,7 +90,7 @@ use their default values, although it is recommended to use them if you need a c def hasSupervisorMode: Boolean = useSupervisor || useVM def instBytes: Int = instBits / 8 def fetchBytes: Int = fetchWidth * instBytes - // Rocket specific: Longest possible latency of Rocket core D1 cache. Simply set it to the default value 80. + // Rocket specific: Longest possible latency of Rocket core D1 cache. Simply set it to the default value 80 if you don't use it. def lrscCycles: Int def dcacheReqTagBits: Int = 6 @@ -109,14 +112,23 @@ use their default values, although it is recommended to use them if you need a c Most of the fields here are originally designed for Rocket core and contains some architecture-specific details, but many of them are general enough to be useful for other cores. It is strongly recommended to use these fields instead -of creating your own custom fields when applicable. +of creating your own custom fields when applicable. + +.. note:: + + Implementations may choose to ignore some fields here or use them in a non-standard way, but using an inaccurate + value may break Chipyard components that rely on them (e.g. inaccurate indication of supported ISA extension will + result in incorrect test suite being generated) as well as any custom module that use them. ALWAYS document any + fields you ignore or with altered usage in your core implementation, and if you are implementing other devices that + would look up these config values, also document them. "Rocket specific" values are generally safe to ignore, but + you should document them if you use them. Tile Class ---------- -In Chipyard, all connections with other components on SoC are defined a core's `Tile` class, while the implementation -of the actual hardware are in the implementation class. This structure allows Chipyard to use the Diplomacy framework -to resolve paramters and connections before elaboration. +In Chipyard, all Tiles are diplomatically instantiated. In the first phase, diplomatic nodes which specify Tile-to-System +interconnects are evaluated, while in the second "Module Implementation" phase, hardware is elaborated. +See :ref:`tilelink_and_diplomacy` for more details. All tile classes implement ``BaseTile`` and will normally implement ``SinksExternalInterrupts`` and ``SourcesExternalNotifications``, which allow the tile to accept external interrupt. A typical tile has the following form: @@ -169,7 +181,8 @@ TileLink Connection ------------------- Chipyard use TileLink as its onboard bus protocol, and if your core doesn't use TileLink, you will need to convert them -in the tile class. Below is an example of how to connect a core using AXI4 to the TileLink bus: +in the tile class. Below is an example of how to connect a core using AXI4 to the TileLink bus with converters provided by +Chipyards: .. code-block:: scala @@ -185,9 +198,12 @@ in the tile class. Below is an example of how to connect a core using AXI4 to th := memAXI4Node) // The custom node, see below Remember, you may not need all of these intermediate widgets. See :ref:`diplomatic_widgets` for the meaning of each intermediate -widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Also, Chipyard -support AHB, APB and AXIS, and most of the AXI4 widgets has equivalent widget for these bus protocol. See the reference page for -more info. +widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Chipyard also +provides converters for AHB, APB and AXIS, and most of the AXI4 widgets has equivalent widget for these bus protocol; see the +source files in ``generators/rocket-chip/src/main/scala/amba`` for more info. + +If you are using other bus protocol, you may implement your own converters, using the files in ``generators/rocket-chip/src/main/scala/amba`` +as the template, but it is not recommended unless you are familiar with TileLink. ``memAXI4Node`` is an AXI4 master node and is defined as following in our example: @@ -232,7 +248,8 @@ The definition of ``TileInterrupts`` is } This function should be in the implementation class since it involves hardware generation. -Also, the tile can also notify other cores or devices for some events by calling following functions (in implementation class): +Also, the tile can also notify other cores or devices for some events by calling following functions in ``SourcesExternalNotifications`` +from the implementation class: .. code-block:: scala @@ -241,39 +258,6 @@ Also, the tile can also notify other cores or devices for some events by calling reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed -Trace (Optional) ----------------- - -Chipyard provides a set of ports for instruction trace that conforms with related RISC-V standard. -If you are using FireSim, it is recommended to implement these trace ports to enable FireSim to read trace. - -There are one inbound node ``traceAuxSinkNode.bundle: TraceAux`` and two outbound nodes ``traceCoreSourceNode.bundle: TraceCoreInterface`` -and ``bpwatchSourceNode.bundle: Vec[BPWatch]``. Note that the length of ``bpwatchSourceNode`` is equal to the max number of -breakpoints (set by ``nBreakpoints`` in ``CoreParams``). Below is the definition of these types: - -.. code-block:: scala - - // Control signal from the external tracer - class TraceAux extends Bundle { - val enable = Bool() // Enable trace output - val stall = Bool() // If true, the core should stall - } - // Check RISC-V Processor Trace spec V1.0 for more information of this interface - class TraceCoreInterface (val params: TraceCoreParams) extends Bundle { - val group = Vec(params.nGroups, new TraceCoreGroup(params)) - val priv = UInt(4.W) - val tval = UInt(params.xlen.W) - val cause = UInt(params.xlen.W) - } - // Address Breakpoint and watchpoint info (n is the retire width) - class BPWatch (val n: Int) extends Bundle() { - val valid = Vec(n, Bool()) // Valid bit of the output - val rvalid = Vec(n, Bool()) // Break on read - val wvalid = Vec(n, Bool()) // Break on write - val ivalid = Vec(n, Bool()) // Break on execute - val action = UInt(3.W) // Exception code (3 usually) - } - Implementation Class -------------------- @@ -313,12 +297,13 @@ the current config. An example of such config will be like this: .. code-block:: scala - class WithNMyCores(n: Int) extends Config((site, here, up) => { - case MyTilesKey => { - List.tabulate(n)(i => MyTileParams(hartId = i)) - } + class WithNMyCores(n: Int, hartidOffset: Int) extends Config((site, here, up) => { + case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem)) :++ List.tabulate(n)(i => MyTileParams(hartId = i + hartidOffset)) }) +Chipyard looks up the tile parameters in the field ``TilesLocated(InSubsystem)``, whose type is a list of ``InstantiableTileParams``. +This config fragment simply appends new tile parameters to the end of this list. + Now you have finished all the steps to prepare your cores for Chipyard! To generate the custom core, simply follow the instructions in :ref:`custom_chisel` to add your project to the build system, then create a config by following the steps in :ref:`hetero_socs_`. You can now run any desired workflow for the new config just as you do for the built-in cores. diff --git a/docs/TileLink-Diplomacy-Reference/index.rst b/docs/TileLink-Diplomacy-Reference/index.rst index dfc2ec5a..9c70287d 100644 --- a/docs/TileLink-Diplomacy-Reference/index.rst +++ b/docs/TileLink-Diplomacy-Reference/index.rst @@ -1,3 +1,5 @@ +.. _tilelink_and_diplomacy: + TileLink and Diplomacy Reference ================================ From 744e73fa9223c9b5571b91d388566b8a07ba8949 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 5 Jul 2020 21:05:21 -0700 Subject: [PATCH 06/16] Editing Docs --- docs/Customization/Custom-Core.rst | 179 ++++++------------ .../src/main/scala/example/TutorialTile.scala | 179 ++++++++++++++++++ 2 files changed, 241 insertions(+), 117 deletions(-) create mode 100644 generators/chipyard/src/main/scala/example/TutorialTile.scala diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index dcf2c257..17b9b125 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -3,21 +3,24 @@ Adding a custom core ==================== -You may want to add a custom RISC-V core to Chipyard generator. If the top module of your core is not in Chisel, -you will first need to create a Verilog blackbox for it. See :ref:`incorporating-verilog-blocks` for instructions. -Once you have a top module in Chisel, you are ready to create integrate it with Chipyard. - -``generators/ariane/src/main/scala/ArianeTile.scala`` and ``generators/boom/src/main/scala/common/tile.scala`` -provide two examples of how to integrate a core. +You may want to integrate a custom RISC-V core into the Chipyard framework. This documentation page provides a step-to-step +instruction on how to achieve this. .. note:: - RoCC is not supported by custom core currently. Please use Rocket or Boom as the RoCC base core if you need to use RoCC. + RoCC is currently not supported by cores other than Rocket and BOOM. Please use Rocket or BOOM as the RoCC base core if you need to use RoCC. -Parameter Case Classes ----------------------- -Chipyard will generate a core for every ``InstantiableTileParams`` object it discovered in the current config. +Wrap Verilog Module with Blackbox (Optional) +-------------------------------------------- + +Since Chipyard uses Scala and Chisel, if the top module of your core is not in Chisel, you will first need to create a Verilog +blackbox for it so that it can be processed by Chipyard. See :ref:`incorporating-verilog-blocks` for instructions. + +Create Parameter Case Classes +----------------------------- + +Chipyard will generate a core for every ``InstantiableTileParams`` object it discovered in the ``TilesLocated(InSubsystem)`` key. This object is derived from``TileParams``, a trait containing the information needed to create a tile. All cores must have their own implementation of ``InstantiableTileParams``, as well as ``CoreParams`` which is passed as a field in ``TileParams``. @@ -41,7 +44,7 @@ need a custom field with similar purposes): val icache: Option[ICacheParams] // Rocket specific: I1 cache option val dcache: Option[DCacheParams] // Rocket specific: D1 cache option val btb: Option[BTBParams] // Rocket specific: BTB / branch predictor option - val hartId: Int // Hart ID: Must be unique within a design config + val hartId: Int // Hart ID: Must be unique within a design config (This MUST be a case class parameter) val beuAddr: Option[BigInt] // Rocket specific: Bus Error Unit for Rocket Core val blockerCtrlAddr: Option[BigInt] // Rocket specific: Bus Blocker for Rocket Core val name: Option[String] // Name of the core @@ -110,110 +113,64 @@ need a custom field with similar purposes): dfmaLatency: Int = 4 // Rocket specific: Fused multiply-add pipeline latency (double precision) ) -Most of the fields here are originally designed for Rocket core and contains some architecture-specific details, but +Most of the fields here are originally designed for the Rocket core and thus contain some implementation-specific details, but many of them are general enough to be useful for other cores. It is strongly recommended to use these fields instead of creating your own custom fields when applicable. +You will also need a ``CanAttachTile`` class to add the tile config into the config system, with the following format: + +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 61-67 + .. note:: Implementations may choose to ignore some fields here or use them in a non-standard way, but using an inaccurate - value may break Chipyard components that rely on them (e.g. inaccurate indication of supported ISA extension will - result in incorrect test suite being generated) as well as any custom module that use them. ALWAYS document any + value may break Chipyard components that rely on them (e.g. an inaccurate indication of supported ISA extension will + result in an incorrect test suite being generated) as well as any custom modules that use them. ALWAYS document any fields you ignore or with altered usage in your core implementation, and if you are implementing other devices that would look up these config values, also document them. "Rocket specific" values are generally safe to ignore, but you should document them if you use them. -Tile Class ----------- +Create Tile Class +----------------- In Chipyard, all Tiles are diplomatically instantiated. In the first phase, diplomatic nodes which specify Tile-to-System interconnects are evaluated, while in the second "Module Implementation" phase, hardware is elaborated. -See :ref:`tilelink_and_diplomacy` for more details. +See :ref:`tilelink_and_diplomacy` for more details. In this step, you will need to implement a tile class for your core. All tile classes implement ``BaseTile`` and will normally implement ``SinksExternalInterrupts`` and ``SourcesExternalNotifications``, which allow the tile to accept external interrupt. A typical tile has the following form: -.. code-block:: scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 87-125, 143 - class MyTile( - val myParams: MyTileParams, - crossing: ClockCrossingType, - lookup: LookupByHartIdImpl, - q: Parameters, - logicalTreeNode: LogicalTreeNode) - extends BaseTile(myParams, crossing, lookup, q) - with SinksExternalInterrupts - with SourcesExternalNotifications - { +Connect TileLink Buses +---------------------- - // Private constructor ensures altered LazyModule.p is used implicitly - def this(params: MyTileParams, crossing: RocketCrossingParams, lookup: LookupByHartIdImpl, logicalTreeNode: LogicalTreeNode)(implicit p: Parameters) = - this(params, crossing.crossingType, lookup, p, logicalTreeNode) - - // Require TileLink nodes - val intOutwardNode = IntIdentityNode() - val masterNode = visibilityNode - val slaveNode = TLIdentityNode() - - // Implementation class (See below) - override lazy val module = new MyTileModuleImp(this) - - // Required entry of CPU device in the device tree for interrupt purpose - val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("my-organization,my-cpu", "riscv")) { - override def parent = Some(ResourceAnchors.cpus) - override def describe(resources: ResourceBindings): Description = { - val Description(name, mapping) = super.describe(resources) - Description(name, mapping ++ - cpuProperties ++ - nextLevelCacheProperty ++ - tileProperties) - } - } - - ResourceBinding { - Resource(cpuDevice, "reg").bind(ResourceAddress(hartId)) - } - - // (Connection to bus, interrupt, etc.) - } - -TileLink Connection -------------------- - -Chipyard use TileLink as its onboard bus protocol, and if your core doesn't use TileLink, you will need to convert them +Chipyard use TileLink as its onboard bus protocol. If your core doesn't use TileLink, you will need to insert converters +between the core's memory protocol and TileLink in the Tile module. in the tile class. Below is an example of how to connect a core using AXI4 to the TileLink bus with converters provided by -Chipyards: +Rocket chip: -.. code-block:: scala - - val memoryTap = TLIdentityNode() // Every bus connection should have their own tap node - (tlMasterXbar.node // tlMasterXbar is the bus crossbar to be used when this core / tile is acting as a master; otherwise, use tlSlaveXBar - := memoryTap - := TLBuffer() - := TLFIFOFixer(TLFIFOFixer.all) // fix FIFO ordering - := TLWidthWidget(beatBytes) // reduce size of TL - := AXI4ToTL() // convert to TL - := AXI4UserYanker(Some(2)) // remove user field on AXI interface. need but in reality user intf. not needed - := AXI4Fragmenter() // deal with multi-beat xacts - := memAXI4Node) // The custom node, see below +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 133-142 Remember, you may not need all of these intermediate widgets. See :ref:`diplomatic_widgets` for the meaning of each intermediate widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Chipyard also provides converters for AHB, APB and AXIS, and most of the AXI4 widgets has equivalent widget for these bus protocol; see the source files in ``generators/rocket-chip/src/main/scala/amba`` for more info. -If you are using other bus protocol, you may implement your own converters, using the files in ``generators/rocket-chip/src/main/scala/amba`` +If you are using some other bus protocol, you may implement your own converters, using the files in ``generators/rocket-chip/src/main/scala/amba`` as the template, but it is not recommended unless you are familiar with TileLink. ``memAXI4Node`` is an AXI4 master node and is defined as following in our example: -.. code-block:: scala - - val memAXI4Node = AXI4MasterNode( - Seq(AXI4MasterPortParameters( - masters = Seq(AXI4MasterParameters( - name = portName, - id = IdRange(0, 1 << idBits)))))) +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 126-132 where ``portName`` and ``idBits`` (number of bits to represent a port ID) are the parameter provides by the tile. Make sure to read :ref:`node_types` to check out what type of nodes Chipyard supports and their parameters! @@ -228,8 +185,8 @@ can override the following two functions to control how to buffer the bus reques You can find more information on ``TLBuffer`` in :ref:`diplomatic_widgets`. -Interrupt ---------- +Connect Interrupt +----------------- Chipyard allows a tile to either receive interrupts from other devices or initiate interrupts to notify other cores/devices. In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and @@ -258,48 +215,33 @@ from the implementation class: reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed -Implementation Class --------------------- +Create Implementation Class +--------------------------- -The implementation class is of the following form: +The implementation class for your core is of the following form: -.. code-block:: scala - - class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ - // annotate the parameters - Annotated.params(this, outer.tileParams) - - // TODO: Create the top module of the core and connect it with the ports in "outer" - } +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 145-149, 160 In the body of this class, you can look up any parameters by calling ``p({key})``, where ``{key}`` is the config key of -the value you want to look up. For a list of available keys, see the appendix below. +the value you want to look up. For a list of frequently used keys, see the appendix below. If you create an AXI4 node (or equivalents), you will need to connect them to your core. You can connect a port like this: -.. code-block:: scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 151-159 - outer.myAXI4Node.out foreach { case (out, edgeOut) => - // Connect your module IO port to "out" - // The type of "out" here is AXI4Bundle, which is defined in generators/rocket-chip/src/main/scala/amba/axi4/Bundles.scala - // Please refer to this file for the definition of the ports. - // If you are using APB, check APBBundle in generators/rocket-chip/src/main/scala/amba/apb/Bundles.scala - // If you are using AHB, check AHBSlaveBundle or AHBMasterBundle in generators/rocket-chip/src/main/scala/amba/ahb/Bundles.scala - // (choose one depends on the type of AHB node you create) - // If you are using AXIS, check AXISBundle and AXISBundleBits in generators/rocket-chip/src/main/scala/amba/axis/Bundles.scala - } +Create Config Fragments to Integrate the Core +--------------------------------------------- -Integrate the Core ------------------- - -To use your core in a set of config, you would need a config fragment that would create a ``TileParams`` object of your core in +To use your core in a Chipyard config, you would need a config fragment that would create a ``TileParams`` object of your core in the current config. An example of such config will be like this: -.. code-block:: scala - - class WithNMyCores(n: Int, hartidOffset: Int) extends Config((site, here, up) => { - case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem)) :++ List.tabulate(n)(i => MyTileParams(hartId = i + hartidOffset)) - }) +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :lines: 162-179 Chipyard looks up the tile parameters in the field ``TilesLocated(InSubsystem)``, whose type is a list of ``InstantiableTileParams``. This config fragment simply appends new tile parameters to the end of this list. @@ -308,6 +250,9 @@ Now you have finished all the steps to prepare your cores for Chipyard! To gener in :ref:`custom_chisel` to add your project to the build system, then create a config by following the steps in :ref:`hetero_socs_`. You can now run any desired workflow for the new config just as you do for the built-in cores. +If you would like to see how an actual core are integrated into Chipyard, ``generators/ariane/src/main/scala/ArianeTile.scala`` +provides a concrete example of integrating a third party Verilog core Ariane. + Appendix: Common Config Keys ---------------------------- diff --git a/generators/chipyard/src/main/scala/example/TutorialTile.scala b/generators/chipyard/src/main/scala/example/TutorialTile.scala new file mode 100644 index 00000000..12173184 --- /dev/null +++ b/generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -0,0 +1,179 @@ +package chipyard.example + +import chisel3._ +import chisel3.util._ + +import freechips.rocketchip.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.diplomaticobjectmodel.logicaltree.{LogicalTreeNode} +import freechips.rocketchip.rocket._ +import freechips.rocketchip.subsystem.{RocketCrossingParams} +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ +import freechips.rocketchip.util._ +import freechips.rocketchip.tile._ +import freechips.rocketchip.amba.axi4._ + +// Example parameter class copied from Ariane, not included in documentation but for compile check only +// If you are here for documentation, DO NOT copy MyCoreParams and MyTileParams directly - always figure +// out what parameters you need before you write the parameter class +case class MyCoreParams( + bootFreqHz: BigInt = BigInt(1700000000), + rasEntries: Int = 4, + btbEntries: Int = 16, + bhtEntries: Int = 16, + enableToFromHostCaching: Boolean = false, +) extends CoreParams { + val useVM: Boolean = true + val useUser: Boolean = true + val useSupervisor: Boolean = false + val useDebug: Boolean = true + val useAtomics: Boolean = true + val useAtomicsOnlyForIO: Boolean = false // copied from Rocket + val useCompressed: Boolean = true + override val useVector: Boolean = false + val useSCIE: Boolean = false + val useRVE: Boolean = false + val mulDiv: Option[MulDivParams] = Some(MulDivParams()) // copied from Rocket + val fpu: Option[FPUParams] = Some(FPUParams()) // copied fma latencies from Rocket + val nLocalInterrupts: Int = 0 + val nPMPs: Int = 0 // TODO: Check + val pmpGranularity: Int = 4 // copied from Rocket + val nBreakpoints: Int = 0 // TODO: Check + val useBPWatch: Boolean = false + val nPerfCounters: Int = 29 + val haveBasicCounters: Boolean = true + val haveFSDirty: Boolean = false + val misaWritable: Boolean = false + val haveCFlush: Boolean = false + val nL2TLBEntries: Int = 512 // copied from Rocket + val mtvecInit: Option[BigInt] = Some(BigInt(0)) // copied from Rocket + val mtvecWritable: Boolean = true // copied from Rocket + val instBits: Int = if (useCompressed) 16 else 32 + val lrscCycles: Int = 80 // copied from Rocket + val decodeWidth: Int = 1 // TODO: Check + val fetchWidth: Int = 1 // TODO: Check + val retireWidth: Int = 2 +} + +case class MyTileAttachParams( + tileParams: MyTileParams, + crossingParams: RocketCrossingParams +) extends CanAttachTile { + type TileType = MyTile + val lookup = PriorityMuxHartIdFromSeq(Seq(tileParams)) +} + +case class MyTileParams( + name: Option[String] = Some("my_tile"), + hartId: Int = 0, + trace: Boolean = false, + val core: MyCoreParams = MyCoreParams() +) extends InstantiableTileParams[MyTile] +{ + val beuAddr: Option[BigInt] = None + val blockerCtrlAddr: Option[BigInt] = None + val btb: Option[BTBParams] = Some(BTBParams()) + val boundaryBuffers: Boolean = false + val dcache: Option[DCacheParams] = Some(DCacheParams()) + val icache: Option[ICacheParams] = Some(ICacheParams()) + def instantiate(crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters): MyTile = { + new MyTile(this, crossing, lookup) + } +} + +class MyTile( + val myParams: MyTileParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters) + extends BaseTile(myParams, crossing, lookup, q) + with SinksExternalInterrupts + with SourcesExternalNotifications +{ + + // Private constructor ensures altered LazyModule.p is used implicitly + def this(params: MyTileParams, crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters) = + this(params, crossing.crossingType, lookup, p) + + // Require TileLink nodes + val intOutwardNode = IntIdentityNode() + val masterNode = visibilityNode + val slaveNode = TLIdentityNode() + + // Implementation class (See below) + override lazy val module = new MyTileModuleImp(this) + + // Required entry of CPU device in the device tree for interrupt purpose + val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("my-organization,my-cpu", "riscv")) { + override def parent = Some(ResourceAnchors.cpus) + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping ++ + cpuProperties ++ + nextLevelCacheProperty ++ + tileProperties) + } + } + + ResourceBinding { + Resource(cpuDevice, "reg").bind(ResourceAddress(hartId)) + } + + // (Connection to bus, interrupt, etc.) + // # of bits used in TileLink ID for master node. 4 bits can support 16 master nodes, but you can have a longer ID if you need more. + val idBits = 4 + val memAXI4Node = AXI4MasterNode( + Seq(AXI4MasterPortParameters( + masters = Seq(AXI4MasterParameters( + name = "myPortName", + id = IdRange(0, 1 << idBits)))))) + val memoryTap = TLIdentityNode() // Every bus connection should have their own tap node + (tlMasterXbar.node // tlMasterXbar is the bus crossbar to be used when this core / tile is acting as a master; otherwise, use tlSlaveXBar + := memoryTap + := TLBuffer() + := TLFIFOFixer(TLFIFOFixer.all) // fix FIFO ordering + := TLWidthWidget(masterPortBeatBytes) // reduce size of TL + := AXI4ToTL() // convert to TL + := AXI4UserYanker(Some(2)) // remove user field on AXI interface. need but in reality user intf. not needed + := AXI4Fragmenter() // deal with multi-beat xacts + := memAXI4Node) // The custom node, see below +} + +class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ + // annotate the parameters + Annotated.params(this, outer.myParams) + + // TODO: Create the top module of the core and connect it with the ports in "outer" + + outer.memAXI4Node.out foreach { case (out, edgeOut) => + // Connect your module IO port to "out" + // The type of "out" here is AXI4Bundle, which is defined in generators/rocket-chip/src/main/scala/amba/axi4/Bundles.scala + // Please refer to this file for the definition of the ports. + // If you are using APB, check APBBundle in generators/rocket-chip/src/main/scala/amba/apb/Bundles.scala + // If you are using AHB, check AHBSlaveBundle or AHBMasterBundle in generators/rocket-chip/src/main/scala/amba/ahb/Bundles.scala + // (choose one depends on the type of AHB node you create) + // If you are using AXIS, check AXISBundle and AXISBundleBits in generators/rocket-chip/src/main/scala/amba/axis/Bundles.scala + } +} + +class WithNMyCores(n: Int = 1, overrideIdOffset: Option[Int] = None) extends Config((site, here, up) => { + case TilesLocated(InSubsystem) => { + // Calculate the next available hart ID (since hart ID cannot be duplicated) + val prev = up(TilesLocated(InSubsystem), site) + val idOffset = overrideIdOffset.getOrElse(prev.size) + // Create TileAttachParams for every core to be instantiated + (0 until n).map { i => + MyTileAttachParams( + tileParams = MyTileParams(hartId = i + idOffset), + crossingParams = RocketCrossingParams() + ) + } ++ prev + } + // Configurate # of bytes in one memory / IO transaction. For RV64, one load/store instruction can transfer 8 bytes at most. + case SystemBusKey => up(SystemBusKey, site).copy(beatBytes = 8) + // The # of instruction bits. Use maximum # of bits if your core supports both 32 and 64 bits. + case XLen => 64 +}) From 6cb8a60a808d426a111cd470cf360460bbde3cbd Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 5 Jul 2020 21:18:31 -0700 Subject: [PATCH 07/16] Remove Key List --- docs/Customization/Custom-Core.rst | 83 ------------------------------ 1 file changed, 83 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 17b9b125..94d47505 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -224,9 +224,6 @@ The implementation class for your core is of the following form: :language: scala :lines: 145-149, 160 -In the body of this class, you can look up any parameters by calling ``p({key})``, where ``{key}`` is the config key of -the value you want to look up. For a list of frequently used keys, see the appendix below. - If you create an AXI4 node (or equivalents), you will need to connect them to your core. You can connect a port like this: .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -252,83 +249,3 @@ You can now run any desired workflow for the new config just as you do for the b If you would like to see how an actual core are integrated into Chipyard, ``generators/ariane/src/main/scala/ArianeTile.scala`` provides a concrete example of integrating a third party Verilog core Ariane. - -Appendix: Common Config Keys ----------------------------- - -Chipyard provide a set of keys to store standard parameters. Below are some of the most common key used in core integration. -(Note that internal fields are hidden) - -.. code-block:: scala - - // keys - // Parameters exposed to the top-level design, set based on external requirements, etc. See RISC-V debug specs for more info. - case object DebugModuleKey extends Field[Option[DebugModuleParams]](Some(DebugModuleParams())) - case object BootROMParams extends Field[BootROMParams] // See chipyard boot process tutorial - case object CLINTKey extends Field[Option[CLINTParams]](None) // Core Local Interrupter setting (See SiFive Interrupt Cookbook) - case object PLICKey extends Field[Option[PLICParams]](None) // Platform Level Interrupt Controller setting (See SiFive Interrupt Cookbook) - case object CacheBlockBytes extends Field[Int](64) // # of bytes in a cache block - case object BroadcastKey extends Field(BroadcastParams()) // L2 Cache broadcast setting - case object BankedL2Key extends Field(BankedL2Params()) // L2 Cache memory setting - case object PgLevels extends Field[Int](2) // Page Level of virtual memory - case object ASIdBits extends Field[Int](0) // Max # of bits for Address Space Identifer (See specs) - case object ExtMem extends Field[Option[MemoryPortParams]](None) // External DRAM setting - case object ExtBus extends Field[Option[MasterPortParams]](None) // External (off-chip) output bus setting - case object ExtIn extends Field[Option[SlavePortParams]](None) // External (off-chip) input bus setting - case object MaxHartIdBits extends Field[Int] // Max # of bits used to represent a Hart ID - case object XLen extends Field[Int] // Instruction bits (32 or 64) - case object BuildRoCC extends Field[Seq[Parameters => LazyRoCC]](Nil) // See custom ROCC tutorial - - // Values - case class DebugModuleParams ( - nDMIAddrSize : Int = 7, // Size of the Debug Bus Address - nProgramBufferWords: Int = 16, // Number of 32-bit words for Program Buffer - nAbstractDataWords : Int = 4, // Number of 32-bit words for Abstract Commands - nScratch : Int = 1, // Number of scratch memories used - hasBusMaster : Boolean = false, // Whether or not a bus master should be included - clockGate : Boolean = true, // Use clock gating - maxSupportedSBAccess : Int = 32, // Maximum transaction size supported by System Bus Access logic. - supportQuickAccess : Boolean = false, // Whether or not to support the quick access command. - supportHartArray : Boolean = true, // Whether or not to implement the hart array register (if >1 hart). - nHaltGroups : Int = 1, // Number of halt groups (group of harts that are halted together) - nExtTriggers : Int = 0, // Number of extra triggers - hasHartResets : Boolean = false, // Whether harts can be reseted with debugging system - hasImplicitEbreak : Boolean = false, // There is an additional RO program buffer word containing an ebreak - hasAuthentication : Boolean = false, // Has authentication (to prevent unauthorized users to use debugging system) - crossingHasSafeReset : Boolean = true // Include "safe" logic in Async Crossings so that only one side needs to be reset. - ) - case class CLINTParams( - baseAddress: BigInt = 0x02000000, // Default interrupt handler base address for CLINT - intStages: Int = 0 // # of cycles (stages) interrupts are delayed - ) - case class PLICParams( - baseAddress: BigInt = 0xC000000, // Default interrupt handler base address for PLIC - maxPriorities: Int = 7, // Maximum allowed interrupt priority (cannot be over 7) - intStages: Int = 0, // # of cycles (stages) interrupts are delayed - maxHarts: Int = PLICConsts.maxMaxHarts // Maximum number or hart / core connected to it - ) - case class BroadcastParams( - nTrackers: Int = 4, // # of broadcast tracker - bufferless: Boolean = false // Bufferless broadcast - ) - case class BankedL2Params( - nBanks: Int = 1 // Number of banks in L2 cache - ) - case class MasterPortParams( - base: BigInt, // Base memory address for this port - size: BigInt, // Size of this external memory - beatBytes: Int, // Interface width in bytes - idBits: Int, // # of bits in the port ID - maxXferBytes: Int = 256, // Maximum bytes in one transfer transaction - executable: Boolean = true // If the data from this port can be executed as instruciton - ) - /** Specifies the width of external slave ports */ - case class SlavePortParams( - beatBytes: Int, // Interface width in bytes - idBits: Int, // # of bits in the port ID - sourceBits: Int // # of bits in the source address - ) - case class MemoryPortParams( - master: MasterPortParams, // The memory port setting - nMemoryChannels: Int // Number of memory channel - ) From 9ad9d00a232ec38dc19269b61df2a5e0151dad8a Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Wed, 8 Jul 2020 16:02:31 -0700 Subject: [PATCH 08/16] Second revision --- docs/Customization/Custom-Core.rst | 66 ++++++++++++------- .../src/main/scala/example/TutorialTile.scala | 22 ++++++- 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 94d47505..9a2249ba 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -121,7 +121,8 @@ You will also need a ``CanAttachTile`` class to add the tile config into the con .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala :language: scala - :lines: 61-67 + :start-after: DOC include start: CanAttachTile + :end-before: DOC include end: CanAttachTile .. note:: @@ -137,14 +138,17 @@ Create Tile Class In Chipyard, all Tiles are diplomatically instantiated. In the first phase, diplomatic nodes which specify Tile-to-System interconnects are evaluated, while in the second "Module Implementation" phase, hardware is elaborated. -See :ref:`tilelink_and_diplomacy` for more details. In this step, you will need to implement a tile class for your core. +See :ref:`tilelink_and_diplomacy` for more details. In this step, you will need to implement a tile class for your core, +which specifies the constraints on the core's parameters and the connections with other diplomatic nodes. This class +usually contains Diplomacy/TileLink code only, and Chisel RTL code should not go here. All tile classes implement ``BaseTile`` and will normally implement ``SinksExternalInterrupts`` and ``SourcesExternalNotifications``, which allow the tile to accept external interrupt. A typical tile has the following form: .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala :language: scala - :lines: 87-125, 143 + :start-after: DOC include start: Tile class + :end-before: DOC include end: Tile class Connect TileLink Buses ---------------------- @@ -156,7 +160,8 @@ Rocket chip: .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala :language: scala - :lines: 133-142 + :start-after: DOC include start: AXI4 convert + :end-before: DOC include end: AXI4 convert Remember, you may not need all of these intermediate widgets. See :ref:`diplomatic_widgets` for the meaning of each intermediate widget. If you are using TileLink, then you only need the tap node and the TileLink node used by your components. Chipyard also @@ -170,7 +175,8 @@ as the template, but it is not recommended unless you are familiar with TileLink .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala :language: scala - :lines: 126-132 + :start-after: DOC include start: AXI4 node + :end-before: DOC include end: AXI4 node where ``portName`` and ``idBits`` (number of bits to represent a port ID) are the parameter provides by the tile. Make sure to read :ref:`node_types` to check out what type of nodes Chipyard supports and their parameters! @@ -185,12 +191,36 @@ can override the following two functions to control how to buffer the bus reques You can find more information on ``TLBuffer`` in :ref:`diplomatic_widgets`. +Create Implementation Class +--------------------------- + +The implementation class contains the parameterized, actual hardware that depends on the values resolved by the Diplomacy +framework according to the info provided in the Tile class. This class will normally contains Chisel RTL codes, and if your +core is in Verilog, you will need to put the black box class you created in the first step here and connect it with the buses +and other components. No Diplomacy/TileLink code should be in this class; you should only connect the IO signals in TileLink +interfaces or other diplomatically defined components, which are located in the tile class. + +The implementation class for your core is of the following form: + +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :start-after: DOC include start: Implementation class + :end-before: DOC include end: Implementation class + +If you create an AXI4 node (or equivalents), you will need to connect them to your core. You can connect a port like this: + +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :start-after: DOC include start: AXI4 connect + :end-before: DOC include end: AXI4 connect + Connect Interrupt ----------------- Chipyard allows a tile to either receive interrupts from other devices or initiate interrupts to notify other cores/devices. In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and -call ``decodeCoreInterrupts`` with the object as the argument. You can then read the interrupt bits from the object. +call ``decodeCoreInterrupts`` with the object as the argument. Note that you should call this function in the implementation +class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the object. The definition of ``TileInterrupts`` is .. code-block:: scala @@ -204,7 +234,6 @@ The definition of ``TileInterrupts`` is val lip = Vec(coreParams.nLocalInterrupts, Bool()) // Local interrupts } -This function should be in the implementation class since it involves hardware generation. Also, the tile can also notify other cores or devices for some events by calling following functions in ``SourcesExternalNotifications`` from the implementation class: @@ -215,21 +244,6 @@ from the implementation class: reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed -Create Implementation Class ---------------------------- - -The implementation class for your core is of the following form: - -.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala - :language: scala - :lines: 145-149, 160 - -If you create an AXI4 node (or equivalents), you will need to connect them to your core. You can connect a port like this: - -.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala - :language: scala - :lines: 151-159 - Create Config Fragments to Integrate the Core --------------------------------------------- @@ -238,7 +252,8 @@ the current config. An example of such config will be like this: .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala :language: scala - :lines: 162-179 + :start-after: DOC include start: Config fragment + :end-before: DOC include end: Config fragment Chipyard looks up the tile parameters in the field ``TilesLocated(InSubsystem)``, whose type is a list of ``InstantiableTileParams``. This config fragment simply appends new tile parameters to the end of this list. @@ -247,5 +262,6 @@ Now you have finished all the steps to prepare your cores for Chipyard! To gener in :ref:`custom_chisel` to add your project to the build system, then create a config by following the steps in :ref:`hetero_socs_`. You can now run any desired workflow for the new config just as you do for the built-in cores. -If you would like to see how an actual core are integrated into Chipyard, ``generators/ariane/src/main/scala/ArianeTile.scala`` -provides a concrete example of integrating a third party Verilog core Ariane. +If you would like to see an example of a complete third-party Verilog core integrated into Chipyard, ``generators/ariane/src/main/scala/ArianeTile.scala`` +provides a concrete example of the Ariane core. Note that this particular example includes additional nuances with respect to the interaction of the AXI +interface with the memory coherency system. \ No newline at end of file diff --git a/generators/chipyard/src/main/scala/example/TutorialTile.scala b/generators/chipyard/src/main/scala/example/TutorialTile.scala index 12173184..41f79892 100644 --- a/generators/chipyard/src/main/scala/example/TutorialTile.scala +++ b/generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -58,6 +58,7 @@ case class MyCoreParams( val retireWidth: Int = 2 } +// DOC include start: CanAttachTile case class MyTileAttachParams( tileParams: MyTileParams, crossingParams: RocketCrossingParams @@ -65,6 +66,7 @@ case class MyTileAttachParams( type TileType = MyTile val lookup = PriorityMuxHartIdFromSeq(Seq(tileParams)) } +// DOC include end: CanAttachTile case class MyTileParams( name: Option[String] = Some("my_tile"), @@ -84,6 +86,7 @@ case class MyTileParams( } } +// DOC include start: Tile class class MyTile( val myParams: MyTileParams, crossing: ClockCrossingType, @@ -123,6 +126,10 @@ class MyTile( } // (Connection to bus, interrupt, etc.) +// } + // DOC include end: Tile class + + // DOC include start: AXI4 node // # of bits used in TileLink ID for master node. 4 bits can support 16 master nodes, but you can have a longer ID if you need more. val idBits = 4 val memAXI4Node = AXI4MasterNode( @@ -131,6 +138,9 @@ class MyTile( name = "myPortName", id = IdRange(0, 1 << idBits)))))) val memoryTap = TLIdentityNode() // Every bus connection should have their own tap node + // DOC include end: AXI4 node + + // DOC include start: AXI4 convert (tlMasterXbar.node // tlMasterXbar is the bus crossbar to be used when this core / tile is acting as a master; otherwise, use tlSlaveXBar := memoryTap := TLBuffer() @@ -140,14 +150,20 @@ class MyTile( := AXI4UserYanker(Some(2)) // remove user field on AXI interface. need but in reality user intf. not needed := AXI4Fragmenter() // deal with multi-beat xacts := memAXI4Node) // The custom node, see below + // DOC include end: AXI4 convert + } +// DOC include start: Implementation class class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ // annotate the parameters Annotated.params(this, outer.myParams) - // TODO: Create the top module of the core and connect it with the ports in "outer" + // TODO: Create the top module of the core and connect it with the ports in "outer" } +//} + // DOC include end: Implementation class + // DOC include start: AXI4 connect outer.memAXI4Node.out foreach { case (out, edgeOut) => // Connect your module IO port to "out" // The type of "out" here is AXI4Bundle, which is defined in generators/rocket-chip/src/main/scala/amba/axi4/Bundles.scala @@ -157,8 +173,11 @@ class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ // (choose one depends on the type of AHB node you create) // If you are using AXIS, check AXISBundle and AXISBundleBits in generators/rocket-chip/src/main/scala/amba/axis/Bundles.scala } + // DOC include end: AXI4 connect + } +// DOC include start: Config fragment class WithNMyCores(n: Int = 1, overrideIdOffset: Option[Int] = None) extends Config((site, here, up) => { case TilesLocated(InSubsystem) => { // Calculate the next available hart ID (since hart ID cannot be duplicated) @@ -177,3 +196,4 @@ class WithNMyCores(n: Int = 1, overrideIdOffset: Option[Int] = None) extends Con // The # of instruction bits. Use maximum # of bits if your core supports both 32 and 64 bits. case XLen => 64 }) +// DOC include end: Config fragment From ced7ea634cc2eae5a8045a20ce81c177f347c0b3 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 12 Jul 2020 01:08:13 -0700 Subject: [PATCH 09/16] 3rd Revision --- docs/Customization/Custom-Core.rst | 41 +++++++++++-------- .../src/main/scala/example/TutorialTile.scala | 4 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 9a2249ba..d6a19f04 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -3,8 +3,8 @@ Adding a custom core ==================== -You may want to integrate a custom RISC-V core into the Chipyard framework. This documentation page provides a step-to-step -instruction on how to achieve this. +You may want to integrate a custom RISC-V core into the Chipyard framework. This documentation page provides step-by-step +instructions on how to achieve this. .. note:: @@ -24,18 +24,18 @@ Chipyard will generate a core for every ``InstantiableTileParams`` object it dis This object is derived from``TileParams``, a trait containing the information needed to create a tile. All cores must have their own implementation of ``InstantiableTileParams``, as well as ``CoreParams`` which is passed as a field in ``TileParams``. -``TileParams`` holds the parameters for the tile, which are the same for every generated core, while ``CoreParams`` -contains the parameters for individual cores. They must be implemented as case classes with fields that can be overridden by +``TileParams`` holds the parameters for the tile, which include parameters for all components in the tile (e.g. +core, cache, MMU, etc.), while ``CoreParams`` contains parameters specific to the core on the tile. +They must be implemented as case classes with fields that can be overridden by other config fragments as the constructor parameters. See the appendix at the bottom of the page for a list of variable to be implemented. You can also add custom fields to them, but standard fields should always be preferred. -``InstantiableTileParams[TileType]`` holds the constructor of ``TileType`` on top of the fields of ``TileParams``. +``InstantiableTileParams[TileType]`` holds the constructor of ``TileType`` on top of the fields of ``TileParams``, +where ``TileType`` is the tile class (see the next section). All custom cores will also need to implement ``instantiate()`` in their tile parameter class to return a new instance of the tile class ``TileType``. -``TileParams``, ``InstantiableTileParams[TileType]`` and ``CoreParams`` contains the following fields (you may ignore -any fields marked "Rocket specific" and use their default values, although it is recommended to use them if you -need a custom field with similar purposes): +``TileParams``, ``InstantiableTileParams[TileType]`` and ``CoreParams`` contains the following fields: .. code-block:: scala @@ -113,9 +113,11 @@ need a custom field with similar purposes): dfmaLatency: Int = 4 // Rocket specific: Fused multiply-add pipeline latency (double precision) ) -Most of the fields here are originally designed for the Rocket core and thus contain some implementation-specific details, but -many of them are general enough to be useful for other cores. It is strongly recommended to use these fields instead -of creating your own custom fields when applicable. +Most of the fields here (marked "Rocket spcific") are originally designed for the Rocket core and thus contain some +implementation-specific details, but many of them are general enough to be useful for other cores. You may ignore +any fields marked "Rocket specific" and use their default values; however, if you need to store additional information +with meaning or usage similar to these "Rocket specific" fields, it is recommended to use these fields instead of +creating your own custom fields. You will also need a ``CanAttachTile`` class to add the tile config into the config system, with the following format: @@ -124,6 +126,9 @@ You will also need a ``CanAttachTile`` class to add the tile config into the con :start-after: DOC include start: CanAttachTile :end-before: DOC include end: CanAttachTile +During elaboration, Chipyard will look for subclasses of ``CanAttachTile`` in the config system and instantiate a tile +from the parameters in this class for every such class it found. + .. note:: Implementations may choose to ignore some fields here or use them in a non-standard way, but using an inaccurate @@ -153,8 +158,8 @@ which allow the tile to accept external interrupt. A typical tile has the follow Connect TileLink Buses ---------------------- -Chipyard use TileLink as its onboard bus protocol. If your core doesn't use TileLink, you will need to insert converters -between the core's memory protocol and TileLink in the Tile module. +Chipyard uses TileLink as its onboard bus protocol. If your core doesn't use TileLink, you will need to insert converters +between the core's memory protocol and TileLink within the Tile module. in the tile class. Below is an example of how to connect a core using AXI4 to the TileLink bus with converters provided by Rocket chip: @@ -195,8 +200,8 @@ Create Implementation Class --------------------------- The implementation class contains the parameterized, actual hardware that depends on the values resolved by the Diplomacy -framework according to the info provided in the Tile class. This class will normally contains Chisel RTL codes, and if your -core is in Verilog, you will need to put the black box class you created in the first step here and connect it with the buses +framework according to the info provided in the Tile class. This class will normally contains Chisel RTL code. If your +core is in Verilog, you will need to instantiate the black box class that wraps your Verilog implementation and connect it with the buses and other components. No Diplomacy/TileLink code should be in this class; you should only connect the IO signals in TileLink interfaces or other diplomatically defined components, which are located in the tile class. @@ -247,7 +252,7 @@ from the implementation class: Create Config Fragments to Integrate the Core --------------------------------------------- -To use your core in a Chipyard config, you would need a config fragment that would create a ``TileParams`` object of your core in +To use your core in a Chipyard config, you will need a config fragment that will create a ``TileParams`` object of your core in the current config. An example of such config will be like this: .. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -260,8 +265,8 @@ This config fragment simply appends new tile parameters to the end of this list. Now you have finished all the steps to prepare your cores for Chipyard! To generate the custom core, simply follow the instructions in :ref:`custom_chisel` to add your project to the build system, then create a config by following the steps in :ref:`hetero_socs_`. -You can now run any desired workflow for the new config just as you do for the built-in cores. +You can now run most desired workflows for the new config just as you would for the built-in cores (depending on the functionality your core supports). If you would like to see an example of a complete third-party Verilog core integrated into Chipyard, ``generators/ariane/src/main/scala/ArianeTile.scala`` provides a concrete example of the Ariane core. Note that this particular example includes additional nuances with respect to the interaction of the AXI -interface with the memory coherency system. \ No newline at end of file +interface with the memory coherency system. diff --git a/generators/chipyard/src/main/scala/example/TutorialTile.scala b/generators/chipyard/src/main/scala/example/TutorialTile.scala index 41f79892..75ff1e5b 100644 --- a/generators/chipyard/src/main/scala/example/TutorialTile.scala +++ b/generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -125,7 +125,7 @@ class MyTile( Resource(cpuDevice, "reg").bind(ResourceAddress(hartId)) } - // (Connection to bus, interrupt, etc.) + // TODO: Create TileLink nodes and connections here. // } // DOC include end: Tile class @@ -159,7 +159,7 @@ class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ // annotate the parameters Annotated.params(this, outer.myParams) - // TODO: Create the top module of the core and connect it with the ports in "outer" } + // TODO: Create the top module of the core and connect it with the ports in "outer" //} // DOC include end: Implementation class From 14399e88b3fb7c048244c93d53c6600129f5c500 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 12 Jul 2020 01:23:34 -0700 Subject: [PATCH 10/16] Minor change --- docs/Customization/Custom-Core.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index d6a19f04..16d19f27 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -225,7 +225,7 @@ Connect Interrupt Chipyard allows a tile to either receive interrupts from other devices or initiate interrupts to notify other cores/devices. In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and call ``decodeCoreInterrupts`` with the object as the argument. Note that you should call this function in the implementation -class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the object. +class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the resulting object. The definition of ``TileInterrupts`` is .. code-block:: scala From 7ea464dc906eb629db1df7deb67ca9641ce01298 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Tue, 14 Jul 2020 12:49:36 -0700 Subject: [PATCH 11/16] 4th revision --- docs/Customization/Custom-Core.rst | 26 ++++++++++++++----- .../src/main/scala/example/TutorialTile.scala | 25 ++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 16d19f27..4a7f57f1 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -224,9 +224,9 @@ Connect Interrupt Chipyard allows a tile to either receive interrupts from other devices or initiate interrupts to notify other cores/devices. In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and -call ``decodeCoreInterrupts`` with the object as the argument. Note that you should call this function in the implementation -class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the resulting object. -The definition of ``TileInterrupts`` is +call ``decodeCoreInterrupts()`` with the object as the argument. Note that you should call this function in the implementation +class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the ``TileInterrupts`` bundle +we create above. The definition of ``TileInterrupts`` is .. code-block:: scala @@ -239,15 +239,29 @@ The definition of ``TileInterrupts`` is val lip = Vec(coreParams.nLocalInterrupts, Bool()) // Local interrupts } +Here is an example on how to connect these signals in the implementation class: + +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :start-after: DOC include start: connect interrupt + :end-before: DOC include end: connect interrupt + Also, the tile can also notify other cores or devices for some events by calling following functions in ``SourcesExternalNotifications`` from the implementation class: .. code-block:: scala def reportHalt(could_halt: Option[Bool]) // Triggered when there is an unrecoverable hardware error (halt the machine) - def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (used only by cache when there's an ECC error) - reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) - reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed + def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (Rocket specific: used only by cache when there's an ECC error) + def reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) + def reportWFI(could_wfi: Option[Bool]) // Triggered when a WFI instruction is executed + +Here is an example on how to use these functions to raise interrupt. + +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/TutorialTile.scala + :language: scala + :start-after: DOC include start: raise interrupt + :end-before: DOC include end: raise interrupt Create Config Fragments to Integrate the Core --------------------------------------------- diff --git a/generators/chipyard/src/main/scala/example/TutorialTile.scala b/generators/chipyard/src/main/scala/example/TutorialTile.scala index 75ff1e5b..c8f71b85 100644 --- a/generators/chipyard/src/main/scala/example/TutorialTile.scala +++ b/generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -163,6 +163,31 @@ class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ //} // DOC include end: Implementation class + // DOC include start: connect interrupt + // For example, our core support debug interrupt and machine-level interrupt, and suppose the following two signals + // are the interrupt inputs to the core. (DO NOT COPY this code - if your core treat each type of interrupt differently, + // you need to connect them to different interrupt ports of your core) + val debug_i = Wire(Bool()) + val mtip_i = Wire(Bool()) + // We create a bundle here and decode the interrupt. + val int_bundle = new TileInterrupts() + outer.decodeCoreInterrupts(int_bundle) + debug_i := int_bundle.debug + mtip_i := int_bundle.meip & int_bundle.msip & int_bundle.mtip + // DOC include end: connect interrupt + + // DOC include start: raise interrupt + // This is a demo. You should call these function according to your core + // Suppose that the following signal is from the decoder indicating a WFI instruction is received. + val wfi_o = Wire(Bool()) + outer.reportWFI(Some(wfi_o)) + // Suppose that the following signal indicate an unreconverable hardware error. + val halt_o = Wire(Bool()) + outer.reportHalt(Some(halt_o)) + // Suppose that our core never stall for a long time / stop retiring. Use None to indicate that this interrupt never fires. + outer.reportCease(None) + // DOC include end: raise interrupt + // DOC include start: AXI4 connect outer.memAXI4Node.out foreach { case (out, edgeOut) => // Connect your module IO port to "out" From 9fbc0a5bea7fe7f1b20a46389b18c9e71ee78f4e Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Wed, 15 Jul 2020 11:08:36 -0700 Subject: [PATCH 12/16] Add links --- docs/Customization/Custom-Core.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 4a7f57f1..3d6a6393 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -39,6 +39,7 @@ of the tile class ``TileType``. .. code-block:: scala + // The two classes below can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/BaseTile.scala. trait TileParams { val core: CoreParams // Core parameters (see below) val icache: Option[ICacheParams] // Rocket specific: I1 cache option @@ -55,6 +56,7 @@ of the tile class ``TileType``. (implicit p: Parameters): TileType } + // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Core.scala. trait CoreParams { val bootFreqHz: BigInt // Frequency val useVM: Boolean // Support virtual memory @@ -105,6 +107,7 @@ of the tile class ``TileType``. def vMemDataBits: Int = 0 } + // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/FPU.scala. case class FPUParams( minFLen: Int = 32, // Minimum floating point length (no need to change) fLen: Int = 64, // Maximum floating point length, use 32 if only single precision is supported @@ -191,6 +194,9 @@ can override the following two functions to control how to buffer the bus reques .. code-block:: scala + // This two functions can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/BaseTile.scala, + // in the class "BaseTile". + // By default, their value is "TLBuffer(BufferParams.none)". protected def makeMasterBoundaryBuffers(implicit p: Parameters): TLBuffer protected def makeSlaveBoundaryBuffers(implicit p: Parameters): TLBuffer @@ -230,6 +236,7 @@ we create above. The definition of ``TileInterrupts`` is .. code-block:: scala + // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Interrupts.scala. class TileInterrupts(implicit p: Parameters) extends CoreBundle()(p) { val debug = Bool() // debug interrupt val mtip = Bool() // Machine level timer interrupt @@ -251,6 +258,8 @@ from the implementation class: .. code-block:: scala + // These functions can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Interrupts.scala, + // in the trait "SourcesExternalNotifications". def reportHalt(could_halt: Option[Bool]) // Triggered when there is an unrecoverable hardware error (halt the machine) def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (Rocket specific: used only by cache when there's an ECC error) def reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) From fddf2181471a1d227f75c592e394ec0bc39cc6c7 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Thu, 16 Jul 2020 15:39:07 -0700 Subject: [PATCH 13/16] 5th revision --- .../src/main/scala/example/TutorialTile.scala | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/generators/chipyard/src/main/scala/example/TutorialTile.scala b/generators/chipyard/src/main/scala/example/TutorialTile.scala index c8f71b85..1f58e5e4 100644 --- a/generators/chipyard/src/main/scala/example/TutorialTile.scala +++ b/generators/chipyard/src/main/scala/example/TutorialTile.scala @@ -126,7 +126,6 @@ class MyTile( } // TODO: Create TileLink nodes and connections here. -// } // DOC include end: Tile class // DOC include start: AXI4 node @@ -160,7 +159,20 @@ class MyTileModuleImp(outer: MyTile) extends BaseTileModuleImp(outer){ Annotated.params(this, outer.myParams) // TODO: Create the top module of the core and connect it with the ports in "outer" -//} + + // If your core is in Verilog (assume your blackbox is called "MyCoreBlackbox"), instantiate it here like + // val core = Module(new MyCoreBlackbox(params...)) + // (as described in the blackbox tutorial) and connect appropriate signals. See the blackbox tutorial + // (link on the top of the page) for more info. + // You can look at https://github.com/ucb-bar/ariane-wrapper/blob/master/src/main/scala/ArianeTile.scala + // for a Verilog example. + + // If your core is in Chisel, you can simply instantiate the top module here like other Chisel module + // and connect appropriate signal. You can even implement this class as your top module. + // See https://github.com/riscv-boom/riscv-boom/blob/master/src/main/scala/common/tile.scala and + // https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/RocketTile.scala for + // Chisel example. + // DOC include end: Implementation class // DOC include start: connect interrupt From 2c7e7f3199d497668a20d8a635dfb2aeca27b81d Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 19 Jul 2020 21:36:50 -0700 Subject: [PATCH 14/16] Fixed file links --- docs/Customization/Custom-Core.rst | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 3d6a6393..fa14fff6 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -35,11 +35,14 @@ where ``TileType`` is the tile class (see the next section). All custom cores will also need to implement ``instantiate()`` in their tile parameter class to return a new instance of the tile class ``TileType``. -``TileParams``, ``InstantiableTileParams[TileType]`` and ``CoreParams`` contains the following fields: +``TileParams`` (in the file `BaseTile.scala `_) , +``InstantiableTileParams`` (in the file `BaseTile.scala `_), +``CoreParams`` (in the file `Core.scala `_), +and ``FPUParams`` (in the file `FPU.scala `_) +contains the following fields: .. code-block:: scala - // The two classes below can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/BaseTile.scala. trait TileParams { val core: CoreParams // Core parameters (see below) val icache: Option[ICacheParams] // Rocket specific: I1 cache option @@ -56,7 +59,6 @@ of the tile class ``TileType``. (implicit p: Parameters): TileType } - // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Core.scala. trait CoreParams { val bootFreqHz: BigInt // Frequency val useVM: Boolean // Support virtual memory @@ -106,8 +108,7 @@ of the tile class ``TileType``. def eLen(xLen: Int, fLen: Int): Int = xLen max fLen def vMemDataBits: Int = 0 } - - // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/FPU.scala. + case class FPUParams( minFLen: Int = 32, // Minimum floating point length (no need to change) fLen: Int = 64, // Maximum floating point length, use 32 if only single precision is supported @@ -191,11 +192,11 @@ Make sure to read :ref:`node_types` to check out what type of nodes Chipyard sup Also, by default, there are boundary buffers for both master and slave connections to the bus when they are leaving the tile, and you can override the following two functions to control how to buffer the bus requests/responses: +(You can find the definition of these two functions in the class ``BaseTile`` in the file +`BaseTile.scala `_) .. code-block:: scala - // This two functions can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/BaseTile.scala, - // in the class "BaseTile". // By default, their value is "TLBuffer(BufferParams.none)". protected def makeMasterBoundaryBuffers(implicit p: Parameters): TLBuffer protected def makeSlaveBoundaryBuffers(implicit p: Parameters): TLBuffer @@ -232,11 +233,11 @@ Chipyard allows a tile to either receive interrupts from other devices or initia In the tile that inherited ``SinksExternalInterrupts``, one can create a ``TileInterrupts`` object (a Chisel bundle) and call ``decodeCoreInterrupts()`` with the object as the argument. Note that you should call this function in the implementation class since it returns a Chisel bundle used by RTL code. You can then read the interrupt bits from the ``TileInterrupts`` bundle -we create above. The definition of ``TileInterrupts`` is +we create above. The definition of ``TileInterrupts`` +(in the file `Interrupts.scala `_) is .. code-block:: scala - // This class can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Interrupts.scala. class TileInterrupts(implicit p: Parameters) extends CoreBundle()(p) { val debug = Bool() // debug interrupt val mtip = Bool() // Machine level timer interrupt @@ -255,11 +256,11 @@ Here is an example on how to connect these signals in the implementation class: Also, the tile can also notify other cores or devices for some events by calling following functions in ``SourcesExternalNotifications`` from the implementation class: +(These functions can be found in in the trait ``SourcesExternalNotifications`` in the file +`Interrupts.scala `_) .. code-block:: scala - // These functions can be found in https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/tile/Interrupts.scala, - // in the trait "SourcesExternalNotifications". def reportHalt(could_halt: Option[Bool]) // Triggered when there is an unrecoverable hardware error (halt the machine) def reportHalt(errors: Seq[CanHaveErrors]) // Varient for standard error bundle (Rocket specific: used only by cache when there's an ECC error) def reportCease(could_cease: Option[Bool], quiescenceCycles: Int = 8) // Triggered when the core stop retiring instructions (like clock gating) From 0a39819f442ce54d3b280870eab0d3e398235f6c Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 19 Jul 2020 21:46:32 -0700 Subject: [PATCH 15/16] Add source file note --- docs/Customization/Custom-Core.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index fa14fff6..4f529efc 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -10,6 +10,11 @@ instructions on how to achieve this. RoCC is currently not supported by cores other than Rocket and BOOM. Please use Rocket or BOOM as the RoCC base core if you need to use RoCC. +.. note:: + + This page contains links to the files that contains important definitions in the Rocket chip repository, which is maintained separately + from Chipyard. If you find any discrepency between the code on this page and the code in the source file, please report it through + GitHub issues! Wrap Verilog Module with Blackbox (Optional) -------------------------------------------- From 692b120b65ab72f6e3f1a7885efed74057b08747 Mon Sep 17 00:00:00 2001 From: Zitao Fang Date: Sun, 19 Jul 2020 21:48:07 -0700 Subject: [PATCH 16/16] Fixed typo --- docs/Customization/Custom-Core.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Customization/Custom-Core.rst b/docs/Customization/Custom-Core.rst index 4f529efc..a76741ec 100644 --- a/docs/Customization/Custom-Core.rst +++ b/docs/Customization/Custom-Core.rst @@ -13,7 +13,7 @@ instructions on how to achieve this. .. note:: This page contains links to the files that contains important definitions in the Rocket chip repository, which is maintained separately - from Chipyard. If you find any discrepency between the code on this page and the code in the source file, please report it through + from Chipyard. If you find any discrepancy between the code on this page and the code in the source file, please report it through GitHub issues! Wrap Verilog Module with Blackbox (Optional)