diff options
Diffstat (limited to 'demo')
| -rw-r--r-- | demo/cache.c | 82 | ||||
| -rw-r--r-- | demo/console.c | 237 | ||||
| -rw-r--r-- | demo/demo.h | 48 | ||||
| -rw-r--r-- | demo/link.ld | 34 | ||||
| -rw-r--r-- | demo/lock.S | 26 | ||||
| -rw-r--r-- | demo/main.c | 115 | ||||
| -rw-r--r-- | demo/smp.c | 48 | ||||
| -rw-r--r-- | demo/start.S | 61 | ||||
| -rw-r--r-- | demo/util.c | 157 |
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; +} |
