Skip to content

Commit 9bb7e6d

Browse files
enjoy-digitalsbourdeauducq
authored andcommittedDec 21, 2014
ethmac: improve testbenchs
1 parent ceb675c commit 9bb7e6d

File tree

5 files changed

+242
-139
lines changed

5 files changed

+242
-139
lines changed
 

‎misoclib/ethmac/test/__init__.py

-54
Original file line numberDiff line numberDiff line change
@@ -1,54 +0,0 @@
1-
import random
2-
3-
from migen.fhdl.std import *
4-
from migen.flow.actor import Sink, Source
5-
6-
from misoclib.ethmac.common import *
7-
8-
class PacketStreamer(Module):
9-
def __init__(self, data):
10-
self.source = Source(eth_description(8))
11-
self.data = data
12-
13-
def gen_simulation(self, selfp):
14-
for n, data in enumerate(self.data):
15-
selfp.source.stb = 1
16-
selfp.source.sop = (n == 0)
17-
selfp.source.eop = (n == len(self.data)-1)
18-
selfp.source.payload.d = data
19-
yield
20-
while selfp.source.ack == 0:
21-
yield
22-
selfp.source.stb = 0
23-
while random.getrandbits(1):
24-
yield
25-
26-
class PacketLogger(Module):
27-
def __init__(self):
28-
self.sink = Sink(eth_description(8))
29-
self.data = []
30-
31-
def do_simulation(self, selfp):
32-
selfp.sink.ack = bool(random.getrandbits(1))
33-
if selfp.sink.stb and selfp.sink.ack:
34-
self.data.append(selfp.sink.payload.d)
35-
36-
def print_results(s, l1, l2):
37-
def comp(l1, l2):
38-
r = True
39-
try:
40-
for i, val in enumerate(l1):
41-
if val != l2[i]:
42-
print(s + " : val : {:02X}, exp : {:02X}".format(val, l2[i]))
43-
r = False
44-
except:
45-
r = False
46-
return r
47-
48-
c = comp(l1, l2)
49-
r = s + " "
50-
if c:
51-
r += "[OK]"
52-
else:
53-
r += "[KO]"
54-
print(r)

‎misoclib/ethmac/test/common.py

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import random, copy
2+
3+
from migen.fhdl.std import *
4+
from migen.flow.actor import Sink, Source
5+
from migen.genlib.record import *
6+
7+
from misoclib.ethmac.common import *
8+
9+
def seed_to_data(seed, random=True):
10+
if random:
11+
return (seed * 0x31415979 + 1) & 0xffffffff
12+
else:
13+
return seed
14+
15+
def check(p1, p2):
16+
p1 = copy.deepcopy(p1)
17+
p2 = copy.deepcopy(p2)
18+
if isinstance(p1, int):
19+
return 0, 1, int(p1 != p2)
20+
else:
21+
if len(p1) >= len(p2):
22+
ref, res = p1, p2
23+
else:
24+
ref, res = p2, p1
25+
shift = 0
26+
while((ref[0] != res[0]) and (len(res)>1)):
27+
res.pop(0)
28+
shift += 1
29+
length = min(len(ref), len(res))
30+
errors = 0
31+
for i in range(length):
32+
if ref.pop(0) != res.pop(0):
33+
errors += 1
34+
return shift, length, errors
35+
36+
def randn(max_n):
37+
return random.randint(0, max_n-1)
38+
39+
class Packet(list):
40+
def __init__(self, init=[]):
41+
self.ongoing = False
42+
self.done = False
43+
for data in init:
44+
self.append(data)
45+
46+
class PacketStreamer(Module):
47+
def __init__(self, description):
48+
self.source = Source(description)
49+
###
50+
self.packets = []
51+
self.packet = Packet()
52+
self.packet.done = 1
53+
54+
def send(self, packet):
55+
packet = copy.deepcopy(packet)
56+
self.packets.append(packet)
57+
58+
def do_simulation(self, selfp):
59+
if len(self.packets) and self.packet.done:
60+
self.packet = self.packets.pop(0)
61+
if not self.packet.ongoing and not self.packet.done:
62+
selfp.source.stb = 1
63+
selfp.source.sop = 1
64+
selfp.source.d = self.packet.pop(0)
65+
self.packet.ongoing = True
66+
elif selfp.source.stb == 1 and selfp.source.ack == 1:
67+
selfp.source.sop = 0
68+
selfp.source.eop = (len(self.packet) == 1)
69+
if len(self.packet) > 0:
70+
selfp.source.stb = 1
71+
selfp.source.d = self.packet.pop(0)
72+
else:
73+
self.packet.done = 1
74+
selfp.source.stb = 0
75+
76+
class PacketLogger(Module):
77+
def __init__(self, description):
78+
self.sink = Sink(description)
79+
###
80+
self.packet = Packet()
81+
82+
def receive(self):
83+
self.packet.done = 0
84+
while self.packet.done == 0:
85+
yield
86+
87+
def do_simulation(self, selfp):
88+
selfp.sink.ack = 1
89+
if selfp.sink.stb == 1 and selfp.sink.sop == 1:
90+
self.packet = Packet()
91+
self.packet.append(selfp.sink.d)
92+
elif selfp.sink.stb:
93+
self.packet.append(selfp.sink.d)
94+
if selfp.sink.stb == 1 and selfp.sink.eop == 1:
95+
self.packet.done = True
96+
97+
class AckRandomizer(Module):
98+
def __init__(self, description, level=0):
99+
self.level = level
100+
101+
self.sink = Sink(description)
102+
self.source = Source(description)
103+
104+
self.run = Signal()
105+
106+
self.comb += \
107+
If(self.run,
108+
Record.connect(self.sink, self.source)
109+
).Else(
110+
self.source.stb.eq(0),
111+
self.sink.ack.eq(0),
112+
)
113+
114+
def do_simulation(self, selfp):
115+
n = randn(100)
116+
if n < self.level:
117+
selfp.run = 0
118+
else:
119+
selfp.run = 1
120+

‎misoclib/ethmac/test/crc_tb.py

+51-26
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
from migen.actorlib.crc import *
33

44
from misoclib.ethmac.common import *
5-
from misoclib.ethmac.test import *
5+
from misoclib.ethmac.test.common import *
66

7-
frame_data = [
7+
payload = [
88
0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
99
0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
1010
0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
@@ -15,44 +15,69 @@
1515
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
1616
]
1717

18-
frame_crc = [
18+
crc = [
1919
0x7A, 0xD5, 0x6B, 0xB3
2020
]
2121

22+
mux = {
23+
"inserter": 0,
24+
"checker": 1,
25+
"both": 2
26+
}
27+
2228
class TB(Module):
23-
def __init__(self):
29+
def __init__(self, random_level=50):
2430
sm = self.submodules
31+
sm.streamer = PacketStreamer(eth_description(8))
32+
sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
33+
sm.logger = PacketLogger(eth_description(8))
34+
sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
2535

26-
# Streamer (DATA) --> CRC32Inserter --> Logger (expect DATA + CRC)
27-
sm.inserter_streamer = PacketStreamer(frame_data)
28-
sm.crc32_inserter = CRC32Inserter(eth_description(8))
29-
sm.inserter_logger = PacketLogger()
30-
self.comb +=[
31-
self.inserter_streamer.source.connect(self.crc32_inserter.sink),
32-
self.crc32_inserter.source.connect(self.inserter_logger.sink),
36+
self.comb += [
37+
self.streamer.source.connect(self.streamer_randomizer.sink),
38+
self.logger_randomizer.source.connect(self.logger.sink)
3339
]
3440

35-
# Streamer (DATA + CRC) --> CRC32Checher --> Logger (except DATA + CRC + check)
36-
sm.checker_streamer = PacketStreamer(frame_data + frame_crc)
41+
sm.crc32_inserter = CRC32Inserter(eth_description(8))
3742
sm.crc32_checker = CRC32Checker(eth_description(8))
38-
sm.checker_logger = PacketLogger()
39-
self.comb +=[
40-
self.checker_streamer.source.connect(self.crc32_checker.sink),
41-
self.crc32_checker.source.connect(self.checker_logger.sink),
43+
44+
self.mux = Signal(2)
45+
self.comb += [
46+
If(self.mux == mux["inserter"],
47+
self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
48+
self.crc32_inserter.source.connect(self.logger_randomizer.sink)
49+
).Elif(self.mux == mux["checker"],
50+
self.streamer_randomizer.source.connect(self.crc32_checker.sink),
51+
self.crc32_checker.source.connect(self.logger_randomizer.sink)
52+
).Elif(self.mux == mux["both"],
53+
self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
54+
self.crc32_inserter.source.connect(self.crc32_checker.sink),
55+
self.crc32_checker.source.connect(self.logger_randomizer.sink)
56+
)
4257
]
4358

4459
def gen_simulation(self, selfp):
45-
for i in range(500):
46-
yield
47-
inserter_reference = frame_data + frame_crc
48-
inserter_generated = self.inserter_logger.data
60+
selfp.mux = mux["inserter"]
61+
print("streamer --> crc32_inserter --> logger:")
62+
self.streamer.send(Packet(payload))
63+
yield from self.logger.receive()
64+
s, l, e = check(payload+crc, self.logger.packet)
65+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
4966

50-
checker_reference = frame_data
51-
checker_generated = self.checker_logger.data
67+
selfp.mux = mux["checker"]
68+
print("streamer --> crc32_checker --> logger:")
69+
self.streamer.send(Packet(payload+crc))
70+
yield from self.logger.receive()
71+
s, l, e = check(payload, self.logger.packet)
72+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
5273

53-
print_results("inserter", inserter_reference, inserter_generated)
54-
print_results("checker", checker_reference, checker_generated)
74+
selfp.mux = mux["both"]
75+
print("streamer --> crc32_inserter --> crc32_checker --> logger:")
76+
self.streamer.send(Packet(payload))
77+
yield from self.logger.receive()
78+
s, l, e = check(payload, self.logger.packet)
79+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
5580

5681
if __name__ == "__main__":
5782
from migen.sim.generic import run_simulation
58-
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
83+
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd")

‎misoclib/ethmac/test/ethmac_tb.py

+13-25
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from misoclib.ethmac import EthMAC
77
from misoclib.ethmac.phy import loopback
88

9+
from misoclib.ethmac.test.common import *
10+
911
class WishboneMaster:
1012
def __init__(self, obj):
1113
self.obj = obj
@@ -90,46 +92,32 @@ def gen_simulation(self, selfp):
9092

9193
length = 1500-2
9294

93-
payload = [i % 0xFF for i in range(length)] + [0, 0, 0, 0]
95+
tx_payload = [seed_to_data(i, True) % 0xFF for i in range(length)] + [0, 0, 0, 0]
9496

9597
errors = 0
9698

9799
for slot in range(2):
100+
print("slot {}:".format(slot))
98101
# fill tx memory
99102
for i in range(length//4+1):
100-
dat = 0
101-
dat |= payload[4*i+0] << 24
102-
dat |= payload[4*i+1] << 16
103-
dat |= payload[4*i+2] << 8
104-
dat |= payload[4*i+3] << 0
103+
dat = int.from_bytes(tx_payload[4*i:4*(i+1)], "big")
105104
yield from wishbone_master.write(sram_reader_slots_offset[slot]+i, dat)
106105

107-
# send tx data & wait
106+
# send tx payload & wait
108107
yield from sram_reader_driver.start(slot, length)
109108
yield from sram_reader_driver.wait_done()
110109
yield from sram_reader_driver.clear_done()
111110

112-
# get rx data (loopback on PHY Model)
113-
rx_dat = []
111+
# get rx payload (loopback on PHY Model)
112+
rx_payload = []
114113
for i in range(length//4+1):
115114
yield from wishbone_master.read(sram_writer_slots_offset[slot]+i)
116115
dat = wishbone_master.dat
117-
rx_dat.append((dat >> 24) & 0xFF)
118-
rx_dat.append((dat >> 16) & 0xFF)
119-
rx_dat.append((dat >> 8) & 0xFF)
120-
rx_dat.append((dat >> 0) & 0xFF)
121-
122-
# check rx data
123-
for i in range(length):
124-
#print("{:02x} / {:02x}".format(rx_dat[i], payload[i]))
125-
if rx_dat[i] != payload[i]:
126-
errors += 1
127-
128-
for i in range(200):
129-
yield
130-
#print(selfp.ethmac.sram_reader._length.storage)
116+
rx_payload += list(dat.to_bytes(4, byteorder='big'))
131117

132-
print("Errors : {}".format(errors))
118+
# check results
119+
s, l, e = check(tx_payload[:length], rx_payload[:min(length, len(rx_payload))])
120+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
133121

134122
if __name__ == "__main__":
135-
run_simulation(TB(), ncycles=16000, vcd_name="my.vcd", keep_files=True)
123+
run_simulation(TB(), vcd_name="my.vcd")

‎misoclib/ethmac/test/preamble_tb.py

+58-34
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,81 @@
22

33
from misoclib.ethmac.common import *
44
from misoclib.ethmac.preamble import *
5-
from misoclib.ethmac.test import *
5+
from misoclib.ethmac.test.common import *
66

7-
frame_preamble = [
7+
preamble = [
88
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5
99
]
1010

11-
frame_data = [
11+
payload = [
1212
0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
13-
0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
14-
0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
15-
0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
16-
0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
17-
0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
18-
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
19-
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
13+
0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
14+
0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
15+
0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
16+
0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
17+
0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
18+
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
19+
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
2020
]
2121

22+
mux = {
23+
"inserter": 0,
24+
"checker": 1,
25+
"both": 2
26+
}
27+
2228
class TB(Module):
23-
def __init__(self):
29+
def __init__(self, random_level=50):
2430
sm = self.submodules
31+
sm.streamer = PacketStreamer(eth_description(8))
32+
sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
33+
sm.logger = PacketLogger(eth_description(8))
34+
sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
2535

26-
# Streamer (DATA) --> PreambleInserter --> Logger (expect PREAMBLE + DATA)
27-
sm.inserter_streamer = PacketStreamer(frame_data)
28-
sm.preamble_inserter = PreambleInserter(8)
29-
sm.inserter_logger = PacketLogger()
30-
self.comb +=[
31-
self.inserter_streamer.source.connect(self.preamble_inserter.sink),
32-
self.preamble_inserter.source.connect(self.inserter_logger.sink),
36+
self.comb += [
37+
self.streamer.source.connect(self.streamer_randomizer.sink),
38+
self.logger_randomizer.source.connect(self.logger.sink)
3339
]
3440

35-
# Streamer (PREAMBLE + DATA) --> CRC32Checher --> Logger (except DATA + check)
36-
sm.checker_streamer = PacketStreamer(frame_preamble + frame_data)
41+
sm.preamble_inserter = PreambleInserter(8)
3742
sm.preamble_checker = PreambleChecker(8)
38-
sm.checker_logger = PacketLogger()
39-
self.comb +=[
40-
self.checker_streamer.source.connect(self.preamble_checker.sink),
41-
self.preamble_checker.source.connect(self.checker_logger.sink),
42-
]
4343

44+
self.mux = Signal(2)
45+
self.comb += [
46+
If(self.mux == mux["inserter"],
47+
self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
48+
self.preamble_inserter.source.connect(self.logger_randomizer.sink)
49+
).Elif(self.mux == mux["checker"],
50+
self.streamer_randomizer.source.connect(self.preamble_checker.sink),
51+
self.preamble_checker.source.connect(self.logger_randomizer.sink)
52+
).Elif(self.mux == mux["both"],
53+
self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
54+
self.preamble_inserter.source.connect(self.preamble_checker.sink),
55+
self.preamble_checker.source.connect(self.logger_randomizer.sink)
56+
)
57+
]
4458
def gen_simulation(self, selfp):
45-
for i in range(500):
46-
yield
47-
inserter_reference = frame_preamble + frame_data
48-
inserter_generated = self.inserter_logger.data
59+
selfp.mux = mux["inserter"]
60+
print("streamer --> preamble_inserter --> logger:")
61+
self.streamer.send(Packet(payload))
62+
yield from self.logger.receive()
63+
s, l, e = check(preamble+payload, self.logger.packet)
64+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
4965

50-
checker_reference = frame_data
51-
checker_generated = self.checker_logger.data
66+
selfp.mux = mux["checker"]
67+
print("streamer --> preamble_checker --> logger:")
68+
self.streamer.send(Packet(preamble+payload))
69+
yield from self.logger.receive()
70+
s, l, e = check(payload, self.logger.packet)
71+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
5272

53-
print_results("inserter", inserter_reference, inserter_generated)
54-
print_results("checker", checker_reference, checker_generated)
73+
selfp.mux = mux["both"]
74+
print("streamer --> preamble_inserter --> preamble_checker --> logger:")
75+
self.streamer.send(Packet(payload))
76+
yield from self.logger.receive()
77+
s, l, e = check(payload, self.logger.packet)
78+
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
5579

5680
if __name__ == "__main__":
5781
from migen.sim.generic import run_simulation
58-
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
82+
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd")

0 commit comments

Comments
 (0)
Please sign in to comment.