-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1ca4e25
Showing
41 changed files
with
7,456 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
node_modules | ||
artifacts | ||
cache | ||
.*.swp | ||
venv | ||
.idea | ||
*.log | ||
|
||
examples/mnist/trainning/data/MNIST/raw | ||
|
||
mlgo | ||
mlgo.bin | ||
|
||
examples/llama/llama | ||
|
||
ml_mips/ml_mips | ||
ml_mips/ml_mips.bin | ||
|
||
examples/llama/data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# MLGO | ||
|
||
MLGO is tensor library for machine learning in pure Golang that can run on MIPS. | ||
|
||
The machine learning part of this project refers to the legendary [ggml.cpp](https://github.com/ggerganov/ggml) framework. | ||
|
||
|
||
## MNIST | ||
|
||
1. Train the AI model. See `examples/mnist/trainning/mnist.ipynb` | ||
2. Convert the AI model into GGML using `examples/mnist/convert-h5-to-ggml.py` | ||
3. Build the AI inference engine for MIPS | ||
`cd examples/mnist_mips && ./build` | ||
`` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
export GOOS=linux | ||
export GOARCH=mips | ||
export GOMIPS=softfloat | ||
go build -o ./mlgo | ||
|
||
file mlgo | ||
|
||
if [[ ! -d venv ]]; then | ||
python3 -m venv venv | ||
fi | ||
|
||
./compile.py mlgo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package common | ||
|
||
import ( | ||
"math" | ||
"os" | ||
"unsafe" | ||
) | ||
|
||
// NB! INT = 32 bits | ||
func ReadInt32FromFile(file *os.File) uint32 { | ||
buf := make([]byte, 4) | ||
if count, err := file.Read(buf); err != nil || count != 4 { | ||
return 0 | ||
} | ||
return uint32(buf[3])<<24 | uint32(buf[2])<<16 | uint32(buf[1])<<8 | uint32(buf[0]) | ||
} | ||
|
||
func ReadStringFromFile(file *os.File, len uint32) string { | ||
buf := make([]byte, len) | ||
if count, err := file.Read(buf); err != nil || count != int(len) { | ||
return "" | ||
} | ||
return string(buf) | ||
} | ||
|
||
|
||
func ReadFP32FromFile(file *os.File) float32 { | ||
buf := make([]byte, 4) | ||
if count, err := file.Read(buf); err != nil || count != 4 { | ||
return 0.0 | ||
} | ||
bits := uint32(buf[3])<<24 | uint32(buf[2])<<16 | uint32(buf[1])<<8 | uint32(buf[0]) | ||
return math.Float32frombits(bits) | ||
} | ||
|
||
func min(a, b int) int { | ||
if a <= b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
|
||
|
||
func DecodeFloat32List(bs []byte) []float32 { | ||
return unsafe.Slice((*float32)(unsafe.Pointer(&bs[0])), len(bs)/4) | ||
} | ||
|
||
func EncodeFloat32List(fs []float32) []byte { | ||
return unsafe.Slice((*byte)(unsafe.Pointer(&fs[0])), len(fs)*4) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package common | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"os" | ||
"reflect" | ||
"unsafe" | ||
) | ||
|
||
// vm only =================================================================================== | ||
|
||
// memory layout in MIPS | ||
const ( | ||
INPUT_ADDR = 0x31000000 | ||
OUTPUT_ADDR = 0x32000000 | ||
MODEL_ADDR = 0x33000000 | ||
MAGIC_ADDR = 0x30000800 | ||
) | ||
|
||
func ByteAt(addr uint64, length int) []byte { | ||
var ret []byte | ||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&ret)) | ||
bh.Data = uintptr(addr) | ||
bh.Len = length | ||
bh.Cap = length | ||
return ret | ||
} | ||
|
||
// reading bytes from bigEndian or littleEndian | ||
func ReadBytes(addr uint64, isBigEndian bool) []byte { | ||
rawSize := CopyBytes(ByteAt(addr, 4)) | ||
size := BytesToInt32(rawSize, isBigEndian) | ||
ret := ByteAt(addr + 4, int(size)) | ||
//shoud we copy here? may not for saving memory | ||
return ret | ||
} | ||
|
||
func Halt() { | ||
//os.Stderr.WriteString("THIS SHOULD BE PATCHED OUT\n") | ||
// the exit syscall is a jump to 0x5ead0000 now | ||
os.Exit(0) | ||
} | ||
|
||
func Output(output []byte, isBigEndian bool) { | ||
size := len(output) | ||
rawSize := IntToBytes(size,isBigEndian) | ||
mSize := ByteAt(OUTPUT_ADDR, 4) | ||
copy(mSize, rawSize) | ||
mData := ByteAt(OUTPUT_ADDR + 4, size) | ||
copy(mData, output) | ||
// magic code => have written the result | ||
magic := ByteAt(MAGIC_ADDR, 4) | ||
copy(magic, []byte{0x12, 0x34, 0x56, 0x78}) | ||
// stop everything | ||
Halt() | ||
} | ||
|
||
|
||
func IntToBytes(n int, isBigEndian bool) []byte { | ||
x := int32(n) | ||
|
||
bytesBuffer := bytes.NewBuffer([]byte{}) | ||
if isBigEndian { | ||
binary.Write(bytesBuffer, binary.BigEndian, x) | ||
} else { | ||
binary.Write(bytesBuffer, binary.LittleEndian, x) | ||
} | ||
return bytesBuffer.Bytes() | ||
} | ||
|
||
func BytesToInt32(b []byte, isBigEndian bool) int32 { | ||
bytesBuffer := bytes.NewBuffer(b) | ||
|
||
var x int32 | ||
if isBigEndian { | ||
binary.Read(bytesBuffer, binary.BigEndian, &x) | ||
} else { | ||
binary.Read(bytesBuffer, binary.LittleEndian, &x) | ||
} | ||
|
||
|
||
return x | ||
} | ||
|
||
func Float32ToBytes(x float32, isBigEndian bool) []byte { | ||
bytesBuffer := bytes.NewBuffer([]byte{}) | ||
if isBigEndian { | ||
binary.Write(bytesBuffer, binary.BigEndian, x) | ||
} else { | ||
binary.Write(bytesBuffer, binary.LittleEndian, x) | ||
} | ||
return bytesBuffer.Bytes() | ||
} | ||
|
||
func BytesToFloat32(b []byte, isBigEndian bool) float32 { | ||
byteBuffer := bytes.NewBuffer(b) | ||
var x float32 | ||
if isBigEndian { | ||
binary.Read(byteBuffer, binary.BigEndian, &x) | ||
} else { | ||
binary.Read(byteBuffer, binary.LittleEndian, &x) | ||
} | ||
|
||
return x | ||
} | ||
|
||
// CopyBytes returns an exact copy of the provided bytes. | ||
func CopyBytes(b []byte) (copiedBytes []byte) { | ||
if b == nil { | ||
return nil | ||
} | ||
copiedBytes = make([]byte, len(b)) | ||
copy(copiedBytes, b) | ||
|
||
return | ||
} | ||
|
||
// read from index then return the result and the next index | ||
func ReadInt32FromBytes(data []byte, index *int, isBigEndian bool) (uint32) { | ||
if (*index + 4 > len(data)) { | ||
*index = len(data) | ||
return 0 | ||
} | ||
buf := CopyBytes(data[*index:*index+4]) | ||
ret := BytesToInt32(buf, isBigEndian) | ||
*index = *index + 4 | ||
return uint32(ret) | ||
} | ||
|
||
func ReadFP32FromBytes(data []byte, index *int, isBigEndian bool) (float32) { | ||
if (*index + 4 > len(data)) { | ||
*index = len(data) | ||
return 0 | ||
} | ||
buf := CopyBytes(data[*index:*index+4]) | ||
ret := BytesToFloat32(buf, isBigEndian) | ||
*index = *index + 4 | ||
return ret | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
"unsafe" | ||
) | ||
|
||
func TestByteFloat(t *testing.T){ | ||
a := 1.234 | ||
ab := Float32ToBytes(float32(a), true) | ||
aa := BytesToFloat32(ab, true) | ||
fmt.Println(a, ab, aa) | ||
} | ||
|
||
func byteSliceToFloat32Slice(src []byte) []float32 { | ||
if len(src) == 0 { | ||
return nil | ||
} | ||
|
||
l := len(src) / 4 | ||
ptr := unsafe.Pointer(&src[0]) | ||
// It is important to keep in mind that the Go garbage collector | ||
// will not interact with this data, and that if src if freed, | ||
// the behavior of any Go code using the slice is nondeterministic. | ||
// Reference: https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices | ||
return (*[1 << 26]float32)((*[1 << 26]float32)(ptr))[:l:l] | ||
} | ||
|
||
func encodeUnsafe(fs []float32) []byte { | ||
return unsafe.Slice((*byte)(unsafe.Pointer(&fs[0])), len(fs)*4) | ||
} | ||
|
||
func decodeUnsafe(bs []byte) []float32 { | ||
return unsafe.Slice((*float32)(unsafe.Pointer(&bs[0])), len(bs)/4) | ||
} | ||
|
||
func TestByteSliceToFloat32Slice(t *testing.T) { | ||
as := []float32{1.234, 2.345} | ||
asBytes := make([]byte, 0) | ||
for i := 0; i < len(as); i++ { | ||
asBytes = append(asBytes, Float32ToBytes(as[i], false)...) | ||
} | ||
fmt.Println(asBytes) | ||
fmt.Println(byteSliceToFloat32Slice(asBytes)) | ||
fmt.Println(encodeUnsafe(as)) | ||
fmt.Println(decodeUnsafe(encodeUnsafe(as))) | ||
fmt.Println(decodeUnsafe(asBytes)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#!/usr/bin/env python3 | ||
import os | ||
import sys | ||
import struct | ||
import hashlib | ||
from rangetree import RangeTree | ||
from elftools.elf.elffile import ELFFile | ||
|
||
def load_minigeth(fn="mlgo"): | ||
elf = open(fn, "rb") | ||
data = elf.read() | ||
elf.seek(0) | ||
|
||
elffile = ELFFile(elf) | ||
|
||
end_addr = 0 | ||
for seg in elffile.iter_segments(): | ||
end_addr = max(end_addr, seg.header.p_vaddr + seg.header.p_memsz) | ||
|
||
# program memory (16 MB) | ||
prog_size = (end_addr+0xFFF) & ~0xFFF | ||
prog_dat = bytearray(prog_size) | ||
print("malloced 0x%x for program" % prog_size) | ||
|
||
for seg in elffile.iter_segments(): | ||
print(seg.header, hex(seg.header.p_vaddr)) | ||
prog_dat[seg.header.p_vaddr:seg.header.p_vaddr+len(seg.data())] = seg.data() | ||
|
||
entry = elffile.header.e_entry | ||
print("entrypoint: 0x%x" % entry) | ||
|
||
# moved to MIPS | ||
sf = os.path.join(os.path.dirname(os.path.abspath(__file__)), "startup", "startup.bin") | ||
start = open(sf, "rb").read() + struct.pack(">I", entry) | ||
prog_dat[:len(start)] = start | ||
entry = 0 | ||
|
||
r = RangeTree() | ||
found = 0 | ||
for section in elffile.iter_sections(): | ||
try: | ||
for nsym, symbol in enumerate(section.iter_symbols()): | ||
ss = symbol['st_value'] | ||
se = ss+symbol['st_size'] | ||
if ss != se: | ||
try: | ||
r[ss:se] = symbol.name | ||
except KeyError: | ||
continue | ||
#print(nsym, symbol.name, symbol['st_value'], symbol['st_size']) | ||
if symbol.name == "runtime.gcenable": | ||
print(nsym, symbol.name) | ||
# nop gcenable | ||
prog_dat[symbol['st_value']:symbol['st_value']+8] = b"\x03\xe0\x00\x08\x00\x00\x00\x00" | ||
found += 1 | ||
except Exception: | ||
#traceback.print_exc() | ||
pass | ||
|
||
#assert(found == 2) | ||
return prog_dat, prog_size, r | ||
|
||
|
||
if __name__ == "__main__": | ||
fn = "minigeth" | ||
if len(sys.argv) > 1: | ||
fn = sys.argv[1] | ||
|
||
prog_dat, prog_size, _ = load_minigeth(fn) | ||
print("compiled %d bytes with md5 %s" % (prog_size, hashlib.md5(prog_dat).hexdigest())) | ||
|
||
with open(fn+".bin", "wb") as f: | ||
f.write(prog_dat) |
Oops, something went wrong.