Skip to content

Commit 0cd95f2

Browse files
committed
stream.CustomAcc
1 parent a441346 commit 0cd95f2

File tree

5 files changed

+359
-1
lines changed

5 files changed

+359
-1
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
TARGET=$(shell ls *.py | grep -v test | grep -v parsetab.py)
2+
ARGS=
3+
4+
PYTHON=python3
5+
#PYTHON=python
6+
#OPT=-m pdb
7+
#OPT=-m cProfile -s time
8+
#OPT=-m cProfile -o profile.rslt
9+
10+
.PHONY: all
11+
all: test
12+
13+
.PHONY: run
14+
run:
15+
$(PYTHON) $(OPT) $(TARGET) $(ARGS)
16+
17+
.PHONY: test
18+
test:
19+
$(PYTHON) -m pytest -vv
20+
21+
.PHONY: check
22+
check:
23+
$(PYTHON) $(OPT) $(TARGET) $(ARGS) > tmp.v
24+
iverilog -tnull -Wall tmp.v
25+
rm -f tmp.v
26+
27+
.PHONY: clean
28+
clean:
29+
rm -rf *.pyc __pycache__ parsetab.py .cache *.out *.png *.dot tmp.v uut.vcd
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
4+
import os
5+
import veriloggen
6+
import thread_stream_custom_acc
7+
8+
9+
def test(request):
10+
veriloggen.reset()
11+
12+
simtype = request.config.getoption('--sim')
13+
14+
rslt = thread_stream_custom_acc.run(filename=None, simtype=simtype,
15+
outputfile=os.path.splitext(os.path.basename(__file__))[0] + '.out')
16+
17+
verify_rslt = rslt.splitlines()[-1]
18+
assert(verify_rslt == '# verify: PASSED')
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
import sys
4+
import os
5+
6+
# the next line can be removed after installation
7+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
8+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
9+
10+
from veriloggen import *
11+
import veriloggen.thread as vthread
12+
import veriloggen.types.axi as axi
13+
14+
15+
def mkLed():
16+
m = Module('blinkled')
17+
clk = m.Input('CLK')
18+
rst = m.Input('RST')
19+
20+
datawidth = 32
21+
addrwidth = 10
22+
myaxi = vthread.AXIM(m, 'myaxi', clk, rst, datawidth)
23+
ram_a = vthread.RAM(m, 'ram_a', clk, rst, datawidth, addrwidth)
24+
ram_b = vthread.RAM(m, 'ram_b', clk, rst, datawidth, addrwidth)
25+
ram_c = vthread.RAM(m, 'ram_c', clk, rst, datawidth, addrwidth)
26+
27+
strm = vthread.Stream(m, 'mystream', clk, rst)
28+
size = strm.parameter('size')
29+
a = strm.source('a')
30+
b = strm.source('b')
31+
32+
def op_func(x, y, x_size, y_size):
33+
x_cond = x == x_size - 1
34+
next_x = Mux(x_cond, 0, x + 1)
35+
y_cond = Ands(x_cond, y == y_size - 1)
36+
next_y = Mux(y_cond, 0, y + 1)
37+
return next_x, next_y
38+
39+
x, y = strm.CustomAcc(func=op_func,
40+
initvals=(0, 0), args=(size, size),
41+
width_list=(32, 32), signed_list=(True, True))
42+
43+
c = a + b + x + y
44+
strm.sink(c, 'c')
45+
46+
def comp_stream(size, offset):
47+
strm.set_parameter('size', size)
48+
strm.set_source('a', ram_a, offset, size)
49+
strm.set_source('b', ram_b, offset, size)
50+
strm.set_sink('c', ram_c, offset, size)
51+
strm.run()
52+
strm.join()
53+
54+
def comp_sequential(size, offset):
55+
sum = 0
56+
x = 0
57+
y = 0
58+
x_size = size
59+
y_size = size
60+
for i in range(size):
61+
a = ram_a.read(i + offset)
62+
b = ram_b.read(i + offset)
63+
sum = a + b + x + y
64+
ram_c.write(i + offset, sum)
65+
x_cond = x == x_size - 1
66+
if x_cond:
67+
x = 0
68+
else:
69+
x = x + 1
70+
y_cond = x_cond and (y == y_size - 1)
71+
if y_cond:
72+
y = 0
73+
else:
74+
y = y + 1
75+
76+
def check(size, offset_stream, offset_seq):
77+
all_ok = True
78+
for i in range(size):
79+
st = ram_c.read(i + offset_stream)
80+
sq = ram_c.read(i + offset_seq)
81+
if vthread.verilog.NotEql(st, sq):
82+
all_ok = False
83+
if all_ok:
84+
print('# verify: PASSED')
85+
else:
86+
print('# verify: FAILED')
87+
88+
def comp(size):
89+
# stream
90+
offset = 0
91+
myaxi.dma_read(ram_a, offset, 0, size)
92+
myaxi.dma_read(ram_b, offset, 512, size)
93+
comp_stream(size, offset)
94+
myaxi.dma_write(ram_c, offset, 1024, size)
95+
96+
# sequential
97+
offset = size
98+
myaxi.dma_read(ram_a, offset, 0, size)
99+
myaxi.dma_read(ram_b, offset, 512, size)
100+
comp_sequential(size, offset)
101+
myaxi.dma_write(ram_c, offset, 1024 * 2, size)
102+
103+
# verification
104+
myaxi.dma_read(ram_c, 0, 1024, size)
105+
myaxi.dma_read(ram_c, offset, 1024 * 2, size)
106+
check(size, 0, offset)
107+
108+
vthread.finish()
109+
110+
th = vthread.Thread(m, 'th_comp', clk, rst, comp)
111+
fsm = th.start(32)
112+
113+
return m
114+
115+
116+
def mkTest(memimg_name=None):
117+
m = Module('test')
118+
119+
# target instance
120+
led = mkLed()
121+
122+
# copy paras and ports
123+
params = m.copy_params(led)
124+
ports = m.copy_sim_ports(led)
125+
126+
clk = ports['CLK']
127+
rst = ports['RST']
128+
129+
memory = axi.AxiMemoryModel(m, 'memory', clk, rst, memimg_name=memimg_name)
130+
memory.connect(ports, 'myaxi')
131+
132+
uut = m.Instance(led, 'uut',
133+
params=m.connect_params(led),
134+
ports=m.connect_ports(led))
135+
136+
# simulation.setup_waveform(m, uut)
137+
simulation.setup_clock(m, clk, hperiod=5)
138+
init = simulation.setup_reset(m, rst, m.make_reset(), period=100)
139+
140+
init.add(
141+
Delay(1000000),
142+
Systask('finish'),
143+
)
144+
145+
return m
146+
147+
148+
def run(filename='tmp.v', simtype='iverilog', outputfile=None):
149+
150+
if outputfile is None:
151+
outputfile = os.path.splitext(os.path.basename(__file__))[0] + '.out'
152+
153+
memimg_name = 'memimg_' + outputfile
154+
155+
test = mkTest(memimg_name=memimg_name)
156+
157+
if filename is not None:
158+
test.to_verilog(filename)
159+
160+
sim = simulation.Simulator(test, sim=simtype)
161+
rslt = sim.run(outputfile=outputfile)
162+
lines = rslt.splitlines()
163+
if simtype == 'verilator' and lines[-1].startswith('-'):
164+
rslt = '\n'.join(lines[:-1])
165+
return rslt
166+
167+
168+
if __name__ == '__main__':
169+
rslt = run(filename='tmp.v')
170+
print(rslt)

veriloggen/stream/stypes.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,6 +3729,146 @@ def CounterValid(size, step=1, interval=None, initval=0, offset=None,
37293729
return data, valid
37303730

37313731

3732+
class _CustomAcc(_SpecialOperator):
3733+
latency = 1
3734+
3735+
default_width = 32
3736+
default_point = 0
3737+
default_signed = True
3738+
3739+
def __init__(self, func, initvals, args=None, reset=None,
3740+
width_list=None, point_list=None, signed_list=None):
3741+
3742+
_args = list(initvals)
3743+
3744+
if args is not None:
3745+
_args.extend(args)
3746+
3747+
if reset is not None:
3748+
_args.append(reset)
3749+
3750+
_SpecialOperator.__init__(self, *_args)
3751+
3752+
self.func = func
3753+
self.reset = self.args[-1] if reset is not None else None
3754+
self.num_vars = len(initvals)
3755+
3756+
if width_list is not None:
3757+
if len(width_list) != self.num_vars:
3758+
raise ValueError('width_list must have %d values, not %d.' %
3759+
(self.num_vars, len(width_list)))
3760+
3761+
total_width = 0
3762+
for width in width_list:
3763+
total_width += width
3764+
3765+
else:
3766+
total_width = self.default_width * self.num_vars
3767+
width_list = [self.default_width] * self.num_vars
3768+
3769+
self.width = total_width
3770+
self.width_list = width_list
3771+
3772+
if signed_list is not None:
3773+
if len(signed_list) != self.num_vars:
3774+
raise ValueError('signed_list must have %d values, not %d.' %
3775+
(self.num_vars, len(signed_list)))
3776+
3777+
else:
3778+
signed_list = [self.default_signed] * self.num_vars
3779+
3780+
self.signed = False
3781+
self.signed_list = signed_list
3782+
3783+
if point_list is not None:
3784+
if len(point_list) != self.num_vars:
3785+
raise ValueError('point_list must have %d values, not %d.' %
3786+
(self.num_vars, len(point_list)))
3787+
else:
3788+
point_list = [self.default_point] * self.num_vars
3789+
3790+
self.point = 0
3791+
self.point_list = point_list
3792+
3793+
def _implement(self, m, seq, svalid=None, senable=None):
3794+
if self.latency != 1:
3795+
raise ValueError("Latency must be '%d', not '%d'" %
3796+
(self.latency, 1))
3797+
3798+
width = self.get_width()
3799+
signed = self.get_signed()
3800+
3801+
arg_data = [arg.sig_data for arg in self.args]
3802+
3803+
enable_cond = senable
3804+
if self.iteration_interval != 1:
3805+
enable_cond = _and_vars(enable_cond, svalid)
3806+
3807+
resetdata = arg_data[-1] if self.reset is not None else None
3808+
3809+
vars = [m.Reg(self.name('var_%d' % i), var_width, initval=0, signed=var_signed)
3810+
for i, (var_width, var_point, var_signed) in enumerate(
3811+
zip(self.width_list, self.point_list, self.signed_list))]
3812+
3813+
data = m.Wire(self.name('data'), width, signed=signed)
3814+
data.assign(vtypes.Cat(*vars))
3815+
self.sig_data = data
3816+
3817+
args = arg_data[self.num_vars:self.num_vars + self.num_vars]
3818+
func_args = vars + args
3819+
next_vars = self.func(*func_args)
3820+
3821+
if len(next_vars) != len(vars):
3822+
raise ValueError('CustomAcc function must return %d values.' % len(vars))
3823+
3824+
for var, next_var in zip(vars, next_vars):
3825+
seq(var(next_var), cond=enable_cond)
3826+
3827+
initvals = arg_data[:self.num_vars]
3828+
3829+
if resetdata is not None:
3830+
reset_cond = _and_vars(enable_cond, resetdata)
3831+
for var, initval in zip(vars, initvals):
3832+
seq(var(initval), cond=reset_cond)
3833+
3834+
# multicycle control
3835+
if self.iteration_interval != 1:
3836+
ii_count = m.Reg(self.name('ii_count'),
3837+
int(ceil(log(self.iteration_interval, 2))) + 1, initval=0)
3838+
ii_stall_cond = m.Wire(self.name('ii_stall_cond'))
3839+
ii_stall_cond.assign(ii_count > 0)
3840+
util.add_disable_cond(self.strm.internal_oready,
3841+
ii_stall_cond, vtypes.Int(0))
3842+
3843+
seq.If(ii_count == 0, enable_cond)(
3844+
ii_count.inc()
3845+
)
3846+
seq.If(ii_count > 0)(
3847+
ii_count.inc()
3848+
)
3849+
seq.If(ii_count == self.iteration_interval - 1)(
3850+
ii_count(0)
3851+
)
3852+
3853+
3854+
def CustomAcc(func, initvals, args=None, reset=None,
3855+
width_list=None, point_list=None, signed_list=None):
3856+
3857+
v = _CustomAcc(func, initvals, args, reset,
3858+
width_list, point_list, signed_list)
3859+
3860+
return_values = []
3861+
3862+
s = 0
3863+
for width, point, signed in zip(reversed(v.width_list), reversed(v.point_list), reversed(v.signed_list)):
3864+
r = Slice(v, s + width - 1, s)
3865+
r = Cast(r, width=width, point=point, signed=signed)
3866+
s += width
3867+
return_values.append(r)
3868+
3869+
return tuple(reversed(return_values))
3870+
3871+
37323872
class Int(_Constant):
37333873

37343874
def __init__(self, value, signed=True):

veriloggen/thread/stream.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3761,7 +3761,8 @@ def __getattr__(self, attr):
37613761
(f.__name__.startswith('Reduce') or
37623762
f.__name__.startswith('Counter') or
37633763
f.__name__.startswith('Pulse') or
3764-
f.__name__.startswith('Producer'))):
3764+
f.__name__.startswith('Producer') or
3765+
f.__name__.startswith('CustomAcc'))):
37653766

37663767
if self.reduce_reset is None:
37673768
self._make_reduce_reset()

0 commit comments

Comments
 (0)