Skip to content

Commit 4d10976

Browse files
feviSpaqin
authored andcommitted
Pipeline PhasedAccu for n>8 and allow non-log(2) values of n when n>8
1 parent 9e07cf5 commit 4d10976

File tree

2 files changed

+146
-35
lines changed

2 files changed

+146
-35
lines changed

misoc/cores/duc.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -205,29 +205,21 @@ def __init__(self, width, constants):
205205

206206
###
207207

208+
self.latency = 2 if n > 8 else 0
209+
208210
# TODO: improve MCM
209-
assert n <= 9
211+
assert n <= 16
210212
assert range(n) == constants
211213

212-
ctx = self.comb
213-
if n > 0:
214-
ctx += o[0].eq(0)
215-
if n > 1:
216-
ctx += o[1].eq(i)
217-
if n > 2:
218-
ctx += o[2].eq(i << 1)
219-
if n > 3:
220-
ctx += o[3].eq(i + (i << 1))
221-
if n > 4:
222-
ctx += o[4].eq(i << 2)
223-
if n > 5:
224-
ctx += o[5].eq(i + (i << 2))
225-
if n > 6:
226-
ctx += o[6].eq(o[3] << 1)
227-
if n > 7:
228-
ctx += o[7].eq((i << 3) - i)
229-
if n > 8:
230-
ctx += o[8].eq(i << 3)
214+
ctx = self.sync if n > 8 else self.comb
215+
216+
# manually generated multiplication for small numbers,
217+
# if you use "x*y" Vivado will use a DSP48E1 instead
218+
for x in range(n):
219+
ctx += o[x].eq((x & 0x1) * i +
220+
((x & 0x2) >> 1) * (i << 1) +
221+
((x & 0x4) >> 2) * (i << 2) +
222+
((x & 0x8) >> 3) * (i << 3))
231223

232224

233225
class PhasedAccu(Module):
@@ -247,26 +239,53 @@ def __init__(self, n, fwidth, pwidth):
247239
self.z = [Signal(pwidth, reset_less=True)
248240
for _ in range(n)]
249241

250-
self.submodules.mcm = MCM(fwidth, range(n))
242+
self.submodules.mcm = MCM(fwidth, range(n+1))
251243
# reset by clr
252244
qa = Signal(fwidth, reset_less=True)
253245
qb = Signal(fwidth, reset_less=True)
254246
clr_d = Signal(reset_less=True)
255-
self.sync += [
256-
clr_d.eq(self.clr),
257-
qa.eq(qa + (self.f << log2_int(n))),
258-
self.mcm.i.eq(self.f),
259-
If(self.clr | clr_d,
260-
qa.eq(0),
261-
),
262-
If(clr_d,
263-
self.mcm.i.eq(0),
264-
),
265-
qb.eq(qa + (self.p << fwidth - pwidth)),
266-
[z.eq((qb + oi)[fwidth - pwidth:])
267-
for oi, z in zip(self.mcm.o, self.z)]
268-
]
269247

248+
if n > 8:
249+
# additional pipelining for n > 8
250+
clr_d2 = Signal(reset_less=True)
251+
mcm_o_d = [Signal(fwidth, reset_less=True) for _ in range(n)]
252+
self.sync += [
253+
# Delay signals to match now increased mcm latency
254+
clr_d.eq(self.clr),
255+
clr_d2.eq(clr_d),
256+
[mcm_o_d[i].eq(self.mcm.o[i]) for i in range(n)],
257+
258+
qa.eq(qa + self.mcm.o[n]),
259+
self.mcm.i.eq(self.f),
260+
If(clr_d | clr_d2,
261+
qa.eq(0),
262+
),
263+
If(clr_d2,
264+
self.mcm.i.eq(0),
265+
),
266+
qb.eq(qa + (self.p << (fwidth - pwidth))),
267+
268+
# Use delayed signals in the final phase calculation
269+
[z.eq((qb + mcm_o_d[i])[fwidth - pwidth:])
270+
for i, z in enumerate(self.z)]
271+
]
272+
else:
273+
self.sync += [
274+
clr_d.eq(self.clr),
275+
qa.eq(qa + (self.f << log2_int(n))),
276+
self.mcm.i.eq(self.f),
277+
If(self.clr | clr_d,
278+
qa.eq(0),
279+
),
280+
If(clr_d,
281+
self.mcm.i.eq(0),
282+
),
283+
qb.eq(qa + (self.p << (fwidth - pwidth))),
284+
285+
# Use non-delayed signals in the final phase calculation
286+
[z.eq((qb + oi)[fwidth - pwidth:])
287+
for oi, z in zip(self.mcm.o, self.z)]
288+
]
270289

271290
class PhaseModulator(Module):
272291
"""Complex phase modulator/shifter.

misoc/test/test_duc.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_init(self):
5050

5151
def test_seq(self):
5252
def gen():
53+
# latency = 2
5354
yield self.dut.clr.eq(0)
5455
yield self.dut.p.eq(0x01)
5556
yield
@@ -104,6 +105,97 @@ def gen():
104105
self.assertEqual((yield self.dut.z[1]), 0xa1)
105106
run_simulation(self.dut, gen())
106107

108+
class TestPhasedAccuNonLog(unittest.TestCase):
109+
def setUp(self):
110+
self.dut = duc.PhasedAccu(n=12, fwidth=32, pwidth=16)
111+
112+
def test_init(self):
113+
self.assertEqual(len(self.dut.f), 32)
114+
self.assertEqual(len(self.dut.p), 16)
115+
self.assertEqual(len(self.dut.z), 12)
116+
self.assertEqual(len(self.dut.z[0]), 16)
117+
118+
def test_seq(self):
119+
def gen():
120+
# latency = 4
121+
n=12
122+
yield self.dut.clr.eq(0)
123+
yield self.dut.p.eq(0x01)
124+
yield
125+
yield
126+
yield
127+
yield
128+
# check phase offset with f=0
129+
self.assertEqual((yield self.dut.z[0]), 0x01)
130+
self.assertEqual((yield self.dut.z[1]), 0x01)
131+
yield self.dut.f.eq(0x10 << 16)
132+
yield
133+
yield
134+
yield
135+
yield
136+
yield
137+
# check first cycle f increments
138+
for i in range(n):
139+
expected_value = (i << 4) | 0x01
140+
self.assertEqual((yield self.dut.z[i]), expected_value)
141+
yield
142+
# second cycle f increments
143+
for i in range(n):
144+
expected_value += 0x10
145+
self.assertEqual((yield self.dut.z[i]), expected_value)
146+
yield self.dut.clr.eq(1)
147+
yield
148+
for i in range(n):
149+
expected_value += 0x10
150+
self.assertEqual((yield self.dut.z[i]), expected_value)
151+
yield
152+
for i in range(n):
153+
expected_value += 0x10
154+
self.assertEqual((yield self.dut.z[i]), expected_value)
155+
yield self.dut.clr.eq(0)
156+
yield
157+
for i in range(n):
158+
expected_value += 0x10
159+
yield
160+
for i in range(n):
161+
expected_value += 0x10
162+
self.assertEqual((yield self.dut.z[i]), expected_value)
163+
yield
164+
# first clr cycle
165+
for i in range(n):
166+
expected_value = (i << 4) | 0x01
167+
self.assertEqual((yield self.dut.z[i]), expected_value)
168+
yield
169+
# second clr cycle
170+
for i in range(n):
171+
expected_value = (i << 4) | 0x01
172+
self.assertEqual((yield self.dut.z[i]), expected_value)
173+
yield self.dut.f.eq(0x20 << 16)
174+
yield
175+
yield
176+
yield
177+
# first cycle after clr with old f
178+
for i in range(n):
179+
expected_value = (i << 4) | 0x01
180+
self.assertEqual((yield self.dut.z[i]), expected_value)
181+
yield
182+
# second cycle with old f
183+
for i in range(n):
184+
expected_value += 0x10
185+
self.assertEqual((yield self.dut.z[i]), expected_value)
186+
yield
187+
# cycle with one old and one new
188+
expected_value += 0x10
189+
for i in range(n):
190+
self.assertEqual((yield self.dut.z[i]), expected_value)
191+
expected_value += 0x20
192+
yield
193+
# cycle with only new increments
194+
expected_value -= 0x20
195+
for i in range(n):
196+
expected_value += 0x20
197+
self.assertEqual((yield self.dut.z[i]), expected_value)
198+
run_simulation(self.dut, gen())
107199

108200
class TestMul(unittest.TestCase):
109201
def setUp(self):

0 commit comments

Comments
 (0)