aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2026-02-24 01:33:45 +0100
committerPaul Oliver <contact@pauloliver.dev>2026-02-24 01:33:45 +0100
commit9f7e70904e6c0fa650323ac5e50ebf6003da333c (patch)
tree3015be498d36e8d5c960cf55667c6c825f7de493 /arch
parent0fb1497a62332e0db45f94b4f195cb37183678cb (diff)
Removes usage of Jinja templates
Use CPP to pre-process C files instead
Diffstat (limited to 'arch')
-rw-r--r--arch/dummy/arch.c (renamed from arch/dummy/arch.j2.c)57
-rw-r--r--arch/dummy/arch_vars.py34
-rw-r--r--arch/v1/arch.c (renamed from arch/salis-v1/arch.j2.c)477
-rw-r--r--arch/v1/arch_vars.py (renamed from arch/salis-v1/arch_vars.py)107
4 files changed, 325 insertions, 350 deletions
diff --git a/arch/dummy/arch.j2.c b/arch/dummy/arch.c
index f51494f..cf30ba1 100644
--- a/arch/dummy/arch.j2.c
+++ b/arch/dummy/arch.c
@@ -1,32 +1,25 @@
-// Author: Paul Oliver <contact@pauloliver.dev>
-// Project: salis-v3
-
-// Defines a minimal viable architecture for the Salis VM.
-// Useful for debugging and benchmarking. May be used as a template when
-// implementing a new architecture.
-
-{% if args.command in ["bench", "new"] and anc_bytes is defined %}
+#if (defined(COMMAND_BENCH) || defined(COMMAND_NEW)) && defined(ANC_BYTES)
void arch_core_init(struct Core *core) {
assert(core);
- {% if arch_vars.mvec_loop %}
- uint64_t addr = {{ uint64_half }};
- {% else %}
+#if defined(MVEC_LOOP)
+ uint64_t addr = UINT64_HALF;
+#else
uint64_t addr = 0;
- {% endif %}
+#endif
- for (uint64_t i = 0; i < {{ args.clones }}; ++i) {
- uint64_t addr_clone = addr + (({{ mvec_size }} / {{ args.clones }})) * i;
+ for (uint64_t i = 0; i < CLONES; ++i) {
+ uint64_t addr_clone = addr + (MVEC_SIZE / CLONES) * i;
struct Proc *panc = proc_fetch(core, i);
panc->mb0a = addr_clone;
- panc->mb0s = {{ anc_bytes|length }};
- panc->ip = addr_clone;
- panc->sp = addr_clone;
+ panc->mb0s = ANC_SIZE;
+ panc->ip = addr_clone;
+ panc->sp = addr_clone;
}
}
-{% endif %}
+#endif
void arch_core_free(struct Core *core) {
assert(core);
@@ -34,7 +27,7 @@ void arch_core_free(struct Core *core) {
(void)core;
}
-{% if args.command in ["load", "new"] %}
+#if defined(COMMAND_LOAD) || defined(COMMAND_NEW)
void arch_core_save(FILE *f, const struct Core *core) {
assert(f);
assert(core);
@@ -42,9 +35,9 @@ void arch_core_save(FILE *f, const struct Core *core) {
(void)f;
(void)core;
}
-{% endif %}
+#endif
-{% if args.command in ["load"] %}
+#if defined(COMMAND_LOAD)
void arch_core_load(FILE *f, struct Core *core) {
assert(f);
assert(core);
@@ -52,7 +45,7 @@ void arch_core_load(FILE *f, struct Core *core) {
(void)f;
(void)core;
}
-{% endif %}
+#endif
uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix) {
assert(core);
@@ -117,7 +110,7 @@ void arch_proc_step(struct Core *core, uint64_t pix) {
return;
}
-{% if not args.optimized %}
+#if !defined(NDEBUG)
void arch_validate_proc(const struct Core *core, uint64_t pix) {
assert(core);
assert(mvec_proc_is_live(core, pix));
@@ -127,25 +120,25 @@ void arch_validate_proc(const struct Core *core, uint64_t pix) {
assert(true);
}
-{% endif %}
+#endif
wchar_t arch_symbol(uint8_t inst) {
switch (inst) {
- {% for i in arch_vars.inst_set %}
- case {{ loop.index0 }}: return L'{{ i[1] }}';
- {% endfor %}
+#define INST(index, label, mnemonic, symbol) case index: return symbol;
+ INST_SET
+#undef INST
}
}
const char *arch_mnemonic(uint8_t inst) {
switch (inst) {
- {% for i in arch_vars.inst_set %}
- case {{ loop.index0 }}: return "{{ i[0]|join(' ') }}";
- {% endfor %}
+#define INST(index, label, mnemonic, symbol) case index: return mnemonic;
+ INST_SET
+#undef INST
}
}
-{% if data_push_path is defined %}
+#if defined(DATA_PUSH_PATH)
void arch_push_data_header() {
assert(g_sim_data);
}
@@ -153,4 +146,4 @@ void arch_push_data_header() {
void arch_push_data_line() {
assert(g_sim_data);
}
-{% endif %}
+#endif
diff --git a/arch/dummy/arch_vars.py b/arch/dummy/arch_vars.py
index bc97ea9..0266e53 100644
--- a/arch/dummy/arch_vars.py
+++ b/arch/dummy/arch_vars.py
@@ -1,18 +1,6 @@
-def gen_arch_vars(_):
- return {
- "core_fields": [],
- "mvec_loop": True,
-
- "proc_fields": [
- ("uint64_t", "ip"),
- ("uint64_t", "sp"),
- ("uint64_t", "mb0a"),
- ("uint64_t", "mb0s"),
- ("uint64_t", "mb1a"),
- ("uint64_t", "mb1s"),
- ],
-
- "inst_set": [
+class ArchVars:
+ def __init__(self, _):
+ self.inst_set = [
(["dummy", f"{i:02x}"], symbol)
for i, symbol in enumerate(
"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟"
@@ -20,7 +8,17 @@ def gen_arch_vars(_):
"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟"
"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿"
)
- ],
+ ]
- "inst_count": 2 ** 7,
- }
+ self.core_fields = []
+ self.data_is_compressed = False
+ self.mvec_loop = True
+
+ self.proc_fields = [
+ ("uint64_t", "ip"),
+ ("uint64_t", "sp"),
+ ("uint64_t", "mb0a"),
+ ("uint64_t", "mb0s"),
+ ("uint64_t", "mb1a"),
+ ("uint64_t", "mb1s"),
+ ]
diff --git a/arch/salis-v1/arch.j2.c b/arch/v1/arch.c
index 06a701d..39cfe15 100644
--- a/arch/salis-v1/arch.j2.c
+++ b/arch/v1/arch.c
@@ -1,50 +1,47 @@
-// Author: Paul Oliver <contact@pauloliver.dev>
-// Project: Salis
-
// Based on the original salis-v1 VM architecture:
// https://git.pauloliver.dev/salis-v1/about/
enum {
- {% for i in arch_vars.inst_set %}
- {{ i[0]|join(' ') }},
- {% endfor %}
+#define INST(index, label, mnemonic, symbol) label,
+ INST_SET
+#undef INST
};
-{% if args.command in ["bench", "new"] and anc_bytes is defined %}
+#if (defined(COMMAND_BENCH) || defined(COMMAND_NEW)) && defined(ANC_BYTES)
void arch_core_init(struct Core *core) {
assert(core);
- {% if arch_vars.mvec_loop %}
- uint64_t addr = {{ uint64_half }};
- {% else %}
+#if defined(MVEC_LOOP)
+ uint64_t addr = UINT64_HALF;
+#else
uint64_t addr = 0;
- {% endif %}
+#endif
- for (uint64_t i = 0; i < {{ args.clones }}; ++i) {
- uint64_t addr_clone = addr + (({{ mvec_size }} / {{ args.clones }})) * i;
+ for (uint64_t i = 0; i < CLONES; ++i) {
+ uint64_t addr_clone = addr + (MVEC_SIZE / CLONES) * i;
struct Proc *panc = proc_fetch(core, i);
panc->mb0a = addr_clone;
- panc->mb0s = {{ anc_bytes|length }};
- panc->ip = addr_clone;
- panc->sp = addr_clone;
+ panc->mb0s = ANC_SIZE;
+ panc->ip = addr_clone;
+ panc->sp = addr_clone;
}
}
-{% endif %}
+#endif
void arch_core_free(struct Core *core) {
assert(core);
(void)core;
}
-{% if args.command in ["load", "new"] %}
+#if defined(COMMAND_LOAD) || defined(COMMAND_NEW)
void arch_core_save(FILE *f, const struct Core *core) {
assert(f);
assert(core);
- fwrite( core->iexe, sizeof(uint64_t), {{ arch_vars.inst_count }}, f);
- fwrite( core->iwrt, sizeof(uint64_t), {{ arch_vars.inst_count }}, f);
+ fwrite(core->iexe, sizeof(uint64_t), INST_COUNT, f);
+ fwrite(core->iwrt, sizeof(uint64_t), INST_COUNT, f);
fwrite(&core->emb0, sizeof(uint64_t), 1, f);
fwrite(&core->emb1, sizeof(uint64_t), 1, f);
fwrite(&core->eliv, sizeof(uint64_t), 1, f);
@@ -52,17 +49,17 @@ void arch_core_save(FILE *f, const struct Core *core) {
fwrite(&core->wmb0, sizeof(uint64_t), 1, f);
fwrite(&core->wmb1, sizeof(uint64_t), 1, f);
fwrite(&core->wdea, sizeof(uint64_t), 1, f);
- fwrite( core->aeva, sizeof(uint64_t), {{ mvec_size }}, f);
+ fwrite(core->aeva, sizeof(uint64_t), MVEC_SIZE, f);
}
-{% endif %}
+#endif
-{% if args.command in ["load"] %}
+#if defined(COMMAND_LOAD)
void arch_core_load(FILE *f, struct Core *core) {
assert(f);
assert(core);
- fread( core->iexe, sizeof(uint64_t), {{ arch_vars.inst_count }}, f);
- fread( core->iwrt, sizeof(uint64_t), {{ arch_vars.inst_count }}, f);
+ fread(core->iexe, sizeof(uint64_t), INST_COUNT, f);
+ fread(core->iwrt, sizeof(uint64_t), INST_COUNT, f);
fread(&core->emb0, sizeof(uint64_t), 1, f);
fread(&core->emb1, sizeof(uint64_t), 1, f);
fread(&core->eliv, sizeof(uint64_t), 1, f);
@@ -70,9 +67,9 @@ void arch_core_load(FILE *f, struct Core *core) {
fread(&core->wmb0, sizeof(uint64_t), 1, f);
fread(&core->wmb1, sizeof(uint64_t), 1, f);
fread(&core->wdea, sizeof(uint64_t), 1, f);
- fread( core->aeva, sizeof(uint64_t), {{ mvec_size }}, f);
+ fread(core->aeva, sizeof(uint64_t), MVEC_SIZE, f);
}
-{% endif %}
+#endif
uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix) {
assert(core);
@@ -150,7 +147,7 @@ void arch_on_proc_kill(struct Core *core) {
uint8_t _get_inst(const struct Core *core, uint64_t addr) {
assert(core);
- return mvec_get_inst(core, addr) % {{ arch_vars.inst_count }};
+ return mvec_get_inst(core, addr) % INST_COUNT;
}
void _increment_ip(struct Core *core, uint64_t pix) {
@@ -164,35 +161,35 @@ void _increment_ip(struct Core *core, uint64_t pix) {
}
bool _is_between(uint8_t inst, uint8_t lo, uint8_t hi) {
- assert(inst < {{ arch_vars.inst_count }});
- assert(lo < {{ arch_vars.inst_count }});
- assert(hi < {{ arch_vars.inst_count }});
+ assert(inst < INST_COUNT);
+ assert(lo < INST_COUNT);
+ assert(hi < INST_COUNT);
assert(lo < hi);
return (inst >= lo) && (inst <= hi);
}
bool _is_key(uint8_t inst) {
- assert(inst < {{ arch_vars.inst_count }});
+ assert(inst < INST_COUNT);
return _is_between(inst, keya, keyp);
}
bool _is_lock(uint8_t inst) {
- assert(inst < {{ arch_vars.inst_count }});
+ assert(inst < INST_COUNT);
return _is_between(inst, loka, lokp);
}
bool _is_rmod(uint8_t inst) {
- assert(inst < {{ arch_vars.inst_count }});
+ assert(inst < INST_COUNT);
return _is_between(inst, nop0, nop3);
}
bool _key_lock_match(uint8_t key, uint8_t lock) {
- assert(key < {{ arch_vars.inst_count }});
- assert(lock < {{ arch_vars.inst_count }});
+ assert(key < INST_COUNT);
+ assert(lock < INST_COUNT);
assert(_is_key(key));
return (key - keya) == (lock - loka);
@@ -203,7 +200,7 @@ bool _seek(struct Core *core, uint64_t pix, bool fwrd) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint8_t next = _get_inst(core, proc->ip + 1);
+ uint8_t next = _get_inst(core, proc->ip + 1);
if (!_is_key(next)) {
_increment_ip(core, pix);
@@ -231,13 +228,13 @@ void _jump(struct Core *core, uint64_t pix) {
struct Proc *proc = proc_fetch(core, pix);
- {% if not args.optimized %}
+#if !defined(NDEBUG)
uint8_t next = _get_inst(core, proc->ip + 1);
uint8_t spin = _get_inst(core, proc->sp);
assert(_is_key(next));
assert(_is_lock(spin));
assert(_key_lock_match(next, spin));
- {% endif %}
+#endif
proc->ip = proc->sp;
}
@@ -251,7 +248,7 @@ void _get_reg_addr_list(struct Core *core, uint64_t pix, uint64_t **rlist, int r
assert(rcount < 4);
struct Proc *proc = proc_fetch(core, pix);
- uint64_t madr = proc->ip + (offset ? 2 : 1);
+ uint64_t madr = proc->ip + (offset ? 2 : 1);
for (int i = 0; i < rcount; ++i) {
rlist[i] = &proc->r0x;
@@ -259,7 +256,7 @@ void _get_reg_addr_list(struct Core *core, uint64_t pix, uint64_t **rlist, int r
for (int i = 0; i < rcount; ++i) {
uint64_t mnxt = madr + i;
- uint8_t mins = _get_inst(core, mnxt);
+ uint8_t mins = _get_inst(core, mnxt);
if (!_is_rmod(mins)) {
break;
@@ -287,15 +284,15 @@ void _addr(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *reg;
+ uint64_t *reg;
- {% if not args.optimized %}
+#if !defined(NDEBUG)
uint8_t next = _get_inst(core, proc->ip + 1);
uint8_t spin = _get_inst(core, proc->sp);
assert(_is_key(next));
assert(_is_lock(spin));
assert(_key_lock_match(next, spin));
- {% endif %}
+#endif
_get_reg_addr_list(core, pix, &reg, 1, true);
*reg = proc->sp;
@@ -308,7 +305,7 @@ void _ifnz(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *reg;
+ uint64_t *reg;
_get_reg_addr_list(core, pix, &reg, 1, false);
@@ -333,15 +330,12 @@ void _free_child_memory_of(struct Core *core, uint64_t pix) {
proc->mb1s = 0;
}
-// Organisms allocate new memory blocks by means of their seek pointer (sp),
-// which sweeps memory 1 byte per simulation step, extending the block as it goes.
-// In case allocated memory is found mid-way, current allocation is discarded.
void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
assert(core);
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *regs[2];
+ uint64_t *regs[2];
_get_reg_addr_list(core, pix, regs, 2, false);
@@ -354,7 +348,6 @@ void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
}
// Do nothing if seek pointer is not adjacent to allocated memory block
- // This is an error condition
if (proc->mb1s) {
uint64_t exp_addr = proc->mb1a;
@@ -370,16 +363,14 @@ void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
}
}
- // Allocation was successful
- // Store block address on register
+ // Allocation was successful, store block address on register
if (proc->mb1s == bsize) {
_increment_ip(core, pix);
*regs[1] = proc->mb1a;
return;
}
- // Seek pointer collided with another allocated block
- // Discard and keep trying
+ // Seek pointer collided with another allocated block, discard and keep looking
if (mvec_is_alloc(core, proc->sp)) {
if (proc->mb1s) {
_free_child_memory_of(core, pix);
@@ -394,8 +385,7 @@ void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
return;
}
- // Free (non-allocated) byte found
- // Enlarge child block 1 byte
+ // Free (non-allocated) byte found, enlarge child block 1 byte
mvec_alloc(core, proc->sp);
// Record allocation event
@@ -407,7 +397,7 @@ void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
proc->mb1s++;
- // Move seek pointer
+ // Advance seek pointer
if (fwrd) {
proc->sp++;
} else {
@@ -456,8 +446,8 @@ void _split(struct Core *core, uint64_t pix) {
if (proc->mb1s) {
struct Proc child = {0};
- child.ip = proc->mb1a;
- child.sp = proc->mb1a;
+ child.ip = proc->mb1a;
+ child.sp = proc->mb1a;
child.mb0a = proc->mb1a;
child.mb0s = proc->mb1s;
@@ -549,7 +539,7 @@ void _push(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *reg;
+ uint64_t *reg;
_get_reg_addr_list(core, pix, &reg, 1, false);
@@ -570,11 +560,11 @@ void _pop(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *reg;
+ uint64_t *reg;
_get_reg_addr_list(core, pix, &reg, 1, false);
- *reg = proc->s0;
+ *reg = proc->s0;
proc->s0 = proc->s1;
proc->s1 = proc->s2;
proc->s2 = proc->s3;
@@ -602,7 +592,7 @@ void _load(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *regs[2];
+ uint64_t *regs[2];
_get_reg_addr_list(core, pix, regs, 2, false);
@@ -630,7 +620,7 @@ void _write(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint64_t *regs[2];
+ uint64_t *regs[2];
_get_reg_addr_list(core, pix, regs, 2, false);
@@ -643,7 +633,7 @@ void _write(struct Core *core, uint64_t pix) {
} else {
if (_is_writeable_by(core, *regs[0], pix)) {
// Store write event
- uint8_t inst = *regs[1] % {{ arch_vars.inst_count }};
+ uint8_t inst = *regs[1] % INST_COUNT;
++core->iwrt[inst];
@@ -656,7 +646,7 @@ void _write(struct Core *core, uint64_t pix) {
}
// Write instruction
- mvec_set_inst(core, *regs[0], *regs[1] % {{ inst_cap }});
+ mvec_set_inst(core, *regs[0], *regs[1] % INST_CAP);
}
_increment_ip(core, pix);
@@ -678,8 +668,8 @@ void _2rop(struct Core *core, uint64_t pix, uint8_t inst) {
case swap:
{
uint64_t tmp = *regs[0];
- *regs[0] = *regs[1];
- *regs[1] = tmp;
+ *regs[0] = *regs[1];
+ *regs[1] = tmp;
}
break;
@@ -695,7 +685,7 @@ void arch_proc_step(struct Core *core, uint64_t pix) {
assert(mvec_proc_is_live(core, pix));
struct Proc *proc = proc_fetch(core, pix);
- uint8_t inst = _get_inst(core, proc->ip);
+ uint8_t inst = _get_inst(core, proc->ip);
// Store execute event
++core->iexe[inst];
@@ -793,7 +783,7 @@ void arch_proc_step(struct Core *core, uint64_t pix) {
return;
}
-{% if not args.optimized %}
+#if !defined(NDEBUG)
void arch_validate_proc(const struct Core *core, uint64_t pix) {
assert(core);
@@ -817,13 +807,13 @@ void arch_validate_proc(const struct Core *core, uint64_t pix) {
assert(mvec_is_proc_owner(core, addr, pix));
}
}
-{% endif %}
+#endif
wchar_t arch_symbol(uint8_t inst) {
- switch (inst % {{ arch_vars.inst_count }}) {
- {% for i in arch_vars.inst_set %}
- case {{ i[0]|join(' ') }}: return L'{{ i[1] }}';
- {% endfor %}
+ switch (inst % INST_COUNT) {
+#define INST(index, label, mnemonic, symbol) case index: return symbol;
+ INST_SET
+#undef INST
}
assert(false);
@@ -831,10 +821,10 @@ wchar_t arch_symbol(uint8_t inst) {
}
const char *arch_mnemonic(uint8_t inst) {
- switch (inst % {{ arch_vars.inst_count }}) {
- {% for i in arch_vars.inst_set %}
- case {{ i[0]|join(' ') }}: return "{{ i[0]|join(' ') }}";
- {% endfor %}
+ switch (inst % INST_COUNT) {
+#define INST(index, label, mnemonic, symbol) case index: return mnemonic;
+ INST_SET
+#undef INST
}
assert(false);
@@ -844,72 +834,74 @@ const char *arch_mnemonic(uint8_t inst) {
// ----------------------------------------------------------------------------
// Data aggregation functions
// ----------------------------------------------------------------------------
-{% if data_push_path is defined %}
+#if defined(DATA_PUSH_PATH)
void arch_push_data_header() {
assert(g_sim_data);
- // Creates general trend table for all cores
g_info("Creating 'trend' table in SQLite database");
salis_exec_sql(
0, NULL, NULL,
"create table trend ("
- "step int not null, "
- {% for i in range(args.cores) %}
- "cycl_{{ i }} int not null, "
- "mall_{{ i }} int not null, "
- "pnum_{{ i }} int not null, "
- "pfst_{{ i }} int not null, "
- "plst_{{ i }} int not null, "
- "amb0_{{ i }} real not null, "
- "amb1_{{ i }} real not null, "
- "emb0_{{ i }} int not null, "
- "emb1_{{ i }} int not null, "
- "eliv_{{ i }} int not null, "
- "edea_{{ i }} int not null, "
- "wmb0_{{ i }} int not null, "
- "wmb1_{{ i }} int not null, "
- "wdea_{{ i }} int not null{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) \
+ "cycl_" #i " int not null, " \
+ "mall_" #i " int not null, " \
+ "pnum_" #i " int not null, " \
+ "pfst_" #i " int not null, " \
+ "plst_" #i " int not null, " \
+ "amb0_" #i " real not null, " \
+ "amb1_" #i " real not null, " \
+ "emb0_" #i " int not null, " \
+ "emb1_" #i " int not null, " \
+ "eliv_" #i " int not null, " \
+ "edea_" #i " int not null, " \
+ "wmb0_" #i " int not null, " \
+ "wmb1_" #i " int not null, " \
+ "wdea_" #i " int not null, "
+ FOR_CORES
+#undef FOR_CORE
+ "step int not null"
");"
);
- // Creates core-specific instruction data tables
- char *iprefs[] = { "pop", "exe", "wrt" };
- int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]);
+ // Instruction events
+ char *iprefs[] = { "pop", "exe", "wrt" };
+ int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]);
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ for (int i = 0; i < CORES; ++i) {
for (int j = 0; j < iprefs_cnt; ++j) {
g_info("Creating '%s_%d' table in SQLite database", iprefs[j], i);
salis_exec_sql(
0, NULL, NULL,
"create table %s_%d ("
- "step int not null, "
- // Cycle data for all cores allows
- // normalizing against each core's cycle speed
- {% for i in range(args.cores) %}
- "cycl_{{ i }} int not null, "
- {% endfor %}
- {% for i in arch_vars.inst_set %}
- "{{ i[0][0] }} int not null{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) "cycl_" #i " int not null, "
+ FOR_CORES
+#undef FOR_CORE
+#define INST(index, label, mnemonic, symbol) #label " int not null, "
+ INST_SET
+#undef INST
+ "step int not null"
");",
iprefs[j], i
);
}
}
- // Creates core-specific memory event tables
- char *eprefs[] = { "aev" };
- int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]);
+ // Memory events
+ char *eprefs[] = { "aev" };
+ int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]);
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ for (int i = 0; i < CORES; ++i) {
for (int j = 0; j < eprefs_cnt; ++j) {
g_info("Creating '%s_%d' table in SQLite database", eprefs[j], i);
salis_exec_sql(
0, NULL, NULL,
"create table %s_%d ("
- "step int not null, "
- "evts blob not null"
+#define FOR_CORE(i) "cycl_" #i " int not null, "
+ FOR_CORES
+#undef FOR_CORE
+ "size int not null, "
+ "evts blob not null ,"
+ "step int not null"
");",
eprefs[j], i
);
@@ -920,13 +912,14 @@ void arch_push_data_header() {
void arch_push_data_line() {
assert(g_sim_data);
- // Measure current instruction population and average memory block sizes
- uint64_t ipop[{{ args.cores }}][{{ arch_vars.inst_count }}] = { 0 };
+ // Instruction population
+ uint64_t ipop[CORES][INST_COUNT] = { 0 };
- double amb0[{{ args.cores }}] = { 0 };
- double amb1[{{ args.cores }}] = { 0 };
+ // Average memory block sizes
+ double amb0[CORES] = { 0 };
+ double amb1[CORES] = { 0 };
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ for (int i = 0; i < CORES; ++i) {
struct Core *core = &g_cores[i];
for (uint64_t j = core->pfst; j <= core->plst; ++j) {
@@ -939,79 +932,77 @@ void arch_push_data_line() {
amb0[i] /= core->pnum;
amb1[i] /= core->pnum;
- for (uint64_t j = 0; j < {{ mvec_size }}; ++j) {
+ for (uint64_t j = 0; j < MVEC_SIZE; ++j) {
++ipop[i][_get_inst(core, j)];
}
- {% if not args.optimized %}
- // Make sure the counting was done right
+#if !defined(NDEBUG)
uint64_t pop_tot = 0;
- for (int j = 0; j < {{ arch_vars.inst_count }}; ++j) {
+ for (int j = 0; j < INST_COUNT; ++j) {
pop_tot += ipop[i][j];
}
- assert(pop_tot == {{ mvec_size }});
- {% endif %}
+ assert(pop_tot == MVEC_SIZE);
+#endif
}
- // Insert row into trend table
g_info("Pushing row to 'trend' table in SQLite database");
salis_exec_sql(
0, NULL, NULL,
"insert into trend ("
- "step, "
- {% for i in range(args.cores) %}
- "cycl_{{ i }}, "
- "mall_{{ i }}, "
- "pnum_{{ i }}, "
- "pfst_{{ i }}, "
- "plst_{{ i }}, "
- "amb0_{{ i }}, "
- "amb1_{{ i }}, "
- "emb0_{{ i }}, "
- "emb1_{{ i }}, "
- "eliv_{{ i }}, "
- "edea_{{ i }}, "
- "wmb0_{{ i }}, "
- "wmb1_{{ i }}, "
- "wdea_{{ i }}{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) \
+ "cycl_" #i ", " \
+ "mall_" #i ", " \
+ "pnum_" #i ", " \
+ "pfst_" #i ", " \
+ "plst_" #i ", " \
+ "amb0_" #i ", " \
+ "amb1_" #i ", " \
+ "emb0_" #i ", " \
+ "emb1_" #i ", " \
+ "eliv_" #i ", " \
+ "edea_" #i ", " \
+ "wmb0_" #i ", " \
+ "wmb1_" #i ", " \
+ "wdea_" #i ", "
+ FOR_CORES
+#undef FOR_CORE
+ "step"
") values ("
- "%ld, "
- {% for i in range(args.cores) %}
- "%ld, %ld, %ld, %ld, %ld, %f, %f, %ld, %ld, %ld, %ld, %ld, %ld, %ld{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) "%ld, %ld, %ld, %ld, %ld, %f, %f, %ld, %ld, %ld, %ld, %ld, %ld, %ld, "
+ FOR_CORES
+#undef FOR_CORE
+ "%ld"
");",
- g_steps,
- {% for i in range(args.cores) +%}
- g_cores[{{ i }}].cycl,
- g_cores[{{ i }}].mall,
- g_cores[{{ i }}].pnum,
- g_cores[{{ i }}].pfst,
- g_cores[{{ i }}].plst,
- amb0[{{ i }}],
- amb1[{{ i }}],
- g_cores[{{ i }}].emb0,
- g_cores[{{ i }}].emb1,
- g_cores[{{ i }}].eliv,
- g_cores[{{ i }}].edea,
- g_cores[{{ i }}].wmb0,
- g_cores[{{ i }}].wmb1,
- g_cores[{{ i }}].wdea{% if not loop.last %},{% endif %}
- {% endfor +%}
+#define FOR_CORE(i) \
+ g_cores[i].cycl, \
+ g_cores[i].mall, \
+ g_cores[i].pnum, \
+ g_cores[i].pfst, \
+ g_cores[i].plst, \
+ amb0[i], \
+ amb1[i], \
+ g_cores[i].emb0, \
+ g_cores[i].emb1, \
+ g_cores[i].eliv, \
+ g_cores[i].edea, \
+ g_cores[i].wmb0, \
+ g_cores[i].wmb1, \
+ g_cores[i].wdea,
+ FOR_CORES
+#undef FOR_CORE
+ g_steps
);
- // Insert row into instruction data tables
- char *iprefs[] = { "pop", "exe", "wrt" };
- int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]);
+ char *iprefs[] = { "pop", "exe", "wrt" };
+ int iprefs_cnt = sizeof(iprefs) / sizeof(iprefs[0]);
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ for (int i = 0; i < CORES; ++i) {
for (int j = 0; j < iprefs_cnt; ++j) {
uint64_t *ia;
if (!strcmp("pop", iprefs[j])) {
- // Population is generated above, prior to push
ia = ipop[i];
} else if (!strcmp("exe", iprefs[j])) {
ia = g_cores[i].iexe;
@@ -1023,101 +1014,102 @@ void arch_push_data_line() {
salis_exec_sql(
0, NULL, NULL,
"insert into %s_%d ("
- "step, "
- {% for i in range(args.cores) %}
- "cycl_{{ i }}, "
- {% endfor %}
- {% for i in arch_vars.inst_set %}
- "{{ i[0][0] }}{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) "cycl_" #i ", "
+ FOR_CORES
+#undef FOR_CORE
+#define INST(index, label, mnemonic, symbol) #label ", "
+ INST_SET
+#undef INST
+ "step"
") values ("
- "%ld, "
- {% for _ in range(args.cores) %}
- "%ld, "
- {% endfor %}
- {% for _ in range(arch_vars.inst_count) %}
- "%ld{% if not loop.last %},{% endif %} "
- {% endfor %}
+#define FOR_CORE(i) "%ld, "
+ FOR_CORES
+#undef FOR_CORE
+#define INST(index, label, mnemonic, symbol) "%ld, "
+ INST_SET
+#undef INST
+ "%ld"
");",
iprefs[j],
i,
- g_steps,
- {% for j in range(args.cores) %}
- g_cores[{{ j }}].cycl,
- {% endfor %}
- {% for j in range(arch_vars.inst_count) %}
- ia[{{ j }}]{% if not loop.last %},{% endif +%}
- {% endfor %}
+#define FOR_CORE(i) g_cores[i].cycl,
+ FOR_CORES
+#undef FOR_CORE
+#define INST(index, label, mnemonic, symbol) ia[index],
+ INST_SET
+#undef INST
+ g_steps
);
}
}
- // Insert row into memory event tables
- char *eprefs[] = { "aev" };
- int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]);
-
- // Event run-length data array is defined as static
- // This prevents heap errors
- static uint64_t erl_data[{{ mvec_size }} * 2];
+ char *eprefs[] = { "aev" };
+ int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]);
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ // TODO: insert memory events
+ for (int i = 0; i < CORES; ++i) {
for (int j = 0; j < eprefs_cnt; ++j) {
- uint64_t *eva;
+ uint64_t *in;
if (!strcmp("aev", eprefs[j])) {
- eva = g_cores[i].aeva;
+ in = g_cores[i].aeva;
}
- // Assume event data to be sparse in most cases
- // Run-length encoding should help keep database size manageable,
- // while making it easy to decode events array, wherever the
- // database is consumed.
- uint64_t addr = 0;
- uint64_t eix = 0;
+ // Compress event data
+ size_t size = sizeof(uint64_t) * MVEC_SIZE;
+ char *out = malloc(size);
+ assert(out);
- while (addr < {{ mvec_size }}) {
- assert(eix < {{ mvec_size }} * 2);
+ z_stream strm = { 0 };
+ strm.zalloc = NULL;
+ strm.zfree = NULL;
+ strm.opaque = NULL;
- erl_data[eix] = eva[addr];
- erl_data[eix + 1] = 0;
+ deflateInit(&strm, Z_DEFAULT_COMPRESSION);
- while (addr < {{ mvec_size }} && eva[addr] == erl_data[eix]) {
- ++erl_data[eix + 1];
- ++addr;
- }
+ strm.avail_in = size;
+ strm.avail_out = size;
+ strm.next_in = (Bytef *)in;
+ strm.next_out = (Bytef *)out;
- eix += 2;
- }
-
- {% if not args.optimized %}
- uint64_t el = 0;
-
- for (uint64_t k = 0; k < eix; k += 2) {
- el += erl_data[k + 1];
- }
-
- assert(el == {{ mvec_size }});
- {% endif %}
+ deflate(&strm, Z_FINISH);
- // Insert blob into database
- const void *blob_ptr = erl_data;
- int blob_size = eix * sizeof(uint64_t);
+ // Insert blob
+ const void *blob = out;
+ int blob_size = strm.total_out;
g_info("Pushing row to '%s_%d' table in SQLite database", eprefs[j], i);
salis_exec_sql(
- 1, &blob_ptr, &blob_size,
- "insert into %s_%d (step, evts) values (%ld, ?);",
- eprefs[j], i, g_steps
+ 1, &blob, &blob_size,
+ "insert into %s_%d ("
+#define FOR_CORE(i) "cycl_" #i ", "
+ FOR_CORES
+#undef FOR_CORE
+ "size, evts, step"
+ ") values ("
+#define FOR_CORE(i) "%ld, "
+ FOR_CORES
+#undef FOR_CORE
+ "%ld, ?, %ld"
+ ");",
+ eprefs[j], i,
+#define FOR_CORE(i) g_cores[i].cycl,
+ FOR_CORES
+#undef FOR_CORE
+ blob_size, g_steps
);
+
+ deflateEnd(&strm);
+ free(out);
}
}
- // Reset all data aggregation core fields to zero
- for (int i = 0; i < {{ args.cores }}; ++i) {
+ // Reset data aggregation fields
+ for (int i = 0; i < CORES; ++i) {
struct Core *core = &g_cores[i];
- memset(core->iexe, 0, sizeof(uint64_t) * {{ arch_vars.inst_count }});
- memset(core->iwrt, 0, sizeof(uint64_t) * {{ arch_vars.inst_count }});
+ memset(core->iexe, 0, sizeof(uint64_t) * INST_COUNT);
+ memset(core->iwrt, 0, sizeof(uint64_t) * INST_COUNT);
core->emb0 = 0;
core->emb1 = 0;
@@ -1127,8 +1119,9 @@ void arch_push_data_line() {
core->wmb1 = 0;
core->wdea = 0;
- // Event vectors
- memset(core->aeva, 0, sizeof(uint64_t) * {{ mvec_size }});
+ memset(core->aeva, 0, sizeof(uint64_t) * MVEC_SIZE);
+ //memset(core->eeva, 0, sizeof(uint64_t) * MVEC_SIZE);
+ //memset(core->weva, 0, sizeof(uint64_t) * MVEC_SIZE);
}
}
-{% endif %}
+#endif
diff --git a/arch/salis-v1/arch_vars.py b/arch/v1/arch_vars.py
index 25687ce..2373e6b 100644
--- a/arch/salis-v1/arch_vars.py
+++ b/arch/v1/arch_vars.py
@@ -1,55 +1,24 @@
-def gen_arch_vars(args):
- return {
- "mvec_loop": False,
-
- # Organisms consist of:
- # - instruction pointer
- # - seeker pointer
- # - main memory block
- # - child memory block
- # - 4 registers
- # - 8 value stack
- "proc_fields": [
- ("uint64_t", "ip"),
- ("uint64_t", "sp"),
- ("uint64_t", "mb0a"),
- ("uint64_t", "mb0s"),
- ("uint64_t", "mb1a"),
- ("uint64_t", "mb1s"),
- ("uint64_t", "r0x"),
- ("uint64_t", "r1x"),
- ("uint64_t", "r2x"),
- ("uint64_t", "r3x"),
- ("uint64_t", "s0"),
- ("uint64_t", "s1"),
- ("uint64_t", "s2"),
- ("uint64_t", "s3"),
- ("uint64_t", "s4"),
- ("uint64_t", "s5"),
- ("uint64_t", "s6"),
- ("uint64_t", "s7"),
- ],
-
- # Salis-v1 instruction set
- "inst_set": [
+class ArchVars:
+ def __init__(self, args):
+ self.inst_set = [
(["noop"], " "),
(["nop0"], "0"),
(["nop1"], "1"),
(["nop2"], "2"),
(["nop3"], "3"),
- # -------------
+
(["jmpb"], "("),
(["jmpf"], ")"),
(["adrb"], "["),
(["adrf"], "]"),
(["ifnz"], "?"),
- # -------------
+
(["allb"], "{"),
(["allf"], "}"),
(["bswp"], "%"),
(["bclr"], "|"),
(["splt"], "$"),
- # -------------
+
(["addn"], "+"),
(["subn"], "-"),
(["muln"], "*"),
@@ -61,15 +30,15 @@ def gen_arch_vars(args):
(["shfr"], ">"),
(["zero"], "z"),
(["unit"], "u"),
- # -------------
+
(["pshn"], "#"),
(["popn"], "~"),
- # -------------
+
(["load"], "."),
(["wrte"], ":"),
(["dupl"], "="),
(["swap"], "x"),
- # -------------
+
(["keya"], "a"),
(["keyb"], "b"),
(["keyc"], "c"),
@@ -86,7 +55,7 @@ def gen_arch_vars(args):
(["keyn"], "n"),
(["keyo"], "o"),
(["keyp"], "p"),
- # -------------
+
(["loka"], "A"),
(["lokb"], "B"),
(["lokc"], "C"),
@@ -103,23 +72,45 @@ def gen_arch_vars(args):
(["lokn"], "N"),
(["loko"], "O"),
(["lokp"], "P"),
- ],
+ ]
- "inst_count": 2 ** 6,
+ self.core_fields = [
+ ("uint64_t", "iexe", f"[{len(self.inst_set)}]"), # instruction execution counter
+ ("uint64_t", "iwrt", f"[{len(self.inst_set)}]"), # instruction write counter
- # Extra fields used exclusively for data aggregation
- "core_fields": [
- ("uint64_t", f"iexe[{2 ** 6}]", False),
- ("uint64_t", f"iwrt[{2 ** 6}]", False),
- ("uint64_t", "emb0", True),
- ("uint64_t", "emb1", True),
- ("uint64_t", "eliv", True),
- ("uint64_t", "edea", True),
- ("uint64_t", "wmb0", True),
- ("uint64_t", "wmb1", True),
- ("uint64_t", "wdea", True),
+ ("uint64_t", "emb0", ""), # executions within mb0 counter
+ ("uint64_t", "emb1", ""), # executions within mb1 counter
+ ("uint64_t", "eliv", ""), # executions within not-owned live code counter
+ ("uint64_t", "edea", ""), # executions within dead code counter
+ ("uint64_t", "wmb0", ""), # writes within mb0 counter
+ ("uint64_t", "wmb1", ""), # writes within mb1 counter
+ ("uint64_t", "wdea", ""), # writes within dead code counter
- # Event data aggregators
- ("uint64_t", f"aeva[{2 ** args.mvec_pow}]", False),
- ],
- }
+ ("uint64_t", "aeva", f"[{2 ** args.mvec_pow}]"), # allocation events array
+ #("uint64_t", "eeva", f"[{2 ** args.mvec_pow}]"), # execution events array
+ #("uint64_t", "weva", f"[{2 ** args.mvec_pow}]"), # write events array
+ ]
+
+ self.data_is_compressed = True
+ self.mvec_loop = False
+
+ self.proc_fields = [
+ ("uint64_t", "ip"),
+ ("uint64_t", "sp"),
+ ("uint64_t", "mb0a"),
+ ("uint64_t", "mb0s"),
+ ("uint64_t", "mb1a"),
+ ("uint64_t", "mb1s"),
+ ("uint64_t", "r0x"),
+ ("uint64_t", "r1x"),
+ ("uint64_t", "r2x"),
+ ("uint64_t", "r3x"),
+ ("uint64_t", "s0"),
+ ("uint64_t", "s1"),
+ ("uint64_t", "s2"),
+ ("uint64_t", "s3"),
+ ("uint64_t", "s4"),
+ ("uint64_t", "s5"),
+ ("uint64_t", "s6"),
+ ("uint64_t", "s7"),
+ ]