Initial commit.
This commit is contained in:
commit
7916ef5249
202
LICENSE
Normal file
202
LICENSE
Normal file
@ -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.
|
295
src/main/scala/devices/gpio/GPIO.scala
Normal file
295
src/main/scala/devices/gpio/GPIO.scala
Normal file
@ -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)
|
22
src/main/scala/devices/gpio/GPIOCtrlRegs.scala
Normal file
22
src/main/scala/devices/gpio/GPIOCtrlRegs.scala
Normal file
@ -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
|
||||||
|
}
|
28
src/main/scala/devices/gpio/GPIOPeriphery.scala
Normal file
28
src/main/scala/devices/gpio/GPIOPeriphery.scala
Normal file
@ -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
|
||||||
|
}
|
43
src/main/scala/devices/gpio/JTAG.scala
Normal file
43
src/main/scala/devices/gpio/JTAG.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
105
src/main/scala/devices/mockaon/MockAON.scala
Normal file
105
src/main/scala/devices/mockaon/MockAON.scala
Normal file
@ -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)
|
42
src/main/scala/devices/mockaon/MockAONPeriphery.scala
Normal file
42
src/main/scala/devices/mockaon/MockAONPeriphery.scala
Normal file
@ -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
|
||||||
|
|
||||||
|
}
|
181
src/main/scala/devices/mockaon/MockAONWrapper.scala
Normal file
181
src/main/scala/devices/mockaon/MockAONWrapper.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
152
src/main/scala/devices/mockaon/PMU.scala
Normal file
152
src/main/scala/devices/mockaon/PMU.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
58
src/main/scala/devices/mockaon/WatchdogTimer.scala
Normal file
58
src/main/scala/devices/mockaon/WatchdogTimer.scala
Normal file
@ -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
|
||||||
|
}
|
82
src/main/scala/devices/pwm/PWM.scala
Normal file
82
src/main/scala/devices/pwm/PWM.scala
Normal file
@ -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)
|
58
src/main/scala/devices/pwm/PWMPeriphery.scala
Normal file
58
src/main/scala/devices/pwm/PWMPeriphery.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
40
src/main/scala/devices/spi/SPIArbiter.scala
Normal file
40
src/main/scala/devices/spi/SPIArbiter.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
src/main/scala/devices/spi/SPIBundle.scala
Normal file
106
src/main/scala/devices/spi/SPIBundle.scala
Normal file
@ -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()
|
||||||
|
}
|
33
src/main/scala/devices/spi/SPIConsts.scala
Normal file
33
src/main/scala/devices/spi/SPIConsts.scala
Normal file
@ -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)
|
||||||
|
}
|
62
src/main/scala/devices/spi/SPIFIFO.scala
Normal file
62
src/main/scala/devices/spi/SPIFIFO.scala
Normal file
@ -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)
|
||||||
|
}
|
162
src/main/scala/devices/spi/SPIFlash.scala
Normal file
162
src/main/scala/devices/spi/SPIFlash.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/main/scala/devices/spi/SPIMedia.scala
Normal file
121
src/main/scala/devices/spi/SPIMedia.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/main/scala/devices/spi/SPIPeriphery.scala
Normal file
57
src/main/scala/devices/spi/SPIPeriphery.scala
Normal file
@ -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
|
||||||
|
}
|
157
src/main/scala/devices/spi/SPIPhysical.scala
Normal file
157
src/main/scala/devices/spi/SPIPhysical.scala
Normal file
@ -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<<i)))
|
||||||
|
when (setup) {
|
||||||
|
txd := Mux1H(txd_sel, txd_shf)
|
||||||
|
}
|
||||||
|
|
||||||
|
val tx = (ctrl.fmt.iodir === SPIDirection.Tx)
|
||||||
|
val txen_in = (proto.head +: proto.tail.map(_ && tx)).scanRight(Bool(false))(_ || _)
|
||||||
|
val txen = txen_in :+ txen_in.last
|
||||||
|
|
||||||
|
io.port.sck := sck
|
||||||
|
io.port.cs := Vec.fill(io.port.cs.size)(Bool(true)) // dummy
|
||||||
|
(io.port.dq zip (txd.toBools zip txen)).foreach {
|
||||||
|
case (dq, (o, oe)) =>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/main/scala/devices/spi/SPIPins.scala
Normal file
34
src/main/scala/devices/spi/SPIPins.scala
Normal file
@ -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)
|
||||||
|
}
|
30
src/main/scala/devices/spi/SPIRegs.scala
Normal file
30
src/main/scala/devices/spi/SPIRegs.scala
Normal file
@ -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
|
||||||
|
}
|
132
src/main/scala/devices/spi/TLSPI.scala
Normal file
132
src/main/scala/devices/spi/TLSPI.scala
Normal file
@ -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:_*)
|
||||||
|
}
|
||||||
|
}
|
114
src/main/scala/devices/spi/TLSPIFlash.scala
Normal file
114
src/main/scala/devices/spi/TLSPIFlash.scala
Normal file
@ -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:_*)
|
||||||
|
}
|
||||||
|
}
|
275
src/main/scala/devices/uart/UART.scala
Normal file
275
src/main/scala/devices/uart/UART.scala
Normal file
@ -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)
|
15
src/main/scala/devices/uart/UARTCtrlRegs.scala
Normal file
15
src/main/scala/devices/uart/UARTCtrlRegs.scala
Normal file
@ -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
|
||||||
|
}
|
54
src/main/scala/devices/uart/UARTPeriphery.scala
Normal file
54
src/main/scala/devices/uart/UARTPeriphery.scala
Normal file
@ -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))
|
||||||
|
}
|
158
src/main/scala/devices/xilinxvc707mig/XilinxVC707MIG.scala
Normal file
158
src/main/scala/devices/xilinxvc707mig/XilinxVC707MIG.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
18
src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala
Normal file
18
src/main/scala/ip/xilinx/ibufds_gte2/ibufds_gte2.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
110
src/main/scala/ip/xilinx/vc707mig/vc707mig.scala
Normal file
110
src/main/scala/ip/xilinx/vc707mig/vc707mig.scala
Normal file
@ -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
|
28
src/main/scala/util/DeglitchShiftRegister.scala
Normal file
28
src/main/scala/util/DeglitchShiftRegister.scala
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
41
src/main/scala/util/RegMapFIFO.scala
Normal file
41
src/main/scala/util/RegMapFIFO.scala
Normal file
@ -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))
|
||||||
|
}
|
||||||
|
}
|
42
src/main/scala/util/ResetCatchAndSync.scala
Normal file
42
src/main/scala/util/ResetCatchAndSync.scala
Normal file
@ -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))
|
||||||
|
|
||||||
|
}
|
12
src/main/scala/util/SRLatch.scala
Normal file
12
src/main/scala/util/SRLatch.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
11
src/main/scala/util/ShiftReg.scala
Normal file
11
src/main/scala/util/ShiftReg.scala
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
104
src/main/scala/util/Timer.scala
Normal file
104
src/main/scala/util/Timer.scala
Normal file
@ -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())
|
||||||
|
}
|
||||||
|
}
|
22
vsrc/SRLatch.v
Normal file
22
vsrc/SRLatch.v
Normal file
@ -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
|
78
vsrc/vc707reset.v
Normal file
78
vsrc/vc707reset.v
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user