-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wide counters (24-bit and wider) are problematic on the fairly slow iCE40 architecture, and this module allows using counters of any width by pipelining them. Co-authored-by: Wanda <wanda@phinode.net>
- Loading branch information
1 parent
59c26de
commit a15df39
Showing
2 changed files
with
89 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import operator | ||
|
||
from amaranth import * | ||
from amaranth.lib import wiring | ||
from amaranth.lib.wiring import In, Out | ||
|
||
|
||
class Accumulator(wiring.Component): | ||
"""Pipelined arithmetic accumulator. | ||
Computes :py:`new_sum = old_sum + addend` using at most :py:`stage_width` wide adders, with | ||
a latency of :py:`(width + stage_width - 1) // stage_width` cycles and throughput of one | ||
addition per cycle. | ||
Members | ||
------- | ||
addend : In(width) | ||
Addend. | ||
sum : Out(width) | ||
Accumulated sum. | ||
""" | ||
def __init__(self, width, *, stage_width=16): | ||
self._width = operator.index(width) | ||
self._stage_width = operator.index(stage_width) | ||
assert self._width >= 1 and self._stage_width >= 1 | ||
self._stages = 1 + (self._width + self._stage_width - 1) // self._stage_width | ||
super().__init__({ | ||
"addend": In(self._width), | ||
"sum": Out(self._width) | ||
}) | ||
|
||
@property | ||
def stages(self): | ||
return self._stages | ||
|
||
def elaborate(self, platform): | ||
m = Module() | ||
|
||
carry = Const(0) | ||
addend = Signal.like(self.addend) | ||
result = Cat() | ||
|
||
m.d.sync += addend.eq(self.addend) | ||
|
||
for index, start_at in enumerate(range(0, self._width, self._stage_width)): | ||
stage_width = min(self._width - start_at, self._stage_width) | ||
|
||
carry_next = Signal(name=f"carry{index}") | ||
addend_next = Signal.like(addend[stage_width:], name=f"addend{index}") | ||
result_next = Signal.like(result, name=f"result{index}") | ||
stage = Signal(stage_width, name=f"stage{index}") | ||
|
||
m.d.sync += Cat(stage, carry_next).eq(stage + addend[:stage_width] + carry) | ||
m.d.sync += addend_next.eq(addend[stage_width:]) | ||
m.d.sync += result_next.eq(result) | ||
|
||
carry = carry_next | ||
addend = addend_next | ||
result = Cat(result_next, stage) | ||
|
||
m.d.comb += self.sum.eq(result) | ||
|
||
return m | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import unittest | ||
from amaranth import * | ||
from amaranth.sim import Tick | ||
|
||
from glasgow.gateware import simulation_test | ||
from glasgow.gateware.accumulator import Accumulator | ||
|
||
|
||
class AccumulatorTestCase(unittest.TestCase): | ||
def setUp(self): | ||
self.tb = Accumulator(5, stage_width=2) | ||
|
||
@simulation_test() | ||
def test_counter(self, tb): | ||
total = 0 | ||
queue = [0] * (self.tb.stages + 1) | ||
for i in range(100): | ||
addend = i * 2137 % 32 | ||
total += addend | ||
total %= 32 | ||
queue.append(total) | ||
self.assertEqual(queue[0], (yield self.tb.sum)) | ||
del queue[0] | ||
yield self.tb.addend.eq(addend) | ||
yield Tick() |