Skip to content

Commit

Permalink
add SESA full adders and parallel prefix adders
Browse files Browse the repository at this point in the history
  • Loading branch information
hansemandse committed Mar 15, 2024
1 parent 79729f7 commit b798152
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 11 deletions.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ The `approx.addition` library contains a vast number of approximate and exact ad

All exact designs are based on descriptions in [Ercegovac and Lang](https://www.sciencedirect.com/book/9781558607989/digital-arithmetic)'s book on digital arithmetic. All designs are purely combinational.

| Type | Name | Code location |
|-----------------------------------------|-------------|----------------------------------------------------------------------------------|
| Half adder | `HalfAdder` | [approx.addition.HalfAdder](./src/main/scala/approx/addition/Exact.scala#L8) |
| Full adder | `FullAdder` | [approx.addition.FullAdder](./src/main/scala/approx/addition/Exact.scala#L14) |
| Ripple-carry adder | `RCA` | [approx.addition.RCA](./src/main/scala/approx/addition/Exact.scala#L23) |
| Carry-lookahead adder | `CLA` | [approx.addition.CLA](./src/main/scala/approx/addition/Exact.scala#L84) |
| Two-layer carry-lookahead adder | `CLA2` | [approx.addition.CLA2](./src/main/scala/approx/addition/Exact.scala#L118) |
| Carry-select adder | `CSA` | [approx.addition.CSA](./src/main/scala/approx/addition/Exact.scala#L178) |
| Self-timed adder | `STA` | [approx.addition.STA](./src/main/scala/approx/addition/ExactSelfTimed.scala#L13) |
| Parallel carry-completion sensing adder | `CCA` | [approx.addition.CCA](./src/main/scala/approx/addition/ExactSelfTimed.scala#L58) |
| Type | Name | Code location |
|-----------------------------------------|----------------|-----------------------------------------------------------------------------------|
| Half adder | `HalfAdder` | [approx.addition.HalfAdder](./src/main/scala/approx/addition/Exact.scala#L8) |
| Full adder | `FullAdder` | [approx.addition.FullAdder](./src/main/scala/approx/addition/Exact.scala#L14) |
| Ripple-carry adder | `RCA` | [approx.addition.RCA](./src/main/scala/approx/addition/Exact.scala#L23) |
| Carry-lookahead adder | `CLA` | [approx.addition.CLA](./src/main/scala/approx/addition/Exact.scala#L84) |
| Two-layer carry-lookahead adder | `CLA2` | [approx.addition.CLA2](./src/main/scala/approx/addition/Exact.scala#L118) |
| Carry-select adder | `CSA` | [approx.addition.CSA](./src/main/scala/approx/addition/Exact.scala#L178) |
| Low fanout parallel prefix adder | `LowFanoutPPA` | [approx.addition.LowFanoutPPA](./src/main/scala/approx/addition/Exact.scala#L298) |
| Minimum levels parallel prefix adder | `MinLevelsPPA` | [approx.addition.MinLevelsPPA](./src/main/scala/approx/addition/Exact.scala#L337) |
| Self-timed adder | `STA` | [approx.addition.STA](./src/main/scala/approx/addition/ExactSelfTimed.scala#L13) |
| Parallel carry-completion sensing adder | `CCA` | [approx.addition.CCA](./src/main/scala/approx/addition/ExactSelfTimed.scala#L58) |

## Approximate designs

Expand All @@ -52,6 +54,7 @@ All exact designs are based on descriptions in [Ercegovac and Lang](https://www.
| Full adder | `AXA1`, `AXA2`, `AXA3` | [approx.addition.AXA](./src/main/scala/approx/addition/AXA.scala) | [Yang et al.](https://ieeexplore.ieee.org/document/6720793) |
| Full adder | `AFA` | [approx.addition.AFA](./src/main/scala/approx/addition/ErrorResilient.scala#L9) | [Dutt et al.](https://dl.acm.org/doi/10.1145/3131274) |
| Full adder | `InXA1`, `InXA2`, `InXA3` | [approx.addition.InXA](./src/main/scala/approx/addition/InXA.scala) | [Almurib et al.](https://ieeexplore.ieee.org/document/7459392) |
| Full adder | `SESA1`, `SESA2`, `SESA3` | [approx.addition.SESA](./src/main/scala/approx/addition/SESA.scala) | [Jha et al.](https://ieeexplore.ieee.org/document/10113797) |
| Full adder | `TCAA` | [approx.addition.TCAA](./src/main/scala/approx/addition/TCAA.scala) | [Yang and Thapliyal](https://ieeexplore.ieee.org/document/9154922) |
| Full adder | `TSAA` | [approx.addition.TSAA](./src/main/scala/approx/addition/TSAA.scala) | [Yang and Thapliyal](https://ieeexplore.ieee.org/document/9154922) |
| Accuracy-configurable adder | `ACA` | [approx.addition.ACA](./src/main/scala/approx/addition/ACA.scala) | [Kahng and Kang](https://dl.acm.org/doi/10.1145/2228360.2228509) |
Expand Down
143 changes: 142 additions & 1 deletion src/main/scala/approx/addition/Exact.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package approx.addition

import chisel3._
import chisel3.experimental.IntParam
import chisel3.util.HasBlackBoxResource
import chisel3.util.{log2Up, HasBlackBoxResource}

/** Accurate half-adder */
class HalfAdder extends HA {
Expand Down Expand Up @@ -212,3 +212,144 @@ class CSA(width: Int, val stages: Int) extends Adder(width) {
io.s := pVec.asUInt ^ carries.asUInt
io.cout := couts(stages)
}

/** Exact parallel prefix adder base class
*
* @param width the width of the adder
*
* The 'fanout' architecture models the PPA from Ercegovac and Lang, Fig. 2.19,
* while the 'delay' architecture models the PPA from Fig. 2.20. The 'fanout'
* architecture is likely to have higher latency but lower area than the
* 'delay' architecture.
*
* @todo Remove redundant extension bits in this design.
*/
abstract class PPA(width: Int) extends Adder(width) {
/** Bundle of generate and alive bits */
private[addition] class GenAlivePair extends Bundle {
val g = Bool()
val a = Bool()
}

/** Compute a full-dot operator
*
* @param l the left `GenAlivePair` bundle
* @param r the right boolean
* @return a boolean prefix bit
*/
private[addition] def fullDot(l: GenAlivePair, r: Bool): Bool = l.g | (l.a & r)

/** Compute an empty-dot operator
*
* @param l a `GenAlivePair` bundle
* @param r a `GenAlivePair` bundle
* @return a combined `GenAlivePair` bundle from `l` and `r`
*/
private[addition] def emptyDot(l: GenAlivePair, r: GenAlivePair): GenAlivePair = {
val res = Wire(new GenAlivePair)
res.g := l.g | (l.a & r.g)
res.a := l.a & r.a
res
}

/** Recursively build the parallel prefix tree
*
* @param ins the inputs to the current level of the tree
* @param lvl the index of the current level of the tree
*/
private[addition] def tree(ins: Seq[Any], lvl: Int = 0): UInt

// The architecture generation part of the design only works for power-of-2
// bit-width, so extend the operands thereafter
def sext(op: UInt, twidth: Int): UInt = {
if (twidth <= op.getWidth) op(twidth-1, 0)
else VecInit(Seq.fill(twidth - op.getWidth)(op(op.getWidth-1))).asUInt ## op
}
val inWidth = 1 << log2Up(width)
val inA = sext(io.a, inWidth)
val inB = sext(io.b, inWidth)

// Compute the generate, alive, and propagate signals from the extended inputs
val g = inA & inB
val a = inA | inB
val p = inA ^ inB

// Compute the inputs to the parallel prefix tree
val inLevel = (io.cin +: (0 until inWidth).map { i =>
val res = Wire(new GenAlivePair)
res.g := g(i)
res.a := a(i)
res
})

// Inheriting architectures must define the output from the parallel prefix
// tree for it to be used in sum and carry-out generation
val outLevel = tree(inLevel)
io.s := p ^ outLevel
io.cout := outLevel(width)
}

/** Exact parallel prefix adder with maximum fanout of three
*
* @param width the width of the adder
*
* Models the PPA from Ercegovac and Lang, Fig. 2.19.
*/
class LowFanoutPPA(width: Int) extends PPA(width) {
private[addition] def tree(ins: Seq[Any], lvl: Int = 0): UInt = {
require(ins.forall(in => in.isInstanceOf[GenAlivePair] || in.isInstanceOf[Bool]))

// Compute the inputs to the next level
val next = if (lvl == 0) {
// Treat the first level differently from the remaining ones
(0 until ins.size).map { i =>
if ((i & 0x1) == 1) (ins(i), ins(i-1)) match {
case (l: GenAlivePair, r: GenAlivePair) => emptyDot(l, r)
case (l: GenAlivePair, r: Bool) => fullDot(l, r)
case _ => // should never occur
} else ins(i)
}
} else {
// Two bit positions share the same right element
val lower = lvl << 1
val upper = scala.math.min(lower + 2, ins.size)
val r = ins(lower - 1)
ins.take(lower) ++ (lower until upper).map { i =>
(ins(i), r) match {
case (l: GenAlivePair, r: Bool) => fullDot(l, r)
case _ => // should never occur
}
} ++ ins.drop(upper)
}

// Finalize this level or the whole tree
if (next.exists(_.isInstanceOf[GenAlivePair])) tree(next, lvl + 1)
else VecInit(next.asInstanceOf[Seq[Bool]]).asUInt
}
}

/** Exact parallel prefix adder with minimum number of levels
*
* @param width the width of the adder
*
* Models the PPA from Ercegovac and Lang, Fig. 2.20.
*/
class MinLevelsPPA(width: Int) extends PPA(width) {
private[addition] def tree(ins: Seq[Any], lvl: Int = 0): UInt = {
require(ins.forall(in => in.isInstanceOf[GenAlivePair] || in.isInstanceOf[Bool]))

// Compute the inputs to the next level or the final result
val skip = 1 << lvl
val next = ins.take(skip) ++ (skip until ins.size).map { i =>
(ins(i), ins(i - skip)) match {
case (l: GenAlivePair, r: GenAlivePair) => emptyDot(l, r)
case (l: GenAlivePair, r: Bool) => fullDot(l, r)
case _ => // should never occur
}
}

// Finalize this level or the whole tree
if (next.exists(_.isInstanceOf[GenAlivePair])) tree(next, lvl + 1)
else VecInit(next.asInstanceOf[Seq[Bool]]).asUInt
}
}
30 changes: 30 additions & 0 deletions src/main/scala/approx/addition/SESA.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package approx.addition

import chisel3._

/** SESA1 approximate full-adder
*
* Implementation from Jha et al. [2023]
*/
class SESA1 extends FA {
io.s := (io.x ^ io.y ^ io.cin) | (!io.x & !io.y & !io.cin)
io.cout := ((io.x ^ io.y) & io.cin) | (io.x & io.y)
}

/** SESA2 approximate full-adder
*
* Implementation from Jha et al. [2023]
*/
class SESA2 extends FA {
io.s := false.B
io.cout := ((io.x ^ io.y) & io.cin) | (io.x & io.y)
}

/** SESA3 approximate full-adder
*
* Implementation from Jha et al. [2023]
*/
class SESA3 extends FA {
io.s := true.B
io.cout := ((io.x ^ io.y) & io.cin) | (io.x & io.y)
}
40 changes: 40 additions & 0 deletions src/test/scala/approx/addition/ExactAdderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,43 @@ class CSASpec extends ExactAdderSpec {
}
}
}

class LowFanoutPPASpec extends ExactAdderSpec {
behavior of "Low-Fanout Parallel Prefix Adder"

it should "do simple additions" in {
test(new LowFanoutPPA(SimpleWidth))
.withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
simpleTest(dut)
}
}

for (width <- CommonWidths) {
it should s"do random $width-bit additions" in {
test(new LowFanoutPPA(width))
.withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
randomTest(dut)
}
}
}
}

class MinLevelsPPASpec extends ExactAdderSpec {
behavior of "Minimum-Levels Parallel Prefix Adder"

it should "do simple additions" in {
test(new MinLevelsPPA(SimpleWidth))
.withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
simpleTest(dut)
}
}

for (width <- CommonWidths) {
it should s"do random $width-bit additions" in {
test(new MinLevelsPPA(width))
.withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
randomTest(dut)
}
}
}
}

0 comments on commit b798152

Please sign in to comment.