diff options
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.py | 34 | ||||
| -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, ®, 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, ®, 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, ®, 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, ®, 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"), + ] |
