Skip to content

Commit 4222f67

Browse files
committed
Added multinode testbench
1 parent 03447a3 commit 4222f67

28 files changed

+555
-41
lines changed

README.md

+13-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ My main resource was the links in <https://tis100.complexity.nl/links.html>. Ias
1010

1111
# Progress
1212

13-
Currently this codebase can generate a Vivado project that can run behavioral simulations of a a T21 node. The node is functional and should be exactly faithful in behavior to the game implementation. The one missing feature is the that the virtual ports ANY and LAST aren't supported. While I think it's possible to implement their behavior, it would require a much more complicated design.
13+
Currently this codebase can generate a Vivado project that can run behavioral simulations of a grid of T21 nodes. The nodes are functional and should be exactly faithful in behavior to the game implementation. The one missing feature is the that the virtual ports ANY and LAST aren't supported. While I think it's possible to implement their behavior, it would require a much more complicated design.
1414

1515
Here's the schematic for the code:
1616

@@ -22,7 +22,7 @@ In addition I've written a series of python scripts to aid in development and te
2222
* A basic emulator for running TIS code in Python
2323
* Tools for generating stimuli and verification data for the FPGA simulation unit tests
2424

25-
The furthest test is running the following code in the full node:
25+
An example simulation is running the following code in a single node:
2626

2727
```
2828
MOV UP ACC
@@ -39,6 +39,16 @@ Feeding in [5, 100] gives the output [50, 999] in the behavioral simulation. Her
3939

4040
<a href="docs/mult_sim.png"><img src="docs/mult_sim.png"/></a>
4141

42+
Another simulation is able to correctly solve the 6th puzzle in the game "Sequence Generator" using two nodes.
43+
44+
The code and input:
45+
46+
<a href="docs/sig_gen_ex.png"><img src="docs/sig_gen_ex.png"/></a>
47+
48+
The first 7 outputs from the simulator:
49+
50+
<a href="docs/sig_gen_sim.png"><img src="docs/sig_gen_sim.png"/></a>
51+
4252
# Instruction Format
4353

4454
I created my own binary representation of the TIS100 instructions. The format is (len in bits):
@@ -112,7 +122,7 @@ It can load the compiler output and prints the register state for each time tick
112122

113123
# Next Steps
114124

115-
- [ ] Connect multiple nodes together using top level design file
125+
- [x] Connect multiple nodes together using top level design file
116126
- [ ] Connect to ARM in Zynq SoC over AXI (use as co-processor)
117127
- [ ] Have ability to view contents, load code, input, output
118128
- [ ] Be able to wire up in Xilinx Block designer

data/seq_gen/seq_gen_ina.mem

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
02e
2+
047
3+
042
4+
015
5+
04f
6+
017
7+
03e
8+
017
9+
024
10+
060
11+
00c
12+
061
13+
02f

data/seq_gen/seq_gen_inb.mem

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
047
2+
01d
3+
05a
4+
043
5+
04f
6+
054
7+
04e
8+
01b
9+
03c
10+
02d
11+
043
12+
02a
13+
040

data/seq_gen/seq_gen_node1.mem

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
000101000000000000001
2+
000100100000000000101
3+
000100100000000000101

data/seq_gen/seq_gen_node1.p

139 Bytes
Binary file not shown.

data/seq_gen/seq_gen_node1.tis

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MOV UP ACC
2+
MOV ACC RIGHT
3+
MOV ACC RIGHT

data/seq_gen/seq_gen_node2.mem

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
000101000000000000001
2+
001100000000000000000
3+
010110000000000000000
4+
101000000000000101000
5+
001000000000000000000
6+
000100100000000000011
7+
000110000000000000011
8+
011100000000000100000
9+
000110000000000000011
10+
001000000000000000000
11+
000100100000000000011
12+
000100000000000000011

data/seq_gen/seq_gen_node2.p

373 Bytes
Binary file not shown.

data/seq_gen/seq_gen_node2.tis

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
MOV UP ACC
2+
SAV
3+
SUB LEFT
4+
JGZ B
5+
SWP
6+
MOV ACC DOWN
7+
MOV LEFT DOWN
8+
JMP END
9+
B: MOV LEFT DOWN
10+
SWP
11+
MOV ACC DOWN
12+
END: MOV 0 DOWN

data/seq_gen/seq_gen_result.mem

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
02e
2+
047
3+
000
4+
01d
5+
047
6+
000
7+
042
8+
05a
9+
000
10+
015
11+
043
12+
000
13+
04f
14+
04f
15+
000
16+
017
17+
054
18+
000
19+
03e
20+
04e
21+
000
22+
017
23+
01b
24+
000
25+
024
26+
03c
27+
000
28+
02d
29+
060
30+
000
31+
00c
32+
043
33+
000
34+
02a
35+
061
36+
000
37+
02f
38+
040
39+
000

docs/sig_gen_ex.png

19.8 KB
Loading

docs/sig_gen_sim.png

49.8 KB
Loading

scripts/gen_node_nets.py

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
from enum import Enum, auto
2+
from typing import Tuple, List, Optional
3+
4+
class NodeType(Enum):
5+
IN = auto()
6+
OUT = auto()
7+
NODE = auto()
8+
9+
class WireType(Enum):
10+
IN = auto()
11+
OUT = auto()
12+
INOUT = auto()
13+
14+
class Direction(Enum):
15+
RIGHT = auto()
16+
DOWN = auto()
17+
18+
class Node:
19+
def __init__(self, name, type: NodeType):
20+
self.type = type
21+
self.name = name
22+
23+
class Wire:
24+
def __init__(self, node, dir: Direction, other, type: WireType):
25+
self.type = type
26+
self.node = node
27+
self.dir = dir
28+
self.other = other
29+
30+
def get_dir(dir):
31+
return {
32+
Direction.RIGHT: 'right',
33+
Direction.DOWN: 'down'
34+
}[dir]
35+
36+
def get_op_dir(dir):
37+
return {
38+
Direction.RIGHT: 'left',
39+
Direction.DOWN: 'up'
40+
}[dir]
41+
42+
def wire_write(a, b, dir):
43+
if (a.type == NodeType.IN or a.type == NodeType.OUT) and \
44+
(b.type == NodeType.IN or b.type == NodeType.OUT):
45+
return None
46+
if a.type == NodeType.IN or b.type == NodeType.OUT:
47+
return Wire(a.name, dir, b.name, WireType.OUT)
48+
elif a.type == NodeType.OUT or b.type == NodeType.IN:
49+
return Wire(a.name, dir, b.name, WireType.IN)
50+
else:
51+
return Wire(a.name, dir, b.name, WireType.INOUT)
52+
53+
54+
55+
def print_assignments(wire: Wire):
56+
if wire is None:
57+
return
58+
if wire.type != WireType.IN:
59+
print(f'assign {wire.other}_{get_op_dir(wire.dir)}_in_data = {wire.node}_{get_dir(wire.dir)}_out_data;')
60+
print(f'assign {wire.other}_{get_op_dir(wire.dir)}_in_valid = {wire.node}_{get_dir(wire.dir)}_out_valid;')
61+
print(f'assign {wire.node}_{get_dir(wire.dir)}_out_ready = {wire.other}_{get_op_dir(wire.dir)}_in_ready;')
62+
if wire.type != WireType.OUT:
63+
print(f'assign {wire.node}_{get_dir(wire.dir)}_in_data = {wire.other}_{get_op_dir(wire.dir)}_out_data;')
64+
print(f'assign {wire.node}_{get_dir(wire.dir)}_in_valid = {wire.other}_{get_op_dir(wire.dir)}_out_valid;')
65+
print(f'assign {wire.other}_{get_op_dir(wire.dir)}_out_ready = {wire.node}_{get_dir(wire.dir)}_in_ready;')
66+
67+
68+
def main():
69+
70+
in_a_stream = Node('in_a_stream', NodeType.IN)
71+
in_b_stream = Node('in_b_stream', NodeType.IN)
72+
out_stream = Node('out_stream', NodeType.OUT)
73+
node1 = Node('node1', NodeType.NODE)
74+
node2 = Node('node2', NodeType.NODE)
75+
76+
nodes:List[List[Optional[Node]]] = [[in_a_stream, in_b_stream],
77+
[node1, node2],
78+
[None, out_stream]]
79+
80+
wires: List[Wire] = []
81+
for r, row in enumerate(nodes):
82+
for c, node in enumerate(row):
83+
if node is None:
84+
continue
85+
if r + 1 < len(nodes) and nodes[r+1][c] is not None:
86+
wires.append(wire_write(node, nodes[r+1][c], Direction.DOWN))
87+
if c + 1 < len(row) and nodes[r][c+1] is not None:
88+
wires.append(wire_write(node, nodes[r][c+1], Direction.RIGHT))
89+
90+
91+
wire_segments = []
92+
for row in nodes:
93+
for node in row:
94+
if node is None:
95+
continue
96+
if node.type == NodeType.IN:
97+
for wire in wires:
98+
if wire is None:
99+
continue
100+
if wire.node == node.name:
101+
print(f'reg signed [10:0] {wire.node}_{get_dir(wire.dir)}_out_data;')
102+
print(f'reg {wire.node}_{get_dir(wire.dir)}_out_valid;')
103+
print(f'wire {wire.node}_{get_dir(wire.dir)}_out_ready;')
104+
print()
105+
elif wire.other == node.name:
106+
print(f'reg signed [10:0] {wire.other}_{get_dir(wire.dir)}_out_data;')
107+
print(f'reg {wire.other}_{get_op_dir(wire.dir)}_out_valid;')
108+
print(f'wire {wire.other}_{get_op_dir(wire.dir)}_out_ready;')
109+
print()
110+
elif node.type == NodeType.OUT:
111+
for wire in wires:
112+
if wire is None:
113+
continue
114+
if wire.node == node.name:
115+
print(f'wire signed [10:0] {wire.node}_{get_dir(wire.dir)}_in_data;')
116+
print(f'wire {wire.node}_{get_dir(wire.dir)}_in_valid;')
117+
print(f'reg {wire.node}_{get_dir(wire.dir)}_in_ready;')
118+
print()
119+
elif wire.other == node.name:
120+
print(f'wire signed [10:0] {wire.other}_{get_op_dir(wire.dir)}_in_data;')
121+
print(f'wire {wire.other}_{get_op_dir(wire.dir)}_in_valid;')
122+
print(f'reg {wire.other}_{get_op_dir(wire.dir)}_in_ready;')
123+
print()
124+
else:
125+
for wire in wires:
126+
if wire is None:
127+
continue
128+
if wire.node == node.name:
129+
if wire.type != WireType.OUT:
130+
wire_segments.append(f'{wire.node}_{get_dir(wire.dir)}_in')
131+
if wire.type != WireType.IN:
132+
wire_segments.append(f'{wire.node}_{get_dir(wire.dir)}_out')
133+
if node.name == wire.other:
134+
if wire.type != WireType.OUT:
135+
wire_segments.append(f'{wire.other}_{get_op_dir(wire.dir)}_out')
136+
if wire.type != WireType.IN:
137+
wire_segments.append(f'{wire.other}_{get_op_dir(wire.dir)}_in')
138+
139+
for wire in wire_segments:
140+
print(f'wire [10:0] {wire}_data;')
141+
print(f'wire {wire}_valid;')
142+
print(f'wire {wire}_ready;')
143+
print()
144+
for wire in wires:
145+
print_assignments(wire)
146+
print()
147+
for row in nodes:
148+
for node in row:
149+
if node is None:
150+
continue
151+
if node.type == NodeType.NODE:
152+
for wire in wire_segments:
153+
if wire.startswith(node.name + '_'):
154+
sub = wire[len(node.name) + 1:]
155+
print(f'.{sub}_data({wire}_data),')
156+
print(f'.{sub}_valid({wire}_valid),')
157+
print(f'.{sub}_ready({wire}_ready),')
158+
print()
159+
160+
161+
162+
163+
if __name__ == "__main__":
164+
main()

scripts/run_tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ source scripts/env.sh
66
# xsim op_decode_tb_behav -key {Behavioral:sim_1:Functional:op_decode_tb} -tclbatch $SCRIPT_PATH/run_tests.tcl -log simulate1.log
77

88
vivado -mode batch -source scripts/run_tests.tcl | tee simulation.log
9-
python scripts/check_sim_logs.py simulation.log 6
9+
python scripts/check_sim_logs.py simulation.log 7

scripts/run_tests.tcl

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ open_project tis100/tis100.xpr
22

33
set_property -name {xsim.simulate.runtime} -value {100us} -objects [get_filesets sim_1]
44

5-
set test_benches {"alu_tb" "op_decode_tb" "registers_tb" "instr_rom_tb" "dir_manager_tb" "t21_node_tb" }
5+
set test_benches {"alu_tb" "op_decode_tb" "registers_tb" "instr_rom_tb" "dir_manager_tb" "t21_node_tb" "t21_2_node_tb" }
66

77
foreach test_bench $test_benches {
88
set_property top $test_bench [get_filesets sim_1]

0 commit comments

Comments
 (0)