Commit a4fa5915 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v4.0.0.beta2'

parents c87ca048 533062b6
GPATH
GRTAGS
GTAGS
Makefile.specific
OBJS = fw-ac.o
OBJS += # add other object files that you need
OUTPUT = fw-ac
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = fw-ac.o
OBJS += # add other object files that you need
OUTPUT = fw-ac
OBJS = fw-dg.o
OBJS += # add other object files that you need
OUTPUT = fw-dg
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = fw-dg.o
OBJS += # add other object files that you need
OUTPUT = fw-dg
include ../../../project.mk
OBJS = fw-spec.o
OBJS += common/fw-spec-smem-code.o
OBJDIR += common
OUTPUT = fw-spec-1
TRTL ?= ../../../../../
TRTL_SW = $(TRTL)/software
EXTRA_CFLAGS += -I../../include
EXTRA_CFLAGS += -I../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=APPLICATION_ID
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU1)
TRTL_FW = $(TRTL)/software/firmware
vpath %.c ../
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
include $(src)/../../../project.mk
OBJS = fw-spec.o
OBJS += common/fw-spec-smem-code.o
OBJDIR += common
OUTPUT = fw-spec-1
EXTRA_CFLAGS += -I$(src)/../../include
EXTRA_CFLAGS += -I$(src)/../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=APPLICATION_ID
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU1)
vpath %.c $(src)../
include ../../../project.mk
OBJS = fw-spec.o
OBJS += common/fw-spec-smem-code.o
OBJDIR += common
OUTPUT = fw-spec-2
TRTL ?= ../../../../../
TRTL_SW = $(TRTL)/software
EXTRA_CFLAGS += -I../../include
EXTRA_CFLAGS += -I../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=$(APPLICATION_ID)
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU2)
EXTRA_CFLAGS += -DLIBRT_DEBUG_VERBOSE -DLIBRT_DEBUG
TRTL_FW = $(TRTL)/software/firmware
vpath %.c ../
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
include $(src)../../../project.mk
OBJS = fw-spec.o
OBJS += common/fw-spec-smem-code.o
OBJDIR += common
OUTPUT = fw-spec-2
EXTRA_CFLAGS += -I$(src)/../../include
EXTRA_CFLAGS += -I$(src)/../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=$(APPLICATION_ID)
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU2)
EXTRA_CFLAGS += -DLIBRT_DEBUG_VERBOSE -DLIBRT_DEBUG
vpath %.c $(src)/../
include ../../../project.mk
OBJS = fw-svec.o
OBJS += common/fw-svec-smem-code.o
OBJDIR += common
OUTPUT = fw-svec-1
TRTL ?= ../../../../../
TRTL_SW = $(TRTL)/software
EXTRA_CFLAGS += -I../../include
EXTRA_CFLAGS += -I../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=APPLICATION_ID
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU1)
TRTL_FW = $(TRTL)/software/firmware
vpath %.c ../
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
include $(src)/../../../project.mk
OBJS = fw-svec.o
OBJS += common/fw-svec-smem-code.o
OBJDIR += common
OUTPUT = fw-svec-1
EXTRA_CFLAGS += -I$(src)/../../include
EXTRA_CFLAGS += -I$(src)/../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=APPLICATION_ID
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU1)
vpath %.c $(src)/../
include ../../../project.mk
OBJS = fw-svec.o
OBJS += common/fw-svec-smem-code.o
OBJDIR += common
OUTPUT = fw-svec-2
TRTL ?= ../../../../../
TRTL_SW = $(TRTL)/software
EXTRA_CFLAGS += -I../../include
EXTRA_CFLAGS += -I../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=$(APPLICATION_ID)
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU2)
EXTRA_CFLAGS += -DLIBRT_DEBUG_VERBOSE -DLIBRT_DEBUG
TRTL_FW = $(TRTL)/software/firmware
vpath %.c ../
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
include $(src)/../../../project.mk
OBJS = fw-svec.o
OBJS += common/fw-svec-smem-code.o
OBJDIR += common
OUTPUT = fw-svec-2
EXTRA_CFLAGS += -I$(src)/../../include
EXTRA_CFLAGS += -I$(src)/../common
EXTRA_CFLAGS += -DFPGA_APPLICATION_ID=$(APPLICATION_ID)
EXTRA_CFLAGS += -DRT_APPLICATION_ID=$(RT_APPLICATION_ID_CPU2)
EXTRA_CFLAGS += -DLIBRT_DEBUG_VERBOSE -DLIBRT_DEBUG
vpath %.c $(src)/../
OBJS = fw-hello.o
OBJS += # add other object files that you need
OUTPUT = fw-hello
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = fw-hello.o
OBJS += # add other object files that you need
OUTPUT = fw-hello
OBJS = fw-hellofrm.o
OBJS += # add other object files that you need
OUTPUT = fw-hellofrm
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = fw-hellofrm.o
OBJS += # add other object files that you need
OUTPUT = fw-hellofrm
......@@ -132,7 +132,9 @@ The typical procedure to send (output) messages over is the following.
#. *claim* a mq in order to get exclusive access to it;
#. *map* the claimed mq slot in order to get the buffer where to write;
#. *map* the claimed mq slot in order to get the buffer where to write. Keep
in mind that the memory is not initialized, so you probably need to set
correctly all header fields;
#. *write* your message;
......
......@@ -43,11 +43,10 @@ memory) or you need much better performances: don't use this framework.
All the Mock Turtle firmware source code can be found in the directory
``/path/to/mockturtle/software/firmware/``.
Mock Turtle has a generic ``Makefile`` that you should include in your
``Makefile`` in order to import all the Mock Turtle building rules.::
TRTL ?= /path/to/mcokturtle/
TRTL_SW = $(TRTL)/software
Mock Turtle has a generic building system which can be used to produce
Mock Turtle firmware applications. What you have to do is to prepare a
file named ``TBuild`` next to your firmware source files. In this file
you have to specify what to build, for example::
# Mandatory
OBJS = source1.o
......@@ -56,15 +55,10 @@ Mock Turtle has a generic ``Makefile`` that you should include in your
OUTPUT = firmware-name
# Optional (prefer default when possible)
CFLAGS_OPT := -O1
CFLAGS_DBG := -ggdb
EXTRA_CFLGAS :=
EXTRA_CFLAGS :=
MOCKTURTLE_LDSCRIPT := myfirmware.ld
# INCLUDE GENERIC Makefile
include $(TRTL_SW)/firmware/Makefile
Here the list of supported Makefile environment variables
Here the list of supported TBuild variables
OBJS
(Mandatory) List of object files to generate from sources with
......@@ -74,10 +68,33 @@ OBJS
OUTPUT
(Mandatory) Final binary name (the firmware).
EXTRA_CFLAGS
(Optional) Additional compiler options.
MOCKTURTLE_LDSCRIPT
(Optional ) Local path to the linker script.
(Optional) Local path to the linker script.
The default is the standard Mock Turtle linker script.
You can build such firmware application by calling ``make`` from the
application directory (where the ``TBuild`` file is) like this::
make -C <path-to-mockturtle-project>/software/firmware M=$PWD
Or alternatively, you can copy the following lines in a Makefile::
TRTL_FW = $(TRTL)/software/firmware
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
Then, you will compile your application with the following command
from the application directory (where the ``TBuild`` file is)::
make TRTL=<path-to-mockturtle-project>
Memory resources on Mock Turtle are very limited and the full framework
may take more space than needed. For this reason Mock Turtle has
*Kconfig* support which allows you to interactivly enable/disable both
......
......@@ -207,17 +207,15 @@ The Mock Turtle driver exports a *char device* for each Mock Turtle HMQ.
All HMQ instances will appear as child of a :ref:`sw:drv:core`; in
*/dev/mockturtle/* you will have devices named ``trtl-%04x-%02d-%02d``
(trtl-<device-id>-<cpu-index>-<hmq-index>). The main purpose of this
interface is to exchange messages and configure filters. These devices
are bidirectional, so you can: ``write(2)`` to send messaged;
``read(2)`` to receive messages; ``poll(2)`` or ``select(2)`` to wait
for the interface to become accessible.
interface is to exchange messages. These devices are bidirectional,
so you can: ``write(2)`` to send messaged; ``read(2)`` to receive messages;
``poll(2)`` or ``select(2)`` to wait for the interface to become accessible.
This *char device* provides a set of ``ioctl(2)`` commands:
.. doxygendefine:: TRTL_IOCTL_MSG_FILTER_ADD
.. doxygendefine:: TRTL_IOCTL_MSG_FILTER_CLEAN
.. doxygendefine:: TRTL_IOCTL_HMQ_SYNC_SET
.. doxygendefine:: TRTL_IOCTL_MSG_SYNC_ABORT
You can find the HMQ *sysfs* attributes at::
......
......@@ -153,21 +153,6 @@ side so that the complete flush happens synchronously.
.. doxygenfunction:: trtl_hmq_flush
The Mock Turtle driver has a basic message filtering mechanism. Each
user can add a set of filters on any host message queue. Mock Turtle
will deliver to the user only those messages which pass the filter.
Rembember that this is a user filter, this means that on the same
host message queue, different users can have different filters.
.. doxygenfunction:: trtl_hmq_filter_add
.. doxygenfunction:: trtl_hmq_filter_clean
.. doxygenenum:: trtl_msg_filter_operation_type
.. doxygenstruct:: trtl_msg_filter
:members:
Then, there are the functions to exchange messages with firmware
running on Mock Turtle. This API offers a minimum set of function
to allow users to send/receive synchronous/asynchronous messages.
......
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
CURDIR:=$(shell /bin/pwd)
......
-include Makefile.specific
-include $(CURDIR)/.config
src := $(M)/
-include $(src)/Makefile.specific
-include $(src)/.config
include $(src)/TBuild
# Validation
ifndef OUTPUT
$(error Missing variable OUTPUT)
else
build_output=$(src)/$(OUTPUT)
endif
ifndef OBJS
$(error Missing variable OBJS)
endif
# and don't touch the rest unless you know what you're doing.
INSTALL_PREFIX ?= .
......@@ -7,11 +22,12 @@ PATH_COMMON_RT ?= .
PATH_COMMON_H ?= ../include
TRTL ?= ../../
TRTL_SW ?= $(TRTL)/software
TRTL_FW ?= $(TRTL_SW)/firmware
TRTL_HDL ?= $(TRTL)/hdl/rtl
RT_GIT_VERSION = 0x$(shell git rev-parse --short=8 HEAD || echo "0") # empty if git is not there
# header file generated from the .config
AUTOCONF = $(CURDIR)/$(BUILDDIR)/include/generated/autoconf.h
AUTOCONF = $(src)/$(BUILDDIR)/include/generated/autoconf.h
CROSS_COMPILE_TARGET ?= riscv32-elf-
# FIXME the mult/div is broken, for the time being remove it completely
......@@ -21,10 +37,12 @@ CFLAGS += -mabi=ilp32 -march=rv32im -ffunction-sections -fdata-sections
LDFLAGS += -lgcc -lc -Wl,--gc-sections
# provide search patch for sources
vpath %.c $(TRTL)/software/firmware
vpath %.S $(TRTL)/software/firmware
vpath %.c $(TRTL_FW)
vpath %.S $(TRTL_FW)
vpath %.c $(src)
vpath %.S $(src)
BUILDDIR := build
BUILDDIR := $(src)/build
# auto dependency generation from
# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
......@@ -42,10 +60,10 @@ STRIP = $(CROSS_COMPILE_TARGET)strip
CFLAGS += -Wall -D__TRTL_FIRMWARE__ -DARCH=urv
CFLAGS += -I.
CFLAGS += -I$(BUILDDIR)/include
CFLAGS += -I$(TRTL)/software/firmware
CFLAGS += -I$(TRTL)/software/firmware/lib
CFLAGS += -I$(TRTL)/software/firmware/framework
CFLAGS += -I$(TRTL)/software/include
CFLAGS += -I$(TRTL_FW)
CFLAGS += -I$(TRTL_FW)/lib
CFLAGS += -I$(TRTL_FW)/framework
CFLAGS += -I$(TRTL_SW)/include
CFLAGS += -DGIT_VERSION=$(RT_GIT_VERSION)
# used for firmware by trtl-project-creator
......@@ -76,11 +94,11 @@ OBJS_BUILD = $(addprefix $(BUILDDIR)/, $(OBJS))
OBJDIR_BUILD = $(addprefix $(BUILDDIR)/, $(OBJDIR))
OBJDIR_BUILD += $(BUILDDIR)
MOCKTURTLE_LDSCRIPT ?= $(TRTL)/software/firmware/urv/mockturtle.ld
MOCKTURTLE_LDSCRIPT ?= $(TRTL_FW)/urv/mockturtle.ld
.PHONY: all clean cleanall check-def
.PHONY: all clean cleanall
all: $(OUTPUT).bin
all: $(build_output).bin
WBGEN_FILES = mt_cpu_csr.wb mt_cpu_lr.wb
WBGEN_HEADERS = mockturtle_cpu_csr.h mockturtle_cpu_lr.h
......@@ -93,16 +111,9 @@ $(TRTL_SW)/include/hw/mockturtle_cpu_csr.h: $(TRTL_HDL)/cpu/mt_cpu_csr.wb
$(TRTL_SW)/include/hw/mockturtle_cpu_lr.h: $(TRTL_HDL)/cpu/mt_cpu_lr.wb
# target to let wbgen2 to generate headers
$(addprefix $(TRTL_SW)/include/hw/,$(WBGEN_HEADERS)): | check-def
$(addprefix $(TRTL_SW)/include/hw/,$(WBGEN_HEADERS)):
make -C $(TRTL_SW) headers
# check needed env variables
check-def:
ifndef OUTPUT
@echo "OUTPUT variable is mandatory"
@exit 1
endif
# create dirs for object files
$(OBJDIR_BUILD):
mkdir -p $@
......@@ -112,12 +123,13 @@ $(OBJDIR_BUILD):
$(OBJS_BUILD): | $(AUTOCONF)
$(OBJS_BUILD): | $(OBJDIR_BUILD)
$(OUTPUT).elf: $(addprefix $(TRTL_SW)/include/hw/,$(WBGEN_HEADERS)) $(MOCKTURTLE_LDSCRIPT) $(OBJS_BUILD) | check-def
${CC} $(CFLAGS) $(LDFLAGS) -o $(OUTPUT).elf -nostartfiles $(OBJS_BUILD) -T $(MOCKTURTLE_LDSCRIPT)
$(build_output).elf: $(addprefix $(TRTL_SW)/include/hw/,$(WBGEN_HEADERS)) $(MOCKTURTLE_LDSCRIPT) $(OBJS_BUILD)
$(warning $(OBJS_BUILD))
${CC} $(CFLAGS) $(LDFLAGS) -o $(build_output).elf -nostartfiles $(OBJS_BUILD) -T $(MOCKTURTLE_LDSCRIPT)
$(OUTPUT).bin: $(OUTPUT).elf | check-def
${OBJCOPY} --remove-section .smem -O binary $(OUTPUT).elf $(OUTPUT).bin
$(SIZE) $(OUTPUT).elf
$(build_output).bin: $(build_output).elf
${OBJCOPY} --remove-section .smem -O binary $(build_output).elf $(build_output).bin
$(SIZE) $(build_output).elf
$(BUILDDIR)/urv/emulate.o: urv/emulate.c
${CC} $(DEPFLAGS) $(CFLAGS) -march=rv32i -c $< -o $@
......@@ -126,13 +138,15 @@ $(BUILDDIR)/urv/emulate.o: urv/emulate.c
$(BUILDDIR)/%.o: %.c
${CC} $(DEPFLAGS) $(CFLAGS) $(LDFLAGS) -c $< -o $@
$(POSTCOMPILE)
$(warning $< ---- $@)
@echo ""
$(BUILDDIR)/%.o: %.S
${CC} $(DEPFLAGS) $(CFLAGS) $(LDFLAGS) -c $< -o $@
$(POSTCOMPILE)
clean:
rm -f $(OUTPUT).bin $(OUTPUT).elf
rm -f $(build_output).bin $(build_output).elf
rm -rf $(BUILDDIR)
clean-dot-config:
......@@ -141,7 +155,7 @@ clean-dot-config:
cleanall: clean clean-dot-config
install:
@cp $(OUTPUT).bin $(INSTALL_PREFIX)
@cp $(build_output).bin $(INSTALL_PREFIX)
# inlude *.d files from the build directory
include $(wildcard $(patsubst %,%/.d/*.d,$(basename $(OBJDIR_BUILD))))
......@@ -150,20 +164,20 @@ include $(wildcard $(patsubst %,%/.d/*.d,$(basename $(OBJDIR_BUILD))))
# following targets from Makefile.kconfig
# this one is used to generate autoconf.h file
$(AUTOCONF) silentoldconfig: .config | $(BUILDDIR)
export KCONFIG_CONFIG=$(CURDIR)/.config; \
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(CURDIR)/Kconfig projtree=$(CURDIR)/$(BUILDDIR) -C $(TRTL)/software/firmware -f Makefile.kconfig silentoldconfig
export KCONFIG_CONFIG=$(src)/.config; \
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(src)/Kconfig projtree=$(BUILDDIR) -C $(TRTL_FW) -f Makefile.kconfig silentoldconfig
scripts_basic config:
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(CURDIR)/Kconfig projtree=$(CURDIR) -C $(TRTL)/software/firmware -f Makefile.kconfig $@
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(src)/Kconfig projtree=$(src) -C $(TRTL_FW) -f Makefile.kconfig $@
%config:
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(CURDIR)/Kconfig projtree=$(CURDIR) -C $(TRTL)/software/firmware -f Makefile.kconfig $@
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(src)/Kconfig projtree=$(src) -C $(TRTL_FW) -f Makefile.kconfig $@
defconfig:
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(CURDIR)/Kconfig projtree=$(CURDIR) -C $(TRTL)/software/firmware -f Makefile.kconfig mt_defconfig
$(MAKE) quiet=quiet_ KBUILD_KCONFIG=$(src)/Kconfig projtree=$(src) -C $(TRTL_FW) -f Makefile.kconfig mt_defconfig
.config: ;
# Explicit rule for .config
# needed since -include XXX triggers build for XXX
$(CURDIR)/.config: ;
$(src)/.config: ;
......@@ -303,7 +303,6 @@ static inline int rt_action_run(enum trtl_mq_type type,
msg_out.header->msg_id = 0;
msg_out.header->len = 0;
msg_out.header->sync_id = msg->header->sync_id;
msg_out.header->seq = app.seq++;
err = action(msg, &msg_out);
if (err)
......@@ -408,7 +407,6 @@ int trtl_fw_mq_send_uint32(enum trtl_mq_type type,
msg.header->flags = 0;
msg.header->msg_id = msg_id;
msg.header->len = n;
msg.header->seq = app.seq++;
va_start(ap, n);
for (i = 0; i < msg.header->len;++i)
......@@ -455,7 +453,6 @@ int trtl_fw_mq_send_buf(enum trtl_mq_type type,
msg.header->flags = 0;
msg.header->msg_id = msg_id;
msg.header->len = n / 4;
msg.header->seq = app.seq++;
memcpy(msg.payload, data, n);
mq_send(type, idx_mq);
......@@ -603,7 +600,6 @@ int trtl_fw_init(void)
{
int err;
app.seq = 0;
app.cfgrom = trtl_config_rom_get();
err = trtl_fw_init_action(&app);
......
......@@ -88,7 +88,6 @@ struct trtl_fw_application {
trtl_fw_action_t **actions; /**< list of custum actions */
unsigned int n_actions; /**< number of custum actions */
unsigned int seq; /** sequence number reference */
const struct trtl_config_rom *cfgrom; /**< configuration ROM */
/* Operations */
......
......@@ -400,6 +400,8 @@ static inline uint32_t mq_readl(enum trtl_mq_type type, uint32_t reg)
* @param[in] type MQ type to use
* @param[in] slot slot number
* @return pointer to the input buffer
*
* Note: uninitialized memory
*/
static inline void *mq_map_out_buffer(enum trtl_mq_type type, int slot)
{
......@@ -426,6 +428,8 @@ static inline void *mq_map_in_buffer(enum trtl_mq_type type, int slot)
* @param[in] type MQ type to use
* @param[in] slot slot number
* @return pointer to the output header
*
* Note: uninitialized memory
*/
static inline void *mq_map_out_header(enum trtl_mq_type type, int slot)
{
......
HEADERS := mockturtle_cpu_csr.h
HEADERS += mockturtle_cpu_lr.h
TRTL = ../../..
TRTL ?= ../../..
TRTL_HDL = $(TRTL)/hdl/rtl/
WBGEN2 ?= wbgen2
......
......@@ -6,6 +6,8 @@
/* Host addresses. */
#define TRTL_ADDR_OFFSET_HMQ 0x00000000
#define TRTL_ADDR_OFFSET_CSR 0x0000C000
/* FIXME update memorymap to have DBG PAGE_ALIGNED */
#define TRTL_ADDR_OFFSET_DBG TRTL_ADDR_OFFSET_CSR
#define TRTL_ADDR_OFFSET_CONFIG_ROM 0x0000E000
#define TRTL_ADDR_OFFSET_SHM 0x00010000
......
......@@ -179,35 +179,6 @@ enum trtl_smem_modifier {
TRTL_SMEM_TYPE_TST_SET, /**< atomic test and set */
};
/**
* @enum trtl_msg_filter_operation_type
* List of available filter's operations
*/
enum trtl_msg_filter_operation_type {
TRTL_MSG_FILTER_AND,
TRTL_MSG_FILTER_OR,
TRTL_MSG_FILTER_EQ,
TRTL_MSG_FILTER_NEQ,
__TRTL_MSG_FILTER_MAX,
};
#define TRTL_MSG_FILTER_FLAG_HEADER (0)
#define TRTL_MSG_FILTER_FLAG_PAYLOAD (1 << 0)
/**
* It describe a filter to apply to messages
*/
struct trtl_msg_filter {
unsigned long flags; /**< options for the filter */
uint32_t operation; /**< kind of operation to perform */
uint32_t word_offset; /**< offset of the word to check */
uint32_t mask; /**< mask to apply before the operation */
uint32_t value; /**< second operand of the operation */
};
/**
* Descriptor of the IO operation on Shared Memory
*/
......@@ -225,8 +196,8 @@ struct trtl_smem_io {
*/
enum trtl_ioctl_commands {
TRTL_SMEM_IO, /**< access to shared memory */
TRTL_MSG_FILTER_ADD, /**< add a message filter */
TRTL_MSG_FILTER_CLEAN, /**< remove all filters */
TRTL_MSG_SYNC_ABORT, /**< abort sync message*/
TRTL_HMQ_SYNC_SET, /**< Mark HMQ user context as synchronous */
};
......@@ -235,19 +206,19 @@ enum trtl_ioctl_commands {
struct trtl_smem_io)
/**
* The IOCTL command to add a filter to an HMQ
* The IOCTL command to set HMQ user context to SYNC
*/
#define TRTL_IOCTL_MSG_FILTER_ADD _IOW(TRTL_IOCTL_MAGIC, \
TRTL_MSG_FILTER_ADD, \
struct trtl_msg_filter)
#define TRTL_IOCTL_HMQ_SYNC_SET _IOW(TRTL_IOCTL_MAGIC, \
TRTL_HMQ_SYNC_SET, \
unsigned int)
/**
* The IOCTL command to remove all filters from an HMQ
* The IOCTL command to abort a sync message. Usefull when the answer is
* not coming
*/
#define TRTL_IOCTL_MSG_FILTER_CLEAN _IOW(TRTL_IOCTL_MAGIC, \
TRTL_MSG_FILTER_CLEAN, \
struct trtl_msg_filter)
#define TRTL_IOCTL_MSG_SYNC_ABORT _IOW(TRTL_IOCTL_MAGIC, \
TRTL_MSG_SYNC_ABORT, \
unsigned int)
/**
* Messages descriptor for host
......
......@@ -26,6 +26,8 @@ mockturtle-y += mockturtle-cpu.o
mockturtle-y += mockturtle-hmq.o
mockturtle-y += mockturtle-tty.o
mockturtle-y += mockturtle-dbg.o
mockturtle-y += mockturtle-dbg.o
mockturtle-y += mockturtle-compat.o
all modules:
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/version.h>
#include <linux/mm.h>
#include "mockturtle-compat.h"
#if KERNEL_VERSION(3, 9, 0) > LINUX_VERSION_CODE
/* Copied from v3.10 */
int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start,
unsigned long len)
{
unsigned long vm_len, pfn, pages;
/* Check that the physical memory area passed in looks valid */
if (start + len < start)
return -EINVAL;
/*
* You *really* shouldn't map things that aren't page-aligned,
* but we've historically allowed it because IO memory might
* just have smaller alignment.
*/
len += start & ~PAGE_MASK;
pfn = start >> PAGE_SHIFT;
pages = (len + ~PAGE_MASK) >> PAGE_SHIFT;
if (pfn + pages < pfn)
return -EINVAL;
/* We start the mapping 'vm_pgoff' pages into the area */
if (vma->vm_pgoff > pages)
return -EINVAL;
pfn += vma->vm_pgoff;
pages -= vma->vm_pgoff;
/* Can we fit all of the mapping? */
vm_len = vma->vm_end - vma->vm_start;
if (vm_len >> PAGE_SHIFT > pages)
return -EINVAL;
/* Ok, let it rip */
return io_remap_pfn_range(vma, vma->vm_start, pfn, vm_len, vma->vm_page_prot);
}
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
{
return devm_request_and_ioremap(dev, res);
}
#endif
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014-2016 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __TRTL_COMPAT_H__
#define __TRTL_COMPAT_H__
#include <linux/version.h>
#include <linux/device.h>
#include <linux/ioport.h>
#if KERNEL_VERSION(3, 9, 0) > LINUX_VERSION_CODE
int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start,
unsigned long len);
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
#endif
#endif
......@@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/idr.h>
#include <linux/init.h>
......@@ -29,6 +30,7 @@
#include <hw/mockturtle_queue.h>
#include "mockturtle-drv.h"
#include "mockturtle-compat.h"
static DEFINE_IDA(trtl_ida);
......@@ -497,7 +499,7 @@ static unsigned int trtl_max_irq_loop(struct trtl_dev *trtl)
int i, k;
for (i = 0; i < trtl->cfgrom.n_cpu; ++i) {
for (k = 0; k < trtl->cfgrom.n_cpu; ++k) {
for (k = 0; k < trtl->cfgrom.n_hmq[i]; ++k) {
size = TRTL_CONFIG_ROM_MQ_SIZE_ENTRIES(trtl->cfgrom.hmq[i][k].sizes);
if (size > max)
max = size;
......@@ -529,7 +531,7 @@ int trtl_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, trtl);
r = platform_get_resource(pdev, IORESOURCE_MEM, TRTL_MEM_BASE);
trtl->base_core = devm_request_and_ioremap(&pdev->dev, r);
trtl->base_core = devm_ioremap_resource(&pdev->dev, r);
if (!trtl->base_core)
return -EADDRNOTAVAIL;
trtl->base_csr = trtl->base_core + TRTL_ADDR_OFFSET_CSR;
......
......@@ -8,9 +8,11 @@
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/mm.h>
#include <hw/mockturtle_cpu_csr.h>
#include "mockturtle-drv.h"
#include "mockturtle-compat.h"
static int trtl_dbg_info_seq_read(struct seq_file *s, void *data)
{
......@@ -54,12 +56,16 @@ static int trtl_dbg_info_seq_read(struct seq_file *s, void *data)
hmq->index);
seq_printf(s, " name: %s\n",
dev_name(&hmq->dev));
seq_printf(s, " buf-in-max: %d\n",
hmq->buf_in.entries);
list_for_each_entry(usr, &hmq->list_usr, list) {
seq_printf(s, " buf-in-r: %d (user %p)\n",
usr->idx_r, usr);
}
seq_printf(s, " buf-in-w: %d\n",
hmq->buf_in.idx_w);
seq_printf(s, " buf-out-max: %d\n",
hmq->buf_out.entries);
seq_printf(s, " buf-out-r: %d\n",
hmq->buf_out.idx_r);
seq_printf(s, " buf-out-w: %d\n",
......@@ -83,6 +89,47 @@ static const struct file_operations trtl_dbg_info_ops = {
.release = single_release,
};
#define TRTL_DBG_PORT_SIZE PAGE_SIZE /* FIXME */
static int trtl_dbg_debugger_mmap(struct file *file,
struct vm_area_struct *vma)
{
struct trtl_dev *trtl = file->private_data;
unsigned long vsize;
int ret;
struct platform_device *pdev = to_platform_device(trtl->dev.parent);
struct resource *r;
if (vma->vm_pgoff > 0) {
dev_err(&trtl->dev,
"Debug Port mapping does not accept offsets\n");
return -EINVAL;
}
vsize = vma->vm_end - vma->vm_start;
if (vsize > TRTL_DBG_PORT_SIZE) {
dev_err(&trtl->dev,
"Debug Port mapping can't map more that %lu bytes (%lu)\n",
TRTL_DBG_PORT_SIZE, vsize);
return -EINVAL;
}
vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
r = platform_get_resource(pdev, IORESOURCE_MEM, TRTL_MEM_BASE);
return vm_iomap_memory(vma,
r->start + TRTL_ADDR_OFFSET_DBG,
TRTL_DBG_PORT_SIZE);
return ret;
}
static const struct file_operations trtl_dbg_debugger_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.mmap = trtl_dbg_debugger_mmap,
};
/**
* It creates the debug info file
......@@ -90,6 +137,8 @@ static const struct file_operations trtl_dbg_info_ops = {
*/
void trtl_debugfs_init(struct trtl_dev *trtl)
{
char name[20];
trtl->dbg_dir = debugfs_create_dir(dev_name(&trtl->dev), NULL);
if (IS_ERR_OR_NULL(trtl->dbg_dir)) {
dev_err(&trtl->dev, "Cannot create debugfs\n");
......@@ -106,6 +155,16 @@ void trtl_debugfs_init(struct trtl_dev *trtl)
PTR_ERR(trtl->dbg_info));
trtl->dbg_info = NULL;
}
snprintf(name, 20, "%s-dbg", dev_name(&trtl->dev));
trtl->debugger = debugfs_create_file(name, 0664, trtl->dbg_dir,
trtl, &trtl_dbg_debugger_ops);
if (IS_ERR_OR_NULL(trtl->debugger)) {
dev_err(&trtl->dev,
"Cannot create debugfs file: %ld\n",
PTR_ERR(trtl->debugger));
trtl->debugger = NULL;
}
}
......
......@@ -75,11 +75,6 @@ static inline uint32_t trtl_get_sequence(struct trtl_msg *msg)
return msg->data[1];
}
struct trtl_msg_filter_element {
struct trtl_msg_filter filter;
struct list_head list;
};
struct trtl_memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
......@@ -117,6 +112,7 @@ struct mturtle_hmq_buffer {
spinlock_t lock;
unsigned int idx_w;
unsigned int idx_r;
unsigned int entries;
struct trtl_msg *msg;
struct trtl_msg msg_tmp;
unsigned int count;
......@@ -177,13 +173,22 @@ struct trtl_hmq {
struct mturtle_hmq_buffer buf_out;
};
/**
* User do only sync messages
*/
#define TRTL_HMQ_USER_FLAG_SYNC BIT(0)
/**
* User is waiting for a sync answer message
*/
#define TRTL_HMQ_USER_FLAG_SYNC_WAIT BIT(1)
/**
* It describes the consumer of the output slot
* @list: to keep it in our local queue
* @hmq: reference to opened HMQ
* @lock: to protect filters
* @list_filters: list of filters to apply
* @n_filters: number of filters
* @lock: to protect flags, wait_id
* @idx_r: index read pointer for the message circular buffer. This is
* protected by the input buffer lock. Accessing this field means that
* you are accessing the buffer input, and in order to do that you need
......@@ -194,9 +199,9 @@ struct trtl_hmq_user {
struct list_head list;
struct trtl_hmq *hmq;
spinlock_t lock;
struct list_head list_filters;
unsigned int n_filters;
unsigned long flags;
unsigned int idx_r;
uint16_t wait_id;
};
......@@ -244,7 +249,8 @@ struct trtl_cpu {
* @irq_mask: IRQ mask in use
* @message_sequence: message sequence number
* @lock_cpu_sel:
* @lock_hmq_sel:
* @lock_hmq_sel: protects the HMQ usage which is based on selection. It
* protects also last_seq which is related to HMQ messages
* @cfgrom: synthesis configuration ROM
* @tty_driver:
* @memops:
......@@ -252,6 +258,9 @@ struct trtl_cpu {
* @dbg_info: information
* @max_irq_loop: maximum number of messages that can be retrieved with a
* single IRQ. It regulates the HMQ IRQ handler
* @last_seq: last sequence number used. The same seq number cannot be used
* on different HMQ on the same CPU
* @debugger: debugfs interface for gdbserver
*/
struct trtl_dev {
struct device dev;
......@@ -272,6 +281,8 @@ struct trtl_dev {
struct dentry *dbg_dir;
struct dentry *dbg_info;
unsigned int max_irq_loop;
uint32_t last_seq;
struct dentry *debugger;
};
static inline u32 trtl_ioread(struct trtl_dev *trtl, void *addr)
......
......@@ -7,7 +7,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
......@@ -22,13 +21,75 @@
#include <hw/mockturtle_cpu_csr.h>
#include "mockturtle-drv.h"
static int hmq_buf_max_msg = 16;
module_param_named(hmq_buf_max_msg, hmq_buf_max_msg, int, 0444);
MODULE_PARM_DESC(hmq_buf_max_msg,
"Maximum number of messages buffered for input and for output.");
static void trtl_message_push(struct trtl_hmq *hmq, struct trtl_msg *msg);
static bool trtl_hmq_user_is_sync(struct trtl_hmq_user *user)
{
return (user->flags & TRTL_HMQ_USER_FLAG_SYNC);
}
static bool trtl_hmq_user_is_waiting(struct trtl_hmq_user *user)
{
return (user->flags & TRTL_HMQ_USER_FLAG_SYNC_WAIT);
}
static bool trtl_msg_is_ack(struct trtl_msg *msg)
{
return (msg->hdr.flags & TRTL_HMQ_HEADER_FLAG_ACK);
}
static bool trtl_hmq_user_msg_is_answer(struct trtl_hmq_user *user,
struct trtl_msg *msg)
{
return (user->wait_id == msg->hdr.sync_id);
}
/**
* Set the user in waiting mode for a sync answer
* @user user
* @sync_id synchronous message identifier
*
* Return: 0 on success otherwise a negative error number
*/
static void trtl_hmq_user_sync_wait_set(struct trtl_hmq_user *user,
uint16_t id)
{
spin_lock(&user->lock);
user->flags |= TRTL_HMQ_USER_FLAG_SYNC_WAIT;
user->wait_id = id;
spin_unlock(&user->lock);
}
/**
* User received the messages, do not wait anymore
* @user user
*/
static void trtl_hmq_user_sync_wait_clr(struct trtl_hmq_user *user)
{
spin_lock(&user->lock);
user->flags &= ~TRTL_HMQ_USER_FLAG_SYNC_WAIT;
spin_unlock(&user->lock);
}
/**
* Get a valid sequence number
* @trtl: trtl device
*
* Return a valid sequence number
*/
static int trtl_msg_seq_get(struct trtl_dev *trtl)
{
unsigned long flags;
int seq;
spin_lock_irqsave(&trtl->lock_hmq_sel, flags);
seq = trtl->last_seq++;
spin_unlock_irqrestore(&trtl->lock_hmq_sel, flags);
return seq;
}
/**
* It is an opaque type. It is used to avoid mistakes when using features
* that requires the HMQ selection. If you do not provide this type (which is
......@@ -309,60 +370,25 @@ static void trtl_hmq_flush(struct trtl_hmq *hmq)
mutex_unlock(&hmq->mtx);
}
/**
* It applies filters on a given message.
* @user HMQ user instance from which we take filters
* @msg the message to filter
* Tell if user accepts the given message
* @user: HMQ user
* @msg: message
*
* Return: 1 if the message passes all filters, otherwise 0
* Return: true if the user can read the message, otherwise false
*/
static int trtl_hmq_filter_check(struct trtl_hmq_user *user,
struct trtl_msg *msg)
static bool trtl_hmq_user_filter_one(struct trtl_hmq_user *user,
struct trtl_msg *msg)
{
struct trtl_msg_filter_element *fltel, *tmp;
unsigned int passed = 1;
uint32_t word, *data = msg->data, *head = (uint32_t *)&msg->hdr;
unsigned long flags;
spin_lock_irqsave(&user->lock, flags);
list_for_each_entry_safe(fltel, tmp, &user->list_filters, list) {
/* If one of the previous filter failed, then stop */
if (!passed)
break;
if (fltel->filter.flags & TRTL_MSG_FILTER_FLAG_PAYLOAD)
word = data[fltel->filter.word_offset];
else
word = head[fltel->filter.word_offset];
switch (fltel->filter.operation) {
case TRTL_MSG_FILTER_AND:
word &= fltel->filter.mask;
if (word != fltel->filter.value)
passed = 0;
break;
case TRTL_MSG_FILTER_OR:
word |= fltel->filter.mask;
if (word != fltel->filter.value)
passed = 0;
break;
case TRTL_MSG_FILTER_EQ:
if (word != fltel->filter.value)
passed = 0;
break;
case TRTL_MSG_FILTER_NEQ:
if (word == fltel->filter.value)
passed = 0;
break;
}
if (trtl_hmq_user_is_sync(user)) {
return trtl_hmq_user_is_waiting(user) &&
trtl_msg_is_ack(msg) &&
trtl_hmq_user_msg_is_answer(user, msg);
}
spin_unlock_irqrestore(&user->lock, flags);
return passed;
return !trtl_msg_is_ack(msg);
}
/**
* It clears the content of the HMQ
*/
......@@ -512,6 +538,11 @@ static const struct attribute_group *trtl_hmq_groups[] = {
NULL,
};
static unsigned int trtl_hmq_buf_idx_get(struct mturtle_hmq_buffer *buf,
unsigned int idx_r_absolute)
{
return idx_r_absolute & (buf->entries - 1);
}
/**
* It simply opens a HMQ device
......@@ -531,7 +562,6 @@ static int trtl_hmq_open(struct inode *inode, struct file *file)
user->hmq = hmq;
spin_lock_init(&user->lock);
INIT_LIST_HEAD(&user->list_filters);
/* Add new user to the list */
spin_lock_irqsave(&hmq->lock, flags);
......@@ -573,15 +603,20 @@ static int trtl_hmq_release(struct inode *inode, struct file *f)
* @buf: source buffer
* Return: 0 on success, otherwise a negative error number
*/
static int trtl_hmq_write_one(struct trtl_hmq *hmq,
static int trtl_hmq_write_one(struct trtl_hmq_user *user,
const char __user *ubuf)
{
struct trtl_hmq *hmq = user->hmq;
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent->parent);
struct trtl_msg *msg;
int err = 0, copy_size;
size_t size;
struct mturtle_hmq_buffer *buf = &hmq->buf_out;
unsigned long flags;
if (trtl_hmq_user_is_sync(user) && trtl_hmq_user_is_waiting(user))
return -EBUSY;
/* Here we can safely sleep */
size = TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(hmq->cfg->sizes);
copy_size = sizeof(struct trtl_hmq_header);
......@@ -597,15 +632,24 @@ static int trtl_hmq_write_one(struct trtl_hmq *hmq,
return -EINVAL;
}
buf->msg_tmp.hdr.seq = trtl_msg_seq_get(trtl);
if (trtl_hmq_user_is_sync(user)) {
uint16_t sync_id = buf->msg_tmp.hdr.seq & 0xFFFF;
buf->msg_tmp.hdr.flags |= TRTL_HMQ_HEADER_FLAG_SYNC;
buf->msg_tmp.hdr.sync_id = sync_id;
trtl_hmq_user_sync_wait_set(user, sync_id);
}
/* don't sleep here */
spin_lock_irqsave(&buf->lock, flags);
if ((buf->idx_w - buf->idx_r) >= hmq_buf_max_msg) {
if ((buf->idx_w - buf->idx_r) >= buf->entries) {
err = -EAGAIN;
} else {
/* copy only the message */
copy_size = sizeof(struct trtl_hmq_header)
+ (buf->msg_tmp.hdr.len) * sizeof(uint32_t);
msg = &buf->msg[buf->idx_w++ & (hmq_buf_max_msg - 1)];
msg = &buf->msg[trtl_hmq_buf_idx_get(buf, buf->idx_w++)];
memcpy(msg, &buf->msg_tmp, copy_size);
}
spin_unlock_irqrestore(&buf->lock, flags);
......@@ -638,7 +682,7 @@ static ssize_t trtl_hmq_write(struct file *f, const char __user *buf,
count = 0;
mutex_lock(&hmq->mtx);
for (i = 0; i < n_msg; i++, curbuf += sizeof(struct trtl_msg)) {
err = trtl_hmq_write_one(hmq, curbuf);
err = trtl_hmq_write_one(user, curbuf);
if (err)
break;
}
......@@ -666,86 +710,6 @@ static ssize_t trtl_hmq_write(struct file *f, const char __user *buf,
return count ? count : err;
}
/**
* Add a filter rule to a given file-descriptor
*/
static int trtl_ioctl_msg_filter_add(struct trtl_hmq_user *user,
void __user *uarg)
{
struct trtl_msg_filter_element *fltel;
uint32_t size;
int err = 0;
unsigned long flags;
fltel = kmalloc(sizeof(struct trtl_msg_filter_element), GFP_KERNEL);
if (!fltel)
return -ENOMEM;
/* Copy the message from user space*/
err = copy_from_user(&fltel->filter, uarg,
sizeof(struct trtl_msg_filter));
if (err)
goto err;
/* validate filter */
if (fltel->filter.operation >= __TRTL_MSG_FILTER_MAX) {
err = -EINVAL;
goto err;
}
size = TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(user->hmq->cfg->sizes);
if ((fltel->filter.flags & TRTL_MSG_FILTER_FLAG_PAYLOAD) &&
fltel->filter.word_offset >= size) {
dev_err(&user->hmq->dev,
"Invalid filter: word offset %d greater than maximum payload size %d\n",
fltel->filter.word_offset, size);
err = -EINVAL;
goto err;
}
size = TRTL_CONFIG_ROM_MQ_SIZE_HEADER(user->hmq->cfg->sizes);
if (!(fltel->filter.flags & TRTL_MSG_FILTER_FLAG_PAYLOAD) &&
fltel->filter.word_offset >= size) {
dev_err(&user->hmq->dev,
"Invalid filter: word offset %d greater than maximum header size %d\n",
fltel->filter.word_offset, size);
err = -EINVAL;
goto err;
}
/* Store filter */
spin_lock_irqsave(&user->lock, flags);
list_add_tail(&fltel->list, &user->list_filters);
user->n_filters++;
spin_unlock_irqrestore(&user->lock, flags);
return 0;
err:
kfree(fltel);
return err;
}
/**
* FIXME: to be tested
* Remove all filter rules form a given file-descriptor
*/
static void trtl_ioctl_msg_filter_clean(struct trtl_hmq_user *user,
void __user *uarg)
{
struct trtl_msg_filter_element *fltel, *tmp;
unsigned long flags;
spin_lock_irqsave(&user->lock, flags);
list_for_each_entry_safe(fltel, tmp, &user->list_filters, list) {
list_del(&fltel->list);
kfree(fltel);
user->n_filters--;
}
spin_unlock_irqrestore(&user->lock, flags);
}
/**
* Set of special operations that can be done on the HMQ
*/
......@@ -768,17 +732,27 @@ static long trtl_hmq_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -EFAULT;
/* Perform commands */
mutex_lock(&user->hmq->mtx);
switch (cmd) {
case TRTL_IOCTL_MSG_FILTER_ADD:
err = trtl_ioctl_msg_filter_add(user, uarg);
case TRTL_IOCTL_MSG_SYNC_ABORT:
if (trtl_hmq_user_is_sync(user)) {
if (trtl_hmq_user_is_waiting(user))
trtl_hmq_user_sync_wait_clr(user);
} else {
err = -EPERM;
}
break;
case TRTL_MSG_FILTER_CLEAN:
trtl_ioctl_msg_filter_clean(user, uarg);
case TRTL_IOCTL_HMQ_SYNC_SET:
spin_lock(&user->lock);
user->flags |= TRTL_HMQ_USER_FLAG_SYNC;
spin_unlock(&user->lock);
break;
default:
pr_warn("trtl: invalid ioctl command %d\n", cmd);
return -EINVAL;
err = -EINVAL;
break;
}
mutex_unlock(&user->hmq->mtx);
return err;
}
......@@ -813,16 +787,21 @@ static ssize_t trtl_hmq_read(struct file *f, char __user *ubuf,
while (!err && i < n_msg && buf->idx_w != usr->idx_r) {
spin_lock_irqsave(&buf->lock, flags);
msg = &buf->msg[usr->idx_r++ & (hmq_buf_max_msg - 1)];
if (!trtl_hmq_filter_check(usr, msg)) {
msg = &buf->msg[trtl_hmq_buf_idx_get(buf, usr->idx_r++)];
if (!trtl_hmq_user_filter_one(usr, msg)) {
spin_unlock_irqrestore(&buf->lock, flags);
continue;
}
copy_size = sizeof(struct trtl_hmq_header);
copy_size += (msg->hdr.len * 4);
memcpy(&buf->msg_tmp, msg, copy_size);
spin_unlock_irqrestore(&buf->lock, flags);
if (trtl_hmq_user_is_sync(usr))
trtl_hmq_user_sync_wait_clr(usr);
/* now we copy to user space and we can safely sleep */
if (copy_to_user(ubuf + count, &buf->msg_tmp, copy_size)) {
err = -EFAULT;
......@@ -837,6 +816,7 @@ static ssize_t trtl_hmq_read(struct file *f, char __user *ubuf,
return count ? count : err;
}
/**
* It filters out messages until a valid one
* @usr: pointer to a user HMQ instance
......@@ -851,8 +831,8 @@ static void trtl_hmq_user_filter(struct trtl_hmq_user *usr)
spin_lock_irqsave(&buf->lock, flags);
/* Loop until we find a valid message for the user */
while (buf->idx_w != usr->idx_r) {
msg = &buf->msg[usr->idx_r & (hmq_buf_max_msg - 1)];
if (trtl_hmq_filter_check(usr, msg))
msg = &buf->msg[trtl_hmq_buf_idx_get(buf, usr->idx_r)];
if (trtl_hmq_user_filter_one(usr, msg))
break;
usr->idx_r++;
}
......@@ -902,7 +882,7 @@ const struct file_operations trtl_hmq_fops = {
*/
static void trtl_message_pop(struct trtl_hmq *hmq, struct trtl_msg *msg)
{
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent);
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent->parent);
struct trtl_hmq_ctx *ctx;
size_t copy_size = TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(hmq->cfg->sizes);
unsigned long flags;
......@@ -920,6 +900,8 @@ static void trtl_message_pop(struct trtl_hmq *hmq, struct trtl_msg *msg)
trtl_hmq_data_read(ctx, msg->data, msg->hdr.len);
trtl_hmq_command(ctx, TRTL_MQ_CMD_DISCARD, 0);
spin_unlock_irqrestore(&trtl->lock_hmq_sel, flags);
msg->hdr.seq = trtl_msg_seq_get(trtl);
}
......@@ -937,7 +919,7 @@ static void trtl_message_pop_to_buf(struct trtl_hmq *hmq)
spin_lock_irqsave(&buf->lock, flags);
msg = &buf->msg[buf->idx_w++ & (hmq_buf_max_msg - 1)];
msg = &buf->msg[trtl_hmq_buf_idx_get(buf, buf->idx_w++)];
trtl_message_pop(hmq, msg);
spin_unlock_irqrestore(&buf->lock, flags);
......@@ -946,8 +928,7 @@ static void trtl_message_pop_to_buf(struct trtl_hmq *hmq)
}
/**
* It handles an output interrupt. It means that the CPU is outputting
* data for us, so we must read it.
* It handles an incoming interrupt, messages coming from the soft-CPU
*/
static void trtl_irq_handler_input(struct trtl_hmq *hmq)
{
......@@ -962,7 +943,7 @@ static void trtl_irq_handler_input(struct trtl_hmq *hmq)
*/
list_for_each_entry_safe(usr, tmp, &hmq->list_usr, list) {
spin_lock_irqsave(&buf->lock, flags);
if (usr->idx_r + hmq_buf_max_msg > buf->idx_w) {
if (usr->idx_r + buf->entries > buf->idx_w) {
spin_unlock_irqrestore(&buf->lock, flags);
continue;
}
......@@ -991,7 +972,7 @@ static uint64_t trtl_hmq_irq_status_in(struct trtl_dev *trtl)
/**
* It handles the HMQ interrupts for messages coming from the soft-CPU
* It handles the HMQ interrupts incoming messages from the soft-CPU
*/
irqreturn_t trtl_irq_handler_in(int irq_core_base, void *arg)
{
......@@ -1035,7 +1016,7 @@ dispatch_irq:
*/
static void trtl_message_push(struct trtl_hmq *hmq, struct trtl_msg *msg)
{
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent);
struct trtl_dev *trtl = to_trtl_dev(hmq->dev.parent->parent);
struct trtl_hmq_ctx *ctx;
unsigned long flags;
size_t size;
......@@ -1076,7 +1057,7 @@ static void trtl_message_push_from_buf(struct trtl_hmq *hmq)
spin_lock_irqsave(&buf->lock, flags);
if (likely(buf->idx_w != buf->idx_r)) {
msg = &buf->msg[buf->idx_r++ & (hmq_buf_max_msg - 1)];
msg = &buf->msg[trtl_hmq_buf_idx_get(buf, buf->idx_r++)];
trtl_message_push(hmq, msg);
buf->count++;
}
......@@ -1105,7 +1086,7 @@ static uint64_t trtl_hmq_irq_status_out(struct trtl_dev *trtl)
/**
* It handles the HMQ interrupts for messages coming from the soft-CPU
* It handles the HMQ interrupts for outgoing messages to the soft-CPU
*/
irqreturn_t trtl_irq_handler_out(int irq_core_base, void *arg)
{
......@@ -1173,7 +1154,6 @@ int trtl_probe_hmq(struct trtl_cpu *cpu, unsigned int hmq_idx)
struct trtl_dev *trtl = to_trtl_dev(cpu->dev.parent);
struct trtl_hmq *hmq = &cpu->hmq[hmq_idx];
int err;
size_t size;
hmq->index = hmq_idx;
......@@ -1214,12 +1194,17 @@ int trtl_probe_hmq(struct trtl_cpu *cpu, unsigned int hmq_idx)
goto out_dev;
/* Allocate buffers */
size = sizeof(struct trtl_msg) * hmq_buf_max_msg;
hmq->buf_in.msg = devm_kzalloc(&hmq->dev, size, GFP_KERNEL);
hmq->buf_in.entries = TRTL_CONFIG_ROM_MQ_SIZE_ENTRIES(hmq->cfg->sizes);
hmq->buf_in.msg = kcalloc(hmq->buf_in.entries,
sizeof(struct trtl_msg),
GFP_KERNEL);
if (!hmq->buf_in.msg)
goto out_msg_in;
hmq->buf_out.msg = devm_kzalloc(&hmq->dev, size, GFP_KERNEL);
hmq->buf_out.entries = TRTL_CONFIG_ROM_MQ_SIZE_ENTRIES(hmq->cfg->sizes);
hmq->buf_out.msg = kcalloc(hmq->buf_out.entries,
sizeof(struct trtl_msg),
GFP_KERNEL);
if (!hmq->buf_out.msg)
goto out_msg_out;
......@@ -1230,6 +1215,7 @@ int trtl_probe_hmq(struct trtl_cpu *cpu, unsigned int hmq_idx)
return 0;
out_msg_out:
kfree(hmq->buf_in.msg);
out_msg_in:
device_unregister(&hmq->dev);
out_dev:
......@@ -1247,5 +1233,8 @@ void trtl_remove_hmq(struct trtl_cpu *cpu, unsigned int hmq_idx)
trtl_hmq_flush(hmq);
kfree(hmq->buf_in.msg);
kfree(hmq->buf_out.msg);
device_unregister(&hmq->dev);
}
......@@ -8,7 +8,8 @@ REPO_PARENT ?= ../..
-include $(REPO_PARENT)/parent_common.mk
TRTL ?= ../
TRTL ?= ../../
TRTL_SW = $(TRTL)/software
LIBS = libmockturtle.so
LIB = libmockturtle.a
......@@ -16,7 +17,7 @@ LOBJ := libmockturtle.o
LOBJ += libmockturtle-rt-msg.o
CFLAGS += -Wall -Werror -ggdb -fPIC
CFLAGS += -I. -I$(TRTL)/include $(EXTRACFLAGS)
CFLAGS += -I. -I$(TRTL_SW)/include $(EXTRACFLAGS)
LDFLAGS = -L. -lmockturtle
ARFLAGS = rc
......
......@@ -15,6 +15,7 @@ TRTL_CONFIG_ROM_MAX_CPU = 8
TRTL_CONFIG_ROM_MAX_HMQ = 8
TRTL_CONFIG_ROM_MAX_RMQ = 8
ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT = 83638
class TrtlConfigMq(Structure):
"""
......@@ -109,6 +110,8 @@ class TrtlHmqHeader(Structure):
return False
return True
def __str__(self):
return "rt_app_id: 0x{:x}, flags: 0x{:x}, msg_id: 0x{:x}, len: {:d}, sync_id: {:d}, seq: {:d}".format(self.rt_app_id,self.flags, self.msg_id, self.len, self.sync_id, self.seq)
class TrtlMessage(Structure):
"""
......@@ -135,6 +138,9 @@ class TrtlMessage(Structure):
break
return True
def __str__(self):
return "{:s}, payload: ##".format(str(self.header))
class TrtlFirmwareVersion(Structure):
"""
......@@ -309,15 +315,6 @@ class TrtlDevice(object):
self.libtrtl.trtl_fw_variable_set.restype = c_int
self.libtrtl.trtl_fw_variable_set.errcheck = self.__errcheck_int
# self.libtrtl.trtl_hmq_filter_add.argtypes = [c_void_p]
# self.libtrtl.trtl_hmq_filter_clean.argtypes = [c_void_p]
# # Return
# self.libtrtl.trtl_hmq_filter_add.restype = c_int
# self.libtrtl.trtl_hmq_filter_clean.restype = c_int
# # Error
# self.libtrtl.trtl_hmq_filter_add.errcheck = self.__errcheck_int
# self.libtrtl.trtl_hmq_filter_clean.errcheck = self.__errcheck_int
def __errcheck_pointer(self, ret, func, args):
"""Generic error handler for functions returning pointers"""
if ret is None:
......@@ -555,16 +552,16 @@ class TrtlHmq(object):
:param timeout: time to wait before returning
:type timeout: int
:return: an asynchronous message
:return: an asynchronous message or None
:rtype: TrtlMessage
:raises OSError: from C library errors
"""
set_errno(0)
msg = TrtlMessage()
self.libtrtl.trtl_msg_async_recv(self.trtl_dev.tkn,
self.idx_cpu, self.idx_hmq,
pointer(msg), 1)
return msg
ret = self.libtrtl.trtl_msg_async_recv(self.trtl_dev.tkn,
self.idx_cpu, self.idx_hmq,
pointer(msg), 1)
return None if ret == 0 else msg
def sync_msg(self, msg_s, timeout=1000):
"""
......@@ -578,6 +575,7 @@ class TrtlHmq(object):
:rtype: TrtlMessage
:raises OSError: from C library errors
"""
set_errno(0)
msg_r = TrtlMessage()
self.libtrtl.trtl_msg_sync(self.trtl_dev.tkn,
self.idx_cpu, self.idx_hmq,
......
......@@ -13,7 +13,8 @@ from .PyMockTurtle import TrtlHmqHeader, TrtlMessage, TrtlConfig, \
TrtlFirmwareVariable, \
TRTL_CONFIG_ROM_MAX_CPU, \
TRTL_CONFIG_ROM_MAX_HMQ, \
TRTL_CONFIG_ROM_MAX_RMQ
TRTL_CONFIG_ROM_MAX_RMQ, \
ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT
__all__ = (
"TrtlDevice",
......@@ -29,4 +30,5 @@ __all__ = (
"TRTL_CONFIG_ROM_MAX_CPU",
"TRTL_CONFIG_ROM_MAX_HMQ",
"TRTL_CONFIG_ROM_MAX_RMQ",
"ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT",
)
......@@ -9,7 +9,7 @@ setup(name='PyMockTurtle',
author_email='federico.vaga@cern.ch',
maintainer="Federico Vaga",
maintainer_email="federico.vaga@cern.ch",
url='http://www.ohwr.org/projects/mock-turtle-sw',
url='http://www.ohwr.org/projects/mock-turtle',
packages=['PyMockTurtle'],
license='LGPLv2',
)
......@@ -31,9 +31,8 @@ struct trtl_desc {
int fd_dev; /**< File Descriptor of the device */
int fd_cpu[TRTL_MAX_CPU]; /**< File Descriptor of the CPUs */
int fd_hmq[TRTL_MAX_CPU][TRTL_MAX_MQ_CHAN]; /**< File Descriptors for the HMQ */
int fd_hmq_sync[TRTL_MAX_CPU][TRTL_MAX_MQ_CHAN]; /**< File Descriptors for the HMQ in sync mode */
struct trtl_config_rom cfgrom;/**< synthesis configuration */
struct trtl_dev *trtl_sync; /**< token for synchronous operations */
};
#endif
......@@ -51,37 +51,6 @@ static const char * const trtl_error_str[] = {
NULL,
};
/**
* Enumeration for predefined filters
*/
enum trtl_msg_filter_predefined {
TRTL_MSG_FILTER_ASYNC = 0, /**< filter for ASYNC messages */
TRTL_MSG_FILTER_SYNC, /**< filter for SYNC messages */
};
/**
* List predefined filters used within the library
*/
static struct trtl_msg_filter filters[] = {
[TRTL_MSG_FILTER_ASYNC] = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 0, /* ATTENTION: flags in the message header */
.mask = (TRTL_HMQ_HEADER_FLAG_SYNC | TRTL_HMQ_HEADER_FLAG_ACK) << 16,
.value = 0,
},
[TRTL_MSG_FILTER_SYNC] = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 0, /* ATTENTION: flags in the message header */
.mask = TRTL_HMQ_HEADER_FLAG_ACK << 16,
.value = TRTL_HMQ_HEADER_FLAG_ACK << 16,
},
};
static int trtl_sysfs_read(char *path, void *buf, size_t len);
static int trtl_sysfs_write(char *path, void *buf, size_t len);
......@@ -207,15 +176,13 @@ void trtl_list_free(char **list)
free(list);
}
/**
* It opens a TRTL device using a string descriptor. The descriptor correspond
* to the main char device name of the Mock-Turtle.
* @param[in] device name of the device to open
*
* @return TRTL token, NULL on error and errno is appropriately set
* @return the TRTL token, NULL on error and errno is appropriately set
*/
static struct trtl_dev *__trtl_open(const char *device)
struct trtl_dev *trtl_open(const char *device)
{
struct trtl_desc *trtl;
char path[TRTL_PATH_LEN + TRTL_NAME_LEN];
......@@ -263,6 +230,20 @@ static struct trtl_dev *__trtl_open(const char *device)
if (fd < 0)
goto out_hmq_fd;
trtl->fd_hmq[i][k] = fd;
fd = open(path, O_RDWR);
if (fd < 0) {
close(trtl->fd_hmq[i][k]);
goto out_hmq_fd;
}
trtl->fd_hmq_sync[i][k] = fd;
err = ioctl(trtl->fd_hmq_sync[i][k],
TRTL_IOCTL_HMQ_SYNC_SET, 1);
if (err) {
close(trtl->fd_hmq[i][k]);
close(trtl->fd_hmq_sync[i][k]);
goto out_hmq_fd;
}
}
}
......@@ -274,6 +255,7 @@ out_hmq_fd:
while(i >= 0) {
while(--k >= 0) {
close(trtl->fd_hmq[i][k]);
close(trtl->fd_hmq_sync[i][k]);
}
--i;
}
......@@ -283,52 +265,6 @@ out_stat:
return NULL;
}
/**
* It opens a TRTL device using a string descriptor. The descriptor correspond
* to the main char device name of the Mock-Turtle.
* @param[in] device name of the device to open
* @return the TRTL token, NULL on error and errno is appropriately set
*/
struct trtl_dev *trtl_open(const char *device)
{
struct trtl_desc *wdesc;
struct trtl_dev *trtl;
int ret, i , k;
trtl = __trtl_open(device);
if (!trtl)
return NULL;
/* quick hack to have a separete buffering for
synchronous messages */
wdesc = (struct trtl_desc *)trtl;
wdesc->trtl_sync = __trtl_open(device);
if (!wdesc->trtl_sync)
goto err_open_sync;
/* set up the filters in order to have synchorouns messages on
a different buffer */
for (i = 0; i < wdesc->cfgrom.n_cpu; ++i) {
for (k = 0; k < wdesc->cfgrom.n_hmq[i]; ++k) {
ret = trtl_hmq_filter_add(trtl, i, k,
&filters[TRTL_MSG_FILTER_ASYNC]);
if (ret < 0)
goto err_filter;
ret = trtl_hmq_filter_add(wdesc->trtl_sync, i, k,
&filters[TRTL_MSG_FILTER_SYNC]);
if (ret < 0)
goto err_filter;
}
}
return trtl;
err_filter:
trtl_close(wdesc->trtl_sync);
err_open_sync:
trtl_close(trtl);
return NULL;
}
/**
* It opens a TRTL device using its device_id. The Mock-Turtle
......@@ -882,40 +818,6 @@ int trtl_cpu_is_enable(struct trtl_dev *trtl, unsigned int index,
}
/**
* It adds a new filter to the given hmq descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @param[in] filter filter to add
* @return 0 on success, -1 otherwise and errno is set appropriately
*/
int trtl_hmq_filter_add(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
struct trtl_msg_filter *filter)
{
return ioctl(trtl_hmq_fd(trtl, idx_cpu, idx_hmq),
TRTL_IOCTL_MSG_FILTER_ADD, filter);
}
/**
* It removes all filters from the given hmq descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @return 0 on success, -1 otherwise and errno is set appropriately
*/
int trtl_hmq_filter_clean(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq)
{
return ioctl(trtl_hmq_fd(trtl, idx_cpu, idx_hmq),
TRTL_IOCTL_MSG_FILTER_CLEAN, NULL);
}
/**
* It returns the device name
* @param[in] trtl device token
......@@ -960,6 +862,38 @@ int trtl_hmq_fd(struct trtl_dev *trtl,
return wdesc->fd_hmq[idx_cpu][idx_hmq];
}
/**
* It returns the HMQ File Descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @return the file descriptor
*/
int trtl_hmq_fd_sync(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
return wdesc->fd_hmq_sync[idx_cpu][idx_hmq];
}
static int trtl_msg_read(int fd, struct trtl_msg *msg, unsigned int n)
{
size_t size = sizeof(*msg);
int ret;
ret = read(fd, msg, size * n);
if (ret < 0)
return ret;
if (ret % size) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
}
return (ret / size);
}
/**
* It reads messages from a given HMQ
......@@ -977,10 +911,7 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
struct trtl_msg *msg,
unsigned int n)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
int ret, size, fd;
fd = wdesc->fd_hmq[idx_cpu][idx_hmq];
int fd = trtl_hmq_fd(trtl, idx_cpu, idx_hmq);
/* validation */
if (fd < 0) {
......@@ -988,12 +919,18 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
return -1;
}
/* Get a message from the driver */
size = sizeof(struct trtl_msg);
ret = read(fd, msg, size * n);
return trtl_msg_read(fd, msg, n);
}
static int trtl_msg_write(int fd, struct trtl_msg *msg, unsigned int n)
{
size_t size = sizeof(*msg);
int ret;
ret = write(fd, msg, size * n);
if (ret < 0)
return ret;
if (ret % sizeof(struct trtl_msg)) {
return -1;
if (ret % size) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
......@@ -1001,8 +938,6 @@ int trtl_msg_async_recv(struct trtl_dev *trtl,
return (ret / size);
}
/**
* It writes messages to a given HMQ
* @param[in] trtl device token
......@@ -1019,31 +954,32 @@ int trtl_msg_async_send(struct trtl_dev *trtl,
struct trtl_msg *msg,
unsigned int n)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
int ret, size, fd;
fd = wdesc->fd_hmq[idx_cpu][idx_hmq];
int fd = trtl_hmq_fd(trtl, idx_cpu, idx_hmq);
/* validation */
if (fd < 0) {
errno = ETRTL_HMQ_CLOSE;
return -1;
}
/* Get a message from the driver */
size = sizeof(struct trtl_msg);
ret = write(fd, msg, size * n);
if (ret < 0)
return -1;
if (ret % sizeof(struct trtl_msg)) {
/* most likely a driver problem */
errno = ETRTL_INVALID_MESSAGE;
return -1;
}
return (ret / size);
return trtl_msg_write(fd, msg, n);
}
/**
* It adds a new filter to the given hmq descriptor
* @param[in] trtl device token
* @param[in] idx_cpu CPU index
* @param[in] idx_hmq HMQ index
* @param[in] err error code
* @return 0 on success, -1 otherwise and errno is set appropriately
*/
int trtl_msg_sync_abort(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
unsigned int err)
{
return ioctl(trtl_hmq_fd_sync(trtl, idx_cpu, idx_hmq),
TRTL_IOCTL_MSG_SYNC_ABORT, err);
}
/**
* It sends and receives a synchronous message. It is up to the user to set the
......@@ -1066,79 +1002,55 @@ int trtl_msg_sync(struct trtl_dev *trtl,
struct trtl_msg *msg_r,
int timeout)
{
struct trtl_desc *wdesc = (struct trtl_desc *)trtl;
struct polltrtl p;
#if 0
struct trtl_msg_filter f_sync = {
.flags = TRTL_MSG_FILTER_FLAG_HEADER,
.operation = TRTL_MSG_FILTER_AND,
.word_offset = 1, /* ATTENTION: sync_id in the message header */
.mask = 0xFFFF0000,
};
#endif
int ret;
msg_s->hdr.flags |= TRTL_HMQ_HEADER_FLAG_SYNC;
#if 0
/* FIXME: commented out as the filter is never removed.
The filter shouldn't be needed as the synchronous messages are
always sent sequentially - if there is only one process. */
f_sync.value = (msg_s->hdr.sync_id << 16);
ret = trtl_hmq_filter_add(wdesc->trtl_sync, idx_cpu, idx_hmq,
&f_sync);
if (ret < 0)
return -1;
#endif
struct pollfd p;
int ret, fd;
/* send message */
ret = trtl_msg_async_send(wdesc->trtl_sync, idx_cpu, idx_hmq, msg_s, 1);
fd = trtl_hmq_fd_sync(trtl, idx_cpu, idx_hmq);
ret = trtl_msg_write(fd, msg_s, 1);
if (ret < 0)
return -1;
goto err;
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_SEND;
return -1;
goto err;
}
/* wait answer */
p.trtl = wdesc->trtl_sync;
p.idx_cpu = idx_cpu;
p.idx_hmq = idx_hmq;
p.fd = fd;
p.events = POLLIN | POLLERR;
ret = trtl_msg_poll(&p, 1, timeout);
ret = poll(&p, 1, timeout);
if (ret < 0)
return -1;
/* I (git blame) know, this if is over-complicated */
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT;
return -1;
goto err_abort;
}
if ((p.revents & POLLERR)) {
errno = ETRTL_MSG_SYNC_FAILED_RECV_POLLERR;
return -1;
goto err_abort;
}
if (!(p.revents & POLLIN)) {
errno = ETRTL_MSG_SYNC_FAILED_RECV;
return -1;
goto err_abort;
}
/* read the answer */
ret = trtl_msg_async_recv(wdesc->trtl_sync, idx_cpu, idx_hmq, msg_r, 1);
ret = trtl_msg_read(fd, msg_r, 1);
if (ret < 0)
return -1;
goto err;
if (ret == 0) {
errno = ETRTL_MSG_SYNC_FAILED_RECV;
return -1;
}
/* paranoid check, the driver should be right */
if (msg_s->hdr.sync_id != msg_r->hdr.sync_id) {
errno = ETRTL_MSG_SYNC_FAILED_INVAL;
return -1;
goto err;
}
return 0;
err_abort:
trtl_msg_sync_abort(trtl, idx_cpu, idx_hmq, errno);
err:
return -1;
}
......
......@@ -151,16 +151,12 @@ static inline int trtl_cpu_restart(struct trtl_dev *trtl, unsigned int index)
* @{
*/
extern int trtl_hmq_filter_add(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
struct trtl_msg_filter *filter);
extern int trtl_hmq_filter_clean(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq);
extern int trtl_hmq_fd(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq);
extern int trtl_hmq_fd_sync(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq);
extern int trtl_msg_async_send(struct trtl_dev *trtl,
unsigned int idx_cpu,
unsigned int idx_hmq,
......
......@@ -7,3 +7,4 @@ mockturtle-smem
mockturtle-ping
mockturtle-variable
mockturtle-buffer
mockturtle-gdbserver
\ No newline at end of file
......@@ -8,14 +8,15 @@ REPO_PARENT ?= ../..
-include $(REPO_PARENT)/parent_common.mk
DESTDIR ?= /usr/local
TRTL ?= ../
TRTL ?= ../../
TRTL_SW = $(TRTL)/software
GIT_VERSION := $(shell git describe --dirty --long --tags)
CFLAGS += -Wall -Werror -ggdb -I$(TRTL)/lib
CFLAGS += -I$(TRTL)/include
CFLAGS += -Wall -Werror -ggdb -I$(TRTL_SW)/lib
CFLAGS += -I$(TRTL_SW)/include
CFLAGS += $(EXTRACFLAGS)
LDLIBS += -Wl,-Bstatic -L$(TRTL)/lib -lmockturtle
LDLIBS += -Wl,-Bstatic -L$(TRTL_SW)/lib -lmockturtle
LDLIBS += -Wl,-Bdynamic -lpthread
PROGS := mockturtle-count
PROGS += lsmockturtle
......@@ -26,6 +27,7 @@ PROGS += mockturtle-smem
PROGS += mockturtle-ping
PROGS += mockturtle-variable
PROGS += mockturtle-buffer
PROGS += mockturtle-gdbserver
all: $(PROGS)
......@@ -33,7 +35,7 @@ install:
install -d $(DESTDIR)/bin
install -D $(PROGS) $(DESTDIR)/bin
%: %.c $(TRTL)/libmockturtle.a
%: %.c $(TRTL_SW)/lib/libmockturtle.a
$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS)
# make nothing for modules_install, but avoid errors
......
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <stdbool.h>
#include <time.h>
#include <assert.h>
#include <limits.h>
#include <hw/mockturtle_cpu_csr.h>
#define TRTL_DBG_PORT_SIZE pagesize
#define TRTL_GDB_PACKET_SIZE_MAX 2048
static long pagesize;
static int verbose;
static int swapping;
/**
* struct trtl_gdb_packet - GDB packet
* @data: message exchanged with GDB
* @size: length in bytes
*/
struct trtl_gdb_packet {
char data[TRTL_GDB_PACKET_SIZE_MAX];
size_t size;
};
/**
* struct trtl_dbg_port - descriptor to handle connection
* @addr: Mock Turtle virtual address
* @cpu: CPU index
* @fd: socket file descriptor
*/
struct trtl_dbg_port {
void *addr;
uint8_t cpu;
int fd;
};
typedef int (trtl_gdb_command_t)(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in);
/**
* Help message
*/
static void help(void)
{
fputs("\n", stderr);
fputs("mockturtle-gdbserver -D 0x<hex-number> -i <number>\n\n",
stderr);
fputs("-D device identificator\n", stderr);
fputs("-i CPU index (default 0)\n", stderr);
fputs("-p GDB port (default 7471)\n", stderr);
fputs("-s 32bit endianness swapping (default not active)\n", stderr);
fputs("-v verbose output (add more for more verbosity)\n", stderr);
fputs("-h show this message\n", stderr);
fputs("\n", stderr);
fputs("The options '-s' should be used when there is an\n", stderr);
fputs("endianness conversion to be done to communicate\n", stderr);
fputs("with the debug port.\n", stderr);
fflush(stderr);
}
/**
* Change byte order but only when user asks for it
* @val: value
*
* Return: value in swapped order
*
* It uses a global variable that is set by the user
*/
static uint32_t __io_swap32(uint32_t val)
{
if (!swapping)
return val;
fputs("Swapping\n", stdout);
return ((val >> 24) & 0x000000FF) |
((val >> 8) & 0x0000FF00) |
((val << 8) & 0x00FF0000) |
((val << 24) & 0xFF000000);
}
/**
* Read value from the Debug Port
*/
static uint32_t trtl_dbg_readl(struct trtl_dbg_port *dbg, uint32_t reg)
{
char *addr = dbg->addr;
return __io_swap32(*((uint32_t *)(addr + reg)));
}
/**
* Write value to the Debug Port
*/
static void trtl_dbg_writel(struct trtl_dbg_port *dbg,
uint32_t reg, uint32_t val)
{
char *addr = dbg->addr;
*((uint32_t *)(addr + reg)) = __io_swap32(val);
}
/**
* Read mail-box
* @dbg: debug port
*
* Return value read
*/
static uint32_t trtl_dbg_read_mbx(struct trtl_dbg_port *dbg)
{
uint32_t reg;
reg = MT_CPU_CSR_REG_DBG_CORE0_MBX;
reg += sizeof(uint32_t) * dbg->cpu;
return trtl_dbg_readl(dbg, reg);
}
/**
* Write mail-box
* @dbg: debug port
* @val: value to write
*/
static void trtl_dbg_write_mbx(struct trtl_dbg_port *dbg, uint32_t val)
{
uint32_t reg;
reg = MT_CPU_CSR_REG_DBG_CORE0_MBX;
reg += sizeof(uint32_t) * dbg->cpu;
trtl_dbg_writel(dbg, reg, val);
}
/**
* Execute one instructions
* @dbg: debug port
* @insn: instruction to execute
*/
static void trtl_dbg_exec_insn(struct trtl_dbg_port *dbg, uint32_t insn)
{
uint32_t reg;
reg = MT_CPU_CSR_REG_DBG_CORE0_INSN;
reg += sizeof(uint32_t) * dbg->cpu;
trtl_dbg_writel(dbg, reg, insn);
}
/**
* Execute instruction to copy a register to the mail-box
* @dbg: debug port
* @reg: register index
*/
static void trtl_dbg_exec_reg_to_mbx(struct trtl_dbg_port *dbg, uint32_t reg)
{
trtl_dbg_exec_insn(dbg, 0x7D001073 | (reg << 15));
}
/**
* Execute instruction to the mail-box to a register
* @dbg: debug port
* @reg: register index
*/
static void trtl_dbg_exec_mbx_to_reg(struct trtl_dbg_port *dbg, uint32_t reg)
{
trtl_dbg_exec_insn(dbg, 0x7D002073 | (reg << 7));
}
/**
* Execute NOP instruction
* @dbg: debug port
*/
static void trtl_dbg_exec_nop(struct trtl_dbg_port *dbg)
{
trtl_dbg_exec_insn(dbg, 0x00000013);
}
/**
* Check if MockTurtle CPU is in debug mode
* @dbg: debug port
*
* Return true when it is in debug mode
*/
static bool trtl_dbg_in_debug_mode(struct trtl_dbg_port *dbg)
{
uint32_t status;
status = trtl_dbg_readl(dbg, MT_CPU_CSR_REG_DBG_STATUS);
return ((status >> dbg->cpu) & 1);
}
/**
* Set MockTurtle CPU in debug mode
* @dbg: debug port
*
* Return 0 on success, -1 on error and errno is appropriately set
*/
static int trtl_dbg_debug_mode_force_set(struct trtl_dbg_port *dbg)
{
int retry;
if (trtl_dbg_in_debug_mode(dbg))
return 0;
trtl_dbg_writel(dbg, MT_CPU_CSR_REG_DBG_FORCE, (1 << dbg->cpu));
/* wait to debug to be ready max ~5s */
retry = 5000;
while (retry >= 0) {
struct timespec ts = {0, 1000000};
nanosleep(&ts, NULL);
if (trtl_dbg_in_debug_mode(dbg))
break;
retry--;
}
trtl_dbg_writel(dbg, MT_CPU_CSR_REG_DBG_FORCE, 0);
if (retry < 0) {
errno = ETIME;
return -1;
}
return 0;
}
/**
* Read from a CPU register
* @dbg: debug port
* @reg: register number [0, 31]
*
* Return: the register content
*/
static uint32_t trtl_dbg_read_reg(struct trtl_dbg_port *dbg,
int reg)
{
assert(reg >= 0 && reg < 32);
trtl_dbg_exec_reg_to_mbx(dbg, reg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
return trtl_dbg_read_mbx(dbg);
}
/**
* Write in a CPU register
* @dbg: debug port
* @reg: register number [0, 31]
* @val: value
*/
static void trtl_dbg_write_reg(struct trtl_dbg_port *dbg,
int reg, uint32_t val)
{
assert(reg >= 0 && reg < 32);
trtl_dbg_write_mbx(dbg, val);
trtl_dbg_exec_mbx_to_reg(dbg, reg);
}
/**
* Copy PC to RA register
* @dbg: debug port
*
* Return PC value
*/
static uint32_t trtl_dbg_pc_read_via_ra(struct trtl_dbg_port *dbg)
{
trtl_dbg_exec_insn(dbg, 0x000000ef); /* ra = pc + 4 */
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_reg_to_mbx(dbg, 1);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
return (trtl_dbg_read_mbx(dbg) - 4) & 0xFFFFFFFF;
}
/**
* Write PC using RA content register
* @dbg: debug port
*/
static void trtl_dbg_pc_write_via_ra(struct trtl_dbg_port *dbg)
{
trtl_dbg_exec_insn(dbg, 0x00008067); /* ret */
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
}
/**
* Increase PC by 4
* @dbg: debug port
*/
static void trtl_dbg_pc_advance_4(struct trtl_dbg_port *dbg)
{
trtl_dbg_exec_insn(dbg, 0x00000263); /* beqz zero, +4 */
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
}
/**
* Continue command
*/
static int trtl_gdb_handle_c(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
if (in->size > 1) {
out->size = 0;
return 0;
}
trtl_dbg_exec_insn(dbg, 0x00100073); /* ebreak */
while (1) {
struct pollfd p = {
.fd = dbg->fd,
.events = POLLIN,
.revents = 0,
};
int ret;
if (trtl_dbg_in_debug_mode(dbg)) {
/*
* TODO not clear but check twice due to possible
* race if the ebreak is not yet executed
*/
if (trtl_dbg_in_debug_mode(dbg)) {
out->size = snprintf(out->data,
TRTL_GDB_PACKET_SIZE_MAX,
"S05");
break;
}
}
ret = poll(&p, 1, 1000);
if (ret > 0) {
/* GDB wants something from us */
ret = trtl_dbg_debug_mode_force_set(dbg);
if (ret < 0)
fprintf(stderr, "Failed to set debug mode\n");
out->size = snprintf(out->data,
TRTL_GDB_PACKET_SIZE_MAX,
"S02");
break;
}
}
return 0;
}
/**
* Detach
*/
static int trtl_gdb_handle_D(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
trtl_dbg_exec_insn(dbg, 0x00100073); /* ebreak */
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX, "OK");
return 0;
}
/**
* Read all registers
*/
static int trtl_gdb_handle_g(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
uint32_t regs[32], pc;
int i;
out->size = 0;
for (i = 0; i < 32; ++i) {
regs[i] = trtl_dbg_read_reg(dbg, i);
out->size += snprintf(out->data + out->size,
TRTL_GDB_PACKET_SIZE_MAX,
"%08"PRIx32, htonl(regs[i]));
}
pc = trtl_dbg_pc_read_via_ra(dbg);
out->size += snprintf(out->data + out->size,
TRTL_GDB_PACKET_SIZE_MAX,
"%08"PRIx32, htonl(pc));
trtl_dbg_write_reg(dbg, 1, regs[1]);
return 0;
}
/**
* Write all register
*/
static int trtl_gdb_handle_G(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
uint32_t regs[33]; /* 32 register, 1 PC */
int i;
if (in->size != (1 + 33 * 8)) {
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E01");
return 0;
}
for (i = 0; i < 33; ++i) {
int ret = sscanf(in->data + 1 + i * 8, "%08"SCNx32, &regs[i]);
if (ret != 1) {
out->size = snprintf(out->data,
TRTL_GDB_PACKET_SIZE_MAX,
"E02");
return 0;
}
}
trtl_dbg_write_reg(dbg, 1, ntohl(regs[32]));
trtl_dbg_pc_write_via_ra(dbg);
for (i = 0; i < 32; ++i)
trtl_dbg_write_reg(dbg, i, ntohl(regs[i]));
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"OK");
return 0;
}
/**
* Set thread for subsequent operations
*
* Partially supported
*/
static int trtl_gdb_handle_H(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
/* we just want to keep GDB quiet */
if (strncmp(in->data, "Hg0", 3) == 0)
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"OK");
else
out->size = 0;
return 0;
}
/**
* Kill
*
* Not supported yet
*
* kill leave the CPU in its current state. In the next connection it
* will restart exactly from that point and the core remains stopped.
*/
static int trtl_gdb_handle_k(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
return 0;
}
/**
* Write data to memory
*/
static int trtl_gdb_handle_M(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
uint32_t addr, n;
uint32_t a0, a1;
char *indata;
int ret;
indata = strchr(in->data, ':');
if (!indata) {
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E01");
return 0;
}
indata++; /* skip ':' */
ret = sscanf(in->data + 1, "%"SCNx32",%"SCNx32":", &addr, &n);
if (ret != 2) {
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E02");
return 0;
}
if (n * 2 != in->size - (indata - in->data)) {
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E03");
return 0;
}
a0 = trtl_dbg_read_reg(dbg, 10);
a1 = trtl_dbg_read_reg(dbg, 11);
trtl_dbg_write_reg(dbg, 10, addr);
if (addr % 4 == 0) {
for (; n >= 4; n -= 4, indata += 8) {
uint32_t w;
ret = sscanf(indata, "%08"SCNx32, &w);
if (ret != 1)
break;
trtl_dbg_write_reg(dbg, 11, ntohl(w));
/* sw a1, 0(a0) */
trtl_dbg_exec_insn(dbg, 0x00B52023);
/* addi a0, a0, 4 */
trtl_dbg_exec_insn(dbg, 0x00450513);
}
}
for (; n > 0; --n, indata += 2) {
uint32_t b;
ret = sscanf(indata, "%02"SCNx32, &b);
if (ret != 1)
break;
trtl_dbg_write_reg(dbg, 11, b);
/* sb a1, 0(a0) */
trtl_dbg_exec_insn(dbg, 0x00B50023);
/* addi a0, a0, 4 */
trtl_dbg_exec_insn(dbg, 0x00150513);
}
trtl_dbg_write_reg(dbg, 10, a0);
trtl_dbg_write_reg(dbg, 11, a1);
if (n > 0)
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E04");
else
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"OK");
return 0;
}
/**
* Read data from memory
*/
static int trtl_gdb_handle_m(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
uint32_t addr, n;
uint32_t a0, a1;
int ret;
ret = sscanf(in->data + 1, "%x,%x", &addr, &n);
if (ret != 2) {
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E01");
return 0;
}
a0 = trtl_dbg_read_reg(dbg, 10);
a1 = trtl_dbg_read_reg(dbg, 11);
trtl_dbg_write_reg(dbg, 10, addr);
out->size = 0;
for (; n > 0; --n) {
uint8_t b;
trtl_dbg_exec_insn(dbg, 0x00054583); /* lbu a1, 0(a0) */
trtl_dbg_exec_insn(dbg, 0x00150513); /* addi a0, a0, 1 */
b = trtl_dbg_read_reg(dbg, 11);
out->size += snprintf(out->data + out->size,
TRTL_GDB_PACKET_SIZE_MAX,
"%02"PRIx8, b);
}
trtl_dbg_write_reg(dbg, 10, a0);
trtl_dbg_write_reg(dbg, 11, a1);
return 0;
}
/**
* Read a specific register
*/
static int trtl_gdb_handle_p(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
unsigned int val;
int ret;
ret = sscanf(in->data + 1, "%x", &val);
if (ret != 1) {
out->size = 0;
return 0;
}
printf("0x%x\n", val);
if (val == (0x301 + 65)) /* MISA CSR */
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"%08x",
(1 << 30) | (1 << ('I' - 65)));
else
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"E01");
return 0;
}
/**
* Write a specific register
*
*
* Not supported yet
*/
static int trtl_gdb_handle_P(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = 0;
return 0;
}
/**
* Answer to qSupported request
*/
static int trtl_gdb_handle_q_supported(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX,
"PacketSize=%x", TRTL_GDB_PACKET_SIZE_MAX);
return 0;
}
static int trtl_gdb_handle_qm(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX, "S05");
return 0;
}
static int trtl_gdb_handle_q(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
if (strncmp(in->data, "qSupported:", 11) == 0)
return trtl_gdb_handle_q_supported(dbg, out, in);
else if (strncmp(in->data, "qm", 2) == 0)
return trtl_gdb_handle_qm(dbg, out, in);
out->size = 0;
return 0;
}
/**
* Single step
*/
static int trtl_gdb_handle_s(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
uint32_t pc, npc, ra, insn;
if (in->size > 1) {
out->size = 0;
return 0;
}
ra = trtl_dbg_read_reg(dbg, 1);
pc = trtl_dbg_pc_read_via_ra(dbg);
trtl_dbg_write_reg(dbg, 1, pc);
trtl_dbg_exec_insn(dbg, 0x0000A083) ;/* lw ra,0(ra) */
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
insn = trtl_dbg_read_reg(dbg, 1);
trtl_dbg_write_reg(dbg, 1, ra);
if (verbose)
fprintf(stdout, "execute: %08"PRIx32" at pc=%08"PRIx32,
insn, pc);
trtl_dbg_exec_insn(dbg, insn);
trtl_dbg_exec_nop(dbg);
trtl_dbg_exec_nop(dbg);
switch (insn & 0x77) {
case 0x67: /* jump */
/* Nothing to do, PC is always updated */
break;
case 0x63: /* branch */
ra = trtl_dbg_read_reg(dbg, 1);
npc = trtl_dbg_pc_read_via_ra(dbg);
trtl_dbg_write_reg(dbg, 1, ra);
if (npc == pc)
trtl_dbg_pc_advance_4(dbg);
break;
default:
trtl_dbg_pc_advance_4(dbg);
break;
}
out->size = snprintf(out->data, TRTL_GDB_PACKET_SIZE_MAX, "S05");
return 0;
}
/**
* vAttach command
*
* Not supported yet
*/
static int trtl_gdb_handle_v_attach(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = 0;
return 0;
}
/**
* vCont command
*
* Not supported yet
*/
static int trtl_gdb_handle_v_cont(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = 0;
return 0;
}
/**
* vCtrlC command
*
* Not supported yet
*/
static int trtl_gdb_handle_v_ctrlc(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = 0;
return 0;
}
/**
* v<name> commands
*/
static int trtl_gdb_handle_v(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
int ret = 0;
if (strncmp(in->data, "vAttach", 7) == 0)
ret = trtl_gdb_handle_v_attach(dbg, out, in);
else if (strncmp(in->data, "vCont", 5) == 0)
ret = trtl_gdb_handle_v_cont(dbg, out, in);
else if (strncmp(in->data, "vCtrlC", 6) == 0)
ret = trtl_gdb_handle_v_ctrlc(dbg, out, in);
else if (strncmp(in->data, "vMustReplyEmpty:", 16) == 0)
out->size = 0;
else
out->size = 0;
return ret;
}
/**
* Write binary data to memory
*
* Not supported yet
*/
static int trtl_gdb_handle_X(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
out->size = 0;
return 0;
}
static trtl_gdb_command_t *gdb_packet_exec[] = {
['c'] = trtl_gdb_handle_c,
['D'] = trtl_gdb_handle_D,
['g'] = trtl_gdb_handle_g,
['G'] = trtl_gdb_handle_G,
['H'] = trtl_gdb_handle_H,
['k'] = trtl_gdb_handle_k,
['M'] = trtl_gdb_handle_M,
['m'] = trtl_gdb_handle_m,
['p'] = trtl_gdb_handle_p,
['P'] = trtl_gdb_handle_P,
['q'] = trtl_gdb_handle_q,
['s'] = trtl_gdb_handle_s,
['v'] = trtl_gdb_handle_v,
['v'] = trtl_gdb_handle_v,
['X'] = trtl_gdb_handle_X,
['?'] = trtl_gdb_handle_qm,
};
/**
* Process incoming packet and generate the outcoming
* @out: outgoing packet
* @in: incoming packet
*
* Return: 0 on success, otherwise -1 and errno is appropriately set
*
* The function does not receive or send packets, it only processes them;
* the caller will handle recv(2) and send(2)
*/
static int gdb_command(struct trtl_dbg_port *dbg,
struct trtl_gdb_packet *out,
struct trtl_gdb_packet *in)
{
int cmd = in->data[0];
trtl_gdb_command_t *exec;
if (in->size == 0)
return -1;
exec = gdb_packet_exec[cmd];
if (exec)
return exec(dbg, out, in);
out->size = 0;
return 0;
}
static void trtl_debugger_print_packet(struct trtl_gdb_packet *pkt,
const char *dir)
{
int i, start, end;
switch (verbose) {
case 0:
return;
case 1:
start = 1;
end = pkt->size - 3;
break;
default:
start = 0;
end = pkt->size;
break;
}
fputs(dir, stdout);
fputc(' ', stdout);
for (i = start; i < end; ++i)
fputc(pkt->data[i], stdout);
fputc('\n', stdout);
fflush(stdout);
}
/**
* Calculate mod 256 checksum
* @data: input data
* @n: number of bytes
*
* Return: checksum value
*/
static uint8_t trtl_debugger_checksum(uint8_t *data, size_t n)
{
uint8_t checksum = 0;
int i;
for (i = 0; i < n; ++i)
checksum += data[i];
return checksum & 0xFF;
}
/**
* Receive a GDB packet
* @fd: socket file descriptor
* @pkt: packet
*
* Return: 0 on success, otherwise -1 and errno is appropriately set
*/
static int __trtl_debugger_recv(int fd, struct trtl_gdb_packet *pkt)
{
pkt->size = 0;
do {
char c;
int ret;
struct pollfd p = {
.fd = fd,
.events = POLLIN,
.revents = 0,
};
ret = poll(&p, 1, 1000);
if (ret < 0)
return ret;
if (ret == 0)
continue;
ret = recv(fd, &c, 1, 0);
if (ret < 0)
return -1;
if (ret == 0) {
errno = ENOTCONN;
return -1;
}
/* FIXME should we control more ? */
if (pkt->size == 0 && c != '$')
continue; /* wait for the beginning */
pkt->data[pkt->size] = c;
pkt->size++;
if (verbose > 2) {
fprintf(stdout, "Building message: [%zu]: %s\n",
pkt->size, pkt->data);
}
if (pkt->size > TRTL_GDB_PACKET_SIZE_MAX - 1) {
/* -1 to leave space for the string terminator */
errno = EINVAL;
return -1;
}
} while (!(pkt->size > 3 && pkt->data[pkt->size - 3] == '#'));
return 0;
}
/**
* Receive a GDB packet
* @fd: socket file descriptor
* @pkt: packet
*
* Return: 0 on success, otherwise -1 and errno is appropriately set
*/
static int trtl_debugger_recv(int fd, struct trtl_gdb_packet *pkt)
{
uint8_t checksum_l, checksum_r;
char ack[1];
int ret;
ret = __trtl_debugger_recv(fd, pkt);
if (ret < 0)
return ret;
trtl_debugger_print_packet(pkt, "->");
checksum_l = trtl_debugger_checksum((uint8_t *)(pkt->data + 1),
pkt->size - 4);
ret = sscanf(pkt->data + pkt->size - 2, "%02"SCNx8, &checksum_r);
if (ret != 1) {
fprintf(stderr, "Received invalid checksum\n");
return -1;
}
/* Remove checksum and special characters $payload#checksum */
pkt->size -= 4;
memmove(pkt->data, pkt->data + 1, pkt->size);
pkt->data[pkt->size] = 0;
if (checksum_l == checksum_r) {
ack[0] = '+';
} else {
ack[0] = '-';
if (verbose)
fprintf(stderr,
"Invalid checksum ' (L) %x != (R) %x'\n",
checksum_l, checksum_r);
}
ret = send(fd, ack, 1, 0);
if (ret != 1) {
fputs("Failed to send acknowledge\n", stderr);
errno = EIO;
return -1;
}
return 0;
}
/**
* Send a GDB packet
* @fd: socket file descriptor
* @pkt: packet
*
* Return: 0 on success, otherwise -1 and errno is appropriately set
*/
static int trtl_debugger_send(int fd, struct trtl_gdb_packet *pkt)
{
uint8_t checksum_l;
int ret;
checksum_l = trtl_debugger_checksum((uint8_t *)pkt->data, pkt->size);
/* Add checksum and special characters $payload#checksum */
memmove(pkt->data + 1, pkt->data, pkt->size);
pkt->data[0] = '$';
snprintf(pkt->data + 1 + pkt->size, TRTL_GDB_PACKET_SIZE_MAX,
"#%02x", checksum_l);
pkt->size += 4; /* 1 $, 1 #, 2 checksum */
trtl_debugger_print_packet(pkt, "<-");
ret = send(fd, pkt->data, pkt->size, 0);
if (ret < 0)
return -1;
if (ret != pkt->size) {
errno = EIO;
return -1;
}
return 0;
}
/**
* Run GDB server
* @addr: MMAP address
*
* Return: 0 on success, otherwise -1 and errno is appropriately set
*/
static int trtl_debugger_run(struct trtl_dbg_port *dbg)
{
bool run = true;
int ret;
struct trtl_gdb_packet *pkt, *in, *out;
pkt = calloc(2, sizeof(struct trtl_gdb_packet));
if (!pkt) {
fprintf(stderr, "Memory allocation failed: %s\n",
strerror(errno));
return -1;
}
in = &pkt[0];
out = &pkt[1];
ret = trtl_dbg_debug_mode_force_set(dbg);
if (ret < 0)
return -1;
fputs("Start receiving messages from GDB", stdout);
fflush(stdout);
while (run) {
memset(in, 0, sizeof(*in));
memset(out, 0, sizeof(*out));
ret = trtl_debugger_recv(dbg->fd, in);
if (ret) {
if (errno == ENOTCONN)
run = false;
else
fprintf(stderr,
"Failed to receive message: %s\n",
strerror(errno));
continue;
}
ret = gdb_command(dbg, out, in);
if (ret < 0)
continue;
ret = trtl_debugger_send(dbg->fd, out);
if (ret) {
fprintf(stderr, "Failed to send message: %s\n",
strerror(errno));
}
}
free(pkt);
return 0;
}
#define MEMPATH_LEN 128
int main(int argc, char *argv[])
{
uint32_t dev_id = 0;
int gdb_port = 7471;
int c, ret, fd, sfd, ret_exit = EXIT_SUCCESS, optval;
char mempath[MEMPATH_LEN];
struct trtl_dbg_port dbg;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
pagesize = sysconf(_SC_PAGE_SIZE);
memset(&dbg, 0, sizeof(dbg));
while ((c = getopt(argc, argv, "hi:D:p:vs")) != -1) {
switch (c) {
case 'h':
case '?':
help();
exit(EXIT_SUCCESS);
break;
case 'i':
ret = sscanf(optarg, "%"SCNd8, &dbg.cpu);
if (ret != 1) {
help();
exit(EXIT_FAILURE);
}
break;
case 'D':
ret = sscanf(optarg, "0x%x", &dev_id);
if (ret != 1) {
help();
exit(EXIT_FAILURE);
}
break;
case 'p':
ret = sscanf(optarg, "%d", &gdb_port);
if (ret != 1) {
help();
exit(EXIT_FAILURE);
}
break;
case 'v':
verbose++;
break;
case 's':
swapping = 1;
break;
}
}
if (dev_id == 0) {
fprintf(stderr, "Invalid device ID '0x%"PRIx32"'\n", dev_id);
exit(EXIT_FAILURE);
}
snprintf(mempath, MEMPATH_LEN,
"/sys/kernel/debug/trtl-%04"PRIx32"/trtl-%04"PRIx32"-dbg",
dev_id, dev_id);
fd = open(mempath, O_RDWR | O_SYNC);
if (fd < 0) {
fprintf(stderr, "Can't open '%s': %s\n",
mempath, strerror(errno));
ret_exit = EXIT_FAILURE;
goto out;
}
dbg.addr = mmap(NULL, TRTL_DBG_PORT_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0);
if (dbg.addr == MAP_FAILED) {
fprintf(stderr, "Failed to map Mock Turtle debug: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_map;
}
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
fprintf(stderr, "Failed to open a socket: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_sock;
}
optval = 1;
ret = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
&optval, sizeof(optval));
if (ret < 0) {
fprintf(stderr, "Failed to set REUSEADDR option: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_sockopt;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(gdb_port);
ret = bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret < 0) {
fprintf(stderr, "Failed to bind to an *:%d: %s\n",
gdb_port, strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_bind;
}
ret = listen(sfd, 1);
if (ret < 0) {
fprintf(stderr, "Failed to listen: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_bind;
}
dbg.fd = accept(sfd, (struct sockaddr *)&client_addr, &client_len);
if (dbg.fd < 0) {
fprintf(stderr, "Failed to accept: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_bind;
}
fprintf(stdout, "Accepted connection from %s\n",
inet_ntoa(client_addr.sin_addr));
ret = trtl_debugger_run(&dbg);
if (ret < 0) {
fprintf(stderr, "Mock Turtle debugger failed: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
}
out_bind:
close(sfd);
out_sockopt:
out_sock:
ret = munmap(NULL, TRTL_DBG_PORT_SIZE);
if (ret < 0) {
fprintf(stderr, "Failed to unmap Mock Turtle debug: %s\n",
strerror(errno));
ret_exit = EXIT_FAILURE;
goto out_map;
}
out_map:
close(fd);
out:
exit(ret_exit);
}
......@@ -7,6 +7,7 @@ DIRS += config_rom
DIRS += sim-verif
DIRS += hmq-async-recv
DIRS += hmq-async-send
DIRS += hmq-sync
DIRS += hmq-purge
DIRS += rmq-udp-send
DIRS += rt-frm
......
OBJS = config_rom.o
OBJS += # add other object files that you need
OUTPUT = fw-config-rom
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = config_rom.o
OBJS += # add other object files that you need
OUTPUT = fw-config-rom
OBJS = byte-addressing.o
OBJS += # add other object files that you need
OUTPUT = fw-byte-addressing
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = byte-addressing.o
OBJS += # add other object files that you need
OUTPUT = fw-byte-addressing
OBJS = cpu-loop.o
OBJS += # add other object files that you need
OUTPUT = fw-loop
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = cpu-loop.o
OBJS += # add other object files that you need
OUTPUT = fw-loop
OBJS = notify.o
OBJS += # add other object files that you need
OUTPUT = fw-notify
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = notify.o
OBJS += # add other object files that you need
OUTPUT = fw-notify
OBJS = hmq-async-recv.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-async-recv
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = hmq-async-recv.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-async-recv
......@@ -23,6 +23,7 @@ int main()
mq_claim(TRTL_HMQ, hmq);
hdr = mq_map_out_header(TRTL_HMQ, hmq);
memset(hdr, 0, sizeof(*hdr));
msg = mq_map_out_buffer(TRTL_HMQ, hmq);
hdr->len = n_word;
for (i = 0; i < hdr->len; i++)
......
OBJS = hmq-async-send.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-async-send
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = hmq-async-send.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-async-send
OBJS = hmq-purge.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-purge
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = hmq-purge.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-purge
mainmenu "hmq_async_send test configuration"
comment "Project specific configuration"
config FPGA_APPLICATION_ID
int "FPGA application ID"
default 0
help
Help text
config RT_APPLICATION_ID
int "RT application ID"
default 0
help
Help text
# include Mock Turtle's Kconfig
source "Kconfig.mt"
TRTL ?= ../../../
TRTL_FW = $(TRTL)/software/firmware
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = hmq-sync.o
OBJS += # add other object files that you need
OUTPUT = fw-hmq-sync
#
# Automatically generated file; DO NOT EDIT.
# hmq_async_send test configuration
#
#
# Project specific configuration
#
CONFIG_FPGA_APPLICATION_ID=0
CONFIG_RT_APPLICATION_ID=0
#
# Mock Turtle configuration
#
CONFIG_CFLAGS_OPT="-O0"
CONFIG_CFLAGS_EXTRA="-ggdb"
# CONFIG_MOCKTURTLE_SIMULATION is not set
#
# Mock Turtle framework configuration
#
# CONFIG_MOCKTURTLE_FRAMEWORK_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_ACTION_ENABLE=y
# CONFIG_MOCKTURTLE_FRAMEWORK_VARIABLE_ENABLE is not set
# CONFIG_MOCKTURTLE_FRAMEWORK_BUFFER_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_PING_ENABLE=y
CONFIG_MOCKTURTLE_FRAMEWORK_VERSION_ENABLE=y
#
# Mock Turtle library configuration
#
CONFIG_MOCKTURTLE_LIBRARY_PRINT_ENABLE=y
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_DEBUG_ENABLE is not set
CONFIG_MOCKTURTLE_LIBRARY_PRINT_ERROR_ENABLE=y
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_MESSAGE_ENABLE is not set
#include <mockturtle-rt.h>
int main()
{
int cpu, hmq;
const struct trtl_config_rom *cfgrom = trtl_config_rom_get();
struct trtl_fw_msg msg, msg_s;
struct payload *p;
uint32_t status;
pr_debug("TEST for: async send, sync\r\n");
cpu = trtl_get_core_id();
while (1) {
for (hmq = 0; hmq < cfgrom->n_hmq[cpu]; ++hmq) {
mq_map_in_message(TRTL_HMQ, hmq, &msg);
mq_map_out_message(TRTL_HMQ, hmq, &msg_s);
status = mq_poll_in_wait(TRTL_HMQ, 1 << hmq, 1);
if (!status)
continue;
if (!(msg.header->flags & TRTL_HMQ_HEADER_FLAG_SYNC))
continue;
/* For sync answer test */
pr_debug("SEND MESSAGES SYNC ANSWER\r\n");
mq_claim(TRTL_HMQ, hmq);
memcpy(msg_s.header, msg.header,
sizeof(struct trtl_hmq_header));
msg_s.header->flags &= ~TRTL_HMQ_HEADER_FLAG_SYNC;
msg_s.header->flags |= TRTL_HMQ_HEADER_FLAG_ACK;
memcpy(msg_s.payload, msg.payload, msg.header->len * 4);
mq_send(TRTL_HMQ, hmq);
mq_discard(TRTL_HMQ, hmq);
}
}
pp_printf("OK\r\n");
return 0;
}
-include ../Makefile.specific
OBJS = rmq-udp-send.o
OBJS += # add other object files that you need
OUTPUT = fw-rmq-udp-send
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
CFLAGS_OPT = -O0 # disable optimization
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = rmq-udp-send.o
OBJS += # add other object files that you need
OUTPUT = fw-rmq-udp-send
CFLAGS_OPT = -O0 # disable optimization
OBJS = rt-frm.o
OBJS += # add other object files that you need
OUTPUT = fw-rt-frm
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = rt-frm.o
OBJS += # add other object files that you need
OUTPUT = fw-rt-frm
-include ../Makefile.specific
OBJS = serial.o
OBJS += # add other object files that you need
OUTPUT = fw-serial
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
all:
include $(TRTL_SW)/firmware/Makefile
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = serial.o
OBJS += # add other object files that you need
OUTPUT = fw-serial
OBJS = sim-verif.o
OBJS += # add other object files that you need
OUTPUT = sim-verif
TRTL ?= ../../../
TRTL_SW = $(TRTL)/software
TRTL_FW = $(TRTL)/software/firmware
include $(TRTL_SW)/firmware/Makefile
all:
# Redirect all rules to MockTurtle
%:
$(MAKE) -C $(TRTL_FW) M=$(shell /bin/pwd) $@
OBJS = sim-verif.o
OBJS += # add other object files that you need
OUTPUT = sim-verif
......@@ -14,7 +14,7 @@ class TestConfig(object):
def test_valid_host(self, trtl_device, cfg):
assert trtl_device.rom.signature == cfg.signature
assert trtl_device.rom.app_id == cfg.app_id
assert ((trtl_device.rom.app_id == cfg.app_id) or (trtl_device.rom.app_id == cfg.app_id | 0x10001)) # SVEC or SPEC DEMO
assert trtl_device.rom.n_cpu == cfg.n_cpu
def test_valid_softcpu(self, trtl_cpu, cfg, firmware_file_config):
......@@ -27,5 +27,5 @@ class TestConfig(object):
msg = trtl_cpu.hmq[0].recv_msg()
assert msg.payload[0] == cfg.signature
assert msg.payload[5] == cfg.app_id
assert ((msg.payload[5] == cfg.app_id) or (msg.payload[5] == cfg.app_id | 0x10001))
assert msg.payload[6] == cfg.n_cpu
......@@ -3,6 +3,8 @@ import PyMockTurtle
import pytest
import serial
import time
import multiprocessing
import errno
@pytest.fixture
def trtl_binary_hmq_purge(trtl_firmware_dir):
......@@ -25,6 +27,25 @@ def trtl_binary_hmq_sync(trtl_firmware_dir):
"firmware/hmq-sync/fw-hmq-sync.bin")
def do_sync_mult(q, hmq_orig, msg):
try:
dev = PyMockTurtle.TrtlDevice(hmq_orig.trtl_dev.device_id)
hmq = dev.cpu[hmq_orig.idx_cpu].hmq[hmq_orig.idx_hmq]
q.put(hmq.sync_msg(msg))
except Exception as e:
print(e)
q.put(None)
def do_sync_no_multone(q, hmq):
try:
msg_r = hmq.sync_msg(PyMockTurtle.TrtlMessage())
q.put(0)
except OSError as err:
q.put(err.errno)
except Exception as e:
print(e)
q.put(None)
class TestHmq(object):
confirm = 'OK\r\n'
......@@ -98,20 +119,103 @@ class TestHmq(object):
trtl_cpu.load_application_file(trtl_binary_hmq_async_recv)
trtl_cpu.enable()
time.sleep(0.1)
time.sleep(0.5)
for hmq in trtl_cpu.hmq:
tot = trtl_cpu.trtl_dev.rom.hmq[trtl_cpu.idx_cpu][hmq.idx_hmq].entries
sa = hmq.get_stats()
assert sb[hmq]["message_received"] + tot == sa["message_received"]
for hmq in trtl_cpu.hmq:
tot = trtl_cpu.trtl_dev.rom.hmq[trtl_cpu.idx_cpu][hmq.idx_hmq].entries
for n in range(tot):
msg = hmq.recv_msg()
assert msg is not None
payload = list(msg.payload)
for i, val in enumerate(payload):
if i >= msg.header.len:
break
assert i == val
def __do_sync_msg(self, hmq, trtl_msg):
msg_r_a = []
# I do not understand why I can't make Pool() working.
# it complains about hmq
# with multiprocessing.Pool(processes=len(trtl_msg)) as p:
# msg_r_a = p.starmap(do_sync_msg,
# [(hmq, msg) for msg in trtl_msg])
proc = []
for msg in trtl_msg:
q = multiprocessing.Queue()
proc.append((q, multiprocessing.Process(target=do_sync_msg,
args=(q, hmq, msg))))
for q, p in proc:
p.start()
for q, p in proc:
msg_r_a.append(q.get())
p.join()
return msg_r_a
@pytest.mark.parametrize("nproc", range(1, 5))
def test_sync_no_multone(self, trtl_cpu, nproc):
"""The driver should not accept more than 1 sync message at time
from the same user. The test is successful when the driver refused
a second message"""
trtl_cpu.disable();
for hmq in trtl_cpu.hmq:
proc = []
for n in range(nproc):
q = multiprocessing.Queue()
p = multiprocessing.Process(target=do_sync_no_multone,
args=(q, hmq))
proc.append((q, p))
p.start()
count_busy = 0
count_timeout = 0
for q, p in proc:
err = q.get()
p.join()
assert err != 0
if err == errno.EBUSY:
count_busy = count_busy + 1
if err == PyMockTurtle.ETRTL_MSG_SYNC_FAILED_RECV_TIMEOUT:
count_timeout = count_timeout + 1
assert count_timeout == 1
assert count_busy == nproc - 1
assert count_timeout + count_busy == nproc
def test_sync_mult(self, trtl_cpu, trtl_msg, trtl_binary_hmq_sync):
"""Test Multiprocess sync messages. The firmware will copy back
the message as answer"""
trtl_cpu.load_application_file(trtl_binary_hmq_sync)
trtl_cpu.enable()
for hmq in trtl_cpu.hmq:
proc = []
sb = hmq.get_stats()
for msg in trtl_msg:
q = multiprocessing.Queue()
p = multiprocessing.Process(target=do_sync_mult,
args=(q, hmq, msg))
proc.append((q, p, msg))
p.start()
for q, p, msg in proc:
msg_r = q.get()
p.join()
assert msg_r is not None
assert msg_r.header.rt_app_id == msg.header.rt_app_id
assert msg_r.header.msg_id == msg.header.msg_id
assert msg_r.header.len == msg.header.len
assert msg_r.header.flags == PyMockTurtle.TrtlHmqHeader.TRTL_HMQ_HEADER_FLAG_ACK
for v1, v2 in zip(msg.payload, msg_r.payload):
assert v1 == v2
sa = hmq.get_stats()
assert sb[hmq]["message_received"] + tot == sa["message_received"]
assert sb["message_sent"] + len(trtl_msg) == sa["message_sent"]
assert sb["message_received"] + len(trtl_msg) == sa["message_received"]
def test_sync(self, trtl_cpu, trtl_msg, trtl_binary_hmq_async_send):
"""It sends the test messages on all available HMQ.
......@@ -131,8 +235,9 @@ class TestHmq(object):
assert msg_r.header.rt_app_id == msg.header.rt_app_id
assert msg_r.header.msg_id == msg.header.msg_id
assert msg_r.header.len == msg.header.len
assert msg_r.header.sync_id == msg.header.sync_id
assert msg.header.flags == PyMockTurtle.TrtlHmqHeader.TRTL_HMQ_HEADER_FLAG_SYNC
assert msg_r.header.flags == PyMockTurtle.TrtlHmqHeader.TRTL_HMQ_HEADER_FLAG_ACK
for v1, v2 in zip(msg.payload, msg_r.payload):
assert v1 == v2
msg_r = hmq.recv_msg()
assert msg_r is None
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment