From ed4bf0214f53df5b8c255ed6f575fcdc6bf361d3 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 11:48:40 +0100 Subject: [PATCH 01/15] ATLEDGE-15 basic analog functions Adding analogWriteResolution and analogReadResolution Signed-off-by: Dan O'Donovan Signed-off-by: David Hunt --- cores/arduino/wiring_analog.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index a176f2f7..73fcc2a1 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -26,12 +26,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /* Standard Arduino PWM resolution */ static int _writeResolution = 8; +static int _readResolution = 10; -void analogResolution(int res) + +void analogWriteResolution(int res) { _writeResolution = res; } +void analogReadResolution(int res) +{ + _readResolution = res; +} + static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) { if (from == to) From 57550ebb85d6f69dc52cefcf38f47a7995f708b3 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 11:52:19 +0100 Subject: [PATCH 02/15] ATLEDGE-83 Component Framework integration into bare metal Author: Dan O'Donovan Date: Fri May 15 14:46:08 2015 +0100 Framework code extracted from the following commit ID in Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 This brings the necessary library code into the source tree, but not yet integrated in the Arduino run-time environment (to follow shortly). Signed-off-by: Dan O'Donovan Signed-off-by: David Hunt --- system/libarc32_edu/Makefile | 13 +- .../libarc32_edu/framework/include/cfw/cfw.h | 202 ++++++++++++++ .../framework/include/cfw/cfw_client.h | 92 +++++++ .../framework/include/cfw/cfw_debug.h | 7 + .../framework/include/cfw/cfw_internal.h | 30 ++ .../framework/include/cfw/cfw_messages.h | 102 +++++++ .../framework/include/cfw/cfw_service.h | 140 ++++++++++ .../framework/include/infra/ipc.h | 79 ++++++ .../framework/include/infra/ipc_requests.h | 13 + .../framework/include/infra/log.h | 151 ++++++++++ .../framework/include/infra/log_backends.h | 80 ++++++ .../framework/include/infra/message.h | 102 +++++++ .../framework/include/infra/port.h | 144 ++++++++++ .../framework/include/log_modules | 4 + system/libarc32_edu/framework/include/os/os.h | 259 ++++++++++++++++++ .../framework/include/os/os_types.h | 82 ++++++ .../framework/include/panic_api.h | 1 + .../libarc32_edu/framework/include/pf_init.h | 32 +++ .../libarc32_edu/framework/include/platform.h | 52 ++++ .../framework/include/services/gpio_service.h | 218 +++++++++++++++ .../framework/include/util/list.h | 94 +++++++ .../framework/src/cfw/cfw_debug.c | 42 +++ .../framework/src/cfw/client_api.c | 96 +++++++ .../framework/src/cfw/service_api.c | 148 ++++++++++ .../framework/src/cfw/service_manager_proxy.c | 256 +++++++++++++++++ system/libarc32_edu/framework/src/infra/ipc.c | 115 ++++++++ .../framework/src/infra/ipc_callback.c | 53 ++++ .../libarc32_edu/framework/src/infra/port.c | 250 +++++++++++++++++ system/libarc32_edu/framework/src/os/os.c | 94 +++++++ .../framework/src/services/gpio_service_api.c | 81 ++++++ system/libarc32_edu/framework/src/util/list.c | 116 ++++++++ 31 files changed, 3145 insertions(+), 3 deletions(-) create mode 100644 system/libarc32_edu/framework/include/cfw/cfw.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_client.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_debug.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_internal.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_messages.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_service.h create mode 100644 system/libarc32_edu/framework/include/infra/ipc.h create mode 100644 system/libarc32_edu/framework/include/infra/ipc_requests.h create mode 100644 system/libarc32_edu/framework/include/infra/log.h create mode 100644 system/libarc32_edu/framework/include/infra/log_backends.h create mode 100644 system/libarc32_edu/framework/include/infra/message.h create mode 100644 system/libarc32_edu/framework/include/infra/port.h create mode 100644 system/libarc32_edu/framework/include/log_modules create mode 100644 system/libarc32_edu/framework/include/os/os.h create mode 100644 system/libarc32_edu/framework/include/os/os_types.h create mode 100644 system/libarc32_edu/framework/include/panic_api.h create mode 100644 system/libarc32_edu/framework/include/pf_init.h create mode 100644 system/libarc32_edu/framework/include/platform.h create mode 100644 system/libarc32_edu/framework/include/services/gpio_service.h create mode 100644 system/libarc32_edu/framework/include/util/list.h create mode 100644 system/libarc32_edu/framework/src/cfw/cfw_debug.c create mode 100644 system/libarc32_edu/framework/src/cfw/client_api.c create mode 100644 system/libarc32_edu/framework/src/cfw/service_api.c create mode 100644 system/libarc32_edu/framework/src/cfw/service_manager_proxy.c create mode 100644 system/libarc32_edu/framework/src/infra/ipc.c create mode 100644 system/libarc32_edu/framework/src/infra/ipc_callback.c create mode 100644 system/libarc32_edu/framework/src/infra/port.c create mode 100644 system/libarc32_edu/framework/src/os/os.c create mode 100644 system/libarc32_edu/framework/src/services/gpio_service_api.c create mode 100644 system/libarc32_edu/framework/src/util/list.c diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index e9143818..21348969 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -4,6 +4,12 @@ ASM_SRC+=$(wildcard $(PWD)/common/*.S) C_SRC+=$(wildcard $(PWD)/bootcode/*.c) C_SRC+=$(wildcard $(PWD)/drivers/*.c) C_SRC+=$(wildcard $(PWD)/common/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/services/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/cfw/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/infra/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/util/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/os/*.c) ARCH=ARC CC=arc-elf32-gcc @@ -25,13 +31,14 @@ TARGET_ELF=$(TARGET).elf TARGET_BIN=$(TARGET).bin HWFLAGS=-mARCv2EM -mav2em -mlittle-endian -CFGFLAGS=-DCONFIG_SOC_GPIO_32 + +CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DHAS_SHARED_MEM OPTFLAGS=-g -Os -Wall -INCLUDES=-Icommon -Ibootcode +INCLUDES=-Icommon -Ibootcode -Iframework/include EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) -LDFLAGS=-nostartfiles -nodefaultlibs -nostdlib -static \ +LDFLAGS=-nostartfiles -static \ -Wl,-X -Wl,-N -Wl,-mARCv2EM -Wl,-marcelf -Wl,--gc-sections \ -Wl,-Map,$(TARGET_ELF).map \ -Wl,--whole-archive $(TARGET_LIB) -Wl,--no-whole-archive \ diff --git a/system/libarc32_edu/framework/include/cfw/cfw.h b/system/libarc32_edu/framework/include/cfw/cfw.h new file mode 100644 index 00000000..d575a532 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw.h @@ -0,0 +1,202 @@ +/** + * \defgroup cfw CFW: Component Framework + * \brief The component framework is the main building block for a custom application. + * + * It consists of: + * - Service APIs + * - Service Manager API + * - Main loop handler that wraps the messaging and exposes a functional / + * callback interface to the application. + * + * An application always need to create a thread managed by the framework in + * order to be interfaced with it. + * It could have other threads for its specific need and would have to manage + * communication with the framework thread for interface with the rest of the platform. + * @{ + */ + +#ifndef _CFW_H_ +#define _CFW_H_ + +#include +#include + +#include "os/os.h" +#include "util/list.h" +#include "infra/port.h" +#include "infra/message.h" + +struct cfw_message { + struct message m; + + /* The two following fields are specific to framework + * messages, and could be avoided / re-used for other + * types of messages. + */ + + /** The belonging connection of the message */ + void * conn; + /** The private data passed with the request. */ + void * priv; +}; + +#define CFW_MESSAGE_ID(msg) MESSAGE_ID(&(msg)->m) +#define CFW_MESSAGE_SRC(msg) MESSAGE_SRC(&(msg)->m) +#define CFW_MESSAGE_DST(msg) MESSAGE_DST(&(msg)->m) +#define CFW_MESSAGE_LEN(msg) MESSAGE_LEN(&(msg)->m) +#define CFW_MESSAGE_TYPE(msg) MESSAGE_TYPE(&(msg)->m) +#define CFW_MESSAGE_CONN(msg) (msg)->conn +#define CFW_MESSAGE_PRIV(msg) (msg)->priv +#define CFW_MESSAGE_HEADER(msg) (&(msg)->m) + +struct cfw_rsp_message { + /** Common message header */ + struct cfw_message header; + /** response status code.*/ + int status; +}; + +/** + * Message handler definition. + */ +typedef void (*handle_msg_cb_t)(struct cfw_message *, void *); + +typedef void * cfw_handle_t; + +/** + * \struct svc_client_handle_t + * \brief the structure used to manage a connection between a client + * and a service. + * + * This structure is returned in \ref cfw_open_conn_req_msg_t in + * the client_handle field + */ +typedef struct svc_client_handle_ { + /** Port to attain the service. */ + uint16_t port; + /** Framework handle. */ + cfw_handle_t cfw_handle; + /** Pointer to store the server-side connection handle. + * Passed in the conn field of \ref struct cfw_message for request messages + */ + void * server_handle; +} svc_client_handle_t; + +/** + * Services id definitions + * + * Keep them numbered manually to avoid shifting on + * removal/addition of services + */ +enum { + FRAMEWORK_SERVICE_ID = 1, + TEST2_SERVICE_ID = 2, + TEST_SERVICE_ID = 3, + BLE_SERVICE_ID = 4, + BLE_CORE_SERVICE_ID = 5, +}; + +/** + * Logging macro. + */ +/* + * Function Prototypes + * + */ +void cfw_log(char * fmt, ...); + +/** + * \brief Allocate a memory buffer + * it is mandatory to free the memory with \ref cfw_free + * + * \param size size of the memory to allocate + * + * \param err (out): execution status: + * -# E_OS_OK : memory was allocated + * -# E_OS_ERR: unable to allocate memory + * + * \return pointer to the allocated memory or NULL if failed. + */ +void * cfw_alloc(int size, OS_ERR_TYPE * err); + +struct cfw_message * cfw_alloc_message(int size, OS_ERR_TYPE * err); + +/** + * \brief free a block of memory allocated with \ref cfw_alloc + * + * \param ptr the block address to free. + * + * \param err (out): execution status: + * -# E_OS_OK : memory was freed + * -# E_OS_ERR: unable to free memory + */ +void cfw_free(void * ptr, OS_ERR_TYPE * err); + +/** + * \brief free a message. + * + * This function will take care to send the freeing request to + * the core that allocated the message. (based on the source + * port) + * + * \param ptr the message to be freed. + */ +void cfw_msg_free(struct cfw_message * msg); + +/** + * Create a copy of a given message. + * + * \param msg the message to be cloned. + * \return the cloned message + */ +struct cfw_message * cfw_clone_message(struct cfw_message * msg); + + +/** + * Send a message. + * The message should be filed with the destination port, + * source port, message identifier, etc... + * + * \param msg the parameter to send. + */ +int _cfw_send_message(struct cfw_message * msg); + +/** + * This macro conveniently casts the parameter to a message header pointer. + */ +#define cfw_send_message(_msg_) _cfw_send_message((struct cfw_message*) (_msg_)) + +/** + * get the local identifier of the service. + */ +int _find_service(int); + +/** + * Get the port id of the given service. + */ +int _cfw_get_service_port(int); + + +/* + * Multi cpu APIs. + */ + +/** + * \brief set the callback to be used to send a message to the given cpu. + * + * \param cpu_id the cpu id for which we want to set the handler. + * \param handler the callback used to send a message to this cpu. + */ +void set_cpu_message_sender(int cpu_id, int (*handler)(struct cfw_message * msg)); + +/** + * \brief set the callback to be used to request free a message to a cpu. + * + * \param cpu_id the cpu id for which we want to set the handler. + * \param handler the callback used to request message free on. + */ +void set_cpu_free_handler(int cpu_id, void (*free_handler)(void *)); + +#endif /* #ifndef _CFW_H_ */ + +/**@}*/ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_client.h b/system/libarc32_edu/framework/include/cfw/cfw_client.h new file mode 100644 index 00000000..02d69149 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_client.h @@ -0,0 +1,92 @@ +/** + * \addtogroup cfw + * @{ + * \defgroup cfw_client CFW Client API + * @{ + * \brief Definition of the structure and functions needed by CFW clients, i.e. + * for users of services. + */ + +#ifndef __CFW_CLIENT_H__ +#define __CFW_CLIENT_H__ +#include "cfw/cfw.h" + +/** + * Create a handle to the component framework. + * This handle is to be used for all other requests + * to the component framework + * + * Implementation is different in the master and the slave contexts. + * The master context will be pseudo-synchronous, while the slave + * implementation will actually pass a message to the master context + * in order to register a new client. + * + * \param cb the callback that will be called for each message reception + * \param param the param passed along with the message to the callback + */ +cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void * param); + + +/** + * Allocate a request message for a service. + * This will fill the needed common message fields needed to interact + * with a service. + */ +struct cfw_message * cfw_alloc_message_for_service(svc_client_handle_t * h, + int msg_id, int msg_size, void * priv); + + +/** + * Open a connection to the specified service. + * The connection handle is returned in the OPEN_CONNECTION + * confirmation message. + * + * \msc + * Client,"FW API","Service Manager"; + * + * Client=>"FW API" [label="cfw_open_connection", URL="\ref cfw_open_connection"]; + * "FW API"->"Service Manager" [label="CFW_OPEN_SERVICE REQ", URL="\ref cfw_open_conn_req_msg_t", ID="1"]; + * Client<<"FW API" ; + * Client<-"Service Manager" [label="CFW_OPEN_SERVICE CNF", URL="\ref cfw_open_conn_rsp_msg_t", ID="2"]; + + * \endmsc + * + * \param handle the handle to the component framework, as returned by @cfw_init + * \param service_id the unique service identifier. + * \return 0 if request succeeded and != 0 if error occured. + */ +int cfw_open_service(cfw_handle_t handle, int service_id, void *param); + +/** + * Closes a connection. + * + * \param handle the client handle representing the connection + * \param priv an opaque data passed back in the close response message + * \ return 0 if request succeeded and !=0 otherwise + */ +int cfw_close_service(svc_client_handle_t *handle, void *priv); + +/** + * Register to service events. + * + * \param handle the service connection handle, as returned in the \ref cfw_open_conn_rsp_msg_t + * \param msg_ids the array of event message ids to register to. + * \param size the size of the msg_ids array. + * \param param the void * private param that will be returned in the \ref cfw_register_evt_rsp_msg_t + */ +int cfw_register_events(svc_client_handle_t * handle, int * msg_ids, int size, void*param); + +/** + * Register to service availability events. + * Whenever a service is registered, the clients that registered to service_available will + * receive a cfw_svc_available_evt_msg_t message whenever a service is available. + * + * \param handle the framework handle. + * \param param the private param sent back with the response message. + */ +int cfw_register_svc_available(cfw_handle_t handle, void *param); + +#endif + +/**@} @}*/ + diff --git a/system/libarc32_edu/framework/include/cfw/cfw_debug.h b/system/libarc32_edu/framework/include/cfw/cfw_debug.h new file mode 100644 index 00000000..40a44657 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_debug.h @@ -0,0 +1,7 @@ +#ifndef __CFW_DEBUG_H__ +#define __CFW_DEBUG_H__ + +char * cfw_get_msg_type_str(struct cfw_message *msg); +void cfw_dump_message(struct cfw_message * msg); + +#endif /* __CFW_DEBUG_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_internal.h b/system/libarc32_edu/framework/include/cfw/cfw_internal.h new file mode 100644 index 00000000..2ae1276e --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_internal.h @@ -0,0 +1,30 @@ +#ifndef __CFW_INTERNAL_H__ +#define __CFW_INTERNAL_H__ + +#include "cfw/cfw.h" +#include "cfw/cfw_service.h" +#include "infra/ipc_requests.h" + +#define MAX_SERVICES 16 +typedef struct { + handle_msg_cb_t handle_msg; + void *data; + uint16_t client_port_id; +}_cfw_handle_t; + +/** + * This function is called when an sync IPC request is issued + * from a secondary CPU. + * + * \param cpu_id the cpu_id that originated the request + * \param request the request id. + * \param param1 first param + * \param param2 second param + * \param ptr third param + * + * \return the value to be passed as response to the requestor + */ +int handle_ipc_sync_request(int cpu_id, int request, int param1, + int param2, void * ptr); + +#endif /* __CFW_INTERNAL_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_messages.h b/system/libarc32_edu/framework/include/cfw/cfw_messages.h new file mode 100644 index 00000000..e88cf39f --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_messages.h @@ -0,0 +1,102 @@ +#ifndef __CFW_MESSAGES_H__ +#define __CFW_MESSAGES_H__ + +#include +#include + +enum +{ + MSG_ID_CFW_OPEN_SERVICE = 0x100, + MSG_ID_CFW_SERVICE_REGISTER, + MSG_ID_CFW_REGISTER_SVC_AVAIL, + MSG_ID_CFW_SVC_AVAIL_EVT, + MSG_ID_CFW_REGISTER_EVT, + MSG_ID_CFW_UNREGISTER_EVT, + MSG_ID_CFW_ALLOC_PORT, + MSG_ID_CFW_CLOSE_SERVICE, + MSG_ID_CFW_LAST +}; + +/** + * \struct cfw_open_conn_req_msg_t + * request message sent by \ref cfw_open_connection API. + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** service to open connection to */ + int service_id; + /** client side service handle */ + void *client_handle; +} cfw_open_conn_req_msg_t; + +/** + * \struct cfw_open_conn_cnf_msg_t + * response message to the \ref cfw_open_connection API. + */ +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; + /** port to attain this service */ + uint16_t port; + /** cpu_id of service */ + int cpu_id; + /** service handle for accessing the service */ + void * svc_server_handle; + /** client side service handle as passed in + * \ref cfw_open_conn_req_msg_t */ + void * client_handle; +} cfw_open_conn_rsp_msg_t; + +/** + * \struct cfw_close_conn_req_msg_t + * request message sent by \ref cfw_close_connection API. + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** service to open connection to */ + void * inst; +} cfw_close_conn_req_msg_t; + +/** + * \struct cfw_close_conn_cnf_msg_t + * response message to the \ref cfw_close_connection API. + */ +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_close_conn_rsp_msg_t; + +typedef struct { + /** common message header */ + struct cfw_message header; + /** indication message identifier. + * all subsequent indication with this identifier will be sent + * to the src port of this request message. + */ + int evt; +} cfw_register_evt_req_msg_t; + +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_register_evt_rsp_msg_t; + + +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_register_svc_avail_rsp_msg_t; +/** + * This message is sent to clients that called \ref cfw_register_svc_available api. + * it notifies of the availability of a service. + * + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** Service id of the newly available service. */ + int service_id; +} cfw_svc_available_evt_msg_t; +#endif /* __CFW_MESSAGE_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_service.h b/system/libarc32_edu/framework/include/cfw/cfw_service.h new file mode 100644 index 00000000..ee19efe9 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_service.h @@ -0,0 +1,140 @@ +/** + * \addtogroup cfw + * @{ + * \defgroup cfw_service CFW Service API + * @{ + * \brief Definition of the structure and functions used by CFW services implementation. + */ + +#ifndef __CFW_SERVICE_H_ +#define __CFW_SERVICE_H_ +#include "os/os.h" +#include "cfw/cfw.h" +#include "cfw/cfw_internal.h" +#include "infra/message.h" +#include "infra/port.h" + +struct service; +struct _port; + +/** + * \struct conn_handle_t + * + * This structure is used by the framework to maintain a connection + * between a client and a service. + */ +typedef struct cfw_conn_ { + /** Port to reach the client */ + uint16_t client_port; + /** The service pointer.*/ + struct service * svc; + /** pointer for the service to store client-specific data. */ + void * priv_data; + /** Pointer to store the client-side specific handle. */ + void * client_handle; +} conn_handle_t; + +/** + * Internal definition of a service. + */ +typedef struct service { + uint16_t port_id; + /** Identifier of the service.*/ + int service_id; + /** This callback is called when a client connects. + * called in the context of the service manager. + */ + void (*client_connected)(conn_handle_t *); + /** This callback is called when a client disconnects. + * Called in the context of the service manager. + */ + void (*client_disconnected)(conn_handle_t *); + /* This callback is called when the registered event + * list is modified. + * Called in the context of the service manager. + */ + void (*registered_events_changed)(conn_handle_t *); +} service_t; + +int _cfw_register_service(service_t * svc); +int _cfw_deregister_service(cfw_handle_t handle, service_t * svc); +#ifdef CFW_MULTI_CPU_SUPPORT +void _cfw_init_proxy(T_QUEUE queue, void * p, service_t**s, uint16_t svc_mgr_port); +#endif +service_t * cfw_get_service(int service_id); + + + +/** + * Allocate and build a response message for a specific request. + * + * \param req the request message + * \param msg_id the id of the response message + * \param size the size of the response message + * + * \return the allocated and initialized response message + */ +struct cfw_rsp_message * cfw_alloc_rsp_msg(struct cfw_message *req, int msg_id, int size); + +/** + * Allocate an event message. + * + * \param svc the service allocating the event message. + * \param msg_id the message identifier of the event. + * \param size the size of the message to be allocated. + * + * \return the allocated event message. + */ +struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size); + +/** + * Allocate an internal event message. + * + * \param msg_id the message identifier of the event. + * \param size the size of the message to be allocated. + * \param priv the private data passed with the event. + * + * \return the allocated event message. + */ +struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv); + +/** + * Register a port to an indication. + * + * \param msgId the indication message id that we want to receive + * \param port the port id where we want to receive the indication + * message. + */ +void _cfw_register_event(conn_handle_t *handle, int msgId); + +/** + * register a service to the system. + * registering a service makes it available to all other services + * and applications. + * + * \param queue the queue on which the service is to be run + * \param service the service structure to register + * \param handle_message the service's message handler + * \param param the parameter passed to message handler + */ +int cfw_register_service(T_QUEUE queue, service_t * service, + handle_msg_cb_t handle_message, void * param); + +/** + * un-register a service from the system. + * all other services and applications cannot see it anymore. + * + * \param service the service structure to unregister. + */ +int cfw_unregister_service(service_t * service); + +/** + * Send an indication message to the registered clients. + * + * \param msg the indication message to send. + */ +void cfw_send_event(struct cfw_message * msg); + +#endif /* __CFW_SERVICE_H_ */ + +/**@} @}*/ diff --git a/system/libarc32_edu/framework/include/infra/ipc.h b/system/libarc32_edu/framework/include/infra/ipc.h new file mode 100644 index 00000000..67c42d2c --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/ipc.h @@ -0,0 +1,79 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +#ifndef __IPC_H__ +#define __IPC_H__ + +#include "infra/ipc_requests.h" + +/** + * \brief initialize ipc component. + * + * \param tx_channel the IPC tx mailbox. + * \param rx_channel the IPC rx mailbox. + * \param tx_ack_channel the tx acknowledge mailbox. + * \param rx_ack_channel the rx acknowledge mailbox. + * \param remote_cpu_id the remote cpu id of this IPC + */ +void ipc_init(int tx_channel, int rx_channel, int tx_ack_channel, + int rx_ack_channel, int remote_cpu_id); + +/** + * \brief request a synchronous ipc call. + * + * This method blocks until the command is answered. + * + * \param request_id the synchronous request id + * \param param1 the first param for the request + * \param param2 the second param for the request + * \param ptr the third param for the request + * + * \return the synchronous command response. + */ +int ipc_request_sync_int(int request_id, int param1, int param2, void*ptr); + +/** + * \brief polling mode polling call. + * + * In polling mode, this method has to be called in order to handle + * ipc requests. + */ +void ipc_handle_message(); + +/** + * \brief Called by platform specific code when an IPC synchronous request is + * received. + * + * Inter-processors request callback called in the context of an interrupt + * + * \param cpu_id the cpu this request comes from + * \param param1 a first int parameter + * \param param2 a second int parameter + * \param ptr a last pointer parameter + * + * \return 0 if success, -1 otherwise + */ +int ipc_sync_callback(int cpu_id, int request, int param1, int param2, + void *ptr); + +#endif diff --git a/system/libarc32_edu/framework/include/infra/ipc_requests.h b/system/libarc32_edu/framework/include/infra/ipc_requests.h new file mode 100644 index 00000000..ebf25ee9 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/ipc_requests.h @@ -0,0 +1,13 @@ +#ifndef __IPC_REQUESTS_H__ +#define __IPC_REQUESTS_H__ + +#define IPC_MSG_TYPE_MESSAGE 0x1 +#define IPC_MSG_TYPE_FREE 0x2 +#define IPC_MSG_TYPE_SYNC 0x3 + +#define IPC_REQUEST_ALLOC_PORT 0x10 +#define IPC_REQUEST_REGISTER_SERVICE 0x11 +#define IPC_REQUEST_DEREGISTER_SERVICE 0x12 +#define IPC_REQUEST_REG_TCMD_ENGINE 0x13 + +#endif diff --git a/system/libarc32_edu/framework/include/infra/log.h b/system/libarc32_edu/framework/include/infra/log.h new file mode 100644 index 00000000..a42edb98 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/log.h @@ -0,0 +1,151 @@ +/* INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __LOG_H +#define __LOG_H + +/** + * \addtogroup infra + * @{ + * \defgroup infra_log Logger infrastructure + * @{ + * \brief Logger infrastructure + * + */ + +#include +#include + +#include "infra/log_backends.h" + +/* Maximum delay for the log_task to wait for a new message notification */ +#define LOG_MAX_DELAY 0xffffffff + +/* log configuration */ +#define LOG_MAX_MSG_LEN (80) /* Max allowed len: uint8_t - 1 */ + +/* log levels */ +enum { + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_NUM /* gives the number of log levels */ +}; + +/* module ids */ +#define DEFINE_LOGGER_MODULE(_i_,_n_) _i_, +enum { +#include "log_modules" + LOG_MODULE_NUM /* gives the number of modules */ +}; +#undef DEFINE_LOGGER_MODULE + +#define LOG_LEVEL_DEFAULT (LOG_LEVEL_INFO) + +/** + * Initializes the logger thread, queue and settings, and calls the + * initialization functions. + */ +void log_init(void); + +/** + * Flushes the log messages queue, called from the panic handler. + */ +void log_flush(void (*be)(const char *s)); + +/** + * Creates and pushes a user's log message into the logging queue. + * + * @return Message's length if inserted, -1 if no available memory, 0 if + * message was discarded. + */ +int8_t log_printk(uint8_t level, uint8_t module, const char *format, ...); + +/** + * Returns a log module's name, as string. + * + * @param module The module id + * + * @return Const pointer on the module name (NULL if module not found). + */ +const char * log_get_module_name(uint8_t module); + +/** + * Returns a log level's name, as string. + * + * @param level The level id + * + * @return Const pointer on the module name (NULL if module not found). + */ +const char * log_get_level_name(uint8_t level); + +/** + * Set the global log level value. This acts as a + * maximum overall level limit. + * + * @param level The new log level value, from the log level enum. + * + * @return -1 if error, + * 0 if new level was set + */ +int8_t log_set_global_level(uint8_t level); + +/** + * Disable or enable the logging for a module. + * + * @param module_id The module id to be disabled + * @param action Disable (0) or enable (1) + * + * @return -1 if the module_id or action is incorrect, + * 0 if module filter was disabled + */ +int8_t log_module_toggle(uint8_t module_id, uint8_t action); + +/** + * Set the level limit per module id. + * + * @param module_id The module id to be changed + * @param level The new log level limit for this module + * @return -2 if the module_id is incorrect, + * -1 if the level is incorrect, + * 0 if module filter was changed + */ +int8_t log_module_set_level(uint8_t module_id, uint8_t level); + +/* TODO - implement proper logging handler later */ +#define log_printk(module, format,...) + +/* log function format */ +#define pr_error(module, format,...) log_printk(LOG_LEVEL_ERROR, module, format,##__VA_ARGS__) + +#define pr_warning(module, format,...) log_printk(LOG_LEVEL_WARNING, module, format,##__VA_ARGS__) + +#define pr_info(module, format,...) log_printk(LOG_LEVEL_INFO, module, format,##__VA_ARGS__) + +#define pr_debug(module, format,...) log_printk(LOG_LEVEL_DEBUG, module, format,##__VA_ARGS__) + + +/* @}*/ +#endif /* __LOG_H */ diff --git a/system/libarc32_edu/framework/include/infra/log_backends.h b/system/libarc32_edu/framework/include/infra/log_backends.h new file mode 100644 index 00000000..780fc020 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/log_backends.h @@ -0,0 +1,80 @@ +/* INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __LOG_BACKENDS_H +#define __LOG_BACKENDS_H + + +/** + * \addtogroup infra + * @{ + * \defgroup infra_log_backend Logger backends management + * @{ + * \brief Backends management + * + */ + + +#include +#define LOG_BACKEND_CNT 3 /*!< Max number of backends (UART, USB, NVM) */ + +/* backend callbacks typedefs */ +typedef void (*backend_puts)(const char *buffer, uint16_t len); +typedef void (*backend_print)(const char *buffer); + +/** + * Structure of backends. + */ +typedef struct log_backend { + backend_print bprint; /*!< Backend print */ + backend_puts bputs; /*!< Backend puts */ +} log_backend_t; + + +/** + * + * Function to call logger backends. + * + * @param s Message + * @param len Length of the message + */ +void log_call_backends(const char *s, uint16_t len); + + +/** + * Function to clear registered backends. + */ +void log_clear_backends(void); + + +/** + * Function to register backends. + * + * @param bprint Backend print + * @param bputs Backend puts + */ +uint8_t log_register_backend(backend_print bprint, backend_puts bputs); + +/* @}*/ +#endif /* __LOG_BACKENDS_H */ diff --git a/system/libarc32_edu/framework/include/infra/message.h b/system/libarc32_edu/framework/include/infra/message.h new file mode 100644 index 00000000..4a263f5e --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/message.h @@ -0,0 +1,102 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +#ifndef __INFRA_MESSAGE_H_ +#define __INFRA_MESSAGE_H_ + +#include +#include "util/list.h" + +/* Message ranges */ +#define INFRA_MSG_TCMD_BASE 0xFF00 + + +/** + * Message types definition. + */ +typedef enum { + TYPE_REQ, + TYPE_RSP, + TYPE_EVT, + TYPE_INT +} msg_type_t; + +/** + * message class definition. + */ +typedef enum { + CLASS_NORMAL, + CLASS_NO_WAKE, + CLASS_NO_WAKE_REPLACE, + CLASS_REPLACE +} msg_class_t; + +/** + * Message flags definitions. + * + * prio: priority of the message. + * class: class of the message + * type: type of message + */ +struct msg_flags { + uint16_t f_prio: 8; + uint16_t f_class: 3; + uint16_t f_type: 2; +}; + +/** + * Message header structure definition. + * Data message follows the header structure. + * Header must be packed in order to ensure that + * it can be simply exchanged between embedded cores. + */ +struct message { +#ifndef NO_MSG_LIST + /** Message queueing member */ + list_t l; +#endif + /** Message identifier */ + uint16_t id; + /** Message destination port */ + uint16_t dst_port_id; + /** Message source port */ + uint16_t src_port_id; + /** Message length */ + uint16_t len; + /** The message flags */ + struct msg_flags flags; +}; + +#define MESSAGE_ID(msg) (msg)->id +#define MESSAGE_SRC(msg) (msg)->src_port_id +#define MESSAGE_DST(msg) (msg)->dst_port_id +#define MESSAGE_LEN(msg) (msg)->len +#define MESSAGE_TYPE(msg) (msg)->flags.f_type +#define MESSAGE_PRIO(msg) (msg)->flags.f_prio +#define MESSAGE_CLASS(msg) (msg)->flags.f_class + +struct message * message_alloc(int size, OS_ERR_TYPE * err); +void message_free(struct message *message); + + +#endif /* __INFRA_MESSAGE_H_ */ diff --git a/system/libarc32_edu/framework/include/infra/port.h b/system/libarc32_edu/framework/include/infra/port.h new file mode 100644 index 00000000..4c12284a --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/port.h @@ -0,0 +1,144 @@ +/* + * INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + */ +#ifndef __INFRA_PORT_H__ +#define __INFRA_PORT_H__ + +#include +#include "infra/message.h" + +#define MAX_PORTS 32 + + +#ifndef INFRA_IS_MASTER +/** + * Set the port table structure for a remote, shared mem enabled CPU. + */ +void * port_alloc_port_table(int numport); +void port_set_ports_table(void * ptbl); +#endif + +/** + * Allocate a port. The allocation of the port id is global to + * the platform. Only the Main Service Manager is allocating. + * The slave frameworks send messages to the master fw in order + * to allocate and get the port. + * The message handler for this port shall be set by the framework. + * + * return the allocated port structure ( filed with the allocated id) + */ +uint16_t port_alloc(void * queue); + + +/** + * Sets the message handler for this port. + * + * \param port the port to which set the given handler. + * \param handler the message handler. + * \param param the private parameter to pass to the handler message + * in addition to the message. + */ +void port_set_handler(uint16_t port_id, void (*handler)(struct message *, void *), + void * param); + +/** + * call the handler function attached to this port. + * This function shall be called when a message is retrieved from a queue. + * \param msg the message to process + */ +void port_process_message(struct message * msg); + +/** + * send a message to the destination port set in the message. + * + * \param msg the message to send + */ +int port_send_message(struct message * msg); + +/** + * set the port id of the given port. + * This is done to initialize a port in the context of a slave node. + * + * \param port_id the port id to initialize + */ +void port_set_port_id(uint16_t port_id); + +/** + * set the cpu_id of the given port. + * This is used to know if the internal or ipc messaging needs to be used. + * + * \param port_id the port_id to set cpu_id for + * \param cpu_id the cpu_id to associate with the port + */ +void port_set_cpu_id(uint16_t port_id, uint16_t cpu_id); + +/** + * get the cpu_id of the given port. + * + * \param port_id the port to retrieve cpu_id from + * \return the cpu_id associated with the port + */ +uint16_t port_get_cpu_id(uint16_t port_id); + +/** + * Retrieve the pointer to the global port table. + * This should only be used by the master in the context of shared memory + * communications, in order to pass the port table to a slave node with + * memory shared with the master node. + * + * \return the address of the global port table + */ +void * port_get_port_table(); + +/** + * Process the next message in a queue. + * + * Gets the first pending message out of the queue and calls the appropriate + * port handler + * + * \param queue The queue to fetch the message from + * + * \return the message id or 0 + * + */ +uint16_t queue_process_message(T_QUEUE queue); + +/** + * Multi CPU support APIs. + */ + +/** + * Set the cpu id for this (port) instance. + * + * \param cpu_id the cpu id to set. + */ +void set_cpu_id(int cpu_id); + +/** + * Get the cpu id for this (port) instance. + * + * \return cpu_id of the instance. + */ +int get_cpu_id(void); + +#endif /* __INFRA_PORT_H_ */ diff --git a/system/libarc32_edu/framework/include/log_modules b/system/libarc32_edu/framework/include/log_modules new file mode 100644 index 00000000..6afe8312 --- /dev/null +++ b/system/libarc32_edu/framework/include/log_modules @@ -0,0 +1,4 @@ +/* DEFINE_LOGGER_MODULE(, ) */ + +DEFINE_LOGGER_MODULE(LOG_MODULE_MAIN, "MAIN") +DEFINE_LOGGER_MODULE(LOG_MODULE_LOG, "LOG") diff --git a/system/libarc32_edu/framework/include/os/os.h b/system/libarc32_edu/framework/include/os/os.h new file mode 100644 index 00000000..56e2e9d2 --- /dev/null +++ b/system/libarc32_edu/framework/include/os/os.h @@ -0,0 +1,259 @@ +/* INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel?s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +/** + * \defgroup os OS API + * \brief Definition of the OS API. + * @{ + */ + +/** + * \addtogroup os + * @{ + * \defgroup os_al OS Abstraction Layer + * @{ + * \brief Definition of the OS abstraction layer API. + */ + +#ifndef __OS_H__ +#define __OS_H__ + +#include "os/os_types.h" +#include "panic_api.h" /* To be provided by the platform */ +#include "interrupt.h" + +/********************************************************** + ************** GENERIC SERVICES*************************** + **********************************************************/ +extern void panic (OS_ERR_TYPE err); + +/********************************************************** + ************** OS ABSTRACTION **************************** + **********************************************************/ + +/** + * Delete a queue. + * + * Free a whole queue. + * \param queue - The queue to free. + */ +void queue_delete(T_QUEUE queue, OS_ERR_TYPE* err); + +/** + * \brief Create a message queue. + * + * Create a message queue. + * This service may panic if err parameter is NULL and: + * -# no queue is available, or + * -# when called from an ISR. + * + * Authorized execution levels: task, fiber. + * + * As for semaphores and mutexes, queues are picked from a pool of + * statically-allocated objects. + * + * \param maxSize: maximum number of messages in the queue. + * (Rationale: queues only contain pointer to messages) + * + * \param err (out): execution status: + * -# E_OS_OK : queue was created + * -# E_OS_ERR: all queues from the pool are already being used + * -# E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + * + * \return Handler on the created queue. + * NULL if all allocated queues are already being used. + */ +extern T_QUEUE queue_create(uint32_t maxSize, OS_ERR_TYPE* err); + +/** + * \brief Read a message from a queue + * + * Read and dequeue a message. + * This service may panic if err parameter is NULL and: + * -# queue parameter is invalid, or + * -# message parameter is NULL, or + * -# when called from an ISR. + * + * Authorized execution levels: task, fiber. + * + * \param queue : handler on the queue (value returned by queue_create). + * + * \param message (out): pointer to read message. + * + * \param timeout: maximum number of milliseconds to wait for the message. Special + * values OS_NO_WAIT and OS_WAIT_FOREVER may be used. + * + * \param err (out): execution status: + * -# E_OS_OK : a message was read + * -# E_OS_ERR_TIMEOUT: no message was received + * -# E_OS_ERR_EMPTY: the queue is empty + * -# E_OS_ERR: invalid parameter + * -# E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + */ +extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int timeout, OS_ERR_TYPE* err); + +/** + * \brief Send a message on a queue + * + * Send / queue a message. + * This service may panic if err parameter is NULL and: + * -# queue parameter is invalid, or + * -# the queue is already full, or + * + * Authorized execution levels: task, fiber, ISR. + * + * \param queue: handler on the queue (value returned by queue_create). + * + * \param message (in): pointer to the message to send. + * + * \param err (out): execution status: + * -# E_OS_OK : a message was read + * -# E_OS_ERR_OVERFLOW: the queue is full (message was not posted) + * -# E_OS_ERR: invalid parameter + */ +extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); + +/** + * \brief Attach an ISR to an IRQ + * + * Attach/connect an interrupt service routing to an interrupt request line. + * Specifying a NULL pointer, as isr parameter, will detach a previous ISR from + * the IRQ. + * This service may panic if err parameter is null and: + * irq parameter is invalid, or + * when called from an ISR. + * Authorized execution levels: task, fiber. + * + * + * - VIPER does not provide an API to detach an ISR from an IRQ. + * When isr parameter is NULL, this function disables the IRQ (if valid) + * instead of replacing the ISR pointer with zeros. + * + * \param irq: interrupt request line number. + * \param isr: pointer on the interrupt service routine + * \param isrData: pointer to the data passed as an argument to isr + * \param priority: requested priority of interrupt + * \param err (out): execution status: + * E_OS_OK : ISR was attached or detached to IRQ + * E_OS_ERR: irq parameter is invalid + * E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + * + * NB: IRQ priority is not a parameter used in lakemont context, as it is inferred from the IRQ number: + * Priority = IRQ number / 16 ( rounded down ) + * --> \ref loApicIntr.c + */ +static inline __attribute__((always_inline)) +void interrupt_set_isr (int irq, T_ENTRY_POINT isr, void* isrData , int priority, OS_ERR_TYPE* err) +{ + unsigned key = interrupt_lock(); + interrupt_connect(irq, isr, isrData); + interrupt_priority_set(irq, priority); + interrupt_unlock(key); + *err = E_OS_OK; +} + +/** + * \brief Get the current tick in ms + * + * Return the current tick converted in milliseconds + * + * Authorized execution levels: task, fiber, ISR + * + * \return current tick converted in milliseconds + */ +extern uint32_t get_time_ms (void); + +/** + * \brief Get the current tick in us + * + * Return the current tick converted in microseconds + * + * Authorized execution levels: task, fiber, ISR + * + * \return current tick converted in microseconds + */ +extern uint64_t get_time_us (void); + +/** + * \brief Reserves a block of memory + * + * Authorized execution levels: task, fiber, ISR + * + * This function returns a pointer on the start of + * a reserved memory block whose size is equal or + * larger than the requested size. + * + * If there is not enough available memory, this + * function returns a null pointer and sets + * "err" parameter to E_OS_ERR_NO_MEMORY, or panic + * if "err" pointer is null. + * + * This function may panic if err is null and + * - size is null, or + * - there is not enough available memory + * + * \param size number of bytes to reserve + * + * + * \param err execution status: + * E_OS_OK : block was successfully reserved + * E_OS_ERR : size is null + * E_OS_ERR_NO_MEMORY: there is not enough available + * memory + * + * \return pointer to the reserved memory block + * or null if no block is available + */ +extern void* balloc (uint32_t size, OS_ERR_TYPE* err); + +/** + * \brief Frees a block of memory + * + * Authorized execution levels: task, fiber, ISR + * + * This function frees a memory block that was + * reserved by malloc. + * + * The "buffer" parameter must point to the + * start of the reserved block (i.e. it shall + * be a pointer returned by malloc). + * + * \param buffer pointer returned by malloc + * + * \return execution status: + * E_OS_OK : block was successfully freed + * E_OS_ERR : "buffer" param did not match + * any reserved block + */ +extern OS_ERR_TYPE bfree(void* buffer); + +/** + * \brief Initialize the OS abstraction layer + * + */ +extern void os_init (void); + +#endif + +/**@} @} @}*/ diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h new file mode 100644 index 00000000..51b7372e --- /dev/null +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -0,0 +1,82 @@ +/* INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __OS_TYPES_H__ +#define __OS_TYPES_H__ + +#include "stddef.h" +#include "stdbool.h" +#include "stdint.h" + + +/********************************************************** + ************** General definitions ********************** + **********************************************************/ +#define UNUSED(p) (void)(p) + +/** + * \enum OS_ERR_TYPE + * \brief Generic type for execution status */ +typedef enum { + E_OS_OK = 0, /** generic OK status */ + /* use negative values for errors */ + E_OS_ERR = -1, /** generic error status */ + E_OS_ERR_TIMEOUT = -2, /** timeout expired */ + E_OS_ERR_BUSY = -3, /** resource is not available */ + E_OS_ERR_OVERFLOW = -4, /** service would cause an overflow */ + E_OS_ERR_EMPTY = -5, /** no data available (e.g. queue is empty) */ + E_OS_ERR_NOT_ALLOWED = -6, /** service is not allowed in current execution context */ + E_OS_ERR_NO_MEMORY = -7, /** all allocated resources are already in use */ + E_OS_ERR_NOT_SUPPORTED = -8, /** service is not supported on current context or OS */ + /* more error codes to be defined */ + E_OS_ERR_UNKNOWN = - 100, /** invalid error code (bug?) */ +} OS_ERR_TYPE; + + + +/** Types for kernel objects */ +typedef void* T_SEMAPHORE ; +typedef void* T_MUTEX; +typedef void* T_QUEUE; +typedef void* T_QUEUE_MESSAGE; +typedef void* T_TIMER; +typedef void (* T_ENTRY_POINT) (void* ) ; +typedef void* T_TASK ; +typedef uint8_t T_TASK_PRIO ; +#define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO +#define LOWEST_TASK_PRIO OS_SPECIFIC_LOWEST_PRIO + +typedef enum { + E_TASK_UNCREATED = 0, + E_TASK_RUNNING, + E_TASK_SUSPENDED, +} T_TASK_STATE; + + +/** Special values for "timeout" parameter */ +#define OS_NO_WAIT 0 +#define OS_WAIT_FOREVER -1 + + +#endif diff --git a/system/libarc32_edu/framework/include/panic_api.h b/system/libarc32_edu/framework/include/panic_api.h new file mode 100644 index 00000000..33cf6a0b --- /dev/null +++ b/system/libarc32_edu/framework/include/panic_api.h @@ -0,0 +1 @@ +#define force_panic() panic(-1) diff --git a/system/libarc32_edu/framework/include/pf_init.h b/system/libarc32_edu/framework/include/pf_init.h new file mode 100644 index 00000000..50542ca9 --- /dev/null +++ b/system/libarc32_edu/framework/include/pf_init.h @@ -0,0 +1,32 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __PF_INIT_H_ +#define __PF_INIT_H_ + +void platform_init(); + +void platform_main_loop(); + +#endif diff --git a/system/libarc32_edu/framework/include/platform.h b/system/libarc32_edu/framework/include/platform.h new file mode 100644 index 00000000..a6d7ac30 --- /dev/null +++ b/system/libarc32_edu/framework/include/platform.h @@ -0,0 +1,52 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __PLATFORM_H_ +#define __PLATFORM_H_ + +#define CPU_ID_LMT 0 +#define CPU_ID_ARC 1 +#define CPU_ID_BLE 2 +#define CPU_ID_HOST 3 +#define NUM_CPU 4 + +struct platform_shared_block_ { + void * arc_start; + void * ports; + void * services; + int service_mgr_port_id; +}; + +#define RAM_START 0xA8000000 +#define shared_data ((struct platform_shared_block_ *) RAM_START) + +#define SS_GPIO_SERVICE_ID 0x1001 +#define SOC_GPIO_SERVICE_ID 0x1002 +#define SS_ADC_SERVICE_ID 0x1003 +#define LL_STOR_SERVICE_ID 0x1004 + +#define MSG_ID_GPIO_BASE 0x2000 + +unsigned int get_timestamp(void); +#endif diff --git a/system/libarc32_edu/framework/include/services/gpio_service.h b/system/libarc32_edu/framework/include/services/gpio_service.h new file mode 100644 index 00000000..329b86e5 --- /dev/null +++ b/system/libarc32_edu/framework/include/services/gpio_service.h @@ -0,0 +1,218 @@ +/** INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +/** + * \defgroup services Services + * \brief Definition of the structure and functions used by services implementation. + * + * It consists of: + * - Service APIs + * + * @{ + */ + +/** + * \addtogroup services + * @{ + * \defgroup gpio_service GPIO Service API + * @{ + * \brief Definition of the structure and functions used by GPIO services implementation. + */ + +#ifndef __GPIO_SERVICE_H__ +#define __GPIO_SERVICE_H__ +#include "cfw/cfw.h" +#include "cfw/cfw_client.h" +#include "stdint.h" +#include "platform.h" + +#define MSG_ID_GPIO_CONFIGURE_REQ MSG_ID_GPIO_BASE +#define MSG_ID_GPIO_SET_REQ (MSG_ID_GPIO_BASE + 1) +#define MSG_ID_GPIO_GET_REQ (MSG_ID_GPIO_BASE + 2) +#define MSG_ID_GPIO_LISTEN_REQ (MSG_ID_GPIO_BASE + 3) +#define MSG_ID_GPIO_UNLISTEN_REQ (MSG_ID_GPIO_BASE + 4) + +#define MSG_ID_GPIO_CONFIGURE_RSP (MSG_ID_GPIO_CONFIGURE_REQ | 0x40) +#define MSG_ID_GPIO_SET_RSP (MSG_ID_GPIO_SET_REQ | 0x40) +#define MSG_ID_GPIO_GET_RSP (MSG_ID_GPIO_GET_REQ | 0x40) +#define MSG_ID_GPIO_LISTEN_RSP (MSG_ID_GPIO_LISTEN_REQ | 0x40) +#define MSG_ID_GPIO_UNLISTEN_RSP (MSG_ID_GPIO_UNLISTEN_REQ | 0x40) + +#define MSG_ID_GPIO_EVT (MSG_ID_GPIO_BASE | 0x80) + +typedef enum { + RISING_EDGE, + FALLING_EDGE, + BOTH_EDGE // Used on soc GPIO only +} gpio_service_isr_mode_t; + +typedef enum { + DEB_OFF = 0, + DEB_ON +} gpio_service_debounce_mode_t; + +void gpio_service_init(void * queue, int service_id); + +typedef struct gpio_configure_req_msg { + struct cfw_message header; + /** requested mode for each gpio + * 0 - gpio is an output + * 1 - gpio is an input + */ + gpio_service_isr_mode_t mode; + /** index of the gpio to configure in the port */ + uint8_t index; +} gpio_configure_req_msg_t; + +typedef struct gpio_configure_rsp_msg { + struct cfw_rsp_message rsp_header; +} gpio_configure_rsp_msg_t; + +typedef struct gpio_set_req_msg { + struct cfw_message header; + /** index of the gpio in the port */ + uint8_t index; + /** state of the gpio to set */ + uint8_t state; +} gpio_set_req_msg_t; + +typedef struct gpio_set_rsp_msg { + struct cfw_rsp_message rsp_header; +} gpio_set_rsp_msg_t; + +typedef struct gpio_listen_req_msg { + struct cfw_message header; + /** index of GPIO to monitor */ + uint8_t index; + /** interrupt mode */ + gpio_service_isr_mode_t mode; + /** debounce mode */ + gpio_service_debounce_mode_t debounce; +} gpio_listen_req_msg_t; + +typedef struct gpio_listen_rsp_msg { + struct cfw_rsp_message rsp_header; + /** index of GPIO to monitor */ + uint8_t index; +} gpio_listen_rsp_msg_t; + +typedef struct gpio_listen_evt_msg { + struct cfw_message header; + /** index of GPIO which triggered the interrupt */ + uint8_t index; + /** gpio current value */ + uint32_t pin_state; +} gpio_listen_evt_msg_t; + +typedef struct gpio_unlisten_req_msg { + struct cfw_message header; + /** index of GPIO to monitor */ + uint8_t index; + /** interrupt mode */ +} gpio_unlisten_req_msg_t; + +typedef struct gpio_unlisten_rsp_msg { + struct cfw_rsp_message rsp_header; + /** index of GPIO to monitor */ + uint8_t index; +} gpio_unlisten_rsp_msg_t; + +typedef struct gpio_get_req_msg { + struct cfw_message header; +} gpio_get_req_msg_t; + +typedef struct gpio_get_rsp_msg { + struct cfw_rsp_message rsp_header; + /** state of the gpio port */ + uint32_t state; +} gpio_get_rsp_msg_t; + +/** + * \brief configure gpio_line + * + * Configures a GPIO line. + * + * \param svc_handle the service connection handle + * \param index the GPIO index in the port to configure + * \param mode the mode of the GPIO line + * 0 - input + * 1 - output + * \param priv the private data passed back in the + * response message. + */ +int gpio_configure(svc_client_handle_t * svc_handle, uint8_t index, + uint8_t mode, void * priv); + +/** + * \brief Set the state of a GPIO line. + * + * \param svc_handle the service connection handle + * \param index the GPIO index in the port to configure + * \param value the state to set the GPIO line + * 0 - low level + * 1 - high level + * \param priv the private data passed back in the + * response message. + */ +int gpio_set_state(svc_client_handle_t * svc_handle, uint8_t index, + uint8_t value, void * priv); + +/** + * \brief get state of a GPIO line + * + * \param svc_handle the service connection handle + * \param priv the private data passed back in the + * response message. + * + * GPIO state will be available in the \ref gpio_get_state_rsp_msg_t + */ +int gpio_get_state(svc_client_handle_t * svc_handle, void * priv); + +/** + * \brief register to gpio state change. + * + * \param svc_handle: the service connection handle + * \param pin : the GPIO index in the port to configure + * \param mode : interrupt mode (RISING_EDGE, FALLING_EDGE) + * \param debounce : debounce config (DEB_OFF, DEB_ON) + * \param priv : the private data passed back in the response message. + * + * GPIO state and changed mask are available in the \ref gpio_listen_rsp_msg_t message. + */ +int gpio_listen(svc_client_handle_t * h, uint8_t pin, gpio_service_isr_mode_t mode, uint8_t debounce, void *priv); + +/** + * \brief unregister to gpio state change. + * + * \param svc_handle: the service connection handle + * \param pin : the GPIO index in the port to configure + * \param priv : the private data passed back in the response message. + * + * GPIO state and changed mask are available in the \ref gpio_unlisten_rsp_msg_t message. + */ +int gpio_unlisten(svc_client_handle_t * h, uint8_t pin, void *priv); + +#endif + +/**@} @} @}*/ diff --git a/system/libarc32_edu/framework/include/util/list.h b/system/libarc32_edu/framework/include/util/list.h new file mode 100644 index 00000000..40744206 --- /dev/null +++ b/system/libarc32_edu/framework/include/util/list.h @@ -0,0 +1,94 @@ +#ifndef __LIST_H__ +#define __LIST_H__ + +#include + +typedef struct list { + struct list * next; +} list_t; + +typedef struct list_head_ { + list_t * head; + list_t * tail; +} list_head_t; + +/** + * initialize a list structure + * + * \param list the list to initialize + */ +void list_init(list_head_t * list); + +/** + * append an element to the end of list. + * + * \param list the list to which element has to be added + * \param element the element we want to add to the list. + */ +void list_add(list_head_t * list, list_t * element); + +/** + * append an element to the begining of list. + * + * \param list the list to which element has to be added + * \param element the element we want to add to the list. + */ +void list_add_head(list_head_t * list, list_t * element); + +/** + * remove an element from the list. + * + * \param list the list to remove the element from + * \param element the element to remove from the list. + */ +void list_remove(list_head_t *list, list_t * element); + +/** + * Iterate through elements of a list. + * + * \param lh the list we want to iterate through + * \param cb the callback function to call for each element + * \param param the parameter to pass to the callback in + * addition to the element. + */ +void list_foreach(list_head_t * lh, void(*cb)(void *, void *), void * param); + +/** + * Iterate through elements of a list, with the option + * to remove element from the callback. + * + * \param lh the list we want to iterate through + * \param cb the callback function to call for each element. + * if the callback returns non-zero, the element is + * removed from the list. + * \param param the parameter to pass to the callback in + * addition to the element. + */ +void list_foreach_del(list_head_t * lh, int(*cb)(void *, void *), void * param); + +/** + * Get the first element from the list. + * + * \param lh the list from which the element has to be retrieved. + * \return the element removed. + */ +list_t * list_get(list_head_t * lh); + +/** + * Check if the list is empty + * + * \return 0 if not empty + */ +int list_empty(list_head_t *lh); + +/** + * Find the first item in a list matching a criteria. + * + * \param cb the test function that will be applied to each item + * \param data an opaque data passed to the test function + * + * \return the item or NULL + */ +list_t * list_find_first(list_head_t * lh, bool(*cb)(list_t*,void*), void *data); + +#endif /* __LIST_H__ */ diff --git a/system/libarc32_edu/framework/src/cfw/cfw_debug.c b/system/libarc32_edu/framework/src/cfw/cfw_debug.c new file mode 100644 index 00000000..c912d25c --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/cfw_debug.c @@ -0,0 +1,42 @@ +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_internal.h" +#include "infra/port.h" + +char * cfw_get_msg_type_str(struct cfw_message *msg) +{ + switch(CFW_MESSAGE_TYPE(msg)) { + case TYPE_REQ: + return "REQ"; + case TYPE_RSP: + return "RSP"; + case TYPE_EVT: + return "EVT"; + case TYPE_INT: + return "INT"; + default: + return "INVALID"; + } +} + +void cfw_dump_service_handle(svc_client_handle_t * svc_handle) +{ + cfw_log("svc_handle: port: %d, fw_handle: %p, server_handle: %p\n", + svc_handle->port, + svc_handle->cfw_handle, + svc_handle->server_handle); +} + +void cfw_dump_message(struct cfw_message * msg) +{ +#if 1 + cfw_log("%p id: %x src: %d[cpu:%d] dst: %d[cpu:%d] type: %s\n", + msg, CFW_MESSAGE_ID(msg), CFW_MESSAGE_SRC(msg), + port_get_cpu_id(CFW_MESSAGE_SRC(msg)), + CFW_MESSAGE_DST(msg), port_get_cpu_id(CFW_MESSAGE_DST(msg)), + cfw_get_msg_type_str(msg)); +#else + cfw_log("id: %x src: %d dst: %d type: %s\n", msg->id, + msg->src, msg->dst, cfw_get_msg_type_str(msg)); +#endif +} diff --git a/system/libarc32_edu/framework/src/cfw/client_api.c b/system/libarc32_edu/framework/src/cfw/client_api.c new file mode 100644 index 00000000..f3db73d8 --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/client_api.c @@ -0,0 +1,96 @@ +#include "cfw/cfw.h" +#include "cfw/cfw_client.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_messages.h" + +/** + * \file client_api.c + * + * Implementation of the client interface. + * + */ + +extern int service_mgr_port_id; + +int cfw_open_service(cfw_handle_t handle, int service_id, void * priv) { + cfw_open_conn_req_msg_t * msg = (cfw_open_conn_req_msg_t*) + cfw_alloc_message(sizeof(*msg), NULL); + CFW_MESSAGE_ID(&msg->header) = MSG_ID_CFW_OPEN_SERVICE; + CFW_MESSAGE_LEN(&msg->header) = sizeof(*msg); + CFW_MESSAGE_DST(&msg->header) = service_mgr_port_id; + CFW_MESSAGE_SRC(&msg->header) = ((_cfw_handle_t*)handle)->client_port_id; + + svc_client_handle_t * svch = (svc_client_handle_t*)cfw_alloc(sizeof(*svch), NULL); + svch->port = CFW_MESSAGE_SRC(&msg->header); + svch->cfw_handle = handle; + svch->server_handle = NULL; + + msg->service_id = service_id; + msg->client_handle = svch; + msg->header.priv = priv; + msg->header.conn = NULL; + cfw_send_message(msg); + return 0; +} + +int cfw_close_service(svc_client_handle_t *handle, void *priv) +{ + cfw_close_conn_req_msg_t *msg = (cfw_close_conn_req_msg_t*) + cfw_alloc_message(sizeof(*msg), NULL); + CFW_MESSAGE_ID(&msg->header) = MSG_ID_CFW_CLOSE_SERVICE; + CFW_MESSAGE_LEN(&msg->header) = sizeof(*msg); + CFW_MESSAGE_DST(&msg->header) = service_mgr_port_id; + CFW_MESSAGE_SRC(&msg->header) = + ((_cfw_handle_t*)handle->cfw_handle)->client_port_id; + msg->header.priv = priv; + msg->header.conn = handle->server_handle; + msg->inst = NULL; + cfw_send_message(msg); + return 0; +} + +int cfw_register_events(svc_client_handle_t * h, int * msg_ids, int size, void * priv) { + int msg_size = sizeof(struct cfw_message) + sizeof(int) * (size+1); + int i; + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_CFW_REGISTER_EVT, msg_size, + priv); + CFW_MESSAGE_DST(msg) = service_mgr_port_id; + ((int *) &msg[1])[0] = size; + for (i = 0; i < size; i++) { + ((int *) (&msg[1]))[i+1] = msg_ids[i]; + } + cfw_send_message(msg); + return 0; +} + +int cfw_register_svc_available(cfw_handle_t handle, void *priv) +{ + struct cfw_message * msg = cfw_alloc(sizeof(*msg), NULL); + CFW_MESSAGE_ID(msg) = MSG_ID_CFW_REGISTER_SVC_AVAIL; + CFW_MESSAGE_LEN(msg) = sizeof(*msg); + CFW_MESSAGE_SRC(msg) = ((_cfw_handle_t*)handle)->client_port_id; + CFW_MESSAGE_DST(msg) = service_mgr_port_id; + CFW_MESSAGE_TYPE(msg) = TYPE_REQ; + msg->priv = priv; + msg->conn = NULL; + cfw_send_message(msg); + return 0; +} + +struct cfw_message * cfw_alloc_message_for_service(svc_client_handle_t * h, int msg_id, int msg_size, + void * priv) { + struct cfw_message * msg = (struct cfw_message*) cfw_alloc(msg_size, NULL); + CFW_MESSAGE_ID(msg) = msg_id; + CFW_MESSAGE_LEN(msg) = msg_size; + CFW_MESSAGE_SRC(msg) = ((_cfw_handle_t*)h->cfw_handle)->client_port_id; + CFW_MESSAGE_DST(msg) = h->port; + CFW_MESSAGE_TYPE(msg) = TYPE_REQ; + msg->priv = priv; + msg->conn = h->server_handle; + return msg; +} + +struct cfw_message * cfw_alloc_message(int size, OS_ERR_TYPE * err) +{ + return (struct cfw_message *)message_alloc(size, err); +} diff --git a/system/libarc32_edu/framework/src/cfw/service_api.c b/system/libarc32_edu/framework/src/cfw/service_api.c new file mode 100644 index 00000000..522a8a9e --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/service_api.c @@ -0,0 +1,148 @@ +#include "util/list.h" +#include "os/os.h" +#include "infra/message.h" +#include "infra/port.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" + +/** + * \file service_api.c implementation of the service_manager API + */ + +/* Structure for holding if a service is local or remote. */ + +#define SEND_MESSAGE(_msg_) cfw_send_message(&(_msg_)->header) + +void cfw_port_set_handler(uint16_t port_id, void (*handler)(struct cfw_message*, void*), void * param) { +#ifdef SVC_API_DEBUG + cfw_log("%s: port: %p h: %p\n", __func__, port, handler); +#endif + port_set_handler(port_id, (void (*)(struct message*, void*))handler, param); +} + +struct cfw_message * cfw_clone_message(struct cfw_message * msg) { + struct cfw_message * ret = cfw_alloc(CFW_MESSAGE_LEN(msg), NULL); + if (ret == NULL) { + cfw_log("%s: Error allocating message", __func__); + } else { + memcpy(ret, msg, CFW_MESSAGE_LEN(msg)); + } + return ret; +} + +int cfw_register_service(T_QUEUE queue, service_t * svc, + handle_msg_cb_t handle_message, void * data) { + uint16_t port_id = port_alloc(queue); + + cfw_port_set_handler(port_id, handle_message, data); + svc->port_id = port_id; + return _cfw_register_service(svc); +} + +int cfw_deregister_service(cfw_handle_t handle, service_t * svc) { + return _cfw_deregister_service(handle, svc); +} + +struct cfw_rsp_message * cfw_alloc_rsp_msg(struct cfw_message *req, int msg_id, int size) { + struct cfw_rsp_message * rsp = (struct cfw_rsp_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(&rsp->header) = TYPE_RSP; + CFW_MESSAGE_ID(&rsp->header) = msg_id; + CFW_MESSAGE_LEN(&rsp->header) = size; + CFW_MESSAGE_DST(&rsp->header) = CFW_MESSAGE_SRC(req); + CFW_MESSAGE_SRC(&rsp->header) = CFW_MESSAGE_DST(req); + rsp->header.priv = req->priv; + /* Substitute server-side with client-side conn */ + if (req->conn != NULL) + rsp->header.conn = ((conn_handle_t*)req->conn)->client_handle; + else + rsp->header.conn = NULL; + return rsp; +} + +struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size) { + struct cfw_message * evt = (struct cfw_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(evt) = TYPE_EVT; + CFW_MESSAGE_ID(evt) = msg_id; + CFW_MESSAGE_LEN(evt) = size; + CFW_MESSAGE_SRC(evt) = svc->port_id; + /* 3 fields below whould be filed by send_event method*/ + CFW_MESSAGE_DST(evt) = 0; + evt->priv = NULL; + evt->conn = NULL; + return evt; +} + +struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv) { + struct cfw_message * evt = (struct cfw_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(evt) = TYPE_INT; + CFW_MESSAGE_ID(evt) = msg_id; + CFW_MESSAGE_LEN(evt) = size; + CFW_MESSAGE_SRC(evt) = 0; + /* 3 fields below whould be filed by send_event method*/ + CFW_MESSAGE_DST(evt) = 0; + evt->priv = priv; + evt->conn = NULL; + return evt; +} + +void default_msg_handler(struct cfw_message * msg, void *data) { + cfw_log("Bug: %s should not be called data: %p\n", __func__, data); + cfw_dump_message(msg); +} + +void client_handle_message(struct cfw_message * msg, void *param) { + _cfw_handle_t * h = (_cfw_handle_t*)param; + switch(CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_OPEN_SERVICE: + { + cfw_open_conn_rsp_msg_t * cnf = (cfw_open_conn_rsp_msg_t *) msg; + /** Make client handle point to server handle */ + ((svc_client_handle_t*)cnf->client_handle)->server_handle = cnf->svc_server_handle; + /** Initialize service port. */ + ((svc_client_handle_t*)cnf->client_handle)->port = cnf->port; +#ifndef INFRA_IS_MASTER + /* Set local port and cpu id */ + if (get_cpu_id() != cnf->cpu_id) { + port_set_port_id(cnf->port); + port_set_cpu_id(cnf->port, cnf->cpu_id); + } +#endif + break; + } + case MSG_ID_CFW_CLOSE_SERVICE: + { + /* Free client-side conn */ + cfw_free(msg->conn, NULL); + break; + } + default: + //Nothing to do + break; + } + h->handle_msg(msg, h->data); +} + +cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void *cb_data) { + _cfw_handle_t * handle = (_cfw_handle_t*)cfw_alloc(sizeof(*handle), NULL); + handle->handle_msg = cb; + handle->data = cb_data; + + handle->client_port_id = port_alloc(queue); + + cfw_port_set_handler(handle->client_port_id, client_handle_message, handle); + + return (cfw_handle_t) handle; +} + +int _cfw_send_message(struct cfw_message * message) +{ + return port_send_message(CFW_MESSAGE_HEADER(message)); +} + +void cfw_msg_free(struct cfw_message * msg) +{ + message_free(CFW_MESSAGE_HEADER(msg)); +} diff --git a/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c b/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c new file mode 100644 index 00000000..fcc3e821 --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c @@ -0,0 +1,256 @@ +#include "util/list.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" + +#include "infra/port.h" +#include "infra/ipc.h" + +/** + * \file service_manager_proxy.c implementation of the service_manager proxy + * + * This file implements the proxy for the service manager. + * It has to be compiled in all cores except the one that holds the service + * manager. + */ +int service_mgr_port_id = 0; + +service_t ** services = NULL; + +void internal_handle_message(struct cfw_message * msg, void * param) { + switch(CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_ALLOC_PORT: { + //_port_t * port = cfw_port_alloc(NULL); + //cfw_port_set_handler(port, send_message_ipc, NULL); + break; + } + + case MSG_ID_CFW_OPEN_SERVICE: { + conn_handle_t * conn_handle; + cfw_open_conn_req_msg_t * req = (cfw_open_conn_req_msg_t *) msg; + service_t * svc = cfw_get_service(req->service_id); + + conn_handle = (conn_handle_t *) cfw_alloc(sizeof(*conn_handle), NULL); + conn_handle->client_port = CFW_MESSAGE_SRC(msg); + conn_handle->priv_data = NULL; + conn_handle->svc = svc; + conn_handle->client_handle = req->client_handle; + /* For OPEN_SERVICE, conn is not know yet, it is just alloc'ed. + * set it here.*/ + req->header.conn = conn_handle; + if (svc->client_connected != NULL) + svc->client_connected(conn_handle); + cfw_open_conn_rsp_msg_t * resp = (cfw_open_conn_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_OPEN_SERVICE, sizeof(*resp)); + resp->port = svc->port_id; + resp->svc_server_handle = conn_handle; + resp->client_handle = req->client_handle; + cfw_send_message(resp); + break; + } + + case MSG_ID_CFW_CLOSE_SERVICE: { + cfw_close_conn_rsp_msg_t * resp = (cfw_close_conn_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_CLOSE_SERVICE, sizeof(*resp)); + conn_handle_t * conn = (conn_handle_t*)msg->conn; + if ( conn != NULL && conn->svc != NULL && conn->svc->client_disconnected != NULL) + conn->svc->client_disconnected(conn); + cfw_send_message(resp); + /* Free server-side conn */ + cfw_free(conn, NULL); + break; + } + + case MSG_ID_CFW_REGISTER_EVT: { + int * params = (int *) &msg[1]; + int i; + for (i = 0; i < params[0]; i++) { + _cfw_register_event((conn_handle_t*)msg->conn, params[i+1]); + } + cfw_register_evt_rsp_msg_t * resp = + (cfw_register_evt_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_REGISTER_EVT, (sizeof(*resp))); + conn_handle_t * conn = (conn_handle_t*)msg->conn; + if ( conn != NULL && conn->svc != NULL && conn->svc->registered_events_changed != NULL) + conn->svc->registered_events_changed(conn); + + cfw_send_message(resp); + break; + } + + default: + cfw_log("%s: unhandled message id: %x\n", __func__, CFW_MESSAGE_ID(msg)); + break; + } +} + +int handle_ipc_sync_request(int cpu_id, int request, int param1, int param2, void * ptr) +{ +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s: from %d, req:%d (%d, %d, %p)\n", __func__, cpu_id, request, param1, + param2, ptr); +#endif + switch (request) { + case IPC_MSG_TYPE_FREE: + cfw_free(ptr, NULL); + break; + case IPC_MSG_TYPE_MESSAGE: + { + struct cfw_message * msg = (struct cfw_message *) ptr; + _cfw_send_message(msg); + break; + } + case IPC_REQUEST_ALLOC_PORT: + { +#ifdef SVC_MANAGER_DEBUG + cfw_log("Updating port id %d\n", param1); +#endif + port_set_port_id(param1); + port_set_cpu_id(param1, get_cpu_id()); /* assign instance cpu id as this is a response */ + break; + } + default: + cfw_log("%s: unexpected ipc_sync_request: %d\n", __func__, request); + break; + } + return 0; +} + + +uint16_t cfw_port_alloc(void * queue) { + return port_alloc(queue); +} + + +/** + * Indication list + * Holds a list of receivers. + */ +typedef struct { + list_t list; + conn_handle_t * conn_handle; +} indication_list_t; + +/** + * \struct registered_int_list_t holds a list of registered clients to an indication + * + * Holds a list of registered receiver for each indication. + */ +typedef struct registered_evt_list_ { + list_t list; /*! Linking stucture */ + list_head_t lh; /*! List of client */ + int ind; /*! Indication message id */ +} registered_evt_list_t; + +list_head_t registered_evt_list; + + + +registered_evt_list_t * get_event_registered_list(int msg_id) { + registered_evt_list_t * l = (registered_evt_list_t*)registered_evt_list.head; + while(l) { + if (l->ind == msg_id) { + return l; + } + l = (registered_evt_list_t*)l->list.next; + } + return NULL; +} + +list_head_t * get_event_list(int msg_id) { + registered_evt_list_t * l = get_event_registered_list(msg_id); + if (l) + return &l->lh; + return NULL; +} + +static void send_event_callback(void * item, void * param) { + struct cfw_message * msg = (struct cfw_message *) param; + indication_list_t * ind = (indication_list_t *)item; + struct cfw_message * m = cfw_clone_message(msg); + if (m != NULL) { + CFW_MESSAGE_DST(m) = ind->conn_handle->client_port; + cfw_send_message(m); + } +} + +void cfw_send_event(struct cfw_message * msg) { +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s : msg:%d\n", __func__, CFW_MESSAGE_ID(msg)); +#endif + list_head_t * list = get_event_list(CFW_MESSAGE_ID(msg)); + if (list != NULL) { + list_foreach(list, send_event_callback, msg); + } +} + +void _cfw_register_event(conn_handle_t * h, int msg_id) { +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s : msg:%d port %d h:%p\n", __func__, msg_id, h->client_port, h); +#endif + registered_evt_list_t * ind = (registered_evt_list_t *) get_event_registered_list(msg_id); + + if (ind == NULL) { + ind = (registered_evt_list_t *) cfw_alloc(sizeof(*ind), NULL); + ind->ind = msg_id; + list_init(&ind->lh); + list_add(®istered_evt_list, &ind->list); + } + + indication_list_t * e = (indication_list_t *)cfw_alloc(sizeof(*e), NULL); + e->conn_handle = h; + list_add(&ind->lh, (list_t *)e); +} + +void _cfw_loop(void * queue) { + struct cfw_message * message; + T_QUEUE_MESSAGE m; + while(1) { + queue_get_message(queue, &m, OS_WAIT_FOREVER, NULL); + message = (struct cfw_message *) m; + if ( message != NULL ) { + port_process_message(&message->m); + } + } +} + +service_t * cfw_get_service(int service_id) { + int i; + for(i=0; iservice_id == service_id) { + return services[i]; + } + } + return NULL; +} + +int _cfw_get_service_port(int service_id) { + service_t * svc = cfw_get_service(service_id); + if (svc != NULL) { + return svc->port_id; + } + return -1; +} +typedef void(*send_msg_t)(struct cfw_message * m); +extern send_msg_t get_ipc_handler(int cpu_id); + +void svc_manager_handle_message(struct cfw_message * msg, void * param) { + get_ipc_handler(0)(msg); +} + +void _cfw_init_proxy(T_QUEUE queue, void *p, service_t **s, uint16_t svc_mgr_port) { + port_set_ports_table(p); + services = s; + service_mgr_port_id = svc_mgr_port; + cfw_log("%s queue: %p\n", __func__, queue); +} + +int _cfw_register_service(service_t * svc) { + return ipc_request_sync_int(IPC_REQUEST_REGISTER_SERVICE, svc->service_id, svc->port_id, svc); +} + +int _cfw_deregister_service(cfw_handle_t handle, service_t * svc) { + return ipc_request_sync_int(IPC_REQUEST_DEREGISTER_SERVICE, svc->service_id, 0, svc); +} diff --git a/system/libarc32_edu/framework/src/infra/ipc.c b/system/libarc32_edu/framework/src/infra/ipc.c new file mode 100644 index 00000000..922a7385 --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/ipc.c @@ -0,0 +1,115 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#include +#include "os/os_types.h" +#include "infra/ipc.h" +#include "cfw/cfw.h" +#include "soc_register.h" +#include "infra/log.h" + +#define MBX_IPC_SYNC_ARC_TO_LMT 5 + +static int rx_chan = 0; +static int tx_chan = 0; +static int tx_ack_chan = 0; +static int rx_ack_chan = 0; +static int remote_cpu = 0; + +/***************************************************************************** + * IPC Protocol: + * 2 Mailboxes per side. + * One mailbox for passing data, one mailbox for acknowledging the transfer + * + ****************************************************************************/ + +unsigned int get_timestamp() +{ + return SCSS_REG_VAL(SCSS_AONC_CNT); +} + +void ipc_init(int tx_channel, int rx_channel, int tx_ack_channel, + int rx_ack_channel, int remote_cpu_id) +{ + rx_chan = rx_channel; + tx_chan = tx_channel; + tx_ack_chan = tx_ack_channel; + rx_ack_chan = rx_ack_channel; + remote_cpu = remote_cpu_id; +} + +void ipc_handle_message() +{ + int ret = 0; + if (!MBX_STS(rx_chan)) return; + int request = MBX_DAT0(rx_chan); + int param1 = MBX_DAT1(rx_chan); + int param2 = MBX_DAT2(rx_chan); + void * ptr = (void *)MBX_DAT3(rx_chan); + + ret = ipc_sync_callback(remote_cpu, request, param1, param2, ptr); + + MBX_CTRL(rx_chan) = 0x80000000; + + MBX_DAT0(tx_ack_chan) = ret; + MBX_CTRL(tx_ack_chan) = 0x80000000; + pr_debug(LOG_MODULE_MAIN, "read message on %d : ack [%d] %p", rx_chan, tx_ack_chan, + MBX_DAT0(tx_ack_chan)); + MBX_STS(rx_chan) = 3; +} + +int ipc_request_sync_int(int request_id, int param1, int param2, void * ptr) +{ + int ret; + int timeout; + + pr_debug(LOG_MODULE_MAIN, "send request %d from: %p", request_id, &ret); + while (MBX_CTRL(tx_chan) & 0x80000000) { + pr_info(LOG_MODULE_MAIN, "Channel busy %d for request: %d msg: %p", + tx_chan, request_id, param1); + pr_info(LOG_MODULE_MAIN, "current request: %p msg: %p", + MBX_CTRL(tx_chan), MBX_DAT0(tx_chan)); + } + + MBX_STS(rx_ack_chan) = 3; + + MBX_DAT0(tx_chan) = request_id; + MBX_DAT1(tx_chan) = param1; + MBX_DAT2(tx_chan) = param2; + MBX_DAT3(tx_chan) = (unsigned int )ptr; + MBX_CTRL(tx_chan) = 0x80000000 | IPC_MSG_TYPE_SYNC; + + timeout = get_timestamp() + 32768; + while(!MBX_STS(rx_ack_chan)) { + if (get_timestamp() > timeout) { + pr_error(LOG_MODULE_MAIN, "Timeout waiting ack %p", &ret); + break; + } + } + ret = MBX_DAT0(rx_ack_chan); + MBX_DAT0(rx_ack_chan) = 0; + MBX_STS(rx_ack_chan) = 3; + pr_debug(LOG_MODULE_MAIN, "ipc_request_sync returns: [%d] %p", rx_ack_chan, ret); + return ret; +} diff --git a/system/libarc32_edu/framework/src/infra/ipc_callback.c b/system/libarc32_edu/framework/src/infra/ipc_callback.c new file mode 100644 index 00000000..2f6b204b --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/ipc_callback.c @@ -0,0 +1,53 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +/* For the framework IPC message handler*/ +#include "cfw/cfw_internal.h" + +#include "infra/port.h" + +int ipc_sync_callback(int cpu_id, int request, int param1, int param2, + void *ptr) +{ + int ret = 0; + + switch (request) { + case IPC_REQUEST_ALLOC_PORT: + { + uint16_t port_id = port_alloc(NULL); + port_set_cpu_id(port_id, cpu_id); + ret = port_id; + break; + } + case IPC_MSG_TYPE_FREE: + message_free(ptr); + break; + default: + { + /* This is a framework message */ + ret = handle_ipc_sync_request(cpu_id, request, param1, param2, + ptr); + } + } + return ret; +} diff --git a/system/libarc32_edu/framework/src/infra/port.c b/system/libarc32_edu/framework/src/infra/port.c new file mode 100644 index 00000000..a96b271d --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/port.c @@ -0,0 +1,250 @@ +/* + * INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + */ +#include "os/os.h" +#include "util/list.h" +#include "infra/port.h" +#include "infra/log.h" +#include "infra/ipc.h" + +//#define PORT_DEBUG + +/** + * Internal definition of a port structure. + * External usage is done with a port id integer. + */ +struct port { + int id; + int cpu_id; + void *handle_param; + void * queue; + void (*handle_message)(struct message *msg, void *param); +}; + +static int this_cpu_id = 0; + +/* required by port_alloc() and other cfw APIs */ +int get_cpu_id(void) +{ + return this_cpu_id; +} + +#ifdef INFRA_IS_MASTER +static struct port ports[MAX_PORTS]; +static int registered_port_count = 0; +#else +static struct port * ports = NULL; +void port_set_ports_table(void * ptbl) +{ + ports = (struct port *) ptbl; +} + +void * port_alloc_port_table(int numports) +{ + return ports = balloc(numports*sizeof(struct port), NULL); +} +#endif + +void * port_get_port_table() +{ + return (void *)&ports[0]; +} + +static struct port * get_port(int port_id) +{ + return &ports[port_id - 1]; +} + +void port_set_queue(uint16_t port_id, void * queue) +{ + struct port * p = get_port(port_id); + p->queue = queue; +} + +#ifdef INFRA_IS_MASTER +uint16_t port_alloc(void * queue) +{ + struct port * ret = NULL; + uint32_t flags = interrupt_lock(); + if (registered_port_count < MAX_PORTS) { + ports[registered_port_count].id = registered_port_count + 1; /* don't use 0 as port.*/ + ports[registered_port_count].cpu_id = get_cpu_id(); /* is overwritten in case of ipc */ + ports[registered_port_count].queue = queue; +#ifdef PORT_DEBUG + pr_info("%s: port: %p id: %d queue: %p\n", __func__, + &ports[registered_port_count], registered_port_count, queue); +#endif + ret = &ports[registered_port_count]; + registered_port_count++; + } + interrupt_unlock(flags); + return ret->id; +} +#else +uint16_t port_alloc(void *queue) +{ + struct port * port = NULL; + int ret = ipc_request_sync_int(IPC_REQUEST_ALLOC_PORT, 0, 0, NULL); + port = get_port((unsigned int)ret); +#ifndef HAS_SHARED_MEM + port->id = ret; +#endif + if (port != NULL) { + port->queue = queue; + } + return port->id; +} +#endif +void port_set_handler(uint16_t port_id, void (*handler)(struct message*, void*), void *param) +{ + struct port * port = get_port(port_id); + port->handle_message = handler; + port->handle_param = param; +} + +struct message * message_alloc(int size, OS_ERR_TYPE * err) +{ + return (struct message *) balloc(size, err); +} + +void port_process_message(struct message * msg) +{ + struct port * p = get_port(msg->dst_port_id); + if (p->handle_message != NULL) { + p->handle_message(msg, p->handle_param); + } +} + +void port_set_cpu_id(uint16_t port_id, uint16_t cpu_id) +{ + struct port * p = get_port(port_id); + p->cpu_id = cpu_id; +} + +void port_set_port_id(uint16_t port_id) +{ + struct port * p = get_port(port_id); + p->id = port_id; +} + +uint16_t port_get_cpu_id(uint16_t port_id) +{ + struct port * p = get_port(port_id); + return p->cpu_id; +} + +#ifdef INFRA_MULTI_CPU_SUPPORT +#include "platform.h" + +typedef int (*send_msg_t)(struct message * m); + +struct ipc_handler { + send_msg_t send_message; + void (*free)(void * ptr); +}; + +struct ipc_handler ipc_handler[NUM_CPU]; + +void set_cpu_id(int cpu_id) +{ + this_cpu_id = cpu_id; +} + +send_msg_t get_ipc_handler(int cpu_id) { + return ipc_handler[cpu_id].send_message; +} + +void set_cpu_message_sender(int cpu_id, send_msg_t handler) { + ipc_handler[cpu_id].send_message = handler; +} + +void set_cpu_free_handler(int cpu_id, void (*free_handler)(void *)) { + ipc_handler[cpu_id].free = free_handler; +} + +int port_send_message(struct message * message) +{ + OS_ERR_TYPE err = 0; + struct port * port = get_port(MESSAGE_DST(message)); + if (port == NULL) { + pr_error(LOG_MODULE_MAIN, "Invalid destination port (%d)\n", MESSAGE_DST(message)); + return E_OS_ERR; + } + if (port->cpu_id == get_cpu_id()) { +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "Sending message %p to port %p(q:%p) ret: %d\n", message, port, port->queue, err); +#endif + queue_send_message(port->queue, message, &err); + return err; + } else { +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "Remote port ! using: %p handler\n", ipc_handler[port->cpu_id].send_message); +#endif + return ipc_handler[port->cpu_id].send_message(message); + } +} + +void message_free(struct message * msg) +{ + struct port * port = get_port(MESSAGE_SRC(msg)); +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "free message %p: port %p[%d] this %d id %d\n", + msg, port, port->cpu_id, get_cpu_id(), MESSAGE_SRC(msg)); +#endif + if (port->cpu_id == get_cpu_id()) { + bfree(msg); + } else { + ipc_handler[port->cpu_id].free(msg); + } +} + +#else /* Single CPU support */ + +int port_send_message(struct message * msg) +{ + struct port * port = get_port(MESSAGE_DST(msg)); + OS_ERR_TYPE err; + queue_send_message(port->queue, msg, &err); + return err; +} + +void message_free(struct message * msg) +{ + bfree(msg); +} +#endif + +uint16_t queue_process_message(T_QUEUE queue) +{ + T_QUEUE_MESSAGE m; + OS_ERR_TYPE err; + struct message * message; + uint16_t id = 0; + queue_get_message(queue, &m, OS_NO_WAIT, &err); + message = (struct message *) m; + if ( message != NULL && err == E_OS_OK) { + id = MESSAGE_ID(message); + port_process_message(message); + } + return id; +} diff --git a/system/libarc32_edu/framework/src/os/os.c b/system/libarc32_edu/framework/src/os/os.c new file mode 100644 index 00000000..a76a61da --- /dev/null +++ b/system/libarc32_edu/framework/src/os/os.c @@ -0,0 +1,94 @@ +#include "cfw/cfw.h" +#include "os/os.h" + +#ifdef TRACK_ALLOCS +int alloc_count = 0; +#endif + +void * cfw_alloc(int size, OS_ERR_TYPE * err) { + void * ptr; + unsigned int flags = interrupt_lock(); + ptr = malloc(size+sizeof(void*)); + (*(int*) ptr) = size; +#ifdef TRACK_ALLOCS + alloc_count++; +#endif + interrupt_unlock(flags); + return ptr; +} + +void cfw_free(void * ptr, OS_ERR_TYPE * err) { + int flags = interrupt_lock(); +#ifdef TRACK_ALLOCS + alloc_count--; +#endif + free(ptr); + interrupt_unlock(flags); +} + +void * balloc(uint32_t size, OS_ERR_TYPE *err) { + return cfw_alloc(size, err); +} + +OS_ERR_TYPE bfree(void *ptr) { + cfw_free(ptr, NULL); + return E_OS_OK; +} + +typedef struct queue_ { + list_head_t lh; + int count; + int used; +} q_t; + +q_t q_pool[10]; + +void queue_put(void *queue, void *msg) { + q_t * q = (q_t*) queue; + list_add(&q->lh, (list_t *)msg); +#ifdef DEBUG_OS + cfw_log("queue_put: %p <- %p\n", queue, msg); +#endif +} + +void * queue_wait(void *queue) { + q_t * q = (q_t*) queue; + void * elem = (void *)list_get(&q->lh); +#ifdef DEBUG_OS + cfw_log("queue_wait: %p -> %p\n", queue, elem); +#endif + return elem; +} + +void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int timeout, OS_ERR_TYPE* err) { + *message = queue_wait(queue); +} + +void queue_send_message (T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err) { + queue_put(queue, message); +} + +T_QUEUE queue_create(uint32_t max_size, OS_ERR_TYPE*err) { + int i, found=0; + q_t * q; + for (i=0;i<10; i++) { + q = &q_pool[i]; + if (q->used == 0) { + q->used = 1; + found = 1; + } + } + if (!found) return (T_QUEUE)NULL; + list_init(&q->lh); + q->count = 0; + return (T_QUEUE) q; +} + +void queue_delete(T_QUEUE queue, OS_ERR_TYPE* err) { + void * element = NULL; + q_t * q = (q_t*) queue; + while((element = list_get(&q->lh)) != NULL) + list_remove(&q->lh, element); + cfw_free(q, NULL); +} + diff --git a/system/libarc32_edu/framework/src/services/gpio_service_api.c b/system/libarc32_edu/framework/src/services/gpio_service_api.c new file mode 100644 index 00000000..1bddd6af --- /dev/null +++ b/system/libarc32_edu/framework/src/services/gpio_service_api.c @@ -0,0 +1,81 @@ +/** INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#include "services/gpio_service.h" +#include "cfw/cfw_client.h" + +/**************************************************************************************** + *********************** SERVICE API IMPLEMENATION ************************************** + ****************************************************************************************/ + +int gpio_configure(svc_client_handle_t * h, uint8_t index, uint8_t mode, void * priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_CONFIGURE_REQ, + sizeof(gpio_configure_req_msg_t), priv); + gpio_configure_req_msg_t * req = (gpio_configure_req_msg_t*) msg; + req->mode = mode; + req->index = index; + cfw_send_message(msg); + return 0; +} + +int gpio_set_state(svc_client_handle_t * h, uint8_t index, uint8_t val, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_SET_REQ, + sizeof(gpio_set_req_msg_t), priv); + gpio_set_req_msg_t * req = (gpio_set_req_msg_t*) msg; + req->state = val; + req->index = index; + cfw_send_message(msg); + return 0; +} + +int gpio_get_state(svc_client_handle_t * h, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_GET_REQ, sizeof(*msg), priv); + cfw_send_message(msg); + return 0; +} + +int gpio_listen(svc_client_handle_t * h, uint8_t pin, gpio_service_isr_mode_t mode, uint8_t debounce, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_LISTEN_REQ, + sizeof(gpio_listen_req_msg_t), priv); + gpio_listen_req_msg_t * req = (gpio_listen_req_msg_t*) msg; + req->index = pin; + req->mode = mode; + req->debounce = debounce; + cfw_send_message(msg); + return 0; +} + +int gpio_unlisten(svc_client_handle_t * h, uint8_t pin, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_UNLISTEN_REQ, + sizeof(gpio_unlisten_req_msg_t), priv); + gpio_unlisten_req_msg_t * req = (gpio_unlisten_req_msg_t*) msg; + req->index = pin; + cfw_send_message(msg); + return 0; +} diff --git a/system/libarc32_edu/framework/src/util/list.c b/system/libarc32_edu/framework/src/util/list.c new file mode 100644 index 00000000..55a731a5 --- /dev/null +++ b/system/libarc32_edu/framework/src/util/list.c @@ -0,0 +1,116 @@ +#include "os/os.h" +#include "util/list.h" + + +void list_init(list_head_t * list) { + list->head = list->tail = NULL; +} + +void list_add_head(list_head_t * list, list_t * element) { + if (list->head == NULL) { + list->head = list->tail = element; + } else { + element->next = list->head; + list->head = element; + } + element->next = NULL; +} + + +void list_add(list_head_t * list, list_t * element) { + uint32_t saved = interrupt_lock(); + if (list->head == NULL) { + list->head = list->tail = element; + } else { + list->tail->next = element; + list->tail = element; + } + element->next = NULL; + interrupt_unlock(saved); +} + +void list_remove(list_head_t *list, list_t * element) { + list_t * l = list->head; + /* remove first? */ + if (element == l) { + list->head = l->next; + if (list->head == NULL) { + list->tail = NULL; + } + } else { + list_t * prev = l; + while (l) { + if (l == element) { + prev->next = l->next; + if (list->tail == l) { + list->tail = prev; + } + } + prev = l; + l = l->next; + } + } +} + +void list_foreach(list_head_t * lh, void(*cb)(void *, void *), void * param) { + list_t * l = lh->head; + while(l) { + cb(l, param); + l = l->next; + } +} + +void list_foreach_del(list_head_t * lh, int(*cb)(void *, void *), void * param) { + list_t * l = lh->head; + list_t * prev = lh->head; + while(l) { + list_t * tmp; + tmp = l->next; + if (cb(l, param)) { + if (l == lh->head) { + lh->head = tmp; + prev = tmp; + if (lh->tail == l) { + lh->tail = NULL; + } + l = tmp; + } else { + prev->next = tmp; + if (lh->tail == l) { + lh->tail = prev; + } + l = prev->next; + } + } else { + prev = l; + l = l->next; + } + } +} + +list_t * list_get(list_head_t *lh) { + uint32_t saved = interrupt_lock(); + list_t * l = lh->head; + if (l != NULL) { + lh->head = l->next; + } + interrupt_unlock(saved); + return l; +} + +int list_empty(list_head_t *lh) { + return (lh->head == NULL); +} + +list_t * list_find_first(list_head_t * lh, bool(*cb)(list_t*,void*), void *data) +{ + list_t *result = NULL; + list_t *item = lh->head; + while (item && !result) { + if (cb(item,data)) { + result = item; + } + item = item->next; + } + return result; +} From 3689741b718ce769a50515d661a7ebf13cf733be Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 11:58:55 +0100 Subject: [PATCH 03/15] ATLEDGE-24 Serial Communication API Intial release of serial functionality, Serial.write, print, println, read Also added files required, Ringbuffer (not used yet), Print, etc. Also some small changes to make Intel toolchain work, still more work needed. Signed-off-by: David Hunt --- cores/arduino/Print.cpp | 261 +++++++ cores/arduino/RingBuffer.cpp | 49 ++ cores/arduino/RingBuffer.h | 41 ++ cores/arduino/Stream.cpp | 317 +++++++++ cores/arduino/UARTClass.cpp | 199 ++++++ cores/arduino/UARTClass.h | 66 ++ cores/arduino/WMath.cpp | 68 ++ cores/arduino/WString.cpp | 712 +++++++++++++++++++ cores/arduino/WString.h | 55 +- cores/arduino/abi.cpp | 38 + platform.txt | 4 +- system/libarc32_edu/bootcode/init.S | 1 + system/libarc32_edu/build/linker.cmd | 17 + system/libarc32_edu/common/board.h | 221 ++++++ system/libarc32_edu/common/irq.h | 111 +++ system/libarc32_edu/common/misc/util.h | 102 +++ system/libarc32_edu/common/uart.h | 76 ++ system/libarc32_edu/drivers/ns16550.c | 541 ++++++++++++++ system/libarc32_edu/main.c | 2 + variants/intel_edu_x/libarc32drv_edu.a | Bin 141462 -> 0 bytes variants/intel_edu_x/linker_scripts/flash.ld | 17 + variants/intel_edu_x/variant.cpp | 8 +- variants/intel_edu_x/variant.h | 20 +- 23 files changed, 2887 insertions(+), 39 deletions(-) create mode 100644 cores/arduino/Print.cpp create mode 100644 cores/arduino/RingBuffer.cpp create mode 100644 cores/arduino/RingBuffer.h create mode 100644 cores/arduino/Stream.cpp create mode 100644 cores/arduino/UARTClass.cpp create mode 100644 cores/arduino/UARTClass.h create mode 100644 cores/arduino/WMath.cpp create mode 100644 cores/arduino/WString.cpp create mode 100644 cores/arduino/abi.cpp create mode 100644 system/libarc32_edu/common/board.h create mode 100644 system/libarc32_edu/common/irq.h create mode 100644 system/libarc32_edu/common/misc/util.h create mode 100644 system/libarc32_edu/common/uart.h create mode 100644 system/libarc32_edu/drivers/ns16550.c delete mode 100755 variants/intel_edu_x/libarc32drv_edu.a diff --git a/cores/arduino/Print.cpp b/cores/arduino/Print.cpp new file mode 100644 index 00000000..78c5e360 --- /dev/null +++ b/cores/arduino/Print.cpp @@ -0,0 +1,261 @@ +/* + Print.cpp - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 23 November 2006 by David A. Mellis + */ + +#include +#include +#include +#include +#include "Arduino.h" + +#include "Print.h" + +// Public Methods ////////////////////////////////////////////////////////////// + +/* default implementation: may be overridden */ +size_t Print::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + while (size--) { + n += write(*buffer++); + } + return n; +} + +size_t Print::print(const __FlashStringHelper *ifsh) +{ + return print(reinterpret_cast(ifsh)); +} + +size_t Print::print(const String &s) +{ + size_t n = 0; + for (uint16_t i = 0; i < s.length(); i++) { + n += write(s[i]); + } + return n; +} + +size_t Print::print(const char str[]) +{ + return write(str); +} + +size_t Print::print(char c) +{ + return write(c); +} + +size_t Print::print(unsigned char b, int base) +{ + return print((unsigned long) b, base); +} + +size_t Print::print(int n, int base) +{ + return print((long) n, base); +} + +size_t Print::print(unsigned int n, int base) +{ + return print((unsigned long) n, base); +} + +size_t Print::print(long n, int base) +{ + if (base == 0) { + return write(n); + } else if (base == 10) { + if (n < 0) { + int t = print('-'); + n = -n; + return printNumber(n, 10) + t; + } + return printNumber(n, 10); + } else { + return printNumber(n, base); + } +} + +size_t Print::print(unsigned long n, int base) +{ + if (base == 0) return write(n); + else return printNumber(n, base); +} + +size_t Print::print(double n, int digits) +{ + return printFloat(n, digits); +} + +size_t Print::println(const __FlashStringHelper *ifsh) +{ + size_t n = print(ifsh); + n += println(); + return n; +} + +size_t Print::print(const Printable& x) +{ + return x.printTo(*this); +} + +size_t Print::println(void) +{ + size_t n = print('\r'); + n += print('\n'); + return n; +} + +size_t Print::println(const String &s) +{ + size_t n = print(s); + n += println(); + return n; +} + +size_t Print::println(const char c[]) +{ + size_t n = print(c); + n += println(); + return n; +} + +size_t Print::println(char c) +{ + size_t n = print(c); + n += println(); + return n; +} + +size_t Print::println(unsigned char b, int base) +{ + size_t n = print(b, base); + n += println(); + return n; +} + +size_t Print::println(int num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(unsigned int num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(unsigned long num, int base) +{ + size_t n = print(num, base); + n += println(); + return n; +} + +size_t Print::println(double num, int digits) +{ + size_t n = print(num, digits); + n += println(); + return n; +} + +size_t Print::println(const Printable& x) +{ + size_t n = print(x); + n += println(); + return n; +} + +// Private Methods ///////////////////////////////////////////////////////////// + +size_t Print::printNumber(unsigned long n, uint8_t base) { + char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. + char *str = &buf[sizeof(buf) - 1]; + + *str = '\0'; + + // prevent crash if called with base == 1 + if (base < 2) base = 10; + + do { + unsigned long m = n; + n /= base; + char c = m - base * n; + *--str = c < 10 ? c + '0' : c + 'A' - 10; + } while(n); + + return write(str); +} + +size_t Print::printFloat(double number, uint8_t digits) +{ + size_t n = 0; + + if (isnan(number)) return print("nan"); + if (isinf(number)) return print("inf"); + if (number > 4294967040.0) return print ("ovf"); // constant determined empirically + if (number <-4294967040.0) return print ("ovf"); // constant determined empirically + + // Handle negative numbers + if (number < 0.0) + { + n += print('-'); + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for (uint8_t i=0; i 0) { + n += print("."); + } + + // Extract digits from the remainder one at a time + while (digits-- > 0) + { + remainder *= 10.0; + int toPrint = int(remainder); + n += print(toPrint); + remainder -= toPrint; + } + + return n; +} diff --git a/cores/arduino/RingBuffer.cpp b/cores/arduino/RingBuffer.cpp new file mode 100644 index 00000000..c6b86498 --- /dev/null +++ b/cores/arduino/RingBuffer.cpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2011 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Dan O'Donovan Apr 29 2014: add buffer overflow control +*/ + +#include "RingBuffer.h" +#include + +RingBuffer::RingBuffer( void ) +{ + memset( _aucBuffer, 0, SERIAL_BUFFER_SIZE ) ; + _iHead=0 ; + _iTail=0 ; +} + +void RingBuffer::store_char( uint8_t c ) +{ + int i = (uint32_t)(_iHead + 1) % SERIAL_BUFFER_SIZE ; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if ( i != _iTail ) + { + _aucBuffer[_iHead] = c ; + _iHead = i ; + } + else + { + _buffer_overflow = true; + } +} + diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h new file mode 100644 index 00000000..e391ace4 --- /dev/null +++ b/cores/arduino/RingBuffer.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2011 Arduino. All right reserved. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified by Dan O'Donovan Apr 29 2014: add buffer overflow control +*/ + +#ifndef _RING_BUFFER_ +#define _RING_BUFFER_ + +#include + +// Define constants and variables for buffering incoming serial data. We're +// using a ring buffer (I think), in which head is the index of the location +// to which to write the next incoming character and tail is the index of the +// location from which to read. +#define SERIAL_BUFFER_SIZE 256 + +class RingBuffer +{ +public: + uint8_t _aucBuffer[SERIAL_BUFFER_SIZE] ; + int _iHead ; + int _iTail ; + bool _buffer_overflow ; + + RingBuffer( void ) ; + void store_char( uint8_t c ) ; + bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } +} ; + +#endif diff --git a/cores/arduino/Stream.cpp b/cores/arduino/Stream.cpp new file mode 100644 index 00000000..b31942f2 --- /dev/null +++ b/cores/arduino/Stream.cpp @@ -0,0 +1,317 @@ +/* + Stream.cpp - adds parsing methods to Stream class + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Created July 2011 + parsing functions based on TextFinder library by Michael Margolis + + findMulti/findUntil routines written by Jim Leonard/Xuth + */ + +#include "Arduino.h" +#include "Stream.h" + +#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait +#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field + +// private method to read stream with timeout +int Stream::timedRead() +{ + int c; + _startMillis = millis(); + do { + c = read(); + if (c >= 0) return c; + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// private method to peek stream with timeout +int Stream::timedPeek() +{ + int c; + _startMillis = millis(); + do { + c = peek(); + if (c >= 0) return c; + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// returns peek of the next digit in the stream or -1 if timeout +// discards non-numeric characters +int Stream::peekNextDigit() +{ + int c; + while (1) { + c = timedPeek(); + if (c < 0) return c; // timeout + if (c == '-') return c; + if (c >= '0' && c <= '9') return c; + read(); // discard non-numeric + } +} + +// Public Methods +////////////////////////////////////////////////////////////// + +void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait +{ + _timeout = timeout; +} + + // find returns true if the target string is found +bool Stream::find(char *target) +{ + return findUntil(target, strlen(target), NULL, 0); +} + +// reads data from the stream until the target string of given length is found +// returns true if target string is found, false if timed out +bool Stream::find(char *target, size_t length) +{ + return findUntil(target, length, NULL, 0); +} + +// as find but search ends if the terminator string is found +bool Stream::findUntil(char *target, char *terminator) +{ + return findUntil(target, strlen(target), terminator, strlen(terminator)); +} + +// reads data from the stream until the target string of the given length is found +// search terminated if the terminator string is found +// returns true if target string is found, false if terminated or timed out +bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) +{ + if (terminator == NULL) { + MultiTarget t[1] = {{target, targetLen, 0}}; + return findMulti(t, 1) == 0 ? true : false; + } else { + MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}}; + return findMulti(t, 2) == 0 ? true : false; + } +} + + +// returns the first valid (long) integer value from the current position. +// initial characters that are not digits (or the minus sign) are skipped +// function is terminated by the first character that is not a digit. +long Stream::parseInt() +{ + return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) +} + +// as above but a given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +long Stream::parseInt(char skipChar) +{ + bool isNegative = false; + long value = 0; + int c; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == skipChar) + ; // ignore this charactor + else if(c == '-') + isNegative = true; + else if(c >= '0' && c <= '9') // is c a digit? + value = value * 10 + c - '0'; + read(); // consume the character we got with peek + c = timedPeek(); + } + while( (c >= '0' && c <= '9') || c == skipChar ); + + if(isNegative) + value = -value; + return value; +} + + +// as parseInt but returns a floating point value +float Stream::parseFloat() +{ + return parseFloat(NO_SKIP_CHAR); +} + +// as above but the given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +float Stream::parseFloat(char skipChar){ + bool isNegative = false; + bool isFraction = false; + long value = 0; + char c; + float fraction = 1.0; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == skipChar) + ; // ignore + else if(c == '-') + isNegative = true; + else if (c == '.') + isFraction = true; + else if(c >= '0' && c <= '9') { // is c a digit? + value = value * 10 + c - '0'; + if(isFraction) + fraction *= 0.1; + } + read(); // consume the character we got with peek + c = timedPeek(); + } + while( (c >= '0' && c <= '9') || c == '.' || c == skipChar ); + + if(isNegative) + value = -value; + if(isFraction) + return value * fraction; + else + return value; +} + +// read characters from stream into buffer +// terminates if length characters have been read, or timeout (see setTimeout) +// returns the number of characters placed in the buffer +// the buffer is NOT null terminated. +// +size_t Stream::readBytes(char *buffer, size_t length) +{ + size_t count = 0; + while (count < length) { + int c = timedRead(); + if (c < 0) break; + *buffer++ = (char)c; + count++; + } + return count; +} + + +// as readBytes with terminator character +// terminates if length characters have been read, timeout, or if the terminator character detected +// returns the number of characters placed in the buffer (0 means no valid data found) + +size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) +{ + if (length < 1) return 0; + size_t index = 0; + while (index < length) { + int c = timedRead(); + if (c < 0 || c == terminator) break; + *buffer++ = (char)c; + index++; + } + return index; // return number of characters, not including null terminator +} + +String Stream::readString() +{ + String ret; + int c = timedRead(); + while (c >= 0) + { + ret += (char)c; + c = timedRead(); + } + return ret; +} + +String Stream::readStringUntil(char terminator) +{ + String ret; + int c = timedRead(); + while (c >= 0 && c != terminator) + { + ret += (char)c; + c = timedRead(); + } + return ret; +} + +int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) { + // any zero length target string automatically matches and would make + // a mess of the rest of the algorithm. + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + if (t->len <= 0) + return t - targets; + } + + while (1) { + int c = timedRead(); + if (c < 0) + return -1; + + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + // the simple case is if we match, deal with that first. + if (c == t->str[t->index]) { + if (++t->index == t->len) + return t - targets; + else + continue; + } + + // if not we need to walk back and see if we could have matched further + // down the stream (ie '1112' doesn't match the first position in '11112' + // but it will match the second position so we can't just reset the current + // index to 0 when we find a mismatch. + if (t->index == 0) + continue; + + int origIndex = t->index; + do { + --t->index; + // first check if current char works against the new current index + if (c != t->str[t->index]) + continue; + + // if it's the only char then we're good, nothing more to check + if (t->index == 0) { + t->index++; + break; + } + + // otherwise we need to check the rest of the found string + int diff = origIndex - t->index; + size_t i; + for (i = 0; i < t->index; ++i) { + if (t->str[i] != t->str[i + diff]) + break; + } + + // if we successfully got through the previous loop then our current + // index is good. + if (i == t->index) { + t->index++; + break; + } + + // otherwise we just try the next index + } while (t->index); + } + } + // unreachable + return -1; +} diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp new file mode 100644 index 00000000..2f89cb93 --- /dev/null +++ b/cores/arduino/UARTClass.cpp @@ -0,0 +1,199 @@ +/* + Copyright (c) 2011 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "Arduino.h" +#include "portable.h" +#include "UARTClass.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +// Constructors //////////////////////////////////////////////////////////////// + +//UARTClass::UARTClass( Uart *pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer ) +UARTClass::UARTClass( uart_init_info *info, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer ) +{ + this->info = info; + this->_rx_buffer = pRx_buffer; + this->_tx_buffer = pTx_buffer; +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void UARTClass::begin(const uint32_t dwBaudRate) +{ + begin(dwBaudRate, 0); +} + +void UARTClass::begin(const uint32_t dwBaudRate, const int config) +{ + init(dwBaudRate, config ); +} + + +void UARTClass::init(const uint32_t dwBaudRate, const uint32_t modeReg) +{ + // Make sure both ring buffers are initialized back to empty. + _rx_buffer->_iHead = _rx_buffer->_iTail = 0; + _tx_buffer->_iHead = _tx_buffer->_iTail = 0; + + + SET_PIN_MODE(17, UART_MUX_MODE); // Rdx SOC PIN + SET_PIN_MODE(16, UART_MUX_MODE); // Txd SOC PIN + + info->options = 0; + info->sys_clk_freq = 32000000; + info->baud_rate = dwBaudRate; + info->regs = PERIPH_ADDR_BASE_UART1; + info->irq = IRQ_UART1_INTR; + info->int_pri = 0; + + uart_init(0, info); + +} + +void UARTClass::end( void ) +{ + // Clear any received data + _rx_buffer->_iHead = _rx_buffer->_iTail; + + // Wait for any outstanding data to be sent + flush(); +} + +void UARTClass::setInterruptPriority(uint32_t priority) +{ + //NVIC_SetPriority(_dwIrq, priority & 0x0F); +} + +uint32_t UARTClass::getInterruptPriority() +{ + //return NVIC_GetPriority(_dwIrq); + return 0; +} + +int UARTClass::available( void ) +{ + //return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE; + return 0; +} + +int UARTClass::availableForWrite(void) +{ + int head = _tx_buffer->_iHead; + int tail = _tx_buffer->_iTail; + if (head >= tail) return SERIAL_BUFFER_SIZE - 1 - head + tail; + return tail - head - 1; +} + +int UARTClass::peek( void ) +{ + if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) + return -1; + + return _rx_buffer->_aucBuffer[_rx_buffer->_iTail]; +} + +int UARTClass::read( void ) +{ + uint8_t uc_data; + int ret; + ret = uart_poll_in(0, &uc_data); + if ( ret==-1 ) + return -1; + else + return uc_data; +#if 0 + // if the head isn't ahead of the tail, we don't have any characters + if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) + return -1; + + uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail]; + _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + return uc; +#endif +} + +void UARTClass::flush( void ) +{ + while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent + // Wait for transmission to complete + //while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY); +} + +size_t UARTClass::write( const uint8_t uc_data ) +{ + + uart_poll_out(0, uc_data); +#if 0 + // Is the hardware currently busy? + if (_tx_buffer->_iTail != _tx_buffer->_iHead) + { + // If busy we buffer + unsigned int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE; + while (_tx_buffer->_iTail == l) + ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent + + _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data; + _tx_buffer->_iHead = l; + // Make sure TX interrupt is enabled + //_pUart->UART_IER = UART_IER_TXRDY; + } + else + { + // Bypass buffering and send character directly + //_pUart->UART_THR = uc_data; + } +#endif + return 1; +} + +void UARTClass::IrqHandler( void ) +{ +#if 0 + uint32_t status = _pUart->UART_SR; + + // Did we receive data? + if ((status & UART_SR_RXRDY) == UART_SR_RXRDY) + _rx_buffer->store_char(_pUart->UART_RHR); + + // Do we need to keep sending data? + if ((status & UART_SR_TXRDY) == UART_SR_TXRDY) + { + if (_tx_buffer->_iTail != _tx_buffer->_iHead) { + _pUart->UART_THR = _tx_buffer->_aucBuffer[_tx_buffer->_iTail]; + _tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + } + else + { + // Mask off transmit interrupt so we don't get it anymore + _pUart->UART_IDR = UART_IDR_TXRDY; + } + } + + // Acknowledge errors + if ((status & UART_SR_OVRE) == UART_SR_OVRE || (status & UART_SR_FRAME) == UART_SR_FRAME) + { + // TODO: error reporting outside ISR + _pUart->UART_CR |= UART_CR_RSTSTA; + } +#endif +} + diff --git a/cores/arduino/UARTClass.h b/cores/arduino/UARTClass.h new file mode 100644 index 00000000..93fd6cc7 --- /dev/null +++ b/cores/arduino/UARTClass.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2011 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _UART_CLASS_ +#define _UART_CLASS_ + +#include "HardwareSerial.h" +#include "RingBuffer.h" + +#include +#include + +class UARTClass : public HardwareSerial +{ + public: + //UARTClass(Uart* pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer, RingBuffer* pTx_buffer); + UARTClass(uart_init_info *info, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer ); + + void begin(const uint32_t dwBaudRate); + void begin(const uint32_t dwBaudRate, const int config); + void end(void); + int available(void); + int availableForWrite(void); + int peek(void); + int read(void); + void flush(void); + size_t write(const uint8_t c); + //TODO implemtn Print + //using Print::write; // pull in write(str) and write(buf, size) from Print + + void setInterruptPriority(uint32_t priority); + uint32_t getInterruptPriority(); + + void IrqHandler(void); + + operator bool() { return true; }; // UART always active + + protected: + void init(const uint32_t dwBaudRate, const uint32_t config); + + RingBuffer *_rx_buffer; + RingBuffer *_tx_buffer; + + uart_init_info *info; + //Uart* _pUart; + //IRQn_Type _dwIrq; + uint32_t _dwId; + +}; + +#endif // _UART_CLASS_ diff --git a/cores/arduino/WMath.cpp b/cores/arduino/WMath.cpp new file mode 100644 index 00000000..ec2e29b2 --- /dev/null +++ b/cores/arduino/WMath.cpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2011 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +extern "C" { + #include "stdlib.h" + #include "stdint.h" +} +#include "WMath.h" + +extern void randomSeed( uint32_t dwSeed ) +{ + if ( dwSeed != 0 ) + { + srand( dwSeed ) ; + } +} + +extern long random( long howbig ) +{ + if ( howbig == 0 ) + { + return 0 ; + } + + return rand() % howbig; +} + +extern long random( long howsmall, long howbig ) +{ + if (howsmall >= howbig) + { + return howsmall; + } + + long diff = howbig - howsmall; + + return random(diff) + howsmall; +} + +extern long map(long x, long in_min, long in_max, long out_min, long out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +extern uint16_t makeWord( uint16_t w ) +{ + return w ; +} + +extern uint16_t makeWord( uint8_t h, uint8_t l ) +{ + return (h << 8) | l ; +} diff --git a/cores/arduino/WString.cpp b/cores/arduino/WString.cpp new file mode 100644 index 00000000..73222678 --- /dev/null +++ b/cores/arduino/WString.cpp @@ -0,0 +1,712 @@ +/* + WString.cpp - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All rights reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Daniel Hugo on 6 Mar 2014: + - Implement operator+ in .h per Arduino spec, fix long snprintf + fmt in .cpp. + + Modified by Manoel Ramon 6 Jun 6 2014: + - String + does not handle implicit type of string "" + +*/ + +#include "WString.h" +#include + + +// following the C++ standard operators with attributes in right +// side must be defined globally +String operator + ( const char *cstr, const String &str_arg) +{ + String &str_arg_o = const_cast(str_arg); + String aux = String(cstr); + aux.concat(str_arg_o); + return aux; +} +/*********************************************/ +/* Constructors */ +/*********************************************/ + +String::String(const char *cstr) +{ + init(); + if (cstr) copy(cstr, strlen(cstr)); +} + +String::String(const String &value) +{ + init(); + *this = value; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +String::String(String &&rval) +{ + init(); + move(rval); +} +String::String(StringSumHelper &&rval) +{ + init(); + move(rval); +} +#endif + +String::String(char c) +{ + init(); + char buf[2]; + buf[0] = c; + buf[1] = 0; + *this = buf; +} + +static char * c_spec_signed[] = { + (char *) "%d", + (char *) "%ld", + (char *) "%o", + (char *) "%x", + (char *) "unsupported base", + }; + +static char * c_spec_unsigned[] = { + (char *) "%u", + (char *) "%lu", + (char *) "%o", + (char *) "%x", + (char *) "unsupported base", + }; + + +char * String::getCSpec(int base, bool issigned, bool islong){ + int int_idx = 0; + + if(islong == true) + int_idx = 1; + + switch(base){ + case 8: + if(issigned == true) + return c_spec_signed[2]; + else + return c_spec_unsigned[2]; + case 10: + if(issigned == true) + return c_spec_signed[int_idx]; + else + return c_spec_unsigned[int_idx]; + case 16: + if(issigned == true) + return c_spec_signed[3]; + else + return c_spec_unsigned[3]; + default: + return c_spec_unsigned[4]; + } +}; + +String::String(unsigned char value, unsigned char base) +{ + init(); + char buf[9]; + //utoa(value, buf, base); + snprintf(buf, sizeof(buf), getCSpec(base, false, false), value); + *this = buf; +} + +String::String(int value, unsigned char base) +{ + init(); + char buf[18]; + //itoa(value, buf, base); + snprintf(buf, sizeof(buf), getCSpec(base, true, false), value); + *this = buf; +} + +String::String(unsigned int value, unsigned char base) +{ + init(); + char buf[17]; + //utoa(value, buf, base); + snprintf(buf, sizeof(buf), getCSpec(base, false, false), value); + *this = buf; +} + +String::String(long value, unsigned char base) +{ + init(); + char buf[34]; + //ltoa(value, buf, base); + snprintf(buf, sizeof(buf), getCSpec(base, true, true), value); + *this = buf; +} + +String::String(unsigned long value, unsigned char base) +{ + init(); + char buf[33]; + //ultoa(value, buf, base); + snprintf(buf, sizeof(buf), getCSpec(base, false, true), value); + *this = buf; +} + +String::~String() +{ + free(buffer); +} + +/*********************************************/ +/* Memory Management */ +/*********************************************/ + +inline void String::init(void) +{ + buffer = NULL; + capacity = 0; + len = 0; + flags = 0; +} + +void String::invalidate(void) +{ + if (buffer) free(buffer); + buffer = NULL; + capacity = len = 0; +} + +unsigned char String::reserve(unsigned int size) +{ + if (buffer && capacity >= size) return 1; + if (changeBuffer(size)) { + if (len == 0) buffer[0] = 0; + return 1; + } + return 0; +} + +unsigned char String::changeBuffer(unsigned int maxStrLen) +{ + char *newbuffer = (char *)realloc(buffer, maxStrLen + 1); + if (newbuffer) { + buffer = newbuffer; + capacity = maxStrLen; + return 1; + } + return 0; +} + +/*********************************************/ +/* Copy and Move */ +/*********************************************/ + +String & String::copy(const char *cstr, unsigned int _length) +{ + if (!reserve(_length)) { + invalidate(); + return *this; + } + len = _length; + strcpy(buffer, cstr); + return *this; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +void String::move(String &rhs) +{ + if (buffer) { + if (capacity >= rhs.len) { + strcpy(buffer, rhs.buffer); + len = rhs.len; + rhs.len = 0; + return; + } else { + free(buffer); + } + } + buffer = rhs.buffer; + capacity = rhs.capacity; + len = rhs.len; + rhs.buffer = NULL; + rhs.capacity = 0; + rhs.len = 0; +} +#endif + +String & String::operator = (const String &rhs) +{ + if (this == &rhs) return *this; + + if (rhs.buffer) copy(rhs.buffer, rhs.len); + else invalidate(); + + return *this; +} + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +String & String::operator = (String &&rval) +{ + if (this != &rval) move(rval); + return *this; +} + +String & String::operator = (StringSumHelper &&rval) +{ + if (this != &rval) move(rval); + return *this; +} +#endif + +String & String::operator = (const char *cstr) +{ + if (cstr) copy(cstr, strlen(cstr)); + else invalidate(); + + return *this; +} + +/*********************************************/ +/* concat */ +/*********************************************/ + +unsigned char String::concat(const String &s) +{ + return concat(s.buffer, s.len); +} + +unsigned char String::concat(const char *cstr, unsigned int _length) +{ + unsigned int newlen = len + _length; + if (!cstr) return 0; + if (_length == 0) return 1; + if (!reserve(newlen)) return 0; + strcpy(buffer + len, cstr); + len = newlen; + return 1; +} + +unsigned char String::concat(const char *cstr) +{ + if (!cstr) return 0; + return concat(cstr, strlen(cstr)); +} + +unsigned char String::concat(char c) +{ + char buf[2]; + buf[0] = c; + buf[1] = 0; + return concat(buf, 1); +} + +unsigned char String::concat(unsigned char num) +{ + char buf[4]; + //itoa(num, buf, 10); + snprintf(buf, sizeof(buf), getCSpec(10, true, false), num); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(int num) +{ + char buf[7]; + //itoa(num, buf, 10); + snprintf(buf, sizeof(buf), getCSpec(10, true, false), num); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(unsigned int num) +{ + char buf[6]; + //utoa(num, buf, 10); + snprintf(buf, sizeof(buf), getCSpec(10, false, false), num); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(long num) +{ + char buf[12]; + //ltoa(num, buf, 10); + snprintf(buf, sizeof(buf), getCSpec(10, true, true), num); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(unsigned long num) +{ + char buf[11]; + //ultoa(num, buf, 10); + snprintf(buf, sizeof(buf), getCSpec(10, false, true), num); + return concat(buf, strlen(buf)); +} + +/*********************************************/ +/* Concatenate */ +/*********************************************/ + +StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(rhs.buffer, rhs.len)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr) +{ + StringSumHelper &a = const_cast(lhs); + if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, char c) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(c)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, int num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, long num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +/*********************************************/ +/* Comparison */ +/*********************************************/ + +int String::compareTo(const String &s) const +{ + if (!buffer || !s.buffer) { + if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer; + if (buffer && len > 0) return *(unsigned char *)buffer; + return 0; + } + return strcmp(buffer, s.buffer); +} + +unsigned char String::equals(const String &s2) const +{ + return (len == s2.len && compareTo(s2) == 0); +} + +unsigned char String::equals(const char *cstr) const +{ + if (len == 0) return (cstr == NULL || *cstr == 0); + if (cstr == NULL) return buffer[0] == 0; + return strcmp(buffer, cstr) == 0; +} + +unsigned char String::operator<(const String &rhs) const +{ + return compareTo(rhs) < 0; +} + +unsigned char String::operator>(const String &rhs) const +{ + return compareTo(rhs) > 0; +} + +unsigned char String::operator<=(const String &rhs) const +{ + return compareTo(rhs) <= 0; +} + +unsigned char String::operator>=(const String &rhs) const +{ + return compareTo(rhs) >= 0; +} + +unsigned char String::equalsIgnoreCase( const String &s2 ) const +{ + if (this == &s2) return 1; + if (len != s2.len) return 0; + if (len == 0) return 1; + const char *p1 = buffer; + const char *p2 = s2.buffer; + while (*p1) { + if (tolower(*p1++) != tolower(*p2++)) return 0; + } + return 1; +} + +unsigned char String::startsWith( const String &s2 ) const +{ + if (len < s2.len) return 0; + return startsWith(s2, 0); +} + +unsigned char String::startsWith( const String &s2, unsigned int offset ) const +{ + if (offset > len - s2.len || !buffer || !s2.buffer) return 0; + return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0; +} + +unsigned char String::endsWith( const String &s2 ) const +{ + if ( len < s2.len || !buffer || !s2.buffer) return 0; + return strcmp(&buffer[len - s2.len], s2.buffer) == 0; +} + +/*********************************************/ +/* Character Access */ +/*********************************************/ + +char String::charAt(unsigned int loc) const +{ + return operator[](loc); +} + +void String::setCharAt(unsigned int loc, char c) +{ + if (loc < len) buffer[loc] = c; +} + +char & String::operator[](unsigned int index) +{ + static char dummy_writable_char; + if (index >= len || !buffer) { + dummy_writable_char = 0; + return dummy_writable_char; + } + return buffer[index]; +} + +char String::operator[]( unsigned int index ) const +{ + if (index >= len || !buffer) return 0; + return buffer[index]; +} + +void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const +{ + if (!bufsize || !buf) return; + if (index >= len) { + buf[0] = 0; + return; + } + unsigned int n = bufsize - 1; + if (n > len - index) n = len - index; + strncpy((char *)buf, buffer + index, n); + buf[n] = 0; +} + +/*********************************************/ +/* Search */ +/*********************************************/ + +int String::indexOf(char c) const +{ + return indexOf(c, 0); +} + +int String::indexOf( char ch, unsigned int fromIndex ) const +{ + if (fromIndex >= len) return -1; + const char* temp = strchr(buffer + fromIndex, ch); + if (temp == NULL) return -1; + return temp - buffer; +} + +int String::indexOf(const String &s2) const +{ + return indexOf(s2, 0); +} + +int String::indexOf(const String &s2, unsigned int fromIndex) const +{ + if (fromIndex >= len) return -1; + const char *found = strstr(buffer + fromIndex, s2.buffer); + if (found == NULL) return -1; + return found - buffer; +} + +int String::lastIndexOf( char theChar ) const +{ + return lastIndexOf(theChar, len - 1); +} + +int String::lastIndexOf(char ch, unsigned int fromIndex) const +{ + if (fromIndex >= len) return -1; + char tempchar = buffer[fromIndex + 1]; + buffer[fromIndex + 1] = '\0'; + char* temp = strrchr( buffer, ch ); + buffer[fromIndex + 1] = tempchar; + if (temp == NULL) return -1; + return temp - buffer; +} + +int String::lastIndexOf(const String &s2) const +{ + return lastIndexOf(s2, len - s2.len); +} + +int String::lastIndexOf(const String &s2, unsigned int fromIndex) const +{ + if (s2.len == 0 || len == 0 || s2.len > len) return -1; + if (fromIndex >= len) fromIndex = len - 1; + int found = -1; + for (char *p = buffer; p <= buffer + fromIndex; p++) { + p = strstr(p, s2.buffer); + if (!p) break; + if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer; + } + return found; +} + +String String::substring(unsigned int left, unsigned int right) const +{ + if (left > right) { + unsigned int temp = right; + right = left; + left = temp; + } + String out; + if (left > len) return out; + if (right > len) right = len; + char temp = buffer[right]; // save the replaced character + buffer[right] = '\0'; + out = buffer + left; // pointer arithmetic + buffer[right] = temp; //restore character + return out; +} + +/*********************************************/ +/* Modification */ +/*********************************************/ + +void String::replace(char find, char _replace) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + if (*p == find) *p = _replace; + } +} + +void String::replace(const String& find, const String& _replace) +{ + if (len == 0 || find.len == 0) return; + int diff = _replace.len - find.len; + char *readFrom = buffer; + char *foundAt; + if (diff == 0) { + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + memcpy(foundAt, _replace.buffer, _replace.len); + readFrom = foundAt + _replace.len; + } + } else if (diff < 0) { + char *writeTo = buffer; + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + unsigned int n = foundAt - readFrom; + memcpy(writeTo, readFrom, n); + writeTo += n; + memcpy(writeTo, _replace.buffer, _replace.len); + writeTo += _replace.len; + readFrom = foundAt + find.len; + len += diff; + } + strcpy(writeTo, readFrom); + } else { + unsigned int size = len; // compute size needed for result + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + readFrom = foundAt + find.len; + size += diff; + } + if (size == len) return; + if (size > capacity && !changeBuffer(size)) return; // XXX: tell user! + int index = len - 1; + while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) { + readFrom = buffer + index + find.len; + memmove(readFrom + diff, readFrom, len - (readFrom - buffer)); + len += diff; + buffer[len] = 0; + memcpy(buffer + index, _replace.buffer, _replace.len); + index--; + } + } +} + +void String::toLowerCase(void) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + *p = tolower(*p); + } +} + +void String::toUpperCase(void) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + *p = toupper(*p); + } +} + +void String::trim(void) +{ + if (!buffer || len == 0) return; + char *begin = buffer; + while (isspace(*begin)) begin++; + char *end = buffer + len - 1; + while (isspace(*end) && end >= begin) end--; + len = end + 1 - begin; + if (begin > buffer) memcpy(buffer, begin, len); + buffer[len] = 0; +} + +/*********************************************/ +/* Parsing / Conversion */ +/*********************************************/ + +long String::toInt(void) const +{ + if (buffer) return atol(buffer); + return 0; +} + + diff --git a/cores/arduino/WString.h b/cores/arduino/WString.h index 5cbdc882..dfe561a9 100644 --- a/cores/arduino/WString.h +++ b/cores/arduino/WString.h @@ -17,6 +17,10 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Daniel Hugo Thu Mar 6 17:14:38 2014: + Implement operator+ in .h per Arduino spec, fix long snprintf + fmt in .cpp. */ #ifndef String_class_h @@ -26,6 +30,7 @@ #include #include #include +//#include // When compiling programs with this class, the following gcc parameters // dramatically increase performance and memory (RAM) efficiency, typically @@ -57,7 +62,6 @@ class String // be false). String(const char *cstr = ""); String(const String &str); - String(const __FlashStringHelper *str); #ifdef __GXX_EXPERIMENTAL_CXX0X__ String(String &&rval); String(StringSumHelper &&rval); @@ -68,8 +72,6 @@ class String explicit String(unsigned int, unsigned char base=10); explicit String(long, unsigned char base=10); explicit String(unsigned long, unsigned char base=10); - explicit String(float, unsigned char decimalPlaces=2); - explicit String(double, unsigned char decimalPlaces=2); ~String(void); // memory management @@ -80,21 +82,20 @@ class String inline unsigned int length(void) const {return len;} // creates a copy of the assigned value. if the value is null or - // invalid, or if the memory allocation fails, the string will be + // invalid, or if the memory allocation fails, the string will be // marked as invalid ("if (s)" will be false). String & operator = (const String &rhs); String & operator = (const char *cstr); - String & operator = (const __FlashStringHelper *str); #ifdef __GXX_EXPERIMENTAL_CXX0X__ String & operator = (String &&rval); String & operator = (StringSumHelper &&rval); #endif // concatenate (works w/ built-in types) - + // returns true on success, false on failure (in which case, the string - // is left unchanged). if the argument is null or invalid, the - // concatenation is considered unsucessful. + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsucessful. unsigned char concat(const String &str); unsigned char concat(const char *cstr); unsigned char concat(char c); @@ -103,24 +104,30 @@ class String unsigned char concat(unsigned int num); unsigned char concat(long num); unsigned char concat(unsigned long num); - unsigned char concat(float num); - unsigned char concat(double num); - unsigned char concat(const __FlashStringHelper * str); - + // if there's not enough memory for the concatenated value, the string // will be left unchanged (but this isn't signalled in any way) String & operator += (const String &rhs) {concat(rhs); return (*this);} String & operator += (const char *cstr) {concat(cstr); return (*this);} String & operator += (char c) {concat(c); return (*this);} - String & operator += (unsigned char num) {concat(num); return (*this);} + String & operator += (unsigned char num) {concat(num); return (*this);} String & operator += (int num) {concat(num); return (*this);} String & operator += (unsigned int num) {concat(num); return (*this);} String & operator += (long num) {concat(num); return (*this);} String & operator += (unsigned long num) {concat(num); return (*this);} - String & operator += (float num) {concat(num); return (*this);} - String & operator += (double num) {concat(num); return (*this);} - String & operator += (const __FlashStringHelper *str){concat(str); return (*this);} + + // Implement StringAdditionOperator per Arduino docs... String + __ + String operator + (const String &rhs) {return String(*this) += rhs;} + String operator + (const char *cstr) {return String(*this) += cstr;} + String operator + (char c) {return String(*this) += c;} + String operator + (unsigned char num) {return String(*this) += num;} + String operator + (int num) {return String(*this) += num;} + String operator + (unsigned int num) {return String(*this) += num;} + String operator + (long num) {return String(*this) += num;} + String operator + (unsigned long num) {return String(*this) += num;} + +#if 0 friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs); friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr); friend StringSumHelper & operator + (const StringSumHelper &lhs, char c); @@ -129,9 +136,7 @@ class String friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num); friend StringSumHelper & operator + (const StringSumHelper &lhs, long num); friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num); - friend StringSumHelper & operator + (const StringSumHelper &lhs, float num); - friend StringSumHelper & operator + (const StringSumHelper &lhs, double num); - friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs); +#endif // comparison (only works w/ Strings and "strings") operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; } @@ -159,7 +164,6 @@ class String void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const; void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const {getBytes((unsigned char *)buf, bufsize, index);} - const char * c_str() const { return buffer; } // search int indexOf( char ch ) const; @@ -176,21 +180,19 @@ class String // modification void replace(char find, char replace); void replace(const String& find, const String& replace); - void remove(unsigned int index); - void remove(unsigned int index, unsigned int count); void toLowerCase(void); void toUpperCase(void); void trim(void); // parsing/conversion long toInt(void) const; - float toFloat(void) const; + char * getCSpec(int base, bool issigned, bool islong); -protected: char *buffer; // the actual char array unsigned int capacity; // the array length minus one (for the '\0') unsigned int len; // the String length (not counting the '\0') -protected: + unsigned char flags; // unused, for future features + void init(void); void invalidate(void); unsigned char changeBuffer(unsigned int maxStrLen); @@ -198,7 +200,6 @@ class String // copy and move String & copy(const char *cstr, unsigned int length); - String & copy(const __FlashStringHelper *pstr, unsigned int length); #ifdef __GXX_EXPERIMENTAL_CXX0X__ void move(String &rhs); #endif @@ -215,8 +216,6 @@ class StringSumHelper : public String StringSumHelper(unsigned int num) : String(num) {} StringSumHelper(long num) : String(num) {} StringSumHelper(unsigned long num) : String(num) {} - StringSumHelper(float num) : String(num) {} - StringSumHelper(double num) : String(num) {} }; #endif // __cplusplus diff --git a/cores/arduino/abi.cpp b/cores/arduino/abi.cpp new file mode 100644 index 00000000..b9472bec --- /dev/null +++ b/cores/arduino/abi.cpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2014 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); +extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); + + +void __cxa_pure_virtual(void) { + // We might want to write some diagnostics to uart in this case + //std::terminate(); + while (1) + ; +} + +void __cxa_deleted_virtual(void) { + // We might want to write some diagnostics to uart in this case + //std::terminate(); + while (1) + ; +} + diff --git a/platform.txt b/platform.txt index a28f58d7..fa229c76 100644 --- a/platform.txt +++ b/platform.txt @@ -20,7 +20,7 @@ compiler.c.elf.flags=-nostartfiles -nodefaultlibs -nostdlib -static -Wl,-X -Wl,- compiler.S.flags=-c -g -x assembler-with-cpp #compiler.cpp.cmd=arc-elf32-g++ compiler.cpp.cmd=arc-elf32-gcc -compiler.cpp.flags=-c -mARCv2EM -mav2em -mlittle-endian -g -Os -Wall -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections -MMD +compiler.cpp.flags=-c -mARCv2EM -mav2em -mlittle-endian -g -Os -Wall -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections -MMD -fno-rtti compiler.ar.cmd=arc-elf32-ar compiler.ar.flags=rcs compiler.objcopy.cmd=arc-elf32-objcopy @@ -73,7 +73,7 @@ recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.f recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" ## Create output (.elf file) -recipe.objcopy.eep.pattern="{compiler.path}{compiler.strip.cmd}" "{build.path}/{build.project_name}.elf" +##recipe.objcopy.eep.pattern="{compiler.path}{compiler.strip.cmd}" "{build.path}/{build.project_name}.elf" ## Compute size recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index a0a34890..243c38ef 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -40,6 +40,7 @@ _do_reset: /* Set up stack pointer */ // mov sp, @_DefaultStack + STACK_SIZE mov sp, @__stack_start + mov gp, @__SDATA_BEGIN__ /* Enable instruction cache */ mov r0, 0 sr r0, [ARC_V2_IC_CTRL] diff --git a/system/libarc32_edu/build/linker.cmd b/system/libarc32_edu/build/linker.cmd index aec52454..383455b3 100644 --- a/system/libarc32_edu/build/linker.cmd +++ b/system/libarc32_edu/build/linker.cmd @@ -32,6 +32,9 @@ MEMORY /* Putting stack at end of SRAM for now */ __stack_start = ORIGIN(SRAM)+LENGTH(SRAM); +/* Allocating heap size of 2048 bytes for now */ +__HEAP_SIZE = 2048; + SECTIONS { /* FLASH Start */ @@ -94,6 +97,12 @@ SECTIONS *(".data.*") } > SRAM + sdata : + { + __SDATA_BEGIN__ = .; + *(.sdata .sdata.* .gnu.linkonce.s.*) + } > SRAM + __data_ram_end = .; bss (NOLOAD) : @@ -132,6 +141,14 @@ SECTIONS *(".seg_rxtx.*") } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM + /* Define linker symbols */ _end = .; /* end of image */ diff --git a/system/libarc32_edu/common/board.h b/system/libarc32_edu/common/board.h new file mode 100644 index 00000000..a2a0a2b7 --- /dev/null +++ b/system/libarc32_edu/common/board.h @@ -0,0 +1,221 @@ +/* board.h - board configuration macros for the atlas_peak-arc BSP */ + +/* + * Copyright (c) 2014-2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +This header file is used to specify and describe board-level aspects for the +'atlas_peak-arc' BSP. +*/ + +#ifndef _BOARD__H_ +#define _BOARD__H_ + +#include + +/* default system clock */ + +#define SYSCLK_DEFAULT_IOSC_HZ MHZ(32) + +/* address bases */ + +#define PERIPH_ADDR_BASE_ADC 0x80015000 /* ADC */ + +#define PERIPH_ADDR_BASE_CREG_MST0 0x80018000 /* CREG Master 0 */ +#define PERIPH_ADDR_BASE_CREG_SLV0 0x80018080 /* CREG Slave 0 */ +#define PERIPH_ADDR_BASE_CREG_SLV1 0x80018180 /* CREG Slave 1 */ + +#define PERIPH_ADDR_BASE_GPIO0 0x80017800 /* GPIO 0 */ +#define PERIPH_ADDR_BASE_GPIO1 0x80017900 /* GPIO 1 */ + +#define PERIPH_ADDR_BASE_I2C_MST0 0x80012000 /* I2C Master 0 */ +#define PERIPH_ADDR_BASE_I2C_MST1 0x80012100 /* I2C Master 1 */ + +#define PERIPH_ADDR_BASE_SPI_MST0 0x80010000 /* SPI Master 0 */ +#define PERIPH_ADDR_BASE_SPI_MST1 0x80010100 /* SPI Master 1 */ + +#ifdef CONFIG_NSIM +#define PERIPH_ADDR_BASE_UART0 0x4242 /* UART A */ +#else +#define PERIPH_ADDR_BASE_UART0 0xB0002000 /* UART A */ +#define PERIPH_ADDR_BASE_UART1 0xB0002400 /* UART B */ +#endif + +/* IRQs */ + +#define IRQ_TIMER0 16 +#define IRQ_TIMER1 17 +#define IRQ_I2C0_RX_AVAIL 18 +#define IRQ_I2C0_TX_REQ 19 +#define IRQ_I2C0_STOP_DET 20 +#define IRQ_I2C0_ERR 21 +#define IRQ_I2C1_RX_AVAIL 22 +#define IRQ_I2C1_TX_REQ 23 +#define IRQ_I2C1_STOP_DET 24 +#define IRQ_I2C1_ERR 25 +#define IRQ_SPI0_ERR_INT 26 +#define IRQ_SPI0_RX_AVAIL 27 +#define IRQ_SPI0_TX_REQ 28 +#define IRQ_SPI1_ERR_INT 29 +#define IRQ_SPI1_RX_AVAIL 30 +#define IRQ_SPI1_TX_REQ 31 +#define IRQ_ADC_IRQ 32 +#define IRQ_ADC_ERR 33 +#define IRQ_GPIO0_INTR 34 +#define IRQ_GPIO1_INTR 35 +#define IRQ_I2C_MST0_INTR 36 +#define IRQ_I2C_MST1_INTR 37 +#define IRQ_SPI_MST0_INTR 38 +#define IRQ_SPI_MST1_INTR 39 +#define IRQ_SPI_SLV_INTR 40 +#define IRQ_UART0_INTR 41 +#define IRQ_UART1_INTR 42 +#define IRQ_I2S_INTR 43 +#define IRQ_GPIO_INTR 44 +#define IRQ_PWM_TIMER_INTR 45 +#define IRQ_USB_INTR 46 +#define IRQ_RTC_INTR 47 +#define IRQ_WDOG_INTR 48 +#define IRQ_DMA_CHAN0 49 +#define IRQ_DMA_CHAN1 50 +#define IRQ_DMA_CHAN2 51 +#define IRQ_DMA_CHAN3 52 +#define IRQ_DMA_CHAN4 53 +#define IRQ_DMA_CHAN5 54 +#define IRQ_DMA_CHAN6 55 +#define IRQ_DMA_CHAN7 56 +#define IRQ_MAILBOXES_INTR 57 +#define IRQ_COMPARATORS_INTR 58 +#define IRQ_SYS_PMU_INTR 59 +#define IRQ_DMA_CHANS_ERR 60 +#define IRQ_INT_SRAM_CTLR 61 +#define IRQ_INT_FLASH0_CTLR 62 +#define IRQ_INT_FLASH1_CTLR 63 +#define IRQ_ALWAYS_ON_TMR 64 +#define IRQ_ADC_PWR 65 +#define IRQ_ADC_CALIB 66 +#define IRQ_ALWAYS_ON_GPIO 67 + +#ifndef _ASMLANGUAGE + +#define EXC_FROM_IRQ(irq) ((irq) + 16) +#define VECTOR_FROM_IRQ(irq) EXC_FROM_IRQ(irq) +#define VECTOR_ADDR(vector) ((uint32_t *)((int)vector << 2)) + +#include +//#include + +/* ARCv2 timer 0 configuration settings for the system clock */ +#ifdef CONFIG_NANOKERNEL +#define CONFIG_ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock \ + */ +#define CONFIG_ARCV2_TIMER1_CLOCK_FREQ CONFIG_ARCV2_TIMER0_CLOCK_FREQ +#endif /* CONFIG_NANOKERNEL */ + +#define CONFIG_ARCV2_TIMER0_INT_LVL IRQ_TIMER0 +#define CONFIG_ARCV2_TIMER0_INT_PRI 0 + +#define CONFIG_ARCV2_TIMER1_INT_LVL IRQ_TIMER1 +#define CONFIG_ARCV2_TIMER1_INT_PRI 1 + +/* + * UART configuration settings + * + * This BSP only supports the nanokernel. Therefore: + * - only polled mode is supported (interrupt-driven mode is NOT supported); and + * - only the target console is supported (hostserver driver is NOT supported). + */ +#define UART_POLL_OUT uart_poll_out +#define CONFIG_UART_NUM_SYSTEM_PORTS 1 +#define CONFIG_UART_NUM_EXTRA_PORTS 0 +#define CONFIG_UART_NUM_PORTS \ + (CONFIG_UART_NUM_SYSTEM_PORTS + CONFIG_UART_NUM_EXTRA_PORTS) + +#define CONFIG_UART_CONSOLE_INDEX 0 +#define CONFIG_UART_CONSOLE_CLK_FREQ SYSCLK_DEFAULT_IOSC_HZ +#define CONFIG_UART_CONSOLE_BAUDRATE 115200 +#define CONFIG_UART_CONSOLE_REGS PERIPH_ADDR_BASE_UART0 +#define CONFIG_UART_CONSOLE_IRQ IRQ_UART0_INTR +#define CONFIG_UART_CONSOLE_INT_PRI 0 + +#define UART_REG_ADDR_INTERVAL 4 /* for ns16550 driver */ + +/* + * Device drivers utilize the macros PLB_BYTE_REG_WRITE() and + * PLB_BYTE_REG_READ() to access byte-wide registers on the processor + * local bus (PLB), as opposed to a PCI bus, for example. Boards are + * expected to provide implementations of these macros. + */ + +#define PLB_BYTE_REG_WRITE(data, address) outByte(data, (unsigned int)address) +#define PLB_BYTE_REG_READ(address) inByte((unsigned int)address) + +/******************************************************************************* +* +* outByte - output byte to memory location +* +* RETURNS: N/A +* +* NOMANUAL +*/ + +static inline void outByte(uint8_t data, uint32_t addr) +{ + *(volatile uint8_t *)addr = data; +} + +/******************************************************************************* +* +* inByte - obtain byte value from memory location +* +* This function issues the 'move' instruction to read a byte from the specified +* memory address. +* +* RETURNS: the byte read from the specified memory address +* +* NOMANUAL +*/ + +static inline uint8_t inByte(uint32_t addr) +{ + return *((volatile uint8_t *)addr); +} + +/* + * Device drivers utilize the macros PLB_WORD_REG_WRITE() and + * PLB_WORD_REG_READ() to access shortword-wide registers on the processor + * local bus (PLB), as opposed to a PCI bus, for example. Boards are + * expected to provide implementations of these macros. + */ + +#endif /* !_ASMLANGUAGE */ + +#endif /* _BOARD__H_ */ diff --git a/system/libarc32_edu/common/irq.h b/system/libarc32_edu/common/irq.h new file mode 100644 index 00000000..204d4586 --- /dev/null +++ b/system/libarc32_edu/common/irq.h @@ -0,0 +1,111 @@ +/* arc/v2/irq.h - ARCv2 public interrupt handling */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * DESCRIPTION + * ARCv2 nanokernel interrupt handling interface. Included by ARC/v2/arch.h. + */ + +#ifndef _ARCH_ARC_V2_IRQ__H_ +#define _ARCH_ARC_V2_IRQ__H_ + +#include + +#ifdef _ASMLANGUAGE +GTEXT(_irq_exit); +GTEXT(irq_lock) +GTEXT(irq_unlock) +GTEXT(irq_handler_set) +GTEXT(irq_connect) +GTEXT(irq_disconnect) +GTEXT(irq_enable) +GTEXT(irq_disable) +GTEXT(irq_priority_set) +#else +extern int irq_lock(void); +extern void irq_unlock(int key); + +extern void irq_handler_set(unsigned int irq, + void (*old)(void *arg), + void (*new)(void *arg), + void *arg); +extern int irq_connect(unsigned int irq, + unsigned int prio, + void (*isr)(void *arg), + void *arg); +extern void irq_disconnect(unsigned int irq); + +extern void irq_enable(unsigned int irq); +extern void irq_disable(unsigned int irq); + +extern void irq_priority_set(unsigned int irq, unsigned int prio); + +extern void _irq_exit(void); + +/******************************************************************************* +* +* irq_lock_inline - disable all interrupts on the CPU (inline) +* +* See irq_lock() for full description +* +* RETURNS: An architecture-dependent lock-out key representing the +* "interrupt disable state" prior to the call. +* +* \NOMANUAL +*/ + +static __attribute__ ((always_inline)) unsigned int irq_lock_inline(void) +{ + unsigned int key; + + __asm__ volatile("clri %0" : "=r"(key)); + return key; +} + +/******************************************************************************* +* +* irq_unlock_inline - enable all interrupts on the CPU (inline) +* +* See irq_unlock() for full description +* +* RETURNS: N/A +* +* \NOMANUAL +*/ + +static __attribute__ ((always_inline)) void irq_unlock_inline(unsigned int key) +{ + __asm__ volatile("seti %0" : : "ir"(key)); +} + +#endif /* _ASMLANGUAGE */ +#endif /* _ARCH_ARC_V2_IRQ__H_ */ diff --git a/system/libarc32_edu/common/misc/util.h b/system/libarc32_edu/common/misc/util.h new file mode 100644 index 00000000..838b770f --- /dev/null +++ b/system/libarc32_edu/common/misc/util.h @@ -0,0 +1,102 @@ +/* util.h - misc utilities */ + +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +Misc utilities usable by nanokernel, microkernel, and application code. +*/ + +#ifndef _UTIL__H_ +#define _UTIL__H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ASMLANGUAGE + +/* round "x" up/down to next multiple of "align" (which must be a power of 2) */ +#define ROUND_UP(x, align) \ + (((unsigned long)(x) + ((unsigned long)align - 1)) & \ + ~((unsigned long)align - 1)) +#define ROUND_DOWN(x, align) ((unsigned long)(x) & ~((unsigned long)align - 1)) + +#ifdef INLINED +#define INLINE inline +#else +#define INLINE +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#ifndef BOOL +#define BOOL int +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +static inline int _IsPowerOfTwo(unsigned int x) +{ + return (x != 0) && !(x & (x - 1)); +} +#endif /* !_ASMLANGUAGE */ + +/* KB, MB, GB */ +#define KB(x) ((x) << 10) +#define MB(x) (KB(x) << 10) +#define GB(x) (MB(x) << 10) + +/* KHZ, MHZ */ +#define KHZ(x) ((x) * 1000) +#define MHZ(x) (KHZ(x) * 1000) + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL__H_ */ diff --git a/system/libarc32_edu/common/uart.h b/system/libarc32_edu/common/uart.h new file mode 100644 index 00000000..22865d5d --- /dev/null +++ b/system/libarc32_edu/common/uart.h @@ -0,0 +1,76 @@ +/* uart.h - public UART driver APIs */ + +/* + * Copyright (c) 2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCuarth +#define __INCuarth + +#ifdef __cplusplus +extern "C" { +#endif + +/* generic UART info structure */ +struct uart_init_info { + int baud_rate; + uint32_t regs; /* base port number or MM base address */ + uint32_t sys_clk_freq; /* in Hz */ + uint8_t options; /* HW Flow Control option */ + uint8_t irq; /* interrupt request number */ + uint8_t int_pri; /* interrupt priority level */ +}; +/* UART driver has to configure the device to 8n1 */ + +void uart_init(int port, const struct uart_init_info *const pinfo); + +/* console I/O functions */ +int uart_poll_in(int port, unsigned char *pChar); +unsigned char uart_poll_out(int which, unsigned char outChar); + +/* interrupt driven I/O functions */ +int uart_fifo_fill(int port, const uint8_t *txData, int len); +int uart_fifo_read(int port, uint8_t *rxData, const int size); +void uart_irq_tx_enable(int port); +void uart_irq_tx_disable(int port); +int uart_irq_tx_ready(int port); +void uart_irq_rx_enable(int port); +void uart_irq_rx_disable(int port); +int uart_irq_rx_ready(int port); +void uart_irq_err_enable(int port); +void uart_irq_err_disable(int port); +int uart_irq_is_pending(int port); +int uart_irq_update(int port); +void uart_int_connect(int port, void (*isr)(void *), void *arg, void *stub); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCuarth */ diff --git a/system/libarc32_edu/drivers/ns16550.c b/system/libarc32_edu/drivers/ns16550.c new file mode 100644 index 00000000..6648733f --- /dev/null +++ b/system/libarc32_edu/drivers/ns16550.c @@ -0,0 +1,541 @@ +/* ns16550.c - NS16550D serial driver */ + +/* + * Copyright (c) 2010, 2012-2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +This is the driver for the Intel NS16550 UART Chip used on the PC 386. +It uses the SCCs in asynchronous mode only. + + +USAGE +An ns16550 structure is used to describe the chip. +The BSP's _InitHardware() routine initializes all the +values in the uart_init_info structure before calling uart_init(). + +A board support package's board.h header must provide definitions for: + +- the following register access routines: + + unsigned int inByte(unsigned int address); + void outByte(unsigned char data, unsigned int address); + +- and the following macro for the number of bytes between register addresses: + + UART_REG_ADDR_INTERVAL + + +INCLUDE FILES: drivers/uart.h +*/ + +/* includes */ + +#include +#include +#include +#include + +/* defines */ + +#define UART_REG_ADDR_INTERVAL 4 + +/* register definitions */ + +#define REG_THR 0x00 /* Transmitter holding reg. */ +#define REG_RDR 0x00 /* Receiver data reg. */ +#define REG_BRDL 0x00 /* Baud rate divisor (LSB) */ +#define REG_BRDH 0x01 /* Baud rate divisor (MSB) */ +#define REG_IER 0x01 /* Interrupt enable reg. */ +#define REG_IIR 0x02 /* Interrupt ID reg. */ +#define REG_FCR 0x02 /* FIFO control reg. */ +#define REG_LCR 0x03 /* Line control reg. */ +#define REG_MDC 0x04 /* Modem control reg. */ +#define REG_LSR 0x05 /* Line status reg. */ +#define REG_MSR 0x06 /* Modem status reg. */ + +/* equates for interrupt enable register */ + +#define IER_RXRDY 0x01 /* receiver data ready */ +#define IER_TBE 0x02 /* transmit bit enable */ +#define IER_LSR 0x04 /* line status interrupts */ +#define IER_MSI 0x08 /* modem status interrupts */ + +/* equates for interrupt identification register */ + +#define IIR_IP 0x01 /* interrupt pending bit */ +#define IIR_MASK 0x07 /* interrupt id bits mask */ +#define IIR_MSTAT 0x00 /* modem status interrupt */ +#define IIR_THRE 0X02 /* transmit holding register empty */ +#define IIR_RBRF 0x04 /* receiver buffer register full */ +#define IIR_ID 0x06 /* interupt ID mask without IP */ +#define IIR_SEOB 0x06 /* serialization error or break */ + +/* equates for FIFO control register */ + +#define FCR_FIFO 0x01 /* enable XMIT and RCVR FIFO */ +#define FCR_RCVRCLR 0x02 /* clear RCVR FIFO */ +#define FCR_XMITCLR 0x04 /* clear XMIT FIFO */ + +/* + * Per PC16550D (Literature Number: SNLS378B): + * + * RXRDY, Mode 0: When in the 16450 Mode (FCR0 = 0) or in + * the FIFO Mode (FCR0 = 1, FCR3 = 0) and there is at least 1 + * character in the RCVR FIFO or RCVR holding register, the + * RXRDY pin (29) will be low active. Once it is activated the + * RXRDY pin will go inactive when there are no more charac- + * ters in the FIFO or holding register. + * + * RXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when the + * FCR3 = 1 and the trigger level or the timeout has been + * reached, the RXRDY pin will go low active. Once it is acti- + * vated it will go inactive when there are no more characters + * in the FIFO or holding register. + * + * TXRDY, Mode 0: In the 16450 Mode (FCR0 = 0) or in the + * FIFO Mode (FCR0 = 1, FCR3 = 0) and there are no charac- + * ters in the XMIT FIFO or XMIT holding register, the TXRDY + * pin (24) will be low active. Once it is activated the TXRDY + * pin will go inactive after the first character is loaded into the + * XMIT FIFO or holding register. + * + * TXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when + * FCR3 = 1 and there are no characters in the XMIT FIFO, the + * TXRDY pin will go low active. This pin will become inactive + * when the XMIT FIFO is completely full. + */ +#define FCR_MODE0 0x00 /* set receiver in mode 0 */ +#define FCR_MODE1 0x08 /* set receiver in mode 1 */ + +/* RCVR FIFO interrupt levels: trigger interrupt with this bytes in FIFO */ +#define FCR_FIFO_1 0x00 /* 1 byte in RCVR FIFO */ +#define FCR_FIFO_4 0x40 /* 4 bytes in RCVR FIFO */ +#define FCR_FIFO_8 0x80 /* 8 bytes in RCVR FIFO */ +#define FCR_FIFO_14 0xC0 /* 14 bytes in RCVR FIFO */ + +/* constants for line control register */ + +#define LCR_CS5 0x00 /* 5 bits data size */ +#define LCR_CS6 0x01 /* 6 bits data size */ +#define LCR_CS7 0x02 /* 7 bits data size */ +#define LCR_CS8 0x03 /* 8 bits data size */ +#define LCR_2_STB 0x04 /* 2 stop bits */ +#define LCR_1_STB 0x00 /* 1 stop bit */ +#define LCR_PEN 0x08 /* parity enable */ +#define LCR_PDIS 0x00 /* parity disable */ +#define LCR_EPS 0x10 /* even parity select */ +#define LCR_SP 0x20 /* stick parity select */ +#define LCR_SBRK 0x40 /* break control bit */ +#define LCR_DLAB 0x80 /* divisor latch access enable */ + +/* constants for the modem control register */ + +#define MCR_DTR 0x01 /* dtr output */ +#define MCR_RTS 0x02 /* rts output */ +#define MCR_OUT1 0x04 /* output #1 */ +#define MCR_OUT2 0x08 /* output #2 */ +#define MCR_LOOP 0x10 /* loop back */ + +/* constants for line status register */ + +#define LSR_RXRDY 0x01 /* receiver data available */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_PE 0x04 /* parity error */ +#define LSR_FE 0x08 /* framing error */ +#define LSR_BI 0x10 /* break interrupt */ +#define LSR_THRE 0x20 /* transmit holding register empty */ +#define LSR_TEMT 0x40 /* transmitter empty */ + +/* constants for modem status register */ + +#define MSR_DCTS 0x01 /* cts change */ +#define MSR_DDSR 0x02 /* dsr change */ +#define MSR_DRI 0x04 /* ring change */ +#define MSR_DDCD 0x08 /* data carrier change */ +#define MSR_CTS 0x10 /* complement of cts */ +#define MSR_DSR 0x20 /* complement of dsr */ +#define MSR_RI 0x40 /* complement of ring signal */ +#define MSR_DCD 0x80 /* complement of dcd */ + +/* convenience defines */ + +#define THR(n) (uart[n].port + REG_THR * UART_REG_ADDR_INTERVAL) +#define RDR(n) (uart[n].port + REG_RDR * UART_REG_ADDR_INTERVAL) +#define BRDL(n) (uart[n].port + REG_BRDL * UART_REG_ADDR_INTERVAL) +#define BRDH(n) (uart[n].port + REG_BRDH * UART_REG_ADDR_INTERVAL) +#define IER(n) (uart[n].port + REG_IER * UART_REG_ADDR_INTERVAL) +#define IIR(n) (uart[n].port + REG_IIR * UART_REG_ADDR_INTERVAL) +#define FCR(n) (uart[n].port + REG_FCR * UART_REG_ADDR_INTERVAL) +#define LCR(n) (uart[n].port + REG_LCR * UART_REG_ADDR_INTERVAL) +#define MDC(n) (uart[n].port + REG_MDC * UART_REG_ADDR_INTERVAL) +#define LSR(n) (uart[n].port + REG_LSR * UART_REG_ADDR_INTERVAL) +#define MSR(n) (uart[n].port + REG_MSR * UART_REG_ADDR_INTERVAL) + +#define IIRC(n) uart[n].iirCache + +#define INBYTE(x) inByte(x) +#define OUTBYTE(x, d) outByte(d, x) + +#if defined(VXMICRO_ARCH_Intel) +#define INT_CONNECT(which, isr, arg, stub) \ + irq_connect((unsigned int)uart[which].irq, \ + (unsigned int)uart[which].intPri, \ + isr, \ + arg, \ + stub) +#else +#define INT_CONNECT(which, isr, arg, stub) \ + do { \ + ARG_UNUSED(stub); \ + irq_connect((unsigned int)uart[which].irq, \ + (unsigned int)uart[which].intPri, \ + isr, \ + arg); \ + } while (0) +#endif /* VXMICRO_ARCH_Intel */ + +/* typedefs */ + +struct ns16550 { + uint32_t port; /* base port number or MM base address */ + uint8_t irq; /* interrupt request level */ + uint8_t intPri; /* interrupt priority */ + uint8_t iirCache; /* cache of IIR since it clears when read */ +}; + +/* locals */ + +//static struct ns16550 __noinit uart[CONFIG_UART_NUM_SYSTEM_PORTS]; +static struct ns16550 uart[CONFIG_UART_NUM_SYSTEM_PORTS]; + +/******************************************************************************* +* +* uart_init - initialize the chip +* +* This routine is called to reset the chip in a quiescent state. +* +* RETURNS: N/A +*/ + +void uart_init(int which, /* UART channel to initialize */ + const struct uart_init_info * const init_info + ) +{ + int oldLevel; /* old interrupt lock level */ + uint32_t divisor; /* baud rate divisor */ + + uart[which].port = init_info->regs; + uart[which].irq = init_info->irq; + uart[which].intPri = init_info->int_pri; + uart[which].iirCache = 0; + + oldLevel = irq_lock_inline(); + + /* calculate baud rate divisor */ + divisor = (init_info->sys_clk_freq / init_info->baud_rate) >> 4; + + /* set the DLAB to access the baud rate divisor registers */ + OUTBYTE(LCR(which), LCR_DLAB); + OUTBYTE(BRDL(which), (unsigned char)(divisor & 0xff)); + OUTBYTE(BRDH(which), (unsigned char)((divisor >> 8) & 0xff)); + + /* 8 data bits, 1 stop bit, no parity, clear DLAB */ + OUTBYTE(LCR(which), LCR_CS8 | LCR_1_STB | LCR_PDIS); + + OUTBYTE(MDC(which), MCR_OUT2 | MCR_RTS | MCR_DTR); + + /* + * Program FIFO: enabled, mode 0 (set for compatibility with quark), + * generate the interrupt at 8th byte + * Clear TX and RX FIFO + */ + OUTBYTE(FCR(which), + FCR_FIFO | FCR_MODE0 | FCR_FIFO_8 | FCR_RCVRCLR | FCR_XMITCLR); + + /* clear the port */ + INBYTE(RDR(which)); + + /* disable interrupts */ + OUTBYTE(IER(which), 0x00); + + irq_unlock_inline(oldLevel); +} + +/******************************************************************************* +* +* uart_poll_in - poll the device for input. +* +* RETURNS: 0 if a character arrived, -1 if the input buffer if empty. +*/ + +int uart_poll_in(int which, /* UART channel to select for input */ + unsigned char *pChar /* pointer to char */ + ) +{ + if ((INBYTE(LSR(which)) & LSR_RXRDY) == 0x00) + return (-1); + + /* got a character */ + *pChar = INBYTE(RDR(which)); + + return 0; +} + +/******************************************************************************* +* +* uart_poll_out - output a character in polled mode. +* +* Checks if the transmitter is empty. If empty, a character is written to +* the data register. +* +* If the hardware flow control is enabled then the handshake signal CTS has to +* be asserted in order to send a character. +* +* RETURNS: sent character +*/ +unsigned char uart_poll_out( + int which, /* UART channel to select for output */ + unsigned char outChar /* char to send */ + ) +{ + /* wait for transmitter to ready to accept a character */ + while ((INBYTE(LSR(which)) & LSR_TEMT) == 0) + ; + + OUTBYTE(THR(which), outChar); + + return outChar; +} + +#if CONFIG_UART_INTERRUPT_DRIVEN +/******************************************************************************* +* +* uart_fifo_fill - fill FIFO with data +* +* RETURNS: number of bytes sent +*/ + +int uart_fifo_fill(int which, /* UART on which to send */ + const uint8_t *txData, /* data to transmit */ + int size /* number of bytes to send */ + ) +{ + int i; + + for (i = 0; i < size && (INBYTE(LSR(which)) & LSR_THRE) != 0; i++) { + OUTBYTE(THR(which), txData[i]); + } + return i; +} + +/******************************************************************************* +* +* uart_fifo_read - read data from FIFO +* +* RETURNS: number of bytes read +*/ + +int uart_fifo_read(int which, /* UART to receive from */ + uint8_t *rxData, /* data container */ + const int size /* container size */ + ) +{ + int i; + + for (i = 0; i < size && (INBYTE(LSR(which)) & LSR_RXRDY) != 0; i++) { + rxData[i] = INBYTE(RDR(which)); + } + + return i; +} + +/******************************************************************************* +* +* uart_irq_tx_enable - enable TX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_tx_enable(int which /* UART to enable Tx + interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_TBE); +} + +/******************************************************************************* +* +* uart_irq_tx_disable - disable TX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_tx_disable(int which /* UART to disable Tx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_TBE)); +} + +/******************************************************************************* +* +* uart_irq_tx_ready - check if Tx IRQ has been raised +* +* RETURNS: N/A +*/ + +int uart_irq_tx_ready(int which /* UART to check */ + ) +{ + return ((IIRC(which) & IIR_ID) == IIR_THRE); +} + +/******************************************************************************* +* +* _uart_irq_rx_enable - enable RX interrupt in IER + +* RETURNS: N/A +*/ + +void uart_irq_rx_enable(int which /* UART to enable Rx + interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_RXRDY); +} + +/******************************************************************************* +* +* uart_irq_rx_disable - disable RX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_rx_disable(int which /* UART to disable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_RXRDY)); +} + +/******************************************************************************* +* +* uart_irq_rx_ready - check if Rx IRQ has been raised +* +* RETURNS: 1 if an IRQ is ready, 0 otherwise +*/ + +int uart_irq_rx_ready(int which /* UART to check */ + ) +{ + return ((IIRC(which) & IIR_ID) == IIR_RBRF); +} + +/******************************************************************************* +* +* uart_irq_err_enable - enable error interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_err_enable(int which /* UART to enable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_LSR); +} + +/******************************************************************************* +* +* uart_irq_err_disable - disable error interrupt in IER +* +* RETURNS: 1 if an IRQ is ready, 0 otherwise +*/ + +void uart_irq_err_disable(int which /* UART to disable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_LSR)); +} + +/******************************************************************************* +* +* uart_irq_is_pending - check if any IRQ is pending +* +* RETURNS: 1 if an IRQ is pending, 0 otherwise +*/ + +int uart_irq_is_pending(int which /* UART to check */ + ) +{ + return (!(IIRC(which) & IIR_IP)); +} + +/******************************************************************************* +* +* uart_irq_update - update cached contents of IIR +* +* RETURNS: always 1 +*/ + +int uart_irq_update(int which /* UART to update */ + ) +{ + IIRC(which) = INBYTE(IIR(which)); + + return 1; +} + +/******************************************************************************* +* +* uart_int_connect - connect an ISR to an interrupt line +* +* The kernel configuration allows to setup an interrupt line for a particular +* DUART. This routine installs the ISR of a UART user to the interrupt line +* chosen for the hardware at configuration time. +* +* RETURNS: N/A +*/ + +void uart_int_connect(int which, /* UART to which to connect */ + void (*isr)(void *), /* interrupt handler */ + void *arg, /* argument to pass to handler */ + void *stub /* ptr to interrupt stub code */ + ) +{ +#if !defined(CONFIG_DYNAMIC_INT_STUBS) + ARG_UNUSED(isr); + ARG_UNUSED(arg); + ARG_UNUSED(stub); +#else + INT_CONNECT(which, isr, arg, stub); +#endif /* CONFIG_DYNAMIC_INT_STUBS */ + + irq_enable((unsigned int)uart[which].irq); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ diff --git a/system/libarc32_edu/main.c b/system/libarc32_edu/main.c index baffe56e..7fb9b1a2 100644 --- a/system/libarc32_edu/main.c +++ b/system/libarc32_edu/main.c @@ -110,6 +110,8 @@ int main(void) soc_gpio_enable(SOC_GPIO_32); SET_PIN_MODE(2, QRK_PMUX_SEL_MODEA); + uart_init(0); + setup(); for(;;) __asm__("nop"); diff --git a/variants/intel_edu_x/libarc32drv_edu.a b/variants/intel_edu_x/libarc32drv_edu.a deleted file mode 100755 index 0f375dca72231c7c8785d34224a7fc945656db51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141462 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

|0dx*MK+KG8-?g5w>HqUS?qBzD|92mElGDH$ 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 BOnLwS diff --git a/variants/intel_edu_x/linker_scripts/flash.ld b/variants/intel_edu_x/linker_scripts/flash.ld index aec52454..383455b3 100644 --- a/variants/intel_edu_x/linker_scripts/flash.ld +++ b/variants/intel_edu_x/linker_scripts/flash.ld @@ -32,6 +32,9 @@ MEMORY /* Putting stack at end of SRAM for now */ __stack_start = ORIGIN(SRAM)+LENGTH(SRAM); +/* Allocating heap size of 2048 bytes for now */ +__HEAP_SIZE = 2048; + SECTIONS { /* FLASH Start */ @@ -94,6 +97,12 @@ SECTIONS *(".data.*") } > SRAM + sdata : + { + __SDATA_BEGIN__ = .; + *(.sdata .sdata.* .gnu.linkonce.s.*) + } > SRAM + __data_ram_end = .; bss (NOLOAD) : @@ -132,6 +141,14 @@ SECTIONS *(".seg_rxtx.*") } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM + /* Define linker symbols */ _end = .; /* end of image */ diff --git a/variants/intel_edu_x/variant.cpp b/variants/intel_edu_x/variant.cpp index 46777ece..105df069 100644 --- a/variants/intel_edu_x/variant.cpp +++ b/variants/intel_edu_x/variant.cpp @@ -86,15 +86,19 @@ PinDescription g_APinDescription[]= uint32_t sizeof_g_APinDescription; -#ifdef OUT /* * UART objects */ RingBuffer rx_buffer1; RingBuffer tx_buffer1; +uart_init_info info1; + +UARTClass Serial(&info1, &rx_buffer1, &tx_buffer1); + + +#ifdef OUT -UARTClass Serial(UART, UART_IRQn, ID_UART, &rx_buffer1, &tx_buffer1); void serialEvent() __attribute__((weak)); void serialEvent() { } diff --git a/variants/intel_edu_x/variant.h b/variants/intel_edu_x/variant.h index 1adf74dd..50a61628 100644 --- a/variants/intel_edu_x/variant.h +++ b/variants/intel_edu_x/variant.h @@ -35,10 +35,10 @@ #include "Arduino.h" #include "wiring_digital.h" #include "pins_arduino.h" -//#ifdef __cplusplus -//#include "UARTClass.h" +#ifdef __cplusplus +#include "UARTClass.h" //#include "USARTClass.h" -//#endif +#endif #ifdef __cplusplus extern "C"{ @@ -65,6 +65,9 @@ extern "C"{ */ #define GPIO_MUX_MODE QRK_PMUX_SEL_MODEA +#define SS_GPIO 1 +#define SOC_GPIO 2 + /* * PWM */ @@ -77,6 +80,12 @@ extern "C"{ #define PWM_SCALE_490HZ 0 /* "Standard" Arduino PWM frequency is 490Hz */ #define PWM_SCALE_980HZ 1 /* Some pins on Arduino boards emit 980Hz PWM */ +/* + * UART + */ + +#define UART_MUX_MODE QRK_PMUX_SEL_MODEC + #ifdef __cplusplus } #endif @@ -87,7 +96,7 @@ extern "C"{ #ifdef __cplusplus -//extern UARTClass Serial; +extern UARTClass Serial; //extern USARTClass Serial1; #endif @@ -113,9 +122,6 @@ extern "C"{ #define SERIAL_PORT_HARDWARE Serial1 #define SERIAL_PORT_HARDWARE1 Serial1 -#define SS_GPIO 1 -#define SOC_GPIO 2 - extern uint32_t sizeof_g_APinDescription; #endif /* _VARIANT_INTEL_EDU_X_ */ From 4139a2efcdee1b210933b7abeee2bea084d1b369 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 12:06:17 +0100 Subject: [PATCH 04/15] ATLEDGE-75 Arduino time related functions. A first draft for the following functions: * delay() * millis(); * micros(); Not yet implemented: * delayMicroseconds. Author: Bogdan Pricop Signed-off-by: Bogdan Pricop Signed-off-by: David Hunt --- cores/arduino/wiring.c | 71 +++++++++ cores/arduino/wiring.h | 23 +-- system/libarc32_edu/.gitignore | 6 + system/libarc32_edu/bootcode/c_init.c | 3 + system/libarc32_edu/bootcode/conf.h | 9 ++ system/libarc32_edu/common/aux_regs.h | 2 + system/libarc32_edu/drivers/arcv2_timer0.c | 162 +++++++++++++++++++++ system/libarc32_edu/drivers/arcv2_timer0.h | 72 +++++++++ system/libarc32_edu/drivers/arcv2_timer1.c | 78 ++-------- system/libarc32_edu/drivers/arcv2_timer1.h | 39 ++++- 10 files changed, 370 insertions(+), 95 deletions(-) create mode 100644 cores/arduino/wiring.c create mode 100644 system/libarc32_edu/.gitignore create mode 100644 system/libarc32_edu/drivers/arcv2_timer0.c create mode 100644 system/libarc32_edu/drivers/arcv2_timer0.h diff --git a/cores/arduino/wiring.c b/cores/arduino/wiring.c new file mode 100644 index 00000000..ac524731 --- /dev/null +++ b/cores/arduino/wiring.c @@ -0,0 +1,71 @@ +/* + * TODO: Copyright header + * */ + +#include "wiring.h" +#include "arcv2_timer0.h" +#include "data_type.h" +#include "conf.h" +#include "interrupt.h" +#include "aux_regs.h" + +#define FREQ_MHZ ((ARCV2_TIMER0_CLOCK_FREQ)/1000000) + +void delay(uint32_t msec) +{ + if(0 == msec) + return; + uint32_t no_of_irqs = timer0_overflows; + uint32_t microseconds = arcv2_timer0_count_get() / FREQ_MHZ; + + while(timer0_overflows - no_of_irqs < msec){ +// yield(); + /* Enter sleep and enable interrupts and sets interrupts threshold to 3 */ + __asm__ volatile ("sleep 0x13"); + } + /* For the last fraction of millisecond don't go to sleep - you'll wake up + * too late - just spin */ + while (arcv2_timer0_count_get() / FREQ_MHZ < microseconds); +} + + +uint32_t millis(void) +{ + return timer0_overflows; +} + + +uint32_t micros(void) +{ + uint32_t tmr0_ctrl_reg; + uint32_t microsecs; + + uint32_t flags = interrupt_lock(); + + tmr0_ctrl_reg = arcv2_timer0_control_get(); + if(tmr0_ctrl_reg && ARC_V2_TMR0_CONTROL_IP_MASK){ + /* The IP bit is set, means the timer overflowed but the IRQ handler + * hasn't updated the timer0_overflows value because the IRQs are + * disabled; it is manually incremented here */ + microsecs = arcv2_timer0_count_get(); + microsecs = (timer0_overflows + 1) * 1000 + microsecs; + }else{ + /* The timer hasn't reached LIMIT from the point where we disabled the + * interrupts until here => read COUNT0 and check again the overflow + * condition */ + microsecs = arcv2_timer0_count_get(); + tmr0_ctrl_reg = arcv2_timer0_control_get(); + if(tmr0_ctrl_reg && ARC_V2_TMR0_CONTROL_IP_MASK){ + /* The COUNT0 reached LIMIT0 while reading COUNT0 value and + * possibly overflowed */ + microsecs = arcv2_timer0_count_get(); + microsecs = (timer0_overflows + 1) * 1000 + microsecs; + }else{ + microsecs = timer0_overflows * 1000 + microsecs; + } + } + + interrupt_unlock(flags); + + return microsecs; +} diff --git a/cores/arduino/wiring.h b/cores/arduino/wiring.h index 2535be0d..ffeb4965 100644 --- a/cores/arduino/wiring.h +++ b/cores/arduino/wiring.h @@ -24,6 +24,9 @@ extern "C" { #endif +#include +#include "wiring_constants.h" + /** * */ @@ -57,28 +60,8 @@ extern uint32_t micros( void ) ; * * \param dwMs the number of milliseconds to pause (uint32_t) */ -#if 0 extern void delay( uint32_t dwMs ) ; -#else - -#include "arcv2_timer1.h" - -static volatile boolean_t expired = true; - -static void timer1_user_isr(void) -{ - expired = true; -} - -static inline void delay(uint32_t) __attribute__((always_inline)); -static inline void delay(uint32_t msec){ - /* TODO */ - expired = false; - timer1_driver_init(timer1_user_isr, msec); - while(!expired); -} -#endif /** * \brief Pauses the program for the amount of time (in microseconds) specified as parameter. diff --git a/system/libarc32_edu/.gitignore b/system/libarc32_edu/.gitignore new file mode 100644 index 00000000..96a65c9e --- /dev/null +++ b/system/libarc32_edu/.gitignore @@ -0,0 +1,6 @@ +*.o +*.a +*.map +*.elf +*.swp +*.bin diff --git a/system/libarc32_edu/bootcode/c_init.c b/system/libarc32_edu/bootcode/c_init.c index 23f529b3..b38666b6 100644 --- a/system/libarc32_edu/bootcode/c_init.c +++ b/system/libarc32_edu/bootcode/c_init.c @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include "interrupt.h" +#include "../drivers/arcv2_timer0.h" /* Application main() function prototype */ extern int main (void); @@ -57,6 +58,8 @@ static void _exec_ctors (void) /* Init the the interrupt unit device - disable all the interrupts; The * default value of IRQ_ENABLE is 0x01 for all configured interrupts */ interrupt_unit_device_init(); + /* Start the system 1 millisecond tick */ + timer0_driver_init(); /* Jump to application main() */ main (); /* Never reached */ diff --git a/system/libarc32_edu/bootcode/conf.h b/system/libarc32_edu/bootcode/conf.h index 300753eb..a12ba5e2 100644 --- a/system/libarc32_edu/bootcode/conf.h +++ b/system/libarc32_edu/bootcode/conf.h @@ -31,5 +31,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define ARCV2_IRQ_TIMER0 16 #define ARCV2_IRQ_TIMER1 17 +#define ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock */ +#define ARCV2_TIMER1_CLOCK_FREQ 32000000 /* 32MHz reference clock */ + +#define ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ +#define ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ +#define ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ +#define ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ + + #endif /* _ARCV2_CONF__H_ */ diff --git a/system/libarc32_edu/common/aux_regs.h b/system/libarc32_edu/common/aux_regs.h index 7b00e968..44dd7d24 100644 --- a/system/libarc32_edu/common/aux_regs.h +++ b/system/libarc32_edu/common/aux_regs.h @@ -52,6 +52,8 @@ Definitions for auxiliary registers. #define ARC_V2_IRQ_PULSE_CANCEL 0x415 #define ARC_V2_IRQ_PENDING 0x416 +#define ARC_V2_TMR0_CONTROL_IP_MASK (0x01 << 3) + /* STATUS32/STATUS32_P0 bits */ #define ARC_V2_STATUS32_H (1 << 0) #define ARC_V2_STATUS32_E(x) ((x) << 1) diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c new file mode 100644 index 00000000..9e0c3fa8 --- /dev/null +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -0,0 +1,162 @@ +/* arcv2_timer0.c - ARC timer 0 device driver */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * The right to copy, distribute, modify or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Wind River license agreement. + */ + +/*modification history +modification history +-------------------- +03Nov14,j_b written +*/ + +/* +DESCRIPTION +This module implements a VxMicro device driver for the ARCv2 processor timer 0 +and provides the standard "system clock driver" interfaces. + +\INTERNAL IMPLEMENTATION DETAILS +The ARCv2 processor timer provides a 32-bit incrementing, wrap-to-zero counter. + +The device driver is also part of a nanokernel-only system, but omits more +complex capabilities (such as tickless idle support) that are only used in +conjunction with a microkernel. +*/ + +#include "arcv2_timer0.h" +#include "aux_regs.h" +#include "conf.h" +#include "interrupt.h" + +/* defines */ + + +#define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000 + +/* Maxim number of Timer0 overflows used to compute micros() + * It overflows at 70 minutes = 70*60 sec = 70*60*1000 millis = 4200000 */ +#define MAX_OVERFLOWS_US 4200000 + + +/* globals */ + +uint32_t volatile timer0_overflows = 0x00; +uint32_t volatile timer0_overflows_us = 0x00; + + +/******************************************************************************* +* +* arcv2_timer0_enable - enable the timer with the given limit/countup value +* +* This routine sets up the timer for operation by: +* - setting value to which the timer will count up to; +* - setting the timer's start value to zero; and +* - enabling interrupt generation. +* +* RETURNS: N/A +* +* \NOMANUAL +*/ + +static inline __attribute__((always_inline)) void arcv2_timer0_enable + ( + uint32_t count /* count to which timer is to increment to */ + ) +{ + aux_reg_write(ARC_V2_TMR0_LIMIT, count); /* write the limit value */ + /* count only when not halted for debug and enable interrupts */ + aux_reg_write(ARC_V2_TMR0_CONTROL, + ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); + //aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* write the start value */ +} + +/******************************************************************************* +* +* arcv2_timer0_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +inline __attribute__((always_inline)) +uint32_t arcv2_timer0_count_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_COUNT)); +} + +/******************************************************************************* +* +* arcv2_timer0_control_get - get the value of CONTROL0 aux register +* +* This routine gets the value from the timer's control register. +* +* RETURNS: the value of CONTROL0 auxiliary register. +* +* \NOMANUAL +*/ +inline __attribute__((always_inline)) +uint32_t arcv2_timer0_control_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_CONTROL)); +} + +/******************************************************************************* +* +* _arcv2_timer0_int_handler - system clock periodic tick handler +* +* This routine handles the system clock periodic tick interrupt. +* It increments number of milliseconds since sketch begun. +* +* RETURNS: N/A +* +* \NOMANUAL +*/ + +void _arcv2_timer0_int_handler(void* unusedArg/* not used */) +{ + (void)(unusedArg); + + /* clear the interrupt (by writing 0 to IP bit of the control register) */ + aux_reg_write(ARC_V2_TMR0_CONTROL, + ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); + /* Increment number of Timer0 overflows */ + timer0_overflows++; + /* Increment number of Timer0 overflows used to compute micros() */ + timer0_overflows_us = + (timer0_overflows_us <= MAX_OVERFLOWS_US) ? timer0_overflows_us++ : 0; +} + +/******************************************************************************* +* +* timer0_driver_init - initialize and enable the system clock +* +* This routine is used to program the ARCv2 timer to deliver interrupts at the +* 1 millisecond rate specified via the ONE_MILLISECOND macro. +* +* RETURNS: N/A +*/ +void timer0_driver_init(void) +{ + + /* ensure that the timer will not generate interrupts */ + aux_reg_write(ARC_V2_TMR0_CONTROL, 0); + aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* clear the count value */ + + /* connect specified routine/parameter to the timer 0 interrupt vector */ + interrupt_connect(ARCV2_IRQ_TIMER0, _arcv2_timer0_int_handler, 0); + + /* configure timer to overflow and fire an IRQ every 1 ms */ + arcv2_timer0_enable(ONE_MILLISECOND); + + /* Everything has been configured. It is now safe to enable the interrupt */ + interrupt_enable(ARCV2_IRQ_TIMER0); + /* Enable ARC's global interrupts */ + interrupt_unlock(0); +} diff --git a/system/libarc32_edu/drivers/arcv2_timer0.h b/system/libarc32_edu/drivers/arcv2_timer0.h new file mode 100644 index 00000000..187f3a77 --- /dev/null +++ b/system/libarc32_edu/drivers/arcv2_timer0.h @@ -0,0 +1,72 @@ +/* arcv2_timer0.h - ARC timer 0 device driver */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * The right to copy, distribute, modify or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Wind River license agreement. + */ + +#ifndef _ARCV2_TIMER0__H_ +#define _ARCV2_TIMER0__H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Increments every Timer0 overflow. + * Timer0 is configured to overflow and fire an IRQ every 1 millisecond + */ +extern uint32_t volatile timer0_overflows; + +/* It is incremented every ms by Timer0 IRQ handler but it overflows at + * MAX_OVERFLOWS_US */ +extern uint32_t volatile timer0_overflows_us; + +/******************************************************************************* +* +* timer0_driver_init - initialize and enable the system clock +* +* This routine is used to program the ARCv2 timer to deliver interrupts at the +* 1 millisecond rate specified via the ONE_MILLISECOND macro. +* +* RETURNS: N/A +*/ +void timer0_driver_init(void); + + +/******************************************************************************* +* +* arcv2_timer0_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +uint32_t arcv2_timer0_count_get(void); + + +/******************************************************************************* +* +* arcv2_timer0_control_get - get the value of CONTROL0 aux register +* +* This routine gets the value from the timer's control register. +* +* RETURNS: the value of CONTROL0 auxiliary register. +* +* \NOMANUAL +*/ +uint32_t arcv2_timer0_control_get(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ARCV2_TIMER0__H_ */ diff --git a/system/libarc32_edu/drivers/arcv2_timer1.c b/system/libarc32_edu/drivers/arcv2_timer1.c index 254f7ece..0fcd65d4 100644 --- a/system/libarc32_edu/drivers/arcv2_timer1.c +++ b/system/libarc32_edu/drivers/arcv2_timer1.c @@ -32,28 +32,15 @@ conjunction with a microkernel. #include "conf.h" #include "../bootcode/interrupt.h" -#define ARCV2_TIMER1_CLOCK_FREQ 32000000 /* 32MHz reference clock */ - -/* defines */ - -#define ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ -#define ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ -#define ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ -#define ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ /* globals */ void (* timer1_user_int_handler)(void) = 0x00; -/* locals */ - -/* forward declarations */ - -uint32_t timer1_read(void); /******************************************************************************* * -* arcv2_timer_enable - enable the timer with the given limit/countup value +* arcv2_timer1_enable - enable the timer with the given limit/countup value * * This routine sets up the timer for operation by: * - setting value to which the timer will count up to; @@ -66,7 +53,7 @@ uint32_t timer1_read(void); */ static inline __attribute__((always_inline)) -void arcv2_timer_enable +void arcv2_timer1_enable ( uint32_t count /* count to which timer is to increment to */ ) @@ -79,7 +66,7 @@ void arcv2_timer_enable /******************************************************************************* * -* arcv2_timer_count_get - get the current counter value +* arcv2_timer1_count_get - get the current counter value * * This routine gets the value from the timer's count register. This * value is the 'time' elapsed from the starting count (assumed to be 0). @@ -88,35 +75,18 @@ void arcv2_timer_enable * * \NOMANUAL */ -static inline __attribute__((always_inline)) +inline __attribute__((always_inline)) uint32_t arcv2_timer1_count_get(void) { return (aux_reg_read(ARC_V2_TMR1_COUNT)); } -/******************************************************************************* - * - * arcv2_timer_limit_get - get the limit/countup value - * - * This routine gets the value from the timer's limit register, which is the - * value to which the timer will count up to. - * - * RETURNS: the current counter value - * - * \NOMANUAL - */ -static inline __attribute__((always_inline)) -uint32_t arcv2_timer1_limit_get(void) -{ - return (aux_reg_read(ARC_V2_TMR1_LIMIT)); -} - /******************************************************************************* * -* _arcv2_timer_int_handler - system clock periodic tick handler +* _arcv2_timer1_int_handler - Timer1 configured tick handler * -* This routine handles the system clock periodic tick interrupt. A TICK_EVENT -* event is pushed onto the microkernel stack. +* This routine handles the Timer1 overflow interrupt. +* It clears Timer1 IRQ and executes the user's interrupt handler. * * RETURNS: N/A * @@ -135,7 +105,7 @@ void _arcv2_timer1_int_handler(void *notused) /******************************************************************************* * -* timer_driver - initialize timer1 and enable interrupt +* timer1_driver_init - initialize timer1 and enable interrupt * * RETURNS: N/A */ @@ -151,11 +121,6 @@ void timer1_driver_init(void(*int_handler)(void), uint32_t ticktime_ms) /* connect specified routine/parameter to the timer 0 interrupt vector */ interrupt_connect(ARCV2_IRQ_TIMER1, _arcv2_timer1_int_handler, 0); timer1_user_int_handler = int_handler; -#if 0 - (void) nanoCpuIntConnect(_WRS_CONFIG_ARCV2_TIMER1_INT_LVL, - _WRS_CONFIG_ARCV2_TIMER1_INT_PRI, - _arcv2_timer1_int_handler, 0); -#endif tickunit = (ARCV2_TIMER1_CLOCK_FREQ / 1000) * ticktime_ms; @@ -166,43 +131,23 @@ void timer1_driver_init(void(*int_handler)(void), uint32_t ticktime_ms) * The global variable 'tickunit' represents the #cycles/tick. */ - arcv2_timer_enable(tickunit); + arcv2_timer1_enable(tickunit); /* Everything has been configured. It is now safe to enable the interrupt */ interrupt_enable(ARCV2_IRQ_TIMER1); /* Enable global ARC interrupts */ -// interrupt_unlock(ARCV2_SETI_IRQ_LVL_2); interrupt_unlock(0); -#if 0 - nanoCpuIntEnable (_WRS_CONFIG_ARCV2_TIMER1_INT_LVL); -#endif } /******************************************************************************* * -* timer_read - read the BSP timer hardware -* -* This routine returns the current time in terms of timer hardware clock cycles. -* -* RETURNS: up counter of elapsed clock cycles -*/ - -uint32_t timer1_read(void) -{ - return arcv2_timer1_count_get(); -} - - -/******************************************************************************* -* -* timer_disable - stop announcing ticks into the kernel +* timer1_disable - Disables Timer1 interrupt generation. * * This routine disables timer interrupt generation and delivery. * Note that the timer's counting cannot be stopped by software. * * RETURNS: N/A */ - void timer1_disable(void) { uint32_t saved; @@ -218,7 +163,4 @@ void timer1_disable(void) /* disable interrupt in the interrupt controller */ interrupt_disable(ARCV2_IRQ_TIMER1); -#if 0 - nanoCpuIntDisable (ARCV2_IRQ_TIMER1); -#endif } diff --git a/system/libarc32_edu/drivers/arcv2_timer1.h b/system/libarc32_edu/drivers/arcv2_timer1.h index 75f38154..a7c891dc 100644 --- a/system/libarc32_edu/drivers/arcv2_timer1.h +++ b/system/libarc32_edu/drivers/arcv2_timer1.h @@ -1,4 +1,4 @@ -/* arcv2_timer1.c - ARC timer 1 device driver */ +/* arcv2_timer1.h - ARC timer 1 device driver */ /* * Copyright (c) 2014 Wind River Systems, Inc. @@ -14,19 +14,44 @@ #include + +#ifdef __cplusplus + extern "C" { +#endif + /******************************************************************************* * -* timer_driver - initialize timer1 and enable interrupt +* timer1_driver_init - initialize timer1 and enable interrupt * * RETURNS: N/A */ +void timer1_driver_init(void(*int_handler)(void), uint32_t ticktime_ms); -#ifdef __cplusplus - extern "C" { -#endif +/******************************************************************************* +* +* arcv2_timer1_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +uint32_t arcv2_timer1_count_get(void); + + +/******************************************************************************* +* +* timer1_disable - Disables Timer1 interrupt generation. +* +* This routine disables timer interrupt generation and delivery. +* Note that the timer's counting cannot be stopped by software. +* +* RETURNS: N/A +*/ +void timer1_disable(void); -void timer1_driver_init(void(*int_handler)(void), uint32_t ticktime_ms); -uint32_t timer1_read(void); #ifdef __cplusplus } From bd205779390d879a5e866941b840e5094204c97c Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Tue, 12 May 2015 17:47:54 +0100 Subject: [PATCH 05/15] build: Adding HEAP section Adding heap section with start/end marker symbols to satisfy malloc() and friends Signed-off-by: Dan O'Donovan --- system/libarc32_edu/build/linker.cmd | 11 +++++++++++ variants/intel_edu_x/linker_scripts/flash.ld | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/system/libarc32_edu/build/linker.cmd b/system/libarc32_edu/build/linker.cmd index aec52454..f9ed938a 100644 --- a/system/libarc32_edu/build/linker.cmd +++ b/system/libarc32_edu/build/linker.cmd @@ -32,6 +32,9 @@ MEMORY /* Putting stack at end of SRAM for now */ __stack_start = ORIGIN(SRAM)+LENGTH(SRAM); +/* Allocating heap size of 2048 bytes for now */ +__HEAP_SIZE = 2048; + SECTIONS { /* FLASH Start */ @@ -132,6 +135,14 @@ SECTIONS *(".seg_rxtx.*") } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM + /* Define linker symbols */ _end = .; /* end of image */ diff --git a/variants/intel_edu_x/linker_scripts/flash.ld b/variants/intel_edu_x/linker_scripts/flash.ld index aec52454..f9ed938a 100644 --- a/variants/intel_edu_x/linker_scripts/flash.ld +++ b/variants/intel_edu_x/linker_scripts/flash.ld @@ -32,6 +32,9 @@ MEMORY /* Putting stack at end of SRAM for now */ __stack_start = ORIGIN(SRAM)+LENGTH(SRAM); +/* Allocating heap size of 2048 bytes for now */ +__HEAP_SIZE = 2048; + SECTIONS { /* FLASH Start */ @@ -132,6 +135,14 @@ SECTIONS *(".seg_rxtx.*") } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM + /* Define linker symbols */ _end = .; /* end of image */ From b9bc0fb221574f5f5a52abfa1d9d2b3da85fc1ba Mon Sep 17 00:00:00 2001 From: Bogdan Pricop Date: Tue, 12 May 2015 21:31:11 +0100 Subject: [PATCH 06/15] .gitignore file added to repo. Signed-off-by: Bogdan Pricop --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..96a65c9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.o +*.a +*.map +*.elf +*.swp +*.bin From 723be266ea682a413efca74e5b69190e17bf2a65 Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Fri, 15 May 2015 14:49:26 +0100 Subject: [PATCH 07/15] Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) Signed-off-by: Dan O'Donovan --- system/libarc32_edu/build/linker.cmd | 2 +- variants/intel_edu_x/linker_scripts/flash.ld | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/libarc32_edu/build/linker.cmd b/system/libarc32_edu/build/linker.cmd index f9ed938a..4ab003a4 100644 --- a/system/libarc32_edu/build/linker.cmd +++ b/system/libarc32_edu/build/linker.cmd @@ -25,7 +25,7 @@ OUTPUT_FORMAT("elf32-littlearc", "elf32-bigarc", "elf32-littlearc") MEMORY { FLASH (rx) : ORIGIN = 0x40000000, LENGTH = 192K - SRAM (wx) : ORIGIN = 0xa8010000, LENGTH = 16K + SRAM (wx) : ORIGIN = 0xa800e000, LENGTH = 24K DCCM (wx) : ORIGIN = 0x80000000, LENGTH = 8K } diff --git a/variants/intel_edu_x/linker_scripts/flash.ld b/variants/intel_edu_x/linker_scripts/flash.ld index f9ed938a..4ab003a4 100644 --- a/variants/intel_edu_x/linker_scripts/flash.ld +++ b/variants/intel_edu_x/linker_scripts/flash.ld @@ -25,7 +25,7 @@ OUTPUT_FORMAT("elf32-littlearc", "elf32-bigarc", "elf32-littlearc") MEMORY { FLASH (rx) : ORIGIN = 0x40000000, LENGTH = 192K - SRAM (wx) : ORIGIN = 0xa8010000, LENGTH = 16K + SRAM (wx) : ORIGIN = 0xa800e000, LENGTH = 24K DCCM (wx) : ORIGIN = 0x80000000, LENGTH = 8K } From c2f3e0413c6e1c4567faedd5b2e7a8308fc8c42e Mon Sep 17 00:00:00 2001 From: Bogdan Pricop Date: Fri, 15 May 2015 14:24:10 +0100 Subject: [PATCH 08/15] ATLEDGE-82 init code - Fix interrupt handling bug. Issue: A simple blink sketch having a 10 ms delay between blinks crashed after a few seconds. It looks like the stack got corrupted. Root cause: The generic assembly hardware ISR, "_do_isr", was not properly written; the "sp" was not properly handled. Solution: Replace the assembly hardware ISR with a C function having "__attribute__((interrupt("ilink")))" Note: We need this generic hardware ISR because the IVT is in flash and we cannot change it at runtime. Signed-off-by: Bogdan Pricop Signed-off-by: David Hunt --- system/libarc32_edu/bootcode/init.S | 3 + system/libarc32_edu/bootcode/interrupt.c | 25 ++- system/libarc32_edu/bootcode/interrupt.h | 4 +- system/libarc32_edu/bootcode/irq.S | 42 ----- system/libarc32_edu/drivers/arcv2_timer0.c | 160 ++++++++++++++++++++ system/libarc32_edu/drivers/arcv2_timer1.c | 3 +- system/libarc32_edu/drivers/intel_qrk_pwm.c | 2 +- system/libarc32_edu/drivers/intel_qrk_pwm.h | 4 +- variants/intel_edu_x/libarc32drv_edu.a | Bin 141462 -> 0 bytes 9 files changed, 188 insertions(+), 55 deletions(-) delete mode 100644 system/libarc32_edu/bootcode/irq.S create mode 100644 system/libarc32_edu/drivers/arcv2_timer0.c delete mode 100755 variants/intel_edu_x/libarc32drv_edu.a diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index a0a34890..08a04f4d 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -18,6 +18,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "aux_regs.h" +.globl _do_isr +.type _do_isr,%function + .section .int_vector_table .balign 4 _start: diff --git a/system/libarc32_edu/bootcode/interrupt.c b/system/libarc32_edu/bootcode/interrupt.c index 47c7ca7c..3f7df2f4 100644 --- a/system/libarc32_edu/bootcode/interrupt.c +++ b/system/libarc32_edu/bootcode/interrupt.c @@ -23,19 +23,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA struct _IsrTableEntry { void *arg; - void (*isr)(void *); + void (*isr)(void); }; struct _IsrTableEntry __attribute__((section(".data"))) _IsrTable[_WRS_CONFIG_NUM_IRQS]; -static void _dummy_isr(void *arg) +static void _dummy_isr(void) { + __asm__ ("flag 0x01"); /* Set the halt flag => halt the CPU */ for(;;); } -void interrupt_connect(unsigned int irq, - void (*isr)(void *arg), - void *arg) +/* + * The default, generic hardware IRQ handler. + * It only decodes the source of IRQ and calls the appropriate handler + * + * We need this because the Interrupt Vector Table is located in the flash + * memory and cannot modify it at run time.*/ +__attribute__ ((interrupt ("ilink"))) +void _do_isr(void) +{ + unsigned int irq_cause = aux_reg_read(ARC_V2_ICAUSE) - 16; + if (_IsrTable[irq_cause].isr != 0x00) + _IsrTable[irq_cause].isr(); + else + _dummy_isr(); +} + +void interrupt_connect(unsigned int irq, void (*isr)(void), void *arg) { int index = irq - 16; unsigned int flags = interrupt_lock(); diff --git a/system/libarc32_edu/bootcode/interrupt.h b/system/libarc32_edu/bootcode/interrupt.h index 65d3e5d0..8d8bfed2 100644 --- a/system/libarc32_edu/bootcode/interrupt.h +++ b/system/libarc32_edu/bootcode/interrupt.h @@ -19,9 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #ifndef __INTERRUPT_H__ #define __INTERRUPT_H__ -extern void interrupt_connect(unsigned int irq, - void (*isr)(void *arg), - void *arg); +extern void interrupt_connect(unsigned int irq, void (*isr)(void), void *arg); extern void interrupt_disconnect(unsigned int irq); extern void interrupt_enable(unsigned int irq); extern void interrupt_disable(unsigned int irq); diff --git a/system/libarc32_edu/bootcode/irq.S b/system/libarc32_edu/bootcode/irq.S deleted file mode 100644 index be347563..00000000 --- a/system/libarc32_edu/bootcode/irq.S +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright (c) 2015 Intel Corporation. All right reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ -#include "aux_regs.h" - -.globl _do_isr -.type _do_isr,%function - -.globl _IsrTable -.type _IsrTable,%object - -.text -.balign 4 -_do_isr: - lr r0, [ARC_V2_ICAUSE] - sub r0, r0, 16 - - mov r1, _IsrTable - add3 r0, r1, r0 /* table entries are 8-bytes wide */ - - ld r1, [r0, 4] /* ISR into r1 */ - jl_s.d [r1] - ld_s r0, [r0] /* delay slot: ISR parameter into r0 */ - - /* back from ISR */ - rtie - nop diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c new file mode 100644 index 00000000..c412fa5c --- /dev/null +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -0,0 +1,160 @@ +/* arcv2_timer0.c - ARC timer 0 device driver */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * The right to copy, distribute, modify or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Wind River license agreement. + */ + +/*modification history +modification history +-------------------- +03Nov14,j_b written +*/ + +/* +DESCRIPTION +This module implements a VxMicro device driver for the ARCv2 processor timer 0 +and provides the standard "system clock driver" interfaces. + +\INTERNAL IMPLEMENTATION DETAILS +The ARCv2 processor timer provides a 32-bit incrementing, wrap-to-zero counter. + +The device driver is also part of a nanokernel-only system, but omits more +complex capabilities (such as tickless idle support) that are only used in +conjunction with a microkernel. +*/ + +#include "arcv2_timer0.h" +#include "aux_regs.h" +#include "conf.h" +#include "interrupt.h" + +/* defines */ + + +#define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000 + +/* Maxim number of Timer0 overflows used to compute micros() + * It overflows at 70 minutes = 70*60 sec = 70*60*1000 millis = 4200000 */ +#define MAX_OVERFLOWS_US 4200000 + + +/* globals */ + +uint32_t volatile timer0_overflows = 0x00; +uint32_t volatile timer0_overflows_us = 0x00; + + +/******************************************************************************* +* +* arcv2_timer0_enable - enable the timer with the given limit/countup value +* +* This routine sets up the timer for operation by: +* - setting value to which the timer will count up to; +* - setting the timer's start value to zero; and +* - enabling interrupt generation. +* +* RETURNS: N/A +* +* \NOMANUAL +*/ + +static inline __attribute__((always_inline)) void arcv2_timer0_enable + ( + uint32_t count /* count to which timer is to increment to */ + ) +{ + aux_reg_write(ARC_V2_TMR0_LIMIT, count); /* write the limit value */ + /* count only when not halted for debug and enable interrupts */ + aux_reg_write(ARC_V2_TMR0_CONTROL, + ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); + //aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* write the start value */ +} + +/******************************************************************************* +* +* arcv2_timer0_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +inline __attribute__((always_inline)) +uint32_t arcv2_timer0_count_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_COUNT)); +} + +/******************************************************************************* +* +* arcv2_timer0_control_get - get the value of CONTROL0 aux register +* +* This routine gets the value from the timer's control register. +* +* RETURNS: the value of CONTROL0 auxiliary register. +* +* \NOMANUAL +*/ +inline __attribute__((always_inline)) +uint32_t arcv2_timer0_control_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_CONTROL)); +} + +/******************************************************************************* +* +* _arcv2_timer0_int_handler - system clock periodic tick handler +* +* This routine handles the system clock periodic tick interrupt. +* It increments number of milliseconds since sketch begun. +* +* RETURNS: N/A +* +* \NOMANUAL +*/ +void _arcv2_timer0_int_handler(void) +{ + + /* clear the interrupt (by writing 0 to IP bit of the control register) */ + aux_reg_write(ARC_V2_TMR0_CONTROL, + ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); + /* Increment number of Timer0 overflows */ + timer0_overflows++; + /* Increment number of Timer0 overflows used to compute micros() */ + timer0_overflows_us = + (timer0_overflows_us <= MAX_OVERFLOWS_US) ? timer0_overflows_us++ : 0; +} + +/******************************************************************************* +* +* timer0_driver_init - initialize and enable the system clock +* +* This routine is used to program the ARCv2 timer to deliver interrupts at the +* 1 millisecond rate specified via the ONE_MILLISECOND macro. +* +* RETURNS: N/A +*/ +void timer0_driver_init(void) +{ + + /* ensure that the timer will not generate interrupts */ + aux_reg_write(ARC_V2_TMR0_CONTROL, 0); + aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* clear the count value */ + + /* connect specified routine/parameter to the timer 0 interrupt vector */ + interrupt_connect(ARCV2_IRQ_TIMER0, _arcv2_timer0_int_handler, 0); + + /* configure timer to overflow and fire an IRQ every 1 ms */ + arcv2_timer0_enable(ONE_MILLISECOND); + + /* Everything has been configured. It is now safe to enable the interrupt */ + interrupt_enable(ARCV2_IRQ_TIMER0); + /* Enable ARC's global interrupts */ + interrupt_unlock(0); +} diff --git a/system/libarc32_edu/drivers/arcv2_timer1.c b/system/libarc32_edu/drivers/arcv2_timer1.c index 254f7ece..9a0a0697 100644 --- a/system/libarc32_edu/drivers/arcv2_timer1.c +++ b/system/libarc32_edu/drivers/arcv2_timer1.c @@ -122,9 +122,8 @@ uint32_t arcv2_timer1_limit_get(void) * * \NOMANUAL */ -void _arcv2_timer1_int_handler(void *notused) +void _arcv2_timer1_int_handler(void) { - (void)(notused); /* clear the interrupt (by writing 0 to IP bit of the control register) */ aux_reg_write(ARC_V2_TMR1_CONTROL, ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); /* execute callback specified by the user */ diff --git a/system/libarc32_edu/drivers/intel_qrk_pwm.c b/system/libarc32_edu/drivers/intel_qrk_pwm.c index d0322c32..3e3ee404 100644 --- a/system/libarc32_edu/drivers/intel_qrk_pwm.c +++ b/system/libarc32_edu/drivers/intel_qrk_pwm.c @@ -311,7 +311,7 @@ static void soc_pwm_unmask_interrupt(uint8_t channel) * * \brief PWM ISR, if specified calls a user defined callback */ -DECLARE_INTERRUPT_HANDLER void pwm_isr(void *arg) +DECLARE_INTERRUPT_HANDLER void pwm_isr(void) { uint32_t pending = 0, pwm = 0; diff --git a/system/libarc32_edu/drivers/intel_qrk_pwm.h b/system/libarc32_edu/drivers/intel_qrk_pwm.h index 75ac002b..3d9c3258 100644 --- a/system/libarc32_edu/drivers/intel_qrk_pwm.h +++ b/system/libarc32_edu/drivers/intel_qrk_pwm.h @@ -123,11 +123,11 @@ void soc_pwm_stop(uint8_t channel); DRIVER_API_RC soc_pwm_block_init(void); -/*! \fn void pwm_isr(void*) +/*! \fn void pwm_isr(void) * * \brief PWM ISR, if specified calls a user defined callback */ -void pwm_isr(void*); +void pwm_isr(void); #ifdef __cplusplus } diff --git a/variants/intel_edu_x/libarc32drv_edu.a b/variants/intel_edu_x/libarc32drv_edu.a deleted file mode 100755 index 0f375dca72231c7c8785d34224a7fc945656db51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141462 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

|0dx*MK+KG8-?g5w>HqUS?qBzD|92mElGDH$ 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 BOnLwS From 4a29f5c30fb52e433f30c1ab7ba30244953e2d05 Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Fri, 15 May 2015 14:46:08 +0100 Subject: [PATCH 09/15] cfw: Importing Component Framework library code from Thunderdome Framework code extracted from the following commit ID in Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 This brings the necessary library code into the source tree, but not yet integrated in the Arduino run-time environment (to follow shortly). Signed-off-by: Dan O'Donovan --- system/libarc32_edu/Makefile | 13 +- .../libarc32_edu/framework/include/cfw/cfw.h | 202 ++++++++++++++ .../framework/include/cfw/cfw_client.h | 92 +++++++ .../framework/include/cfw/cfw_debug.h | 7 + .../framework/include/cfw/cfw_internal.h | 30 ++ .../framework/include/cfw/cfw_messages.h | 102 +++++++ .../framework/include/cfw/cfw_service.h | 140 ++++++++++ .../framework/include/infra/ipc.h | 79 ++++++ .../framework/include/infra/ipc_requests.h | 13 + .../framework/include/infra/log.h | 151 ++++++++++ .../framework/include/infra/log_backends.h | 80 ++++++ .../framework/include/infra/message.h | 102 +++++++ .../framework/include/infra/port.h | 144 ++++++++++ .../framework/include/log_modules | 4 + system/libarc32_edu/framework/include/os/os.h | 259 ++++++++++++++++++ .../framework/include/os/os_types.h | 82 ++++++ .../framework/include/panic_api.h | 1 + .../libarc32_edu/framework/include/pf_init.h | 32 +++ .../libarc32_edu/framework/include/platform.h | 52 ++++ .../framework/include/services/gpio_service.h | 218 +++++++++++++++ .../framework/include/util/list.h | 94 +++++++ .../framework/src/cfw/cfw_debug.c | 42 +++ .../framework/src/cfw/client_api.c | 96 +++++++ .../framework/src/cfw/service_api.c | 148 ++++++++++ .../framework/src/cfw/service_manager_proxy.c | 256 +++++++++++++++++ system/libarc32_edu/framework/src/infra/ipc.c | 115 ++++++++ .../framework/src/infra/ipc_callback.c | 53 ++++ .../libarc32_edu/framework/src/infra/port.c | 250 +++++++++++++++++ system/libarc32_edu/framework/src/os/os.c | 94 +++++++ .../framework/src/services/gpio_service_api.c | 81 ++++++ system/libarc32_edu/framework/src/util/list.c | 116 ++++++++ 31 files changed, 3145 insertions(+), 3 deletions(-) create mode 100644 system/libarc32_edu/framework/include/cfw/cfw.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_client.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_debug.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_internal.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_messages.h create mode 100644 system/libarc32_edu/framework/include/cfw/cfw_service.h create mode 100644 system/libarc32_edu/framework/include/infra/ipc.h create mode 100644 system/libarc32_edu/framework/include/infra/ipc_requests.h create mode 100644 system/libarc32_edu/framework/include/infra/log.h create mode 100644 system/libarc32_edu/framework/include/infra/log_backends.h create mode 100644 system/libarc32_edu/framework/include/infra/message.h create mode 100644 system/libarc32_edu/framework/include/infra/port.h create mode 100644 system/libarc32_edu/framework/include/log_modules create mode 100644 system/libarc32_edu/framework/include/os/os.h create mode 100644 system/libarc32_edu/framework/include/os/os_types.h create mode 100644 system/libarc32_edu/framework/include/panic_api.h create mode 100644 system/libarc32_edu/framework/include/pf_init.h create mode 100644 system/libarc32_edu/framework/include/platform.h create mode 100644 system/libarc32_edu/framework/include/services/gpio_service.h create mode 100644 system/libarc32_edu/framework/include/util/list.h create mode 100644 system/libarc32_edu/framework/src/cfw/cfw_debug.c create mode 100644 system/libarc32_edu/framework/src/cfw/client_api.c create mode 100644 system/libarc32_edu/framework/src/cfw/service_api.c create mode 100644 system/libarc32_edu/framework/src/cfw/service_manager_proxy.c create mode 100644 system/libarc32_edu/framework/src/infra/ipc.c create mode 100644 system/libarc32_edu/framework/src/infra/ipc_callback.c create mode 100644 system/libarc32_edu/framework/src/infra/port.c create mode 100644 system/libarc32_edu/framework/src/os/os.c create mode 100644 system/libarc32_edu/framework/src/services/gpio_service_api.c create mode 100644 system/libarc32_edu/framework/src/util/list.c diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index e9143818..21348969 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -4,6 +4,12 @@ ASM_SRC+=$(wildcard $(PWD)/common/*.S) C_SRC+=$(wildcard $(PWD)/bootcode/*.c) C_SRC+=$(wildcard $(PWD)/drivers/*.c) C_SRC+=$(wildcard $(PWD)/common/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/services/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/cfw/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/infra/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/util/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/os/*.c) ARCH=ARC CC=arc-elf32-gcc @@ -25,13 +31,14 @@ TARGET_ELF=$(TARGET).elf TARGET_BIN=$(TARGET).bin HWFLAGS=-mARCv2EM -mav2em -mlittle-endian -CFGFLAGS=-DCONFIG_SOC_GPIO_32 + +CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DHAS_SHARED_MEM OPTFLAGS=-g -Os -Wall -INCLUDES=-Icommon -Ibootcode +INCLUDES=-Icommon -Ibootcode -Iframework/include EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) -LDFLAGS=-nostartfiles -nodefaultlibs -nostdlib -static \ +LDFLAGS=-nostartfiles -static \ -Wl,-X -Wl,-N -Wl,-mARCv2EM -Wl,-marcelf -Wl,--gc-sections \ -Wl,-Map,$(TARGET_ELF).map \ -Wl,--whole-archive $(TARGET_LIB) -Wl,--no-whole-archive \ diff --git a/system/libarc32_edu/framework/include/cfw/cfw.h b/system/libarc32_edu/framework/include/cfw/cfw.h new file mode 100644 index 00000000..d575a532 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw.h @@ -0,0 +1,202 @@ +/** + * \defgroup cfw CFW: Component Framework + * \brief The component framework is the main building block for a custom application. + * + * It consists of: + * - Service APIs + * - Service Manager API + * - Main loop handler that wraps the messaging and exposes a functional / + * callback interface to the application. + * + * An application always need to create a thread managed by the framework in + * order to be interfaced with it. + * It could have other threads for its specific need and would have to manage + * communication with the framework thread for interface with the rest of the platform. + * @{ + */ + +#ifndef _CFW_H_ +#define _CFW_H_ + +#include +#include + +#include "os/os.h" +#include "util/list.h" +#include "infra/port.h" +#include "infra/message.h" + +struct cfw_message { + struct message m; + + /* The two following fields are specific to framework + * messages, and could be avoided / re-used for other + * types of messages. + */ + + /** The belonging connection of the message */ + void * conn; + /** The private data passed with the request. */ + void * priv; +}; + +#define CFW_MESSAGE_ID(msg) MESSAGE_ID(&(msg)->m) +#define CFW_MESSAGE_SRC(msg) MESSAGE_SRC(&(msg)->m) +#define CFW_MESSAGE_DST(msg) MESSAGE_DST(&(msg)->m) +#define CFW_MESSAGE_LEN(msg) MESSAGE_LEN(&(msg)->m) +#define CFW_MESSAGE_TYPE(msg) MESSAGE_TYPE(&(msg)->m) +#define CFW_MESSAGE_CONN(msg) (msg)->conn +#define CFW_MESSAGE_PRIV(msg) (msg)->priv +#define CFW_MESSAGE_HEADER(msg) (&(msg)->m) + +struct cfw_rsp_message { + /** Common message header */ + struct cfw_message header; + /** response status code.*/ + int status; +}; + +/** + * Message handler definition. + */ +typedef void (*handle_msg_cb_t)(struct cfw_message *, void *); + +typedef void * cfw_handle_t; + +/** + * \struct svc_client_handle_t + * \brief the structure used to manage a connection between a client + * and a service. + * + * This structure is returned in \ref cfw_open_conn_req_msg_t in + * the client_handle field + */ +typedef struct svc_client_handle_ { + /** Port to attain the service. */ + uint16_t port; + /** Framework handle. */ + cfw_handle_t cfw_handle; + /** Pointer to store the server-side connection handle. + * Passed in the conn field of \ref struct cfw_message for request messages + */ + void * server_handle; +} svc_client_handle_t; + +/** + * Services id definitions + * + * Keep them numbered manually to avoid shifting on + * removal/addition of services + */ +enum { + FRAMEWORK_SERVICE_ID = 1, + TEST2_SERVICE_ID = 2, + TEST_SERVICE_ID = 3, + BLE_SERVICE_ID = 4, + BLE_CORE_SERVICE_ID = 5, +}; + +/** + * Logging macro. + */ +/* + * Function Prototypes + * + */ +void cfw_log(char * fmt, ...); + +/** + * \brief Allocate a memory buffer + * it is mandatory to free the memory with \ref cfw_free + * + * \param size size of the memory to allocate + * + * \param err (out): execution status: + * -# E_OS_OK : memory was allocated + * -# E_OS_ERR: unable to allocate memory + * + * \return pointer to the allocated memory or NULL if failed. + */ +void * cfw_alloc(int size, OS_ERR_TYPE * err); + +struct cfw_message * cfw_alloc_message(int size, OS_ERR_TYPE * err); + +/** + * \brief free a block of memory allocated with \ref cfw_alloc + * + * \param ptr the block address to free. + * + * \param err (out): execution status: + * -# E_OS_OK : memory was freed + * -# E_OS_ERR: unable to free memory + */ +void cfw_free(void * ptr, OS_ERR_TYPE * err); + +/** + * \brief free a message. + * + * This function will take care to send the freeing request to + * the core that allocated the message. (based on the source + * port) + * + * \param ptr the message to be freed. + */ +void cfw_msg_free(struct cfw_message * msg); + +/** + * Create a copy of a given message. + * + * \param msg the message to be cloned. + * \return the cloned message + */ +struct cfw_message * cfw_clone_message(struct cfw_message * msg); + + +/** + * Send a message. + * The message should be filed with the destination port, + * source port, message identifier, etc... + * + * \param msg the parameter to send. + */ +int _cfw_send_message(struct cfw_message * msg); + +/** + * This macro conveniently casts the parameter to a message header pointer. + */ +#define cfw_send_message(_msg_) _cfw_send_message((struct cfw_message*) (_msg_)) + +/** + * get the local identifier of the service. + */ +int _find_service(int); + +/** + * Get the port id of the given service. + */ +int _cfw_get_service_port(int); + + +/* + * Multi cpu APIs. + */ + +/** + * \brief set the callback to be used to send a message to the given cpu. + * + * \param cpu_id the cpu id for which we want to set the handler. + * \param handler the callback used to send a message to this cpu. + */ +void set_cpu_message_sender(int cpu_id, int (*handler)(struct cfw_message * msg)); + +/** + * \brief set the callback to be used to request free a message to a cpu. + * + * \param cpu_id the cpu id for which we want to set the handler. + * \param handler the callback used to request message free on. + */ +void set_cpu_free_handler(int cpu_id, void (*free_handler)(void *)); + +#endif /* #ifndef _CFW_H_ */ + +/**@}*/ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_client.h b/system/libarc32_edu/framework/include/cfw/cfw_client.h new file mode 100644 index 00000000..02d69149 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_client.h @@ -0,0 +1,92 @@ +/** + * \addtogroup cfw + * @{ + * \defgroup cfw_client CFW Client API + * @{ + * \brief Definition of the structure and functions needed by CFW clients, i.e. + * for users of services. + */ + +#ifndef __CFW_CLIENT_H__ +#define __CFW_CLIENT_H__ +#include "cfw/cfw.h" + +/** + * Create a handle to the component framework. + * This handle is to be used for all other requests + * to the component framework + * + * Implementation is different in the master and the slave contexts. + * The master context will be pseudo-synchronous, while the slave + * implementation will actually pass a message to the master context + * in order to register a new client. + * + * \param cb the callback that will be called for each message reception + * \param param the param passed along with the message to the callback + */ +cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void * param); + + +/** + * Allocate a request message for a service. + * This will fill the needed common message fields needed to interact + * with a service. + */ +struct cfw_message * cfw_alloc_message_for_service(svc_client_handle_t * h, + int msg_id, int msg_size, void * priv); + + +/** + * Open a connection to the specified service. + * The connection handle is returned in the OPEN_CONNECTION + * confirmation message. + * + * \msc + * Client,"FW API","Service Manager"; + * + * Client=>"FW API" [label="cfw_open_connection", URL="\ref cfw_open_connection"]; + * "FW API"->"Service Manager" [label="CFW_OPEN_SERVICE REQ", URL="\ref cfw_open_conn_req_msg_t", ID="1"]; + * Client<<"FW API" ; + * Client<-"Service Manager" [label="CFW_OPEN_SERVICE CNF", URL="\ref cfw_open_conn_rsp_msg_t", ID="2"]; + + * \endmsc + * + * \param handle the handle to the component framework, as returned by @cfw_init + * \param service_id the unique service identifier. + * \return 0 if request succeeded and != 0 if error occured. + */ +int cfw_open_service(cfw_handle_t handle, int service_id, void *param); + +/** + * Closes a connection. + * + * \param handle the client handle representing the connection + * \param priv an opaque data passed back in the close response message + * \ return 0 if request succeeded and !=0 otherwise + */ +int cfw_close_service(svc_client_handle_t *handle, void *priv); + +/** + * Register to service events. + * + * \param handle the service connection handle, as returned in the \ref cfw_open_conn_rsp_msg_t + * \param msg_ids the array of event message ids to register to. + * \param size the size of the msg_ids array. + * \param param the void * private param that will be returned in the \ref cfw_register_evt_rsp_msg_t + */ +int cfw_register_events(svc_client_handle_t * handle, int * msg_ids, int size, void*param); + +/** + * Register to service availability events. + * Whenever a service is registered, the clients that registered to service_available will + * receive a cfw_svc_available_evt_msg_t message whenever a service is available. + * + * \param handle the framework handle. + * \param param the private param sent back with the response message. + */ +int cfw_register_svc_available(cfw_handle_t handle, void *param); + +#endif + +/**@} @}*/ + diff --git a/system/libarc32_edu/framework/include/cfw/cfw_debug.h b/system/libarc32_edu/framework/include/cfw/cfw_debug.h new file mode 100644 index 00000000..40a44657 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_debug.h @@ -0,0 +1,7 @@ +#ifndef __CFW_DEBUG_H__ +#define __CFW_DEBUG_H__ + +char * cfw_get_msg_type_str(struct cfw_message *msg); +void cfw_dump_message(struct cfw_message * msg); + +#endif /* __CFW_DEBUG_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_internal.h b/system/libarc32_edu/framework/include/cfw/cfw_internal.h new file mode 100644 index 00000000..2ae1276e --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_internal.h @@ -0,0 +1,30 @@ +#ifndef __CFW_INTERNAL_H__ +#define __CFW_INTERNAL_H__ + +#include "cfw/cfw.h" +#include "cfw/cfw_service.h" +#include "infra/ipc_requests.h" + +#define MAX_SERVICES 16 +typedef struct { + handle_msg_cb_t handle_msg; + void *data; + uint16_t client_port_id; +}_cfw_handle_t; + +/** + * This function is called when an sync IPC request is issued + * from a secondary CPU. + * + * \param cpu_id the cpu_id that originated the request + * \param request the request id. + * \param param1 first param + * \param param2 second param + * \param ptr third param + * + * \return the value to be passed as response to the requestor + */ +int handle_ipc_sync_request(int cpu_id, int request, int param1, + int param2, void * ptr); + +#endif /* __CFW_INTERNAL_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_messages.h b/system/libarc32_edu/framework/include/cfw/cfw_messages.h new file mode 100644 index 00000000..e88cf39f --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_messages.h @@ -0,0 +1,102 @@ +#ifndef __CFW_MESSAGES_H__ +#define __CFW_MESSAGES_H__ + +#include +#include + +enum +{ + MSG_ID_CFW_OPEN_SERVICE = 0x100, + MSG_ID_CFW_SERVICE_REGISTER, + MSG_ID_CFW_REGISTER_SVC_AVAIL, + MSG_ID_CFW_SVC_AVAIL_EVT, + MSG_ID_CFW_REGISTER_EVT, + MSG_ID_CFW_UNREGISTER_EVT, + MSG_ID_CFW_ALLOC_PORT, + MSG_ID_CFW_CLOSE_SERVICE, + MSG_ID_CFW_LAST +}; + +/** + * \struct cfw_open_conn_req_msg_t + * request message sent by \ref cfw_open_connection API. + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** service to open connection to */ + int service_id; + /** client side service handle */ + void *client_handle; +} cfw_open_conn_req_msg_t; + +/** + * \struct cfw_open_conn_cnf_msg_t + * response message to the \ref cfw_open_connection API. + */ +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; + /** port to attain this service */ + uint16_t port; + /** cpu_id of service */ + int cpu_id; + /** service handle for accessing the service */ + void * svc_server_handle; + /** client side service handle as passed in + * \ref cfw_open_conn_req_msg_t */ + void * client_handle; +} cfw_open_conn_rsp_msg_t; + +/** + * \struct cfw_close_conn_req_msg_t + * request message sent by \ref cfw_close_connection API. + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** service to open connection to */ + void * inst; +} cfw_close_conn_req_msg_t; + +/** + * \struct cfw_close_conn_cnf_msg_t + * response message to the \ref cfw_close_connection API. + */ +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_close_conn_rsp_msg_t; + +typedef struct { + /** common message header */ + struct cfw_message header; + /** indication message identifier. + * all subsequent indication with this identifier will be sent + * to the src port of this request message. + */ + int evt; +} cfw_register_evt_req_msg_t; + +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_register_evt_rsp_msg_t; + + +typedef struct { + /** common response message header */ + struct cfw_rsp_message rsp_header; +} cfw_register_svc_avail_rsp_msg_t; +/** + * This message is sent to clients that called \ref cfw_register_svc_available api. + * it notifies of the availability of a service. + * + */ +typedef struct { + /** common message header */ + struct cfw_message header; + /** Service id of the newly available service. */ + int service_id; +} cfw_svc_available_evt_msg_t; +#endif /* __CFW_MESSAGE_H__ */ diff --git a/system/libarc32_edu/framework/include/cfw/cfw_service.h b/system/libarc32_edu/framework/include/cfw/cfw_service.h new file mode 100644 index 00000000..ee19efe9 --- /dev/null +++ b/system/libarc32_edu/framework/include/cfw/cfw_service.h @@ -0,0 +1,140 @@ +/** + * \addtogroup cfw + * @{ + * \defgroup cfw_service CFW Service API + * @{ + * \brief Definition of the structure and functions used by CFW services implementation. + */ + +#ifndef __CFW_SERVICE_H_ +#define __CFW_SERVICE_H_ +#include "os/os.h" +#include "cfw/cfw.h" +#include "cfw/cfw_internal.h" +#include "infra/message.h" +#include "infra/port.h" + +struct service; +struct _port; + +/** + * \struct conn_handle_t + * + * This structure is used by the framework to maintain a connection + * between a client and a service. + */ +typedef struct cfw_conn_ { + /** Port to reach the client */ + uint16_t client_port; + /** The service pointer.*/ + struct service * svc; + /** pointer for the service to store client-specific data. */ + void * priv_data; + /** Pointer to store the client-side specific handle. */ + void * client_handle; +} conn_handle_t; + +/** + * Internal definition of a service. + */ +typedef struct service { + uint16_t port_id; + /** Identifier of the service.*/ + int service_id; + /** This callback is called when a client connects. + * called in the context of the service manager. + */ + void (*client_connected)(conn_handle_t *); + /** This callback is called when a client disconnects. + * Called in the context of the service manager. + */ + void (*client_disconnected)(conn_handle_t *); + /* This callback is called when the registered event + * list is modified. + * Called in the context of the service manager. + */ + void (*registered_events_changed)(conn_handle_t *); +} service_t; + +int _cfw_register_service(service_t * svc); +int _cfw_deregister_service(cfw_handle_t handle, service_t * svc); +#ifdef CFW_MULTI_CPU_SUPPORT +void _cfw_init_proxy(T_QUEUE queue, void * p, service_t**s, uint16_t svc_mgr_port); +#endif +service_t * cfw_get_service(int service_id); + + + +/** + * Allocate and build a response message for a specific request. + * + * \param req the request message + * \param msg_id the id of the response message + * \param size the size of the response message + * + * \return the allocated and initialized response message + */ +struct cfw_rsp_message * cfw_alloc_rsp_msg(struct cfw_message *req, int msg_id, int size); + +/** + * Allocate an event message. + * + * \param svc the service allocating the event message. + * \param msg_id the message identifier of the event. + * \param size the size of the message to be allocated. + * + * \return the allocated event message. + */ +struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size); + +/** + * Allocate an internal event message. + * + * \param msg_id the message identifier of the event. + * \param size the size of the message to be allocated. + * \param priv the private data passed with the event. + * + * \return the allocated event message. + */ +struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv); + +/** + * Register a port to an indication. + * + * \param msgId the indication message id that we want to receive + * \param port the port id where we want to receive the indication + * message. + */ +void _cfw_register_event(conn_handle_t *handle, int msgId); + +/** + * register a service to the system. + * registering a service makes it available to all other services + * and applications. + * + * \param queue the queue on which the service is to be run + * \param service the service structure to register + * \param handle_message the service's message handler + * \param param the parameter passed to message handler + */ +int cfw_register_service(T_QUEUE queue, service_t * service, + handle_msg_cb_t handle_message, void * param); + +/** + * un-register a service from the system. + * all other services and applications cannot see it anymore. + * + * \param service the service structure to unregister. + */ +int cfw_unregister_service(service_t * service); + +/** + * Send an indication message to the registered clients. + * + * \param msg the indication message to send. + */ +void cfw_send_event(struct cfw_message * msg); + +#endif /* __CFW_SERVICE_H_ */ + +/**@} @}*/ diff --git a/system/libarc32_edu/framework/include/infra/ipc.h b/system/libarc32_edu/framework/include/infra/ipc.h new file mode 100644 index 00000000..67c42d2c --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/ipc.h @@ -0,0 +1,79 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +#ifndef __IPC_H__ +#define __IPC_H__ + +#include "infra/ipc_requests.h" + +/** + * \brief initialize ipc component. + * + * \param tx_channel the IPC tx mailbox. + * \param rx_channel the IPC rx mailbox. + * \param tx_ack_channel the tx acknowledge mailbox. + * \param rx_ack_channel the rx acknowledge mailbox. + * \param remote_cpu_id the remote cpu id of this IPC + */ +void ipc_init(int tx_channel, int rx_channel, int tx_ack_channel, + int rx_ack_channel, int remote_cpu_id); + +/** + * \brief request a synchronous ipc call. + * + * This method blocks until the command is answered. + * + * \param request_id the synchronous request id + * \param param1 the first param for the request + * \param param2 the second param for the request + * \param ptr the third param for the request + * + * \return the synchronous command response. + */ +int ipc_request_sync_int(int request_id, int param1, int param2, void*ptr); + +/** + * \brief polling mode polling call. + * + * In polling mode, this method has to be called in order to handle + * ipc requests. + */ +void ipc_handle_message(); + +/** + * \brief Called by platform specific code when an IPC synchronous request is + * received. + * + * Inter-processors request callback called in the context of an interrupt + * + * \param cpu_id the cpu this request comes from + * \param param1 a first int parameter + * \param param2 a second int parameter + * \param ptr a last pointer parameter + * + * \return 0 if success, -1 otherwise + */ +int ipc_sync_callback(int cpu_id, int request, int param1, int param2, + void *ptr); + +#endif diff --git a/system/libarc32_edu/framework/include/infra/ipc_requests.h b/system/libarc32_edu/framework/include/infra/ipc_requests.h new file mode 100644 index 00000000..ebf25ee9 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/ipc_requests.h @@ -0,0 +1,13 @@ +#ifndef __IPC_REQUESTS_H__ +#define __IPC_REQUESTS_H__ + +#define IPC_MSG_TYPE_MESSAGE 0x1 +#define IPC_MSG_TYPE_FREE 0x2 +#define IPC_MSG_TYPE_SYNC 0x3 + +#define IPC_REQUEST_ALLOC_PORT 0x10 +#define IPC_REQUEST_REGISTER_SERVICE 0x11 +#define IPC_REQUEST_DEREGISTER_SERVICE 0x12 +#define IPC_REQUEST_REG_TCMD_ENGINE 0x13 + +#endif diff --git a/system/libarc32_edu/framework/include/infra/log.h b/system/libarc32_edu/framework/include/infra/log.h new file mode 100644 index 00000000..a42edb98 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/log.h @@ -0,0 +1,151 @@ +/* INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __LOG_H +#define __LOG_H + +/** + * \addtogroup infra + * @{ + * \defgroup infra_log Logger infrastructure + * @{ + * \brief Logger infrastructure + * + */ + +#include +#include + +#include "infra/log_backends.h" + +/* Maximum delay for the log_task to wait for a new message notification */ +#define LOG_MAX_DELAY 0xffffffff + +/* log configuration */ +#define LOG_MAX_MSG_LEN (80) /* Max allowed len: uint8_t - 1 */ + +/* log levels */ +enum { + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_NUM /* gives the number of log levels */ +}; + +/* module ids */ +#define DEFINE_LOGGER_MODULE(_i_,_n_) _i_, +enum { +#include "log_modules" + LOG_MODULE_NUM /* gives the number of modules */ +}; +#undef DEFINE_LOGGER_MODULE + +#define LOG_LEVEL_DEFAULT (LOG_LEVEL_INFO) + +/** + * Initializes the logger thread, queue and settings, and calls the + * initialization functions. + */ +void log_init(void); + +/** + * Flushes the log messages queue, called from the panic handler. + */ +void log_flush(void (*be)(const char *s)); + +/** + * Creates and pushes a user's log message into the logging queue. + * + * @return Message's length if inserted, -1 if no available memory, 0 if + * message was discarded. + */ +int8_t log_printk(uint8_t level, uint8_t module, const char *format, ...); + +/** + * Returns a log module's name, as string. + * + * @param module The module id + * + * @return Const pointer on the module name (NULL if module not found). + */ +const char * log_get_module_name(uint8_t module); + +/** + * Returns a log level's name, as string. + * + * @param level The level id + * + * @return Const pointer on the module name (NULL if module not found). + */ +const char * log_get_level_name(uint8_t level); + +/** + * Set the global log level value. This acts as a + * maximum overall level limit. + * + * @param level The new log level value, from the log level enum. + * + * @return -1 if error, + * 0 if new level was set + */ +int8_t log_set_global_level(uint8_t level); + +/** + * Disable or enable the logging for a module. + * + * @param module_id The module id to be disabled + * @param action Disable (0) or enable (1) + * + * @return -1 if the module_id or action is incorrect, + * 0 if module filter was disabled + */ +int8_t log_module_toggle(uint8_t module_id, uint8_t action); + +/** + * Set the level limit per module id. + * + * @param module_id The module id to be changed + * @param level The new log level limit for this module + * @return -2 if the module_id is incorrect, + * -1 if the level is incorrect, + * 0 if module filter was changed + */ +int8_t log_module_set_level(uint8_t module_id, uint8_t level); + +/* TODO - implement proper logging handler later */ +#define log_printk(module, format,...) + +/* log function format */ +#define pr_error(module, format,...) log_printk(LOG_LEVEL_ERROR, module, format,##__VA_ARGS__) + +#define pr_warning(module, format,...) log_printk(LOG_LEVEL_WARNING, module, format,##__VA_ARGS__) + +#define pr_info(module, format,...) log_printk(LOG_LEVEL_INFO, module, format,##__VA_ARGS__) + +#define pr_debug(module, format,...) log_printk(LOG_LEVEL_DEBUG, module, format,##__VA_ARGS__) + + +/* @}*/ +#endif /* __LOG_H */ diff --git a/system/libarc32_edu/framework/include/infra/log_backends.h b/system/libarc32_edu/framework/include/infra/log_backends.h new file mode 100644 index 00000000..780fc020 --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/log_backends.h @@ -0,0 +1,80 @@ +/* INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __LOG_BACKENDS_H +#define __LOG_BACKENDS_H + + +/** + * \addtogroup infra + * @{ + * \defgroup infra_log_backend Logger backends management + * @{ + * \brief Backends management + * + */ + + +#include +#define LOG_BACKEND_CNT 3 /*!< Max number of backends (UART, USB, NVM) */ + +/* backend callbacks typedefs */ +typedef void (*backend_puts)(const char *buffer, uint16_t len); +typedef void (*backend_print)(const char *buffer); + +/** + * Structure of backends. + */ +typedef struct log_backend { + backend_print bprint; /*!< Backend print */ + backend_puts bputs; /*!< Backend puts */ +} log_backend_t; + + +/** + * + * Function to call logger backends. + * + * @param s Message + * @param len Length of the message + */ +void log_call_backends(const char *s, uint16_t len); + + +/** + * Function to clear registered backends. + */ +void log_clear_backends(void); + + +/** + * Function to register backends. + * + * @param bprint Backend print + * @param bputs Backend puts + */ +uint8_t log_register_backend(backend_print bprint, backend_puts bputs); + +/* @}*/ +#endif /* __LOG_BACKENDS_H */ diff --git a/system/libarc32_edu/framework/include/infra/message.h b/system/libarc32_edu/framework/include/infra/message.h new file mode 100644 index 00000000..4a263f5e --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/message.h @@ -0,0 +1,102 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +#ifndef __INFRA_MESSAGE_H_ +#define __INFRA_MESSAGE_H_ + +#include +#include "util/list.h" + +/* Message ranges */ +#define INFRA_MSG_TCMD_BASE 0xFF00 + + +/** + * Message types definition. + */ +typedef enum { + TYPE_REQ, + TYPE_RSP, + TYPE_EVT, + TYPE_INT +} msg_type_t; + +/** + * message class definition. + */ +typedef enum { + CLASS_NORMAL, + CLASS_NO_WAKE, + CLASS_NO_WAKE_REPLACE, + CLASS_REPLACE +} msg_class_t; + +/** + * Message flags definitions. + * + * prio: priority of the message. + * class: class of the message + * type: type of message + */ +struct msg_flags { + uint16_t f_prio: 8; + uint16_t f_class: 3; + uint16_t f_type: 2; +}; + +/** + * Message header structure definition. + * Data message follows the header structure. + * Header must be packed in order to ensure that + * it can be simply exchanged between embedded cores. + */ +struct message { +#ifndef NO_MSG_LIST + /** Message queueing member */ + list_t l; +#endif + /** Message identifier */ + uint16_t id; + /** Message destination port */ + uint16_t dst_port_id; + /** Message source port */ + uint16_t src_port_id; + /** Message length */ + uint16_t len; + /** The message flags */ + struct msg_flags flags; +}; + +#define MESSAGE_ID(msg) (msg)->id +#define MESSAGE_SRC(msg) (msg)->src_port_id +#define MESSAGE_DST(msg) (msg)->dst_port_id +#define MESSAGE_LEN(msg) (msg)->len +#define MESSAGE_TYPE(msg) (msg)->flags.f_type +#define MESSAGE_PRIO(msg) (msg)->flags.f_prio +#define MESSAGE_CLASS(msg) (msg)->flags.f_class + +struct message * message_alloc(int size, OS_ERR_TYPE * err); +void message_free(struct message *message); + + +#endif /* __INFRA_MESSAGE_H_ */ diff --git a/system/libarc32_edu/framework/include/infra/port.h b/system/libarc32_edu/framework/include/infra/port.h new file mode 100644 index 00000000..4c12284a --- /dev/null +++ b/system/libarc32_edu/framework/include/infra/port.h @@ -0,0 +1,144 @@ +/* + * INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + */ +#ifndef __INFRA_PORT_H__ +#define __INFRA_PORT_H__ + +#include +#include "infra/message.h" + +#define MAX_PORTS 32 + + +#ifndef INFRA_IS_MASTER +/** + * Set the port table structure for a remote, shared mem enabled CPU. + */ +void * port_alloc_port_table(int numport); +void port_set_ports_table(void * ptbl); +#endif + +/** + * Allocate a port. The allocation of the port id is global to + * the platform. Only the Main Service Manager is allocating. + * The slave frameworks send messages to the master fw in order + * to allocate and get the port. + * The message handler for this port shall be set by the framework. + * + * return the allocated port structure ( filed with the allocated id) + */ +uint16_t port_alloc(void * queue); + + +/** + * Sets the message handler for this port. + * + * \param port the port to which set the given handler. + * \param handler the message handler. + * \param param the private parameter to pass to the handler message + * in addition to the message. + */ +void port_set_handler(uint16_t port_id, void (*handler)(struct message *, void *), + void * param); + +/** + * call the handler function attached to this port. + * This function shall be called when a message is retrieved from a queue. + * \param msg the message to process + */ +void port_process_message(struct message * msg); + +/** + * send a message to the destination port set in the message. + * + * \param msg the message to send + */ +int port_send_message(struct message * msg); + +/** + * set the port id of the given port. + * This is done to initialize a port in the context of a slave node. + * + * \param port_id the port id to initialize + */ +void port_set_port_id(uint16_t port_id); + +/** + * set the cpu_id of the given port. + * This is used to know if the internal or ipc messaging needs to be used. + * + * \param port_id the port_id to set cpu_id for + * \param cpu_id the cpu_id to associate with the port + */ +void port_set_cpu_id(uint16_t port_id, uint16_t cpu_id); + +/** + * get the cpu_id of the given port. + * + * \param port_id the port to retrieve cpu_id from + * \return the cpu_id associated with the port + */ +uint16_t port_get_cpu_id(uint16_t port_id); + +/** + * Retrieve the pointer to the global port table. + * This should only be used by the master in the context of shared memory + * communications, in order to pass the port table to a slave node with + * memory shared with the master node. + * + * \return the address of the global port table + */ +void * port_get_port_table(); + +/** + * Process the next message in a queue. + * + * Gets the first pending message out of the queue and calls the appropriate + * port handler + * + * \param queue The queue to fetch the message from + * + * \return the message id or 0 + * + */ +uint16_t queue_process_message(T_QUEUE queue); + +/** + * Multi CPU support APIs. + */ + +/** + * Set the cpu id for this (port) instance. + * + * \param cpu_id the cpu id to set. + */ +void set_cpu_id(int cpu_id); + +/** + * Get the cpu id for this (port) instance. + * + * \return cpu_id of the instance. + */ +int get_cpu_id(void); + +#endif /* __INFRA_PORT_H_ */ diff --git a/system/libarc32_edu/framework/include/log_modules b/system/libarc32_edu/framework/include/log_modules new file mode 100644 index 00000000..6afe8312 --- /dev/null +++ b/system/libarc32_edu/framework/include/log_modules @@ -0,0 +1,4 @@ +/* DEFINE_LOGGER_MODULE(, ) */ + +DEFINE_LOGGER_MODULE(LOG_MODULE_MAIN, "MAIN") +DEFINE_LOGGER_MODULE(LOG_MODULE_LOG, "LOG") diff --git a/system/libarc32_edu/framework/include/os/os.h b/system/libarc32_edu/framework/include/os/os.h new file mode 100644 index 00000000..56e2e9d2 --- /dev/null +++ b/system/libarc32_edu/framework/include/os/os.h @@ -0,0 +1,259 @@ +/* INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel?s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +/** + * \defgroup os OS API + * \brief Definition of the OS API. + * @{ + */ + +/** + * \addtogroup os + * @{ + * \defgroup os_al OS Abstraction Layer + * @{ + * \brief Definition of the OS abstraction layer API. + */ + +#ifndef __OS_H__ +#define __OS_H__ + +#include "os/os_types.h" +#include "panic_api.h" /* To be provided by the platform */ +#include "interrupt.h" + +/********************************************************** + ************** GENERIC SERVICES*************************** + **********************************************************/ +extern void panic (OS_ERR_TYPE err); + +/********************************************************** + ************** OS ABSTRACTION **************************** + **********************************************************/ + +/** + * Delete a queue. + * + * Free a whole queue. + * \param queue - The queue to free. + */ +void queue_delete(T_QUEUE queue, OS_ERR_TYPE* err); + +/** + * \brief Create a message queue. + * + * Create a message queue. + * This service may panic if err parameter is NULL and: + * -# no queue is available, or + * -# when called from an ISR. + * + * Authorized execution levels: task, fiber. + * + * As for semaphores and mutexes, queues are picked from a pool of + * statically-allocated objects. + * + * \param maxSize: maximum number of messages in the queue. + * (Rationale: queues only contain pointer to messages) + * + * \param err (out): execution status: + * -# E_OS_OK : queue was created + * -# E_OS_ERR: all queues from the pool are already being used + * -# E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + * + * \return Handler on the created queue. + * NULL if all allocated queues are already being used. + */ +extern T_QUEUE queue_create(uint32_t maxSize, OS_ERR_TYPE* err); + +/** + * \brief Read a message from a queue + * + * Read and dequeue a message. + * This service may panic if err parameter is NULL and: + * -# queue parameter is invalid, or + * -# message parameter is NULL, or + * -# when called from an ISR. + * + * Authorized execution levels: task, fiber. + * + * \param queue : handler on the queue (value returned by queue_create). + * + * \param message (out): pointer to read message. + * + * \param timeout: maximum number of milliseconds to wait for the message. Special + * values OS_NO_WAIT and OS_WAIT_FOREVER may be used. + * + * \param err (out): execution status: + * -# E_OS_OK : a message was read + * -# E_OS_ERR_TIMEOUT: no message was received + * -# E_OS_ERR_EMPTY: the queue is empty + * -# E_OS_ERR: invalid parameter + * -# E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + */ +extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int timeout, OS_ERR_TYPE* err); + +/** + * \brief Send a message on a queue + * + * Send / queue a message. + * This service may panic if err parameter is NULL and: + * -# queue parameter is invalid, or + * -# the queue is already full, or + * + * Authorized execution levels: task, fiber, ISR. + * + * \param queue: handler on the queue (value returned by queue_create). + * + * \param message (in): pointer to the message to send. + * + * \param err (out): execution status: + * -# E_OS_OK : a message was read + * -# E_OS_ERR_OVERFLOW: the queue is full (message was not posted) + * -# E_OS_ERR: invalid parameter + */ +extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); + +/** + * \brief Attach an ISR to an IRQ + * + * Attach/connect an interrupt service routing to an interrupt request line. + * Specifying a NULL pointer, as isr parameter, will detach a previous ISR from + * the IRQ. + * This service may panic if err parameter is null and: + * irq parameter is invalid, or + * when called from an ISR. + * Authorized execution levels: task, fiber. + * + * + * - VIPER does not provide an API to detach an ISR from an IRQ. + * When isr parameter is NULL, this function disables the IRQ (if valid) + * instead of replacing the ISR pointer with zeros. + * + * \param irq: interrupt request line number. + * \param isr: pointer on the interrupt service routine + * \param isrData: pointer to the data passed as an argument to isr + * \param priority: requested priority of interrupt + * \param err (out): execution status: + * E_OS_OK : ISR was attached or detached to IRQ + * E_OS_ERR: irq parameter is invalid + * E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. + * + * NB: IRQ priority is not a parameter used in lakemont context, as it is inferred from the IRQ number: + * Priority = IRQ number / 16 ( rounded down ) + * --> \ref loApicIntr.c + */ +static inline __attribute__((always_inline)) +void interrupt_set_isr (int irq, T_ENTRY_POINT isr, void* isrData , int priority, OS_ERR_TYPE* err) +{ + unsigned key = interrupt_lock(); + interrupt_connect(irq, isr, isrData); + interrupt_priority_set(irq, priority); + interrupt_unlock(key); + *err = E_OS_OK; +} + +/** + * \brief Get the current tick in ms + * + * Return the current tick converted in milliseconds + * + * Authorized execution levels: task, fiber, ISR + * + * \return current tick converted in milliseconds + */ +extern uint32_t get_time_ms (void); + +/** + * \brief Get the current tick in us + * + * Return the current tick converted in microseconds + * + * Authorized execution levels: task, fiber, ISR + * + * \return current tick converted in microseconds + */ +extern uint64_t get_time_us (void); + +/** + * \brief Reserves a block of memory + * + * Authorized execution levels: task, fiber, ISR + * + * This function returns a pointer on the start of + * a reserved memory block whose size is equal or + * larger than the requested size. + * + * If there is not enough available memory, this + * function returns a null pointer and sets + * "err" parameter to E_OS_ERR_NO_MEMORY, or panic + * if "err" pointer is null. + * + * This function may panic if err is null and + * - size is null, or + * - there is not enough available memory + * + * \param size number of bytes to reserve + * + * + * \param err execution status: + * E_OS_OK : block was successfully reserved + * E_OS_ERR : size is null + * E_OS_ERR_NO_MEMORY: there is not enough available + * memory + * + * \return pointer to the reserved memory block + * or null if no block is available + */ +extern void* balloc (uint32_t size, OS_ERR_TYPE* err); + +/** + * \brief Frees a block of memory + * + * Authorized execution levels: task, fiber, ISR + * + * This function frees a memory block that was + * reserved by malloc. + * + * The "buffer" parameter must point to the + * start of the reserved block (i.e. it shall + * be a pointer returned by malloc). + * + * \param buffer pointer returned by malloc + * + * \return execution status: + * E_OS_OK : block was successfully freed + * E_OS_ERR : "buffer" param did not match + * any reserved block + */ +extern OS_ERR_TYPE bfree(void* buffer); + +/** + * \brief Initialize the OS abstraction layer + * + */ +extern void os_init (void); + +#endif + +/**@} @} @}*/ diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h new file mode 100644 index 00000000..51b7372e --- /dev/null +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -0,0 +1,82 @@ +/* INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __OS_TYPES_H__ +#define __OS_TYPES_H__ + +#include "stddef.h" +#include "stdbool.h" +#include "stdint.h" + + +/********************************************************** + ************** General definitions ********************** + **********************************************************/ +#define UNUSED(p) (void)(p) + +/** + * \enum OS_ERR_TYPE + * \brief Generic type for execution status */ +typedef enum { + E_OS_OK = 0, /** generic OK status */ + /* use negative values for errors */ + E_OS_ERR = -1, /** generic error status */ + E_OS_ERR_TIMEOUT = -2, /** timeout expired */ + E_OS_ERR_BUSY = -3, /** resource is not available */ + E_OS_ERR_OVERFLOW = -4, /** service would cause an overflow */ + E_OS_ERR_EMPTY = -5, /** no data available (e.g. queue is empty) */ + E_OS_ERR_NOT_ALLOWED = -6, /** service is not allowed in current execution context */ + E_OS_ERR_NO_MEMORY = -7, /** all allocated resources are already in use */ + E_OS_ERR_NOT_SUPPORTED = -8, /** service is not supported on current context or OS */ + /* more error codes to be defined */ + E_OS_ERR_UNKNOWN = - 100, /** invalid error code (bug?) */ +} OS_ERR_TYPE; + + + +/** Types for kernel objects */ +typedef void* T_SEMAPHORE ; +typedef void* T_MUTEX; +typedef void* T_QUEUE; +typedef void* T_QUEUE_MESSAGE; +typedef void* T_TIMER; +typedef void (* T_ENTRY_POINT) (void* ) ; +typedef void* T_TASK ; +typedef uint8_t T_TASK_PRIO ; +#define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO +#define LOWEST_TASK_PRIO OS_SPECIFIC_LOWEST_PRIO + +typedef enum { + E_TASK_UNCREATED = 0, + E_TASK_RUNNING, + E_TASK_SUSPENDED, +} T_TASK_STATE; + + +/** Special values for "timeout" parameter */ +#define OS_NO_WAIT 0 +#define OS_WAIT_FOREVER -1 + + +#endif diff --git a/system/libarc32_edu/framework/include/panic_api.h b/system/libarc32_edu/framework/include/panic_api.h new file mode 100644 index 00000000..33cf6a0b --- /dev/null +++ b/system/libarc32_edu/framework/include/panic_api.h @@ -0,0 +1 @@ +#define force_panic() panic(-1) diff --git a/system/libarc32_edu/framework/include/pf_init.h b/system/libarc32_edu/framework/include/pf_init.h new file mode 100644 index 00000000..50542ca9 --- /dev/null +++ b/system/libarc32_edu/framework/include/pf_init.h @@ -0,0 +1,32 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __PF_INIT_H_ +#define __PF_INIT_H_ + +void platform_init(); + +void platform_main_loop(); + +#endif diff --git a/system/libarc32_edu/framework/include/platform.h b/system/libarc32_edu/framework/include/platform.h new file mode 100644 index 00000000..a6d7ac30 --- /dev/null +++ b/system/libarc32_edu/framework/include/platform.h @@ -0,0 +1,52 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#ifndef __PLATFORM_H_ +#define __PLATFORM_H_ + +#define CPU_ID_LMT 0 +#define CPU_ID_ARC 1 +#define CPU_ID_BLE 2 +#define CPU_ID_HOST 3 +#define NUM_CPU 4 + +struct platform_shared_block_ { + void * arc_start; + void * ports; + void * services; + int service_mgr_port_id; +}; + +#define RAM_START 0xA8000000 +#define shared_data ((struct platform_shared_block_ *) RAM_START) + +#define SS_GPIO_SERVICE_ID 0x1001 +#define SOC_GPIO_SERVICE_ID 0x1002 +#define SS_ADC_SERVICE_ID 0x1003 +#define LL_STOR_SERVICE_ID 0x1004 + +#define MSG_ID_GPIO_BASE 0x2000 + +unsigned int get_timestamp(void); +#endif diff --git a/system/libarc32_edu/framework/include/services/gpio_service.h b/system/libarc32_edu/framework/include/services/gpio_service.h new file mode 100644 index 00000000..329b86e5 --- /dev/null +++ b/system/libarc32_edu/framework/include/services/gpio_service.h @@ -0,0 +1,218 @@ +/** INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +/** + * \defgroup services Services + * \brief Definition of the structure and functions used by services implementation. + * + * It consists of: + * - Service APIs + * + * @{ + */ + +/** + * \addtogroup services + * @{ + * \defgroup gpio_service GPIO Service API + * @{ + * \brief Definition of the structure and functions used by GPIO services implementation. + */ + +#ifndef __GPIO_SERVICE_H__ +#define __GPIO_SERVICE_H__ +#include "cfw/cfw.h" +#include "cfw/cfw_client.h" +#include "stdint.h" +#include "platform.h" + +#define MSG_ID_GPIO_CONFIGURE_REQ MSG_ID_GPIO_BASE +#define MSG_ID_GPIO_SET_REQ (MSG_ID_GPIO_BASE + 1) +#define MSG_ID_GPIO_GET_REQ (MSG_ID_GPIO_BASE + 2) +#define MSG_ID_GPIO_LISTEN_REQ (MSG_ID_GPIO_BASE + 3) +#define MSG_ID_GPIO_UNLISTEN_REQ (MSG_ID_GPIO_BASE + 4) + +#define MSG_ID_GPIO_CONFIGURE_RSP (MSG_ID_GPIO_CONFIGURE_REQ | 0x40) +#define MSG_ID_GPIO_SET_RSP (MSG_ID_GPIO_SET_REQ | 0x40) +#define MSG_ID_GPIO_GET_RSP (MSG_ID_GPIO_GET_REQ | 0x40) +#define MSG_ID_GPIO_LISTEN_RSP (MSG_ID_GPIO_LISTEN_REQ | 0x40) +#define MSG_ID_GPIO_UNLISTEN_RSP (MSG_ID_GPIO_UNLISTEN_REQ | 0x40) + +#define MSG_ID_GPIO_EVT (MSG_ID_GPIO_BASE | 0x80) + +typedef enum { + RISING_EDGE, + FALLING_EDGE, + BOTH_EDGE // Used on soc GPIO only +} gpio_service_isr_mode_t; + +typedef enum { + DEB_OFF = 0, + DEB_ON +} gpio_service_debounce_mode_t; + +void gpio_service_init(void * queue, int service_id); + +typedef struct gpio_configure_req_msg { + struct cfw_message header; + /** requested mode for each gpio + * 0 - gpio is an output + * 1 - gpio is an input + */ + gpio_service_isr_mode_t mode; + /** index of the gpio to configure in the port */ + uint8_t index; +} gpio_configure_req_msg_t; + +typedef struct gpio_configure_rsp_msg { + struct cfw_rsp_message rsp_header; +} gpio_configure_rsp_msg_t; + +typedef struct gpio_set_req_msg { + struct cfw_message header; + /** index of the gpio in the port */ + uint8_t index; + /** state of the gpio to set */ + uint8_t state; +} gpio_set_req_msg_t; + +typedef struct gpio_set_rsp_msg { + struct cfw_rsp_message rsp_header; +} gpio_set_rsp_msg_t; + +typedef struct gpio_listen_req_msg { + struct cfw_message header; + /** index of GPIO to monitor */ + uint8_t index; + /** interrupt mode */ + gpio_service_isr_mode_t mode; + /** debounce mode */ + gpio_service_debounce_mode_t debounce; +} gpio_listen_req_msg_t; + +typedef struct gpio_listen_rsp_msg { + struct cfw_rsp_message rsp_header; + /** index of GPIO to monitor */ + uint8_t index; +} gpio_listen_rsp_msg_t; + +typedef struct gpio_listen_evt_msg { + struct cfw_message header; + /** index of GPIO which triggered the interrupt */ + uint8_t index; + /** gpio current value */ + uint32_t pin_state; +} gpio_listen_evt_msg_t; + +typedef struct gpio_unlisten_req_msg { + struct cfw_message header; + /** index of GPIO to monitor */ + uint8_t index; + /** interrupt mode */ +} gpio_unlisten_req_msg_t; + +typedef struct gpio_unlisten_rsp_msg { + struct cfw_rsp_message rsp_header; + /** index of GPIO to monitor */ + uint8_t index; +} gpio_unlisten_rsp_msg_t; + +typedef struct gpio_get_req_msg { + struct cfw_message header; +} gpio_get_req_msg_t; + +typedef struct gpio_get_rsp_msg { + struct cfw_rsp_message rsp_header; + /** state of the gpio port */ + uint32_t state; +} gpio_get_rsp_msg_t; + +/** + * \brief configure gpio_line + * + * Configures a GPIO line. + * + * \param svc_handle the service connection handle + * \param index the GPIO index in the port to configure + * \param mode the mode of the GPIO line + * 0 - input + * 1 - output + * \param priv the private data passed back in the + * response message. + */ +int gpio_configure(svc_client_handle_t * svc_handle, uint8_t index, + uint8_t mode, void * priv); + +/** + * \brief Set the state of a GPIO line. + * + * \param svc_handle the service connection handle + * \param index the GPIO index in the port to configure + * \param value the state to set the GPIO line + * 0 - low level + * 1 - high level + * \param priv the private data passed back in the + * response message. + */ +int gpio_set_state(svc_client_handle_t * svc_handle, uint8_t index, + uint8_t value, void * priv); + +/** + * \brief get state of a GPIO line + * + * \param svc_handle the service connection handle + * \param priv the private data passed back in the + * response message. + * + * GPIO state will be available in the \ref gpio_get_state_rsp_msg_t + */ +int gpio_get_state(svc_client_handle_t * svc_handle, void * priv); + +/** + * \brief register to gpio state change. + * + * \param svc_handle: the service connection handle + * \param pin : the GPIO index in the port to configure + * \param mode : interrupt mode (RISING_EDGE, FALLING_EDGE) + * \param debounce : debounce config (DEB_OFF, DEB_ON) + * \param priv : the private data passed back in the response message. + * + * GPIO state and changed mask are available in the \ref gpio_listen_rsp_msg_t message. + */ +int gpio_listen(svc_client_handle_t * h, uint8_t pin, gpio_service_isr_mode_t mode, uint8_t debounce, void *priv); + +/** + * \brief unregister to gpio state change. + * + * \param svc_handle: the service connection handle + * \param pin : the GPIO index in the port to configure + * \param priv : the private data passed back in the response message. + * + * GPIO state and changed mask are available in the \ref gpio_unlisten_rsp_msg_t message. + */ +int gpio_unlisten(svc_client_handle_t * h, uint8_t pin, void *priv); + +#endif + +/**@} @} @}*/ diff --git a/system/libarc32_edu/framework/include/util/list.h b/system/libarc32_edu/framework/include/util/list.h new file mode 100644 index 00000000..40744206 --- /dev/null +++ b/system/libarc32_edu/framework/include/util/list.h @@ -0,0 +1,94 @@ +#ifndef __LIST_H__ +#define __LIST_H__ + +#include + +typedef struct list { + struct list * next; +} list_t; + +typedef struct list_head_ { + list_t * head; + list_t * tail; +} list_head_t; + +/** + * initialize a list structure + * + * \param list the list to initialize + */ +void list_init(list_head_t * list); + +/** + * append an element to the end of list. + * + * \param list the list to which element has to be added + * \param element the element we want to add to the list. + */ +void list_add(list_head_t * list, list_t * element); + +/** + * append an element to the begining of list. + * + * \param list the list to which element has to be added + * \param element the element we want to add to the list. + */ +void list_add_head(list_head_t * list, list_t * element); + +/** + * remove an element from the list. + * + * \param list the list to remove the element from + * \param element the element to remove from the list. + */ +void list_remove(list_head_t *list, list_t * element); + +/** + * Iterate through elements of a list. + * + * \param lh the list we want to iterate through + * \param cb the callback function to call for each element + * \param param the parameter to pass to the callback in + * addition to the element. + */ +void list_foreach(list_head_t * lh, void(*cb)(void *, void *), void * param); + +/** + * Iterate through elements of a list, with the option + * to remove element from the callback. + * + * \param lh the list we want to iterate through + * \param cb the callback function to call for each element. + * if the callback returns non-zero, the element is + * removed from the list. + * \param param the parameter to pass to the callback in + * addition to the element. + */ +void list_foreach_del(list_head_t * lh, int(*cb)(void *, void *), void * param); + +/** + * Get the first element from the list. + * + * \param lh the list from which the element has to be retrieved. + * \return the element removed. + */ +list_t * list_get(list_head_t * lh); + +/** + * Check if the list is empty + * + * \return 0 if not empty + */ +int list_empty(list_head_t *lh); + +/** + * Find the first item in a list matching a criteria. + * + * \param cb the test function that will be applied to each item + * \param data an opaque data passed to the test function + * + * \return the item or NULL + */ +list_t * list_find_first(list_head_t * lh, bool(*cb)(list_t*,void*), void *data); + +#endif /* __LIST_H__ */ diff --git a/system/libarc32_edu/framework/src/cfw/cfw_debug.c b/system/libarc32_edu/framework/src/cfw/cfw_debug.c new file mode 100644 index 00000000..c912d25c --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/cfw_debug.c @@ -0,0 +1,42 @@ +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_internal.h" +#include "infra/port.h" + +char * cfw_get_msg_type_str(struct cfw_message *msg) +{ + switch(CFW_MESSAGE_TYPE(msg)) { + case TYPE_REQ: + return "REQ"; + case TYPE_RSP: + return "RSP"; + case TYPE_EVT: + return "EVT"; + case TYPE_INT: + return "INT"; + default: + return "INVALID"; + } +} + +void cfw_dump_service_handle(svc_client_handle_t * svc_handle) +{ + cfw_log("svc_handle: port: %d, fw_handle: %p, server_handle: %p\n", + svc_handle->port, + svc_handle->cfw_handle, + svc_handle->server_handle); +} + +void cfw_dump_message(struct cfw_message * msg) +{ +#if 1 + cfw_log("%p id: %x src: %d[cpu:%d] dst: %d[cpu:%d] type: %s\n", + msg, CFW_MESSAGE_ID(msg), CFW_MESSAGE_SRC(msg), + port_get_cpu_id(CFW_MESSAGE_SRC(msg)), + CFW_MESSAGE_DST(msg), port_get_cpu_id(CFW_MESSAGE_DST(msg)), + cfw_get_msg_type_str(msg)); +#else + cfw_log("id: %x src: %d dst: %d type: %s\n", msg->id, + msg->src, msg->dst, cfw_get_msg_type_str(msg)); +#endif +} diff --git a/system/libarc32_edu/framework/src/cfw/client_api.c b/system/libarc32_edu/framework/src/cfw/client_api.c new file mode 100644 index 00000000..f3db73d8 --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/client_api.c @@ -0,0 +1,96 @@ +#include "cfw/cfw.h" +#include "cfw/cfw_client.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_messages.h" + +/** + * \file client_api.c + * + * Implementation of the client interface. + * + */ + +extern int service_mgr_port_id; + +int cfw_open_service(cfw_handle_t handle, int service_id, void * priv) { + cfw_open_conn_req_msg_t * msg = (cfw_open_conn_req_msg_t*) + cfw_alloc_message(sizeof(*msg), NULL); + CFW_MESSAGE_ID(&msg->header) = MSG_ID_CFW_OPEN_SERVICE; + CFW_MESSAGE_LEN(&msg->header) = sizeof(*msg); + CFW_MESSAGE_DST(&msg->header) = service_mgr_port_id; + CFW_MESSAGE_SRC(&msg->header) = ((_cfw_handle_t*)handle)->client_port_id; + + svc_client_handle_t * svch = (svc_client_handle_t*)cfw_alloc(sizeof(*svch), NULL); + svch->port = CFW_MESSAGE_SRC(&msg->header); + svch->cfw_handle = handle; + svch->server_handle = NULL; + + msg->service_id = service_id; + msg->client_handle = svch; + msg->header.priv = priv; + msg->header.conn = NULL; + cfw_send_message(msg); + return 0; +} + +int cfw_close_service(svc_client_handle_t *handle, void *priv) +{ + cfw_close_conn_req_msg_t *msg = (cfw_close_conn_req_msg_t*) + cfw_alloc_message(sizeof(*msg), NULL); + CFW_MESSAGE_ID(&msg->header) = MSG_ID_CFW_CLOSE_SERVICE; + CFW_MESSAGE_LEN(&msg->header) = sizeof(*msg); + CFW_MESSAGE_DST(&msg->header) = service_mgr_port_id; + CFW_MESSAGE_SRC(&msg->header) = + ((_cfw_handle_t*)handle->cfw_handle)->client_port_id; + msg->header.priv = priv; + msg->header.conn = handle->server_handle; + msg->inst = NULL; + cfw_send_message(msg); + return 0; +} + +int cfw_register_events(svc_client_handle_t * h, int * msg_ids, int size, void * priv) { + int msg_size = sizeof(struct cfw_message) + sizeof(int) * (size+1); + int i; + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_CFW_REGISTER_EVT, msg_size, + priv); + CFW_MESSAGE_DST(msg) = service_mgr_port_id; + ((int *) &msg[1])[0] = size; + for (i = 0; i < size; i++) { + ((int *) (&msg[1]))[i+1] = msg_ids[i]; + } + cfw_send_message(msg); + return 0; +} + +int cfw_register_svc_available(cfw_handle_t handle, void *priv) +{ + struct cfw_message * msg = cfw_alloc(sizeof(*msg), NULL); + CFW_MESSAGE_ID(msg) = MSG_ID_CFW_REGISTER_SVC_AVAIL; + CFW_MESSAGE_LEN(msg) = sizeof(*msg); + CFW_MESSAGE_SRC(msg) = ((_cfw_handle_t*)handle)->client_port_id; + CFW_MESSAGE_DST(msg) = service_mgr_port_id; + CFW_MESSAGE_TYPE(msg) = TYPE_REQ; + msg->priv = priv; + msg->conn = NULL; + cfw_send_message(msg); + return 0; +} + +struct cfw_message * cfw_alloc_message_for_service(svc_client_handle_t * h, int msg_id, int msg_size, + void * priv) { + struct cfw_message * msg = (struct cfw_message*) cfw_alloc(msg_size, NULL); + CFW_MESSAGE_ID(msg) = msg_id; + CFW_MESSAGE_LEN(msg) = msg_size; + CFW_MESSAGE_SRC(msg) = ((_cfw_handle_t*)h->cfw_handle)->client_port_id; + CFW_MESSAGE_DST(msg) = h->port; + CFW_MESSAGE_TYPE(msg) = TYPE_REQ; + msg->priv = priv; + msg->conn = h->server_handle; + return msg; +} + +struct cfw_message * cfw_alloc_message(int size, OS_ERR_TYPE * err) +{ + return (struct cfw_message *)message_alloc(size, err); +} diff --git a/system/libarc32_edu/framework/src/cfw/service_api.c b/system/libarc32_edu/framework/src/cfw/service_api.c new file mode 100644 index 00000000..522a8a9e --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/service_api.c @@ -0,0 +1,148 @@ +#include "util/list.h" +#include "os/os.h" +#include "infra/message.h" +#include "infra/port.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" + +/** + * \file service_api.c implementation of the service_manager API + */ + +/* Structure for holding if a service is local or remote. */ + +#define SEND_MESSAGE(_msg_) cfw_send_message(&(_msg_)->header) + +void cfw_port_set_handler(uint16_t port_id, void (*handler)(struct cfw_message*, void*), void * param) { +#ifdef SVC_API_DEBUG + cfw_log("%s: port: %p h: %p\n", __func__, port, handler); +#endif + port_set_handler(port_id, (void (*)(struct message*, void*))handler, param); +} + +struct cfw_message * cfw_clone_message(struct cfw_message * msg) { + struct cfw_message * ret = cfw_alloc(CFW_MESSAGE_LEN(msg), NULL); + if (ret == NULL) { + cfw_log("%s: Error allocating message", __func__); + } else { + memcpy(ret, msg, CFW_MESSAGE_LEN(msg)); + } + return ret; +} + +int cfw_register_service(T_QUEUE queue, service_t * svc, + handle_msg_cb_t handle_message, void * data) { + uint16_t port_id = port_alloc(queue); + + cfw_port_set_handler(port_id, handle_message, data); + svc->port_id = port_id; + return _cfw_register_service(svc); +} + +int cfw_deregister_service(cfw_handle_t handle, service_t * svc) { + return _cfw_deregister_service(handle, svc); +} + +struct cfw_rsp_message * cfw_alloc_rsp_msg(struct cfw_message *req, int msg_id, int size) { + struct cfw_rsp_message * rsp = (struct cfw_rsp_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(&rsp->header) = TYPE_RSP; + CFW_MESSAGE_ID(&rsp->header) = msg_id; + CFW_MESSAGE_LEN(&rsp->header) = size; + CFW_MESSAGE_DST(&rsp->header) = CFW_MESSAGE_SRC(req); + CFW_MESSAGE_SRC(&rsp->header) = CFW_MESSAGE_DST(req); + rsp->header.priv = req->priv; + /* Substitute server-side with client-side conn */ + if (req->conn != NULL) + rsp->header.conn = ((conn_handle_t*)req->conn)->client_handle; + else + rsp->header.conn = NULL; + return rsp; +} + +struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size) { + struct cfw_message * evt = (struct cfw_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(evt) = TYPE_EVT; + CFW_MESSAGE_ID(evt) = msg_id; + CFW_MESSAGE_LEN(evt) = size; + CFW_MESSAGE_SRC(evt) = svc->port_id; + /* 3 fields below whould be filed by send_event method*/ + CFW_MESSAGE_DST(evt) = 0; + evt->priv = NULL; + evt->conn = NULL; + return evt; +} + +struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv) { + struct cfw_message * evt = (struct cfw_message *) cfw_alloc(size, NULL); + CFW_MESSAGE_TYPE(evt) = TYPE_INT; + CFW_MESSAGE_ID(evt) = msg_id; + CFW_MESSAGE_LEN(evt) = size; + CFW_MESSAGE_SRC(evt) = 0; + /* 3 fields below whould be filed by send_event method*/ + CFW_MESSAGE_DST(evt) = 0; + evt->priv = priv; + evt->conn = NULL; + return evt; +} + +void default_msg_handler(struct cfw_message * msg, void *data) { + cfw_log("Bug: %s should not be called data: %p\n", __func__, data); + cfw_dump_message(msg); +} + +void client_handle_message(struct cfw_message * msg, void *param) { + _cfw_handle_t * h = (_cfw_handle_t*)param; + switch(CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_OPEN_SERVICE: + { + cfw_open_conn_rsp_msg_t * cnf = (cfw_open_conn_rsp_msg_t *) msg; + /** Make client handle point to server handle */ + ((svc_client_handle_t*)cnf->client_handle)->server_handle = cnf->svc_server_handle; + /** Initialize service port. */ + ((svc_client_handle_t*)cnf->client_handle)->port = cnf->port; +#ifndef INFRA_IS_MASTER + /* Set local port and cpu id */ + if (get_cpu_id() != cnf->cpu_id) { + port_set_port_id(cnf->port); + port_set_cpu_id(cnf->port, cnf->cpu_id); + } +#endif + break; + } + case MSG_ID_CFW_CLOSE_SERVICE: + { + /* Free client-side conn */ + cfw_free(msg->conn, NULL); + break; + } + default: + //Nothing to do + break; + } + h->handle_msg(msg, h->data); +} + +cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void *cb_data) { + _cfw_handle_t * handle = (_cfw_handle_t*)cfw_alloc(sizeof(*handle), NULL); + handle->handle_msg = cb; + handle->data = cb_data; + + handle->client_port_id = port_alloc(queue); + + cfw_port_set_handler(handle->client_port_id, client_handle_message, handle); + + return (cfw_handle_t) handle; +} + +int _cfw_send_message(struct cfw_message * message) +{ + return port_send_message(CFW_MESSAGE_HEADER(message)); +} + +void cfw_msg_free(struct cfw_message * msg) +{ + message_free(CFW_MESSAGE_HEADER(msg)); +} diff --git a/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c b/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c new file mode 100644 index 00000000..fcc3e821 --- /dev/null +++ b/system/libarc32_edu/framework/src/cfw/service_manager_proxy.c @@ -0,0 +1,256 @@ +#include "util/list.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" + +#include "infra/port.h" +#include "infra/ipc.h" + +/** + * \file service_manager_proxy.c implementation of the service_manager proxy + * + * This file implements the proxy for the service manager. + * It has to be compiled in all cores except the one that holds the service + * manager. + */ +int service_mgr_port_id = 0; + +service_t ** services = NULL; + +void internal_handle_message(struct cfw_message * msg, void * param) { + switch(CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_ALLOC_PORT: { + //_port_t * port = cfw_port_alloc(NULL); + //cfw_port_set_handler(port, send_message_ipc, NULL); + break; + } + + case MSG_ID_CFW_OPEN_SERVICE: { + conn_handle_t * conn_handle; + cfw_open_conn_req_msg_t * req = (cfw_open_conn_req_msg_t *) msg; + service_t * svc = cfw_get_service(req->service_id); + + conn_handle = (conn_handle_t *) cfw_alloc(sizeof(*conn_handle), NULL); + conn_handle->client_port = CFW_MESSAGE_SRC(msg); + conn_handle->priv_data = NULL; + conn_handle->svc = svc; + conn_handle->client_handle = req->client_handle; + /* For OPEN_SERVICE, conn is not know yet, it is just alloc'ed. + * set it here.*/ + req->header.conn = conn_handle; + if (svc->client_connected != NULL) + svc->client_connected(conn_handle); + cfw_open_conn_rsp_msg_t * resp = (cfw_open_conn_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_OPEN_SERVICE, sizeof(*resp)); + resp->port = svc->port_id; + resp->svc_server_handle = conn_handle; + resp->client_handle = req->client_handle; + cfw_send_message(resp); + break; + } + + case MSG_ID_CFW_CLOSE_SERVICE: { + cfw_close_conn_rsp_msg_t * resp = (cfw_close_conn_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_CLOSE_SERVICE, sizeof(*resp)); + conn_handle_t * conn = (conn_handle_t*)msg->conn; + if ( conn != NULL && conn->svc != NULL && conn->svc->client_disconnected != NULL) + conn->svc->client_disconnected(conn); + cfw_send_message(resp); + /* Free server-side conn */ + cfw_free(conn, NULL); + break; + } + + case MSG_ID_CFW_REGISTER_EVT: { + int * params = (int *) &msg[1]; + int i; + for (i = 0; i < params[0]; i++) { + _cfw_register_event((conn_handle_t*)msg->conn, params[i+1]); + } + cfw_register_evt_rsp_msg_t * resp = + (cfw_register_evt_rsp_msg_t *) cfw_alloc_rsp_msg(msg, + MSG_ID_CFW_REGISTER_EVT, (sizeof(*resp))); + conn_handle_t * conn = (conn_handle_t*)msg->conn; + if ( conn != NULL && conn->svc != NULL && conn->svc->registered_events_changed != NULL) + conn->svc->registered_events_changed(conn); + + cfw_send_message(resp); + break; + } + + default: + cfw_log("%s: unhandled message id: %x\n", __func__, CFW_MESSAGE_ID(msg)); + break; + } +} + +int handle_ipc_sync_request(int cpu_id, int request, int param1, int param2, void * ptr) +{ +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s: from %d, req:%d (%d, %d, %p)\n", __func__, cpu_id, request, param1, + param2, ptr); +#endif + switch (request) { + case IPC_MSG_TYPE_FREE: + cfw_free(ptr, NULL); + break; + case IPC_MSG_TYPE_MESSAGE: + { + struct cfw_message * msg = (struct cfw_message *) ptr; + _cfw_send_message(msg); + break; + } + case IPC_REQUEST_ALLOC_PORT: + { +#ifdef SVC_MANAGER_DEBUG + cfw_log("Updating port id %d\n", param1); +#endif + port_set_port_id(param1); + port_set_cpu_id(param1, get_cpu_id()); /* assign instance cpu id as this is a response */ + break; + } + default: + cfw_log("%s: unexpected ipc_sync_request: %d\n", __func__, request); + break; + } + return 0; +} + + +uint16_t cfw_port_alloc(void * queue) { + return port_alloc(queue); +} + + +/** + * Indication list + * Holds a list of receivers. + */ +typedef struct { + list_t list; + conn_handle_t * conn_handle; +} indication_list_t; + +/** + * \struct registered_int_list_t holds a list of registered clients to an indication + * + * Holds a list of registered receiver for each indication. + */ +typedef struct registered_evt_list_ { + list_t list; /*! Linking stucture */ + list_head_t lh; /*! List of client */ + int ind; /*! Indication message id */ +} registered_evt_list_t; + +list_head_t registered_evt_list; + + + +registered_evt_list_t * get_event_registered_list(int msg_id) { + registered_evt_list_t * l = (registered_evt_list_t*)registered_evt_list.head; + while(l) { + if (l->ind == msg_id) { + return l; + } + l = (registered_evt_list_t*)l->list.next; + } + return NULL; +} + +list_head_t * get_event_list(int msg_id) { + registered_evt_list_t * l = get_event_registered_list(msg_id); + if (l) + return &l->lh; + return NULL; +} + +static void send_event_callback(void * item, void * param) { + struct cfw_message * msg = (struct cfw_message *) param; + indication_list_t * ind = (indication_list_t *)item; + struct cfw_message * m = cfw_clone_message(msg); + if (m != NULL) { + CFW_MESSAGE_DST(m) = ind->conn_handle->client_port; + cfw_send_message(m); + } +} + +void cfw_send_event(struct cfw_message * msg) { +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s : msg:%d\n", __func__, CFW_MESSAGE_ID(msg)); +#endif + list_head_t * list = get_event_list(CFW_MESSAGE_ID(msg)); + if (list != NULL) { + list_foreach(list, send_event_callback, msg); + } +} + +void _cfw_register_event(conn_handle_t * h, int msg_id) { +#ifdef SVC_MANAGER_DEBUG + cfw_log("%s : msg:%d port %d h:%p\n", __func__, msg_id, h->client_port, h); +#endif + registered_evt_list_t * ind = (registered_evt_list_t *) get_event_registered_list(msg_id); + + if (ind == NULL) { + ind = (registered_evt_list_t *) cfw_alloc(sizeof(*ind), NULL); + ind->ind = msg_id; + list_init(&ind->lh); + list_add(®istered_evt_list, &ind->list); + } + + indication_list_t * e = (indication_list_t *)cfw_alloc(sizeof(*e), NULL); + e->conn_handle = h; + list_add(&ind->lh, (list_t *)e); +} + +void _cfw_loop(void * queue) { + struct cfw_message * message; + T_QUEUE_MESSAGE m; + while(1) { + queue_get_message(queue, &m, OS_WAIT_FOREVER, NULL); + message = (struct cfw_message *) m; + if ( message != NULL ) { + port_process_message(&message->m); + } + } +} + +service_t * cfw_get_service(int service_id) { + int i; + for(i=0; iservice_id == service_id) { + return services[i]; + } + } + return NULL; +} + +int _cfw_get_service_port(int service_id) { + service_t * svc = cfw_get_service(service_id); + if (svc != NULL) { + return svc->port_id; + } + return -1; +} +typedef void(*send_msg_t)(struct cfw_message * m); +extern send_msg_t get_ipc_handler(int cpu_id); + +void svc_manager_handle_message(struct cfw_message * msg, void * param) { + get_ipc_handler(0)(msg); +} + +void _cfw_init_proxy(T_QUEUE queue, void *p, service_t **s, uint16_t svc_mgr_port) { + port_set_ports_table(p); + services = s; + service_mgr_port_id = svc_mgr_port; + cfw_log("%s queue: %p\n", __func__, queue); +} + +int _cfw_register_service(service_t * svc) { + return ipc_request_sync_int(IPC_REQUEST_REGISTER_SERVICE, svc->service_id, svc->port_id, svc); +} + +int _cfw_deregister_service(cfw_handle_t handle, service_t * svc) { + return ipc_request_sync_int(IPC_REQUEST_DEREGISTER_SERVICE, svc->service_id, 0, svc); +} diff --git a/system/libarc32_edu/framework/src/infra/ipc.c b/system/libarc32_edu/framework/src/infra/ipc.c new file mode 100644 index 00000000..922a7385 --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/ipc.c @@ -0,0 +1,115 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#include +#include "os/os_types.h" +#include "infra/ipc.h" +#include "cfw/cfw.h" +#include "soc_register.h" +#include "infra/log.h" + +#define MBX_IPC_SYNC_ARC_TO_LMT 5 + +static int rx_chan = 0; +static int tx_chan = 0; +static int tx_ack_chan = 0; +static int rx_ack_chan = 0; +static int remote_cpu = 0; + +/***************************************************************************** + * IPC Protocol: + * 2 Mailboxes per side. + * One mailbox for passing data, one mailbox for acknowledging the transfer + * + ****************************************************************************/ + +unsigned int get_timestamp() +{ + return SCSS_REG_VAL(SCSS_AONC_CNT); +} + +void ipc_init(int tx_channel, int rx_channel, int tx_ack_channel, + int rx_ack_channel, int remote_cpu_id) +{ + rx_chan = rx_channel; + tx_chan = tx_channel; + tx_ack_chan = tx_ack_channel; + rx_ack_chan = rx_ack_channel; + remote_cpu = remote_cpu_id; +} + +void ipc_handle_message() +{ + int ret = 0; + if (!MBX_STS(rx_chan)) return; + int request = MBX_DAT0(rx_chan); + int param1 = MBX_DAT1(rx_chan); + int param2 = MBX_DAT2(rx_chan); + void * ptr = (void *)MBX_DAT3(rx_chan); + + ret = ipc_sync_callback(remote_cpu, request, param1, param2, ptr); + + MBX_CTRL(rx_chan) = 0x80000000; + + MBX_DAT0(tx_ack_chan) = ret; + MBX_CTRL(tx_ack_chan) = 0x80000000; + pr_debug(LOG_MODULE_MAIN, "read message on %d : ack [%d] %p", rx_chan, tx_ack_chan, + MBX_DAT0(tx_ack_chan)); + MBX_STS(rx_chan) = 3; +} + +int ipc_request_sync_int(int request_id, int param1, int param2, void * ptr) +{ + int ret; + int timeout; + + pr_debug(LOG_MODULE_MAIN, "send request %d from: %p", request_id, &ret); + while (MBX_CTRL(tx_chan) & 0x80000000) { + pr_info(LOG_MODULE_MAIN, "Channel busy %d for request: %d msg: %p", + tx_chan, request_id, param1); + pr_info(LOG_MODULE_MAIN, "current request: %p msg: %p", + MBX_CTRL(tx_chan), MBX_DAT0(tx_chan)); + } + + MBX_STS(rx_ack_chan) = 3; + + MBX_DAT0(tx_chan) = request_id; + MBX_DAT1(tx_chan) = param1; + MBX_DAT2(tx_chan) = param2; + MBX_DAT3(tx_chan) = (unsigned int )ptr; + MBX_CTRL(tx_chan) = 0x80000000 | IPC_MSG_TYPE_SYNC; + + timeout = get_timestamp() + 32768; + while(!MBX_STS(rx_ack_chan)) { + if (get_timestamp() > timeout) { + pr_error(LOG_MODULE_MAIN, "Timeout waiting ack %p", &ret); + break; + } + } + ret = MBX_DAT0(rx_ack_chan); + MBX_DAT0(rx_ack_chan) = 0; + MBX_STS(rx_ack_chan) = 3; + pr_debug(LOG_MODULE_MAIN, "ipc_request_sync returns: [%d] %p", rx_ack_chan, ret); + return ret; +} diff --git a/system/libarc32_edu/framework/src/infra/ipc_callback.c b/system/libarc32_edu/framework/src/infra/ipc_callback.c new file mode 100644 index 00000000..2f6b204b --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/ipc_callback.c @@ -0,0 +1,53 @@ +/** INTEL CONFIDENTIAL Copyright 2014 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel’s prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ +/* For the framework IPC message handler*/ +#include "cfw/cfw_internal.h" + +#include "infra/port.h" + +int ipc_sync_callback(int cpu_id, int request, int param1, int param2, + void *ptr) +{ + int ret = 0; + + switch (request) { + case IPC_REQUEST_ALLOC_PORT: + { + uint16_t port_id = port_alloc(NULL); + port_set_cpu_id(port_id, cpu_id); + ret = port_id; + break; + } + case IPC_MSG_TYPE_FREE: + message_free(ptr); + break; + default: + { + /* This is a framework message */ + ret = handle_ipc_sync_request(cpu_id, request, param1, param2, + ptr); + } + } + return ret; +} diff --git a/system/libarc32_edu/framework/src/infra/port.c b/system/libarc32_edu/framework/src/infra/port.c new file mode 100644 index 00000000..a96b271d --- /dev/null +++ b/system/libarc32_edu/framework/src/infra/port.c @@ -0,0 +1,250 @@ +/* + * INTEL CONFIDENTIAL Copyright 2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + */ +#include "os/os.h" +#include "util/list.h" +#include "infra/port.h" +#include "infra/log.h" +#include "infra/ipc.h" + +//#define PORT_DEBUG + +/** + * Internal definition of a port structure. + * External usage is done with a port id integer. + */ +struct port { + int id; + int cpu_id; + void *handle_param; + void * queue; + void (*handle_message)(struct message *msg, void *param); +}; + +static int this_cpu_id = 0; + +/* required by port_alloc() and other cfw APIs */ +int get_cpu_id(void) +{ + return this_cpu_id; +} + +#ifdef INFRA_IS_MASTER +static struct port ports[MAX_PORTS]; +static int registered_port_count = 0; +#else +static struct port * ports = NULL; +void port_set_ports_table(void * ptbl) +{ + ports = (struct port *) ptbl; +} + +void * port_alloc_port_table(int numports) +{ + return ports = balloc(numports*sizeof(struct port), NULL); +} +#endif + +void * port_get_port_table() +{ + return (void *)&ports[0]; +} + +static struct port * get_port(int port_id) +{ + return &ports[port_id - 1]; +} + +void port_set_queue(uint16_t port_id, void * queue) +{ + struct port * p = get_port(port_id); + p->queue = queue; +} + +#ifdef INFRA_IS_MASTER +uint16_t port_alloc(void * queue) +{ + struct port * ret = NULL; + uint32_t flags = interrupt_lock(); + if (registered_port_count < MAX_PORTS) { + ports[registered_port_count].id = registered_port_count + 1; /* don't use 0 as port.*/ + ports[registered_port_count].cpu_id = get_cpu_id(); /* is overwritten in case of ipc */ + ports[registered_port_count].queue = queue; +#ifdef PORT_DEBUG + pr_info("%s: port: %p id: %d queue: %p\n", __func__, + &ports[registered_port_count], registered_port_count, queue); +#endif + ret = &ports[registered_port_count]; + registered_port_count++; + } + interrupt_unlock(flags); + return ret->id; +} +#else +uint16_t port_alloc(void *queue) +{ + struct port * port = NULL; + int ret = ipc_request_sync_int(IPC_REQUEST_ALLOC_PORT, 0, 0, NULL); + port = get_port((unsigned int)ret); +#ifndef HAS_SHARED_MEM + port->id = ret; +#endif + if (port != NULL) { + port->queue = queue; + } + return port->id; +} +#endif +void port_set_handler(uint16_t port_id, void (*handler)(struct message*, void*), void *param) +{ + struct port * port = get_port(port_id); + port->handle_message = handler; + port->handle_param = param; +} + +struct message * message_alloc(int size, OS_ERR_TYPE * err) +{ + return (struct message *) balloc(size, err); +} + +void port_process_message(struct message * msg) +{ + struct port * p = get_port(msg->dst_port_id); + if (p->handle_message != NULL) { + p->handle_message(msg, p->handle_param); + } +} + +void port_set_cpu_id(uint16_t port_id, uint16_t cpu_id) +{ + struct port * p = get_port(port_id); + p->cpu_id = cpu_id; +} + +void port_set_port_id(uint16_t port_id) +{ + struct port * p = get_port(port_id); + p->id = port_id; +} + +uint16_t port_get_cpu_id(uint16_t port_id) +{ + struct port * p = get_port(port_id); + return p->cpu_id; +} + +#ifdef INFRA_MULTI_CPU_SUPPORT +#include "platform.h" + +typedef int (*send_msg_t)(struct message * m); + +struct ipc_handler { + send_msg_t send_message; + void (*free)(void * ptr); +}; + +struct ipc_handler ipc_handler[NUM_CPU]; + +void set_cpu_id(int cpu_id) +{ + this_cpu_id = cpu_id; +} + +send_msg_t get_ipc_handler(int cpu_id) { + return ipc_handler[cpu_id].send_message; +} + +void set_cpu_message_sender(int cpu_id, send_msg_t handler) { + ipc_handler[cpu_id].send_message = handler; +} + +void set_cpu_free_handler(int cpu_id, void (*free_handler)(void *)) { + ipc_handler[cpu_id].free = free_handler; +} + +int port_send_message(struct message * message) +{ + OS_ERR_TYPE err = 0; + struct port * port = get_port(MESSAGE_DST(message)); + if (port == NULL) { + pr_error(LOG_MODULE_MAIN, "Invalid destination port (%d)\n", MESSAGE_DST(message)); + return E_OS_ERR; + } + if (port->cpu_id == get_cpu_id()) { +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "Sending message %p to port %p(q:%p) ret: %d\n", message, port, port->queue, err); +#endif + queue_send_message(port->queue, message, &err); + return err; + } else { +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "Remote port ! using: %p handler\n", ipc_handler[port->cpu_id].send_message); +#endif + return ipc_handler[port->cpu_id].send_message(message); + } +} + +void message_free(struct message * msg) +{ + struct port * port = get_port(MESSAGE_SRC(msg)); +#ifdef PORT_DEBUG + pr_info(LOG_MODULE_MAIN, "free message %p: port %p[%d] this %d id %d\n", + msg, port, port->cpu_id, get_cpu_id(), MESSAGE_SRC(msg)); +#endif + if (port->cpu_id == get_cpu_id()) { + bfree(msg); + } else { + ipc_handler[port->cpu_id].free(msg); + } +} + +#else /* Single CPU support */ + +int port_send_message(struct message * msg) +{ + struct port * port = get_port(MESSAGE_DST(msg)); + OS_ERR_TYPE err; + queue_send_message(port->queue, msg, &err); + return err; +} + +void message_free(struct message * msg) +{ + bfree(msg); +} +#endif + +uint16_t queue_process_message(T_QUEUE queue) +{ + T_QUEUE_MESSAGE m; + OS_ERR_TYPE err; + struct message * message; + uint16_t id = 0; + queue_get_message(queue, &m, OS_NO_WAIT, &err); + message = (struct message *) m; + if ( message != NULL && err == E_OS_OK) { + id = MESSAGE_ID(message); + port_process_message(message); + } + return id; +} diff --git a/system/libarc32_edu/framework/src/os/os.c b/system/libarc32_edu/framework/src/os/os.c new file mode 100644 index 00000000..a76a61da --- /dev/null +++ b/system/libarc32_edu/framework/src/os/os.c @@ -0,0 +1,94 @@ +#include "cfw/cfw.h" +#include "os/os.h" + +#ifdef TRACK_ALLOCS +int alloc_count = 0; +#endif + +void * cfw_alloc(int size, OS_ERR_TYPE * err) { + void * ptr; + unsigned int flags = interrupt_lock(); + ptr = malloc(size+sizeof(void*)); + (*(int*) ptr) = size; +#ifdef TRACK_ALLOCS + alloc_count++; +#endif + interrupt_unlock(flags); + return ptr; +} + +void cfw_free(void * ptr, OS_ERR_TYPE * err) { + int flags = interrupt_lock(); +#ifdef TRACK_ALLOCS + alloc_count--; +#endif + free(ptr); + interrupt_unlock(flags); +} + +void * balloc(uint32_t size, OS_ERR_TYPE *err) { + return cfw_alloc(size, err); +} + +OS_ERR_TYPE bfree(void *ptr) { + cfw_free(ptr, NULL); + return E_OS_OK; +} + +typedef struct queue_ { + list_head_t lh; + int count; + int used; +} q_t; + +q_t q_pool[10]; + +void queue_put(void *queue, void *msg) { + q_t * q = (q_t*) queue; + list_add(&q->lh, (list_t *)msg); +#ifdef DEBUG_OS + cfw_log("queue_put: %p <- %p\n", queue, msg); +#endif +} + +void * queue_wait(void *queue) { + q_t * q = (q_t*) queue; + void * elem = (void *)list_get(&q->lh); +#ifdef DEBUG_OS + cfw_log("queue_wait: %p -> %p\n", queue, elem); +#endif + return elem; +} + +void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int timeout, OS_ERR_TYPE* err) { + *message = queue_wait(queue); +} + +void queue_send_message (T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err) { + queue_put(queue, message); +} + +T_QUEUE queue_create(uint32_t max_size, OS_ERR_TYPE*err) { + int i, found=0; + q_t * q; + for (i=0;i<10; i++) { + q = &q_pool[i]; + if (q->used == 0) { + q->used = 1; + found = 1; + } + } + if (!found) return (T_QUEUE)NULL; + list_init(&q->lh); + q->count = 0; + return (T_QUEUE) q; +} + +void queue_delete(T_QUEUE queue, OS_ERR_TYPE* err) { + void * element = NULL; + q_t * q = (q_t*) queue; + while((element = list_get(&q->lh)) != NULL) + list_remove(&q->lh, element); + cfw_free(q, NULL); +} + diff --git a/system/libarc32_edu/framework/src/services/gpio_service_api.c b/system/libarc32_edu/framework/src/services/gpio_service_api.c new file mode 100644 index 00000000..1bddd6af --- /dev/null +++ b/system/libarc32_edu/framework/src/services/gpio_service_api.c @@ -0,0 +1,81 @@ +/** INTEL CONFIDENTIAL Copyright 2014-2015 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code ("Material") are owned by Intel Corporation or its suppliers + * or licensors. + * Title to the Material remains with Intel Corporation or its suppliers and + * licensors. + * The Material contains trade secrets and proprietary and confidential information + * of Intel or its suppliers and licensors. The Material is protected by worldwide + * copyright and trade secret laws and treaty provisions. + * No part of the Material may be used, copied, reproduced, modified, published, + * uploaded, posted, transmitted, distributed, or disclosed in any way without + * Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. + * + * Any license under such intellectual property rights must be express and + * approved by Intel in writing + * + ******************************************************************************/ + +#include "services/gpio_service.h" +#include "cfw/cfw_client.h" + +/**************************************************************************************** + *********************** SERVICE API IMPLEMENATION ************************************** + ****************************************************************************************/ + +int gpio_configure(svc_client_handle_t * h, uint8_t index, uint8_t mode, void * priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_CONFIGURE_REQ, + sizeof(gpio_configure_req_msg_t), priv); + gpio_configure_req_msg_t * req = (gpio_configure_req_msg_t*) msg; + req->mode = mode; + req->index = index; + cfw_send_message(msg); + return 0; +} + +int gpio_set_state(svc_client_handle_t * h, uint8_t index, uint8_t val, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_SET_REQ, + sizeof(gpio_set_req_msg_t), priv); + gpio_set_req_msg_t * req = (gpio_set_req_msg_t*) msg; + req->state = val; + req->index = index; + cfw_send_message(msg); + return 0; +} + +int gpio_get_state(svc_client_handle_t * h, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_GET_REQ, sizeof(*msg), priv); + cfw_send_message(msg); + return 0; +} + +int gpio_listen(svc_client_handle_t * h, uint8_t pin, gpio_service_isr_mode_t mode, uint8_t debounce, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_LISTEN_REQ, + sizeof(gpio_listen_req_msg_t), priv); + gpio_listen_req_msg_t * req = (gpio_listen_req_msg_t*) msg; + req->index = pin; + req->mode = mode; + req->debounce = debounce; + cfw_send_message(msg); + return 0; +} + +int gpio_unlisten(svc_client_handle_t * h, uint8_t pin, void *priv) +{ + struct cfw_message * msg = cfw_alloc_message_for_service(h, MSG_ID_GPIO_UNLISTEN_REQ, + sizeof(gpio_unlisten_req_msg_t), priv); + gpio_unlisten_req_msg_t * req = (gpio_unlisten_req_msg_t*) msg; + req->index = pin; + cfw_send_message(msg); + return 0; +} diff --git a/system/libarc32_edu/framework/src/util/list.c b/system/libarc32_edu/framework/src/util/list.c new file mode 100644 index 00000000..55a731a5 --- /dev/null +++ b/system/libarc32_edu/framework/src/util/list.c @@ -0,0 +1,116 @@ +#include "os/os.h" +#include "util/list.h" + + +void list_init(list_head_t * list) { + list->head = list->tail = NULL; +} + +void list_add_head(list_head_t * list, list_t * element) { + if (list->head == NULL) { + list->head = list->tail = element; + } else { + element->next = list->head; + list->head = element; + } + element->next = NULL; +} + + +void list_add(list_head_t * list, list_t * element) { + uint32_t saved = interrupt_lock(); + if (list->head == NULL) { + list->head = list->tail = element; + } else { + list->tail->next = element; + list->tail = element; + } + element->next = NULL; + interrupt_unlock(saved); +} + +void list_remove(list_head_t *list, list_t * element) { + list_t * l = list->head; + /* remove first? */ + if (element == l) { + list->head = l->next; + if (list->head == NULL) { + list->tail = NULL; + } + } else { + list_t * prev = l; + while (l) { + if (l == element) { + prev->next = l->next; + if (list->tail == l) { + list->tail = prev; + } + } + prev = l; + l = l->next; + } + } +} + +void list_foreach(list_head_t * lh, void(*cb)(void *, void *), void * param) { + list_t * l = lh->head; + while(l) { + cb(l, param); + l = l->next; + } +} + +void list_foreach_del(list_head_t * lh, int(*cb)(void *, void *), void * param) { + list_t * l = lh->head; + list_t * prev = lh->head; + while(l) { + list_t * tmp; + tmp = l->next; + if (cb(l, param)) { + if (l == lh->head) { + lh->head = tmp; + prev = tmp; + if (lh->tail == l) { + lh->tail = NULL; + } + l = tmp; + } else { + prev->next = tmp; + if (lh->tail == l) { + lh->tail = prev; + } + l = prev->next; + } + } else { + prev = l; + l = l->next; + } + } +} + +list_t * list_get(list_head_t *lh) { + uint32_t saved = interrupt_lock(); + list_t * l = lh->head; + if (l != NULL) { + lh->head = l->next; + } + interrupt_unlock(saved); + return l; +} + +int list_empty(list_head_t *lh) { + return (lh->head == NULL); +} + +list_t * list_find_first(list_head_t * lh, bool(*cb)(list_t*,void*), void *data) +{ + list_t *result = NULL; + list_t *item = lh->head; + while (item && !result) { + if (cb(item,data)) { + result = item; + } + item = item->next; + } + return result; +} From 82c3529091c7c2d1f13d1d55c3d31cdf0aa56840 Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Fri, 15 May 2015 16:25:07 +0100 Subject: [PATCH 10/15] ATLEDGE-82 init code for arc Eliminating compiler warnings and cleanup. Signed-off-by: Dan O'Donovan Signed-off-by: David Hunt Conflicts: system/libarc32_edu/drivers/ns16550.c system/libarc32_edu/drivers/uart.h system/libarc32_edu/main.c --- system/libarc32_edu/Makefile | 2 +- system/libarc32_edu/common/board.h | 221 +++++++ system/libarc32_edu/common/misc/util.h | 102 ++++ system/libarc32_edu/drivers/arcv2_timer0.c | 12 +- system/libarc32_edu/drivers/arcv2_timer0.h | 72 +++ system/libarc32_edu/drivers/ns16550.c | 542 ++++++++++++++++++ system/libarc32_edu/drivers/uart.h | 76 +++ system/libarc32_edu/framework/include/os/os.h | 39 -- .../framework/include/os/os_types.h | 1 - system/libarc32_edu/main.c | 147 ++++- 10 files changed, 1158 insertions(+), 56 deletions(-) create mode 100644 system/libarc32_edu/common/board.h create mode 100644 system/libarc32_edu/common/misc/util.h create mode 100644 system/libarc32_edu/drivers/arcv2_timer0.h create mode 100644 system/libarc32_edu/drivers/ns16550.c create mode 100644 system/libarc32_edu/drivers/uart.h diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index 21348969..25606592 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -33,7 +33,7 @@ TARGET_BIN=$(TARGET).bin HWFLAGS=-mARCv2EM -mav2em -mlittle-endian CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DHAS_SHARED_MEM -OPTFLAGS=-g -Os -Wall +OPTFLAGS=-g -Os -Wall -Werror INCLUDES=-Icommon -Ibootcode -Iframework/include EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) diff --git a/system/libarc32_edu/common/board.h b/system/libarc32_edu/common/board.h new file mode 100644 index 00000000..a2a0a2b7 --- /dev/null +++ b/system/libarc32_edu/common/board.h @@ -0,0 +1,221 @@ +/* board.h - board configuration macros for the atlas_peak-arc BSP */ + +/* + * Copyright (c) 2014-2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +This header file is used to specify and describe board-level aspects for the +'atlas_peak-arc' BSP. +*/ + +#ifndef _BOARD__H_ +#define _BOARD__H_ + +#include + +/* default system clock */ + +#define SYSCLK_DEFAULT_IOSC_HZ MHZ(32) + +/* address bases */ + +#define PERIPH_ADDR_BASE_ADC 0x80015000 /* ADC */ + +#define PERIPH_ADDR_BASE_CREG_MST0 0x80018000 /* CREG Master 0 */ +#define PERIPH_ADDR_BASE_CREG_SLV0 0x80018080 /* CREG Slave 0 */ +#define PERIPH_ADDR_BASE_CREG_SLV1 0x80018180 /* CREG Slave 1 */ + +#define PERIPH_ADDR_BASE_GPIO0 0x80017800 /* GPIO 0 */ +#define PERIPH_ADDR_BASE_GPIO1 0x80017900 /* GPIO 1 */ + +#define PERIPH_ADDR_BASE_I2C_MST0 0x80012000 /* I2C Master 0 */ +#define PERIPH_ADDR_BASE_I2C_MST1 0x80012100 /* I2C Master 1 */ + +#define PERIPH_ADDR_BASE_SPI_MST0 0x80010000 /* SPI Master 0 */ +#define PERIPH_ADDR_BASE_SPI_MST1 0x80010100 /* SPI Master 1 */ + +#ifdef CONFIG_NSIM +#define PERIPH_ADDR_BASE_UART0 0x4242 /* UART A */ +#else +#define PERIPH_ADDR_BASE_UART0 0xB0002000 /* UART A */ +#define PERIPH_ADDR_BASE_UART1 0xB0002400 /* UART B */ +#endif + +/* IRQs */ + +#define IRQ_TIMER0 16 +#define IRQ_TIMER1 17 +#define IRQ_I2C0_RX_AVAIL 18 +#define IRQ_I2C0_TX_REQ 19 +#define IRQ_I2C0_STOP_DET 20 +#define IRQ_I2C0_ERR 21 +#define IRQ_I2C1_RX_AVAIL 22 +#define IRQ_I2C1_TX_REQ 23 +#define IRQ_I2C1_STOP_DET 24 +#define IRQ_I2C1_ERR 25 +#define IRQ_SPI0_ERR_INT 26 +#define IRQ_SPI0_RX_AVAIL 27 +#define IRQ_SPI0_TX_REQ 28 +#define IRQ_SPI1_ERR_INT 29 +#define IRQ_SPI1_RX_AVAIL 30 +#define IRQ_SPI1_TX_REQ 31 +#define IRQ_ADC_IRQ 32 +#define IRQ_ADC_ERR 33 +#define IRQ_GPIO0_INTR 34 +#define IRQ_GPIO1_INTR 35 +#define IRQ_I2C_MST0_INTR 36 +#define IRQ_I2C_MST1_INTR 37 +#define IRQ_SPI_MST0_INTR 38 +#define IRQ_SPI_MST1_INTR 39 +#define IRQ_SPI_SLV_INTR 40 +#define IRQ_UART0_INTR 41 +#define IRQ_UART1_INTR 42 +#define IRQ_I2S_INTR 43 +#define IRQ_GPIO_INTR 44 +#define IRQ_PWM_TIMER_INTR 45 +#define IRQ_USB_INTR 46 +#define IRQ_RTC_INTR 47 +#define IRQ_WDOG_INTR 48 +#define IRQ_DMA_CHAN0 49 +#define IRQ_DMA_CHAN1 50 +#define IRQ_DMA_CHAN2 51 +#define IRQ_DMA_CHAN3 52 +#define IRQ_DMA_CHAN4 53 +#define IRQ_DMA_CHAN5 54 +#define IRQ_DMA_CHAN6 55 +#define IRQ_DMA_CHAN7 56 +#define IRQ_MAILBOXES_INTR 57 +#define IRQ_COMPARATORS_INTR 58 +#define IRQ_SYS_PMU_INTR 59 +#define IRQ_DMA_CHANS_ERR 60 +#define IRQ_INT_SRAM_CTLR 61 +#define IRQ_INT_FLASH0_CTLR 62 +#define IRQ_INT_FLASH1_CTLR 63 +#define IRQ_ALWAYS_ON_TMR 64 +#define IRQ_ADC_PWR 65 +#define IRQ_ADC_CALIB 66 +#define IRQ_ALWAYS_ON_GPIO 67 + +#ifndef _ASMLANGUAGE + +#define EXC_FROM_IRQ(irq) ((irq) + 16) +#define VECTOR_FROM_IRQ(irq) EXC_FROM_IRQ(irq) +#define VECTOR_ADDR(vector) ((uint32_t *)((int)vector << 2)) + +#include +//#include + +/* ARCv2 timer 0 configuration settings for the system clock */ +#ifdef CONFIG_NANOKERNEL +#define CONFIG_ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock \ + */ +#define CONFIG_ARCV2_TIMER1_CLOCK_FREQ CONFIG_ARCV2_TIMER0_CLOCK_FREQ +#endif /* CONFIG_NANOKERNEL */ + +#define CONFIG_ARCV2_TIMER0_INT_LVL IRQ_TIMER0 +#define CONFIG_ARCV2_TIMER0_INT_PRI 0 + +#define CONFIG_ARCV2_TIMER1_INT_LVL IRQ_TIMER1 +#define CONFIG_ARCV2_TIMER1_INT_PRI 1 + +/* + * UART configuration settings + * + * This BSP only supports the nanokernel. Therefore: + * - only polled mode is supported (interrupt-driven mode is NOT supported); and + * - only the target console is supported (hostserver driver is NOT supported). + */ +#define UART_POLL_OUT uart_poll_out +#define CONFIG_UART_NUM_SYSTEM_PORTS 1 +#define CONFIG_UART_NUM_EXTRA_PORTS 0 +#define CONFIG_UART_NUM_PORTS \ + (CONFIG_UART_NUM_SYSTEM_PORTS + CONFIG_UART_NUM_EXTRA_PORTS) + +#define CONFIG_UART_CONSOLE_INDEX 0 +#define CONFIG_UART_CONSOLE_CLK_FREQ SYSCLK_DEFAULT_IOSC_HZ +#define CONFIG_UART_CONSOLE_BAUDRATE 115200 +#define CONFIG_UART_CONSOLE_REGS PERIPH_ADDR_BASE_UART0 +#define CONFIG_UART_CONSOLE_IRQ IRQ_UART0_INTR +#define CONFIG_UART_CONSOLE_INT_PRI 0 + +#define UART_REG_ADDR_INTERVAL 4 /* for ns16550 driver */ + +/* + * Device drivers utilize the macros PLB_BYTE_REG_WRITE() and + * PLB_BYTE_REG_READ() to access byte-wide registers on the processor + * local bus (PLB), as opposed to a PCI bus, for example. Boards are + * expected to provide implementations of these macros. + */ + +#define PLB_BYTE_REG_WRITE(data, address) outByte(data, (unsigned int)address) +#define PLB_BYTE_REG_READ(address) inByte((unsigned int)address) + +/******************************************************************************* +* +* outByte - output byte to memory location +* +* RETURNS: N/A +* +* NOMANUAL +*/ + +static inline void outByte(uint8_t data, uint32_t addr) +{ + *(volatile uint8_t *)addr = data; +} + +/******************************************************************************* +* +* inByte - obtain byte value from memory location +* +* This function issues the 'move' instruction to read a byte from the specified +* memory address. +* +* RETURNS: the byte read from the specified memory address +* +* NOMANUAL +*/ + +static inline uint8_t inByte(uint32_t addr) +{ + return *((volatile uint8_t *)addr); +} + +/* + * Device drivers utilize the macros PLB_WORD_REG_WRITE() and + * PLB_WORD_REG_READ() to access shortword-wide registers on the processor + * local bus (PLB), as opposed to a PCI bus, for example. Boards are + * expected to provide implementations of these macros. + */ + +#endif /* !_ASMLANGUAGE */ + +#endif /* _BOARD__H_ */ diff --git a/system/libarc32_edu/common/misc/util.h b/system/libarc32_edu/common/misc/util.h new file mode 100644 index 00000000..838b770f --- /dev/null +++ b/system/libarc32_edu/common/misc/util.h @@ -0,0 +1,102 @@ +/* util.h - misc utilities */ + +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +Misc utilities usable by nanokernel, microkernel, and application code. +*/ + +#ifndef _UTIL__H_ +#define _UTIL__H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ASMLANGUAGE + +/* round "x" up/down to next multiple of "align" (which must be a power of 2) */ +#define ROUND_UP(x, align) \ + (((unsigned long)(x) + ((unsigned long)align - 1)) & \ + ~((unsigned long)align - 1)) +#define ROUND_DOWN(x, align) ((unsigned long)(x) & ~((unsigned long)align - 1)) + +#ifdef INLINED +#define INLINE inline +#else +#define INLINE +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#ifndef BOOL +#define BOOL int +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +static inline int _IsPowerOfTwo(unsigned int x) +{ + return (x != 0) && !(x & (x - 1)); +} +#endif /* !_ASMLANGUAGE */ + +/* KB, MB, GB */ +#define KB(x) ((x) << 10) +#define MB(x) (KB(x) << 10) +#define GB(x) (MB(x) << 10) + +/* KHZ, MHZ */ +#define KHZ(x) ((x) * 1000) +#define MHZ(x) (KHZ(x) * 1000) + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL__H_ */ diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c index c412fa5c..0acbe875 100644 --- a/system/libarc32_edu/drivers/arcv2_timer0.c +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -32,8 +32,15 @@ conjunction with a microkernel. #include "conf.h" #include "interrupt.h" +#define ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock */ + /* defines */ +#define ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ +#define ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ +#define ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ +#define ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ + #define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000 @@ -127,8 +134,9 @@ void _arcv2_timer0_int_handler(void) /* Increment number of Timer0 overflows */ timer0_overflows++; /* Increment number of Timer0 overflows used to compute micros() */ - timer0_overflows_us = - (timer0_overflows_us <= MAX_OVERFLOWS_US) ? timer0_overflows_us++ : 0; + timer0_overflows_us++; + if (timer0_overflows_us > MAX_OVERFLOWS_US) + timer0_overflows_us = 0; } /******************************************************************************* diff --git a/system/libarc32_edu/drivers/arcv2_timer0.h b/system/libarc32_edu/drivers/arcv2_timer0.h new file mode 100644 index 00000000..187f3a77 --- /dev/null +++ b/system/libarc32_edu/drivers/arcv2_timer0.h @@ -0,0 +1,72 @@ +/* arcv2_timer0.h - ARC timer 0 device driver */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * The right to copy, distribute, modify or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Wind River license agreement. + */ + +#ifndef _ARCV2_TIMER0__H_ +#define _ARCV2_TIMER0__H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Increments every Timer0 overflow. + * Timer0 is configured to overflow and fire an IRQ every 1 millisecond + */ +extern uint32_t volatile timer0_overflows; + +/* It is incremented every ms by Timer0 IRQ handler but it overflows at + * MAX_OVERFLOWS_US */ +extern uint32_t volatile timer0_overflows_us; + +/******************************************************************************* +* +* timer0_driver_init - initialize and enable the system clock +* +* This routine is used to program the ARCv2 timer to deliver interrupts at the +* 1 millisecond rate specified via the ONE_MILLISECOND macro. +* +* RETURNS: N/A +*/ +void timer0_driver_init(void); + + +/******************************************************************************* +* +* arcv2_timer0_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +uint32_t arcv2_timer0_count_get(void); + + +/******************************************************************************* +* +* arcv2_timer0_control_get - get the value of CONTROL0 aux register +* +* This routine gets the value from the timer's control register. +* +* RETURNS: the value of CONTROL0 auxiliary register. +* +* \NOMANUAL +*/ +uint32_t arcv2_timer0_control_get(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ARCV2_TIMER0__H_ */ diff --git a/system/libarc32_edu/drivers/ns16550.c b/system/libarc32_edu/drivers/ns16550.c new file mode 100644 index 00000000..4dc23bcd --- /dev/null +++ b/system/libarc32_edu/drivers/ns16550.c @@ -0,0 +1,542 @@ +/* ns16550.c - NS16550D serial driver */ + +/* + * Copyright (c) 2010, 2012-2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +DESCRIPTION +This is the driver for the Intel NS16550 UART Chip used on the PC 386. +It uses the SCCs in asynchronous mode only. + + +USAGE +An ns16550 structure is used to describe the chip. +The BSP's _InitHardware() routine initializes all the +values in the uart_init_info structure before calling uart_init(). + +A board support package's board.h header must provide definitions for: + +- the following register access routines: + + unsigned int inByte(unsigned int address); + void outByte(unsigned char data, unsigned int address); + +- and the following macro for the number of bytes between register addresses: + + UART_REG_ADDR_INTERVAL + + +INCLUDE FILES: drivers/uart.h +*/ + +/* includes */ + +#include + +#include "board.h" +#include "interrupt.h" +#include "uart.h" + +/* defines */ + +#define UART_REG_ADDR_INTERVAL 4 + +/* register definitions */ + +#define REG_THR 0x00 /* Transmitter holding reg. */ +#define REG_RDR 0x00 /* Receiver data reg. */ +#define REG_BRDL 0x00 /* Baud rate divisor (LSB) */ +#define REG_BRDH 0x01 /* Baud rate divisor (MSB) */ +#define REG_IER 0x01 /* Interrupt enable reg. */ +#define REG_IIR 0x02 /* Interrupt ID reg. */ +#define REG_FCR 0x02 /* FIFO control reg. */ +#define REG_LCR 0x03 /* Line control reg. */ +#define REG_MDC 0x04 /* Modem control reg. */ +#define REG_LSR 0x05 /* Line status reg. */ +#define REG_MSR 0x06 /* Modem status reg. */ + +/* equates for interrupt enable register */ + +#define IER_RXRDY 0x01 /* receiver data ready */ +#define IER_TBE 0x02 /* transmit bit enable */ +#define IER_LSR 0x04 /* line status interrupts */ +#define IER_MSI 0x08 /* modem status interrupts */ + +/* equates for interrupt identification register */ + +#define IIR_IP 0x01 /* interrupt pending bit */ +#define IIR_MASK 0x07 /* interrupt id bits mask */ +#define IIR_MSTAT 0x00 /* modem status interrupt */ +#define IIR_THRE 0X02 /* transmit holding register empty */ +#define IIR_RBRF 0x04 /* receiver buffer register full */ +#define IIR_ID 0x06 /* interupt ID mask without IP */ +#define IIR_SEOB 0x06 /* serialization error or break */ + +/* equates for FIFO control register */ + +#define FCR_FIFO 0x01 /* enable XMIT and RCVR FIFO */ +#define FCR_RCVRCLR 0x02 /* clear RCVR FIFO */ +#define FCR_XMITCLR 0x04 /* clear XMIT FIFO */ + +/* + * Per PC16550D (Literature Number: SNLS378B): + * + * RXRDY, Mode 0: When in the 16450 Mode (FCR0 = 0) or in + * the FIFO Mode (FCR0 = 1, FCR3 = 0) and there is at least 1 + * character in the RCVR FIFO or RCVR holding register, the + * RXRDY pin (29) will be low active. Once it is activated the + * RXRDY pin will go inactive when there are no more charac- + * ters in the FIFO or holding register. + * + * RXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when the + * FCR3 = 1 and the trigger level or the timeout has been + * reached, the RXRDY pin will go low active. Once it is acti- + * vated it will go inactive when there are no more characters + * in the FIFO or holding register. + * + * TXRDY, Mode 0: In the 16450 Mode (FCR0 = 0) or in the + * FIFO Mode (FCR0 = 1, FCR3 = 0) and there are no charac- + * ters in the XMIT FIFO or XMIT holding register, the TXRDY + * pin (24) will be low active. Once it is activated the TXRDY + * pin will go inactive after the first character is loaded into the + * XMIT FIFO or holding register. + * + * TXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when + * FCR3 = 1 and there are no characters in the XMIT FIFO, the + * TXRDY pin will go low active. This pin will become inactive + * when the XMIT FIFO is completely full. + */ +#define FCR_MODE0 0x00 /* set receiver in mode 0 */ +#define FCR_MODE1 0x08 /* set receiver in mode 1 */ + +/* RCVR FIFO interrupt levels: trigger interrupt with this bytes in FIFO */ +#define FCR_FIFO_1 0x00 /* 1 byte in RCVR FIFO */ +#define FCR_FIFO_4 0x40 /* 4 bytes in RCVR FIFO */ +#define FCR_FIFO_8 0x80 /* 8 bytes in RCVR FIFO */ +#define FCR_FIFO_14 0xC0 /* 14 bytes in RCVR FIFO */ + +/* constants for line control register */ + +#define LCR_CS5 0x00 /* 5 bits data size */ +#define LCR_CS6 0x01 /* 6 bits data size */ +#define LCR_CS7 0x02 /* 7 bits data size */ +#define LCR_CS8 0x03 /* 8 bits data size */ +#define LCR_2_STB 0x04 /* 2 stop bits */ +#define LCR_1_STB 0x00 /* 1 stop bit */ +#define LCR_PEN 0x08 /* parity enable */ +#define LCR_PDIS 0x00 /* parity disable */ +#define LCR_EPS 0x10 /* even parity select */ +#define LCR_SP 0x20 /* stick parity select */ +#define LCR_SBRK 0x40 /* break control bit */ +#define LCR_DLAB 0x80 /* divisor latch access enable */ + +/* constants for the modem control register */ + +#define MCR_DTR 0x01 /* dtr output */ +#define MCR_RTS 0x02 /* rts output */ +#define MCR_OUT1 0x04 /* output #1 */ +#define MCR_OUT2 0x08 /* output #2 */ +#define MCR_LOOP 0x10 /* loop back */ + +/* constants for line status register */ + +#define LSR_RXRDY 0x01 /* receiver data available */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_PE 0x04 /* parity error */ +#define LSR_FE 0x08 /* framing error */ +#define LSR_BI 0x10 /* break interrupt */ +#define LSR_THRE 0x20 /* transmit holding register empty */ +#define LSR_TEMT 0x40 /* transmitter empty */ + +/* constants for modem status register */ + +#define MSR_DCTS 0x01 /* cts change */ +#define MSR_DDSR 0x02 /* dsr change */ +#define MSR_DRI 0x04 /* ring change */ +#define MSR_DDCD 0x08 /* data carrier change */ +#define MSR_CTS 0x10 /* complement of cts */ +#define MSR_DSR 0x20 /* complement of dsr */ +#define MSR_RI 0x40 /* complement of ring signal */ +#define MSR_DCD 0x80 /* complement of dcd */ + +/* convenience defines */ + +#define THR(n) (uart[n].port + REG_THR * UART_REG_ADDR_INTERVAL) +#define RDR(n) (uart[n].port + REG_RDR * UART_REG_ADDR_INTERVAL) +#define BRDL(n) (uart[n].port + REG_BRDL * UART_REG_ADDR_INTERVAL) +#define BRDH(n) (uart[n].port + REG_BRDH * UART_REG_ADDR_INTERVAL) +#define IER(n) (uart[n].port + REG_IER * UART_REG_ADDR_INTERVAL) +#define IIR(n) (uart[n].port + REG_IIR * UART_REG_ADDR_INTERVAL) +#define FCR(n) (uart[n].port + REG_FCR * UART_REG_ADDR_INTERVAL) +#define LCR(n) (uart[n].port + REG_LCR * UART_REG_ADDR_INTERVAL) +#define MDC(n) (uart[n].port + REG_MDC * UART_REG_ADDR_INTERVAL) +#define LSR(n) (uart[n].port + REG_LSR * UART_REG_ADDR_INTERVAL) +#define MSR(n) (uart[n].port + REG_MSR * UART_REG_ADDR_INTERVAL) + +#define IIRC(n) uart[n].iirCache + +#define INBYTE(x) inByte(x) +#define OUTBYTE(x, d) outByte(d, x) + +#if defined(VXMICRO_ARCH_Intel) +#define INT_CONNECT(which, isr, arg, stub) \ + irq_connect((unsigned int)uart[which].irq, \ + (unsigned int)uart[which].intPri, \ + isr, \ + arg, \ + stub) +#else +#define INT_CONNECT(which, isr, arg, stub) \ + do { \ + ARG_UNUSED(stub); \ + irq_connect((unsigned int)uart[which].irq, \ + (unsigned int)uart[which].intPri, \ + isr, \ + arg); \ + } while (0) +#endif /* VXMICRO_ARCH_Intel */ + +/* typedefs */ + +struct ns16550 { + uint32_t port; /* base port number or MM base address */ + uint8_t irq; /* interrupt request level */ + uint8_t intPri; /* interrupt priority */ + uint8_t iirCache; /* cache of IIR since it clears when read */ +}; + +/* locals */ + +//static struct ns16550 __noinit uart[CONFIG_UART_NUM_SYSTEM_PORTS]; +static struct ns16550 uart[CONFIG_UART_NUM_SYSTEM_PORTS]; + +/******************************************************************************* +* +* uart_init - initialize the chip +* +* This routine is called to reset the chip in a quiescent state. +* +* RETURNS: N/A +*/ + +void uart_init(int which, /* UART channel to initialize */ + const struct uart_init_info * const init_info + ) +{ + unsigned int oldLevel; /* old interrupt lock level */ + uint32_t divisor; /* baud rate divisor */ + + uart[which].port = init_info->regs; + uart[which].irq = init_info->irq; + uart[which].intPri = init_info->int_pri; + uart[which].iirCache = 0; + + oldLevel = interrupt_lock(); + + /* calculate baud rate divisor */ + divisor = (init_info->sys_clk_freq / init_info->baud_rate) >> 4; + + /* set the DLAB to access the baud rate divisor registers */ + OUTBYTE(LCR(which), LCR_DLAB); + OUTBYTE(BRDL(which), (unsigned char)(divisor & 0xff)); + OUTBYTE(BRDH(which), (unsigned char)((divisor >> 8) & 0xff)); + + /* 8 data bits, 1 stop bit, no parity, clear DLAB */ + OUTBYTE(LCR(which), LCR_CS8 | LCR_1_STB | LCR_PDIS); + + OUTBYTE(MDC(which), MCR_OUT2 | MCR_RTS | MCR_DTR); + + /* + * Program FIFO: enabled, mode 0 (set for compatibility with quark), + * generate the interrupt at 8th byte + * Clear TX and RX FIFO + */ + OUTBYTE(FCR(which), + FCR_FIFO | FCR_MODE0 | FCR_FIFO_8 | FCR_RCVRCLR | FCR_XMITCLR); + + /* clear the port */ + INBYTE(RDR(which)); + + /* disable interrupts */ + OUTBYTE(IER(which), 0x00); + + interrupt_unlock(oldLevel); +} + +/******************************************************************************* +* +* uart_poll_in - poll the device for input. +* +* RETURNS: 0 if a character arrived, -1 if the input buffer if empty. +*/ + +int uart_poll_in(int which, /* UART channel to select for input */ + unsigned char *pChar /* pointer to char */ + ) +{ + if ((INBYTE(LSR(which)) & LSR_RXRDY) == 0x00) + return (-1); + + /* got a character */ + *pChar = INBYTE(RDR(which)); + + return 0; +} + +/******************************************************************************* +* +* uart_poll_out - output a character in polled mode. +* +* Checks if the transmitter is empty. If empty, a character is written to +* the data register. +* +* If the hardware flow control is enabled then the handshake signal CTS has to +* be asserted in order to send a character. +* +* RETURNS: sent character +*/ +unsigned char uart_poll_out( + int which, /* UART channel to select for output */ + unsigned char outChar /* char to send */ + ) +{ + /* wait for transmitter to ready to accept a character */ + while ((INBYTE(LSR(which)) & LSR_TEMT) == 0) + ; + + OUTBYTE(THR(which), outChar); + + return outChar; +} + +#if CONFIG_UART_INTERRUPT_DRIVEN +/******************************************************************************* +* +* uart_fifo_fill - fill FIFO with data +* +* RETURNS: number of bytes sent +*/ + +int uart_fifo_fill(int which, /* UART on which to send */ + const uint8_t *txData, /* data to transmit */ + int size /* number of bytes to send */ + ) +{ + int i; + + for (i = 0; i < size && (INBYTE(LSR(which)) & LSR_THRE) != 0; i++) { + OUTBYTE(THR(which), txData[i]); + } + return i; +} + +/******************************************************************************* +* +* uart_fifo_read - read data from FIFO +* +* RETURNS: number of bytes read +*/ + +int uart_fifo_read(int which, /* UART to receive from */ + uint8_t *rxData, /* data container */ + const int size /* container size */ + ) +{ + int i; + + for (i = 0; i < size && (INBYTE(LSR(which)) & LSR_RXRDY) != 0; i++) { + rxData[i] = INBYTE(RDR(which)); + } + + return i; +} + +/******************************************************************************* +* +* uart_irq_tx_enable - enable TX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_tx_enable(int which /* UART to enable Tx + interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_TBE); +} + +/******************************************************************************* +* +* uart_irq_tx_disable - disable TX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_tx_disable(int which /* UART to disable Tx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_TBE)); +} + +/******************************************************************************* +* +* uart_irq_tx_ready - check if Tx IRQ has been raised +* +* RETURNS: N/A +*/ + +int uart_irq_tx_ready(int which /* UART to check */ + ) +{ + return ((IIRC(which) & IIR_ID) == IIR_THRE); +} + +/******************************************************************************* +* +* _uart_irq_rx_enable - enable RX interrupt in IER + +* RETURNS: N/A +*/ + +void uart_irq_rx_enable(int which /* UART to enable Rx + interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_RXRDY); +} + +/******************************************************************************* +* +* uart_irq_rx_disable - disable RX interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_rx_disable(int which /* UART to disable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_RXRDY)); +} + +/******************************************************************************* +* +* uart_irq_rx_ready - check if Rx IRQ has been raised +* +* RETURNS: 1 if an IRQ is ready, 0 otherwise +*/ + +int uart_irq_rx_ready(int which /* UART to check */ + ) +{ + return ((IIRC(which) & IIR_ID) == IIR_RBRF); +} + +/******************************************************************************* +* +* uart_irq_err_enable - enable error interrupt in IER +* +* RETURNS: N/A +*/ + +void uart_irq_err_enable(int which /* UART to enable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) | IER_LSR); +} + +/******************************************************************************* +* +* uart_irq_err_disable - disable error interrupt in IER +* +* RETURNS: 1 if an IRQ is ready, 0 otherwise +*/ + +void uart_irq_err_disable(int which /* UART to disable Rx interrupt */ + ) +{ + OUTBYTE(IER(which), INBYTE(IER(which)) & (~IER_LSR)); +} + +/******************************************************************************* +* +* uart_irq_is_pending - check if any IRQ is pending +* +* RETURNS: 1 if an IRQ is pending, 0 otherwise +*/ + +int uart_irq_is_pending(int which /* UART to check */ + ) +{ + return (!(IIRC(which) & IIR_IP)); +} + +/******************************************************************************* +* +* uart_irq_update - update cached contents of IIR +* +* RETURNS: always 1 +*/ + +int uart_irq_update(int which /* UART to update */ + ) +{ + IIRC(which) = INBYTE(IIR(which)); + + return 1; +} + +/******************************************************************************* +* +* uart_int_connect - connect an ISR to an interrupt line +* +* The kernel configuration allows to setup an interrupt line for a particular +* DUART. This routine installs the ISR of a UART user to the interrupt line +* chosen for the hardware at configuration time. +* +* RETURNS: N/A +*/ + +void uart_int_connect(int which, /* UART to which to connect */ + void (*isr)(void *), /* interrupt handler */ + void *arg, /* argument to pass to handler */ + void *stub /* ptr to interrupt stub code */ + ) +{ +#if !defined(CONFIG_DYNAMIC_INT_STUBS) + ARG_UNUSED(isr); + ARG_UNUSED(arg); + ARG_UNUSED(stub); +#else + INT_CONNECT(which, isr, arg, stub); +#endif /* CONFIG_DYNAMIC_INT_STUBS */ + + irq_enable((unsigned int)uart[which].irq); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ diff --git a/system/libarc32_edu/drivers/uart.h b/system/libarc32_edu/drivers/uart.h new file mode 100644 index 00000000..22865d5d --- /dev/null +++ b/system/libarc32_edu/drivers/uart.h @@ -0,0 +1,76 @@ +/* uart.h - public UART driver APIs */ + +/* + * Copyright (c) 2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCuarth +#define __INCuarth + +#ifdef __cplusplus +extern "C" { +#endif + +/* generic UART info structure */ +struct uart_init_info { + int baud_rate; + uint32_t regs; /* base port number or MM base address */ + uint32_t sys_clk_freq; /* in Hz */ + uint8_t options; /* HW Flow Control option */ + uint8_t irq; /* interrupt request number */ + uint8_t int_pri; /* interrupt priority level */ +}; +/* UART driver has to configure the device to 8n1 */ + +void uart_init(int port, const struct uart_init_info *const pinfo); + +/* console I/O functions */ +int uart_poll_in(int port, unsigned char *pChar); +unsigned char uart_poll_out(int which, unsigned char outChar); + +/* interrupt driven I/O functions */ +int uart_fifo_fill(int port, const uint8_t *txData, int len); +int uart_fifo_read(int port, uint8_t *rxData, const int size); +void uart_irq_tx_enable(int port); +void uart_irq_tx_disable(int port); +int uart_irq_tx_ready(int port); +void uart_irq_rx_enable(int port); +void uart_irq_rx_disable(int port); +int uart_irq_rx_ready(int port); +void uart_irq_err_enable(int port); +void uart_irq_err_disable(int port); +int uart_irq_is_pending(int port); +int uart_irq_update(int port); +void uart_int_connect(int port, void (*isr)(void *), void *arg, void *stub); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCuarth */ diff --git a/system/libarc32_edu/framework/include/os/os.h b/system/libarc32_edu/framework/include/os/os.h index 56e2e9d2..34a6f950 100644 --- a/system/libarc32_edu/framework/include/os/os.h +++ b/system/libarc32_edu/framework/include/os/os.h @@ -134,45 +134,6 @@ extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int time */ extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); -/** - * \brief Attach an ISR to an IRQ - * - * Attach/connect an interrupt service routing to an interrupt request line. - * Specifying a NULL pointer, as isr parameter, will detach a previous ISR from - * the IRQ. - * This service may panic if err parameter is null and: - * irq parameter is invalid, or - * when called from an ISR. - * Authorized execution levels: task, fiber. - * - * - * - VIPER does not provide an API to detach an ISR from an IRQ. - * When isr parameter is NULL, this function disables the IRQ (if valid) - * instead of replacing the ISR pointer with zeros. - * - * \param irq: interrupt request line number. - * \param isr: pointer on the interrupt service routine - * \param isrData: pointer to the data passed as an argument to isr - * \param priority: requested priority of interrupt - * \param err (out): execution status: - * E_OS_OK : ISR was attached or detached to IRQ - * E_OS_ERR: irq parameter is invalid - * E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. - * - * NB: IRQ priority is not a parameter used in lakemont context, as it is inferred from the IRQ number: - * Priority = IRQ number / 16 ( rounded down ) - * --> \ref loApicIntr.c - */ -static inline __attribute__((always_inline)) -void interrupt_set_isr (int irq, T_ENTRY_POINT isr, void* isrData , int priority, OS_ERR_TYPE* err) -{ - unsigned key = interrupt_lock(); - interrupt_connect(irq, isr, isrData); - interrupt_priority_set(irq, priority); - interrupt_unlock(key); - *err = E_OS_OK; -} - /** * \brief Get the current tick in ms * diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h index 51b7372e..79b241dc 100644 --- a/system/libarc32_edu/framework/include/os/os_types.h +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -61,7 +61,6 @@ typedef void* T_MUTEX; typedef void* T_QUEUE; typedef void* T_QUEUE_MESSAGE; typedef void* T_TIMER; -typedef void (* T_ENTRY_POINT) (void* ) ; typedef void* T_TASK ; typedef uint8_t T_TASK_PRIO ; #define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO diff --git a/system/libarc32_edu/main.c b/system/libarc32_edu/main.c index baffe56e..49128c42 100644 --- a/system/libarc32_edu/main.c +++ b/system/libarc32_edu/main.c @@ -16,12 +16,24 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include "drivers/soc_gpio.h" #include "drivers/arcv2_timer1.h" #include "scss_registers.h" +#include "infra/ipc.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" +#include "infra/log.h" +#include "infra/port.h" +#include "platform.h" +#include "services/gpio_service.h" + #define GPIO_2 2 #define PIN13 GPIO_2 @@ -32,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define LOW 0 #define HIGH 1 + static void configure_soc_gpio(pin, mode) { gpio_cfg_data_t cfg; @@ -81,39 +94,147 @@ void digitalWrite(pin, state) soc_gpio_write(SOC_GPIO_32, bit, state); } -#define TIMER1_TICK 500 +static T_QUEUE service_mgr_queue; +static uint8_t gpio_txled_index = 12; +static uint8_t gpio_rxled_index = 26; +static svc_client_handle_t * gpio_service_handle = NULL; +static volatile boolean_t gpio_txled_init; +static volatile boolean_t gpio_rxled_init; + +#if 0 +#define TIMER1_TICK 1000 void timer1_user_isr(void) { - uint8_t static pin_state = LOW; pin_state = !pin_state; - digitalWrite(13, pin_state); } +#endif void setup(void) { pinMode(13, OUTPUT); - timer1_driver_init(timer1_user_isr, TIMER1_TICK); +// timer1_driver_init(timer1_user_isr, TIMER1_TICK); } -#define DELAY_CYCLES 1000000 +static void handle_gpio_message(struct cfw_message * msg, void * param) +{ + switch (CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_OPEN_SERVICE: { + cfw_open_conn_rsp_msg_t * cnf = (cfw_open_conn_rsp_msg_t*)msg; + gpio_service_handle = cnf->client_handle; + gpio_configure(gpio_service_handle, gpio_txled_index, 1 /* output */, + (void *)(unsigned long)gpio_txled_index); + gpio_configure(gpio_service_handle, gpio_rxled_index, 1 /* output */, + (void *)(unsigned long)gpio_rxled_index); + } + break; + + case MSG_ID_GPIO_CONFIGURE_RSP: { + uint8_t index = (uint8_t)(unsigned long)(msg->priv); + if (index == gpio_txled_index) { + gpio_txled_init = true; + gpio_set_state(gpio_service_handle, gpio_txled_index, 0, NULL); + } + if (index == gpio_rxled_index) { + gpio_rxled_init = true; + gpio_set_state(gpio_service_handle, gpio_rxled_index, 0, NULL); + } + } + break; + case MSG_ID_GPIO_SET_RSP: + case MSG_ID_GPIO_GET_RSP: + case MSG_ID_GPIO_LISTEN_RSP: + case MSG_ID_GPIO_UNLISTEN_RSP: + case MSG_ID_GPIO_EVT: + /* TODO - add response message handling if needed */ + break; + } + cfw_msg_free(msg); +} + +void gpio_client_init(T_QUEUE queue) +{ + cfw_handle_t *h = cfw_init(queue, handle_gpio_message, "client"); + cfw_open_service(h, SOC_GPIO_SERVICE_ID, "connect"); +} + +#define DELAY_CYCLES 100000 void loop(void) { - unsigned i; - for (i = 0; i < DELAY_CYCLES; i++) - digitalWrite(13, HIGH); - for (i = 0; i < DELAY_CYCLES; i++) - digitalWrite(13, LOW); + static uint32_t loop_count; + static uint8_t pin_state; + + if (++loop_count > DELAY_CYCLES) { + loop_count = 0; + pin_state = !pin_state; + if (gpio_txled_init) + gpio_set_state(gpio_service_handle, gpio_txled_index, pin_state ? 0 : 1, NULL); + if (gpio_rxled_init) + gpio_set_state(gpio_service_handle, gpio_rxled_index, pin_state ? 0 : 1, NULL); + } +} + +int send_message_ipc(struct cfw_message * msg) { + return ipc_request_sync_int(IPC_MSG_TYPE_MESSAGE, 0, 0, msg); +} + +void free_message_ipc(void * msg) { + ipc_request_sync_int(IPC_MSG_TYPE_FREE, 0, 0, msg); +} + +#define TMP_SIZE 256 +static char tmp[TMP_SIZE]; + +void cfw_log(char * fmt, ... ) { + va_list args; + uint32_t timeout; + va_start(args, fmt); + unsigned int tstamp = SCSS_REG_VAL(SCSS_AONC_CNT); + int hours = tstamp / (32191 * 60 * 60); + tstamp -= hours * (32191 * 60 * 60); + int minutes = (tstamp) / (32191 * 60); + tstamp -= minutes * (32191 * 60); + int seconds = (tstamp) / 32191; + tstamp -= seconds * 32191; + int millis = (tstamp) / 32; + int it = interrupt_lock(); + + sprintf(tmp, "%03d:%02d:%02d.%03d ", hours, minutes, seconds, millis); + vsnprintf(tmp + strlen(tmp) - 1, TMP_SIZE, fmt, args); + //pr_info(LOG_MODULE_MAIN, tmp); + MBX_DAT0(4) = (unsigned int)tmp; + MBX_DAT1(4) = 0; + MBX_CTRL(4) = 0x80000000; + timeout = get_timestamp() + 10000; + while((MBX_STS(4) & 0x1) && (get_timestamp() < timeout)) + ; + + interrupt_unlock(it); + va_end(args); } int main(void) { + /* CFW IPC initialisation */ + ipc_init(5, 0, 6, 1, CPU_ID_LMT); + service_mgr_queue = queue_create(10, NULL); + _cfw_init_proxy(service_mgr_queue, shared_data->ports, + shared_data->services, shared_data->service_mgr_port_id); + set_cpu_id(CPU_ID_ARC); + set_cpu_message_sender(0, send_message_ipc); + set_cpu_free_handler(0, free_message_ipc); + soc_gpio_enable(SOC_GPIO_32); SET_PIN_MODE(2, QRK_PMUX_SEL_MODEA); + gpio_client_init(service_mgr_queue); + setup(); - for(;;) - __asm__("nop"); -// loop(); + for(;;) { + queue_process_message(service_mgr_queue); + ipc_handle_message(); +// __asm__("nop"); + loop(); + } __builtin_unreachable(); } From d4fe5885d8e2bebd071e4e7d378d3fbf8e85a2eb Mon Sep 17 00:00:00 2001 From: Bogdan Pricop Date: Mon, 18 May 2015 11:12:35 +0100 Subject: [PATCH 11/15] ATLEDGE-82 init code Interrupt handling - add hw context saving * Disable instruction cache (we don't need it at this stage) * Setup automatic (hardware) context saving feature for non-fast interrupts. * Set optimization to none (-O0) in Makefile. Signed-off-by: Bogdan Pricop Signed-off-by: David Hunt Author: Bogdan Pricop --- system/libarc32_edu/Makefile | 4 ++-- system/libarc32_edu/bootcode/init.S | 6 ++++-- system/libarc32_edu/common/aux_regs.h | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index 25606592..dfa54cfa 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -33,7 +33,7 @@ TARGET_BIN=$(TARGET).bin HWFLAGS=-mARCv2EM -mav2em -mlittle-endian CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DHAS_SHARED_MEM -OPTFLAGS=-g -Os -Wall -Werror +OPTFLAGS=-g -O0 -Wall -Werror INCLUDES=-Icommon -Ibootcode -Iframework/include EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) @@ -86,4 +86,4 @@ lib_install: lib fi \ clean: - -$(RM) $(C_OBJ) $(ASM_OBJ) $(TARGET_LIB) $(TARGET_ELF) $(TARGET_BIN) $(TARGET_ELF).map + -$(RM) $(C_OBJ) $(ASM_OBJ) $(TARGET_LIB) $(TARGET_ELF) $(TARGET_BIN) $(TARGET_ELF).map main.o diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index 08a04f4d..015481d7 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -43,9 +43,11 @@ _do_reset: /* Set up stack pointer */ // mov sp, @_DefaultStack + STACK_SIZE mov sp, @__stack_start + /* Setup automatic (hardware) context saving feature */ + sr AUX_IRQ_CTRL_SAVE_ALL, [ARC_V2_AUX_IRQ_CTRL] /* Enable instruction cache */ - mov r0, 0 - sr r0, [ARC_V2_IC_CTRL] +// mov r0, 0 +// sr r0, [ARC_V2_IC_CTRL] /* Configure the interrupt priority threshold of the ARC core to 3 */ seti 0x23 /* Jump to C init function */ diff --git a/system/libarc32_edu/common/aux_regs.h b/system/libarc32_edu/common/aux_regs.h index 7b00e968..0a1462b7 100644 --- a/system/libarc32_edu/common/aux_regs.h +++ b/system/libarc32_edu/common/aux_regs.h @@ -52,6 +52,9 @@ Definitions for auxiliary registers. #define ARC_V2_IRQ_PULSE_CANCEL 0x415 #define ARC_V2_IRQ_PENDING 0x416 +#define ARC_V2_TMR0_CONTROL_IP_MASK (0x01 << 3) +#define AUX_IRQ_CTRL_SAVE_ALL (0x1F | (0x01 << 9) | (0x01 << 10)) + /* STATUS32/STATUS32_P0 bits */ #define ARC_V2_STATUS32_H (1 << 0) #define ARC_V2_STATUS32_E(x) ((x) << 1) From d37176a3b8483127244c5c72bad861ef4598d756 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 12:56:33 +0100 Subject: [PATCH 12/15] ATLEDGE-82 init code Interrupt handling - add hw context saving * Disable instruction cache (we don't need it at this stage) * Setup automatic (hardware) context saving feature for non-fast interrupts. * Set optimization to none (-O0) in Makefile. build: Eliminating compiler warnings Fix interrupt handling bug Issue: A simple blink sketch having a 10 ms delay between blinks crashed after a few seconds. It looks like the stack got corrupted. Root cause: The generic assembly hardware ISR, "_do_isr", was not properly written; the "sp" was not properly handled. Solution: Replace the assembly hardware ISR with a C function having "__attribute__((interrupt("ilink")))" Note: We need this generic hardware ISR because the IVT is in flash and we cannot change it at runtime. Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) build: Removing sdata section declaration from linker scripts ARC doesn't support an SDATA section bigger than 1024 bytes, and we overflow this too easily when we link in code that hasn't been compiled with the -mno-sdata compiler flag. So we will ensure that we only use code compiled with -mno-sdata (including standard C lib) and we remove this section declaration to weed out any exceptions to this. In other words, if we link any code that tries to use the sdata section, we will see a linker error. build: Adding HEAP section Adding heap section with start/end marker symbols to satisfy malloc() and friends Signed-off-by: David Hunt --- .gitignore | 6 + system/libarc32_edu/Makefile | 2 +- system/libarc32_edu/bootcode/init.S | 10 +- system/libarc32_edu/bootcode/interrupt.c | 25 ++- system/libarc32_edu/bootcode/interrupt.h | 4 +- system/libarc32_edu/bootcode/irq.S | 42 ----- system/libarc32_edu/build/linker.cmd | 16 +- system/libarc32_edu/common/aux_regs.h | 1 + system/libarc32_edu/drivers/arcv2_timer0.c | 16 +- system/libarc32_edu/drivers/arcv2_timer1.c | 3 +- system/libarc32_edu/drivers/intel_qrk_pwm.c | 2 +- system/libarc32_edu/drivers/intel_qrk_pwm.h | 4 +- system/libarc32_edu/drivers/ns16550.c | 13 +- system/libarc32_edu/drivers/uart.h | 76 +++++++++ system/libarc32_edu/framework/include/os/os.h | 39 ----- .../framework/include/os/os_types.h | 2 +- system/libarc32_edu/main.c | 147 ++++++++++++++++-- variants/intel_edu_x/linker_scripts/flash.ld | 16 +- 18 files changed, 284 insertions(+), 140 deletions(-) create mode 100644 .gitignore delete mode 100644 system/libarc32_edu/bootcode/irq.S create mode 100644 system/libarc32_edu/drivers/uart.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..96a65c9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.o +*.a +*.map +*.elf +*.swp +*.bin diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index 21348969..55eb1110 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -86,4 +86,4 @@ lib_install: lib fi \ clean: - -$(RM) $(C_OBJ) $(ASM_OBJ) $(TARGET_LIB) $(TARGET_ELF) $(TARGET_BIN) $(TARGET_ELF).map + -$(RM) $(C_OBJ) $(ASM_OBJ) $(TARGET_LIB) $(TARGET_ELF) $(TARGET_BIN) $(TARGET_ELF).map main.o diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index 243c38ef..015481d7 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -18,6 +18,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "aux_regs.h" +.globl _do_isr +.type _do_isr,%function + .section .int_vector_table .balign 4 _start: @@ -40,10 +43,11 @@ _do_reset: /* Set up stack pointer */ // mov sp, @_DefaultStack + STACK_SIZE mov sp, @__stack_start - mov gp, @__SDATA_BEGIN__ + /* Setup automatic (hardware) context saving feature */ + sr AUX_IRQ_CTRL_SAVE_ALL, [ARC_V2_AUX_IRQ_CTRL] /* Enable instruction cache */ - mov r0, 0 - sr r0, [ARC_V2_IC_CTRL] +// mov r0, 0 +// sr r0, [ARC_V2_IC_CTRL] /* Configure the interrupt priority threshold of the ARC core to 3 */ seti 0x23 /* Jump to C init function */ diff --git a/system/libarc32_edu/bootcode/interrupt.c b/system/libarc32_edu/bootcode/interrupt.c index 47c7ca7c..3f7df2f4 100644 --- a/system/libarc32_edu/bootcode/interrupt.c +++ b/system/libarc32_edu/bootcode/interrupt.c @@ -23,19 +23,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA struct _IsrTableEntry { void *arg; - void (*isr)(void *); + void (*isr)(void); }; struct _IsrTableEntry __attribute__((section(".data"))) _IsrTable[_WRS_CONFIG_NUM_IRQS]; -static void _dummy_isr(void *arg) +static void _dummy_isr(void) { + __asm__ ("flag 0x01"); /* Set the halt flag => halt the CPU */ for(;;); } -void interrupt_connect(unsigned int irq, - void (*isr)(void *arg), - void *arg) +/* + * The default, generic hardware IRQ handler. + * It only decodes the source of IRQ and calls the appropriate handler + * + * We need this because the Interrupt Vector Table is located in the flash + * memory and cannot modify it at run time.*/ +__attribute__ ((interrupt ("ilink"))) +void _do_isr(void) +{ + unsigned int irq_cause = aux_reg_read(ARC_V2_ICAUSE) - 16; + if (_IsrTable[irq_cause].isr != 0x00) + _IsrTable[irq_cause].isr(); + else + _dummy_isr(); +} + +void interrupt_connect(unsigned int irq, void (*isr)(void), void *arg) { int index = irq - 16; unsigned int flags = interrupt_lock(); diff --git a/system/libarc32_edu/bootcode/interrupt.h b/system/libarc32_edu/bootcode/interrupt.h index 65d3e5d0..8d8bfed2 100644 --- a/system/libarc32_edu/bootcode/interrupt.h +++ b/system/libarc32_edu/bootcode/interrupt.h @@ -19,9 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #ifndef __INTERRUPT_H__ #define __INTERRUPT_H__ -extern void interrupt_connect(unsigned int irq, - void (*isr)(void *arg), - void *arg); +extern void interrupt_connect(unsigned int irq, void (*isr)(void), void *arg); extern void interrupt_disconnect(unsigned int irq); extern void interrupt_enable(unsigned int irq); extern void interrupt_disable(unsigned int irq); diff --git a/system/libarc32_edu/bootcode/irq.S b/system/libarc32_edu/bootcode/irq.S deleted file mode 100644 index be347563..00000000 --- a/system/libarc32_edu/bootcode/irq.S +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright (c) 2015 Intel Corporation. All right reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ -#include "aux_regs.h" - -.globl _do_isr -.type _do_isr,%function - -.globl _IsrTable -.type _IsrTable,%object - -.text -.balign 4 -_do_isr: - lr r0, [ARC_V2_ICAUSE] - sub r0, r0, 16 - - mov r1, _IsrTable - add3 r0, r1, r0 /* table entries are 8-bytes wide */ - - ld r1, [r0, 4] /* ISR into r1 */ - jl_s.d [r1] - ld_s r0, [r0] /* delay slot: ISR parameter into r0 */ - - /* back from ISR */ - rtie - nop diff --git a/system/libarc32_edu/build/linker.cmd b/system/libarc32_edu/build/linker.cmd index 383455b3..9c036aac 100644 --- a/system/libarc32_edu/build/linker.cmd +++ b/system/libarc32_edu/build/linker.cmd @@ -25,7 +25,7 @@ OUTPUT_FORMAT("elf32-littlearc", "elf32-bigarc", "elf32-littlearc") MEMORY { FLASH (rx) : ORIGIN = 0x40000000, LENGTH = 192K - SRAM (wx) : ORIGIN = 0xa8010000, LENGTH = 16K + SRAM (wx) : ORIGIN = 0xa800e000, LENGTH = 24K DCCM (wx) : ORIGIN = 0x80000000, LENGTH = 8K } @@ -141,13 +141,13 @@ SECTIONS *(".seg_rxtx.*") } > SRAM - heap (NOLOAD) : - { - . = ALIGN(4); - __start_heap = . ; - . = . + __HEAP_SIZE ; - __end_heap = . ; - } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM /* Define linker symbols */ diff --git a/system/libarc32_edu/common/aux_regs.h b/system/libarc32_edu/common/aux_regs.h index 44dd7d24..0a1462b7 100644 --- a/system/libarc32_edu/common/aux_regs.h +++ b/system/libarc32_edu/common/aux_regs.h @@ -53,6 +53,7 @@ Definitions for auxiliary registers. #define ARC_V2_IRQ_PENDING 0x416 #define ARC_V2_TMR0_CONTROL_IP_MASK (0x01 << 3) +#define AUX_IRQ_CTRL_SAVE_ALL (0x1F | (0x01 << 9) | (0x01 << 10)) /* STATUS32/STATUS32_P0 bits */ #define ARC_V2_STATUS32_H (1 << 0) diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c index 9e0c3fa8..0acbe875 100644 --- a/system/libarc32_edu/drivers/arcv2_timer0.c +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -32,8 +32,15 @@ conjunction with a microkernel. #include "conf.h" #include "interrupt.h" +#define ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock */ + /* defines */ +#define ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ +#define ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ +#define ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ +#define ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ + #define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000 @@ -118,10 +125,8 @@ uint32_t arcv2_timer0_control_get(void) * * \NOMANUAL */ - -void _arcv2_timer0_int_handler(void* unusedArg/* not used */) +void _arcv2_timer0_int_handler(void) { - (void)(unusedArg); /* clear the interrupt (by writing 0 to IP bit of the control register) */ aux_reg_write(ARC_V2_TMR0_CONTROL, @@ -129,8 +134,9 @@ void _arcv2_timer0_int_handler(void* unusedArg/* not used */) /* Increment number of Timer0 overflows */ timer0_overflows++; /* Increment number of Timer0 overflows used to compute micros() */ - timer0_overflows_us = - (timer0_overflows_us <= MAX_OVERFLOWS_US) ? timer0_overflows_us++ : 0; + timer0_overflows_us++; + if (timer0_overflows_us > MAX_OVERFLOWS_US) + timer0_overflows_us = 0; } /******************************************************************************* diff --git a/system/libarc32_edu/drivers/arcv2_timer1.c b/system/libarc32_edu/drivers/arcv2_timer1.c index 0fcd65d4..8c3bfa12 100644 --- a/system/libarc32_edu/drivers/arcv2_timer1.c +++ b/system/libarc32_edu/drivers/arcv2_timer1.c @@ -92,9 +92,8 @@ uint32_t arcv2_timer1_count_get(void) * * \NOMANUAL */ -void _arcv2_timer1_int_handler(void *notused) +void _arcv2_timer1_int_handler(void) { - (void)(notused); /* clear the interrupt (by writing 0 to IP bit of the control register) */ aux_reg_write(ARC_V2_TMR1_CONTROL, ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); /* execute callback specified by the user */ diff --git a/system/libarc32_edu/drivers/intel_qrk_pwm.c b/system/libarc32_edu/drivers/intel_qrk_pwm.c index d0322c32..3e3ee404 100644 --- a/system/libarc32_edu/drivers/intel_qrk_pwm.c +++ b/system/libarc32_edu/drivers/intel_qrk_pwm.c @@ -311,7 +311,7 @@ static void soc_pwm_unmask_interrupt(uint8_t channel) * * \brief PWM ISR, if specified calls a user defined callback */ -DECLARE_INTERRUPT_HANDLER void pwm_isr(void *arg) +DECLARE_INTERRUPT_HANDLER void pwm_isr(void) { uint32_t pending = 0, pwm = 0; diff --git a/system/libarc32_edu/drivers/intel_qrk_pwm.h b/system/libarc32_edu/drivers/intel_qrk_pwm.h index 75ac002b..3d9c3258 100644 --- a/system/libarc32_edu/drivers/intel_qrk_pwm.h +++ b/system/libarc32_edu/drivers/intel_qrk_pwm.h @@ -123,11 +123,11 @@ void soc_pwm_stop(uint8_t channel); DRIVER_API_RC soc_pwm_block_init(void); -/*! \fn void pwm_isr(void*) +/*! \fn void pwm_isr(void) * * \brief PWM ISR, if specified calls a user defined callback */ -void pwm_isr(void*); +void pwm_isr(void); #ifdef __cplusplus } diff --git a/system/libarc32_edu/drivers/ns16550.c b/system/libarc32_edu/drivers/ns16550.c index 6648733f..4dc23bcd 100644 --- a/system/libarc32_edu/drivers/ns16550.c +++ b/system/libarc32_edu/drivers/ns16550.c @@ -59,9 +59,10 @@ INCLUDE FILES: drivers/uart.h /* includes */ #include -#include -#include -#include + +#include "board.h" +#include "interrupt.h" +#include "uart.h" /* defines */ @@ -249,7 +250,7 @@ void uart_init(int which, /* UART channel to initialize */ const struct uart_init_info * const init_info ) { - int oldLevel; /* old interrupt lock level */ + unsigned int oldLevel; /* old interrupt lock level */ uint32_t divisor; /* baud rate divisor */ uart[which].port = init_info->regs; @@ -257,7 +258,7 @@ void uart_init(int which, /* UART channel to initialize */ uart[which].intPri = init_info->int_pri; uart[which].iirCache = 0; - oldLevel = irq_lock_inline(); + oldLevel = interrupt_lock(); /* calculate baud rate divisor */ divisor = (init_info->sys_clk_freq / init_info->baud_rate) >> 4; @@ -286,7 +287,7 @@ void uart_init(int which, /* UART channel to initialize */ /* disable interrupts */ OUTBYTE(IER(which), 0x00); - irq_unlock_inline(oldLevel); + interrupt_unlock(oldLevel); } /******************************************************************************* diff --git a/system/libarc32_edu/drivers/uart.h b/system/libarc32_edu/drivers/uart.h new file mode 100644 index 00000000..22865d5d --- /dev/null +++ b/system/libarc32_edu/drivers/uart.h @@ -0,0 +1,76 @@ +/* uart.h - public UART driver APIs */ + +/* + * Copyright (c) 2015 Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCuarth +#define __INCuarth + +#ifdef __cplusplus +extern "C" { +#endif + +/* generic UART info structure */ +struct uart_init_info { + int baud_rate; + uint32_t regs; /* base port number or MM base address */ + uint32_t sys_clk_freq; /* in Hz */ + uint8_t options; /* HW Flow Control option */ + uint8_t irq; /* interrupt request number */ + uint8_t int_pri; /* interrupt priority level */ +}; +/* UART driver has to configure the device to 8n1 */ + +void uart_init(int port, const struct uart_init_info *const pinfo); + +/* console I/O functions */ +int uart_poll_in(int port, unsigned char *pChar); +unsigned char uart_poll_out(int which, unsigned char outChar); + +/* interrupt driven I/O functions */ +int uart_fifo_fill(int port, const uint8_t *txData, int len); +int uart_fifo_read(int port, uint8_t *rxData, const int size); +void uart_irq_tx_enable(int port); +void uart_irq_tx_disable(int port); +int uart_irq_tx_ready(int port); +void uart_irq_rx_enable(int port); +void uart_irq_rx_disable(int port); +int uart_irq_rx_ready(int port); +void uart_irq_err_enable(int port); +void uart_irq_err_disable(int port); +int uart_irq_is_pending(int port); +int uart_irq_update(int port); +void uart_int_connect(int port, void (*isr)(void *), void *arg, void *stub); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCuarth */ diff --git a/system/libarc32_edu/framework/include/os/os.h b/system/libarc32_edu/framework/include/os/os.h index 56e2e9d2..34a6f950 100644 --- a/system/libarc32_edu/framework/include/os/os.h +++ b/system/libarc32_edu/framework/include/os/os.h @@ -134,45 +134,6 @@ extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int time */ extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); -/** - * \brief Attach an ISR to an IRQ - * - * Attach/connect an interrupt service routing to an interrupt request line. - * Specifying a NULL pointer, as isr parameter, will detach a previous ISR from - * the IRQ. - * This service may panic if err parameter is null and: - * irq parameter is invalid, or - * when called from an ISR. - * Authorized execution levels: task, fiber. - * - * - * - VIPER does not provide an API to detach an ISR from an IRQ. - * When isr parameter is NULL, this function disables the IRQ (if valid) - * instead of replacing the ISR pointer with zeros. - * - * \param irq: interrupt request line number. - * \param isr: pointer on the interrupt service routine - * \param isrData: pointer to the data passed as an argument to isr - * \param priority: requested priority of interrupt - * \param err (out): execution status: - * E_OS_OK : ISR was attached or detached to IRQ - * E_OS_ERR: irq parameter is invalid - * E_OS_ERR_NOT_ALLOWED: service cannot be executed from ISR context. - * - * NB: IRQ priority is not a parameter used in lakemont context, as it is inferred from the IRQ number: - * Priority = IRQ number / 16 ( rounded down ) - * --> \ref loApicIntr.c - */ -static inline __attribute__((always_inline)) -void interrupt_set_isr (int irq, T_ENTRY_POINT isr, void* isrData , int priority, OS_ERR_TYPE* err) -{ - unsigned key = interrupt_lock(); - interrupt_connect(irq, isr, isrData); - interrupt_priority_set(irq, priority); - interrupt_unlock(key); - *err = E_OS_OK; -} - /** * \brief Get the current tick in ms * diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h index 51b7372e..7793bdb9 100644 --- a/system/libarc32_edu/framework/include/os/os_types.h +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -61,7 +61,7 @@ typedef void* T_MUTEX; typedef void* T_QUEUE; typedef void* T_QUEUE_MESSAGE; typedef void* T_TIMER; -typedef void (* T_ENTRY_POINT) (void* ) ; +//typedef void (* T_ENTRY_POINT) (void* ) ; typedef void* T_TASK ; typedef uint8_t T_TASK_PRIO ; #define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO diff --git a/system/libarc32_edu/main.c b/system/libarc32_edu/main.c index 7fb9b1a2..49128c42 100644 --- a/system/libarc32_edu/main.c +++ b/system/libarc32_edu/main.c @@ -16,12 +16,24 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include "drivers/soc_gpio.h" #include "drivers/arcv2_timer1.h" #include "scss_registers.h" +#include "infra/ipc.h" +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" +#include "infra/log.h" +#include "infra/port.h" +#include "platform.h" +#include "services/gpio_service.h" + #define GPIO_2 2 #define PIN13 GPIO_2 @@ -32,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define LOW 0 #define HIGH 1 + static void configure_soc_gpio(pin, mode) { gpio_cfg_data_t cfg; @@ -81,41 +94,147 @@ void digitalWrite(pin, state) soc_gpio_write(SOC_GPIO_32, bit, state); } -#define TIMER1_TICK 500 +static T_QUEUE service_mgr_queue; +static uint8_t gpio_txled_index = 12; +static uint8_t gpio_rxled_index = 26; +static svc_client_handle_t * gpio_service_handle = NULL; +static volatile boolean_t gpio_txled_init; +static volatile boolean_t gpio_rxled_init; + +#if 0 +#define TIMER1_TICK 1000 void timer1_user_isr(void) { - uint8_t static pin_state = LOW; pin_state = !pin_state; - digitalWrite(13, pin_state); } +#endif void setup(void) { pinMode(13, OUTPUT); - timer1_driver_init(timer1_user_isr, TIMER1_TICK); +// timer1_driver_init(timer1_user_isr, TIMER1_TICK); +} + +static void handle_gpio_message(struct cfw_message * msg, void * param) +{ + switch (CFW_MESSAGE_ID(msg)) { + case MSG_ID_CFW_OPEN_SERVICE: { + cfw_open_conn_rsp_msg_t * cnf = (cfw_open_conn_rsp_msg_t*)msg; + gpio_service_handle = cnf->client_handle; + gpio_configure(gpio_service_handle, gpio_txled_index, 1 /* output */, + (void *)(unsigned long)gpio_txled_index); + gpio_configure(gpio_service_handle, gpio_rxled_index, 1 /* output */, + (void *)(unsigned long)gpio_rxled_index); + } + break; + + case MSG_ID_GPIO_CONFIGURE_RSP: { + uint8_t index = (uint8_t)(unsigned long)(msg->priv); + if (index == gpio_txled_index) { + gpio_txled_init = true; + gpio_set_state(gpio_service_handle, gpio_txled_index, 0, NULL); + } + if (index == gpio_rxled_index) { + gpio_rxled_init = true; + gpio_set_state(gpio_service_handle, gpio_rxled_index, 0, NULL); + } + } + break; + case MSG_ID_GPIO_SET_RSP: + case MSG_ID_GPIO_GET_RSP: + case MSG_ID_GPIO_LISTEN_RSP: + case MSG_ID_GPIO_UNLISTEN_RSP: + case MSG_ID_GPIO_EVT: + /* TODO - add response message handling if needed */ + break; + } + cfw_msg_free(msg); } -#define DELAY_CYCLES 1000000 +void gpio_client_init(T_QUEUE queue) +{ + cfw_handle_t *h = cfw_init(queue, handle_gpio_message, "client"); + cfw_open_service(h, SOC_GPIO_SERVICE_ID, "connect"); +} + +#define DELAY_CYCLES 100000 void loop(void) { - unsigned i; - for (i = 0; i < DELAY_CYCLES; i++) - digitalWrite(13, HIGH); - for (i = 0; i < DELAY_CYCLES; i++) - digitalWrite(13, LOW); + static uint32_t loop_count; + static uint8_t pin_state; + + if (++loop_count > DELAY_CYCLES) { + loop_count = 0; + pin_state = !pin_state; + if (gpio_txled_init) + gpio_set_state(gpio_service_handle, gpio_txled_index, pin_state ? 0 : 1, NULL); + if (gpio_rxled_init) + gpio_set_state(gpio_service_handle, gpio_rxled_index, pin_state ? 0 : 1, NULL); + } +} + +int send_message_ipc(struct cfw_message * msg) { + return ipc_request_sync_int(IPC_MSG_TYPE_MESSAGE, 0, 0, msg); +} + +void free_message_ipc(void * msg) { + ipc_request_sync_int(IPC_MSG_TYPE_FREE, 0, 0, msg); +} + +#define TMP_SIZE 256 +static char tmp[TMP_SIZE]; + +void cfw_log(char * fmt, ... ) { + va_list args; + uint32_t timeout; + va_start(args, fmt); + unsigned int tstamp = SCSS_REG_VAL(SCSS_AONC_CNT); + int hours = tstamp / (32191 * 60 * 60); + tstamp -= hours * (32191 * 60 * 60); + int minutes = (tstamp) / (32191 * 60); + tstamp -= minutes * (32191 * 60); + int seconds = (tstamp) / 32191; + tstamp -= seconds * 32191; + int millis = (tstamp) / 32; + int it = interrupt_lock(); + + sprintf(tmp, "%03d:%02d:%02d.%03d ", hours, minutes, seconds, millis); + vsnprintf(tmp + strlen(tmp) - 1, TMP_SIZE, fmt, args); + //pr_info(LOG_MODULE_MAIN, tmp); + MBX_DAT0(4) = (unsigned int)tmp; + MBX_DAT1(4) = 0; + MBX_CTRL(4) = 0x80000000; + timeout = get_timestamp() + 10000; + while((MBX_STS(4) & 0x1) && (get_timestamp() < timeout)) + ; + + interrupt_unlock(it); + va_end(args); } int main(void) { + /* CFW IPC initialisation */ + ipc_init(5, 0, 6, 1, CPU_ID_LMT); + service_mgr_queue = queue_create(10, NULL); + _cfw_init_proxy(service_mgr_queue, shared_data->ports, + shared_data->services, shared_data->service_mgr_port_id); + set_cpu_id(CPU_ID_ARC); + set_cpu_message_sender(0, send_message_ipc); + set_cpu_free_handler(0, free_message_ipc); + soc_gpio_enable(SOC_GPIO_32); SET_PIN_MODE(2, QRK_PMUX_SEL_MODEA); - uart_init(0); + gpio_client_init(service_mgr_queue); setup(); - for(;;) - __asm__("nop"); -// loop(); + for(;;) { + queue_process_message(service_mgr_queue); + ipc_handle_message(); +// __asm__("nop"); + loop(); + } __builtin_unreachable(); } diff --git a/variants/intel_edu_x/linker_scripts/flash.ld b/variants/intel_edu_x/linker_scripts/flash.ld index 383455b3..9c036aac 100644 --- a/variants/intel_edu_x/linker_scripts/flash.ld +++ b/variants/intel_edu_x/linker_scripts/flash.ld @@ -25,7 +25,7 @@ OUTPUT_FORMAT("elf32-littlearc", "elf32-bigarc", "elf32-littlearc") MEMORY { FLASH (rx) : ORIGIN = 0x40000000, LENGTH = 192K - SRAM (wx) : ORIGIN = 0xa8010000, LENGTH = 16K + SRAM (wx) : ORIGIN = 0xa800e000, LENGTH = 24K DCCM (wx) : ORIGIN = 0x80000000, LENGTH = 8K } @@ -141,13 +141,13 @@ SECTIONS *(".seg_rxtx.*") } > SRAM - heap (NOLOAD) : - { - . = ALIGN(4); - __start_heap = . ; - . = . + __HEAP_SIZE ; - __end_heap = . ; - } > SRAM + heap (NOLOAD) : + { + . = ALIGN(4); + __start_heap = . ; + . = . + __HEAP_SIZE ; + __end_heap = . ; + } > SRAM /* Define linker symbols */ From 9ec6fafc1665504fe90fc99623f1348267caeed9 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Tue, 19 May 2015 13:13:45 +0100 Subject: [PATCH 13/15] ATLEDGE-82 init code final cleanup before push. no functional changes. --- system/libarc32_edu/Makefile | 2 +- system/libarc32_edu/build/linker.cmd | 6 - system/libarc32_edu/common/irq.h | 111 ------------------ system/libarc32_edu/common/uart.h | 76 ------------ system/libarc32_edu/drivers/arcv2_timer0.c | 7 -- .../framework/include/os/os_types.h | 1 - 6 files changed, 1 insertion(+), 202 deletions(-) delete mode 100644 system/libarc32_edu/common/irq.h delete mode 100644 system/libarc32_edu/common/uart.h diff --git a/system/libarc32_edu/Makefile b/system/libarc32_edu/Makefile index 55eb1110..dfa54cfa 100644 --- a/system/libarc32_edu/Makefile +++ b/system/libarc32_edu/Makefile @@ -33,7 +33,7 @@ TARGET_BIN=$(TARGET).bin HWFLAGS=-mARCv2EM -mav2em -mlittle-endian CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DHAS_SHARED_MEM -OPTFLAGS=-g -Os -Wall +OPTFLAGS=-g -O0 -Wall -Werror INCLUDES=-Icommon -Ibootcode -Iframework/include EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -fno-reorder-functions -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -fno-defer-pop -Wno-unused-but-set-variable -Wno-main -ffreestanding -fno-stack-protector -mno-sdata -ffunction-sections -fdata-sections CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) diff --git a/system/libarc32_edu/build/linker.cmd b/system/libarc32_edu/build/linker.cmd index 9c036aac..4ab003a4 100644 --- a/system/libarc32_edu/build/linker.cmd +++ b/system/libarc32_edu/build/linker.cmd @@ -97,12 +97,6 @@ SECTIONS *(".data.*") } > SRAM - sdata : - { - __SDATA_BEGIN__ = .; - *(.sdata .sdata.* .gnu.linkonce.s.*) - } > SRAM - __data_ram_end = .; bss (NOLOAD) : diff --git a/system/libarc32_edu/common/irq.h b/system/libarc32_edu/common/irq.h deleted file mode 100644 index 204d4586..00000000 --- a/system/libarc32_edu/common/irq.h +++ /dev/null @@ -1,111 +0,0 @@ -/* arc/v2/irq.h - ARCv2 public interrupt handling */ - -/* - * Copyright (c) 2014 Wind River Systems, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1) Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3) Neither the name of Wind River Systems nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * DESCRIPTION - * ARCv2 nanokernel interrupt handling interface. Included by ARC/v2/arch.h. - */ - -#ifndef _ARCH_ARC_V2_IRQ__H_ -#define _ARCH_ARC_V2_IRQ__H_ - -#include - -#ifdef _ASMLANGUAGE -GTEXT(_irq_exit); -GTEXT(irq_lock) -GTEXT(irq_unlock) -GTEXT(irq_handler_set) -GTEXT(irq_connect) -GTEXT(irq_disconnect) -GTEXT(irq_enable) -GTEXT(irq_disable) -GTEXT(irq_priority_set) -#else -extern int irq_lock(void); -extern void irq_unlock(int key); - -extern void irq_handler_set(unsigned int irq, - void (*old)(void *arg), - void (*new)(void *arg), - void *arg); -extern int irq_connect(unsigned int irq, - unsigned int prio, - void (*isr)(void *arg), - void *arg); -extern void irq_disconnect(unsigned int irq); - -extern void irq_enable(unsigned int irq); -extern void irq_disable(unsigned int irq); - -extern void irq_priority_set(unsigned int irq, unsigned int prio); - -extern void _irq_exit(void); - -/******************************************************************************* -* -* irq_lock_inline - disable all interrupts on the CPU (inline) -* -* See irq_lock() for full description -* -* RETURNS: An architecture-dependent lock-out key representing the -* "interrupt disable state" prior to the call. -* -* \NOMANUAL -*/ - -static __attribute__ ((always_inline)) unsigned int irq_lock_inline(void) -{ - unsigned int key; - - __asm__ volatile("clri %0" : "=r"(key)); - return key; -} - -/******************************************************************************* -* -* irq_unlock_inline - enable all interrupts on the CPU (inline) -* -* See irq_unlock() for full description -* -* RETURNS: N/A -* -* \NOMANUAL -*/ - -static __attribute__ ((always_inline)) void irq_unlock_inline(unsigned int key) -{ - __asm__ volatile("seti %0" : : "ir"(key)); -} - -#endif /* _ASMLANGUAGE */ -#endif /* _ARCH_ARC_V2_IRQ__H_ */ diff --git a/system/libarc32_edu/common/uart.h b/system/libarc32_edu/common/uart.h deleted file mode 100644 index 22865d5d..00000000 --- a/system/libarc32_edu/common/uart.h +++ /dev/null @@ -1,76 +0,0 @@ -/* uart.h - public UART driver APIs */ - -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1) Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3) Neither the name of Wind River Systems nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __INCuarth -#define __INCuarth - -#ifdef __cplusplus -extern "C" { -#endif - -/* generic UART info structure */ -struct uart_init_info { - int baud_rate; - uint32_t regs; /* base port number or MM base address */ - uint32_t sys_clk_freq; /* in Hz */ - uint8_t options; /* HW Flow Control option */ - uint8_t irq; /* interrupt request number */ - uint8_t int_pri; /* interrupt priority level */ -}; -/* UART driver has to configure the device to 8n1 */ - -void uart_init(int port, const struct uart_init_info *const pinfo); - -/* console I/O functions */ -int uart_poll_in(int port, unsigned char *pChar); -unsigned char uart_poll_out(int which, unsigned char outChar); - -/* interrupt driven I/O functions */ -int uart_fifo_fill(int port, const uint8_t *txData, int len); -int uart_fifo_read(int port, uint8_t *rxData, const int size); -void uart_irq_tx_enable(int port); -void uart_irq_tx_disable(int port); -int uart_irq_tx_ready(int port); -void uart_irq_rx_enable(int port); -void uart_irq_rx_disable(int port); -int uart_irq_rx_ready(int port); -void uart_irq_err_enable(int port); -void uart_irq_err_disable(int port); -int uart_irq_is_pending(int port); -int uart_irq_update(int port); -void uart_int_connect(int port, void (*isr)(void *), void *arg, void *stub); - -#ifdef __cplusplus -} -#endif - -#endif /* __INCuarth */ diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c index 0acbe875..17b31e60 100644 --- a/system/libarc32_edu/drivers/arcv2_timer0.c +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -32,15 +32,8 @@ conjunction with a microkernel. #include "conf.h" #include "interrupt.h" -#define ARCV2_TIMER0_CLOCK_FREQ 32000000 /* 32MHz reference clock */ - /* defines */ -#define ARC_V2_TMR_CTRL_IE 0x1 /* interrupt enable */ -#define ARC_V2_TMR_CTRL_NH 0x2 /* count only while not halted */ -#define ARC_V2_TMR_CTRL_W 0x4 /* watchdog mode enable */ -#define ARC_V2_TMR_CTRL_IP 0x8 /* interrupt pending flag */ - #define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000 diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h index 7793bdb9..79b241dc 100644 --- a/system/libarc32_edu/framework/include/os/os_types.h +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -61,7 +61,6 @@ typedef void* T_MUTEX; typedef void* T_QUEUE; typedef void* T_QUEUE_MESSAGE; typedef void* T_TIMER; -//typedef void (* T_ENTRY_POINT) (void* ) ; typedef void* T_TASK ; typedef uint8_t T_TASK_PRIO ; #define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO From e187e5ed742bd85ecafa6cde5dfa3ad35ec90bb9 Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Tue, 19 May 2015 13:42:45 +0100 Subject: [PATCH 14/15] ATLEDGE-82 init code Minor tidy-up to interrupt init code Currently, timer init functions enable global interrupts. This should be done in the interrupt setup code during the chip init sequence. Signed-off-by: Dan O'Donovan Signed-off-by: David Hunt --- system/libarc32_edu/bootcode/init.S | 6 ++---- system/libarc32_edu/bootcode/interrupt.c | 6 ++++++ system/libarc32_edu/drivers/arcv2_timer0.c | 3 --- system/libarc32_edu/drivers/arcv2_timer1.c | 6 ------ 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index 015481d7..f28bc69b 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -46,10 +46,8 @@ _do_reset: /* Setup automatic (hardware) context saving feature */ sr AUX_IRQ_CTRL_SAVE_ALL, [ARC_V2_AUX_IRQ_CTRL] /* Enable instruction cache */ -// mov r0, 0 -// sr r0, [ARC_V2_IC_CTRL] - /* Configure the interrupt priority threshold of the ARC core to 3 */ - seti 0x23 + mov r0, 0 + sr r0, [ARC_V2_IC_CTRL] /* Jump to C init function */ j @_main diff --git a/system/libarc32_edu/bootcode/interrupt.c b/system/libarc32_edu/bootcode/interrupt.c index 3f7df2f4..4f36b127 100644 --- a/system/libarc32_edu/bootcode/interrupt.c +++ b/system/libarc32_edu/bootcode/interrupt.c @@ -20,6 +20,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "aux_regs.h" #include "interrupt.h" +#define INTERRUPT_ENABLE (1 << 4) +#define INTERRUPT_THRESHOLD (3) + struct _IsrTableEntry { void *arg; @@ -101,4 +104,7 @@ void interrupt_unit_device_init(void) aux_reg_write(ARC_V2_IRQ_ENABLE, ARC_V2_INT_DISABLE); aux_reg_write(ARC_V2_IRQ_TRIGGER, ARC_V2_INT_LEVEL); } + + /* Configure the interrupt priority threshold and enable interrupts */ + __builtin_arc_seti(INTERRUPT_ENABLE | INTERRUPT_THRESHOLD); } diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c index 0acbe875..49f261fd 100644 --- a/system/libarc32_edu/drivers/arcv2_timer0.c +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -150,7 +150,6 @@ void _arcv2_timer0_int_handler(void) */ void timer0_driver_init(void) { - /* ensure that the timer will not generate interrupts */ aux_reg_write(ARC_V2_TMR0_CONTROL, 0); aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* clear the count value */ @@ -163,6 +162,4 @@ void timer0_driver_init(void) /* Everything has been configured. It is now safe to enable the interrupt */ interrupt_enable(ARCV2_IRQ_TIMER0); - /* Enable ARC's global interrupts */ - interrupt_unlock(0); } diff --git a/system/libarc32_edu/drivers/arcv2_timer1.c b/system/libarc32_edu/drivers/arcv2_timer1.c index 9a0a0697..07cb8b4c 100644 --- a/system/libarc32_edu/drivers/arcv2_timer1.c +++ b/system/libarc32_edu/drivers/arcv2_timer1.c @@ -169,12 +169,6 @@ void timer1_driver_init(void(*int_handler)(void), uint32_t ticktime_ms) /* Everything has been configured. It is now safe to enable the interrupt */ interrupt_enable(ARCV2_IRQ_TIMER1); - /* Enable global ARC interrupts */ -// interrupt_unlock(ARCV2_SETI_IRQ_LVL_2); - interrupt_unlock(0); -#if 0 - nanoCpuIntEnable (_WRS_CONFIG_ARCV2_TIMER1_INT_LVL); -#endif } /******************************************************************************* From e77ff7a37c2bd3a00199471cbc328d7bf338b87b Mon Sep 17 00:00:00 2001 From: David Hunt Date: Thu, 21 May 2015 14:08:03 +0100 Subject: [PATCH 15/15] Updated due to code reviews * Fix delay() issue Issue: If value of microsecond is Timer0 limit, arcv2_timer0_count_get() microseconds condition will always be true, spinning for ever. The main issue is that the time between 2 consecutive readings of COUNT0 register is around 90 ticks and the Timer may overflow before the next reafing of COUNT0 if last values read > LIMIT - 90. Fix: Take in consideration the number of overflows. If TIMER0 overflows just leave the loop (even if COUNT0 < microseconds) * Restructure a bit arcv2_timer0_enable() function in order to reflect the procedure described in chapter 3.3.76.1 of DesignWare ARCv2 ISA Programmer's Reference Manual. Make sure Timer0 initialisation clears the counter register as well. * code review - added yield via hooks.c * added back in strip stage when building * Remove unused code in os.h, os_types.h and pf_init.h * Uncomment call to yield() from delay() Arduino function; yield() is added for sake of compatibility. * Remove dead code from timer0 init function. * Commented out enable instruction cache * Added Copyright header to wiring.c * Setup automatic (hardware) context saving feature for non-fast interrupts * Serial Communication API Initial checkin of Serial.read() and Serial.write() using header pins 0 and 1 * Eliminated some compiler warnings. Signed-off-by: David Hunt --- cores/arduino/hooks.c | 58 +++++++++++++ cores/arduino/wiring.c | 34 ++++++-- system/libarc32_edu/bootcode/c_init.c | 2 +- system/libarc32_edu/bootcode/init.S | 4 +- system/libarc32_edu/bootcode/interrupt.c | 3 + system/libarc32_edu/common/arcv2_timer0.h | 81 +++++++++++++++++++ .../{drivers => common}/arcv2_timer1.h | 0 system/libarc32_edu/drivers/arcv2_timer0.c | 54 ++----------- system/libarc32_edu/framework/include/os/os.h | 28 ------- .../framework/include/os/os_types.h | 14 ---- system/libarc32_edu/main.c | 2 +- 11 files changed, 180 insertions(+), 100 deletions(-) create mode 100644 cores/arduino/hooks.c create mode 100644 system/libarc32_edu/common/arcv2_timer0.h rename system/libarc32_edu/{drivers => common}/arcv2_timer1.h (100%) diff --git a/cores/arduino/hooks.c b/cores/arduino/hooks.c new file mode 100644 index 00000000..aa16d119 --- /dev/null +++ b/cores/arduino/hooks.c @@ -0,0 +1,58 @@ +/* + Copyright (c) 2012 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * Empty yield() hook. + * + * This function is intended to be used by library writers to build + * libraries or sketches that supports cooperative threads. + * + * Its defined as a weak symbol and it can be redefined to implement a + * real cooperative scheduler. + */ +static void __empty() { + // Empty +} +void yield(void) __attribute__ ((weak, alias("__empty"))); + +/** + * SysTick hook + * + * This function is called from SysTick handler, before the default + * handler provided by Arduino. + */ +static int __false() { + // Return false + return 0; +} +int sysTickHook(void) __attribute__ ((weak, alias("__false"))); + +/** + * SVC hook + * PendSV hook + * + * These functions are called from SVC handler, and PensSV handler. + * Default action is halting. + */ +static void __halt() { + // Halts + while (1) + ; +} +void svcHook(void) __attribute__ ((weak, alias("__halt"))); +void pendSVHook(void) __attribute__ ((weak, alias("__halt"))); diff --git a/cores/arduino/wiring.c b/cores/arduino/wiring.c index ac524731..f52597eb 100644 --- a/cores/arduino/wiring.c +++ b/cores/arduino/wiring.c @@ -1,6 +1,23 @@ /* - * TODO: Copyright header - * */ +Copyright (c) 2015 Intel Corporation. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "Arduino.h" #include "wiring.h" #include "arcv2_timer0.h" @@ -15,17 +32,18 @@ void delay(uint32_t msec) { if(0 == msec) return; - uint32_t no_of_irqs = timer0_overflows; - uint32_t microseconds = arcv2_timer0_count_get() / FREQ_MHZ; + uint32_t no_of_irqs = timer0_overflows + msec; + uint32_t microseconds = arcv2_timer0_count_get(); - while(timer0_overflows - no_of_irqs < msec){ -// yield(); + while(timer0_overflows < no_of_irqs){ + yield(); /* Enter sleep and enable interrupts and sets interrupts threshold to 3 */ - __asm__ volatile ("sleep 0x13"); + __asm__ volatile ("sleep 0x13"); } /* For the last fraction of millisecond don't go to sleep - you'll wake up * too late - just spin */ - while (arcv2_timer0_count_get() / FREQ_MHZ < microseconds); + while ((arcv2_timer0_count_get() < microseconds) && + (timer0_overflows == no_of_irqs)); } diff --git a/system/libarc32_edu/bootcode/c_init.c b/system/libarc32_edu/bootcode/c_init.c index b38666b6..393147c4 100644 --- a/system/libarc32_edu/bootcode/c_init.c +++ b/system/libarc32_edu/bootcode/c_init.c @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include "interrupt.h" -#include "../drivers/arcv2_timer0.h" +#include "arcv2_timer0.h" /* Application main() function prototype */ extern int main (void); diff --git a/system/libarc32_edu/bootcode/init.S b/system/libarc32_edu/bootcode/init.S index f28bc69b..71dba364 100644 --- a/system/libarc32_edu/bootcode/init.S +++ b/system/libarc32_edu/bootcode/init.S @@ -46,8 +46,8 @@ _do_reset: /* Setup automatic (hardware) context saving feature */ sr AUX_IRQ_CTRL_SAVE_ALL, [ARC_V2_AUX_IRQ_CTRL] /* Enable instruction cache */ - mov r0, 0 - sr r0, [ARC_V2_IC_CTRL] + //mov r0, 0 + //sr r0, [ARC_V2_IC_CTRL] /* Jump to C init function */ j @_main diff --git a/system/libarc32_edu/bootcode/interrupt.c b/system/libarc32_edu/bootcode/interrupt.c index 4f36b127..4537484a 100644 --- a/system/libarc32_edu/bootcode/interrupt.c +++ b/system/libarc32_edu/bootcode/interrupt.c @@ -105,6 +105,9 @@ void interrupt_unit_device_init(void) aux_reg_write(ARC_V2_IRQ_TRIGGER, ARC_V2_INT_LEVEL); } + /* Setup automatic (hardware) context saving feature */ + aux_reg_write(ARC_V2_AUX_IRQ_CTRL,AUX_IRQ_CTRL_SAVE_ALL); + /* Configure the interrupt priority threshold and enable interrupts */ __builtin_arc_seti(INTERRUPT_ENABLE | INTERRUPT_THRESHOLD); } diff --git a/system/libarc32_edu/common/arcv2_timer0.h b/system/libarc32_edu/common/arcv2_timer0.h new file mode 100644 index 00000000..9e9990a2 --- /dev/null +++ b/system/libarc32_edu/common/arcv2_timer0.h @@ -0,0 +1,81 @@ +/* arcv2_timer0.h - ARC timer 0 device driver */ + +/* + * Copyright (c) 2014 Wind River Systems, Inc. + * + * The right to copy, distribute, modify or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Wind River license agreement. + */ + +#ifndef _ARCV2_TIMER0__H_ +#define _ARCV2_TIMER0__H_ + +#include +#include "aux_regs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Increments every Timer0 overflow. + * Timer0 is configured to overflow and fire an IRQ every 1 millisecond + */ +extern uint32_t volatile timer0_overflows; + +/* It is incremented every ms by Timer0 IRQ handler but it overflows at + * MAX_OVERFLOWS_US */ +extern uint32_t volatile timer0_overflows_us; + +/******************************************************************************* +* +* timer0_driver_init - initialize and enable the system clock +* +* This routine is used to program the ARCv2 timer to deliver interrupts at the +* 1 millisecond rate specified via the ONE_MILLISECOND macro. +* +* RETURNS: N/A +*/ +void timer0_driver_init(void); + + +/******************************************************************************* +* +* arcv2_timer0_count_get - get the current counter value +* +* This routine gets the value from the timer's count register. This +* value is the 'time' elapsed from the starting count (assumed to be 0). +* +* RETURNS: the current counter value +* +* \NOMANUAL +*/ +static inline __attribute__((always_inline)) +uint32_t arcv2_timer0_count_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_COUNT)); +} + + +/******************************************************************************* +* +* arcv2_timer0_control_get - get the value of CONTROL0 aux register +* +* This routine gets the value from the timer's control register. +* +* RETURNS: the value of CONTROL0 auxiliary register. +* +* \NOMANUAL +*/ +static inline __attribute__((always_inline)) +uint32_t arcv2_timer0_control_get(void) +{ + return (aux_reg_read(ARC_V2_TMR0_CONTROL)); +} + + +#ifdef __cplusplus +} +#endif + +#endif /* _ARCV2_TIMER0__H_ */ diff --git a/system/libarc32_edu/drivers/arcv2_timer1.h b/system/libarc32_edu/common/arcv2_timer1.h similarity index 100% rename from system/libarc32_edu/drivers/arcv2_timer1.h rename to system/libarc32_edu/common/arcv2_timer1.h diff --git a/system/libarc32_edu/drivers/arcv2_timer0.c b/system/libarc32_edu/drivers/arcv2_timer0.c index 746c0b79..e259ae9f 100644 --- a/system/libarc32_edu/drivers/arcv2_timer0.c +++ b/system/libarc32_edu/drivers/arcv2_timer0.c @@ -28,7 +28,6 @@ conjunction with a microkernel. */ #include "arcv2_timer0.h" -#include "aux_regs.h" #include "conf.h" #include "interrupt.h" @@ -59,49 +58,18 @@ uint32_t volatile timer0_overflows_us = 0x00; * \NOMANUAL */ -static inline __attribute__((always_inline)) void arcv2_timer0_enable - ( - uint32_t count /* count to which timer is to increment to */ - ) +static inline __attribute__((always_inline)) +void arcv2_timer0_enable(uint32_t count) { - aux_reg_write(ARC_V2_TMR0_LIMIT, count); /* write the limit value */ + /* ensure that the timer will not generate interrupts */ + aux_reg_write(ARC_V2_TMR0_CONTROL, 0); + /* write the limit value */ + aux_reg_write(ARC_V2_TMR0_LIMIT, count); /* count only when not halted for debug and enable interrupts */ aux_reg_write(ARC_V2_TMR0_CONTROL, ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE); - //aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* write the start value */ -} - -/******************************************************************************* -* -* arcv2_timer0_count_get - get the current counter value -* -* This routine gets the value from the timer's count register. This -* value is the 'time' elapsed from the starting count (assumed to be 0). -* -* RETURNS: the current counter value -* -* \NOMANUAL -*/ -inline __attribute__((always_inline)) -uint32_t arcv2_timer0_count_get(void) -{ - return (aux_reg_read(ARC_V2_TMR0_COUNT)); -} - -/******************************************************************************* -* -* arcv2_timer0_control_get - get the value of CONTROL0 aux register -* -* This routine gets the value from the timer's control register. -* -* RETURNS: the value of CONTROL0 auxiliary register. -* -* \NOMANUAL -*/ -inline __attribute__((always_inline)) -uint32_t arcv2_timer0_control_get(void) -{ - return (aux_reg_read(ARC_V2_TMR0_CONTROL)); + /* clear the count value */ + aux_reg_write(ARC_V2_TMR0_COUNT, 0); } /******************************************************************************* @@ -140,16 +108,10 @@ void _arcv2_timer0_int_handler(void) */ void timer0_driver_init(void) { - /* ensure that the timer will not generate interrupts */ - aux_reg_write(ARC_V2_TMR0_CONTROL, 0); - aux_reg_write(ARC_V2_TMR0_COUNT, 0); /* clear the count value */ - /* connect specified routine/parameter to the timer 0 interrupt vector */ interrupt_connect(ARCV2_IRQ_TIMER0, _arcv2_timer0_int_handler, 0); - /* configure timer to overflow and fire an IRQ every 1 ms */ arcv2_timer0_enable(ONE_MILLISECOND); - /* Everything has been configured. It is now safe to enable the interrupt */ interrupt_enable(ARCV2_IRQ_TIMER0); } diff --git a/system/libarc32_edu/framework/include/os/os.h b/system/libarc32_edu/framework/include/os/os.h index 34a6f950..4bb9920d 100644 --- a/system/libarc32_edu/framework/include/os/os.h +++ b/system/libarc32_edu/framework/include/os/os.h @@ -134,28 +134,6 @@ extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int time */ extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); -/** - * \brief Get the current tick in ms - * - * Return the current tick converted in milliseconds - * - * Authorized execution levels: task, fiber, ISR - * - * \return current tick converted in milliseconds - */ -extern uint32_t get_time_ms (void); - -/** - * \brief Get the current tick in us - * - * Return the current tick converted in microseconds - * - * Authorized execution levels: task, fiber, ISR - * - * \return current tick converted in microseconds - */ -extern uint64_t get_time_us (void); - /** * \brief Reserves a block of memory * @@ -209,12 +187,6 @@ extern void* balloc (uint32_t size, OS_ERR_TYPE* err); */ extern OS_ERR_TYPE bfree(void* buffer); -/** - * \brief Initialize the OS abstraction layer - * - */ -extern void os_init (void); - #endif /**@} @} @}*/ diff --git a/system/libarc32_edu/framework/include/os/os_types.h b/system/libarc32_edu/framework/include/os/os_types.h index 79b241dc..58dc4e9e 100644 --- a/system/libarc32_edu/framework/include/os/os_types.h +++ b/system/libarc32_edu/framework/include/os/os_types.h @@ -56,22 +56,8 @@ typedef enum { /** Types for kernel objects */ -typedef void* T_SEMAPHORE ; -typedef void* T_MUTEX; typedef void* T_QUEUE; typedef void* T_QUEUE_MESSAGE; -typedef void* T_TIMER; -typedef void* T_TASK ; -typedef uint8_t T_TASK_PRIO ; -#define HIGHEST_TASK_PRIO OS_SPECIFIC_HIGHEST_PRIO -#define LOWEST_TASK_PRIO OS_SPECIFIC_LOWEST_PRIO - -typedef enum { - E_TASK_UNCREATED = 0, - E_TASK_RUNNING, - E_TASK_SUSPENDED, -} T_TASK_STATE; - /** Special values for "timeout" parameter */ #define OS_NO_WAIT 0 diff --git a/system/libarc32_edu/main.c b/system/libarc32_edu/main.c index 49128c42..f4d18de5 100644 --- a/system/libarc32_edu/main.c +++ b/system/libarc32_edu/main.c @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include "drivers/soc_gpio.h" -#include "drivers/arcv2_timer1.h" +#include "arcv2_timer1.h" #include "scss_registers.h" #include "infra/ipc.h"