summaryrefslogtreecommitdiff
path: root/demo
diff options
context:
space:
mode:
Diffstat (limited to 'demo')
-rw-r--r--demo/cache.c82
-rw-r--r--demo/console.c237
-rw-r--r--demo/demo.h48
-rw-r--r--demo/link.ld34
-rw-r--r--demo/lock.S26
-rw-r--r--demo/main.c115
-rw-r--r--demo/smp.c48
-rw-r--r--demo/start.S61
-rw-r--r--demo/util.c157
9 files changed, 808 insertions, 0 deletions
diff --git a/demo/cache.c b/demo/cache.c
new file mode 100644
index 0000000..c9e56f0
--- /dev/null
+++ b/demo/cache.c
@@ -0,0 +1,82 @@
+#include "demo.h"
+
+#define CACHE_DEBUG_BASE(n) (0x30100000 | (0x10000 * (n)))
+#define CACHE_DEBUG_WORD(n, i) (*(volatile unsigned *)(CACHE_DEBUG_BASE(n) + 16 + 4 * (i)))
+
+#define CACHE_STATUS(n) (*(volatile unsigned *)CACHE_DEBUG_BASE(n))
+#define CACHE_STATUS_CACHED (1 << 1)
+#define CACHE_STATUS_STATE_MASK 0x0000000c
+#define CACHE_STATUS_STATE_SHIFT 2
+#define CACHE_STATUS_INDEX_MASK 0x0000fff0
+#define CACHE_STATUS_INDEX_SHIFT 4
+#define CACHE_STATUS_INDEX_BITS 12
+#define CACHE_STATUS_TAG_MASK 0x1fff0000
+#define CACHE_STATUS_TAG_SHIFT 16
+#define CACHE_STATUS_TAG_BITS 13
+#define CACHE_STATUS_STATE_I 0b00
+#define CACHE_STATUS_STATE_S 0b01
+#define CACHE_STATUS_STATE_E 0b10
+#define CACHE_STATUS_STATE_M 0b11
+
+void cache_debug(unsigned cpu, void *ptr)
+{
+ unsigned ptr_val = (unsigned)ptr;
+
+ CACHE_STATUS(cpu) = (unsigned)ptr;
+ unsigned status = CACHE_STATUS(cpu);
+
+ int cached = status & CACHE_STATUS_CACHED;
+
+ print("req_addr: %p", ptr);
+ print("cacheability: %s", cached ? "write-back" : "uncached (I/O)");
+
+ if (!cached)
+ return;
+
+ unsigned index = (status & CACHE_STATUS_INDEX_MASK) >> CACHE_STATUS_INDEX_SHIFT;
+ unsigned addr_tag = (ptr_val & CACHE_STATUS_TAG_MASK) >> CACHE_STATUS_TAG_SHIFT;
+ unsigned cache_tag = (status & CACHE_STATUS_TAG_MASK) >> CACHE_STATUS_TAG_SHIFT;
+ unsigned line_addr = status & (CACHE_STATUS_TAG_MASK | CACHE_STATUS_INDEX_MASK);
+
+ print("index: %r", index, CACHE_STATUS_INDEX_BITS);
+ print("req_tag: %r", addr_tag, CACHE_STATUS_TAG_BITS);
+ print("cache_tag: %r", cache_tag, CACHE_STATUS_TAG_BITS);
+ print("req_line: %p", ptr_val & (CACHE_STATUS_TAG_MASK | CACHE_STATUS_INDEX_MASK));
+ print("cache_line: %p", line_addr);
+
+ int valid, dirty;
+ const char *state;
+
+ switch ((status & CACHE_STATUS_STATE_MASK) >> CACHE_STATUS_STATE_SHIFT) {
+ case CACHE_STATUS_STATE_I:
+ valid = 0;
+ dirty = 0;
+ state = "INVALID";
+ break;
+
+ case CACHE_STATUS_STATE_S:
+ valid = 1;
+ dirty = 0;
+ state = "SHARED";
+ break;
+
+ case CACHE_STATUS_STATE_E:
+ valid = 1;
+ dirty = 0;
+ state = "EXCLUSIVE";
+ break;
+
+ case CACHE_STATUS_STATE_M:
+ valid = 1;
+ dirty = 1;
+ state = "MODIFIED";
+ break;
+ }
+
+ print("valid=%d dirty=%d state=%s", valid, dirty, state);
+ print("access is a %s on cpu%u", valid && addr_tag == cache_tag ? "hit" : "miss", cpu);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ print("%p: %x", line_addr | i << CACHE_STATUS_STATE_SHIFT, CACHE_DEBUG_WORD(cpu, i));
+ }
+}
diff --git a/demo/console.c b/demo/console.c
new file mode 100644
index 0000000..d2d13fa
--- /dev/null
+++ b/demo/console.c
@@ -0,0 +1,237 @@
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "demo.h"
+
+#define JTAG_UART_BASE 0x30000000
+#define JTAG_UART_DATA (*(volatile unsigned *)(JTAG_UART_BASE + 0))
+#define JTAG_UART_DATA_RVALID (1 << 15)
+#define JTAG_UART_DATA_RAVAIL 0xffff0000
+#define JTAG_UART_CONTROL (*(volatile unsigned *)(JTAG_UART_BASE + 4))
+#define JTAG_UART_CONTROL_RE (1 << 0)
+#define JTAG_UART_CONTROL_AC (1 << 10)
+#define JTAG_UART_CONTROL_WSPACE 0xffff0000
+
+#define INTC_BASE 0x30070000
+#define INTC_MASK (*(volatile unsigned *)(INTC_BASE + 4))
+#define INTC_MASK_TIMER (1 << 0)
+#define INTC_MASK_UART (1 << 1)
+
+static struct lock console_lock;
+
+static int input_run = 0;
+static unsigned input_len = 0;
+static unsigned input_max;
+static char *input_buf;
+
+static void print_char(char c)
+{
+ while (!(JTAG_UART_CONTROL & JTAG_UART_CONTROL_WSPACE));
+ JTAG_UART_DATA = (unsigned char)c;
+}
+
+static void print_str(const char *s)
+{
+ while (*s)
+ print_char(*s++);
+}
+
+static void print_hex(unsigned val, unsigned bits)
+{
+ static const char hex_digits[16] = "0123456789abcdef";
+
+ print_str("0x");
+
+ if (bits < 4)
+ bits = 4;
+
+ if (bits > 32)
+ bits = 32;
+
+ bits = (bits + 3) & ~3;
+
+ do
+ print_char(hex_digits[(val >> (bits -= 4)) & 0xf]);
+ while (bits);
+}
+
+static void print_dec(unsigned val)
+{
+ char dec[11] = {'\0'};
+ char *next = dec;
+
+ do {
+ *next++ = '0' + val % 10;
+ val /= 10;
+ } while (val && next < dec + sizeof dec);
+
+ const char *c = next;
+ while (c > dec)
+ print_char(*--c);
+}
+
+void console_init(void)
+{
+ spin_init(&console_lock);
+
+ input_buf = NULL;
+ input_max = 0;
+
+ // Habilitar irqs de entrada uart
+ JTAG_UART_CONTROL = JTAG_UART_CONTROL_RE;
+
+ // Habilitar irqs en controlador de interrupciones
+ INTC_MASK = INTC_MASK_UART;
+
+ // Habilitar irqs en core
+ asm volatile("msr cpsr_c, #0x53");
+}
+
+void print(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ unsigned irq_save;
+ spin_lock(&console_lock, &irq_save);
+
+ while (!(JTAG_UART_CONTROL & JTAG_UART_CONTROL_AC));
+
+ print_str("\r ");
+ for (unsigned i = 0; i < input_len; ++i)
+ print_char(' ');
+
+ print_str("\rcpu");
+ print_dec(this_cpu->num);
+ print_str(": ");
+
+ char c;
+ while ((c = *fmt++)) {
+ if (c != '%') {
+ print_char(c);
+ continue;
+ }
+
+ unsigned val;
+ switch ((c = *fmt++)) {
+ case 'c':
+ print_char((char)va_arg(args, int));
+ break;
+
+ case 'd':
+ case 'i':
+ print_dec((unsigned)va_arg(args, int));
+ break;
+
+ case 'p':
+ print_hex((unsigned)va_arg(args, const void *), 32);
+ break;
+
+ case 'r':
+ val = va_arg(args, unsigned);
+ print_hex(val, va_arg(args, unsigned));
+ break;
+
+ case 'u':
+ print_dec(va_arg(args, unsigned));
+ break;
+
+ case 's':
+ print_str(va_arg(args, const char *));
+ break;
+
+ case 'x':
+ print_hex(va_arg(args, unsigned), 32);
+ break;
+
+ default:
+ print_char('%');
+ if (c)
+ print_char(c);
+
+ break;
+ }
+ }
+
+ print_str("\r\n> ");
+ if (!input_run && input_buf)
+ print_str(input_buf);
+
+ JTAG_UART_CONTROL = JTAG_UART_CONTROL_RE | JTAG_UART_CONTROL_AC;
+
+ spin_unlock(&console_lock, irq_save);
+ va_end(args);
+}
+
+void read_line(char *buf, unsigned size)
+{
+ if (!size)
+ return;
+
+ buf[0] = '\0';
+
+ unsigned irq_save;
+ spin_lock(&console_lock, &irq_save);
+ input_buf = buf;
+ input_max = size;
+ input_len = 0;
+ spin_unlock(&console_lock, irq_save);
+
+ while (1) {
+ spin_lock(&console_lock, &irq_save);
+ if (input_run)
+ break;
+
+ spin_unlock(&console_lock, irq_save);
+ }
+
+ input_buf = NULL;
+ input_len = 0;
+ input_max = 0;
+ input_run = 0;
+ spin_unlock(&console_lock, irq_save);
+}
+
+void irq(void)
+{
+ unsigned irq_save;
+ spin_lock(&console_lock, &irq_save);
+
+ unsigned data;
+ do {
+ data = JTAG_UART_DATA;
+ if (!(data & JTAG_UART_DATA_RVALID))
+ break;
+ else if (input_run || !input_buf)
+ continue;
+
+ char c = (char)data;
+
+ switch (c) {
+ case 0x7f: // DEL
+ if (input_len > 0) {
+ --input_len;
+ print_str("\b \b");
+ }
+
+ break;
+
+ case '\n':
+ input_run = 1;
+ input_len = 0;
+ print_str("\r\n> ");
+ break;
+
+ default:
+ if (input_len < input_max - 1 && c >= ' ' && c <= '~') {
+ print_char(c);
+ input_buf[input_len++] = c;
+ input_buf[input_len] = '\0';
+ }
+
+ break;
+ }
+ } while (data & JTAG_UART_DATA_RAVAIL);
+
+ spin_unlock(&console_lock, irq_save);
+}
diff --git a/demo/demo.h b/demo/demo.h
new file mode 100644
index 0000000..f98d599
--- /dev/null
+++ b/demo/demo.h
@@ -0,0 +1,48 @@
+#ifndef DEMO_H
+#define DEMO_H
+
+#define NUM_CPUS 4
+
+struct lock
+{
+ volatile unsigned val;
+};
+
+struct cpu
+{
+ unsigned num;
+};
+
+/* Esto viola la ABI, pero no importa porque no dependemos de bibliotecas
+ * https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.html
+ */
+register struct cpu *this_cpu asm("r9");
+
+void spin_init(struct lock *lock);
+void spin_lock(struct lock *lock, unsigned *irq_save);
+void spin_unlock(struct lock *lock, unsigned irq_save);
+
+void console_init(void);
+void print(const char *fmt, ...);
+void read_line(char *vuf, unsigned size);
+
+void run_cpu(unsigned num);
+void run_cpus(unsigned mask);
+void halt_cpu(unsigned num);
+void halt_cpus(unsigned mask);
+
+int strcmp(const char *s1, const char *s2);
+char *strtok_input(char **tokens);
+
+int expect_end(char **tokens);
+
+int parse_hex(char **tokens, unsigned *val);
+int parse_ptr(char **tokens, void **ptr);
+int parse_aligned(char **tokens, void **ptr);
+
+int parse_cpu(char **tokens, unsigned *cpu);
+int parse_cpu_mask(char **tokens, unsigned *mask);
+
+void cache_debug(unsigned cpu, void *ptr);
+
+#endif
diff --git a/demo/link.ld b/demo/link.ld
new file mode 100644
index 0000000..8a4d40b
--- /dev/null
+++ b/demo/link.ld
@@ -0,0 +1,34 @@
+MEMORY
+{
+ HPS_SDRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 512M
+}
+
+SECTIONS
+{
+ ._img :
+ {
+ KEEP(*(.interrupt_vector))
+ *(.text)
+ *(.text*)
+ *(.rodata)
+ *(.rodata*)
+ *(.data)
+ *(.data*)
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ } > HPS_SDRAM
+
+ _stack_shift = 13;
+ _stack_size = (1 << _stack_shift) * (4 + 1); # Hay un stack extra para irq
+ _stack_end = ORIGIN(HPS_SDRAM) + LENGTH(HPS_SDRAM);
+ _irq_stack = _stack_end;
+ _stack_begin = (_stack_end - _stack_size) + (1 << _stack_shift);
+
+ . = _stack_begin;
+ ._stack :
+ {
+ . = . + _stack_size;
+ } > HPS_SDRAM
+}
diff --git a/demo/lock.S b/demo/lock.S
new file mode 100644
index 0000000..3ec2765
--- /dev/null
+++ b/demo/lock.S
@@ -0,0 +1,26 @@
+.global spin_init
+spin_init:
+ mov r1, #0
+ str r1, [r0]
+ mov pc, lr
+
+.global spin_lock
+spin_lock:
+ mrs r2, cpsr
+ str r2, [r1]
+ orr r2, r2, #0xc0 @ Levanta I, F
+ msr cpsr_c, r2
+ mov r3, #1
+1: ldrex r2, [r0]
+ teq r2, #0
+ strexeq r2, r3, [r0]
+ teqeq r2, #0
+ bne 1b
+ mov pc, lr
+
+.global spin_unlock
+spin_unlock:
+ mov r2, #0
+ str r2, [r0]
+ msr cpsr_c, r1
+ mov pc, lr
diff --git a/demo/main.c b/demo/main.c
new file mode 100644
index 0000000..988ef55
--- /dev/null
+++ b/demo/main.c
@@ -0,0 +1,115 @@
+#include "demo.h"
+
+static struct cpu cpu0, cpu1, cpu2, cpu3;
+
+struct cpu *__cpus[] = { &cpu0, &cpu1, &cpu2, &cpu3 };
+
+static volatile unsigned boot_done;
+
+static void cmd_run(char **tokens)
+{
+ unsigned mask;
+ if (parse_cpu_mask(tokens, &mask) < 0)
+ return;
+
+ run_cpus(mask);
+}
+
+static void cmd_halt(char **tokens)
+{
+ unsigned mask;
+ if (parse_cpu_mask(tokens, &mask) < 0)
+ return;
+
+ halt_cpus(mask);
+}
+
+static void cmd_read(char **tokens)
+{
+ void *ptr;
+ if (parse_aligned(tokens, &ptr) < 0 || expect_end(tokens) < 0)
+ return;
+
+ print("%p: %x", ptr, *(volatile unsigned *)ptr);
+}
+
+static void cmd_write(char **tokens)
+{
+ void *ptr;
+ unsigned val;
+
+ if (parse_aligned(tokens, &ptr) < 0 || parse_hex(tokens, &val) < 0 || expect_end(tokens) < 0)
+ return;
+
+ *(volatile unsigned *)ptr = val;
+}
+
+static void cmd_cache(char **tokens)
+{
+ void *ptr;
+ unsigned cpu;
+
+ if (parse_cpu(tokens, &cpu) < 0 || parse_ptr(tokens, &ptr) < 0)
+ return;
+
+ cache_debug(cpu, ptr);
+}
+
+static void bsp_main(void)
+{
+ boot_done = 0;
+ run_cpu(1);
+
+ while (!boot_done);
+ print("booted %u cpus", NUM_CPUS);
+
+ while (1) {
+ char input[64];
+ read_line(input, sizeof input);
+
+ char *tokens = input;
+
+ char *cmd = strtok_input(&tokens);
+ if (!cmd)
+ continue;
+
+ if (!strcmp(cmd, "run"))
+ cmd_run(&tokens);
+ else if (!strcmp(cmd, "halt"))
+ cmd_halt(&tokens);
+ else if (!strcmp(cmd, "read"))
+ cmd_read(&tokens);
+ else if (!strcmp(cmd, "write"))
+ cmd_write(&tokens);
+ else if (!strcmp(cmd, "cache"))
+ cmd_cache(&tokens);
+ else
+ print("unknown command '%s'", cmd);
+ }
+}
+
+static void ap_main(void)
+{
+ if (this_cpu->num < NUM_CPUS - 1)
+ run_cpu(this_cpu->num + 1);
+ else
+ boot_done = 1;
+
+ halt_cpu(this_cpu->num);
+
+ while (1) {
+ }
+}
+
+void reset(void)
+{
+ if (this_cpu->num == 0)
+ console_init();
+
+ print("exited reset");
+
+ if (this_cpu->num == 0)
+ bsp_main();
+ else
+ ap_main();
+}
diff --git a/demo/smp.c b/demo/smp.c
new file mode 100644
index 0000000..4357d35
--- /dev/null
+++ b/demo/smp.c
@@ -0,0 +1,48 @@
+#include "demo.h"
+
+#define SMP_CTRL_BASE 0x30140000
+#define SMP_CTRL (*(volatile unsigned *)SMP_CTRL_BASE)
+
+void run_cpu(unsigned num)
+{
+ run_cpus(1 << num);
+}
+
+void run_cpus(unsigned mask)
+{
+ unsigned ctrl_word = 0, ctrl_read = SMP_CTRL;
+ for (unsigned cpu = 0; cpu < NUM_CPUS; ++cpu) {
+ if (!(mask & (1 << cpu)))
+ continue;
+
+ if (ctrl_read & (0b001 << (cpu * 8))) {
+ print("run cpu%u", cpu);
+ ctrl_word |= 0b001 << (cpu * 8);
+ } else
+ print("cpu%u already running", cpu);
+ }
+
+ SMP_CTRL = ctrl_word;
+}
+
+void halt_cpu(unsigned num)
+{
+ halt_cpus(1 << num);
+}
+
+void halt_cpus(unsigned mask)
+{
+ unsigned ctrl_word = 0, ctrl_read = SMP_CTRL;
+ for (unsigned cpu = 0; cpu < NUM_CPUS; ++cpu) {
+ if (!(mask & (1 << cpu)))
+ continue;
+
+ if (!(ctrl_read & (0b001 << (cpu * 8)))) {
+ print("halt cpu%u%s", cpu, cpu == this_cpu->num ? " (myself)" : "");
+ ctrl_word |= 0b010 << (cpu * 8);
+ } else
+ print("cpu%u already halted", cpu);
+ }
+
+ SMP_CTRL = ctrl_word;
+}
diff --git a/demo/start.S b/demo/start.S
new file mode 100644
index 0000000..ecdeb3c
--- /dev/null
+++ b/demo/start.S
@@ -0,0 +1,61 @@
+.section .interrupt_vector
+
+__reset:
+ b _start
+
+__undefined:
+ b undefined
+
+__swi:
+ b swi
+
+__prefetch_abort:
+ b prefetch_abort
+
+__data_abort:
+ b data_abort
+
+__reserved:
+ b .
+
+__irq:
+ b _irq_entry
+
+__fiq:
+ b .
+
+.text
+
+.global _start
+_start:
+ ldr r0, =_boot_num
+ ldr r1, [r0]
+ add r2, r1, #1
+ str r2, [r0]
+ ldr r2, =__cpus
+ lsl r3, r1, #2
+ ldr r9, [r2, r3]
+ str r1, [r9]
+ ldr r2, =_stack_shift
+ lsl r1, r1, r2
+ ldr sp, =_stack_begin
+ add sp, sp, r1
+ mov lr, #0
+ bl reset
+ b .
+
+_boot_num:
+ .word 0
+
+_irq_entry:
+ ldr sp, =_irq_stack
+ push {r0-r11, lr}
+ bl irq
+ pop {r0-r11, lr}
+ subs pc, lr, #4
+
+.weak undefined, swi, data_abort, prefetch_abort
+.set swi, __swi
+.set undefined, __undefined
+.set data_abort, __data_abort
+.set prefetch_abort, __prefetch_abort
diff --git a/demo/util.c b/demo/util.c
new file mode 100644
index 0000000..d08ec20
--- /dev/null
+++ b/demo/util.c
@@ -0,0 +1,157 @@
+#include <stddef.h>
+
+#include "demo.h"
+
+static void bad_input(void)
+{
+ print("bad input");
+}
+
+static void unexpected_eof(void)
+{
+ print("unexpected end-of-input");
+}
+
+static int parse_cpu_token(const char *token, unsigned *cpu)
+{
+ if (token[0] != 'c' || token[1] != 'p' || token[2] != 'u'
+ || !('0' <= token[3] && token[3] < '0' + NUM_CPUS) || token[4]) {
+ bad_input();
+ return -1;
+ }
+
+ *cpu = token[3] - '0';
+ return 0;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+ while (*s1 && *s1 == *s2)
+ s1++, s2++;
+
+ return (int)*(const unsigned char *)s1 - (int)*(const unsigned char *)s2;
+}
+
+char *strtok_input(char **tokens)
+{
+ char *start = *tokens;
+ while (*start && *start == ' ')
+ ++start;
+
+ if (!*start) {
+ *tokens = start;
+ return NULL;
+ }
+
+ char *end = start + 1;
+ while (*end && *end != ' ')
+ ++end;
+
+ *tokens = *end ? end + 1 : end;
+ *end = '\0';
+
+ return start;
+}
+
+int parse_cpu(char **tokens, unsigned *cpu)
+{
+ char *token = strtok_input(tokens);
+ if (!token) {
+ unexpected_eof();
+ return -1;
+ }
+
+ return parse_cpu_token(token, cpu);
+}
+
+int parse_cpu_mask(char **tokens, unsigned *mask)
+{
+ *mask = 0;
+
+ char *token;
+ while ((token = strtok_input(tokens))) {
+ unsigned cpu;
+ if (parse_cpu_token(token, &cpu) < 0)
+ return -1;
+
+ *mask |= 1 << cpu;
+ }
+
+ if (!*mask) {
+ print("must specify at least one cpu");
+ return -1;
+ }
+
+ return 0;
+}
+
+int parse_hex(char **tokens, unsigned *val)
+{
+ char *token = strtok_input(tokens);
+ if (!token) {
+ unexpected_eof();
+ return -1;
+ } else if (token[0] != '0' || token[1] != 'x') {
+ bad_input();
+ return -1;
+ }
+
+ *val = 0;
+
+ char *c = &token[2];
+ unsigned nibbles = 0;
+
+ while (*c) {
+ *val <<= 4;
+
+ if ('0' <= *c && *c <= '9')
+ *val |= *c - '0';
+ else if ('a' <= *c && *c <= 'f')
+ *val |= *c - 'a' + 10;
+ else if ('A' <= *c && *c <= 'F')
+ *val |= *c - 'A' + 10;
+
+ ++c;
+ ++nibbles;
+ }
+
+ if (!nibbles || nibbles > 8) {
+ bad_input();
+ return -1;
+ }
+
+ return 0;
+}
+
+int parse_ptr(char **tokens, void **ptr)
+{
+ unsigned ptr_val;
+ if (parse_hex(tokens, &ptr_val) < 0)
+ return -1;
+
+ *ptr = (void *)ptr_val;
+ return 0;
+}
+
+int parse_aligned(char **tokens, void **ptr)
+{
+ if (parse_ptr(tokens, ptr) < 0)
+ return -1;
+ else if ((unsigned)*ptr & 0b11) {
+ print("unaligned address: %p", *ptr);
+ return -1;
+ }
+
+ return 0;
+}
+
+int expect_end(char **tokens)
+{
+ char *token = strtok_input(tokens);
+ if (token) {
+ print("too many arguments starting from '%s'", token);
+ return -1;
+ }
+
+ return 0;
+}