Compare commits

..

13 Commits

8 changed files with 365 additions and 35 deletions

View File

@ -1,13 +1,14 @@
test: test_asm c_test/encode_decode c_test/creole
CFLAGS=-DCREOLE_TEST
asm/libcreole.so: creole.c creole.h
$(CC) -Wall -fPIC -c creole.c -o c_test/creole.o
$(CC) $(CFLAGS) -Wall -fPIC -c creole.c -o c_test/creole.o
$(CC) -shared -o asm/libcreole.so c_test/creole.o
test_asm: asm/libcreole.so
cd asm && python3 test.py -f
c_test/encode_decode: c_test/encode_decode.c creole.c creole.h
$(CC) creole.c c_test/encode_decode.c -Wall -pedantic -std=c89 -g -fopenmp -o c_test/encode_decode
$(CC) $(CFLAGS) creole.c c_test/encode_decode.c -Wall -pedantic -std=c99 -g -fopenmp -o c_test/encode_decode
# c_test/encode_decode
c_test/creole: c_test/creole.c creole.c creole.h c_test/greatest.h
$(CC) -g c_test/creole.c -Wall -pedantic -std=c89 -o c_test/creole
$(CC) $(CFLAGS) -g c_test/creole.c -Wall -pedantic -std=c99 -o c_test/creole
c_test/creole

View File

@ -1,4 +1,4 @@
Version 0.2.1
Version 0.2.0
Creole is a bytecode designed for microcontrollers. It's C source file
is less than 1000 lines long and does not depend on the C standard

18
asm/comm.py Normal file
View File

@ -0,0 +1,18 @@
from creole import *
import socket
def connect(ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
return s
def execute(creole_instance, socket):
code = creole_instance()
l = len(code)
assert l <= 0xFFFF
socket.sendall(bytes([l & 0xFF, l >> 8]) + code)
def connect_exec(cr, ip="192.168.1.50", port=6626):
s = connect(ip, port)
execute(cr, s)
return s.recv(1024)

View File

@ -75,7 +75,7 @@ class StringArgument(Argument):
def __call__(self):
b = bytes()
for v in self.val:
b = b + Argument(ArgType.IMM, int(v, base=16))()
b = b + Argument(ArgType.IMM, int(v, base=10))()
return b
class LabelArgument(Argument):
@ -210,6 +210,25 @@ class Instruction(Enum):
J = "JE", "_render_j", ArgType.LAB
JNE = 10, "_render_default", ArgType.LAB, ArgType.VAL, ArgType.VAL
DB = 11, "_render_default", ArgType.DAT, ArgType.STR
READ_ADC = 12, "_render_default", ArgType.VAL, ArgType.REG
READ_DAC = 13, "_render_default", ArgType.VAL, ArgType.REG
WRITE_DAC = 14, "_render_default", ArgType.VAL, ArgType.VAL
SLEEP = 15, "_render_default", ArgType.VAL
CLOOP_READ = 16, "_render_default", ArgType.VAL, ArgType.REG, ArgType.REG
CLOOP_WRITE = 17, "_render_default", ArgType.VAL, ArgType.VAL, ArgType.VAL
WF_LOAD = 18, "_render_default", ArgType.VAL, ArgType.DAT
WF_ARM = 19, "_render_default", ArgType.VAL, ArgType.VAL, ArgType.VAL
SENDVAL = 20, "_render_default", ArgType.VAL
SENDDAT = 21, "_render_default", ArgType.DAT
WF_DISARM = 22, "_render_default", ArgType.VAL
TAKE_ADC = 23, "_render_default", ArgType.VAL, ArgType.VAL
RELEASE_ADC = 24, "_render_default", ArgType.VAL
TAKE_DAC = 25, "_render_default", ArgType.VAL, ArgType.VAL
RELEASE_DAC = 26, "_render_default", ArgType.VAL
TAKE_WF = 27, "_render_default", ArgType.VAL, ArgType.VAL
RELEASE_WF = 28, "_render_default", ArgType.VAL
TAKE_CLOOP = 29, "_render_default", ArgType.VAL
RELEASE_CLOOP = 30, "_render_default"
def __int__(self):
""" Returns the opcode associated with the Instruction.
@ -459,7 +478,7 @@ class Program:
assert len(b) < encoding_types[lablen][0]
return b
def __init__(self, reglen=16, datlen=16):
def __init__(self, reglen=32, datlen=64):
self.asm = []
self.reglen = reglen
self.datlen = datlen

View File

@ -60,7 +60,7 @@ class Reader:
class CEnv(Structure):
_fields_ = [
("dats", POINTER(POINTER(c_ubyte))),
("dats", POINTER(CReader)),
("datlen", c_size_t),
("reg", POINTER(c_uint)),
("reglen", c_size_t),
@ -68,7 +68,8 @@ class CEnv(Structure):
("stkptr", c_size_t),
("stklen", c_size_t),
("r_current", CReader),
("r_start", CReader)
("r_start", CReader),
("fd", c_int)
]
class RegisterOverflowError(Exception):
@ -116,13 +117,7 @@ class Environment:
def getdat(self, n):
if n >= self.cenv.datlen or n < 0:
raise DataOverflowError(n)
rdr = CReader()
rdr.p = self.cenv.dats[n]
# Python does not allow for direct pointer arithmetic
rdr_p_v = addressof(rdr.p.contents)
r_start_p_v = addressof(self.cenv.r_start.p)
rdr.left = self.cenv.r_start.left - (rdr_p_v - r_start_p_v)
rdr = self.cenv.dats[n]
l = []
w = CWord()
@ -143,7 +138,7 @@ class Environment:
def __init__(self, prog=None, reglen=32, datlen=32, stklen=4096, prglen=4096):
cenv = CEnv()
cenv.dats = (POINTER(c_ubyte) * datlen)()
cenv.dats = (CReader * datlen)()
cenv.datlen = datlen
cenv.reglen = reglen

25
asm/test_comm.py Normal file
View File

@ -0,0 +1,25 @@
from comm import *
# These require a connection!
def test_print_char():
p = Program()
p.parse_asm_line(f"SENDVAL 100")
assert connect_exec(p) == b'100'
def test_print_string():
p = Program()
p.parse_lines([
"j .l",
f"db d0 {[ord(x) for x in 'hello world']}",
".l",
"senddat d0"
])
return connect_exec(p).decode()
def test_run_adc():
p = Program()
p.parse_lines([
"read_adc 0 r0",
"sendval r0"
])
return connect_exec(p).decode()

259
creole.c
View File

@ -12,8 +12,17 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include "creole.h"
#ifndef CREOLE_TEST
# include <zephyr/kernel.h>
# include <zephyr/logging/log.h>
# include <zephyr/sys_clock.h>
# include "access.h"
# include "control_loop_cmds.h"
# include "sock.h"
LOG_MODULE_REGISTER(creole, 4);
#endif
#include "creole.h"
/*************************************************************************
* Static information
************************************************************************/
@ -37,7 +46,7 @@ enum creole_arg_type {
[i] = v,
* in C89 indicies are implicit from 0 to the maximum filled-in value.
*/
#define defop(s, n, a1, a2, a3) {n, {a1, a2, a3}}
#define defop(s, n, a1, a2, a3) [CREOLE_##s] = {n, {a1, a2, a3}}
static const struct {
int arglen;
enum creole_arg_type argtype[CREOLE_MAX_ARG];
@ -53,7 +62,28 @@ static const struct {
defop(JLE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(JE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(JNE, 3, TYPE_IMM, TYPE_VAL, TYPE_VAL),
defop(DB, 1, TYPE_IMM, TYPE_NONE, TYPE_NONE)
defop(DB, 1, TYPE_IMM, TYPE_NONE, TYPE_NONE),
defop(READ_ADC, 2, TYPE_VAL, TYPE_REG, TYPE_NONE),
defop(READ_DAC, 2, TYPE_VAL, TYPE_REG, TYPE_NONE),
defop(WRITE_DAC, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(SLEEP, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(CLOOP_READ, 2, TYPE_VAL, TYPE_REG, TYPE_NONE),
defop(CLOOP_WRITE, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(WF_LOAD, 2, TYPE_VAL, TYPE_IMM, TYPE_NONE),
defop(WF_ARM, 3, TYPE_VAL, TYPE_VAL, TYPE_VAL),
defop(SENDVAL, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(SENDDAT, 1, TYPE_IMM, TYPE_NONE, TYPE_NONE),
defop(WF_DISARM, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(TAKE_ADC, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(RELEASE_ADC, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(TAKE_DAC, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(RELEASE_DAC, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(TAKE_WF, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(RELEASE_WF, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(TAKE_CLOOP, 1, TYPE_VAL, TYPE_NONE, TYPE_NONE),
defop(RELEASE_CLOOP, 0, TYPE_NONE, TYPE_NONE, TYPE_NONE),
defop(SWITCH_ADC, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE),
defop(SWITCH_DAC, 2, TYPE_VAL, TYPE_VAL, TYPE_NONE)
};
/*************************************************************************
@ -327,6 +357,7 @@ int creole_encode(creole_word i, unsigned encode_to, unsigned high_bits,
struct ins {
unsigned char *start;
unsigned char *datapt;
size_t dataptlen;
enum creole_opcode opcode;
creole_word w[CREOLE_MAX_ARG];
creole_word w_flags[CREOLE_MAX_ARG];
@ -393,10 +424,12 @@ parse_line(struct creole_env *env, struct ins *ins, struct creole_reader *r)
if (ins->opcode == CREOLE_DB) {
ins->datapt = r->p;
ins->dataptlen = 0;
do {
if (!creole_decode(r, &w))
return CREOLE_ARG_READ_ERROR;
} while (w.len != 1);
ins->dataptlen = r->p - ins->datapt - 1;
if (w.word != 0)
return CREOLE_LAST_READ_ERROR;
return CREOLE_COMPILE_OK;
@ -419,7 +452,8 @@ add_to_env(struct creole_env *env, struct ins *ins)
{
switch (ins->opcode) {
case CREOLE_DB:
env->dats[ins->w[0]] = ins->datapt;
env->dats[ins->w[0]].p = ins->datapt;
env->dats[ins->w[0]].left = ins->dataptlen;
break;
default:
;
@ -526,6 +560,67 @@ int creole_jump(struct creole_env *env, creole_word off)
return 1;
}
#ifndef CREOLE_TEST
static size_t
load_into_array(const struct creole_reader *start, creole_word *buf, size_t buflen)
{
size_t i = 0;
struct creole_word w;
struct creole_reader r = *start;
while (creole_decode(&r, &w) && i < buflen) {
buf[i++] = w.word;
}
return i;
}
static creole_word
upsilon_load_waveform(struct creole_env *env, creole_word slot,
creole_word db)
{
creole_word buf[MAX_WL_SIZE];
size_t len = load_into_array(env->dats + db, buf, ARRAY_SIZE(buf));
if (len < MAX_WL_SIZE)
return 0;
return waveform_load(buf, slot, K_FOREVER);
}
static creole_word
upsilon_sendval(struct creole_env *env, creole_word num)
{
char buf[32];
struct bufptr bp = {buf, sizeof(buf)};
return sock_printf(env->fd, &bp, "%u", num) == BUF_OK;
}
static creole_word
upsilon_senddat(struct creole_env *env, creole_word db)
{
#define SENDDAT_BUFLEN 1024
char buf[SENDDAT_BUFLEN];
struct bufptr bp = {buf, 0};
struct creole_word w;
struct creole_reader r = env->dats[db];
while (creole_decode(&r, &w) && bp.left < SENDDAT_BUFLEN) {
if (w.word > 0xFF)
return -EINVAL;
buf[bp.left++] = w.word;
}
if (r.left != 0) {
return -E2BIG;
}
return sock_write_buf(env->fd, &bp);
}
#endif /* CREOLE_TEST */
/* Upsilon interface */
#define chk_sign_op(OPER) do { \
switch (check_sign_bits(ins.w_flags[1], ins.w_flags[2])) { \
case ALL_UNSIGNED: \
@ -559,26 +654,32 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
switch (ins.opcode) {
case CREOLE_DB:
env->dats[ins.w[0]] = ins.datapt;
env->dats[ins.w[0]].p = ins.datapt;
env->dats[ins.w[0]].left = ins.dataptlen;
break;
case CREOLE_PUSH:
check(read_val(env, &ins, 0, &a1));
check(creole_push(env, a1));
break;
case CREOLE_POP:
check(creole_pop(env, &a1));
check(creole_reg_write(env, ins.w[0], a1));
break;
case CREOLE_ADD:
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
check(creole_reg_write(env, ins.w[0], a1 + a2));
break;
case CREOLE_MUL:
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
check(creole_reg_write(env, ins.w[0], a1 * a2));
break;
case CREOLE_DIV:
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
@ -588,10 +689,12 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
chk_sign_op(/);
check(creole_reg_write(env, ins.w[0], a1));
break;
case CREOLE_SYS:
check(read_val(env, &ins, 0, sc));
rcode = CREOLE_STEP_SYSCALL;
break;
case CREOLE_JL:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
@ -600,6 +703,7 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
if (a1 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JLE:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
@ -608,6 +712,7 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
if (a1 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JE:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
@ -615,6 +720,7 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
if (a1 == a2 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
case CREOLE_JNE:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
@ -622,8 +728,151 @@ enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
if (a1 != a2 && !creole_jump(env, a0))
return CREOLE_JUMP_OVERFLOW;
break;
#ifndef CREOLE_TEST
case CREOLE_READ_ADC:
check(read_val(env, &ins, 0, &a0));
a1 = adc_read(a0, K_FOREVER, &a2);
check(creole_reg_write(env, ins.w[1], a2));
check(creole_push(env, a1));
break;
case CREOLE_READ_DAC:
check(read_val(env, &ins, 0, &a0));
a1 = dac_read_write(a0, 0x1 << 23 | 0x1 << 20, K_FOREVER, NULL);
if (a1 == 0) {
a1 = dac_read_write(a0, 0, K_FOREVER, &a2);
check(creole_reg_write(env, ins.w[1], a2));
}
check(creole_push(env, a1));
break;
case CREOLE_WRITE_DAC:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
a2 = dac_read_write(a0, 0x1 << 20 | a1, K_FOREVER, NULL);
check(creole_push(env, a2));
break;
case CREOLE_SLEEP:
check(read_val(env, &ins, 0, &a0));
k_sleep(K_USEC(a0));
check(creole_push(env, 0));
break;
case CREOLE_CLOOP_READ:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 0, &a1));
check(read_val(env, &ins, 0, &a2));
if (valid_register(env, a1) && valid_register(env, a2)) {
a0 = cloop_read(a0, env->reg + a1, env->reg + a2, K_FOREVER);
check(creole_push(env, a0));
} else {
check(creole_push(env, -EINVAL));
}
break;
case CREOLE_CLOOP_WRITE:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 0, &a1));
check(read_val(env, &ins, 0, &a2));
a0 = cloop_write(a0, a1, a2, K_FOREVER);
check(creole_push(env, a0));
break;
case CREOLE_WF_LOAD:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(creole_push(env, upsilon_load_waveform(env, a0, a1)));
break;
case CREOLE_WF_ARM:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(read_val(env, &ins, 2, &a2));
check(creole_push(env, waveform_arm(a0, a1, a2, K_FOREVER)));
break;
case CREOLE_WF_DISARM:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, waveform_disarm(a0)));
break;
case CREOLE_SENDVAL:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, upsilon_sendval(env, a0)));
break;
case CREOLE_SENDDAT:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, upsilon_senddat(env, a0)));
break;
case CREOLE_TAKE_ADC:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 0, &a1));
check(creole_push(env, adc_take(a0, K_USEC(a1))));
break;
case CREOLE_RELEASE_ADC:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, adc_release(a0)));
break;
case CREOLE_TAKE_DAC:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 0, &a1));
check(creole_push(env, dac_take(a0, K_USEC(a1))));
break;
case CREOLE_RELEASE_DAC:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, dac_release(a0)));
break;
case CREOLE_TAKE_WF:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 0, &a1));
check(creole_push(env, waveform_take(a0, K_USEC(a1))));
break;
case CREOLE_RELEASE_WF:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, waveform_release(a0)));
break;
case CREOLE_TAKE_CLOOP:
check(read_val(env, &ins, 0, &a0));
check(creole_push(env, cloop_take(K_USEC(a0))));
break;
case CREOLE_RELEASE_CLOOP:
check(creole_push(env, cloop_release()));
break;
case CREOLE_SWITCH_ADC:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(creole_push(env, adc_switch(a0, a1, K_FOREVER)));
break;
case CREOLE_SWITCH_DAC:
check(read_val(env, &ins, 0, &a0));
check(read_val(env, &ins, 1, &a1));
check(creole_push(env, dac_switch(a0, a1, K_FOREVER)));
break;
#endif /* CREOLE_TEST */
default:
rcode = CREOLE_STEP_UNKNOWN_OPCODE;
break;
}
return rcode;

View File

@ -19,7 +19,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#define CREOLE_MAJOR 0
#define CREOLE_MINOR 2
#define CREOLE_BUGFIX 1
#define CREOLE_BUGFIX 0
#ifndef CREOLE_WORD
# define CREOLE_WORD unsigned int
@ -34,18 +34,39 @@ typedef CREOLE_WORD creole_word;
typedef CREOLE_SIGNED_WORD creole_signed;
enum creole_opcode {
CREOLE_NOOP,
CREOLE_PUSH,
CREOLE_POP,
CREOLE_ADD,
CREOLE_MUL,
CREOLE_DIV,
CREOLE_SYS,
CREOLE_JL,
CREOLE_JLE,
CREOLE_JE,
CREOLE_JNE,
CREOLE_DB,
CREOLE_NOOP = 0,
CREOLE_PUSH = 1,
CREOLE_POP = 2,
CREOLE_ADD = 3,
CREOLE_MUL = 4,
CREOLE_DIV = 5,
CREOLE_SYS = 6,
CREOLE_JL = 7,
CREOLE_JLE = 8,
CREOLE_JE = 9,
CREOLE_JNE = 10,
CREOLE_DB = 11,
CREOLE_READ_ADC = 12,
CREOLE_READ_DAC = 13,
CREOLE_WRITE_DAC = 14,
CREOLE_SLEEP = 15,
CREOLE_CLOOP_READ = 16,
CREOLE_CLOOP_WRITE = 17,
CREOLE_WF_LOAD = 18,
CREOLE_WF_ARM = 19,
CREOLE_SENDVAL = 20,
CREOLE_SENDDAT = 21,
CREOLE_WF_DISARM = 22,
CREOLE_TAKE_ADC = 23,
CREOLE_RELEASE_ADC = 24,
CREOLE_TAKE_DAC = 25,
CREOLE_RELEASE_DAC = 26,
CREOLE_TAKE_WF = 27,
CREOLE_RELEASE_WF = 28,
CREOLE_TAKE_CLOOP = 29,
CREOLE_RELEASE_CLOOP = 30,
CREOLE_SWITCH_ADC = 31,
CREOLE_SWITCH_DAC = 32,
CREOLE_OPCODE_LEN
};
@ -96,7 +117,7 @@ struct creole_reader {
};
struct creole_env {
unsigned char **dats;
struct creole_reader *dats;
size_t datlen;
creole_word *reg;
@ -107,6 +128,8 @@ struct creole_env {
struct creole_reader r_current;
struct creole_reader r_start;
int fd;
};
int creole_decode(struct creole_reader *r, struct creole_word *w);