Skip to content

Commit 6d67f2a

Browse files
mutinifniweberlo
authored andcommitted
implemented micro_common and added Python interfaces
1 parent b8de921 commit 6d67f2a

File tree

6 files changed

+332
-10
lines changed

6 files changed

+332
-10
lines changed

python/tvm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
from . import ndarray as nd
4444
from .ndarray import context, cpu, gpu, opencl, cl, vulkan, metal, mtl
45-
from .ndarray import vpi, rocm, opengl, ext_dev
45+
from .ndarray import vpi, rocm, opengl, ext_dev, micro_dev
4646

4747
from ._ffi.runtime_ctypes import TypeCode, TVMType
4848
from ._ffi.ndarray import TVMContext

python/tvm/contrib/binutil.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""Utilities for binary file manipulation"""
2+
import subprocess
3+
from os.path import join, exists
4+
from . import util
5+
from .._ffi.base import py_str
6+
from ..api import register_func, convert
7+
8+
9+
@register_func("tvm_get_section_size")
10+
def tvm_get_section_size(binary_name, section):
11+
"""Finds size of the section in the binary.
12+
Assumes "size" shell command exists (typically works only on Linux machines)
13+
14+
Parameters
15+
----------
16+
binary_name : string
17+
name of the binary file
18+
19+
section : string
20+
type of section
21+
22+
Return
23+
------
24+
size : integer
25+
size of the section in bytes
26+
"""
27+
section_map = {"text": "1", "data": "2", "bss": "3"}
28+
p1 = subprocess.Popen(["size", binary_name], stdout=subprocess.PIPE)
29+
p2 = subprocess.Popen(["awk", "{print $" + section_map[section] + "}"],
30+
stdin=p1.stdout, stdout=subprocess.PIPE)
31+
p3 = subprocess.Popen(["tail", "-1"], stdin=p2.stdout, stdout=subprocess.PIPE)
32+
p1.stdout.close()
33+
p2.stdout.close()
34+
(out, _) = p3.communicate()
35+
if p3.returncode != 0:
36+
msg = "Error in finding section size:\n"
37+
msg += py_str(out)
38+
raise RuntimeError(msg)
39+
return int(out)
40+
41+
42+
@register_func("tvm_relocate_binary")
43+
def tvm_relocate_binary(binary_name, text, data, bss):
44+
"""Relocates sections in the binary to new addresses
45+
46+
Parameters
47+
----------
48+
binary_name : string
49+
name of the binary file
50+
51+
text : string
52+
text section address
53+
54+
data : string
55+
data section address
56+
57+
bss : string
58+
bss section address
59+
60+
Return
61+
------
62+
rel_bin : bytearray
63+
the relocated binary
64+
"""
65+
tmp_dir = util.tempdir()
66+
rel_obj = tmp_dir.relpath("relocated.o")
67+
p1 = subprocess.Popen(["ld", binary_name,
68+
"-Ttext", text,
69+
"-Tdata", data,
70+
"-Tbss", bss,
71+
"-o", rel_obj],
72+
stdout=subprocess.PIPE,
73+
stderr=subprocess.STDOUT)
74+
(out, _) = p1.communicate()
75+
if p1.returncode != 0:
76+
msg = "Linking error using ld:\n"
77+
msg += py_str(out)
78+
raise RuntimeError(msg)
79+
rel_bin = bytearray(open(rel_obj, "rb").read())
80+
return rel_bin
81+
82+
83+
@register_func("tvm_read_binary_section")
84+
def tvm_read_binary_section(binary_name, section):
85+
"""Returns the contents of the specified section in the binary file
86+
87+
Parameters
88+
----------
89+
binary_name : string
90+
name of the binary file
91+
92+
section : string
93+
type of section
94+
95+
Return
96+
------
97+
section_bin : bytearray
98+
contents of the read section
99+
"""
100+
tmp_dir = util.tempdir()
101+
tmp_section = tmp_dir.relpath("tmp_section.bin")
102+
p1 = subprocess.Popen(["objcopy", "--dump-section",
103+
"." + section + "=" + tmp_section,
104+
binary_name],
105+
stdout=subprocess.PIPE,
106+
stderr=subprocess.STDOUT)
107+
(out, _) = p1.communicate()
108+
if p1.returncode != 0:
109+
msg = "Error in using objcopy:\n"
110+
msg += py_str(out)
111+
raise RuntimeError(msg)
112+
try:
113+
# get section content if it exits
114+
section_bin = bytearray(open(tmp_section, "rb").read())
115+
except IOError:
116+
# return empty bytearray if the section does not exist
117+
section_bin = bytearray("")
118+
return section_bin
119+
120+
121+
@register_func("tvm_get_symbol_map")
122+
def tvm_get_symbol_map(binary):
123+
"""Obtains a map of symbols to addresses in the passed binary
124+
125+
Parameters
126+
----------
127+
binary : bytearray
128+
the object file
129+
130+
Return
131+
------
132+
symbol_map : dictionary
133+
map of defined symbols to addresses
134+
"""
135+
tmp_dir = util.tempdir()
136+
tmp_obj = tmp_dir.relpath("tmp_obj.bin")
137+
with open(tmp_obj, "wb") as out_file:
138+
out_file.write(bytes(binary))
139+
p1 = subprocess.Popen(["nm", "-C", "--defined-only", tmp_obj],
140+
stdout=subprocess.PIPE,
141+
stderr=subprocess.STDOUT)
142+
(out, _) = p1.communicate()
143+
if p1.returncode != 0:
144+
msg = "Error in using nm:\n"
145+
msg += py_str(out)
146+
raise RuntimeError(msg)
147+
out = out.splitlines()
148+
map_str = ""
149+
for line in out:
150+
line = line.split()
151+
map_str += line[2] + "\n"
152+
map_str += line[0] + "\n"
153+
return map_str

python/tvm/micro/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""uTVM module for bare-metal backends.
2+
3+
uTVM (or the micro backend) enables provides support for bare-metal devices.
4+
Its targets currently include a host-emulated device which is used for testing,
5+
and JTAG-based openocd device which allows actual interfacing with microdevices.
6+
"""
7+
8+
from ..contrib import binutil

python/tvm/ndarray.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,22 @@ def ext_dev(dev_id=0):
189189
return TVMContext(12, dev_id)
190190

191191

192+
def micro_dev(dev_id=0):
193+
"""Construct a micro device
194+
195+
Parameters
196+
----------
197+
dev_id : int, optional
198+
The integer device id
199+
200+
Returns
201+
-------
202+
ctx : TVMContext
203+
The created context
204+
"""
205+
return TVMContext(13, dev_id)
206+
207+
192208
cl = opencl
193209
mtl = metal
194210

src/runtime/micro/micro_common.cc

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
/*!
22
* Copyright (c) 2019 by Contributors
3-
* \file bin_util.cc
4-
* \brief binary modification utilities
3+
* \file micro_common.cc
4+
* \brief common utilties for uTVM
55
*/
66

7-
#include <stdio.h>
7+
#include <tvm/runtime/c_runtime_api.h>
8+
#include <tvm/runtime/registry.h>
9+
#include <cstdio>
810
#include <string>
11+
#include <sstream>
12+
#include <cstdint>
913
#include "micro_session.h"
1014
#include "micro_common.h"
1115

@@ -25,30 +29,75 @@ const char* SectionToString(SectionKind section) {
2529
}
2630
}
2731

28-
// TODO: implement these in Python using PackedFunc + Registry
2932
void* GetSymbol(std::unordered_map<std::string, void*> symbol_map,
3033
std::string name,
3134
void* base_addr) {
32-
return nullptr;
35+
void* symbol_addr = symbol_map[name];
36+
return (void*)((uint8_t*) symbol_addr - (uint8_t*) base_addr);
37+
}
38+
39+
static std::string AddrToString(void* addr) {
40+
std::stringstream stream;
41+
if (addr != nullptr)
42+
stream << addr;
43+
else
44+
stream << "0x0";
45+
std::string string_addr = stream.str();
46+
return string_addr;
3347
}
3448

3549
std::string RelocateBinarySections(std::string binary_name,
3650
void* text,
3751
void* data,
3852
void* bss) {
39-
return "";
53+
const auto* f = Registry::Get("tvm_relocate_binary");
54+
CHECK(f != nullptr) << "Require tvm_relocate_binary to exist in registry";
55+
std::string relocated_bin = (*f)(binary_name,
56+
AddrToString(text),
57+
AddrToString(data),
58+
AddrToString(bss));
59+
return relocated_bin;
4060
}
4161

4262
std::string ReadSection(std::string binary_name, SectionKind section) {
43-
return "";
63+
CHECK(section == kText || section == kData || section == kBss)
64+
<< "ReadSection requires section to be one of text, data or bss.";
65+
const auto* f = Registry::Get("tvm_read_binary_section");
66+
CHECK(f != nullptr) << "Require tvm_read_binary_section to exist in registry";
67+
std::string section_contents = (*f)(binary_name, SectionToString(section));
68+
return section_contents;
4469
}
4570

4671
size_t GetSectionSize(std::string binary_name, SectionKind section) {
47-
return 0;
72+
CHECK(section == kText || section == kData || section == kBss)
73+
<< "GetSectionSize requires section to be one of text, data or bss.";
74+
const auto* f = Registry::Get("tvm_get_section_size");
75+
CHECK(f != nullptr) << "Require tvm_get_section_size to exist in registry";
76+
size_t size = (*f)(binary_name, SectionToString(section));
77+
return size;
4878
}
4979

5080
std::unordered_map<std::string, void*> GetSymbolMap(std::string binary) {
51-
return std::unordered_map<std::string, void*>();
81+
const auto* f = Registry::Get("tvm_get_symbol_map");
82+
CHECK(f != nullptr) << "Require tvm_get_symbol_map to exist in registry";
83+
TVMByteArray arr;
84+
arr.data = &binary[0];
85+
arr.size = binary.length();
86+
std::string map_str = (*f)(arr);
87+
// parse symbols and addresses from returned string
88+
std::unordered_map<std::string, void*> symbol_map;
89+
std::stringstream stream;
90+
stream << map_str;
91+
std::string name;
92+
void* addr;
93+
stream >> name;
94+
stream >> std::hex >> addr;
95+
while (stream) {
96+
symbol_map[name] = addr;
97+
stream >> name;
98+
stream >> std::hex >> addr;
99+
}
100+
return symbol_map;
52101
}
53102
} // namespace runtime
54103
} // namespace tvm
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import tvm
2+
import subprocess
3+
from tvm.contrib import util
4+
from tvm.contrib import cc
5+
from tvm.contrib.binutil import *
6+
7+
8+
def make_binary():
9+
prog = "int a = 7; \
10+
int main() { \
11+
int b = 5; \
12+
return 0; \
13+
}"
14+
tmp_dir = util.tempdir()
15+
tmp_source = tmp_dir.relpath("source.c")
16+
tmp_obj = tmp_dir.relpath("obj.o")
17+
with open(tmp_source, "w") as f:
18+
f.write(prog)
19+
p1 = subprocess.Popen(["gcc", "-c", tmp_source, "-o", tmp_obj],
20+
stdout=subprocess.PIPE,
21+
stderr=subprocess.STDOUT)
22+
p1.communicate()
23+
prog_bin = bytearray(open(tmp_obj, "rb").read())
24+
return prog_bin
25+
26+
27+
def test_tvm_get_section_size(binary):
28+
tmp_dir = util.tempdir()
29+
tmp_bin = tmp_dir.relpath("obj.bin")
30+
with open(tmp_bin, "wb") as f:
31+
f.write(binary)
32+
def verify():
33+
print("Text section size: %d" % tvm_get_section_size(tmp_bin, "text"))
34+
print("Data section size: %d" % tvm_get_section_size(tmp_bin, "data"))
35+
print("Bss section size: %d" % tvm_get_section_size(tmp_bin, "bss"))
36+
print
37+
verify()
38+
39+
40+
def test_tvm_relocate_binary(binary):
41+
tmp_dir = util.tempdir()
42+
tmp_bin = tmp_dir.relpath("obj.bin")
43+
with open(tmp_bin, "wb") as f:
44+
f.write(binary)
45+
def verify():
46+
rel_bin = tvm_relocate_binary(tmp_bin, "0x0", "0x10000", "0x20000")
47+
print("Relocated binary section sizes")
48+
test_tvm_get_section_size(rel_bin)
49+
relf = tmp_dir.relpath("rel.bin")
50+
with open(relf, "wb") as f:
51+
f.write(rel_bin)
52+
p1 = subprocess.Popen(["nm", "-C", "--defined-only", relf],
53+
stdout=subprocess.PIPE,
54+
stderr=subprocess.STDOUT)
55+
(out, _) = p1.communicate()
56+
print("Relocated binary symbols")
57+
print(out)
58+
print
59+
verify()
60+
61+
62+
def test_tvm_read_binary_section(binary):
63+
tmp_dir = util.tempdir()
64+
tmp_bin = tmp_dir.relpath("obj.bin")
65+
with open(tmp_bin, "wb") as f:
66+
f.write(binary)
67+
def verify():
68+
text_bin = tvm_read_binary_section(tmp_bin, "text")
69+
data_bin = tvm_read_binary_section(tmp_bin, "data")
70+
bss_bin = tvm_read_binary_section(tmp_bin, "bss")
71+
print("Read text section part of binary? %r" % (text_bin in binary))
72+
print("Read data section part of binary? %r" % (data_bin in binary))
73+
print("Read bss section part of binary? %r" % (bss_bin in binary))
74+
print
75+
verify()
76+
77+
78+
def test_tvm_get_symbol_map(binary):
79+
tmp_dir = util.tempdir()
80+
tmp_bin = tmp_dir.relpath("obj.bin")
81+
with open(tmp_bin, "wb") as f:
82+
f.write(binary)
83+
def verify():
84+
rel_bin = tvm_relocate_binary(tmp_bin, "0x0", "0x10000", "0x20000")
85+
symbol_map = tvm_get_symbol_map(rel_bin)
86+
print("Obtained symbol map")
87+
print(symbol_map)
88+
verify()
89+
90+
91+
if __name__ == "__main__":
92+
prog_bin = make_binary()
93+
test_tvm_get_section_size(prog_bin)
94+
test_tvm_relocate_binary(prog_bin)
95+
test_tvm_read_binary_section(prog_bin)
96+
test_tvm_get_symbol_map(prog_bin)

0 commit comments

Comments
 (0)