aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pu3/pu3.cpu.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/pu3/pu3.cpu.c b/pu3/pu3.cpu.c
new file mode 100644
index 0000000..39d4a1d
--- /dev/null
+++ b/pu3/pu3.cpu.c
@@ -0,0 +1,152 @@
+#define _POSIX_C_SOURCE 200809L
+//
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+// -- disable use of compiler intrinsics
+// #define PU3_NO_INTRINSICS
+// -- inaccurate clocks
+// #define PU3_BAD_CLOCK
+
+#ifndef PU3_BAD_CLOCK
+clock_t ntime() {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+
+ return t.tv_nsec + t.tv_sec * 1e9;
+}
+#else
+clock_t ntime() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
+}
+#endif
+
+// it is time
+// the thing you've none been waiting for
+// pu3
+
+typedef char *string;
+typedef unsigned char byte;
+typedef byte bool;
+
+typedef struct {
+ byte __spare[ 1 ];
+ byte op;
+ uint16_t data;
+ uint32_t dest;
+} pu3_inst_t;
+
+typedef union {
+ pu3_inst_t inst_struct;
+ uint64_t inst_num;
+} pu3_inst_u;
+
+typedef struct {
+ pu3_inst_u *memory;
+ uint32_t mem_size;
+ uint32_t *pc;
+ uint32_t *time_slice;
+} pu3_ctx;
+
+// instructions
+// 0: nop
+// 1: halt
+// 2: mov [target], src
+
+pu3(pu3_ctx ctx) {
+ static uint32_t i;
+ static pu3_inst_t cur;
+
+ pu3_inst_u *pc = ctx.memory + *ctx.pc;
+
+ static const __attribute__((aligned(64))) void *GOTO_TABLE[] = {
+ &&inst_0,
+ &&pu3_end,
+ &&inst_2,
+ };
+
+pu3_start:
+ cur = pc->inst_struct;
+ pc++;
+
+#ifndef PU3_NO_INTRINSICS
+ __builtin_prefetch(pc + 1, 0, 3);
+#endif
+#define get_data() ((cur.op & 0b10000000) ? (ctx.memory[ cur.data ].inst_num) : (cur.data))
+
+ // 1 - 4: opcode
+ // 5: data_type [0 = constant, 1 = memory]
+
+ // [5] [ ] [ ] [ ] [1] [2] [3] [4]
+ goto *GOTO_TABLE[ cur.op & 0b1111 ];
+
+inst_0: // nop
+ pc += cur.dest;
+ goto pu3_start;
+
+inst_1: // halt
+ // unused, we just jump to break
+
+inst_2: // mov
+ ctx.memory[ cur.dest ].inst_num = get_data();
+ goto pu3_start;
+
+pu3_end:
+ *ctx.pc = pc - ctx.memory;
+ return *ctx.time_slice - i;
+}
+
+const string op_names[] = { "nop", "halt", "mov" };
+
+// bench: measure using clock()
+double bench_cycle(byte instr, pu3_ctx ctx) {
+ int i;
+ for (i = 16; i < ctx.mem_size; i++) {
+ ctx.memory[ i ].inst_struct.op = instr & 0b1111;
+ ctx.memory[ i ].inst_struct.dest = 5;
+ ctx.memory[ i ].inst_struct.data = 6;
+
+ if (i == ctx.mem_size - 1) ctx.memory[ i ].inst_struct.op = 1;
+ }
+ ctx.memory[ i ].inst_struct.op = 0;
+ *ctx.pc = 8;
+
+ clock_t start = ntime();
+ // int wstart = rdtsc();
+ pu3(ctx);
+ // int wend = rdtsc();
+ clock_t end = ntime();
+
+ double nanoseconds = (end - start);
+ double inst_count = *ctx.pc - 8;
+ double insts_per_ns = inst_count / nanoseconds;
+ double insts_per_s = insts_per_ns * 1e9;
+
+ printf(
+ "%-8s | time: %.3fms | insts: %.0f (%li real) | ips: %.3fM inst/s\n",
+ op_names[ instr ],
+ nanoseconds / 1000000,
+ inst_count,
+ end - start,
+ insts_per_ns * 1e3);
+
+ return insts_per_s / 1e6; // return M inst/s
+}
+
+main() {
+ static const uint32_t SIZE = 1 << 24;
+
+ pu3_ctx ctx = { .memory = calloc(SIZE, sizeof(pu3_inst_u)),
+ .mem_size = SIZE,
+ .pc = calloc(1, sizeof(uint32_t)),
+ .time_slice = calloc(1, sizeof(uint32_t)) };
+
+ bench_cycle(2, ctx);
+} \ No newline at end of file