comm: refactor to support lwip event model

pull/109/head
Sebastien Bourdeauducq 2015-04-22 01:31:31 +08:00
parent 9f52277245
commit 18106cc014
20 changed files with 832 additions and 625 deletions

View File

@ -1,10 +1,8 @@
import struct
import zlib
import logging
from enum import Enum
from fractions import Fraction
from artiq.language import units
from artiq.coredevice.runtime import Environment
from artiq.coredevice import runtime_exceptions
from artiq.language import core as core_language
@ -15,33 +13,30 @@ logger = logging.getLogger(__name__)
class _H2DMsgType(Enum):
LINK_MESSAGE = 1
REQUEST_IDENT = 2
LOG_REQUEST = 1
IDENT_REQUEST = 2
SWITCH_CLOCK = 3
LOAD_OBJECT = 4
RUN_KERNEL = 5
RPC_REPLY = 6
class _D2HMsgType(Enum):
MESSAGE_UNRECOGNIZED = 1
LOG = 2
LOG_REPLY = 1
IDENT_REPLY = 2
CLOCK_SWITCH_COMPLETED = 3
CLOCK_SWITCH_FAILED = 4
IDENT = 3
CLOCK_SWITCH_COMPLETED = 4
CLOCK_SWITCH_FAILED = 5
LOAD_COMPLETED = 5
LOAD_FAILED = 6
OBJECT_LOADED = 6
OBJECT_INCORRECT_LENGTH = 7
OBJECT_CRC_FAILED = 8
OBJECT_UNRECOGNIZED = 9
KERNEL_FINISHED = 7
KERNEL_STARTUP_FAILED = 8
KERNEL_EXCEPTION = 9
KERNEL_FINISHED = 10
KERNEL_STARTUP_FAILED = 11
KERNEL_EXCEPTION = 12
RPC_REQUEST = 13
RPC_REQUEST = 10
class UnsupportedDevice(Exception):
@ -71,39 +66,35 @@ class CommGeneric:
raise NotImplementedError
#
def _read(self, length):
def _read_header(self):
self.open()
return self.read(length)
def _write(self, data):
self.open()
self.write(data)
def _get_device_msg(self):
while True:
(reply, ) = struct.unpack("B", self._read(1))
msg = _D2HMsgType(reply)
if msg == _D2HMsgType.LOG:
(length, ) = struct.unpack(">h", self._read(2))
log_message = ""
for i in range(length):
(c, ) = struct.unpack("B", self._read(1))
log_message += chr(c)
logger.info("DEVICE LOG: %s", log_message)
sync_count = 0
while sync_count < 4:
(c, ) = struct.unpack("B", self.read(1))
if c == 0x5a:
sync_count += 1
else:
logger.debug("message received: %r", msg)
return msg
sync_count = 0
length, tyv = struct.unpack(">lB", self.read(5))
ty = _D2HMsgType(tyv)
logger.debug("receiving message: type=%r length=%d", ty, length)
return length, ty
def _write_header(self, length, ty):
self.open()
logger.debug("sending message: type=%r length=%d", ty, length)
self.write(struct.pack(">llB", 0x5a5a5a5a, length, ty.value))
def get_runtime_env(self):
self._write(struct.pack(">lb", 0x5a5a5a5a,
_H2DMsgType.REQUEST_IDENT.value))
msg = self._get_device_msg()
if msg != _D2HMsgType.IDENT:
raise IOError("Incorrect reply from device: {}".format(msg))
(reply, ) = struct.unpack("B", self._read(1))
self._write_header(9, _H2DMsgType.IDENT_REQUEST)
_, ty = self._read_header()
if ty != _D2HMsgType.IDENT_REPLY:
raise IOError("Incorrect reply from device: {}".format(ty))
(reply, ) = struct.unpack("B", self.read(1))
runtime_id = chr(reply)
for i in range(3):
(reply, ) = struct.unpack("B", self._read(1))
(reply, ) = struct.unpack("B", self.read(1))
runtime_id += chr(reply)
if runtime_id != "AROR":
raise UnsupportedDevice("Unsupported runtime ID: {}"
@ -111,63 +102,59 @@ class CommGeneric:
return Environment()
def switch_clock(self, external):
self._write(struct.pack(
">lbb", 0x5a5a5a5a, _H2DMsgType.SWITCH_CLOCK.value,
int(external)))
msg = self._get_device_msg()
if msg != _D2HMsgType.CLOCK_SWITCH_COMPLETED:
raise IOError("Incorrect reply from device: {}".format(msg))
self._write_header(10, _H2DMsgType.SWITCH_CLOCK)
self.write(struct.pack("B", int(external)))
_, ty = self._read_header()
if ty != _D2HMsgType.CLOCK_SWITCH_COMPLETED:
raise IOError("Incorrect reply from device: {}".format(ty))
def load(self, kcode):
self._write(struct.pack(
">lblL",
0x5a5a5a5a, _H2DMsgType.LOAD_OBJECT.value,
len(kcode), zlib.crc32(kcode)))
self._write(kcode)
msg = self._get_device_msg()
if msg != _D2HMsgType.OBJECT_LOADED:
raise IOError("Incorrect reply from device: "+str(msg))
self._write_header(len(kcode) + 9, _H2DMsgType.LOAD_OBJECT)
self.write(kcode)
_, ty = self._read_header()
if ty != _D2HMsgType.LOAD_COMPLETED:
raise IOError("Incorrect reply from device: "+str(ty))
def run(self, kname):
self._write(struct.pack(
">lbl", 0x5a5a5a5a, _H2DMsgType.RUN_KERNEL.value, len(kname)))
for c in kname:
self._write(struct.pack(">B", ord(c)))
self._write_header(len(kname) + 9, _H2DMsgType.RUN_KERNEL)
self.write(bytes(kname, "ascii"))
logger.debug("running kernel: %s", kname)
def _receive_rpc_values(self):
r = []
while True:
type_tag = chr(struct.unpack(">B", self._read(1))[0])
type_tag = chr(struct.unpack("B", self.read(1))[0])
if type_tag == "\x00":
return r
if type_tag == "n":
r.append(None)
if type_tag == "b":
r.append(bool(struct.unpack(">B", self._read(1))[0]))
r.append(bool(struct.unpack("B", self.read(1))[0]))
if type_tag == "i":
r.append(struct.unpack(">l", self._read(4))[0])
r.append(struct.unpack(">l", self.read(4))[0])
if type_tag == "I":
r.append(struct.unpack(">q", self._read(8))[0])
r.append(struct.unpack(">q", self.read(8))[0])
if type_tag == "f":
r.append(struct.unpack(">d", self._read(8))[0])
r.append(struct.unpack(">d", self.read(8))[0])
if type_tag == "F":
n, d = struct.unpack(">qq", self._read(16))
n, d = struct.unpack(">qq", self.read(16))
r.append(Fraction(n, d))
if type_tag == "l":
r.append(self._receive_rpc_values())
def _serve_rpc(self, rpc_wrapper, rpc_map, user_exception_map):
rpc_num = struct.unpack(">h", self._read(2))[0]
rpc_num = struct.unpack(">l", self.read(4))[0]
args = self._receive_rpc_values()
logger.debug("rpc service: %d %r", rpc_num, args)
eid, r = rpc_wrapper.run_rpc(
user_exception_map, rpc_map[rpc_num], args)
self._write(struct.pack(">ll", eid, r))
logger.debug("rpc service: %d %r == %r", rpc_num, args, r)
self._write_header(9+2*4, _H2DMsgType.RPC_REPLY)
self.write(struct.pack(">ll", eid, r))
logger.debug("rpc service: %d %r == %r (eid %d)", rpc_num, args,
r, eid)
def _serve_exception(self, rpc_wrapper, user_exception_map):
eid, p0, p1, p2 = struct.unpack(">lqqq", self._read(4+3*8))
eid, p0, p1, p2 = struct.unpack(">lqqq", self.read(4+3*8))
rpc_wrapper.filter_rpc_exception(eid)
if eid < core_language.first_user_eid:
exception = runtime_exceptions.exception_map[eid]
@ -179,17 +166,24 @@ class CommGeneric:
def serve(self, rpc_map, user_exception_map):
rpc_wrapper = RPCWrapper()
while True:
msg = self._get_device_msg()
if msg == _D2HMsgType.RPC_REQUEST:
_, ty = self._read_header()
if ty == _D2HMsgType.RPC_REQUEST:
self._serve_rpc(rpc_wrapper, rpc_map, user_exception_map)
elif msg == _D2HMsgType.KERNEL_EXCEPTION:
elif ty == _D2HMsgType.KERNEL_EXCEPTION:
self._serve_exception(rpc_wrapper, user_exception_map)
elif msg == _D2HMsgType.KERNEL_FINISHED:
elif ty == _D2HMsgType.KERNEL_FINISHED:
return
else:
raise IOError("Incorrect request from device: "+str(msg))
raise IOError("Incorrect request from device: "+str(ty))
def send_link_message(self, data):
self._write(struct.pack(
">lb", 0x5a5a5a5a, _H2DMsgType.LINK_MESSAGE.value))
self._write(data)
def get_log(self):
self._write_header(9, _H2DMsgType.LOG_REQUEST)
length, ty = self._read_header()
if ty != _D2HMsgType.LOG_REPLY:
raise IOError("Incorrect request from device: "+str(ty))
r = ""
for i in range(length - 9):
c = struct.unpack("B", self.read(1))[0]
if c:
r += chr(c)
return r

View File

@ -17,15 +17,12 @@ class Comm(CommGeneric, AutoDB):
def open(self):
if hasattr(self, "port"):
return
self.port = serial.serial_for_url(self.serial_dev, baudrate=115200)
self.port.flush()
self.set_remote_baud(self.baud_rate)
self.set_baud(self.baud_rate)
self.port = serial.serial_for_url(self.serial_dev,
baudrate=self.baud_rate)
def close(self):
if not hasattr(self, "port"):
return
self.set_remote_baud(115200)
self.port.close()
del self.port
@ -42,27 +39,3 @@ class Comm(CommGeneric, AutoDB):
written = self.port.write(data[pos:])
remaining -= written
pos += written
def set_baud(self, baud):
self.port.baudrate = baud
self.port.flush()
logger.debug("local baud rate set to %d", baud)
def set_remote_baud(self, baud):
self.send_link_message(struct.pack(">l", baud))
handshake = 0
fails = 0
while handshake < 4:
(recv, ) = struct.unpack("B", self.read(1))
if recv == 0x5a:
handshake += 1
else:
# FIXME: when loading immediately after a board reset,
# we erroneously get some zeros back.
logger.warning("unexpected sync character: %02x", recv)
handshake = 0
if recv != 0:
fails += 1
if fails > 3:
raise IOError("Baudrate ack failed")
logger.debug("remote baud rate set to %d", baud)

View File

@ -1,6 +1,6 @@
include $(MSCDIR)/software/common.mak
OBJECTS := isr.o elf_loader.o services.o comm_serial.o test_mode.o main.o
OBJECTS := isr.o elf_loader.o services.o session.o log.o test_mode.o kloader.o main.o
OBJECTS_KSUPPORT := exception_jmp.o exceptions.o rtio.o dds.o
# NOTE: this does not handle dependencies well. Run "make clean"
@ -8,7 +8,7 @@ OBJECTS_KSUPPORT := exception_jmp.o exceptions.o rtio.o dds.o
UNIPROCESSOR := $(shell printf "\#include <generated/csr.h>\nCSR_KERNEL_CPU_BASE" | $(CC_normal) $(CFLAGS) -E - | tail -n 1 | grep -c CSR_KERNEL_CPU_BASE)
ifeq ($(UNIPROCESSOR),0)
OBJECTS += mailbox.o kernelcpu.o ksupport_data.o
OBJECTS += mailbox.o ksupport_data.o
OBJECTS_KSUPPORT += mailbox.o bridge.o ksupport.o
CFLAGS += -DARTIQ_AMP
SERVICE_TABLE_INPUT = ksupport.elf

View File

@ -1,23 +0,0 @@
#ifndef __COMM_H
#define __COMM_H
#include <stdarg.h>
enum {
KERNEL_RUN_INVALID_STATUS,
KERNEL_RUN_FINISHED,
KERNEL_RUN_EXCEPTION,
KERNEL_RUN_STARTUP_FAILED
};
typedef int (*object_loader)(void *, int);
typedef int (*kernel_runner)(const char *, int *, long long int *);
void comm_serve(object_loader load_object, kernel_runner run_kernel);
void comm_rpc_va(int rpc_num, va_list args, int *eid, int *retval);
int comm_rpc(int rpc_num, ...);
void comm_log_va(const char *fmt, va_list args);
void comm_log(const char *fmt, ...);
#endif /* __COMM_H */

View File

@ -1,292 +0,0 @@
#include <stdarg.h>
#include <crc.h>
#include <uart.h>
#include <generated/csr.h>
#include "comm.h"
#ifndef ARTIQ_AMP
#include "exceptions.h"
#endif
/* host to device */
enum {
MSGTYPE_SET_BAUD_RATE = 1,
MSGTYPE_REQUEST_IDENT,
MSGTYPE_SWITCH_CLOCK,
MSGTYPE_LOAD_OBJECT,
MSGTYPE_RUN_KERNEL,
};
/* device to host */
enum {
MSGTYPE_MESSAGE_UNRECOGNIZED = 1,
MSGTYPE_LOG,
MSGTYPE_IDENT,
MSGTYPE_CLOCK_SWITCH_COMPLETED,
MSGTYPE_CLOCK_SWITCH_FAILED,
MSGTYPE_OBJECT_LOADED,
MSGTYPE_OBJECT_INCORRECT_LENGTH,
MSGTYPE_OBJECT_CRC_FAILED,
MSGTYPE_OBJECT_UNRECOGNIZED,
MSGTYPE_KERNEL_FINISHED,
MSGTYPE_KERNEL_STARTUP_FAILED,
MSGTYPE_KERNEL_EXCEPTION,
MSGTYPE_RPC_REQUEST,
};
static int receive_int(void)
{
unsigned int r;
int i;
r = 0;
for(i=0;i<4;i++) {
r <<= 8;
r |= (unsigned char)uart_read();
}
return r;
}
static char receive_char(void)
{
return uart_read();
}
static void send_llint(long long int x)
{
int i;
for(i=0;i<8;i++) {
uart_write((x & 0xff00000000000000LL) >> 56);
x <<= 8;
}
}
static void send_int(int x)
{
int i;
for(i=0;i<4;i++) {
uart_write((x & 0xff000000) >> 24);
x <<= 8;
}
}
static void send_sint(short int i)
{
uart_write((i >> 8) & 0xff);
uart_write(i & 0xff);
}
static void send_char(char c)
{
uart_write(c);
}
static void receive_sync(void)
{
char c;
int recognized;
recognized = 0;
while(recognized < 4) {
c = uart_read();
if(c == 0x5a)
recognized++;
else
recognized = 0;
}
}
static void receive_and_load_object(object_loader load_object)
{
int length;
int i;
unsigned char buffer[256*1024];
unsigned int crc;
length = receive_int();
if(length > sizeof(buffer)) {
send_char(MSGTYPE_OBJECT_INCORRECT_LENGTH);
return;
}
crc = receive_int();
for(i=0;i<length;i++)
buffer[i] = receive_char();
if(crc32(buffer, length) != crc) {
send_char(MSGTYPE_OBJECT_CRC_FAILED);
return;
}
if(load_object(buffer, length))
send_char(MSGTYPE_OBJECT_LOADED);
else
send_char(MSGTYPE_OBJECT_UNRECOGNIZED);
}
static void receive_and_run_kernel(kernel_runner run_kernel)
{
int length;
int i;
char kernel_name[256];
int r, eid;
long long int eparams[3];
length = receive_int();
if(length > (sizeof(kernel_name)-1)) {
send_char(MSGTYPE_OBJECT_INCORRECT_LENGTH);
return;
}
for(i=0;i<length;i++)
kernel_name[i] = receive_char();
kernel_name[length] = 0;
r = run_kernel(kernel_name, &eid, eparams);
switch(r) {
case KERNEL_RUN_FINISHED:
send_char(MSGTYPE_KERNEL_FINISHED);
break;
case KERNEL_RUN_EXCEPTION:
send_char(MSGTYPE_KERNEL_EXCEPTION);
send_int(eid);
for(i=0;i<3;i++)
send_llint(eparams[i]);
break;
case KERNEL_RUN_STARTUP_FAILED:
send_char(MSGTYPE_KERNEL_STARTUP_FAILED);
break;
default:
comm_log("BUG: run_kernel returned unexpected value '%d'", r);
break;
}
}
void comm_serve(object_loader load_object, kernel_runner run_kernel)
{
char msgtype;
while(1) {
receive_sync();
msgtype = receive_char();
if(msgtype == MSGTYPE_REQUEST_IDENT) {
send_char(MSGTYPE_IDENT);
send_int(0x41524f52); /* "AROR" - ARTIQ runtime on OpenRISC */
} else if(msgtype == MSGTYPE_LOAD_OBJECT)
receive_and_load_object(load_object);
else if(msgtype == MSGTYPE_RUN_KERNEL)
receive_and_run_kernel(run_kernel);
else if(msgtype == MSGTYPE_SET_BAUD_RATE) {
unsigned int ftw;
ftw = ((long long)receive_int() << 32LL)/(long long)identifier_frequency_read();
send_int(0x5a5a5a5a);
uart_sync();
uart_phy_tuning_word_write(ftw);
} else if(msgtype == MSGTYPE_SWITCH_CLOCK) {
rtiocrg_clock_sel_write(receive_char());
send_char(MSGTYPE_CLOCK_SWITCH_COMPLETED);
} else
send_char(MSGTYPE_MESSAGE_UNRECOGNIZED);
}
}
static int send_value(int type_tag, void *value)
{
char base_type;
int i, p;
int len;
base_type = type_tag;
send_char(base_type);
switch(base_type) {
case 'n':
return 0;
case 'b':
if(*(char *)value)
send_char(1);
else
send_char(0);
return 1;
case 'i':
send_int(*(int *)value);
return 4;
case 'I':
case 'f':
send_int(*(int *)value);
send_int(*((int *)value + 1));
return 8;
case 'F':
for(i=0;i<4;i++)
send_int(*((int *)value + i));
return 16;
case 'l':
len = *(int *)value;
p = 4;
for(i=0;i<len;i++)
p += send_value(type_tag >> 8, (char *)value + p);
send_char(0);
return p;
}
return 0;
}
void comm_rpc_va(int rpc_num, va_list args, int *eid, int *retval)
{
int type_tag;
send_char(MSGTYPE_RPC_REQUEST);
send_sint(rpc_num);
while((type_tag = va_arg(args, int)))
send_value(type_tag, type_tag == 'n' ? NULL : va_arg(args, void *));
send_char(0);
*eid = receive_int();
*retval = receive_int();
}
#ifndef ARTIQ_AMP
int comm_rpc(int rpc_num, ...)
{
va_list args;
int eid, retval;
va_start(args, rpc_num);
comm_rpc_va(rpc_num, args, &eid, &retval);
va_end(args);
if(eid != EID_NONE)
exception_raise(eid);
return retval;
}
#endif
void comm_log_va(const char *fmt, va_list args)
{
int len;
char outbuf[256];
int i;
len = vscnprintf(outbuf, sizeof(outbuf), fmt, args);
send_char(MSGTYPE_LOG);
send_sint(len);
for(i=0;i<len;i++)
send_char(outbuf[i]);
}
void comm_log(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
comm_log_va(fmt, args);
va_end(args);
}

View File

@ -1,6 +1,6 @@
#include <string.h>
#include "comm.h"
#include "log.h"
#include "elf_loader.h"
#define EI_NIDENT 16
@ -85,11 +85,11 @@ struct elf32_sym {
#define SANITIZE_OFFSET_SIZE(offset, size) \
if(offset > 0x10000000) { \
comm_log("Incorrect offset in ELF data"); \
log("Incorrect offset in ELF data"); \
return 0; \
} \
if((offset + size) > elf_length) { \
comm_log("Attempted to access past the end of ELF data"); \
log("Attempted to access past the end of ELF data"); \
return 0; \
}
@ -121,7 +121,7 @@ static int fixup(void *dest, int dest_length, struct elf32_rela *rela, void *tar
val = _target - (_dest + offset);
_dest[offset] = (_dest[offset] & 0xfc000000) | (val & 0x03ffffff);
} else
comm_log("Unsupported relocation type: %d", type);
log("Unsupported relocation type: %d", type);
return 1;
}
@ -141,15 +141,15 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data,
/* validate ELF */
GET_POINTER_SAFE(ehdr, struct elf32_ehdr, 0);
if(memcmp(ehdr->ident, elf_magic_header, sizeof(elf_magic_header)) != 0) {
comm_log("Incorrect ELF header");
log("Incorrect ELF header");
return 0;
}
if(ehdr->type != ET_REL) {
comm_log("ELF is not relocatable");
log("ELF is not relocatable");
return 0;
}
if(ehdr->machine != EM_OR1K) {
comm_log("ELF is for a different machine");
log("ELF is for a different machine");
return 0;
}
@ -190,7 +190,7 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data,
/* load .text section */
if(textsize > dest_length) {
comm_log(".text section is too large");
log(".text section is too large");
return 0;
}
memcpy(dest, (char *)elf_data + textoff, textsize);
@ -209,13 +209,13 @@ int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data,
name = (char *)elf_data + strtaboff + sym->name;
target = resolver(name);
if(target == NULL) {
comm_log("Undefined symbol: %s", name);
log("Undefined symbol: %s", name);
return 0;
}
if(!fixup(dest, dest_length, rela, target))
return 0;
} else {
comm_log("Unsupported relocation");
log("Unsupported relocation");
return 0;
}
}

View File

@ -1,6 +1,6 @@
#include <generated/csr.h>
#include "comm.h"
#include "log.h"
#include "exceptions.h"
#define MAX_EXCEPTION_CONTEXTS 64
@ -52,7 +52,7 @@ void exception_raise_params(int id,
stored_params[2] = p2;
exception_longjmp(exception_contexts[--ec_top].jb);
} else {
comm_log("ERROR: uncaught exception, ID=%d\n", id);
log("ERROR: uncaught exception, ID=%d\n", id);
while(1);
}
}

View File

@ -5,7 +5,7 @@ import sys
services = [
("syscalls", [
("rpc", "comm_rpc"),
("rpc", "rpc"),
("rtio_set_o", "rtio_set_o"),
("rtio_set_oe", "rtio_set_oe"),
("rtio_set_sensitivity", "rtio_set_sensitivity"),

View File

@ -1,24 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <generated/csr.h>
#include "mailbox.h"
#include "kernelcpu.h"
extern char _binary_ksupport_bin_start;
extern char _binary_ksupport_bin_end;
void kernelcpu_start(void *addr)
{
memcpy((void *)KERNELCPU_EXEC_ADDRESS, &_binary_ksupport_bin_start,
&_binary_ksupport_bin_end - &_binary_ksupport_bin_start);
mailbox_acknowledge();
mailbox_send(addr);
kernel_cpu_reset_write(0);
}
void kernelcpu_stop(void)
{
kernel_cpu_reset_write(1);
}

View File

@ -1,12 +0,0 @@
#ifndef __KERNELCPU_H
#define __KERNELCPU_H
#include <hw/common.h>
#define KERNELCPU_EXEC_ADDRESS 0x40020000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40024000
void kernelcpu_start(void *addr);
void kernelcpu_stop(void);
#endif /* __KERNELCPU_H */

111
soc/runtime/kloader.c Normal file
View File

@ -0,0 +1,111 @@
#include <string.h>
#include <generated/csr.h>
#include "log.h"
#include "mailbox.h"
#include "elf_loader.h"
#include "services.h"
#include "kloader.h"
static struct symbol symtab[128];
static int _symtab_count;
static char _symtab_strings[128*16];
static char *_symtab_strptr;
static void symtab_init(void)
{
memset(symtab, 0, sizeof(symtab));
_symtab_count = 0;
_symtab_strptr = _symtab_strings;
}
static int symtab_add(const char *name, void *target)
{
if(_symtab_count >= sizeof(symtab)/sizeof(symtab[0])) {
log("Too many provided symbols in object");
symtab_init();
return 0;
}
symtab[_symtab_count].name = _symtab_strptr;
symtab[_symtab_count].target = target;
_symtab_count++;
while(1) {
if(_symtab_strptr >= &_symtab_strings[sizeof(_symtab_strings)]) {
log("Provided symbol string table overflow");
symtab_init();
return 0;
}
*_symtab_strptr = *name;
_symtab_strptr++;
if(*name == 0)
break;
name++;
}
return 1;
}
int kloader_load(void *buffer, int length)
{
#ifdef ARTIQ_AMP
if(!kernel_cpu_reset_read()) {
log("BUG: attempted to load while kernel CPU running");
return 0;
}
#endif
symtab_init();
return load_elf(
resolve_service_symbol, symtab_add,
buffer, length, (void *)KERNELCPU_PAYLOAD_ADDRESS, 4*1024*1024);
}
kernel_function kloader_find(const char *name)
{
return find_symbol(symtab, name);
}
#ifdef ARTIQ_AMP
extern char _binary_ksupport_bin_start;
extern char _binary_ksupport_bin_end;
static void start_kernel_cpu(void *addr)
{
memcpy((void *)KERNELCPU_EXEC_ADDRESS, &_binary_ksupport_bin_start,
&_binary_ksupport_bin_end - &_binary_ksupport_bin_start);
mailbox_acknowledge();
mailbox_send(addr);
kernel_cpu_reset_write(0);
}
void kloader_start_bridge(void)
{
start_kernel_cpu(NULL);
}
void kloader_start_user_kernel(kernel_function k)
{
if(!kernel_cpu_reset_read()) {
log("BUG: attempted to start kernel CPU while already running (user kernel)");
return;
}
start_kernel_cpu((void *)k);
}
void kloader_start_idle_kernel(void)
{
if(!kernel_cpu_reset_read()) {
log("BUG: attempted to start kernel CPU while already running (idle kernel)");
return;
}
/* TODO */
}
void kloader_stop_kernel(void)
{
kernel_cpu_reset_write(1);
}
#endif

19
soc/runtime/kloader.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __KLOADER_H
#define __KLOADER_H
#define KERNELCPU_EXEC_ADDRESS 0x40020000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40024000
typedef void (*kernel_function)(void);
int kloader_load(void *buffer, int length);
kernel_function kloader_find(const char *name);
#ifdef ARTIQ_AMP
void kloader_start_bridge(void);
void kloader_start_idle_kernel(void);
void kloader_start_user_kernel(kernel_function k);
void kloader_stop_kernel(void);
#endif
#endif /* __KLOADER_H */

View File

@ -7,8 +7,10 @@
#include "rtio.h"
#include "dds.h"
/* for the prototypes for comm_rpc and comm_log */
#include "comm.h"
/* for the prototype for rpc() */
#include "session.h"
/* for the prototype for log() */
#include "log.h"
void exception_handler(unsigned long vect, unsigned long *sp);
void exception_handler(unsigned long vect, unsigned long *sp)
@ -59,7 +61,7 @@ int main(void)
while(1);
}
int comm_rpc(int rpc_num, ...)
int rpc(int rpc_num, ...)
{
struct msg_rpc_request request;
struct msg_rpc_reply *reply;
@ -83,7 +85,7 @@ int comm_rpc(int rpc_num, ...)
return retval;
}
void comm_log(const char *fmt, ...)
void log(const char *fmt, ...)
{
struct msg_log request;

41
soc/runtime/log.c Normal file
View File

@ -0,0 +1,41 @@
#include <stdarg.h>
#include <stdio.h>
#include "log.h"
static int buffer_index;
static char buffer[LOG_BUFFER_SIZE];
void log_va(const char *fmt, va_list args)
{
char outbuf[256];
int i, len;
len = vscnprintf(outbuf, sizeof(outbuf), fmt, args);
for(i=0;i<len;i++) {
buffer[buffer_index] = outbuf[i];
buffer_index = (buffer_index + 1) % LOG_BUFFER_SIZE;
}
buffer[buffer_index] = '\n';
buffer_index = (buffer_index + 1) % LOG_BUFFER_SIZE;
}
void log(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
log_va(fmt, args);
va_end(args);
}
void log_get(char *outbuf)
{
int i, j;
j = buffer_index + 1;
for(i=0;i<LOG_BUFFER_SIZE;i++) {
outbuf[i] = buffer[j];
j = (j + 1) % LOG_BUFFER_SIZE;
}
}

13
soc/runtime/log.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __LOG_H
#define __LOG_H
#include <stdarg.h>
#define LOG_BUFFER_SIZE 4096
void log_va(const char *fmt, va_list args);
void log(const char *fmt, ...);
void log_get(char *outbuf);
#endif /* __LOG_H */

View File

@ -8,153 +8,41 @@
#include <generated/csr.h>
#include "test_mode.h"
#include "comm.h"
#include "kernelcpu.h"
#include "elf_loader.h"
#include "exceptions.h"
#include "services.h"
#include "rtio.h"
#include "dds.h"
#include "session.h"
#ifdef ARTIQ_AMP
#include "mailbox.h"
#include "messages.h"
#endif
static struct symbol symtab[128];
static int _symtab_count;
static char _symtab_strings[128*16];
static char *_symtab_strptr;
static void symtab_init(void)
void comm_service(void)
{
memset(symtab, 0, sizeof(symtab));
_symtab_count = 0;
_symtab_strptr = _symtab_strings;
char *txdata;
int txlen;
static char rxdata;
static int rxpending;
int r, i;
if(!rxpending && uart_read_nonblock()) {
rxdata = uart_read();
rxpending = 1;
}
if(rxpending) {
r = session_input(&rxdata, 1);
if(r > 0)
rxpending = 0;
}
session_poll((void **)&txdata, &txlen);
if(txlen > 0) {
for(i=0;i<txlen;i++)
uart_write(txdata[i]);
session_ack(txlen);
}
}
static int symtab_add(const char *name, void *target)
static void regular_main(void)
{
if(_symtab_count >= sizeof(symtab)/sizeof(symtab[0])) {
comm_log("Too many provided symbols in object");
symtab_init();
return 0;
}
symtab[_symtab_count].name = _symtab_strptr;
symtab[_symtab_count].target = target;
_symtab_count++;
while(1) {
if(_symtab_strptr >= &_symtab_strings[sizeof(_symtab_strings)]) {
comm_log("Provided symbol string table overflow");
symtab_init();
return 0;
}
*_symtab_strptr = *name;
_symtab_strptr++;
if(*name == 0)
break;
name++;
}
return 1;
session_start();
while(1)
comm_service();
}
static int load_object(void *buffer, int length)
{
symtab_init();
return load_elf(
resolve_service_symbol, symtab_add,
buffer, length, (void *)KERNELCPU_PAYLOAD_ADDRESS, 4*1024*1024);
}
#ifdef ARTIQ_AMP
static int process_msg(struct msg_base *umsg, int *eid, long long int *eparams)
{
int i;
switch(umsg->type) {
case MESSAGE_TYPE_FINISHED:
return KERNEL_RUN_FINISHED;
case MESSAGE_TYPE_EXCEPTION: {
struct msg_exception *msg = (struct msg_exception *)umsg;
*eid = msg->eid;
for(i=0;i<3;i++)
eparams[i] = msg->eparams[i];
return KERNEL_RUN_EXCEPTION;
}
case MESSAGE_TYPE_RPC_REQUEST: {
struct msg_rpc_request *msg = (struct msg_rpc_request *)umsg;
struct msg_rpc_reply reply;
reply.type = MESSAGE_TYPE_RPC_REPLY;
comm_rpc_va(msg->rpc_num, msg->args, &reply.eid, &reply.retval);
mailbox_send_and_wait(&reply);
return KERNEL_RUN_INVALID_STATUS;
}
case MESSAGE_TYPE_LOG: {
struct msg_log *msg = (struct msg_log *)umsg;
comm_log(msg->fmt, msg->args);
return KERNEL_RUN_INVALID_STATUS;
}
default:
*eid = EID_INTERNAL_ERROR;
for(i=0;i<3;i++)
eparams[i] = 0;
return KERNEL_RUN_EXCEPTION;
}
}
#endif
typedef void (*kernel_function)(void);
static int run_kernel(const char *kernel_name, int *eid, long long int *eparams)
{
kernel_function k;
#ifdef ARTIQ_AMP
int r;
#else
void *jb;
#endif
k = find_symbol(symtab, kernel_name);
if(k == NULL) {
comm_log("Failed to find kernel entry point '%s' in object", kernel_name);
return KERNEL_RUN_STARTUP_FAILED;
}
#ifdef ARTIQ_AMP
kernelcpu_start(k);
while(1) {
struct msg_base *umsg;
umsg = mailbox_receive();
r = KERNEL_RUN_INVALID_STATUS;
if(umsg)
r = process_msg(umsg, eid, eparams);
if(r != KERNEL_RUN_INVALID_STATUS)
break;
}
kernelcpu_stop();
return r;
#else
jb = exception_push();
if(exception_setjmp(jb)) {
*eid = exception_getid(eparams);
return KERNEL_RUN_EXCEPTION;
} else {
dds_init();
rtio_init();
flush_cpu_icache();
k();
exception_pop(1);
return KERNEL_RUN_FINISHED;
}
#endif
}
static void blink_led(void)
{
@ -208,7 +96,7 @@ int main(void)
test_main();
} else {
puts("Entering regular mode.");
comm_serve(load_object, run_kernel);
regular_main();
}
return 0;
}

View File

@ -1,7 +1,7 @@
#include <string.h>
#include "elf_loader.h"
#include "comm.h"
#include "session.h"
#include "rtio.h"
#include "dds.h"
#include "exceptions.h"

503
soc/runtime/session.c Normal file
View File

@ -0,0 +1,503 @@
#include <string.h>
#include <stdarg.h>
#include <generated/csr.h>
#ifdef ARTIQ_AMP
#include "mailbox.h"
#include "messages.h"
#else
#include <system.h>
#include "exceptions.h"
#include "rtio.h"
#include "dds.h"
#endif
#include "log.h"
#include "kloader.h"
#include "exceptions.h"
#include "session.h"
#define BUFFER_IN_SIZE (32*1024)
#define BUFFER_OUT_SIZE (32*1024)
static int buffer_in_index;
/* The 9th byte (right after the header) of buffer_in must be aligned
* to a 32-bit boundary for elf_loader to work.
*/
static struct {
char padding[3];
char data[BUFFER_IN_SIZE];
} __attribute__((packed)) _buffer_in __attribute__((aligned(4)));
#define buffer_in _buffer_in.data
static int buffer_out_index;
static char buffer_out[BUFFER_OUT_SIZE];
static int get_in_packet_len(void)
{
int r;
memcpy(&r, &buffer_in[4], 4);
return r;
}
static int get_out_packet_len(void)
{
int r;
memcpy(&r, &buffer_out[4], 4);
return r;
}
static void submit_output(int len)
{
memset(&buffer_out[0], 0x5a, 4);
memcpy(&buffer_out[4], &len, 4);
buffer_out_index = 0;
}
static int user_kernel_state;
enum {
USER_KERNEL_NONE = 0,
USER_KERNEL_LOADED,
USER_KERNEL_RUNNING,
USER_KERNEL_WAIT_RPC /* < must come after _RUNNING */
};
void session_start(void)
{
buffer_in_index = 0;
buffer_out_index = 0;
memset(&buffer_out[4], 0, 4);
#ifdef ARTIQ_AMP
kloader_stop_kernel();
#endif
user_kernel_state = USER_KERNEL_NONE;
}
void session_end(void)
{
#ifdef ARTIQ_AMP
kloader_stop_kernel();
kloader_start_idle_kernel();
#endif
}
/* host to device */
enum {
REMOTEMSG_TYPE_LOG_REQUEST = 1,
REMOTEMSG_TYPE_IDENT_REQUEST,
REMOTEMSG_TYPE_SWITCH_CLOCK,
REMOTEMSG_TYPE_LOAD_OBJECT,
REMOTEMSG_TYPE_RUN_KERNEL,
REMOTEMSG_TYPE_RPC_REPLY
};
/* device to host */
enum {
REMOTEMSG_TYPE_LOG_REPLY = 1,
REMOTEMSG_TYPE_IDENT_REPLY,
REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED,
REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED,
REMOTEMSG_TYPE_LOAD_COMPLETED,
REMOTEMSG_TYPE_LOAD_FAILED,
REMOTEMSG_TYPE_KERNEL_FINISHED,
REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED,
REMOTEMSG_TYPE_KERNEL_EXCEPTION,
REMOTEMSG_TYPE_RPC_REQUEST,
};
static int add_rpc_value(int bi, int type_tag, void *value)
{
char base_type;
int obi, r;
int i, p;
int len;
obi = bi;
base_type = type_tag;
if((bi + 1) > BUFFER_OUT_SIZE)
return -1;
buffer_out[bi++] = base_type;
switch(base_type) {
case 'n':
return bi - obi;
case 'b':
if((bi + 1) > BUFFER_OUT_SIZE)
return -1;
if(*(char *)value)
buffer_out[bi++] = 1;
else
buffer_out[bi++] = 0;
return bi - obi;
case 'i':
if((bi + 4) > BUFFER_OUT_SIZE)
return -1;
memcpy(&buffer_out[bi], value, 4);
bi += 4;
return bi - obi;
case 'I':
case 'f':
if((bi + 8) > BUFFER_OUT_SIZE)
return -1;
memcpy(&buffer_out[bi], value, 8);
bi += 8;
return bi - obi;
case 'F':
if((bi + 16) > BUFFER_OUT_SIZE)
return -1;
memcpy(&buffer_out[bi], value, 16);
bi += 16;
return bi - obi;
case 'l':
len = *(int *)value;
p = 4;
for(i=0;i<len;i++) {
r = add_rpc_value(bi, type_tag >> 8, (char *)value + p);
if(r < 0)
return r;
bi += r;
p += r;
}
if((bi + 1) > BUFFER_OUT_SIZE)
return -1;
buffer_out[bi++] = 0;
return bi - obi;
}
return -1;
}
static int send_rpc_request(int rpc_num, va_list args)
{
int r;
int bi = 8;
int type_tag;
buffer_out[bi++] = REMOTEMSG_TYPE_RPC_REQUEST;
memcpy(&buffer_out[bi], &rpc_num, 4);
bi += 4;
while((type_tag = va_arg(args, int))) {
r = add_rpc_value(bi, type_tag,
type_tag == 'n' ? NULL : va_arg(args, void *));
if(bi < 0)
return 0;
bi += r;
}
if((bi + 1) > BUFFER_OUT_SIZE)
return 0;
buffer_out[bi++] = 0;
submit_output(bi);
return 1;
}
#ifndef ARTIQ_AMP
static int rpc_reply_eid;
static int rpc_reply_retval;
int rpc(int rpc_num, ...)
{
va_list args;
va_start(args, rpc_num);
send_rpc_request(rpc_num, args);
va_end(args);
user_kernel_state = USER_KERNEL_WAIT_RPC;
while(user_kernel_state == USER_KERNEL_WAIT_RPC)
comm_service();
if(rpc_reply_eid != EID_NONE)
exception_raise(rpc_reply_eid);
return rpc_reply_retval;
}
/* assumes output buffer is empty when called */
static void run_kernel_up(kernel_function k)
{
void *jb;
int eid;
long long eparams[3];
jb = exception_push();
if(exception_setjmp(jb)) {
eid = exception_getid(eparams);
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION;
memcpy(&buffer_out[9], &eid, 4);
memcpy(&buffer_out[13], eparams, 3*8);
submit_output(9+4+3*8);
} else {
dds_init();
rtio_init();
flush_cpu_icache();
k();
exception_pop(1);
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED;
submit_output(9);
}
}
#endif /* !ARTIQ_AMP */
static int process_input(void)
{
switch(buffer_in[8]) {
case REMOTEMSG_TYPE_LOG_REQUEST:
#if (LOG_BUFFER_SIZE + 9) > BUFFER_OUT_SIZE
#error Output buffer cannot hold the log buffer
#endif
buffer_out[8] = REMOTEMSG_TYPE_LOG_REPLY;
log_get(&buffer_out[9]);
submit_output(9 + LOG_BUFFER_SIZE);
break;
case REMOTEMSG_TYPE_IDENT_REQUEST:
buffer_out[8] = REMOTEMSG_TYPE_IDENT_REPLY;
buffer_out[9] = 'A';
buffer_out[10] = 'R';
buffer_out[11] = 'O';
buffer_out[12] = 'R';
submit_output(13);
break;
case REMOTEMSG_TYPE_SWITCH_CLOCK:
if(user_kernel_state >= USER_KERNEL_RUNNING) {
log("Attempted to switch RTIO clock while kernel running");
buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED;
submit_output(9);
break;
}
rtiocrg_clock_sel_write(buffer_in[9]);
buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED;
submit_output(9);
break;
case REMOTEMSG_TYPE_LOAD_OBJECT:
if(user_kernel_state >= USER_KERNEL_RUNNING) {
log("Attempted to load new kernel while already running");
buffer_out[8] = REMOTEMSG_TYPE_LOAD_FAILED;
submit_output(9);
break;
}
if(kloader_load(&buffer_in[9], get_in_packet_len() - 8)) {
buffer_out[8] = REMOTEMSG_TYPE_LOAD_COMPLETED;
user_kernel_state = USER_KERNEL_LOADED;
} else
buffer_out[8] = REMOTEMSG_TYPE_LOAD_FAILED;
submit_output(9);
break;
case REMOTEMSG_TYPE_RUN_KERNEL: {
kernel_function k;
if(user_kernel_state != USER_KERNEL_LOADED) {
log("Attempted to run kernel while not in the LOADED state");
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED;
submit_output(9);
break;
}
if((buffer_in_index + 1) > BUFFER_OUT_SIZE) {
log("Kernel name too long");
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED;
submit_output(9);
break;
}
buffer_in[buffer_in_index] = 0;
k = kloader_find((char *)&buffer_in[9]);
if(k == NULL) {
log("Failed to find kernel entry point '%s' in object", &buffer_in[9]);
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED;
submit_output(9);
break;
}
#ifdef ARTIQ_AMP
kloader_start_user_kernel(k);
#else
run_kernel_up(k);
#endif
user_kernel_state = USER_KERNEL_RUNNING;
break;
}
case REMOTEMSG_TYPE_RPC_REPLY: {
#ifdef ARTIQ_AMP
struct msg_rpc_reply reply;
#endif
if(user_kernel_state != USER_KERNEL_WAIT_RPC) {
log("Unsolicited RPC reply");
return 0;
}
#ifdef ARTIQ_AMP
reply.type = MESSAGE_TYPE_RPC_REPLY;
memcpy(&reply.eid, &buffer_in[9], 4);
memcpy(&reply.retval, &buffer_in[13], 4);
mailbox_send_and_wait(&reply);
#else
memcpy(&rpc_reply_eid, &buffer_in[9], 4);
memcpy(&rpc_reply_retval, &buffer_in[13], 4);
#endif
user_kernel_state = USER_KERNEL_RUNNING;
break;
}
default:
return 0;
}
return 1;
}
/* Returns -1 in case of irrecoverable error
* (the session must be dropped and session_end called)
*/
int session_input(void *data, int len)
{
unsigned char *_data = data;
int consumed;
consumed = 0;
while(len > 0) {
/* Make sure the output buffer is available for any reply
* we might need to send. */
if(get_out_packet_len() != 0)
return consumed;
if(buffer_in_index < 4) {
/* synchronizing */
if(_data[consumed] == 0x5a)
buffer_in[buffer_in_index++] = 0x5a;
else
buffer_in_index = 0;
consumed++; len--;
} else if(buffer_in_index < 8) {
/* receiving length */
buffer_in[buffer_in_index++] = _data[consumed];
consumed++; len--;
} else {
/* receiving payload */
int packet_len;
int count;
packet_len = get_in_packet_len();
if(packet_len > BUFFER_IN_SIZE)
return -1;
count = packet_len - buffer_in_index;
if(count > len)
count = len;
memcpy(&buffer_in[buffer_in_index], &_data[consumed], count);
buffer_in_index += count;
if(buffer_in_index == packet_len) {
if(!process_input())
return -1;
buffer_in_index = 0;
}
consumed += count; len -= count;
}
}
return consumed;
}
#ifdef ARTIQ_AMP
/* assumes output buffer is empty when called */
static void process_kmsg(struct msg_base *umsg)
{
if(user_kernel_state != USER_KERNEL_RUNNING) {
log("Received message from kernel CPU while not in running state");
return;
}
switch(umsg->type) {
case MESSAGE_TYPE_FINISHED:
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED;
submit_output(9);
kloader_stop_kernel();
user_kernel_state = USER_KERNEL_LOADED;
break;
case MESSAGE_TYPE_EXCEPTION: {
struct msg_exception *msg = (struct msg_exception *)umsg;
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION;
memcpy(&buffer_out[9], &msg->eid, 4);
memcpy(&buffer_out[13], msg->eparams, 3*8);
submit_output(9+4+3*8);
kloader_stop_kernel();
user_kernel_state = USER_KERNEL_LOADED;
break;
}
case MESSAGE_TYPE_RPC_REQUEST: {
struct msg_rpc_request *msg = (struct msg_rpc_request *)umsg;
send_rpc_request(msg->rpc_num, msg->args);
user_kernel_state = USER_KERNEL_WAIT_RPC;
break;
}
case MESSAGE_TYPE_LOG: {
struct msg_log *msg = (struct msg_log *)umsg;
log(msg->fmt, msg->args);
break;
}
default: {
int eid;
log("Received invalid message type from kernel CPU");
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_EXCEPTION;
eid = EID_INTERNAL_ERROR;
memcpy(&buffer_out[9], &eid, 4);
memset(&buffer_out[13], 0, 3*8);
submit_output(9+4+3*8);
kloader_stop_kernel();
user_kernel_state = USER_KERNEL_LOADED;
break;
}
}
}
#endif /* ARTIQ_AMP */
void session_poll(void **data, int *len)
{
int l;
l = get_out_packet_len();
#ifdef ARTIQ_AMP
/* If the output buffer is available,
* check if the kernel CPU has something to transmit.
*/
if(l == 0) {
struct msg_base *umsg;
umsg = mailbox_receive();
if(umsg) {
process_kmsg(umsg);
mailbox_acknowledge();
}
l = get_out_packet_len();
}
#endif
*len = l - buffer_out_index;
*data = &buffer_out[buffer_out_index];
}
void session_ack(int len)
{
buffer_out_index += len;
if(buffer_out_index >= get_out_packet_len()) {
memset(&buffer_out[4], 0, 4);
buffer_out_index = 0;
}
}

14
soc/runtime/session.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __SESSION_H
#define __SESSION_H
void session_start(void);
void session_end(void);
int session_input(void *data, int len);
void session_poll(void **data, int *len);
void session_ack(int len);
int rpc(int rpc_num, ...);
void comm_service(void);
#endif /* __SESSION_H */

View File

@ -12,7 +12,7 @@
#ifdef ARTIQ_AMP
#include "kernelcpu.h"
#include "kloader.h"
#include "mailbox.h"
#include "messages.h"
@ -20,7 +20,7 @@ static void amp_bridge_init(void)
{
struct msg_base *umsg;
kernelcpu_start(NULL);
kloader_start_bridge();
while(1) {
umsg = mailbox_wait_and_receive();