Skip to content

Commit

Permalink
Merge pull request #10 from hutch31/master
Browse files Browse the repository at this point in the history
Added dclib components
  • Loading branch information
chick authored Feb 13, 2020
2 parents 2aa2a7e + 4e1522b commit ec5e73c
Show file tree
Hide file tree
Showing 14 changed files with 699 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ the community. This is the place to do it.
| uart | internal | Martin Schoeberl | A basic serial port (UART) |
| fifo | internal | Martin Schoeberl | Variations of FIFO queues |
| spi2wb | maven | Fabien Marteau | Drive a wishbone master bus with SPI |
| dclib | internal | Guy Hutchison | Utility components for DecoupledIO interfaces |

### Getting Started

Expand Down
104 changes: 104 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCArbiter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package chisel.lib.dclib

import chisel3._
import chisel3.util._

/**
* Round-robin arbiter
*
* Accepts number of inputs and arbitrates between them on a per-cycle basis.
*
* @param data Data type of item to be arbitrated
* @param inputs Number of inputs to arbiter
* @param locking Creates a locking arbiter with a rearb input
*/
class DCArbiter[D <: Data](data: D, inputs: Int, locking: Boolean) extends Module {
val io = IO(new Bundle {
val c = Vec(inputs, Flipped(Decoupled(data.cloneType)))
val p = Decoupled(data.cloneType)
val grant = Output(UInt(inputs.W))
val rearb = if(locking) Some(Input(UInt(inputs.W))) else None
})
override def desiredName: String = "DCArbiter_" + data.toString

val just_granted = RegInit(1.asUInt(inputs.W))
val to_be_granted = Wire(UInt(inputs.W))
val nxt_rr_locked = Wire(Bool())
val io_c_valid = Wire(UInt(inputs.W))

for (i <- 0 until inputs) {
io.c(i).ready := to_be_granted(i) && io.p.ready
}
io.grant := to_be_granted

def nxt_grant(cur_grant: UInt, cur_req: UInt, cur_accept: Bool): UInt = {
val msk_req = Wire(UInt(inputs.W))
val tmp_grant = Wire(UInt(inputs.W))
val tmp_grant2 = Wire(UInt(inputs.W))
val rv = Wire(UInt(inputs.W))

msk_req := cur_req & ~((cur_grant - 1.U) | cur_grant)
tmp_grant := msk_req & (~msk_req + 1.U)
tmp_grant2 := cur_req & (~cur_req + 1.U)


when(cur_accept) {
when(msk_req =/= 0.U) {
rv := tmp_grant
}.otherwise {
rv := tmp_grant2
}
}.elsewhen(cur_req =/= 0.U) {
when(msk_req =/= 0.U) {
rv := Cat(tmp_grant(0), tmp_grant(inputs - 1, 1))
}.otherwise {
rv := Cat(tmp_grant2(0), tmp_grant2(inputs - 1, 1))
}
}.otherwise {
rv := cur_grant
}
rv
}

io_c_valid := Cat(io.c.map(_.valid).reverse)

io.p.valid := io_c_valid.orR()
to_be_granted := just_granted

if (locking) {
val rr_locked = RegInit(false.B)

when ((io_c_valid & just_granted).orR() && !rr_locked) {
nxt_rr_locked := true.B
}.elsewhen ((io_c_valid & just_granted & io.rearb.get).orR()) {
nxt_rr_locked := false.B
}.otherwise {
nxt_rr_locked := rr_locked
}

when (nxt_rr_locked && (io_c_valid & just_granted).orR()) {
to_be_granted := just_granted
}.otherwise {
when (io.p.ready) {
to_be_granted := Cat(just_granted(0), just_granted(inputs-1,1))
}.otherwise {
to_be_granted := just_granted
}
}
} else {
nxt_rr_locked := false.B
to_be_granted := nxt_grant(just_granted, io_c_valid, io.p.ready)
}

when (to_be_granted =/= 0.U) {
just_granted := to_be_granted
}

io.p.bits := io.c(0).bits
for (i <- 0 until inputs) {
when (to_be_granted(i)) {
io.p.bits := io.c(i).bits
}
}
}

33 changes: 33 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCDemux.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package chisel.lib.dclib

import chisel3.util._
import chisel3._

/**
* Demultiplex a stream of tokens with an identifier "sel",
* as inverse of RRArbiter.
*
* @param data Data type of incoming/outgoing data
* @param n Number of mux outputs
*/
class DCDemux[D <: Data](data: D, n: Int) extends Module {
val io = IO(new Bundle {
val sel = Input(UInt(log2Ceil(n).W))
val c = Flipped(new DecoupledIO(data.cloneType))
val p = Vec(n, new DecoupledIO(data.cloneType))
})
override def desiredName: String = "DCDemux_" + data.toString

io.c.ready := 0.U
for (i <- 0 until n) {
io.p(i).bits := io.c.bits
when (i.U === io.sel) {
io.p(i).valid := io.c.valid
io.c.ready := io.p(i).ready
}.otherwise {
io.p(i).valid := 0.U
}
}
}


18 changes: 18 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCFull.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package chisel.lib.dclib
import chisel3._
import chisel3.util._

/**
* Provides timing closure on valid, ready and bits interfaces by
* using DCInput and DCOutput back to back. Effectively a 2-entry
* FIFO.
*/
object DCFull {
def apply[D <: Data](x : DecoupledIO[D]) : DecoupledIO[D] = {
val tin = Module(new DCInput(x.bits.cloneType))
val tout = Module(new DCOutput(x.bits.cloneType))
tin.io.enq <> x
tin.io.deq <> tout.io.enq
tout.io.deq
}
}
43 changes: 43 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCHold.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package chisel.lib.dclib

import chisel3._
import chisel3.util.DecoupledIO

/**
* Creates a ready/valid holding register, will not accept new data
* until current data word is unloaded.
*
* This block has no combinational paths through it, although it can
* accept data at a maximum of every other cycle.
*
* @param data The data type for the payload
*/
class DCHold[D <: Data](data: D) extends Module {
val io = IO(new Bundle {
val enq = Flipped(new DecoupledIO(data.cloneType))
val deq = new DecoupledIO(data.cloneType)
})
override def desiredName: String = "DCHold_" + data.toString

val p_valid = RegInit(init = 0.U)
val p_data = Reg(data.cloneType)

when (io.enq.valid && !p_valid) {
p_valid := io.enq.valid
p_data := io.enq.bits
}.elsewhen((p_valid & io.deq.ready) === 1.U) {
p_valid := 0.U
}
io.deq.valid := p_valid
io.deq.bits := p_data
io.enq.ready := ~p_valid
}

// Helper function for functional inference
object DCHold {
def apply[D <: Data](x : DecoupledIO[D]) : DecoupledIO[D] = {
val tout = Module(new DCHold(x.bits.cloneType))
tout.io.enq <> x
tout.io.deq
}
}
57 changes: 57 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCInput.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package chisel.lib.dclib

import chisel3._
import chisel3.util.DecoupledIO

/**
* Closes timing on the ready signal of a decoupled interface. Called an
* "input" module because if used on input interfaces provides registered-output
* ready. deq.valid and deq.bits have combination paths.
*
* Internally implements a single hold register to hold data in the event that
* deq interface is not ready
*/
class DCInput[D <: Data](data: D) extends Module {
val io = IO(new Bundle {
val enq = Flipped(new DecoupledIO(data.cloneType))
val deq = new DecoupledIO(data.cloneType)
})
override def desiredName: String = "DCInput_" + data.toString

// val r_valid = RegInit(false.B)
val ready_r = RegInit(true.B)
val occupied = RegInit(false.B)
val hold = Reg(data.cloneType)
val load = Wire(Bool())
val drain = Wire(Bool())

drain := occupied && io.deq.ready
load := io.enq.valid && ready_r && (!io.deq.ready || drain)

when (occupied) {
io.deq.bits := hold
}.otherwise {
io.deq.bits := io.enq.bits
}

io.deq.valid := io.enq.valid || occupied
when (load) {
occupied := true.B
hold := io.enq.bits
}.elsewhen (drain) {
occupied := false.B
}

ready_r := (!occupied && !load) || (drain && !load)
io.enq.ready := ready_r
}

// Helper function for functional inference
object DCInput {
def apply[D <: Data](x: DecoupledIO[D]): DecoupledIO[D] = {
val tout = Module(new DCInput(x.bits.cloneType))
tout.io.enq <> x
tout.io.deq
}
}

37 changes: 37 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCMirror.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package chisel.lib.dclib
import chisel3._
import chisel3.util._

/**
* Sends tokens to multiple output destinations, as selected by bit
* vector "dst". dst must have at least one bit set for correct
* operation.
*
* @param data Payload data type
* @param n Number of output destinations
*/
class DCMirror[D <: Data](data: D, n: Int) extends Module {
val io = IO(new Bundle {
val dst = Input(UInt(n.W))
val c = Flipped(new DecoupledIO(data.cloneType))
val p = Vec(n, new DecoupledIO(data.cloneType))
})

val p_data = Reg(data.cloneType)
val p_valid = RegInit(0.asUInt(n.W))
val p_ready = Cat(io.p.map(_.ready).reverse)
val nxt_accept = (p_valid === 0.U) || ((p_valid =/= 0.U) && ((p_valid & p_ready) === p_valid))

when (nxt_accept) {
p_valid := Fill(n, io.c.valid) & io.dst
p_data := io.c.bits
}.otherwise {
p_valid := p_valid & ~p_ready
}
io.c.ready := nxt_accept

for (i <- 0 until n) {
io.p(i).bits := p_data
io.p(i).valid := p_valid(i)
}
}
32 changes: 32 additions & 0 deletions src/main/scala/chisel/lib/dclib/DCOutput.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package chisel.lib.dclib

import chisel3._
import chisel3.util._

/**
* Closes output timing on an input of type D
* deq.valid and deq.bits will be registered, enq.ready will be combinatorial
*/
class DCOutput[D <: Data](data: D) extends Module {
val io = IO(new Bundle {
val enq = Flipped(new DecoupledIO(data.cloneType))
val deq = new DecoupledIO(data.cloneType)
})
override def desiredName: String = "DCOutput_" + data.toString

val r_valid = RegInit(false.B)

io.enq.ready := io.deq.ready || !r_valid
r_valid := io.enq.fire() || (r_valid && !io.deq.ready)
io.deq.bits := RegEnable(next=io.enq.bits, enable=io.enq.fire())
io.deq.valid := r_valid
}

// Helper function for functional inference
object DCOutput {
def apply[D <: Data](x : DecoupledIO[D]) : DecoupledIO[D] = {
val tout = Module(new DCOutput(x.bits.cloneType))
tout.io.enq <> x
tout.io.deq
}
}
14 changes: 14 additions & 0 deletions src/main/scala/chisel/lib/dclib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
DecoupledIO Library
===================

This is a library of components which are useful to building systems with DecoupledIO interfaces.

## Components

- DCInput
- DCOutput
- DCFull
- DCHold
- DCArbiter
- DCDemux
- DCMirror
Loading

0 comments on commit ec5e73c

Please # to comment.