Skip to content

Commit 3d590d0

Browse files
committed
AxiMemoryModel supports read_word and write_word methods for narrow bit-width operations.
1 parent 8cf75c0 commit 3d590d0

File tree

4 files changed

+310
-1
lines changed

4 files changed

+310
-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_memorymodel_readwrite_narrow
7+
8+
9+
def test(request):
10+
veriloggen.reset()
11+
12+
simtype = request.config.getoption('--sim')
13+
14+
rslt = thread_memorymodel_readwrite_narrow.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: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
import sys
4+
import os
5+
import math
6+
7+
# the next line can be removed after installation
8+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
9+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
10+
11+
from veriloggen import *
12+
import veriloggen.thread as vthread
13+
import veriloggen.types.axi as axi
14+
15+
16+
def mkLed(axi_datawidth=32, datawidth=4, addrwidth=10):
17+
m = Module('blinkled')
18+
clk = m.Input('CLK')
19+
rst = m.Input('RST')
20+
21+
numbanks = int(math.ceil(axi_datawidth / datawidth))
22+
myaxi = vthread.AXIM(m, 'myaxi', clk, rst, axi_datawidth)
23+
myram = vthread.MultibankRAM(m, 'myram', clk, rst, datawidth, addrwidth,
24+
numbanks=numbanks)
25+
26+
saxi = vthread.AXISLiteRegister(m, 'saxi', clk, rst, 32)
27+
28+
all_ok = m.TmpReg(initval=0)
29+
30+
def blink(size):
31+
# wait start
32+
saxi.wait_flag(0, value=1, resetvalue=0)
33+
# reset done
34+
saxi.write(1, 0)
35+
36+
all_ok.value = True
37+
38+
for i in range(4):
39+
print('# iter %d start' % i)
40+
# Test for 4KB boundary check
41+
offset = i * 1024 * 16 + (myaxi.boundary_size - 4)
42+
body(size, offset)
43+
print('# iter %d end' % i)
44+
45+
if all_ok:
46+
print('# verify (local): PASSED')
47+
else:
48+
print('# verify (local): FAILED')
49+
50+
# result
51+
saxi.write(2, all_ok)
52+
53+
# done
54+
saxi.write_flag(1, 1, resetvalue=0)
55+
56+
def body(size, offset):
57+
# write
58+
for i in range(size):
59+
wdata = (i + 100) % (2 ** datawidth)
60+
myram.write(i, wdata)
61+
62+
laddr = 0
63+
gaddr = offset
64+
myaxi.dma_write(myram, laddr, gaddr, size)
65+
print('dma_write: [%d] -> [%d]' % (laddr, gaddr))
66+
67+
# write
68+
for i in range(size):
69+
wdata = (i + 1000) % (2 ** datawidth)
70+
myram.write(i, wdata)
71+
72+
laddr = 0
73+
gaddr = (size + size) * 4 + offset
74+
myaxi.dma_write(myram, laddr, gaddr, size)
75+
print('dma_write: [%d] -> [%d]' % (laddr, gaddr))
76+
77+
# read
78+
laddr = 0
79+
gaddr = offset
80+
myaxi.dma_read(myram, laddr, gaddr, size)
81+
print('dma_read: [%d] <- [%d]' % (laddr, gaddr))
82+
83+
for i in range(size):
84+
rdata = myram.read(i) & (2 ** datawidth - 1)
85+
verify = (i + 100) % (2 ** datawidth)
86+
if vthread.verilog.NotEql(rdata, verify):
87+
print('rdata[%d] = %d (!= %d)' % (i, rdata, verify))
88+
all_ok.value = False
89+
90+
# read
91+
laddr = 0
92+
gaddr = (size + size) * 4 + offset
93+
myaxi.dma_read(myram, laddr, gaddr, size)
94+
print('dma_read: [%d] <- [%d]' % (laddr, gaddr))
95+
96+
for i in range(size):
97+
rdata = myram.read(i) & (2 ** datawidth - 1)
98+
verify = (i + 1000) % (2 ** datawidth)
99+
if vthread.verilog.NotEql(rdata, verify):
100+
print('rdata[%d] = %d (!= %d)' % (i, rdata, verify))
101+
all_ok.value = False
102+
103+
th = vthread.Thread(m, 'th_blink', clk, rst, blink)
104+
fsm = th.start(16)
105+
106+
return m
107+
108+
109+
def mkTest(memimg_name=None):
110+
m = Module('test')
111+
112+
axi_datawidth = 32
113+
datawidth = 4
114+
addrwidth = 10
115+
116+
# target instance
117+
led = mkLed(axi_datawidth, datawidth, addrwidth)
118+
119+
# copy paras and ports
120+
params = m.copy_params(led)
121+
ports = m.copy_sim_ports(led)
122+
123+
clk = ports['CLK']
124+
rst = ports['RST']
125+
126+
memory = axi.AxiMemoryModel(m, 'memory', clk, rst, memimg_name=memimg_name)
127+
memory.connect(ports, 'myaxi')
128+
129+
# AXI-Slave controller
130+
_saxi = vthread.AXIMLite(m, '_saxi', clk, rst, noio=True)
131+
_saxi.connect(ports, 'saxi')
132+
133+
def ctrl():
134+
for i in range(100):
135+
pass
136+
137+
for i in range(16):
138+
# word addressing
139+
v = memory.read_word(i, 0, datawidth)
140+
print('read: mem[%d] -> %x' % (i, v))
141+
v = v + 1024
142+
# word addressing
143+
memory.write_word(i, 0, datawidth)
144+
print('write: mem[%d] <- %x' % (i, v))
145+
146+
awaddr = 0
147+
_saxi.write(awaddr, 1)
148+
149+
araddr = 4
150+
v = _saxi.read(araddr)
151+
while v == 0:
152+
v = _saxi.read(araddr)
153+
154+
araddr = 8
155+
v = _saxi.read(araddr)
156+
if v:
157+
print('# verify: PASSED')
158+
else:
159+
print('# verify: FAILED')
160+
161+
th = vthread.Thread(m, 'th_ctrl', clk, rst, ctrl)
162+
fsm = th.start()
163+
164+
uut = m.Instance(led, 'uut',
165+
params=m.connect_params(led),
166+
ports=m.connect_ports(led))
167+
168+
# simulation.setup_waveform(m, uut)
169+
simulation.setup_clock(m, clk, hperiod=5)
170+
init = simulation.setup_reset(m, rst, m.make_reset(), period=100)
171+
172+
init.add(
173+
Delay(1000000),
174+
Systask('finish'),
175+
)
176+
177+
return m
178+
179+
180+
def run(filename='tmp.v', simtype='iverilog', outputfile=None):
181+
182+
if outputfile is None:
183+
outputfile = os.path.splitext(os.path.basename(__file__))[0] + '.out'
184+
185+
memimg_name = 'memimg_' + outputfile
186+
187+
if outputfile is None:
188+
outputfile = os.path.splitext(os.path.basename(__file__))[0] + '.out'
189+
190+
memimg_name = 'memimg_' + outputfile
191+
192+
test = mkTest(memimg_name=memimg_name)
193+
194+
if filename is not None:
195+
test.to_verilog(filename)
196+
197+
sim = simulation.Simulator(test, sim=simtype)
198+
rslt = sim.run(outputfile=outputfile)
199+
lines = rslt.splitlines()
200+
if simtype == 'verilator' and lines[-1].startswith('-'):
201+
rslt = '\n'.join(lines[:-1])
202+
return rslt
203+
204+
205+
if __name__ == '__main__':
206+
rslt = run(filename='tmp.v')
207+
print(rslt)

veriloggen/types/axi.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,8 @@ def AxiLiteSlave(m, name, clk, rst, datawidth=32, addrwidth=32,
13641364

13651365

13661366
class AxiMemoryModel(object):
1367-
__intrinsics__ = ('read', 'write')
1367+
__intrinsics__ = ('read', 'write',
1368+
'read_word', 'write_word')
13681369

13691370
burst_size_width = 8
13701371

@@ -1661,6 +1662,60 @@ def write(self, fsm, addr, wdata):
16611662

16621663
return 0
16631664

1665+
def read_word(self, fsm, word_index, byte_offset, bits=8):
1666+
""" intrinsic method word-indexed read """
1667+
1668+
cond = fsm.state == fsm.current
1669+
rdata = self.m.TmpReg(bits, initval=0, signed=True)
1670+
num_bytes = int(math.ceil(bits / 8))
1671+
addr = byte_offset + word_index * bits / 8
1672+
shift = word_index * bits % 8
1673+
1674+
raw_data = vtypes.Cat(*reversed([self.mem[addr + i]
1675+
for i in range(num_bytes)]))
1676+
1677+
fsm.If(cond)(
1678+
rdata(raw_data >> shift)
1679+
)
1680+
fsm.goto_next()
1681+
1682+
return rdata
1683+
1684+
def write_word(self, fsm, word_index, byte_offset, wdata, bits=8):
1685+
""" intrinsic method word-indexed write """
1686+
1687+
cond = fsm.state == fsm.current
1688+
rdata = self.m.TmpReg(bits, initval=0, signed=True)
1689+
num_bytes = int(math.ceil(bits / 8))
1690+
addr = byte_offset + word_index * bits / 8
1691+
shift = word_index * bits % 8
1692+
1693+
wdata_wire = self.m.TmpWire(bits)
1694+
wdata_wire.assign(wdata)
1695+
mem_data = vtypes.Cat(*reversed([self.mem[addr + i]
1696+
for i in range(num_bytes)]))
1697+
mem_data_wire = self.m.TmpWire(8 * num_bytes)
1698+
mem_data_wire.assign(mem_data)
1699+
1700+
inv_mask = self.m.TmpWire(8 * num_bytes)
1701+
inv_mask.assign(vtypes.Repeat(vtypes.Int(1, 1), bits) << shift)
1702+
mask = self.m.TmpWire(8 * num_bytes)
1703+
mask.assign(vtypes.Unot(inv_mask))
1704+
1705+
raw_data = vtypes.Or(wdata_wire << shift,
1706+
vtypes.Or(mem_data_wire, mask))
1707+
raw_data_wire = self.m.TmpWire(8 * num_bytes)
1708+
raw_data_wire.assign(raw_data)
1709+
1710+
for i in range(num_bytes):
1711+
self.fsm.seq.If(cond)(
1712+
self.mem[addr + i](raw_data_wire[i * 8:i * 8 + 8])
1713+
)
1714+
1715+
fsm.goto_next()
1716+
1717+
return 0
1718+
16641719

16651720
def make_memory_image(filename, length, pattern='inc', dtype=None,
16661721
datawidth=32, wordwidth=8, endian='little'):

0 commit comments

Comments
 (0)