From: SiFive <> Date: Tue, 29 Nov 2016 12:08:44 +0000 (-0800) Subject: Initial commit. X-Git-Url: https://git.libre-soc.org/?p=sifive-blocks.git;a=commitdiff_plain;h=7916ef5249c72a3a84c599d123760f4d716de58a Initial commit. --- 7916ef5249c72a3a84c599d123760f4d716de58a diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..56daab2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 SiFive, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/main/scala/devices/gpio/GPIO.scala b/src/main/scala/devices/gpio/GPIO.scala new file mode 100644 index 0000000..73ba3d0 --- /dev/null +++ b/src/main/scala/devices/gpio/GPIO.scala @@ -0,0 +1,295 @@ +// See LICENSE for license details. +package sifive.blocks.devices.gpio + +import Chisel._ +import config._ +import regmapper._ +import uncore.tilelink2._ +import rocketchip.PeripheryBusConfig +import util.AsyncResetRegVec + +case class GPIOConfig(address: BigInt, width: Int) + +trait HasGPIOParameters { + val params: Tuple2[Parameters, GPIOConfig] + implicit val p = params._1 + val c = params._2 +} + +// YAGNI: Make the PUE, DS, and +// these also optionally HW controllable. +// This is the base class of things you "always" +// want to control from a HW block. +class GPIOCtrl extends Bundle { + val oval = Bool() + val oe = Bool() + val ie = Bool() +} + +// This is the actual IOF interface. +// Add a valid bit to indicate whether +// there is something actually connected +// to this. +class GPIOPinIOFCtrl extends GPIOCtrl { + val valid = Bool() +} + +// By default, +object GPIOPinIOFCtrl { + def apply(): GPIOPinIOFCtrl = { + val iof = Wire(new GPIOPinIOFCtrl()) + iof.valid := Bool(false) + iof.oval := Bool(false) + iof.oe := Bool(false) + iof.ie := Bool(false) + iof + } +} + +// This is the control for a physical +// Pad. + +class GPIOPinCtrl extends GPIOCtrl { + val pue = Bool() // Pull-up Enable + val ds = Bool() // Drive Strength +} + +object GPIOPinCtrl { + def apply(): GPIOPinCtrl = { + val pin = Wire(new GPIOPinCtrl()) + pin.oval := Bool(false) + pin.oe := Bool(false) + pin.pue := Bool(false) + pin.ds := Bool(false) + pin.ie := Bool(false) + pin + } +} + +// Package up the inputs and outputs +// for the IOF +class GPIOPinIOF extends Bundle { + val i = new Bundle { + val ival = Bool(INPUT) + } + val o = new GPIOPinIOFCtrl().asOutput +} + +// Connect both the i and o side of the pin, +// and drive the valid signal for the IOF. +object GPIOPinToIOF { + + def apply (pin: GPIOPin, iof: GPIOPinIOF): Unit = { + iof <> pin + iof.o.valid := Bool(true) + } + +} + +// Package up the inputs and outputs +// for the Pin +class GPIOPin extends Bundle { + val i = new Bundle { + val ival = Bool(INPUT) + } + val o = new GPIOPinCtrl().asOutput +} + +// This is sort of weird because +// the IOF end up at the RocketChipTop +// level, and we have to do the pinmux +// outside of RocketChipTop. + +class GPIOPortIO(c: GPIOConfig) extends Bundle { + val pins = Vec(c.width, new GPIOPin) + val iof_0 = Vec(c.width, new GPIOPinIOF).flip + val iof_1 = Vec(c.width, new GPIOPinIOF).flip +} + +// It would be better if the IOF were here and +// we could do the pinmux inside. +trait GPIOBundle extends Bundle with HasGPIOParameters { + val port = new GPIOPortIO(c) +} + +trait GPIOModule extends Module with HasGPIOParameters with HasRegMap { + val io: GPIOBundle + + //-------------------------------------------------- + // CSR Declarations + // ------------------------------------------------- + + // SW Control only. + val portReg = Reg(init = UInt(0, c.width)) + + val oeReg = Module(new AsyncResetRegVec(c.width, 0)) + val pueReg = Module(new AsyncResetRegVec(c.width, 0)) + val dsReg = Reg(init = UInt(0, c.width)) + val ieReg = Module(new AsyncResetRegVec(c.width, 0)) + + // Synchronize Input to get valueReg + val inVal = Wire(UInt(0, width=c.width)) + inVal := Vec(io.port.pins.map(_.i.ival)).asUInt + val inSyncReg = ShiftRegister(inVal, 3) + val valueReg = Reg(init = UInt(0, c.width), next = inSyncReg) + + // Interrupt Configuration + val highIeReg = Reg(init = UInt(0, c.width)) + val lowIeReg = Reg(init = UInt(0, c.width)) + val riseIeReg = Reg(init = UInt(0, c.width)) + val fallIeReg = Reg(init = UInt(0, c.width)) + val highIpReg = Reg(init = UInt(0, c.width)) + val lowIpReg = Reg(init = UInt(0, c.width)) + val riseIpReg = Reg(init = UInt(0, c.width)) + val fallIpReg = Reg(init = UInt(0, c.width)) + + // HW IO Function + val iofEnReg = Module(new AsyncResetRegVec(c.width, 0)) + val iofSelReg = Reg(init = UInt(0, c.width)) + + // Invert Output + val xorReg = Reg(init = UInt(0, c.width)) + + //-------------------------------------------------- + // CSR Access Logic (most of this section is boilerplate) + // ------------------------------------------------- + + val rise = ~valueReg & inSyncReg; + val fall = valueReg & ~inSyncReg; + + // Note that these are out of order. + regmap( + GPIOCtrlRegs.value -> Seq(RegField.r(c.width, valueReg)), + GPIOCtrlRegs.output_en -> Seq(RegField.rwReg(c.width, oeReg.io)), + GPIOCtrlRegs.rise_ie -> Seq(RegField(c.width, riseIeReg)), + GPIOCtrlRegs.rise_ip -> Seq(RegField.w1ToClear(c.width, riseIpReg, rise)), + GPIOCtrlRegs.fall_ie -> Seq(RegField(c.width, fallIeReg)), + GPIOCtrlRegs.fall_ip -> Seq(RegField.w1ToClear(c.width, fallIpReg, fall)), + GPIOCtrlRegs.high_ie -> Seq(RegField(c.width, highIeReg)), + GPIOCtrlRegs.high_ip -> Seq(RegField.w1ToClear(c.width, highIpReg, valueReg)), + GPIOCtrlRegs.low_ie -> Seq(RegField(c.width, lowIeReg)), + GPIOCtrlRegs.low_ip -> Seq(RegField.w1ToClear(c.width,lowIpReg, ~valueReg)), + GPIOCtrlRegs.port -> Seq(RegField(c.width, portReg)), + GPIOCtrlRegs.pullup_en -> Seq(RegField.rwReg(c.width, pueReg.io)), + GPIOCtrlRegs.iof_en -> Seq(RegField.rwReg(c.width, iofEnReg.io)), + GPIOCtrlRegs.iof_sel -> Seq(RegField(c.width, iofSelReg)), + GPIOCtrlRegs.drive -> Seq(RegField(c.width, dsReg)), + GPIOCtrlRegs.input_en -> Seq(RegField.rwReg(c.width, ieReg.io)), + GPIOCtrlRegs.out_xor -> Seq(RegField(c.width, xorReg)) + + ) + + //-------------------------------------------------- + // Actual Pinmux + // ------------------------------------------------- + + val swPinCtrl = Wire(Vec(c.width, new GPIOPinCtrl())) + + // This strips off the valid. + val iof0Ctrl = Wire(Vec(c.width, new GPIOCtrl())) + val iof1Ctrl = Wire(Vec(c.width, new GPIOCtrl())) + + val iofCtrl = Wire(Vec(c.width, new GPIOCtrl())) + val iofPlusSwPinCtrl = Wire(Vec(c.width, new GPIOPinCtrl())) + + + for (pin <- 0 until c.width) { + + // Software Pin Control + swPinCtrl(pin).pue := pueReg.io.q(pin) + swPinCtrl(pin).oval := portReg(pin) + swPinCtrl(pin).oe := oeReg.io.q(pin) + swPinCtrl(pin).ds := dsReg(pin) + swPinCtrl(pin).ie := ieReg.io.q(pin) + + // Allow SW Override for invalid inputs. + iof0Ctrl(pin) <> swPinCtrl(pin) + when (io.port.iof_0(pin).o.valid) { + iof0Ctrl(pin) <> io.port.iof_0(pin).o + } + + iof1Ctrl(pin) <> swPinCtrl(pin) + when (io.port.iof_1(pin).o.valid) { + iof1Ctrl(pin) <> io.port.iof_1(pin).o + } + + // Select IOF 0 vs. IOF 1. + iofCtrl(pin) <> Mux(iofSelReg(pin), iof1Ctrl(pin), iof0Ctrl(pin)) + + // Allow SW Override for things IOF doesn't control. + iofPlusSwPinCtrl(pin) <> swPinCtrl(pin) + iofPlusSwPinCtrl(pin) <> iofCtrl(pin) + + // Final XOR & Pin Control + val pre_xor: GPIOPinCtrl = Mux(iofEnReg.io.q(pin), iofPlusSwPinCtrl(pin), swPinCtrl(pin)) + io.port.pins(pin).o := pre_xor + io.port.pins(pin).o.oval := pre_xor.oval ^ xorReg(pin) + + // Generate Interrupts + interrupts(pin) := (riseIpReg(pin) & riseIeReg(pin)) | + (fallIpReg(pin) & fallIeReg(pin)) | + (highIpReg(pin) & highIeReg(pin)) | + (lowIpReg(pin) & lowIeReg(pin)) + + // Send Value to all consumers + io.port.iof_0(pin).i.ival := inSyncReg(pin) + io.port.iof_1(pin).i.ival := inSyncReg(pin) + } +} + +object GPIOOutputPinCtrl { + + def apply( pin: GPIOPin, signal: Bool, + pue: Bool = Bool(false), + ds: Bool = Bool(false), + ie: Bool = Bool(false) + ): Unit = { + pin.o.oval := signal + pin.o.oe := Bool(true) + pin.o.pue := pue + pin.o.ds := ds + pin.o.ie := ie + } + + def apply(pins: Vec[GPIOPin], signals: Bits, + pue: Bool, ds: Bool, ie: Bool + ): Unit = { + for ((signal, pin) <- (signals.toBools zip pins)) { + apply(pin, signal, pue, ds, ie) + } + } + + def apply(pins: Vec[GPIOPin], signals: Bits): Unit = apply(pins, signals, + Bool(false), Bool(false), Bool(false)) + +} + +object GPIOInputPinCtrl { + + def apply (pin: GPIOPin, pue: Bool = Bool(false)): Bool = { + pin.o.oval := Bool(false) + pin.o.oe := Bool(false) + pin.o.pue := pue + pin.o.ds := Bool(false) + pin.o.ie := Bool(true) + + pin.i.ival + } + + def apply (pins: Vec[GPIOPin], pue: Bool): Vec[Bool] = { + val signals = Wire(Vec.fill(pins.size)(Bool(false))) + for ((signal, pin) <- (signals zip pins)) { + signal := GPIOInputPinCtrl(pin, pue) + } + signals + } + + def apply (pins: Vec[GPIOPin]): Vec[Bool] = apply(pins, Bool(false)) + +} + +// Magic TL2 Incantation to create a TL2 Slave +class TLGPIO(p: Parameters, c: GPIOConfig) + extends TLRegisterRouter(c.address, interrupts = c.width, beatBytes = p(PeripheryBusConfig).beatBytes)( + new TLRegBundle(Tuple2(p, c), _) with GPIOBundle)( + new TLRegModule(Tuple2(p, c), _, _) with GPIOModule) diff --git a/src/main/scala/devices/gpio/GPIOCtrlRegs.scala b/src/main/scala/devices/gpio/GPIOCtrlRegs.scala new file mode 100644 index 0000000..fda6e88 --- /dev/null +++ b/src/main/scala/devices/gpio/GPIOCtrlRegs.scala @@ -0,0 +1,22 @@ +// See LICENSE for license details. +package sifive.blocks.devices.gpio + +object GPIOCtrlRegs { + val value = 0x00 + val input_en = 0x04 + val output_en = 0x08 + val port = 0x0c + val pullup_en = 0x10 + val drive = 0x14 + val rise_ie = 0x18 + val rise_ip = 0x1c + val fall_ie = 0x20 + val fall_ip = 0x24 + val high_ie = 0x28 + val high_ip = 0x2c + val low_ie = 0x30 + val low_ip = 0x34 + val iof_en = 0x38 + val iof_sel = 0x3c + val out_xor = 0x40 +} diff --git a/src/main/scala/devices/gpio/GPIOPeriphery.scala b/src/main/scala/devices/gpio/GPIOPeriphery.scala new file mode 100644 index 0000000..414e713 --- /dev/null +++ b/src/main/scala/devices/gpio/GPIOPeriphery.scala @@ -0,0 +1,28 @@ +// See LICENSE for license details. +package sifive.blocks.devices.gpio + +import Chisel._ +import diplomacy.LazyModule +import rocketchip.{TopNetwork,TopNetworkModule} +import uncore.tilelink2.TLFragmenter + +trait PeripheryGPIO { + this: TopNetwork { val gpioConfig: GPIOConfig } => + val gpio = LazyModule(new TLGPIO(p, gpioConfig)) + gpio.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + intBus.intnode := gpio.intnode +} + +trait PeripheryGPIOBundle { + this: { val gpioConfig: GPIOConfig } => + val gpio = new GPIOPortIO(gpioConfig) +} + +trait PeripheryGPIOModule { + this: TopNetworkModule { + val gpioConfig: GPIOConfig + val outer: PeripheryGPIO + val io: PeripheryGPIOBundle + } => + io.gpio <> outer.gpio.module.io.port +} diff --git a/src/main/scala/devices/gpio/JTAG.scala b/src/main/scala/devices/gpio/JTAG.scala new file mode 100644 index 0000000..8734539 --- /dev/null +++ b/src/main/scala/devices/gpio/JTAG.scala @@ -0,0 +1,43 @@ +// See LICENSE for license details. +package sifive.blocks.devices.gpio + +import Chisel._ + +// ------------------------------------------------------------ +// SPI, UART, etc are with their +// respective packages, +// This file is for those that don't seem to have a good place +// to put them otherwise. +// ------------------------------------------------------------ + +import config._ +import junctions.{JTAGIO} + +class JTAGPinsIO extends Bundle { + + val TCK = new GPIOPin() + val TMS = new GPIOPin() + val TDI = new GPIOPin() + val TDO = new GPIOPin() + val TRST_n = new GPIOPin() + +} + +class JTAGGPIOPort(drvTdo: Boolean = false)(implicit p: Parameters) extends Module { + + val io = new Bundle { + val jtag = new JTAGIO(drvTdo) + val pins = new JTAGPinsIO() + } + + io.jtag.TCK := GPIOInputPinCtrl(io.pins.TCK, pue = Bool(true)).asClock + io.jtag.TMS := GPIOInputPinCtrl(io.pins.TMS, pue = Bool(true)) + io.jtag.TDI := GPIOInputPinCtrl(io.pins.TDI, pue = Bool(true)) + io.jtag.TRST := ~GPIOInputPinCtrl(io.pins.TRST_n, pue = Bool(true)) + + GPIOOutputPinCtrl(io.pins.TDO, io.jtag.TDO) + if (drvTdo) { + io.pins.TDO.o.oe := io.jtag.DRV_TDO.get + } + +} diff --git a/src/main/scala/devices/mockaon/MockAON.scala b/src/main/scala/devices/mockaon/MockAON.scala new file mode 100644 index 0000000..914a92c --- /dev/null +++ b/src/main/scala/devices/mockaon/MockAON.scala @@ -0,0 +1,105 @@ +// See LICENSE for license details. +package sifive.blocks.devices.mockaon + +import Chisel._ +import config._ +import regmapper._ +import uncore.tilelink2._ +import rocketchip.PeripheryBusConfig + +import sifive.blocks.util.GenericTimer + +case class MockAONConfig( + address: BigInt = BigInt(0x10000000), + nBackupRegs: Int = 16) { + def size: Int = 0x1000 + def regBytes: Int = 4 + def wdogOffset: Int = 0 + def rtcOffset: Int = 0x40 + def backupRegOffset: Int = 0x80 + def pmuOffset: Int = 0x100 +} + +trait HasMockAONParameters { + val params: (MockAONConfig, Parameters) + val c = params._1 + implicit val p = params._2 +} + +class MockAONPMUIO extends Bundle { + val vddpaden = Bool(OUTPUT) + val dwakeup = Bool(INPUT) +} + +class MockAONMOffRstIO extends Bundle { + val hfclkrst = Bool(OUTPUT) + val corerst = Bool(OUTPUT) +} + +trait MockAONBundle extends Bundle with HasMockAONParameters { + + // Output of the Power Management Sequencer + val moff = new MockAONMOffRstIO () + + // This goes out to wrapper + // to be combined to create aon_rst. + val wdog_rst = Bool(OUTPUT) + + // This goes out to wrapper + // and comes back as our clk + val lfclk = Clock(OUTPUT) + + val pmu = new MockAONPMUIO + + val lfextclk = Clock(INPUT) + + val resetCauses = new ResetCauses().asInput +} + +trait MockAONModule extends Module with HasRegMap with HasMockAONParameters { + val io: MockAONBundle + + // the expectation here is that Chisel's implicit reset is aonrst, + // which is asynchronous, so don't use synchronous-reset registers. + + val rtc = Module(new RTC) + + val pmu = Module(new PMU(new DevKitPMUConfig)) + io.moff <> pmu.io.control + io.pmu.vddpaden := pmu.io.control.vddpaden + pmu.io.wakeup.dwakeup := io.pmu.dwakeup + pmu.io.wakeup.awakeup := Bool(false) + pmu.io.wakeup.rtc := rtc.io.ip(0) + pmu.io.resetCauses := io.resetCauses + val pmuRegMap = { + val regs = pmu.io.regs.wakeupProgram ++ pmu.io.regs.sleepProgram ++ + Seq(pmu.io.regs.ie, pmu.io.regs.cause, pmu.io.regs.sleep, pmu.io.regs.key) + for ((r, i) <- regs.zipWithIndex) + yield (c.pmuOffset + c.regBytes*i) -> Seq(r.toRegField()) + } + interrupts(1) := rtc.io.ip(0) + + val wdog = Module(new WatchdogTimer) + io.wdog_rst := wdog.io.rst + wdog.io.corerst := pmu.io.control.corerst + interrupts(0) := wdog.io.ip(0) + + // If there are multiple lfclks to choose from, we can mux them here. + io.lfclk := io.lfextclk + + val backupRegs = Seq.fill(c.nBackupRegs)(Reg(UInt(width = c.regBytes * 8))) + val backupRegMap = + for ((reg, i) <- backupRegs.zipWithIndex) + yield (c.backupRegOffset + c.regBytes*i) -> Seq(RegField(reg.getWidth, RegReadFn(reg), RegWriteFn(reg))) + + regmap((backupRegMap ++ + GenericTimer.timerRegMap(wdog, c.wdogOffset, c.regBytes) ++ + GenericTimer.timerRegMap(rtc, c.rtcOffset, c.regBytes) ++ + pmuRegMap):_*) + +} + +class MockAON(c: MockAONConfig)(implicit val p: Parameters) + extends TLRegisterRouter(c.address, interrupts = 2, size = c.size, beatBytes = p(PeripheryBusConfig).beatBytes, concurrency = 1)( + new TLRegBundle((c, p), _) with MockAONBundle)( + new TLRegModule((c, p), _, _) with MockAONModule) diff --git a/src/main/scala/devices/mockaon/MockAONPeriphery.scala b/src/main/scala/devices/mockaon/MockAONPeriphery.scala new file mode 100644 index 0000000..870ba2e --- /dev/null +++ b/src/main/scala/devices/mockaon/MockAONPeriphery.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details. +package sifive.blocks.devices.mockaon + +import Chisel._ +import diplomacy.LazyModule +import rocketchip.{TopNetwork,TopNetworkModule} +import uncore.tilelink2.{IntXing, TLAsyncCrossingSource, TLFragmenter} +import coreplex._ + +trait PeripheryMockAON extends TopNetwork { + val mockAONConfig: MockAONConfig + val coreplex: CoreplexRISCVPlatform + + // We override the clock & Reset here so that all synchronizers, etc + // are in the proper clock domain. + val aon = LazyModule(new MockAONWrapper(mockAONConfig)) + val aon_int = LazyModule(new IntXing) + aon.node := TLAsyncCrossingSource()(TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)) + aon_int.intnode := aon.intnode + intBus.intnode := aon_int.intnode +} + +trait PeripheryMockAONBundle { + val aon = new MockAONWrapperBundle() +} + +trait PeripheryMockAONModule { + this: TopNetworkModule { + val outer: PeripheryMockAON + val io: PeripheryMockAONBundle + } => + + io.aon <> outer.aon.module.io + + // Explicit clock & reset are unused in MockAONWrapper. + // Tie to check this assumption. + outer.aon.module.clock := Bool(false).asClock + outer.aon.module.reset := Bool(true) + + outer.coreplex.module.io.rtcToggle := outer.aon.module.io.rtc.asUInt.toBool + +} diff --git a/src/main/scala/devices/mockaon/MockAONWrapper.scala b/src/main/scala/devices/mockaon/MockAONWrapper.scala new file mode 100644 index 0000000..15d755d --- /dev/null +++ b/src/main/scala/devices/mockaon/MockAONWrapper.scala @@ -0,0 +1,181 @@ +// See LICENSE for license details. +package sifive.blocks.devices.mockaon + +import Chisel._ +import config._ +import diplomacy._ +import uncore.tilelink2._ +import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl, GPIOInputPinCtrl} +import sifive.blocks.util.{DeglitchShiftRegister, ResetCatchAndSync} +import util._ +/* The wrapper handles the Clock and Reset Generation for The AON block itself, + and instantiates real pad controls (aka pull-ups)*/ + +class MockAONWrapperPMUIO extends Bundle { + val dwakeup_n = new GPIOPin() + val vddpaden = new GPIOPin() +} + +class MockAONWrapperPadsIO extends Bundle { + val erst_n = new GPIOPin() + val lfextclk = new GPIOPin() + val pmu = new MockAONWrapperPMUIO() +} + +class MockAONWrapperBundle extends Bundle { + val pads = new MockAONWrapperPadsIO() + val rsts = new MockAONMOffRstIO() +} + +class MockAONWrapper(c: MockAONConfig)(implicit val p: Parameters) extends LazyModule { + + val node = TLAsyncInputNode() + val intnode = IntOutputNode() + val aon = LazyModule (new MockAON(c)(p)) + + // We only need to isolate the signals + // coming from MOFF to AON, + // since AON is never off while MOFF is on. + // The MOFF is on the "in" side of the Isolation. + // AON is on the "out" side of the Isolation. + + def isoOut(iso: Bool, x: UInt): UInt = IsoZero(iso, x) + def isoIn(iso: Bool, x: UInt): UInt = x + val isolation = LazyModule(new TLIsolation(fOut = isoOut, fIn = isoIn)) + val crossing = LazyModule(new TLAsyncCrossingSink(depth = 1)) + + isolation.node := node + crossing.node := isolation.node + val crossing_monitor = (aon.node := crossing.node) + + // crossing lives outside in Periphery + intnode := aon.intnode + + lazy val module = new LazyModuleImp(this) { + val io = new MockAONWrapperBundle { + val in = node.bundleIn + val ip = intnode.bundleOut + val rtc = Clock(OUTPUT) + } + + val aon_io = aon.module.io + val pads = io.pads + + // ----------------------------------------------- + // Generation of aonrst + // ----------------------------------------------- + + // ERST + val erst = ~ GPIOInputPinCtrl(pads.erst_n, pue = Bool(true)) + aon_io.resetCauses.erst := erst + aon_io.resetCauses.wdogrst := aon_io.wdog_rst + + // PORRST + val porrst = Bool(false) // TODO + aon_io.resetCauses.porrst := porrst + + //-------------------------------------------------- + // Drive "Mostly Off" Reset Signals (these + // are synchronized inside MOFF as needed) + //-------------------------------------------------- + + io.rsts.hfclkrst := aon_io.moff.hfclkrst + io.rsts.corerst := aon_io.moff.corerst + + //-------------------------------------------------- + // Generate the LFCLK input to AON + // This is the same clock that is driven to this + // block as 'clock'. + //-------------------------------------------------- + + // LFCLK Override + // Note that the actual mux lives inside AON itself. + // Therefore, the lfclk which comes out of AON is the + // true clock that AON and AONWrapper are running off of. + val lfextclk = GPIOInputPinCtrl(pads.lfextclk, pue=Bool(true)) + aon_io.lfextclk := lfextclk.asClock + + // Drive AON's clock and Reset + val lfclk = aon_io.lfclk + + val aonrst_catch = Module (new ResetCatchAndSync(3)) + aonrst_catch.reset := erst | aon_io.wdog_rst + aonrst_catch.clock := lfclk + aon.module.reset := aonrst_catch.io.sync_reset + + aon.module.clock := lfclk + + //-------------------------------------------------- + // TL2 Register Access Interface + //-------------------------------------------------- + + // Safely cross TL2 into AON Domain + // Ensure that both are reset and clocked + // at the same time. + // Note that aon.moff.corerst is synchronous + // to aon.module.clock, so this is safe. + val crossing_slave_reset = ResetCatchAndSync(lfclk, + aon.module.io.moff.corerst | aon.module.reset) + + crossing.module.clock := lfclk + crossing.module.reset := crossing_slave_reset + + crossing_monitor.foreach { lm => + lm.module.clock := lfclk + lm.module.reset := crossing_slave_reset + } + + // Note that aon.moff.corerst is synchronous + // to aon.module.clock, so this is safe. + isolation.module.io.iso_out := aon.module.io.moff.corerst + isolation.module.io.iso_in := Bool(true) + + //-------------------------------------------------- + // PMU <--> pads Interface + //-------------------------------------------------- + + val dwakeup_n_async = GPIOInputPinCtrl(pads.pmu.dwakeup_n, pue=Bool(true)) + + val dwakeup_deglitch = Module (new DeglitchShiftRegister(3)) + dwakeup_deglitch.clock := lfclk + dwakeup_deglitch.io.d := ~dwakeup_n_async + aon.module.io.pmu.dwakeup := dwakeup_deglitch.io.q + + GPIOOutputPinCtrl(pads.pmu.vddpaden, aon.module.io.pmu.vddpaden) + + //-------------------------------------------------- + // Connect signals to MOFF + //-------------------------------------------------- + + io.rtc := aon_io.lfclk + } + +} + +// ----------------------------------------------- +// Isolation Cells +// ----------------------------------------------- + +class IsoZero extends Module { + val io = new Bundle { + val in = Bool(INPUT) + val iso = Bool(INPUT) + val out = Bool(OUTPUT) + } + io.out := io.in & ~io.iso +} + +object IsoZero { + def apply (iso: Bool, in: UInt): UInt = { + + val w = in.getWidth + val isos: List[IsoZero] = List.tabulate(in.getWidth)( + x => Module(new IsoZero).suggestName(s"iso_$x") + ) + for ((z, i) <- isos.zipWithIndex) { + z.io.in := in(i) + z.io.iso := iso + } + isos.map(_.io.out).asUInt + } +} diff --git a/src/main/scala/devices/mockaon/PMU.scala b/src/main/scala/devices/mockaon/PMU.scala new file mode 100644 index 0000000..2c7964a --- /dev/null +++ b/src/main/scala/devices/mockaon/PMU.scala @@ -0,0 +1,152 @@ +// See LICENSE for license details. +package sifive.blocks.devices.mockaon + +import Chisel._ +import Chisel.ImplicitConversions._ +import util._ +import sifive.blocks.util.SRLatch + +import sifive.blocks.util.{SlaveRegIF} + +class WakeupCauses extends Bundle { + val awakeup = Bool() + val dwakeup = Bool() + val rtc = Bool() + val reset = Bool() +} + +class ResetCauses extends Bundle { + val wdogrst = Bool() + val erst = Bool() + val porrst = Bool() +} + +class PMUSignals extends Bundle { + val hfclkrst = Bool() + val corerst = Bool() + val reserved1 = Bool() + val vddpaden = Bool() + val reserved0 = Bool() +} + +class PMUInstruction extends Bundle { + val sigs = new PMUSignals + val dt = UInt(width = 4) +} + +class PMUConfig(wakeupProgramIn: Seq[Int], + sleepProgramIn: Seq[Int]) { + val programLength = 8 + val nWakeupCauses = new WakeupCauses().elements.size + val wakeupProgram = wakeupProgramIn.padTo(programLength, wakeupProgramIn.last) + val sleepProgram = sleepProgramIn.padTo(programLength, sleepProgramIn.last) + require(wakeupProgram.length == programLength) + require(sleepProgram.length == programLength) +} + +class DevKitPMUConfig extends PMUConfig( // TODO + Seq(0x1f0, 0x0f8, 0x030), + Seq(0x0f0, 0x1f0, 0x1d0, 0x1c0)) + +class PMURegs(c: PMUConfig) extends Bundle { + val ie = new SlaveRegIF(c.nWakeupCauses) + val cause = new SlaveRegIF(32) + val sleep = new SlaveRegIF(32) + val key = new SlaveRegIF(32) + val wakeupProgram = Vec(c.programLength, new SlaveRegIF(32)) + val sleepProgram = Vec(c.programLength, new SlaveRegIF(32)) +} + +class PMUCore(c: PMUConfig)(resetIn: Bool) extends Module(_reset = resetIn) { + val io = new Bundle { + val wakeup = new WakeupCauses().asInput + val control = Valid(new PMUSignals) + val resetCause = UInt(INPUT, log2Ceil(new ResetCauses().getWidth)) + val regs = new PMURegs(c) + } + + val run = Reg(init = Bool(true)) + val awake = Reg(init = Bool(true)) + val unlocked = { + val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key) + RegEnable(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, Bool(false), io.regs.key.write.valid || writeAny) + } + val wantSleep = RegEnable(Bool(true), Bool(false), io.regs.sleep.write.valid && unlocked) + val pc = Reg(init = UInt(0, log2Ceil(c.programLength))) + val wakeupCause = Reg(init = UInt(0, log2Ceil(c.nWakeupCauses))) + val ie = RegEnable(io.regs.ie.write.bits, io.regs.ie.write.valid && unlocked) | 1 /* POR always enabled */ + + val insnWidth = new PMUInstruction().getWidth + val wakeupProgram = c.wakeupProgram.map(v => Reg(init = UInt(v, insnWidth))) + val sleepProgram = c.sleepProgram.map(v => Reg(init = UInt(v, insnWidth))) + val insnBits = Mux(awake, wakeupProgram(pc), sleepProgram(pc)) + val insn = new PMUInstruction().fromBits(insnBits) + + val count = Reg(init = UInt(0, 1 << insn.dt.getWidth)) + val tick = (count ^ (count + 1))(insn.dt) + val npc = pc +& 1 + val last = npc >= c.programLength + io.control.valid := run && !last && tick + io.control.bits := insn.sigs + + when (run) { + count := count + 1 + when (tick) { + count := 0 + + require(isPow2(c.programLength)) + run := !last + pc := npc + } + }.otherwise { + val maskedWakeupCauses = ie & io.wakeup.asUInt + when (!awake && maskedWakeupCauses.orR) { + run := true + awake := true + wakeupCause := PriorityEncoder(maskedWakeupCauses) + } + when (awake && wantSleep) { + run := true + awake := false + wantSleep := false + } + } + + io.regs.cause.read := wakeupCause | (io.resetCause << 8) + io.regs.ie.read := ie + io.regs.key.read := unlocked + io.regs.sleep.read := 0 + + for ((port, reg) <- (io.regs.wakeupProgram ++ io.regs.sleepProgram) zip (wakeupProgram ++ sleepProgram)) { + port.read := reg + when (port.write.valid && unlocked) { reg := port.write.bits } + } +} + +class PMU(val c: PMUConfig) extends Module { + val io = new Bundle { + val wakeup = new WakeupCauses().asInput + val control = new PMUSignals().asOutput + val regs = new PMURegs(c) + val resetCauses = new ResetCauses().asInput + } + + val core = Module(new PMUCore(c)(resetIn = Reg(next = Reg(next = reset)))) + io <> core.io + core.io.wakeup.reset := false // this is implied by resetting the PMU + + // during aonrst, hold all control signals high + val latch = ~AsyncResetReg(~core.io.control.bits.asUInt, core.io.control.valid) + io.control := io.control.fromBits(latch) + + core.io.resetCause := { + val cause = io.resetCauses.asUInt + val latches = for (i <- 0 until cause.getWidth) yield { + val latch = Module(new SRLatch) + latch.io.set := cause(i) + latch.io.reset := (0 until cause.getWidth).filter(_ != i).map(cause(_)).reduce(_||_) + latch.io.q + } + OHToUInt(latches) + } +} diff --git a/src/main/scala/devices/mockaon/WatchdogTimer.scala b/src/main/scala/devices/mockaon/WatchdogTimer.scala new file mode 100644 index 0000000..5383dbe --- /dev/null +++ b/src/main/scala/devices/mockaon/WatchdogTimer.scala @@ -0,0 +1,58 @@ +// See LICENSE for license details. +package sifive.blocks.devices.mockaon + +import Chisel._ +import Chisel.ImplicitConversions._ +import util.AsyncResetReg + +import sifive.blocks.util.{SlaveRegIF, GenericTimer} + +object WatchdogTimer { + def writeAnyExceptKey(regs: Bundle, keyReg: SlaveRegIF): Bool = { + regs.elements.values.filter(_ ne keyReg).map({ + case v: Vec[SlaveRegIF] @unchecked => v.map(_.write.valid).reduce(_||_) + case s: SlaveRegIF => s.write.valid + }).reduce(_||_) + } + + val key = 0x51F15E +} + +class WatchdogTimer extends GenericTimer { + protected def countWidth = 31 + protected def cmpWidth = 16 + protected def ncmp = 1 + protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.bits(12), io.regs.cfg.write.valid && unlocked)(0) + override protected lazy val countAwake = AsyncResetReg(io.regs.cfg.write.bits(13), io.regs.cfg.write.valid && unlocked)(0) + protected lazy val countEn = { + val corerstSynchronized = Reg(next = Reg(next = io.corerst)) + countAlways || (countAwake && !corerstSynchronized) + } + override protected lazy val rsten = AsyncResetReg(io.regs.cfg.write.bits(8), io.regs.cfg.write.valid && unlocked)(0) + protected lazy val ip = RegEnable(io.regs.cfg.write.bits(28) || elapsed(0), (io.regs.cfg.write.valid && unlocked) || elapsed(0)) + override protected lazy val unlocked = { + val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key) + AsyncResetReg(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, io.regs.key.write.valid || writeAny)(0) + } + protected lazy val feed = { + val food = 0xD09F00D + unlocked && io.regs.feed.write.valid && io.regs.feed.write.bits === food + } + lazy val io = new GenericTimerIO { + val corerst = Bool(INPUT) + val rst = Bool(OUTPUT) + } + io.rst := AsyncResetReg(Bool(true), rsten && elapsed(0)) +} + +class RTC extends GenericTimer { + protected def countWidth = 48 + protected def cmpWidth = 32 + protected def ncmp = 1 + protected def countEn = countAlways + override protected lazy val ip = Reg(next = elapsed(0)) + override protected lazy val zerocmp = Bool(false) + protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.bits(12), io.regs.cfg.write.valid && unlocked)(0) + protected lazy val feed = Bool(false) + lazy val io = new GenericTimerIO +} diff --git a/src/main/scala/devices/pwm/PWM.scala b/src/main/scala/devices/pwm/PWM.scala new file mode 100644 index 0000000..2fd870c --- /dev/null +++ b/src/main/scala/devices/pwm/PWM.scala @@ -0,0 +1,82 @@ +// See LICENSE for license details. +package sifive.blocks.devices.pwm + +import Chisel._ +import Chisel.ImplicitConversions._ +import config._ +import regmapper._ +import rocketchip.PeripheryBusConfig +import uncore.tilelink2._ +import util._ + +import sifive.blocks.util.GenericTimer + +// Core PWM Functionality & Register Interface + +class PWM(val ncmp: Int = 4, val cmpWidth: Int = 16)(implicit p: Parameters) extends GenericTimer { + protected def countWidth = ((1 << scaleWidth) - 1) + cmpWidth + protected lazy val countAlways = RegEnable(io.regs.cfg.write.bits(12), Bool(false), io.regs.cfg.write.valid && unlocked) + protected lazy val feed = count.carryOut(scale + UInt(cmpWidth)) + protected lazy val countEn = Wire(Bool()) + override protected lazy val oneShot = RegEnable(io.regs.cfg.write.bits(13) && !countReset, Bool(false), (io.regs.cfg.write.valid && unlocked) || countReset) + override protected lazy val center = RegEnable(io.regs.cfg.write.bits(16 + ncmp - 1, 16), io.regs.cfg.write.valid && unlocked) + override protected lazy val gang = RegEnable(io.regs.cfg.write.bits(24 + ncmp - 1, 24), io.regs.cfg.write.valid && unlocked) + override protected lazy val deglitch = RegEnable(io.regs.cfg.write.bits(10), io.regs.cfg.write.valid && unlocked)(0) + override protected lazy val sticky = RegEnable(io.regs.cfg.write.bits(8), io.regs.cfg.write.valid && unlocked)(0) + override protected lazy val ip = { + val doSticky = Reg(next = (deglitch && !countReset) || sticky) + val sel = ((0 until ncmp).map(i => s(cmpWidth-1) && center(i))).asUInt + val reg = Reg(UInt(width = ncmp)) + reg := (sel & elapsed.asUInt) | (~sel & (elapsed.asUInt | (Fill(ncmp, doSticky) & reg))) + when (io.regs.cfg.write.valid && unlocked) { reg := io.regs.cfg.write.bits(28 + ncmp - 1, 28) } + reg + } + lazy val io = new GenericTimerIO { + val gpio = Vec(ncmp, Bool()).asOutput + } + io.gpio := io.gpio.fromBits(ip & ~(gang & Cat(ip(0), ip >> 1))) + countEn := countAlways || oneShot +} + +case class PWMConfig( + address: BigInt, + size: Int = 0x1000, + regBytes: Int = 4, + ncmp: Int = 4, + cmpWidth: Int = 16) +{ + val bc = new PWMBundleConfig(ncmp) +} + +case class PWMBundleConfig( + ncmp: Int) +{ + def union(that: PWMBundleConfig): PWMBundleConfig = + PWMBundleConfig(scala.math.max(ncmp, that.ncmp)) +} + +trait HasPWMParameters { + val params: (PWMConfig, Parameters) + val c = params._1 + implicit val p = params._2 +} + +trait PWMBundle extends Bundle with HasPWMParameters { + val gpio = Vec(c.ncmp, Bool()).asOutput +} + +trait PWMModule extends Module with HasRegMap with HasPWMParameters { + val io: PWMBundle + + val pwm = Module(new PWM(c.ncmp, c.cmpWidth)) + + interrupts := pwm.io.ip + io.gpio := pwm.io.gpio + + regmap((GenericTimer.timerRegMap(pwm, 0, c.regBytes)):_*) +} + +class TLPWM(c: PWMConfig)(implicit val p: Parameters) + extends TLRegisterRouter(c.address, interrupts = c.ncmp, size = c.size, beatBytes = p(PeripheryBusConfig).beatBytes)( + new TLRegBundle((c, p), _) with PWMBundle)( + new TLRegModule((c, p), _, _) with PWMModule) diff --git a/src/main/scala/devices/pwm/PWMPeriphery.scala b/src/main/scala/devices/pwm/PWMPeriphery.scala new file mode 100644 index 0000000..992699f --- /dev/null +++ b/src/main/scala/devices/pwm/PWMPeriphery.scala @@ -0,0 +1,58 @@ +// See LICENSE for license details. +package sifive.blocks.devices.pwm + +import Chisel._ +import config._ +import diplomacy.LazyModule +import rocketchip.{TopNetwork,TopNetworkModule} +import uncore.tilelink2.TLFragmenter + +import sifive.blocks.devices.gpio._ + +class PWMPortIO(c: PWMBundleConfig)(implicit p: Parameters) extends Bundle { + val port = Vec(c.ncmp, Bool()).asOutput + override def cloneType: this.type = new PWMPortIO(c).asInstanceOf[this.type] +} + +class PWMPinsIO(c: PWMBundleConfig)(implicit p: Parameters) extends Bundle { + val pwm = Vec(c.ncmp, new GPIOPin) +} + +class PWMGPIOPort(c: PWMBundleConfig)(implicit p: Parameters) extends Module { + val io = new Bundle { + val pwm = new PWMPortIO(c).flip() + val pins = new PWMPinsIO(c) + } + + GPIOOutputPinCtrl(io.pins.pwm, io.pwm.port.asUInt) +} + +trait PeripheryPWM { + this: TopNetwork { val pwmConfigs: Seq[PWMConfig] } => + + val pwmDevices = (pwmConfigs.zipWithIndex) map { case (c, i) => + val pwm = LazyModule(new TLPWM(c) { override lazy val valName = Some(s"pwm$i") }) + pwm.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + intBus.intnode := pwm.intnode + pwm + } +} + +trait PeripheryPWMBundle { + this: { + val p: Parameters + val pwmConfigs: Seq[PWMConfig] + } => + val pwm_bc = pwmConfigs.map(_.bc).reduce(_.union(_)) + val pwms = Vec(pwmConfigs.size, new PWMPortIO(pwm_bc)(p)) +} + +trait PeripheryPWMModule { + this: TopNetworkModule { + val outer: PeripheryPWM + val io: PeripheryPWMBundle + } => + (io.pwms.zipWithIndex zip outer.pwmDevices) foreach { case ((io, i), device) => + io.port := device.module.io.gpio + } +} diff --git a/src/main/scala/devices/spi/SPIArbiter.scala b/src/main/scala/devices/spi/SPIArbiter.scala new file mode 100644 index 0000000..b47cea3 --- /dev/null +++ b/src/main/scala/devices/spi/SPIArbiter.scala @@ -0,0 +1,40 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +class SPIInnerIO(c: SPIConfigBase) extends SPILinkIO(c) { + val lock = Bool(OUTPUT) +} + +class SPIArbiter(c: SPIConfigBase, n: Int) extends Module { + val io = new Bundle { + val inner = Vec(n, new SPIInnerIO(c)).flip + val outer = new SPILinkIO(c) + val sel = UInt(INPUT, log2Up(n)) + } + + val sel = Reg(init = Vec(Bool(true) +: Seq.fill(n-1)(Bool(false)))) + + io.outer.tx.valid := Mux1H(sel, io.inner.map(_.tx.valid)) + io.outer.tx.bits := Mux1H(sel, io.inner.map(_.tx.bits)) + io.outer.cnt := Mux1H(sel, io.inner.map(_.cnt)) + io.outer.fmt := Mux1H(sel, io.inner.map(_.fmt)) + io.outer.cs := Mux1H(sel, io.inner.map(_.cs)) + + (io.inner zip sel).foreach { case (inner, s) => + inner.tx.ready := io.outer.tx.ready && s + inner.rx.valid := io.outer.rx.valid && s + inner.rx.bits := io.outer.rx.bits + inner.active := io.outer.active && s + } + + val nsel = Vec.tabulate(n)(io.sel === UInt(_)) + val lock = Mux1H(sel, io.inner.map(_.lock)) + when (!lock) { + sel := nsel + when (sel.asUInt =/= nsel.asUInt) { + io.outer.cs.clear := Bool(true) + } + } +} diff --git a/src/main/scala/devices/spi/SPIBundle.scala b/src/main/scala/devices/spi/SPIBundle.scala new file mode 100644 index 0000000..ed7a679 --- /dev/null +++ b/src/main/scala/devices/spi/SPIBundle.scala @@ -0,0 +1,106 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +abstract class SPIBundle(val c: SPIConfigBase) extends Bundle { + override def cloneType: SPIBundle.this.type = + this.getClass.getConstructors.head.newInstance(c).asInstanceOf[this.type] +} + +class SPIDataIO extends Bundle { + val i = Bool(INPUT) + val o = Bool(OUTPUT) + val oe = Bool(OUTPUT) +} + +class SPIPortIO(c: SPIConfigBase) extends SPIBundle(c) { + val sck = Bool(OUTPUT) + val dq = Vec(4, new SPIDataIO) + val cs = Vec(c.csWidth, Bool(OUTPUT)) +} + +trait HasSPIProtocol { + val proto = Bits(width = SPIProtocol.width) +} +trait HasSPIEndian { + val endian = Bits(width = SPIEndian.width) +} +class SPIFormat(c: SPIConfigBase) extends SPIBundle(c) + with HasSPIProtocol + with HasSPIEndian { + val iodir = Bits(width = SPIDirection.width) +} + +trait HasSPILength extends SPIBundle { + val len = UInt(width = c.lengthBits) +} + +class SPIClocking(c: SPIConfigBase) extends SPIBundle(c) { + val div = UInt(width = c.divisorBits) + val pol = Bool() + val pha = Bool() +} + +class SPIChipSelect(c: SPIConfigBase) extends SPIBundle(c) { + val id = UInt(width = c.csIdBits) + val dflt = Vec(c.csWidth, Bool()) + + def toggle(en: Bool): Vec[Bool] = { + val mask = en << id + val out = Cat(dflt.reverse) ^ mask + Vec.tabulate(c.csWidth)(out(_)) + } +} + +trait HasSPICSMode { + val mode = Bits(width = SPICSMode.width) +} + +class SPIDelay(c: SPIConfigBase) extends SPIBundle(c) { + val cssck = UInt(width = c.delayBits) + val sckcs = UInt(width = c.delayBits) + val intercs = UInt(width = c.delayBits) + val interxfr = UInt(width = c.delayBits) +} + +class SPIWatermark(c: SPIConfigBase) extends SPIBundle(c) { + val tx = UInt(width = c.txDepthBits) + val rx = UInt(width = c.rxDepthBits) +} + +class SPIControl(c: SPIConfigBase) extends SPIBundle(c) { + val fmt = new SPIFormat(c) with HasSPILength + val sck = new SPIClocking(c) + val cs = new SPIChipSelect(c) with HasSPICSMode + val dla = new SPIDelay(c) + val wm = new SPIWatermark(c) +} + +object SPIControl { + def init(c: SPIConfigBase): SPIControl = { + val ctrl = Wire(new SPIControl(c)) + ctrl.fmt.proto := SPIProtocol.Single + ctrl.fmt.iodir := SPIDirection.Rx + ctrl.fmt.endian := SPIEndian.MSB + ctrl.fmt.len := UInt(math.min(c.frameBits, 8)) + ctrl.sck.div := UInt(3) + ctrl.sck.pol := Bool(false) + ctrl.sck.pha := Bool(false) + ctrl.cs.id := UInt(0) + ctrl.cs.dflt.foreach { _ := Bool(true) } + ctrl.cs.mode := SPICSMode.Auto + ctrl.dla.cssck := UInt(1) + ctrl.dla.sckcs := UInt(1) + ctrl.dla.intercs := UInt(1) + ctrl.dla.interxfr := UInt(0) + ctrl.wm.tx := UInt(0) + ctrl.wm.rx := UInt(0) + ctrl + } +} + +class SPIInterrupts extends Bundle { + val txwm = Bool() + val rxwm = Bool() +} diff --git a/src/main/scala/devices/spi/SPIConsts.scala b/src/main/scala/devices/spi/SPIConsts.scala new file mode 100644 index 0000000..b18b08a --- /dev/null +++ b/src/main/scala/devices/spi/SPIConsts.scala @@ -0,0 +1,33 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +object SPIProtocol { + val width = 2 + val Single = UInt(0, width) + val Dual = UInt(1, width) + val Quad = UInt(2, width) + + val cases = Seq(Single, Dual, Quad) + def decode(x: UInt): Seq[Bool] = cases.map(_ === x) +} + +object SPIDirection { + val width = 1 + val Rx = UInt(0, width) + val Tx = UInt(1, width) +} + +object SPIEndian { + val width = 1 + val MSB = UInt(0, width) + val LSB = UInt(1, width) +} + +object SPICSMode { + val width = 2 + val Auto = UInt(0, width) + val Hold = UInt(2, width) + val Off = UInt(3, width) +} diff --git a/src/main/scala/devices/spi/SPIFIFO.scala b/src/main/scala/devices/spi/SPIFIFO.scala new file mode 100644 index 0000000..c7d8005 --- /dev/null +++ b/src/main/scala/devices/spi/SPIFIFO.scala @@ -0,0 +1,62 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +class SPIFIFOControl(c: SPIConfigBase) extends SPIBundle(c) { + val fmt = new SPIFormat(c) with HasSPILength + val cs = new Bundle with HasSPICSMode + val wm = new SPIWatermark(c) +} + +class SPIFIFO(c: SPIConfigBase) extends Module { + val io = new Bundle { + val ctrl = new SPIFIFOControl(c).asInput + val link = new SPIInnerIO(c) + val tx = Decoupled(Bits(width = c.frameBits)).flip + val rx = Decoupled(Bits(width = c.frameBits)) + val ip = new SPIInterrupts().asOutput + } + + val txq = Module(new Queue(io.tx.bits, c.txDepth)) + val rxq = Module(new Queue(io.rx.bits, c.rxDepth)) + + txq.io.enq <> io.tx + io.link.tx <> txq.io.deq + + val fire_tx = io.link.tx.fire() + val fire_rx = io.link.rx.fire() + val rxen = Reg(init = Bool(false)) + + rxq.io.enq.valid := io.link.rx.valid && rxen + rxq.io.enq.bits := io.link.rx.bits + io.rx <> rxq.io.deq + + when (fire_rx) { + rxen := Bool(false) + } + when (fire_tx) { + rxen := (io.link.fmt.iodir === SPIDirection.Rx) + } + + val proto = SPIProtocol.decode(io.link.fmt.proto).zipWithIndex + val cnt_quot = Mux1H(proto.map { case (s, i) => s -> (io.ctrl.fmt.len >> i) }) + val cnt_rmdr = Mux1H(proto.map { case (s, i) => s -> (io.ctrl.fmt.len(i, 0).orR) }) + io.link.fmt <> io.ctrl.fmt + io.link.cnt := cnt_quot + cnt_rmdr + + val cs_mode = RegNext(io.ctrl.cs.mode, SPICSMode.Auto) + val cs_mode_hold = (cs_mode === SPICSMode.Hold) + val cs_mode_off = (cs_mode === SPICSMode.Off) + val cs_update = (cs_mode =/= io.ctrl.cs.mode) + val cs_clear = !(cs_mode_hold || cs_mode_off) + + io.link.cs.set := !cs_mode_off + io.link.cs.clear := cs_update || (fire_tx && cs_clear) + io.link.cs.hold := Bool(false) + + io.link.lock := io.link.tx.valid || rxen + + io.ip.txwm := (txq.io.count < io.ctrl.wm.tx) + io.ip.rxwm := (rxq.io.count > io.ctrl.wm.rx) +} diff --git a/src/main/scala/devices/spi/SPIFlash.scala b/src/main/scala/devices/spi/SPIFlash.scala new file mode 100644 index 0000000..1ad55ca --- /dev/null +++ b/src/main/scala/devices/spi/SPIFlash.scala @@ -0,0 +1,162 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +class SPIFlashInsn(c: SPIFlashConfigBase) extends SPIBundle(c) { + val cmd = new Bundle with HasSPIProtocol { + val code = Bits(width = c.insnCmdBits) + val en = Bool() + } + val addr = new Bundle with HasSPIProtocol { + val len = UInt(width = c.insnAddrLenBits) + } + val pad = new Bundle { + val code = Bits(width = c.frameBits) + val cnt = Bits(width = c.insnPadLenBits) + } + val data = new Bundle with HasSPIProtocol +} + +class SPIFlashControl(c: SPIFlashConfigBase) extends SPIBundle(c) { + val insn = new SPIFlashInsn(c) + val fmt = new Bundle with HasSPIEndian +} + +object SPIFlashInsn { + def init(c: SPIFlashConfigBase): SPIFlashInsn = { + val insn = Wire(new SPIFlashInsn(c)) + insn.cmd.en := Bool(true) + insn.cmd.code := Bits(0x03) + insn.cmd.proto := SPIProtocol.Single + insn.addr.len := UInt(3) + insn.addr.proto := SPIProtocol.Single + insn.pad.cnt := UInt(0) + insn.pad.code := Bits(0) + insn.data.proto := SPIProtocol.Single + insn + } +} + +class SPIFlashAddr(c: SPIFlashConfigBase) extends SPIBundle(c) { + val next = UInt(width = c.insnAddrBits) + val hold = UInt(width = c.insnAddrBits) +} + +class SPIFlashMap(c: SPIFlashConfigBase) extends Module { + val io = new Bundle { + val en = Bool(INPUT) + val ctrl = new SPIFlashControl(c).asInput + val addr = Decoupled(new SPIFlashAddr(c)).flip + val data = Decoupled(UInt(width = c.frameBits)) + val link = new SPIInnerIO(c) + } + + val addr = io.addr.bits.hold + UInt(1) + val merge = io.link.active && (io.addr.bits.next === addr) + + private val insn = io.ctrl.insn + io.link.tx.valid := Bool(true) + io.link.fmt.proto := insn.addr.proto + io.link.fmt.iodir := SPIDirection.Tx + io.link.fmt.endian := io.ctrl.fmt.endian + io.link.cnt := Mux1H( + SPIProtocol.decode(io.link.fmt.proto).zipWithIndex.map { + case (s, i) => (s -> UInt(c.frameBits >> i)) + }) + io.link.cs.set := Bool(true) + io.link.cs.clear := Bool(false) + io.link.cs.hold := Bool(true) + io.link.lock := Bool(true) + + io.addr.ready := Bool(false) + io.data.valid := Bool(false) + io.data.bits := io.link.rx.bits + + val cnt = Reg(UInt(width = math.max(c.insnPadLenBits, c.insnAddrLenBits))) + val cnt_en = Wire(init = Bool(false)) + val cnt_cmp = (0 to c.insnAddrBytes).map(cnt === UInt(_)) + val cnt_zero = cnt_cmp(0) + val cnt_last = cnt_cmp(1) && io.link.tx.ready + val cnt_done = cnt_last || cnt_zero + when (cnt_en) { + io.link.tx.valid := !cnt_zero + when (io.link.tx.fire()) { + cnt := cnt - UInt(1) + } + } + + val (s_idle :: s_cmd :: s_addr :: s_pad :: s_data_pre :: s_data_post :: Nil) = Enum(UInt(), 6) + val state = Reg(init = s_idle) + + switch (state) { + is (s_idle) { + io.link.tx.valid := Bool(false) + when (io.en) { + io.addr.ready := Bool(true) + when (io.addr.valid) { + when (merge) { + state := s_data_pre + } .otherwise { + state := Mux(insn.cmd.en, s_cmd, s_addr) + io.link.cs.clear := Bool(true) + } + } .otherwise { + io.link.lock := Bool(false) + } + } .otherwise { + io.data.valid := io.addr.valid + io.addr.ready := io.data.ready + io.data.bits := UInt(0) + io.link.lock := Bool(false) + } + } + + is (s_cmd) { + io.link.fmt.proto := insn.cmd.proto + io.link.tx.bits := insn.cmd.code + when (io.link.tx.ready) { + state := s_addr + cnt := insn.addr.len + } + } + + is (s_addr) { + io.link.tx.bits := Mux1H(cnt_cmp.tail.zipWithIndex.map { + case (s, i) => + val n = i * c.frameBits + val m = n + (c.frameBits - 1) + s -> io.addr.bits.hold(m, n) + }) + + cnt_en := Bool(true) + when (cnt_done) { + state := s_pad + } + } + + is (s_pad) { + io.link.cnt := insn.pad.cnt + io.link.tx.bits := insn.pad.code + when (io.link.tx.ready) { + state := s_data_pre + } + } + + is (s_data_pre) { + io.link.fmt.proto := insn.data.proto + io.link.fmt.iodir := SPIDirection.Rx + when (io.link.tx.ready) { + state := s_data_post + } + } + + is (s_data_post) { + io.link.tx.valid := Bool(false) + io.data.valid := io.link.rx.valid + when (io.data.fire()) { + state := s_idle + } + } + } +} diff --git a/src/main/scala/devices/spi/SPIMedia.scala b/src/main/scala/devices/spi/SPIMedia.scala new file mode 100644 index 0000000..1f049e3 --- /dev/null +++ b/src/main/scala/devices/spi/SPIMedia.scala @@ -0,0 +1,121 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ + +class SPILinkIO(c: SPIConfigBase) extends SPIBundle(c) { + val tx = Decoupled(Bits(width = c.frameBits)) + val rx = Valid(Bits(width = c.frameBits)).flip + + val cnt = UInt(OUTPUT, c.countBits) + val fmt = new SPIFormat(c).asOutput + val cs = new Bundle { + val set = Bool(OUTPUT) + val clear = Bool(OUTPUT) // Deactivate CS + val hold = Bool(OUTPUT) // Supress automatic CS deactivation + } + val active = Bool(INPUT) +} + +class SPIMedia(c: SPIConfigBase) extends Module { + val io = new Bundle { + val port = new SPIPortIO(c) + val ctrl = new Bundle { + val sck = new SPIClocking(c).asInput + val dla = new SPIDelay(c).asInput + val cs = new SPIChipSelect(c).asInput + } + val link = new SPILinkIO(c).flip + } + + val phy = Module(new SPIPhysical(c)) + phy.io.ctrl.sck := io.ctrl.sck + phy.io.ctrl.fmt := io.link.fmt + + private val op = phy.io.op + op.valid := Bool(true) + op.bits.fn := SPIMicroOp.Delay + op.bits.stb := Bool(false) + op.bits.cnt := io.link.cnt + op.bits.data := io.link.tx.bits + + val cs = Reg(io.ctrl.cs) + val cs_set = Reg(Bool()) + val cs_active = io.ctrl.cs.toggle(io.link.cs.set) + val cs_update = (cs_active.asUInt =/= cs.dflt.asUInt) + + val clear = Reg(init = Bool(false)) + val cs_assert = Reg(init = Bool(false)) + val cs_deassert = clear || (cs_update && !io.link.cs.hold) + + clear := clear || (io.link.cs.clear && cs_assert) + + val continuous = (io.ctrl.dla.interxfr === UInt(0)) + + io.port.sck := phy.io.port.sck + io.port.dq <> phy.io.port.dq + io.port.cs := cs.dflt + + io.link.rx := phy.io.rx + io.link.tx.ready := Bool(false) + io.link.active := cs_assert + + val (s_main :: s_interxfr :: s_intercs :: Nil) = Enum(UInt(), 3) + val state = Reg(init = s_main) + + switch (state) { + is (s_main) { + when (cs_assert) { + when (cs_deassert) { + op.bits.cnt := io.ctrl.dla.sckcs + when (op.ready) { + state := s_intercs + } + } .otherwise { + op.bits.fn := SPIMicroOp.Transfer + op.bits.stb := Bool(true) + + op.valid := io.link.tx.valid + io.link.tx.ready := op.ready + when (op.fire()) { + state := s_interxfr + } + } + } .elsewhen (io.link.tx.valid) { + // Assert CS + op.bits.cnt := io.ctrl.dla.cssck + when (op.ready) { + cs_assert := Bool(true) + cs_set := io.link.cs.set + cs.dflt := cs_active + } + } .otherwise { + // Idle + op.bits.cnt := UInt(0) + op.bits.stb := Bool(true) + cs := io.ctrl.cs + } + } + + is (s_interxfr) { + // Skip if interxfr delay is zero + op.valid := !continuous + op.bits.cnt := io.ctrl.dla.interxfr + when (op.ready || continuous) { + state := s_main + } + } + + is (s_intercs) { + // Deassert CS + op.bits.cnt := io.ctrl.dla.intercs + op.bits.stb := Bool(true) + cs_assert := Bool(false) + clear := Bool(false) + when (op.ready) { + cs.dflt := cs.toggle(cs_set) + state := s_main + } + } + } +} diff --git a/src/main/scala/devices/spi/SPIPeriphery.scala b/src/main/scala/devices/spi/SPIPeriphery.scala new file mode 100644 index 0000000..40bcec6 --- /dev/null +++ b/src/main/scala/devices/spi/SPIPeriphery.scala @@ -0,0 +1,57 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ +import diplomacy.LazyModule +import uncore.tilelink2._ +import rocketchip.{TopNetwork,TopNetworkModule} + +trait PeripherySPI { + this: TopNetwork { val spiConfigs: Seq[SPIConfig] } => + val spiDevices = (spiConfigs.zipWithIndex) map {case (c, i) => + val spi = LazyModule(new TLSPI(c) { override lazy val valName = Some(s"spi$i") } ) + spi.rnode := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + intBus.intnode := spi.intnode + spi + } +} + +trait PeripherySPIBundle { + this: { val spiConfigs: Seq[SPIConfig] } => + val spi_bc = spiConfigs.map(_.bc).reduce(_.union(_)) + val spis = Vec(spiConfigs.size, new SPIPortIO(spi_bc.toSPIConfig)) +} + +trait PeripherySPIModule { + this: TopNetworkModule { + val spiConfigs: Seq[SPIConfig] + val outer: PeripherySPI + val io: PeripherySPIBundle + } => + (io.spis zip outer.spiDevices).foreach { case (io, device) => + io <> device.module.io.port + } +} + + +trait PeripherySPIFlash { + this: TopNetwork { val spiFlashConfig: SPIFlashConfig } => + val qspi = LazyModule(new TLSPIFlash(spiFlashConfig)) + qspi.rnode := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + qspi.fnode := TLFragmenter(1, cacheBlockBytes)(TLWidthWidget(peripheryBusConfig.beatBytes)(peripheryBus.node)) + intBus.intnode := qspi.intnode +} + +trait PeripherySPIFlashBundle { + this: { val spiFlashConfig: SPIFlashConfig } => + val qspi = new SPIPortIO(spiFlashConfig) +} + +trait PeripherySPIFlashModule { + this: TopNetworkModule { + val spiConfigs: Seq[SPIConfig] + val outer: PeripherySPIFlash + val io: PeripherySPIFlashBundle + } => + io.qspi <> outer.qspi.module.io.port +} diff --git a/src/main/scala/devices/spi/SPIPhysical.scala b/src/main/scala/devices/spi/SPIPhysical.scala new file mode 100644 index 0000000..6584be8 --- /dev/null +++ b/src/main/scala/devices/spi/SPIPhysical.scala @@ -0,0 +1,157 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ +import sifive.blocks.util.ShiftRegisterInit + +class SPIMicroOp(c: SPIConfigBase) extends SPIBundle(c) { + val fn = Bits(width = 1) + val stb = Bool() + val cnt = UInt(width = c.countBits) + val data = Bits(width = c.frameBits) +} + +object SPIMicroOp { + val Transfer = UInt(0, 1) + val Delay = UInt(1, 1) +} + +class SPIPhyControl(c: SPIConfigBase) extends SPIBundle(c) { + val sck = new SPIClocking(c) + val fmt = new SPIFormat(c) +} + +class SPIPhysical(c: SPIConfigBase) extends Module { + val io = new SPIBundle(c) { + val port = new SPIPortIO(c) + val ctrl = new SPIPhyControl(c).asInput + val op = Decoupled(new SPIMicroOp(c)).flip + val rx = Valid(Bits(width = c.frameBits)) + } + + private val op = io.op.bits + val ctrl = Reg(io.ctrl) + val proto = SPIProtocol.decode(ctrl.fmt.proto) + + val accept = Wire(init = Bool(false)) + val sample = Wire(init = Bool(false)) + val setup = Wire(init = Bool(false)) + val last = Wire(init = Bool(false)) + // Delayed versions + val setup_d = Reg(next = setup) + val sample_d = ShiftRegisterInit(sample, c.sampleDelay, Bool(false)) + val last_d = ShiftRegisterInit(last, c.sampleDelay, Bool(false)) + + val scnt = Reg(init = UInt(0, c.countBits)) + val tcnt = Reg(io.ctrl.sck.div) + + val stop = (scnt === UInt(0)) + val beat = (tcnt === UInt(0)) + val decr = Mux(beat, scnt, tcnt) - UInt(1) + val sched = Wire(init = beat) + tcnt := Mux(sched, ctrl.sck.div, decr) + + val sck = Reg(Bool()) + val cref = Reg(init = Bool(true)) + val cinv = ctrl.sck.pha ^ ctrl.sck.pol + + private def convert(data: UInt, fmt: SPIFormat) = + Mux(fmt.endian === SPIEndian.MSB, data, Cat(data.toBools)) + + val rxd = Cat(io.port.dq.reverse.map(_.i)) + val samples = Seq(rxd(1), rxd(1, 0), rxd) + + val buffer = Reg(op.data) + val buffer_in = convert(io.op.bits.data, io.ctrl.fmt) + val shift = if (c.sampleDelay > 0) setup_d || (sample_d && stop) else sample_d + buffer := Mux1H(proto, samples.zipWithIndex.map { case (data, i) => + val n = 1 << i + val m = c.frameBits -1 + Cat(Mux(shift, buffer(m-n, 0), buffer(m, n)), + Mux(sample_d, data, buffer(n-1, 0))) + }) + + private def upper(x: UInt, n: Int) = x(c.frameBits-1, c.frameBits-n) + + val txd = Reg(init = Bits(0, io.port.dq.size)) + val txd_in = Mux(accept, upper(buffer_in, 4), upper(buffer, 4)) + val txd_sel = SPIProtocol.decode(Mux(accept, io.ctrl.fmt.proto, ctrl.fmt.proto)) + val txd_shf = (0 until txd_sel.size).map(i => txd_in(3, 4-(1< + dq.o := o + dq.oe := oe + } + io.op.ready := Bool(false) + + val done = Reg(init = Bool(true)) + done := done || last_d + + io.rx.valid := done + io.rx.bits := convert(buffer, ctrl.fmt) + + val xfr = Reg(Bool()) + + when (stop) { + sched := Bool(true) + accept := Bool(true) + } .otherwise { + when (beat) { + cref := !cref + when (xfr) { + sck := cref ^ cinv + sample := cref + setup := !cref + } + when (!cref) { + scnt := decr + } + } + } + + when (scnt === UInt(1)) { + last := beat && cref && xfr // Final sample + when (beat && !cref) { // Final shift + accept := Bool(true) + setup := Bool(false) + sck := ctrl.sck.pol + } + } + + when (accept && done) { + io.op.ready := Bool(true) + when (io.op.valid) { + scnt := op.cnt + when (op.stb) { + ctrl.fmt := io.ctrl.fmt + } + + xfr := Bool(false) + switch (op.fn) { + is (SPIMicroOp.Transfer) { + buffer := buffer_in + sck := cinv + setup := Bool(true) + done := (op.cnt === UInt(0)) + xfr := Bool(true) + } + is (SPIMicroOp.Delay) { + when (op.stb) { + sck := io.ctrl.sck.pol + ctrl.sck := io.ctrl.sck + } + } + } + } + } +} diff --git a/src/main/scala/devices/spi/SPIPins.scala b/src/main/scala/devices/spi/SPIPins.scala new file mode 100644 index 0000000..48c3020 --- /dev/null +++ b/src/main/scala/devices/spi/SPIPins.scala @@ -0,0 +1,34 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ +import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl, GPIOInputPinCtrl} + +class SPIPinsIO(c: SPIConfigBase) extends SPIBundle(c) { + val sck = new GPIOPin + val dq = Vec(4, new GPIOPin) + val cs = Vec(c.csWidth, new GPIOPin) +} + +class SPIGPIOPort(c: SPIConfigBase, syncStages: Int = 0, driveStrength: Bool = Bool(false)) extends Module { + val io = new SPIBundle(c) { + val spi = new SPIPortIO(c).flip + val pins = new SPIPinsIO(c) + } + + GPIOOutputPinCtrl(io.pins.sck, io.spi.sck, ds = driveStrength) + + GPIOOutputPinCtrl(io.pins.dq, Bits(0, io.spi.dq.size)) + (io.pins.dq zip io.spi.dq).foreach { + case (p, s) => + p.o.oval := s.o + p.o.oe := s.oe + p.o.ie := ~s.oe + p.o.pue := Bool(true) + p.o.ds := driveStrength + s.i := ShiftRegister(p.i.ival, syncStages) + } + + GPIOOutputPinCtrl(io.pins.cs, io.spi.cs.asUInt) + io.pins.cs.foreach(_.o.ds := driveStrength) +} diff --git a/src/main/scala/devices/spi/SPIRegs.scala b/src/main/scala/devices/spi/SPIRegs.scala new file mode 100644 index 0000000..b10237d --- /dev/null +++ b/src/main/scala/devices/spi/SPIRegs.scala @@ -0,0 +1,30 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +object SPICRs { + val sckdiv = 0x00 + val sckmode = 0x04 + val csid = 0x10 + val csdef = 0x14 + val csmode = 0x18 + val dcssck = 0x28 + val dsckcs = 0x2a + val dintercs = 0x2c + val dinterxfr = 0x2e + + val fmt = 0x40 + val len = 0x42 + val txfifo = 0x48 + val rxfifo = 0x4c + val txmark = 0x50 + val rxmark = 0x54 + + val insnmode = 0x60 + val insnfmt = 0x64 + val insnproto = 0x65 + val insncmd = 0x66 + val insnpad = 0x67 + + val ie = 0x70 + val ip = 0x74 +} diff --git a/src/main/scala/devices/spi/TLSPI.scala b/src/main/scala/devices/spi/TLSPI.scala new file mode 100644 index 0000000..bc920ea --- /dev/null +++ b/src/main/scala/devices/spi/TLSPI.scala @@ -0,0 +1,132 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ +import config._ +import uncore.tilelink2._ +import diplomacy._ +import regmapper._ +import junctions._ +import rocketchip.PeripheryBusConfig +import sifive.blocks.util.{NonBlockingEnqueue, NonBlockingDequeue} + +trait SPIConfigBase { + val rAddress: BigInt + val rSize: BigInt + val rxDepth: Int + val txDepth: Int + + val csWidth: Int + val frameBits: Int + val delayBits: Int + val divisorBits: Int + + val sampleDelay: Int + + lazy val csIdBits = log2Up(csWidth) + lazy val lengthBits = log2Floor(frameBits) + 1 + lazy val countBits = math.max(lengthBits, delayBits) + + lazy val txDepthBits = log2Floor(txDepth) + 1 + lazy val rxDepthBits = log2Floor(rxDepth) + 1 + + lazy val bc = new SPIBundleConfig(csWidth) +} + +case class SPIConfig( + rAddress: BigInt, + rSize: BigInt = 0x1000, + rxDepth: Int = 8, + txDepth: Int = 8, + csWidth: Int = 1, + frameBits: Int = 8, + delayBits: Int = 8, + divisorBits: Int = 12, + sampleDelay: Int = 2) + extends SPIConfigBase { + + require(frameBits >= 4) + require(sampleDelay >= 0) +} + +case class SPIBundleConfig(csWidth: Int) + { + def union(that: SPIBundleConfig): SPIBundleConfig = + SPIBundleConfig(scala.math.max(csWidth, that.csWidth)) + + def toSPIConfig: SPIConfig = new SPIConfig(rAddress = -1, + csWidth = csWidth) + } + +class SPITopBundle(val i: Vec[Vec[Bool]], val r: Vec[TLBundle]) extends Bundle + +class SPITopModule[B <: SPITopBundle](c: SPIConfigBase, bundle: => B, outer: TLSPIBase) + extends LazyModuleImp(outer) { + + val io = new Bundle { + val port = new SPIPortIO(c) + val tl = bundle + } + + val ctrl = Reg(init = SPIControl.init(c)) + + val fifo = Module(new SPIFIFO(c)) + val mac = Module(new SPIMedia(c)) + io.port <> mac.io.port + + fifo.io.ctrl.fmt := ctrl.fmt + fifo.io.ctrl.cs <> ctrl.cs + fifo.io.ctrl.wm := ctrl.wm + mac.io.ctrl.sck := ctrl.sck + mac.io.ctrl.dla := ctrl.dla + mac.io.ctrl.cs <> ctrl.cs + + val ie = Reg(init = new SPIInterrupts().fromBits(Bits(0))) + val ip = fifo.io.ip + io.tl.i(0)(0) := (ip.txwm && ie.txwm) || (ip.rxwm && ie.rxwm) + + protected val regmapBase = Seq( + SPICRs.sckdiv -> Seq(RegField(c.divisorBits, ctrl.sck.div)), + SPICRs.sckmode -> Seq( + RegField(1, ctrl.sck.pha), + RegField(1, ctrl.sck.pol)), + SPICRs.csid -> Seq(RegField(c.csIdBits, ctrl.cs.id)), + SPICRs.csdef -> ctrl.cs.dflt.map(x => RegField(1, x)), + SPICRs.csmode -> Seq(RegField(SPICSMode.width, ctrl.cs.mode)), + SPICRs.dcssck -> Seq(RegField(c.delayBits, ctrl.dla.cssck)), + SPICRs.dsckcs -> Seq(RegField(c.delayBits, ctrl.dla.sckcs)), + SPICRs.dintercs -> Seq(RegField(c.delayBits, ctrl.dla.intercs)), + SPICRs.dinterxfr -> Seq(RegField(c.delayBits, ctrl.dla.interxfr)), + + SPICRs.fmt -> Seq( + RegField(SPIProtocol.width, ctrl.fmt.proto), + RegField(SPIEndian.width, ctrl.fmt.endian), + RegField(SPIDirection.width, ctrl.fmt.iodir)), + SPICRs.len -> Seq(RegField(c.lengthBits, ctrl.fmt.len)), + + SPICRs.txfifo -> NonBlockingEnqueue(fifo.io.tx), + SPICRs.rxfifo -> NonBlockingDequeue(fifo.io.rx), + + SPICRs.txmark -> Seq(RegField(c.txDepthBits, ctrl.wm.tx)), + SPICRs.rxmark -> Seq(RegField(c.rxDepthBits, ctrl.wm.rx)), + + SPICRs.ie -> Seq( + RegField(1, ie.txwm), + RegField(1, ie.rxwm)), + SPICRs.ip -> Seq( + RegField.r(1, ip.txwm), + RegField.r(1, ip.rxwm))) +} + +abstract class TLSPIBase(c: SPIConfigBase)(implicit val p: Parameters) extends LazyModule { + require(isPow2(c.rSize)) + val rnode = TLRegisterNode(address = AddressSet(c.rAddress, c.rSize-1), beatBytes = p(PeripheryBusConfig).beatBytes) + val intnode = IntSourceNode(1) +} + +class TLSPI(c: SPIConfig)(implicit p: Parameters) extends TLSPIBase(c)(p) { + lazy val module = new SPITopModule(c, new SPITopBundle(intnode.bundleOut, rnode.bundleIn), this) { + mac.io.link <> fifo.io.link + rnode.regmap(regmapBase:_*) + } +} diff --git a/src/main/scala/devices/spi/TLSPIFlash.scala b/src/main/scala/devices/spi/TLSPIFlash.scala new file mode 100644 index 0000000..284692f --- /dev/null +++ b/src/main/scala/devices/spi/TLSPIFlash.scala @@ -0,0 +1,114 @@ +// See LICENSE for license details. +package sifive.blocks.devices.spi + +import Chisel._ +import config._ +import diplomacy._ +import regmapper._ +import uncore.tilelink2._ + +trait SPIFlashConfigBase extends SPIConfigBase { + val fAddress: BigInt + val fSize: BigInt + + val insnAddrBytes: Int + val insnPadLenBits: Int + lazy val insnCmdBits = frameBits + lazy val insnAddrBits = insnAddrBytes * frameBits + lazy val insnAddrLenBits = log2Floor(insnAddrBytes) + 1 +} + +case class SPIFlashConfig( + rAddress: BigInt, + fAddress: BigInt, + rSize: BigInt = 0x1000, + fSize: BigInt = 0x20000000, + rxDepth: Int = 8, + txDepth: Int = 8, + csWidth: Int = 1, + delayBits: Int = 8, + divisorBits: Int = 12, + sampleDelay: Int = 2) + extends SPIFlashConfigBase { + val frameBits = 8 + val insnAddrBytes = 4 + val insnPadLenBits = 4 + + require(insnPadLenBits <= delayBits) + require(sampleDelay >= 0) +} + +class SPIFlashTopBundle(i: Vec[Vec[Bool]], r: Vec[TLBundle], val f: Vec[TLBundle]) extends SPITopBundle(i, r) + +class SPIFlashTopModule[B <: SPIFlashTopBundle] + (c: SPIFlashConfigBase, bundle: => B, outer: TLSPIFlashBase) + extends SPITopModule(c, bundle, outer) { + + val flash = Module(new SPIFlashMap(c)) + val arb = Module(new SPIArbiter(c, 2)) + + private val f = io.tl.f.head + // Tie unused channels + f.b.valid := Bool(false) + f.c.ready := Bool(true) + f.e.ready := Bool(true) + + val a = Reg(f.a.bits) + val a_msb = log2Ceil(c.fSize) - 1 + + when (f.a.fire()) { + a := f.a.bits + } + + flash.io.addr.bits.next := f.a.bits.address(a_msb, 0) + flash.io.addr.bits.hold := a.address(a_msb, 0) + flash.io.addr.valid := f.a.valid + f.a.ready := flash.io.addr.ready + + f.d.bits := outer.fnode.edgesIn.head.AccessAck(a, UInt(0), flash.io.data.bits) + f.d.valid := flash.io.data.valid + flash.io.data.ready := f.d.ready + + val insn = Reg(init = SPIFlashInsn.init(c)) + val flash_en = Reg(init = Bool(true)) + + flash.io.ctrl.insn := insn + flash.io.ctrl.fmt <> ctrl.fmt + flash.io.en := flash_en + arb.io.sel := !flash_en + + protected val regmapFlash = Seq( + SPICRs.insnmode -> Seq(RegField(1, flash_en)), + SPICRs.insnfmt -> Seq( + RegField(1, insn.cmd.en), + RegField(c.insnAddrLenBits, insn.addr.len), + RegField(c.insnPadLenBits, insn.pad.cnt)), + SPICRs.insnproto -> Seq( + RegField(SPIProtocol.width, insn.cmd.proto), + RegField(SPIProtocol.width, insn.addr.proto), + RegField(SPIProtocol.width, insn.data.proto)), + SPICRs.insncmd -> Seq(RegField(c.insnCmdBits, insn.cmd.code)), + SPICRs.insnpad -> Seq(RegField(c.frameBits, insn.pad.code))) +} + +abstract class TLSPIFlashBase(c: SPIFlashConfigBase)(implicit p: Parameters) extends TLSPIBase(c)(p) { + require(isPow2(c.fSize)) + val fnode = TLManagerNode(1, TLManagerParameters( + address = Seq(AddressSet(c.fAddress, c.fSize-1)), + regionType = RegionType.UNCACHED, + executable = true, + supportsGet = TransferSizes(1, 1), + fifoId = Some(0))) +} + +class TLSPIFlash(c: SPIFlashConfig)(implicit p: Parameters) extends TLSPIFlashBase(c)(p) { + lazy val module = new SPIFlashTopModule(c, + new SPIFlashTopBundle(intnode.bundleOut, rnode.bundleIn, fnode.bundleIn), this) { + + arb.io.inner(0) <> flash.io.link + arb.io.inner(1) <> fifo.io.link + mac.io.link <> arb.io.outer + + rnode.regmap(regmapBase ++ regmapFlash:_*) + } +} diff --git a/src/main/scala/devices/uart/UART.scala b/src/main/scala/devices/uart/UART.scala new file mode 100644 index 0000000..68b47fe --- /dev/null +++ b/src/main/scala/devices/uart/UART.scala @@ -0,0 +1,275 @@ +// See LICENSE for license details. +package sifive.blocks.devices.uart + +import Chisel._ +import config._ +import regmapper._ +import uncore.tilelink2._ +import junctions._ +import util._ +import rocketchip.PeripheryBusConfig +import sifive.blocks.util.{NonBlockingEnqueue, NonBlockingDequeue} + +case class UARTConfig( + address: BigInt, + dataBits: Int = 8, + stopBits: Int = 2, + divisorBits: Int = 16, + oversample: Int = 4, + nSamples: Int = 3, + nTxEntries: Int = 8, + nRxEntries: Int = 8) + +trait HasUARTParameters { + val c: UARTConfig + val uartDataBits = c.dataBits + val uartStopBits = c.stopBits + val uartDivisorBits = c.divisorBits + + val uartOversample = c.oversample + val uartOversampleFactor = 1 << uartOversample + val uartNSamples = c.nSamples + + val uartNTxEntries = c.nTxEntries + val uartNRxEntries = c.nRxEntries + + require(uartDivisorBits > uartOversample) + require(uartOversampleFactor > uartNSamples) +} + +abstract class UARTModule(val c: UARTConfig)(implicit val p: Parameters) + extends Module with HasUARTParameters + +class UARTPortIO extends Bundle { + val txd = Bool(OUTPUT) + val rxd = Bool(INPUT) +} + +trait MixUARTParameters { + val params: (UARTConfig, Parameters) + val c = params._1 + implicit val p = params._2 +} + +trait UARTTopBundle extends Bundle with MixUARTParameters with HasUARTParameters { + val port = new UARTPortIO +} + +class UARTTx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) { + val io = new Bundle { + val en = Bool(INPUT) + val in = Decoupled(Bits(width = uartDataBits)).flip + val out = Bits(OUTPUT, 1) + val div = UInt(INPUT, uartDivisorBits) + val nstop = UInt(INPUT, log2Up(uartStopBits)) + } + + val prescaler = Reg(init = UInt(0, uartDivisorBits)) + val pulse = (prescaler === UInt(0)) + + private val n = uartDataBits + 1 + val counter = Reg(init = UInt(0, log2Floor(n + uartStopBits) + 1)) + val shifter = Reg(Bits(width = n)) + val out = Reg(init = Bits(1, 1)) + io.out := out + + val busy = (counter =/= UInt(0)) + io.in.ready := io.en && !busy + when (io.in.fire()) { + printf("%c", io.in.bits) + shifter := Cat(io.in.bits, Bits(0, 1)) + counter := Mux1H((0 until uartStopBits).map(i => + (io.nstop === UInt(i)) -> UInt(n + i + 1))) + } + when (busy) { + prescaler := Mux(pulse, io.div, prescaler - UInt(1)) + } + when (pulse && busy) { + counter := counter - UInt(1) + shifter := Cat(Bits(1, 1), shifter >> 1) + out := shifter(0) + } +} + +class UARTRx(c: UARTConfig)(implicit p: Parameters) extends UARTModule(c)(p) { + val io = new Bundle { + val en = Bool(INPUT) + val in = Bits(INPUT, 1) + val out = Valid(Bits(width = uartDataBits)) + val div = UInt(INPUT, uartDivisorBits) + } + + val debounce = Reg(init = UInt(0, 2)) + val debounce_max = (debounce === UInt(3)) + val debounce_min = (debounce === UInt(0)) + + val prescaler = Reg(init = UInt(0, uartDivisorBits - uartOversample)) + val start = Wire(init = Bool(false)) + val busy = Wire(init = Bool(false)) + val pulse = (prescaler === UInt(0)) && busy + + when (busy) { + prescaler := prescaler - UInt(1) + } + when (start || pulse) { + prescaler := io.div >> uartOversample + } + + val sample = Reg(Bits(width = uartNSamples)) + val voter = new Majority(sample.toBools.toSet) + when (pulse) { + sample := Cat(sample, io.in) + } + + private val delay0 = (uartOversampleFactor + uartNSamples) >> 1 + private val delay1 = uartOversampleFactor + + val timer = Reg(UInt(width = uartOversample + 1)) + val counter = Reg(UInt(width = log2Floor(uartDataBits) + 1)) + val shifter = Reg(Bits(width = uartDataBits)) + val expire = (timer === UInt(0)) && pulse + + val sched = Wire(init = Bool(false)) + when (pulse) { + timer := timer - UInt(1) + } + when (sched) { + timer := UInt(delay1-1) + } + + val valid = Reg(init = Bool(false)) + valid := Bool(false) + io.out.valid := valid + io.out.bits := shifter + + val (s_idle :: s_start :: s_data :: Nil) = Enum(UInt(), 3) + val state = Reg(init = s_idle) + + switch (state) { + is (s_idle) { + when (!(!io.in) && !debounce_min) { + debounce := debounce - UInt(1) + } + when (!io.in) { + debounce := debounce + UInt(1) + when (debounce_max) { + state := s_start + start := Bool(true) + timer := UInt(delay0-1) + } + } + } + + is (s_start) { + busy := Bool(true) + when (expire) { + sched := Bool(true) + when (voter.out) { + state := s_idle + } .otherwise { + state := s_data + counter := UInt(uartDataBits) + } + } + } + + is (s_data) { + busy := Bool(true) + when (expire) { + counter := counter - UInt(1) + when (counter === UInt(0)) { + state := s_idle + valid := Bool(true) + } .otherwise { + shifter := Cat(voter.out, shifter >> 1) + sched := Bool(true) + } + } + } + } + + when (!io.en) { + debounce := UInt(0) + } +} + +class UARTInterrupts extends Bundle { + val rxwm = Bool() + val txwm = Bool() +} + +trait UARTTopModule extends Module with MixUARTParameters with HasUARTParameters with HasRegMap { + val io: UARTTopBundle + + val txm = Module(new UARTTx(c)) + val txq = Module(new Queue(txm.io.in.bits, uartNTxEntries)) + + val rxm = Module(new UARTRx(c)) + val rxq = Module(new Queue(rxm.io.out.bits, uartNRxEntries)) + + val divinit = 542 // (62.5MHz / 115200) + val div = Reg(init = UInt(divinit, uartDivisorBits)) + + private val stopCountBits = log2Up(uartStopBits) + private val txCountBits = log2Floor(uartNTxEntries) + 1 + private val rxCountBits = log2Floor(uartNRxEntries) + 1 + + val txen = Reg(init = Bool(false)) + val rxen = Reg(init = Bool(false)) + val txwm = Reg(init = UInt(0, txCountBits)) + val rxwm = Reg(init = UInt(0, rxCountBits)) + val nstop = Reg(init = UInt(0, stopCountBits)) + + txm.io.en := txen + txm.io.in <> txq.io.deq + txm.io.div := div + txm.io.nstop := nstop + io.port.txd := txm.io.out + + rxm.io.en := rxen + rxm.io.in := io.port.rxd + rxq.io.enq <> rxm.io.out + rxm.io.div := div + + val ie = Reg(init = new UARTInterrupts().fromBits(Bits(0))) + val ip = Wire(new UARTInterrupts) + + ip.txwm := (txq.io.count < txwm) + ip.rxwm := (rxq.io.count > rxwm) + interrupts(0) := (ip.txwm && ie.txwm) || (ip.rxwm && ie.rxwm) + + regmap( + UARTCtrlRegs.txfifo -> NonBlockingEnqueue(txq.io.enq), + UARTCtrlRegs.rxfifo -> NonBlockingDequeue(rxq.io.deq), + + UARTCtrlRegs.txctrl -> Seq( + RegField(1, txen), + RegField(stopCountBits, nstop)), + UARTCtrlRegs.rxctrl -> Seq(RegField(1, rxen)), + UARTCtrlRegs.txmark -> Seq(RegField(txCountBits, txwm)), + UARTCtrlRegs.rxmark -> Seq(RegField(rxCountBits, rxwm)), + + UARTCtrlRegs.ie -> Seq( + RegField(1, ie.txwm), + RegField(1, ie.rxwm)), + + UARTCtrlRegs.ip -> Seq( + RegField.r(1, ip.txwm), + RegField.r(1, ip.rxwm)), + + UARTCtrlRegs.div -> Seq( + RegField(uartDivisorBits, div)) + ) +} + +class Majority(in: Set[Bool]) { + private val n = (in.size >> 1) + 1 + private val clauses = in.subsets(n).map(_.reduce(_ && _)) + val out = clauses.reduce(_ || _) +} + +// Magic TL2 Incantation to create a TL2 Slave +class UART(c: UARTConfig)(implicit val p: Parameters) + extends TLRegisterRouter(c.address, interrupts = 1, beatBytes = p(PeripheryBusConfig).beatBytes)( + new TLRegBundle((c, p), _) with UARTTopBundle)( + new TLRegModule((c, p), _, _) with UARTTopModule) diff --git a/src/main/scala/devices/uart/UARTCtrlRegs.scala b/src/main/scala/devices/uart/UARTCtrlRegs.scala new file mode 100644 index 0000000..3556583 --- /dev/null +++ b/src/main/scala/devices/uart/UARTCtrlRegs.scala @@ -0,0 +1,15 @@ +// See LICENSE for license details. +package sifive.blocks.devices.uart + +object UARTCtrlRegs { + val txfifo = 0x00 + val rxfifo = 0x04 + val txctrl = 0x08 + val txmark = 0x0a + val rxctrl = 0x0c + val rxmark = 0x0e + + val ie = 0x10 + val ip = 0x14 + val div = 0x18 +} diff --git a/src/main/scala/devices/uart/UARTPeriphery.scala b/src/main/scala/devices/uart/UARTPeriphery.scala new file mode 100644 index 0000000..fd5bc35 --- /dev/null +++ b/src/main/scala/devices/uart/UARTPeriphery.scala @@ -0,0 +1,54 @@ +// See LICENSE for license details. +package sifive.blocks.devices.uart + +import Chisel._ +import config._ +import diplomacy._ +import uncore.tilelink2._ +import rocketchip._ + +import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl, GPIOInputPinCtrl} +import sifive.blocks.util.ShiftRegisterInit + +trait PeripheryUART { + this: TopNetwork { + val uartConfigs: Seq[UARTConfig] + } => + val uartDevices = uartConfigs.zipWithIndex.map { case (c, i) => + val uart = LazyModule(new UART(c) { override lazy val valName = Some(s"uart$i") } ) + uart.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + intBus.intnode := uart.intnode + uart + } +} + +trait PeripheryUARTBundle { + this: { val uartConfigs: Seq[UARTConfig] } => + val uarts = Vec(uartConfigs.size, new UARTPortIO) +} + +trait PeripheryUARTModule { + this: TopNetworkModule { + val outer: PeripheryUART + val io: PeripheryUARTBundle + } => + (io.uarts zip outer.uartDevices).foreach { case (io, device) => + io <> device.module.io.port + } +} + +class UARTPinsIO extends Bundle { + val rxd = new GPIOPin + val txd = new GPIOPin +} + +class UARTGPIOPort(syncStages: Int = 0) extends Module { + val io = new Bundle{ + val uart = new UARTPortIO().flip() + val pins = new UARTPinsIO + } + + GPIOOutputPinCtrl(io.pins.txd, io.uart.txd) + val rxd = GPIOInputPinCtrl(io.pins.rxd) + io.uart.rxd := ShiftRegisterInit(rxd, syncStages, Bool(true)) +} diff --git a/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIG.scala b/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIG.scala new file mode 100644 index 0000000..bbbfcd3 --- /dev/null +++ b/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIG.scala @@ -0,0 +1,158 @@ +// See LICENSE for license details. +package sifive.blocks.devices.xilinxvc707mig + +import Chisel._ +import config._ +import diplomacy._ +import uncore.tilelink2._ +import uncore.axi4._ +import rocketchip._ +import sifive.blocks.ip.xilinx.vc707mig.{VC707MIGUnidirectionalIOClocksReset, VC707MIGUnidirectionalIODDR, vc707mig} + +trait HasXilinxVC707MIGParameters { +} + +class XilinxVC707MIGPads extends Bundle with VC707MIGUnidirectionalIODDR { + val _inout_ddr3_dq = Bits(OUTPUT,64) + val _inout_ddr3_dqs_n = Bits(OUTPUT,8) + val _inout_ddr3_dqs_p = Bits(OUTPUT,8) +} + +class XilinxVC707MIGIO extends Bundle with VC707MIGUnidirectionalIODDR + with VC707MIGUnidirectionalIOClocksReset { + val _inout_ddr3_dq = Bits(OUTPUT,64) + val _inout_ddr3_dqs_n = Bits(OUTPUT,8) + val _inout_ddr3_dqs_p = Bits(OUTPUT,8) +} + +class XilinxVC707MIG(implicit p: Parameters) extends LazyModule with HasXilinxVC707MIGParameters { + val node = TLInputNode() + val axi4 = AXI4InternalOutputNode(AXI4SlavePortParameters( + slaves = Seq(AXI4SlaveParameters( + address = Seq(AddressSet(p(ExtMem).base, p(ExtMem).size-1)), + regionType = RegionType.UNCACHED, + executable = true, + supportsWrite = TransferSizes(1, 256*8), + supportsRead = TransferSizes(1, 256*8), + interleavedId = Some(0))), + beatBytes = 8)) + + val xing = LazyModule(new TLAsyncCrossing) + val toaxi4 = LazyModule(new TLToAXI4(idBits = 4)) + + xing.node := node + val monitor = (toaxi4.node := xing.node) + axi4 := toaxi4.node + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val port = new XilinxVC707MIGIO + val tl = node.bundleIn + } + + //MIG black box instantiation + val blackbox = Module(new vc707mig) + + //pins to top level + + //inouts + io.port._inout_ddr3_dq := blackbox.io.ddr3_dq + io.port._inout_ddr3_dqs_n := blackbox.io.ddr3_dqs_n + io.port._inout_ddr3_dqs_p := blackbox.io.ddr3_dqs_p + + //outputs + io.port.ddr3_addr := blackbox.io.ddr3_addr + io.port.ddr3_ba := blackbox.io.ddr3_ba + io.port.ddr3_ras_n := blackbox.io.ddr3_ras_n + io.port.ddr3_cas_n := blackbox.io.ddr3_cas_n + io.port.ddr3_we_n := blackbox.io.ddr3_we_n + io.port.ddr3_reset_n := blackbox.io.ddr3_reset_n + io.port.ddr3_ck_p := blackbox.io.ddr3_ck_p + io.port.ddr3_ck_n := blackbox.io.ddr3_ck_n + io.port.ddr3_cke := blackbox.io.ddr3_cke + io.port.ddr3_cs_n := blackbox.io.ddr3_cs_n + io.port.ddr3_dm := blackbox.io.ddr3_dm + io.port.ddr3_odt := blackbox.io.ddr3_odt + + //inputs + //differential system clock + blackbox.io.sys_clk_n := io.port.sys_clk_n + blackbox.io.sys_clk_p := io.port.sys_clk_p + + //user interface signals + val axi_async = axi4.bundleIn(0) + xing.module.io.in_clock := clock + xing.module.io.in_reset := reset + xing.module.io.out_clock := blackbox.io.ui_clk + xing.module.io.out_reset := blackbox.io.ui_clk_sync_rst + toaxi4.module.clock := blackbox.io.ui_clk + toaxi4.module.reset := blackbox.io.ui_clk_sync_rst + monitor.foreach { lm => + lm.module.clock := blackbox.io.ui_clk + lm.module.reset := blackbox.io.ui_clk_sync_rst + } + + io.port.ui_clk := blackbox.io.ui_clk + io.port.ui_clk_sync_rst := blackbox.io.ui_clk_sync_rst + io.port.mmcm_locked := blackbox.io.mmcm_locked + blackbox.io.aresetn := io.port.aresetn + blackbox.io.app_sr_req := Bool(false) + blackbox.io.app_ref_req := Bool(false) + blackbox.io.app_zq_req := Bool(false) + //app_sr_active := unconnected + //app_ref_ack := unconnected + //app_zq_ack := unconnected + + //slave AXI interface write address ports + blackbox.io.s_axi_awid := axi_async.aw.bits.id + blackbox.io.s_axi_awaddr := axi_async.aw.bits.addr //truncation ?? + blackbox.io.s_axi_awlen := axi_async.aw.bits.len + blackbox.io.s_axi_awsize := axi_async.aw.bits.size + blackbox.io.s_axi_awburst := axi_async.aw.bits.burst + blackbox.io.s_axi_awlock := axi_async.aw.bits.lock + blackbox.io.s_axi_awcache := UInt("b0011") + blackbox.io.s_axi_awprot := axi_async.aw.bits.prot + blackbox.io.s_axi_awqos := axi_async.aw.bits.qos + blackbox.io.s_axi_awvalid := axi_async.aw.valid + axi_async.aw.ready := blackbox.io.s_axi_awready + + //slave interface write data ports + blackbox.io.s_axi_wdata := axi_async.w.bits.data + blackbox.io.s_axi_wstrb := axi_async.w.bits.strb + blackbox.io.s_axi_wlast := axi_async.w.bits.last + blackbox.io.s_axi_wvalid := axi_async.w.valid + axi_async.w.ready := blackbox.io.s_axi_wready + + //slave interface write response + blackbox.io.s_axi_bready := axi_async.b.ready + axi_async.b.bits.id := blackbox.io.s_axi_bid + axi_async.b.bits.resp := blackbox.io.s_axi_bresp + axi_async.b.valid := blackbox.io.s_axi_bvalid + + //slave AXI interface read address ports + blackbox.io.s_axi_arid := axi_async.ar.bits.id + blackbox.io.s_axi_araddr := axi_async.ar.bits.addr //truncation ?? + blackbox.io.s_axi_arlen := axi_async.ar.bits.len + blackbox.io.s_axi_arsize := axi_async.ar.bits.size + blackbox.io.s_axi_arburst := axi_async.ar.bits.burst + blackbox.io.s_axi_arlock := axi_async.ar.bits.lock + blackbox.io.s_axi_arcache := UInt("b0011") + blackbox.io.s_axi_arprot := axi_async.ar.bits.prot + blackbox.io.s_axi_arqos := axi_async.ar.bits.qos + blackbox.io.s_axi_arvalid := axi_async.ar.valid + axi_async.ar.ready := blackbox.io.s_axi_arready + + //slace AXI interface read data ports + blackbox.io.s_axi_rready := axi_async.r.ready + axi_async.r.bits.id := blackbox.io.s_axi_rid + axi_async.r.bits.data := blackbox.io.s_axi_rdata + axi_async.r.bits.resp := blackbox.io.s_axi_rresp + axi_async.r.bits.last := blackbox.io.s_axi_rlast + axi_async.r.valid := blackbox.io.s_axi_rvalid + + //misc + io.port.init_calib_complete := blackbox.io.init_calib_complete + blackbox.io.sys_rst :=io.port.sys_rst + //mig.device_temp :- unconnceted + } +} diff --git a/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIGPeriphery.scala b/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIGPeriphery.scala new file mode 100644 index 0000000..b52b37c --- /dev/null +++ b/src/main/scala/devices/xilinxvc707mig/XilinxVC707MIGPeriphery.scala @@ -0,0 +1,26 @@ +// See LICENSE for license details. +package sifive.blocks.devices.xilinxvc707mig + +import Chisel._ +import diplomacy._ +import rocketchip.{TopNetwork,TopNetworkModule,TopNetworkBundle} +import coreplex.BankedL2Config + +trait PeripheryXilinxVC707MIG extends TopNetwork { + val module: PeripheryXilinxVC707MIGModule + + val xilinxvc707mig = LazyModule(new XilinxVC707MIG) + require(p(BankedL2Config).nMemoryChannels == 1, "Coreplex must have 1 master memory port") + val mem = Seq(xilinxvc707mig.node) +} + +trait PeripheryXilinxVC707MIGBundle extends TopNetworkBundle { + val xilinxvc707mig = new XilinxVC707MIGIO +} + +trait PeripheryXilinxVC707MIGModule extends TopNetworkModule { + val outer: PeripheryXilinxVC707MIG + val io: PeripheryXilinxVC707MIGBundle + + io.xilinxvc707mig <> outer.xilinxvc707mig.module.io.port +} diff --git a/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1.scala b/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1.scala new file mode 100644 index 0000000..a795bf0 --- /dev/null +++ b/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1.scala @@ -0,0 +1,52 @@ +// See LICENSE for license details. +package sifive.blocks.devices.xilinxvc707pciex1 + +import Chisel._ +import config._ +import diplomacy._ +import uncore.tilelink2._ +import uncore.axi4._ +import rocketchip._ +import sifive.blocks.ip.xilinx.vc707axi_to_pcie_x1.{VC707AXIToPCIeX1, VC707AXIToPCIeX1IOClocksReset, VC707AXIToPCIeX1IOSerial} +import sifive.blocks.ip.xilinx.ibufds_gte2.IBUFDS_GTE2 + +class XilinxVC707PCIeX1Pads extends Bundle with VC707AXIToPCIeX1IOSerial + +class XilinxVC707PCIeX1IO extends Bundle with VC707AXIToPCIeX1IOSerial + with VC707AXIToPCIeX1IOClocksReset { + val axi_ctl_aresetn = Bool(INPUT) + val REFCLK_rxp = Bool(INPUT) + val REFCLK_rxn = Bool(INPUT) +} + +class XilinxVC707PCIeX1(implicit p: Parameters) extends LazyModule { + val slave = TLInputNode() + val control = TLInputNode() + val master = TLOutputNode() + val intnode = IntSourceNode(1) + + val axi_to_pcie_x1 = LazyModule(new VC707AXIToPCIeX1) + axi_to_pcie_x1.slave := TLToAXI4(idBits=4)(slave) + axi_to_pcie_x1.control := AXI4Fragmenter(lite=true, maxInFlight=4)(TLToAXI4(idBits=0)(control)) + master := TLWidthWidget(64)(AXI4ToTL()(AXI4Fragmenter()(axi_to_pcie_x1.master))) + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val port = new XilinxVC707PCIeX1IO + val slave_in = slave.bundleIn + val control_in = control.bundleIn + val master_out = master.bundleOut + val interrupt = intnode.bundleOut + } + + io.port <> axi_to_pcie_x1.module.io.port + io.interrupt(0)(0) := axi_to_pcie_x1.module.io.interrupt_out + + //PCIe Reference Clock + val ibufds_gte2 = Module(new IBUFDS_GTE2) + axi_to_pcie_x1.module.io.REFCLK := ibufds_gte2.io.O + ibufds_gte2.io.CEB := UInt(0) + ibufds_gte2.io.I := io.port.REFCLK_rxp + ibufds_gte2.io.IB := io.port.REFCLK_rxn + } +} diff --git a/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1Periphery.scala b/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1Periphery.scala new file mode 100644 index 0000000..494d787 --- /dev/null +++ b/src/main/scala/devices/xilinxvc707pciex1/XilinxVC707PCIeX1Periphery.scala @@ -0,0 +1,27 @@ +// See LICENSE for license details. +package sifive.blocks.devices.xilinxvc707pciex1 + +import Chisel._ +import diplomacy.LazyModule +import rocketchip.{L2Crossbar,L2CrossbarModule,L2CrossbarBundle} +import uncore.tilelink2.TLWidthWidget + +trait PeripheryXilinxVC707PCIeX1 extends L2Crossbar { + + val xilinxvc707pcie = LazyModule(new XilinxVC707PCIeX1) + l2.node := xilinxvc707pcie.master + xilinxvc707pcie.slave := TLWidthWidget(socBusConfig.beatBytes)(socBus.node) + xilinxvc707pcie.control := TLWidthWidget(socBusConfig.beatBytes)(socBus.node) + intBus.intnode := xilinxvc707pcie.intnode +} + +trait PeripheryXilinxVC707PCIeX1Bundle extends L2CrossbarBundle { + val xilinxvc707pcie = new XilinxVC707PCIeX1IO +} + +trait PeripheryXilinxVC707PCIeX1Module extends L2CrossbarModule { + val outer: PeripheryXilinxVC707PCIeX1 + val io: PeripheryXilinxVC707PCIeX1Bundle + + io.xilinxvc707pcie <> outer.xilinxvc707pcie.module.io.port +} diff --git a/src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala b/src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala new file mode 100644 index 0000000..7fca6f1 --- /dev/null +++ b/src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala @@ -0,0 +1,18 @@ +// See LICENSE for license details. +package sifive.blocks.ip.xilinx.ibufds_gte2 + +import Chisel._ + +//IP : xilinx unisim IBUFDS_GTE2 +//Differential Signaling Input Buffer +//unparameterized + +class IBUFDS_GTE2 extends BlackBox { + val io = new Bundle { + val O = Bool(OUTPUT) + val ODIV2 = Bool(OUTPUT) + val CEB = Bool(INPUT) + val I = Bool(INPUT) + val IB = Bool(INPUT) + } +} diff --git a/src/main/scala/ip/xilinx/vc707axi_to_pcie_x1/vc707axi_to_pcie_x1.scala b/src/main/scala/ip/xilinx/vc707axi_to_pcie_x1/vc707axi_to_pcie_x1.scala new file mode 100644 index 0000000..a7cf844 --- /dev/null +++ b/src/main/scala/ip/xilinx/vc707axi_to_pcie_x1/vc707axi_to_pcie_x1.scala @@ -0,0 +1,374 @@ +// See LICENSE for license details. +package sifive.blocks.ip.xilinx.vc707axi_to_pcie_x1 + +import Chisel._ +import config._ +import diplomacy._ +import uncore.axi4._ +import junctions._ + +// IP VLNV: xilinx.com:customize_ip:vc707pcietoaxi:1.0 +// Black Box +// Signals named _exactly_ as per Vivado generated verilog +// s : -{lock, cache, prot, qos} + +trait VC707AXIToPCIeX1IOSerial extends Bundle { + //serial external pins + val pci_exp_txp = Bits(OUTPUT,1) + val pci_exp_txn = Bits(OUTPUT,1) + val pci_exp_rxp = Bits(INPUT,1) + val pci_exp_rxn = Bits(INPUT,1) +} + +trait VC707AXIToPCIeX1IOClocksReset extends Bundle { + //clock, reset, control + val axi_aresetn = Bool(INPUT) + val axi_aclk_out = Clock(OUTPUT) + val axi_ctl_aclk_out = Clock(OUTPUT) + val mmcm_lock = Bool(OUTPUT) +} + +//scalastyle:off +//turn off linter: blackbox name must match verilog module +class vc707axi_to_pcie_x1() extends BlackBox +{ + val io = new Bundle with VC707AXIToPCIeX1IOSerial + with VC707AXIToPCIeX1IOClocksReset { + //refclk + val REFCLK = Bool(INPUT) + + //clock, reset, control + val INTX_MSI_Request = Bool(INPUT) + val INTX_MSI_Grant = Bool(OUTPUT) + val MSI_enable = Bool(OUTPUT) + val MSI_Vector_Num = Bits(INPUT,5) + val MSI_Vector_Width = Bits(OUTPUT,3) + + //interrupt + val interrupt_out = Bool(OUTPUT) + + //axi slave + //-{lock, cache, prot, qos} + //slave interface write address + val s_axi_awid = Bits(INPUT,4) + val s_axi_awaddr = Bits(INPUT,32) + val s_axi_awregion = Bits(INPUT,4) + val s_axi_awlen = Bits(INPUT,8) + val s_axi_awsize = Bits(INPUT,3) + val s_axi_awburst = Bits(INPUT,2) + //val s_axi_awlock = Bool(INPUT) + //val s_axi_awcache = Bits(INPUT,4) + //val s_axi_awprot = Bits(INPUT,3) + //val s_axi_awqos = Bits(INPUT,4) + val s_axi_awvalid = Bool(INPUT) + val s_axi_awready = Bool(OUTPUT) + //slave interface write data + val s_axi_wdata = Bits(INPUT,64) + val s_axi_wstrb = Bits(INPUT,8) + val s_axi_wlast = Bool(INPUT) + val s_axi_wvalid = Bool(INPUT) + val s_axi_wready = Bool(OUTPUT) + //slave interface write response + val s_axi_bready = Bool(INPUT) + val s_axi_bid = Bits(OUTPUT,4) + val s_axi_bresp = Bits(OUTPUT,2) + val s_axi_bvalid = Bool(OUTPUT) + //slave interface read address + val s_axi_arid = Bits(INPUT,4) + val s_axi_araddr = Bits(INPUT,32) + val s_axi_arregion = Bits(INPUT,4) + val s_axi_arlen = Bits(INPUT,8) + val s_axi_arsize = Bits(INPUT,3) + val s_axi_arburst = Bits(INPUT,2) + //val s_axi_arlock = Bits(INPUT,1) + //val s_axi_arcache = Bits(INPUT,4) + //val s_axi_arprot = Bits(INPUT,3) + //val s_axi_arqos = Bits(INPUT,4) + val s_axi_arvalid = Bool(INPUT) + val s_axi_arready = Bool(OUTPUT) + //slave interface read data + val s_axi_rready = Bool(INPUT) + val s_axi_rid = Bits(OUTPUT,4) + val s_axi_rdata = Bits(OUTPUT,64) + val s_axi_rresp = Bits(OUTPUT,2) + val s_axi_rlast = Bool(OUTPUT) + val s_axi_rvalid = Bool(OUTPUT) + + //axi master + //-{id,region,qos} + //slave interface write address ports + //val m_axi_awid = Bits(OUTPUT,4) + val m_axi_awaddr = Bits(OUTPUT,32) + //val m_axi_awregion = Bits(OUTPUT,4) + val m_axi_awlen = Bits(OUTPUT,8) + val m_axi_awsize = Bits(OUTPUT,3) + val m_axi_awburst = Bits(OUTPUT,2) + val m_axi_awlock = Bool(OUTPUT) + val m_axi_awcache = Bits(OUTPUT,4) + val m_axi_awprot = Bits(OUTPUT,3) + //val m_axi_awqos = Bits(OUTPUT,4) + val m_axi_awvalid = Bool(OUTPUT) + val m_axi_awready = Bool(INPUT) + //slave interface write data ports + val m_axi_wdata = Bits(OUTPUT,64) + val m_axi_wstrb = Bits(OUTPUT,8) + val m_axi_wlast = Bool(OUTPUT) + val m_axi_wvalid = Bool(OUTPUT) + val m_axi_wready = Bool(INPUT) + //slave interface write response ports + val m_axi_bready = Bool(OUTPUT) + //val m_axi_bid = Bits(INPUT,4) + val m_axi_bresp = Bits(INPUT,2) + val m_axi_bvalid = Bool(INPUT) + //slave interface read address ports + //val m_axi_arid = Bits(OUTPUT,4) + val m_axi_araddr = Bits(OUTPUT,32) + //val m_axi_arregion = Bits(OUTPUT,4) + val m_axi_arlen = Bits(OUTPUT,8) + val m_axi_arsize = Bits(OUTPUT,3) + val m_axi_arburst = Bits(OUTPUT,2) + val m_axi_arlock = Bits(OUTPUT,1) + val m_axi_arcache = Bits(OUTPUT,4) + val m_axi_arprot = Bits(OUTPUT,3) + //val m_axi_arqos = Bits(OUTPUT,4) + val m_axi_arvalid = Bool(OUTPUT) + val m_axi_arready = Bool(INPUT) + //slave interface read data ports + val m_axi_rready = Bool(OUTPUT) + //val m_axi_rid = Bits(INPUT,4) + val m_axi_rdata = Bits(INPUT,64) + val m_axi_rresp = Bits(INPUT,2) + val m_axi_rlast = Bool(INPUT) + val m_axi_rvalid = Bool(INPUT) + + //axi lite slave for control + val s_axi_ctl_awaddr = Bits(INPUT,32) + val s_axi_ctl_awvalid = Bool(INPUT) + val s_axi_ctl_awready = Bool(OUTPUT) + val s_axi_ctl_wdata = Bits(INPUT,32) + val s_axi_ctl_wstrb = Bits(INPUT,4) + val s_axi_ctl_wvalid = Bool(INPUT) + val s_axi_ctl_wready = Bool(OUTPUT) + val s_axi_ctl_bresp = Bits(OUTPUT,2) + val s_axi_ctl_bvalid = Bool(OUTPUT) + val s_axi_ctl_bready = Bool(INPUT) + val s_axi_ctl_araddr = Bits(INPUT,32) + val s_axi_ctl_arvalid = Bool(INPUT) + val s_axi_ctl_arready = Bool(OUTPUT) + val s_axi_ctl_rdata = Bits(OUTPUT,32) + val s_axi_ctl_rresp = Bits(OUTPUT,2) + val s_axi_ctl_rvalid = Bool(OUTPUT) + val s_axi_ctl_rready = Bool(INPUT) + } +} +//scalastyle:off + +//wrap vc707_axi_to_pcie_x1 black box in Nasti Bundles + +class VC707AXIToPCIeX1(implicit p:Parameters) extends LazyModule +{ + val slave = AXI4SlaveNode(AXI4SlavePortParameters( + slaves = Seq(AXI4SlaveParameters( + address = List(AddressSet(0x60000000L, 0x1fffffffL)), + executable = true, + supportsWrite = TransferSizes(1, 256), + supportsRead = TransferSizes(1, 256), + interleavedId = Some(0))), // the Xilinx IP is friendly + beatBytes = 8)) + + val control = AXI4SlaveNode(AXI4SlavePortParameters( + slaves = Seq(AXI4SlaveParameters( + address = List(AddressSet(0x50000000L, 0x03ffffffL)), + supportsWrite = TransferSizes(1, 4), + supportsRead = TransferSizes(1, 4), + interleavedId = Some(0))), // no read interleaving b/c AXI-lite + beatBytes = 4)) + + val master = AXI4MasterNode(AXI4MasterPortParameters( + masters = Seq(AXI4MasterParameters( + id = IdRange(0, 1), + aligned = false)))) + + lazy val module = new LazyModuleImp(this) { + // The master on the control port must be AXI-lite + require (control.edgesIn(0).master.endId == 1) + // Must have exactly the right number of idBits + require (slave.edgesIn(0).bundle.idBits == 4) + + class VC707AXIToPCIeX1IOBundle extends Bundle with VC707AXIToPCIeX1IOSerial + with VC707AXIToPCIeX1IOClocksReset; + + val io = new Bundle { + val port = new VC707AXIToPCIeX1IOBundle + val slave_in = slave.bundleIn + val control_in = control.bundleIn + val master_out = master.bundleOut + val REFCLK = Bool(INPUT) + val interrupt_out = Bool(OUTPUT) + } + + val blackbox = Module(new vc707axi_to_pcie_x1) + + val s = io.slave_in(0) + val c = io.control_in(0) + val m = io.master_out(0) + + //to top level + blackbox.io.axi_aresetn := io.port.axi_aresetn + io.port.axi_aclk_out := blackbox.io.axi_aclk_out + io.port.axi_ctl_aclk_out := blackbox.io.axi_ctl_aclk_out + io.port.mmcm_lock := blackbox.io.mmcm_lock + io.port.pci_exp_txp := blackbox.io.pci_exp_txp + io.port.pci_exp_txn := blackbox.io.pci_exp_txn + blackbox.io.pci_exp_rxp := io.port.pci_exp_rxp + blackbox.io.pci_exp_rxn := io.port.pci_exp_rxn + io.interrupt_out := blackbox.io.interrupt_out + blackbox.io.REFCLK := io.REFCLK + + //s + //AXI4 signals ordered as per AXI4 Specification (Release D) Section A.2 + //-{lock, cache, prot, qos} + //-{aclk, aresetn, awuser, wid, wuser, buser, ruser} + //global signals + //aclk := + //aresetn := + //slave interface write address + blackbox.io.s_axi_awid := s.aw.bits.id + blackbox.io.s_axi_awaddr := s.aw.bits.addr + blackbox.io.s_axi_awlen := s.aw.bits.len + blackbox.io.s_axi_awsize := s.aw.bits.size + blackbox.io.s_axi_awburst := s.aw.bits.burst + //blackbox.io.s_axi_awlock := s.aw.bits.lock + //blackbox.io.s_axi_awcache := s.aw.bits.cache + //blackbox.io.s_axi_awprot := s.aw.bits.prot + //blackbox.io.s_axi_awqos := s.aw.bits.qos + blackbox.io.s_axi_awregion := UInt(0) + //blackbox.io.awuser := s.aw.bits.user + blackbox.io.s_axi_awvalid := s.aw.valid + s.aw.ready := blackbox.io.s_axi_awready + //slave interface write data ports + //blackbox.io.s_axi_wid := s.w.bits.id + blackbox.io.s_axi_wdata := s.w.bits.data + blackbox.io.s_axi_wstrb := s.w.bits.strb + blackbox.io.s_axi_wlast := s.w.bits.last + //blackbox.io.s_axi_wuser := s.w.bits.user + blackbox.io.s_axi_wvalid := s.w.valid + s.w.ready := blackbox.io.s_axi_wready + //slave interface write response + s.b.bits.id := blackbox.io.s_axi_bid + s.b.bits.resp := blackbox.io.s_axi_bresp + //s.b.bits.user := blackbox.io.s_axi_buser + s.b.valid := blackbox.io.s_axi_bvalid + blackbox.io.s_axi_bready := s.b.ready + //slave AXI interface read address ports + blackbox.io.s_axi_arid := s.ar.bits.id + blackbox.io.s_axi_araddr := s.ar.bits.addr + blackbox.io.s_axi_arlen := s.ar.bits.len + blackbox.io.s_axi_arsize := s.ar.bits.size + blackbox.io.s_axi_arburst := s.ar.bits.burst + //blackbox.io.s_axi_arlock := s.ar.bits.lock + //blackbox.io.s_axi_arcache := s.ar.bits.cache + //blackbox.io.s_axi_arprot := s.ar.bits.prot + //blackbox.io.s_axi_arqos := s.ar.bits.qos + blackbox.io.s_axi_arregion := UInt(0) + //blackbox.io.s_axi_aruser := s.ar.bits.user + blackbox.io.s_axi_arvalid := s.ar.valid + s.ar.ready := blackbox.io.s_axi_arready + //slave AXI interface read data ports + s.r.bits.id := blackbox.io.s_axi_rid + s.r.bits.data := blackbox.io.s_axi_rdata + s.r.bits.resp := blackbox.io.s_axi_rresp + s.r.bits.last := blackbox.io.s_axi_rlast + //s.r.bits.ruser := blackbox.io.s_axi_ruser + s.r.valid := blackbox.io.s_axi_rvalid + blackbox.io.s_axi_rready := s.r.ready + + //ctl + //axi-lite slave interface write address + blackbox.io.s_axi_ctl_awaddr := c.aw.bits.addr + blackbox.io.s_axi_ctl_awvalid := c.aw.valid + c.aw.ready := blackbox.io.s_axi_ctl_awready + //axi-lite slave interface write data ports + blackbox.io.s_axi_ctl_wdata := c.w.bits.data + blackbox.io.s_axi_ctl_wstrb := c.w.bits.strb + blackbox.io.s_axi_ctl_wvalid := c.w.valid + c.w.ready := blackbox.io.s_axi_ctl_wready + //axi-lite slave interface write response + blackbox.io.s_axi_ctl_bready := c.b.ready + c.b.bits.id := UInt(0) + c.b.bits.resp := blackbox.io.s_axi_ctl_bresp + c.b.valid := blackbox.io.s_axi_ctl_bvalid + //axi-lite slave AXI interface read address ports + blackbox.io.s_axi_ctl_araddr := c.ar.bits.addr + blackbox.io.s_axi_ctl_arvalid := c.ar.valid + c.ar.ready := blackbox.io.s_axi_ctl_arready + //slave AXI interface read data ports + blackbox.io.s_axi_ctl_rready := c.r.ready + c.r.bits.id := UInt(0) + c.r.bits.data := blackbox.io.s_axi_ctl_rdata + c.r.bits.resp := blackbox.io.s_axi_ctl_rresp + c.r.bits.last := Bool(true) + c.r.valid := blackbox.io.s_axi_ctl_rvalid + + //m + //AXI4 signals ordered per AXI4 Specification (Release D) Section A.2 + //-{id,region,qos} + //-{aclk, aresetn, awuser, wid, wuser, buser, ruser} + //global signals + //aclk := + //aresetn := + //master interface write address + m.aw.bits.id := UInt(0) + m.aw.bits.addr := blackbox.io.m_axi_awaddr + m.aw.bits.len := blackbox.io.m_axi_awlen + m.aw.bits.size := blackbox.io.m_axi_awsize + m.aw.bits.burst := blackbox.io.m_axi_awburst + m.aw.bits.lock := blackbox.io.m_axi_awlock + m.aw.bits.cache := blackbox.io.m_axi_awcache + m.aw.bits.prot := blackbox.io.m_axi_awprot + m.aw.bits.qos := UInt(0) + //m.aw.bits.region := blackbox.io.m_axi_awregion + //m.aw.bits.user := blackbox.io.m_axi_awuser + m.aw.valid := blackbox.io.m_axi_awvalid + blackbox.io.m_axi_awready := m.aw.ready + + //master interface write data ports + m.w.bits.data := blackbox.io.m_axi_wdata + m.w.bits.strb := blackbox.io.m_axi_wstrb + m.w.bits.last := blackbox.io.m_axi_wlast + //m.w.bits.user := blackbox.io.m_axi_wuser + m.w.valid := blackbox.io.m_axi_wvalid + blackbox.io.m_axi_wready := m.w.ready + + //master interface write response + //blackbox.io.m_axi_bid := m.b.bits.id + blackbox.io.m_axi_bresp := m.b.bits.resp + //blackbox.io.m_axi_buser := m.b.bits.user + blackbox.io.m_axi_bvalid := m.b.valid + m.b.ready := blackbox.io.m_axi_bready + + //master AXI interface read address ports + m.ar.bits.id := UInt(0) + m.ar.bits.addr := blackbox.io.m_axi_araddr + m.ar.bits.len := blackbox.io.m_axi_arlen + m.ar.bits.size := blackbox.io.m_axi_arsize + m.ar.bits.burst := blackbox.io.m_axi_arburst + m.ar.bits.lock := blackbox.io.m_axi_arlock + m.ar.bits.cache := blackbox.io.m_axi_arcache + m.ar.bits.prot := blackbox.io.m_axi_arprot + m.ar.bits.qos := UInt(0) + //m.ar.bits.region := blackbox.io.m_axi_arregion + //m.ar.bits.user := blackbox.io.s_axi_aruser + m.ar.valid := blackbox.io.m_axi_arvalid + blackbox.io.m_axi_arready := m.ar.ready + + //master AXI interface read data ports + //blackbox.io.m_axi_rid := m.r.bits.id + blackbox.io.m_axi_rdata := m.r.bits.data + blackbox.io.m_axi_rresp := m.r.bits.resp + blackbox.io.m_axi_rlast := m.r.bits.last + //blackbox.io.s_axi_ruser := s.bits.ruser + blackbox.io.m_axi_rvalid := m.r.valid + m.r.ready := blackbox.io.m_axi_rready + } +} diff --git a/src/main/scala/ip/xilinx/vc707mig/vc707mig.scala b/src/main/scala/ip/xilinx/vc707mig/vc707mig.scala new file mode 100644 index 0000000..923956b --- /dev/null +++ b/src/main/scala/ip/xilinx/vc707mig/vc707mig.scala @@ -0,0 +1,110 @@ +// See LICENSE for license details. +package sifive.blocks.ip.xilinx.vc707mig + +import Chisel._ +import config._ +import junctions._ + +// IP VLNV: xilinx.com:customize_ip:vc707mig:1.0 +// Black Box +// Signals named _exactly_ as per MIG generated verilog + +trait VC707MIGUnidirectionalIODDR extends Bundle { + //outputs + val ddr3_addr = Bits(OUTPUT,14) + val ddr3_ba = Bits(OUTPUT,3) + val ddr3_ras_n = Bool(OUTPUT) + val ddr3_cas_n = Bool(OUTPUT) + val ddr3_we_n = Bool(OUTPUT) + val ddr3_reset_n = Bool(OUTPUT) + val ddr3_ck_p = Bits(OUTPUT,1) + val ddr3_ck_n = Bits(OUTPUT,1) + val ddr3_cke = Bits(OUTPUT,1) + val ddr3_cs_n = Bits(OUTPUT,1) + val ddr3_dm = Bits(OUTPUT,8) + val ddr3_odt = Bits(OUTPUT,1) +} + +//reused directly in io bundle for sifive.blocks.devices.xilinxvc707mig +trait VC707MIGUnidirectionalIOClocksReset extends Bundle { + //inputs + //differential system clocks + val sys_clk_n = Bool(INPUT) + val sys_clk_p = Bool(INPUT) + //user interface signals + val ui_clk = Clock(OUTPUT) + val ui_clk_sync_rst = Bool(OUTPUT) + val mmcm_locked = Bool(OUTPUT) + val aresetn = Bool(INPUT) + //misc + val init_calib_complete = Bool(OUTPUT) + val sys_rst = Bool(INPUT) +} + +//scalastyle:off +//turn off linter: blackbox name must match verilog module +class vc707mig(implicit val p:Parameters) extends BlackBox +{ + val io = new Bundle with VC707MIGUnidirectionalIODDR + with VC707MIGUnidirectionalIOClocksReset { + // bidirectional signals on blackbox interface + // defined here as an output so "__inout" signal name does not have to be used + // verilog does not check the + val ddr3_dq = Bits(OUTPUT,64) + val ddr3_dqs_n = Bits(OUTPUT,8) + val ddr3_dqs_p = Bits(OUTPUT,8) + // User interface signals + val app_sr_req = Bool(INPUT) + val app_ref_req = Bool(INPUT) + val app_zq_req = Bool(INPUT) + val app_sr_active = Bool(OUTPUT) + val app_ref_ack = Bool(OUTPUT) + val app_zq_ack = Bool(OUTPUT) + //axi_s + //slave interface write address ports + val s_axi_awid = Bits(INPUT,4) + val s_axi_awaddr = Bits(INPUT,30) + val s_axi_awlen = Bits(INPUT,8) + val s_axi_awsize = Bits(INPUT,3) + val s_axi_awburst = Bits(INPUT,2) + val s_axi_awlock = Bits(INPUT,1) + val s_axi_awcache = Bits(INPUT,4) + val s_axi_awprot = Bits(INPUT,3) + val s_axi_awqos = Bits(INPUT,4) + val s_axi_awvalid = Bool(INPUT) + val s_axi_awready = Bool(OUTPUT) + //slave interface write data ports + val s_axi_wdata = Bits(INPUT,64) + val s_axi_wstrb = Bits(INPUT,8) + val s_axi_wlast = Bool(INPUT) + val s_axi_wvalid = Bool(INPUT) + val s_axi_wready = Bool(OUTPUT) + //slave interface write response ports + val s_axi_bready = Bool(INPUT) + val s_axi_bid = Bits(OUTPUT,4) + val s_axi_bresp = Bits(OUTPUT,2) + val s_axi_bvalid = Bool(OUTPUT) + //slave interface read address ports + val s_axi_arid = Bits(INPUT,4) + val s_axi_araddr = Bits(INPUT,30) + val s_axi_arlen = Bits(INPUT,8) + val s_axi_arsize = Bits(INPUT,3) + val s_axi_arburst = Bits(INPUT,2) + val s_axi_arlock = Bits(INPUT,1) + val s_axi_arcache = Bits(INPUT,4) + val s_axi_arprot = Bits(INPUT,3) + val s_axi_arqos = Bits(INPUT,4) + val s_axi_arvalid = Bool(INPUT) + val s_axi_arready = Bool(OUTPUT) + //slave interface read data ports + val s_axi_rready = Bool(INPUT) + val s_axi_rid = Bits(OUTPUT,4) + val s_axi_rdata = Bits(OUTPUT,64) + val s_axi_rresp = Bits(OUTPUT,2) + val s_axi_rlast = Bool(OUTPUT) + val s_axi_rvalid = Bool(OUTPUT) + //misc + val device_temp = Bits(OUTPUT,12) + } +} +//scalastyle:on diff --git a/src/main/scala/util/DeglitchShiftRegister.scala b/src/main/scala/util/DeglitchShiftRegister.scala new file mode 100644 index 0000000..e40de96 --- /dev/null +++ b/src/main/scala/util/DeglitchShiftRegister.scala @@ -0,0 +1,28 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ + +//Allows us to specify a different clock for a shift register +// and to force input to be high for > 1 cycle. +class DeglitchShiftRegister(shift: Int) extends Module { + val io = new Bundle { + val d = Bool(INPUT) + val q = Bool(OUTPUT) + } + val sync = ShiftRegister(io.d, shift) + val last = ShiftRegister(sync, 1) + io.q := sync & last +} + +object DeglitchShiftRegister { + def apply (shift: Int, d: Bool, clock: Clock, + name: Option[String] = None): Bool = { + val deglitch = Module (new DeglitchShiftRegister(shift)) + name.foreach(deglitch.suggestName(_)) + deglitch.clock := clock + deglitch.reset := Bool(false) + deglitch.io.d := d + deglitch.io.q + } +} diff --git a/src/main/scala/util/RegMapFIFO.scala b/src/main/scala/util/RegMapFIFO.scala new file mode 100644 index 0000000..aaeba59 --- /dev/null +++ b/src/main/scala/util/RegMapFIFO.scala @@ -0,0 +1,41 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ +import regmapper._ + +// MSB indicates full status +object NonBlockingEnqueue { + def apply(enq: DecoupledIO[UInt], regWidth: Int = 32): Seq[RegField] = { + val enqWidth = enq.bits.getWidth + require(enqWidth > 0) + require(regWidth > enqWidth) + Seq( + RegField(enqWidth, + RegReadFn(UInt(0)), + RegWriteFn((valid, data) => { + enq.valid := valid + enq.bits := data + Bool(true) + })), + RegField(regWidth - enqWidth - 1), + RegField.r(1, !enq.ready)) + } +} + +// MSB indicates empty status +object NonBlockingDequeue { + def apply(deq: DecoupledIO[UInt], regWidth: Int = 32): Seq[RegField] = { + val deqWidth = deq.bits.getWidth + require(deqWidth > 0) + require(regWidth > deqWidth) + Seq( + RegField.r(deqWidth, + RegReadFn(ready => { + deq.ready := ready + (Bool(true), deq.bits) + })), + RegField(regWidth - deqWidth - 1), + RegField.r(1, !deq.valid)) + } +} diff --git a/src/main/scala/util/ResetCatchAndSync.scala b/src/main/scala/util/ResetCatchAndSync.scala new file mode 100644 index 0000000..cc35686 --- /dev/null +++ b/src/main/scala/util/ResetCatchAndSync.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ +import util.AsyncResetRegVec + +/** Reset: asynchronous assert, + * synchronous de-assert + * + */ + +class ResetCatchAndSync (sync: Int = 3) extends Module { + + val io = new Bundle { + val sync_reset = Bool(OUTPUT) + } + + val reset_n_catch_reg = Module (new AsyncResetRegVec(sync, 0)) + + reset_n_catch_reg.io.en := Bool(true) + reset_n_catch_reg.io.d := Cat(Bool(true), reset_n_catch_reg.io.q >> 1) + + io.sync_reset := ~reset_n_catch_reg.io.q(0) + +} + +object ResetCatchAndSync { + + def apply(clk: Clock, rst: Bool, sync: Int = 3, name: Option[String] = None): Bool = { + + val catcher = Module (new ResetCatchAndSync(sync)) + if (name.isDefined) {catcher.suggestName(name.get)} + catcher.clock := clk + catcher.reset := rst + + catcher.io.sync_reset + } + + def apply(clk: Clock, rst: Bool, sync: Int, name: String): Bool = apply(clk, rst, sync, Some(name)) + def apply(clk: Clock, rst: Bool, name: String): Bool = apply(clk, rst, name = Some(name)) + +} diff --git a/src/main/scala/util/SRLatch.scala b/src/main/scala/util/SRLatch.scala new file mode 100644 index 0000000..95fae4f --- /dev/null +++ b/src/main/scala/util/SRLatch.scala @@ -0,0 +1,12 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ + +class SRLatch extends BlackBox { + val io = new Bundle { + val set = Bool(INPUT) + val reset = Bool(INPUT) + val q = Bool(OUTPUT) + } +} diff --git a/src/main/scala/util/ShiftReg.scala b/src/main/scala/util/ShiftReg.scala new file mode 100644 index 0000000..53bb29e --- /dev/null +++ b/src/main/scala/util/ShiftReg.scala @@ -0,0 +1,11 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ + +object ShiftRegisterInit { + def apply[T <: Data](in: T, n: Int, init: T): T = + (0 until n).foldLeft(in) { + case (next, _) => Reg(next, next = next, init = init) + } +} diff --git a/src/main/scala/util/Timer.scala b/src/main/scala/util/Timer.scala new file mode 100644 index 0000000..e0cba87 --- /dev/null +++ b/src/main/scala/util/Timer.scala @@ -0,0 +1,104 @@ +// See LICENSE for license details. +package sifive.blocks.util + +import Chisel._ +import Chisel.ImplicitConversions._ +import regmapper._ +import util.WideCounter + +class SlaveRegIF(w: Int) extends Bundle { + val write = Valid(UInt(width = w)).flip + val read = UInt(OUTPUT, w) + + override def cloneType: this.type = new SlaveRegIF(w).asInstanceOf[this.type] + + def toRegField(dummy: Int = 0): RegField = { + def writeFn(valid: Bool, data: UInt): Bool = { + write.valid := valid + write.bits := data + Bool(true) + } + RegField(w, RegReadFn(read), RegWriteFn((v, d) => writeFn(v, d))) + } +} + + +abstract class GenericTimer extends Module { + protected def countWidth: Int + protected def cmpWidth: Int + protected def ncmp: Int + protected def countAlways: Bool + protected def countEn: Bool + protected def feed: Bool + protected def ip: UInt + protected def countAwake: Bool = Bool(false) + protected def unlocked: Bool = Bool(true) + protected def rsten: Bool = Bool(false) + protected def deglitch: Bool = Bool(false) + protected def sticky: Bool = Bool(false) + protected def oneShot: Bool = Bool(false) + protected def center: UInt = UInt(0) + protected def gang: UInt = UInt(0) + protected val scaleWidth = 4 + protected val regWidth = 32 + val maxcmp = 4 + require(ncmp <= maxcmp) + + class GenericTimerIO extends Bundle { + val regs = new Bundle { + val cfg = new SlaveRegIF(regWidth) + val countLo = new SlaveRegIF(regWidth) + val countHi = new SlaveRegIF(regWidth) + val s = new SlaveRegIF(cmpWidth) + val cmp = Vec(ncmp, new SlaveRegIF(cmpWidth)) + val feed = new SlaveRegIF(regWidth) + val key = new SlaveRegIF(regWidth) + } + val ip = Vec(ncmp, Bool()).asOutput + } + + def io: GenericTimerIO + + protected val scale = RegEnable(io.regs.cfg.write.bits(scaleWidth-1, 0), io.regs.cfg.write.valid && unlocked) + protected lazy val zerocmp = RegEnable(io.regs.cfg.write.bits(9), io.regs.cfg.write.valid && unlocked) + protected val cmp = io.regs.cmp.map(c => RegEnable(c.write.bits, c.write.valid && unlocked)) + + protected val count = WideCounter(countWidth, countEn, reset = false) + when (io.regs.countLo.write.valid && unlocked) { count := Cat(count >> regWidth, io.regs.countLo.write.bits) } + if (countWidth > regWidth) when (io.regs.countHi.write.valid && unlocked) { count := Cat(io.regs.countHi.write.bits, count(regWidth-1, 0)) } + + // generate periodic interrupt + protected val s = (count >> scale)(cmpWidth-1, 0) + // reset counter when fed or elapsed + protected val elapsed = + for (i <- 0 until ncmp) + yield Mux(s(cmpWidth-1) && center(i), ~s, s) >= cmp(i) + protected val countReset = feed || (zerocmp && elapsed(0)) + when (countReset) { count := 0 } + + io.regs.cfg.read := Cat(ip, gang | UInt(0, maxcmp), UInt(0, maxcmp), center | UInt(0, maxcmp), + UInt(0, 2), countAwake || oneShot, countAlways, UInt(0, 1), deglitch, zerocmp, rsten || sticky, UInt(0, 8-scaleWidth), scale) + io.regs.countLo.read := count + io.regs.countHi.read := count >> regWidth + io.regs.s.read := s + (io.regs.cmp zip cmp) map { case (r, c) => r.read := c } + io.regs.feed.read := 0 + io.regs.key.read := unlocked + io.ip := io.ip.fromBits(ip) +} + + +object GenericTimer { + def timerRegMap(t: GenericTimer, offset: Int, regBytes: Int): Seq[(Int, Seq[RegField])] = { + val regs = Seq( + 0 -> t.io.regs.cfg, + 2 -> t.io.regs.countLo, + 3 -> t.io.regs.countHi, + 4 -> t.io.regs.s, + 6 -> t.io.regs.feed, + 7 -> t.io.regs.key) + val cmpRegs = t.io.regs.cmp.zipWithIndex map { case (r, i) => (8 + i) -> r } + for ((i, r) <- (regs ++ cmpRegs)) + yield (offset + regBytes*i) -> Seq(r.toRegField()) + } +} diff --git a/vsrc/SRLatch.v b/vsrc/SRLatch.v new file mode 100644 index 0000000..d4849a1 --- /dev/null +++ b/vsrc/SRLatch.v @@ -0,0 +1,22 @@ +// See LICENSE for license details. +module SRLatch ( + input set, + input reset, + output q +); + + reg latch; + + // synopsys async_set_reset "set" + // synopsys one_hot "set, reset" + always @(set or reset) + begin + if (set) + latch <= 1'b1; + else if (reset) + latch <= 1'b0; + end + + assign q = latch; + +endmodule diff --git a/vsrc/vc707reset.v b/vsrc/vc707reset.v new file mode 100644 index 0000000..8501027 --- /dev/null +++ b/vsrc/vc707reset.v @@ -0,0 +1,78 @@ +// See LICENSE for license details. +`timescale 1ns/1ps +`default_nettype none +`define RESET_SYNC 4 +`define DEBOUNCE_BITS 8 + +module vc707reset( + // Asynchronous reset input, should be held high until + // all clocks are locked and power is stable. + input wire areset, + // Clock domains are brought up in increasing order + // All clocks are reset for at least 2^DEBOUNCE_BITS * period(clock1) + input wire clock1, + output wire reset1, + input wire clock2, + output wire reset2, + input wire clock3, + output wire reset3, + input wire clock4, + output wire reset4 +); + sifive_reset_hold hold_clock0(areset, clock1, reset1); + sifive_reset_sync sync_clock2(reset1, clock2, reset2); + sifive_reset_sync sync_clock3(reset2, clock3, reset3); + sifive_reset_sync sync_clock4(reset3, clock4, reset4); +endmodule + +// Assumes that areset is held for more than one clock +// Allows areset to be deasserted asynchronously +module sifive_reset_sync( + input wire areset, + input wire clock, + output wire reset +); + reg [`RESET_SYNC-1:0] gen_reset = {`RESET_SYNC{1'b1}}; + always @(posedge clock, posedge areset) begin + if (areset) begin + gen_reset <= {`RESET_SYNC{1'b1}}; + end else begin + gen_reset <= {1'b0,gen_reset[`RESET_SYNC-1:1]}; + end + end + assign reset = gen_reset[0]; +endmodule + +module sifive_reset_hold( + input wire areset, + input wire clock, + output wire reset +); + wire raw_reset; + reg [`RESET_SYNC-1:0] sync_reset = {`RESET_SYNC{1'b1}}; + reg [`DEBOUNCE_BITS:0] debounce_reset = {`DEBOUNCE_BITS{1'b1}}; + wire out_reset; + + // Captures reset even if clock is not running + sifive_reset_sync capture(areset, clock, raw_reset); + + // Remove any glitches due to runt areset + always @(posedge clock) begin + sync_reset <= {raw_reset,sync_reset[`RESET_SYNC-1:1]}; + end + + // Debounce the reset + assign out_reset = debounce_reset[`DEBOUNCE_BITS]; + always @(posedge clock) begin + if (sync_reset[0]) begin + debounce_reset <= {(`DEBOUNCE_BITS+1){1'b1}}; + end else begin + debounce_reset <= debounce_reset - out_reset; + end + end + + assign reset = out_reset; + +endmodule + +`default_nettype wire