pru: Rework command processing so that most of it is done on pru0

Change the command dispatch and response generation so that most of
the work is done on pru0 instead of pru1.  This allows more code to
fit into the limited space on pru1.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-06-29 18:14:39 -04:00
parent c1bd628ce5
commit e8356afa26
6 changed files with 222 additions and 101 deletions

View File

@ -13,19 +13,6 @@
#include "command.h" // output_P #include "command.h" // output_P
#include "sched.h" // DECL_TASK #include "sched.h" // DECL_TASK
#define MESSAGE_MIN 5
#define MESSAGE_MAX 64
#define MESSAGE_HEADER_SIZE 2
#define MESSAGE_TRAILER_SIZE 3
#define MESSAGE_POS_LEN 0
#define MESSAGE_POS_SEQ 1
#define MESSAGE_TRAILER_CRC 3
#define MESSAGE_TRAILER_SYNC 1
#define MESSAGE_PAYLOAD_MAX (MESSAGE_MAX - MESSAGE_MIN)
#define MESSAGE_SEQ_MASK 0x0f
#define MESSAGE_DEST 0x10
#define MESSAGE_SYNC 0x7E
static uint8_t next_sequence = MESSAGE_DEST; static uint8_t next_sequence = MESSAGE_DEST;
@ -68,8 +55,9 @@ parse_int(char **pp)
} }
// Parse an incoming command into 'args' // Parse an incoming command into 'args'
static char * char *
parsef(char *p, char *maxend, const struct command_parser *cp, uint32_t *args) command_parsef(char *p, char *maxend
, const struct command_parser *cp, uint32_t *args)
{ {
uint8_t num_params = READP(cp->num_params); uint8_t num_params = READP(cp->num_params);
const uint8_t *param_types = READP(cp->param_types); const uint8_t *param_types = READP(cp->param_types);
@ -301,7 +289,7 @@ command_dispatch(char *buf, uint8_t msglen)
uint8_t cmdid = *p++; uint8_t cmdid = *p++;
const struct command_parser *cp = command_lookup_parser(cmdid); const struct command_parser *cp = command_lookup_parser(cmdid);
uint32_t args[READP(cp->num_args)]; uint32_t args[READP(cp->num_args)];
p = parsef(p, msgend, cp, args); p = command_parsef(p, msgend, cp, args);
if (sched_is_shutdown() && !(READP(cp->flags) & HF_IN_SHUTDOWN)) { if (sched_is_shutdown() && !(READP(cp->flags) & HF_IN_SHUTDOWN)) {
sched_report_shutdown(); sched_report_shutdown();
continue; continue;

View File

@ -33,6 +33,19 @@
#define try_shutdown(msg) \ #define try_shutdown(msg) \
sched_try_shutdown(_DECL_STATIC_STR(msg)) sched_try_shutdown(_DECL_STATIC_STR(msg))
#define MESSAGE_MIN 5
#define MESSAGE_MAX 64
#define MESSAGE_HEADER_SIZE 2
#define MESSAGE_TRAILER_SIZE 3
#define MESSAGE_POS_LEN 0
#define MESSAGE_POS_SEQ 1
#define MESSAGE_TRAILER_CRC 3
#define MESSAGE_TRAILER_SYNC 1
#define MESSAGE_PAYLOAD_MAX (MESSAGE_MAX - MESSAGE_MIN)
#define MESSAGE_SEQ_MASK 0x0f
#define MESSAGE_DEST 0x10
#define MESSAGE_SYNC 0x7E
struct command_encoder { struct command_encoder {
uint8_t msg_id, max_size, num_params; uint8_t msg_id, max_size, num_params;
const uint8_t *param_types; const uint8_t *param_types;
@ -48,6 +61,8 @@ enum {
}; };
// command.c // command.c
char *command_parsef(char *p, char *maxend
, const struct command_parser *cp, uint32_t *args);
uint8_t command_encodef(char *buf, uint8_t buf_len uint8_t command_encodef(char *buf, uint8_t buf_len
, const struct command_encoder *ce, va_list args); , const struct command_encoder *ce, va_list args);
void command_sendf(const struct command_encoder *ce, ...); void command_sendf(const struct command_encoder *ce, ...);

View File

@ -6,28 +6,27 @@ CROSS_PREFIX=pru-
dirs-y += src/pru src/generic dirs-y += src/pru src/generic
dirs-y += lib/pru_rpmsg dirs-y += lib/pru_rpmsg
CFLAGS += -Os -mmcu=am335x.pru1 CFLAGS += -minrt -mmcu=am335x.pru1
CFLAGS += -Ilib/pru_rpmsg/include -Ilib/pru_rpmsg/include/am335x CFLAGS += -Ilib/pru_rpmsg/include -Ilib/pru_rpmsg/include/am335x
CFLAGS_klipper.elf := $(filter-out -mmcu=%, $(CFLAGS)) CFLAGS_klipper.elf := $(filter-out -mmcu=%, $(CFLAGS))
CFLAGS_klipper.elf += -Wl,-r -nostdlib -Wl,-T,src/pru/pru.lds CFLAGS_klipper.elf += -Wl,-r -nostdlib -Wl,-T,src/pru/pru.lds
CFLAGS_pru0.elf := $(filter-out -mmcu=%, $(CFLAGS)) -minrt -mmcu=am335x.pru0 CFLAGS_pru0.elf := $(filter-out -mmcu=%, $(CFLAGS)) -mmcu=am335x.pru0
CFLAGS_pru1.elf := $(CFLAGS) -minrt CFLAGS_pru1.elf := $(CFLAGS)
# Add source files # Add source files
src-y := $(filter-out debugcmds.c, $(src-y)) src-y += pru/main.c pru/gpio.c generic/timer_irq.c
src-y += pru/main.c pru/gpio.c
src-y += generic/crc16_ccitt.c generic/timer_irq.c
pru0-y := pru/pru0.c pru0-y := pru/pru0.c generic/crc16_ccitt.c command.c
pru0-y += ../lib/pru_rpmsg/pru_rpmsg.c ../lib/pru_rpmsg/pru_virtqueue.c pru0-y += ../lib/pru_rpmsg/pru_rpmsg.c ../lib/pru_rpmsg/pru_virtqueue.c
# Build the additional PRU0 binary # Build the PRU binaries
target-y += $(OUT)pru0.elf $(OUT)pru1.elf target-y += $(OUT)pru0.elf $(OUT)pru1.elf
$(OUT)pru0.elf: $(patsubst %.c, $(OUT)src/%.o,$(pru0-y)) $(OUT)pru0.elf: $(patsubst %.c, $(OUT)src/%.o,$(pru0-y))
@echo " Linking $@" @echo " Linking $@"
$(Q)$(CC) $(CFLAGS_pru0.elf) $^ -o $@ $(Q)$(CC) $(CFLAGS_klipper.elf) $^ -o $(OUT)pru0.o
$(Q)$(CC) $(CFLAGS_pru0.elf) $(OUT)pru0.o -o $@
$(OUT)pru1.elf: $(OUT)klipper.elf $(OUT)pru1.elf: $(OUT)klipper.elf
@echo " Linking $@" @echo " Linking $@"

View File

@ -19,16 +19,22 @@
#define R31_WRITE_IRQ_SELECT (1<<5) #define R31_WRITE_IRQ_SELECT (1<<5)
#define R31_WRITE_IRQ_OFFSET 16 #define R31_WRITE_IRQ_OFFSET 16
#define ALT_PRU_PTR(ptr) ((typeof(ptr))((uint32_t)(ptr) ^ 0x2000))
// Layout of shared memory // Layout of shared memory
struct shared_mem { struct shared_mem {
uint32_t signal; uint32_t signal;
uint32_t read_pos, read_count; const struct command_parser *next_command;
char read_data[512]; uint32_t next_command_args[16];
uint32_t send_push_pos, send_pop_pos; uint32_t send_push_pos, send_pop_pos;
struct { struct {
uint32_t count; uint32_t count;
char data[64]; char data[64];
} send_data[4]; } send_data[4];
const struct command_parser *command_index;
uint32_t command_index_size;
const struct command_parser *shutdown_handler;
char read_data[512];
}; };
#define SIGNAL_PRU0_WAITING 0xefefefef #define SIGNAL_PRU0_WAITING 0xefefefef

View File

@ -99,74 +99,69 @@ DECL_INIT(timer_init);
* Console IO * Console IO
****************************************************************/ ****************************************************************/
// Return a buffer (and length) containing any incoming messages
static char *
console_get_input(uint8_t *plen)
{
uint32_t read_count = readl(&SHARED_MEM->read_count);
if (read_count > 64)
read_count = 64;
*plen = read_count;
return SHARED_MEM->read_data;
}
// Remove from the receive buffer the given number of bytes
static void
console_pop_input(uint8_t len)
{
writel(&SHARED_MEM->read_count, 0);
}
// Process any incoming commands // Process any incoming commands
void void
console_task(void) console_task(void)
{ {
uint8_t buf_len, pop_count; const struct command_parser *cp = SHARED_MEM->next_command;
char *buf = console_get_input(&buf_len); if (!cp)
int8_t ret = command_find_block(buf, buf_len, &pop_count); return;
if (ret) barrier();
command_dispatch(buf, pop_count);
console_pop_input(pop_count); if (sched_is_shutdown() && !(cp->flags & HF_IN_SHUTDOWN)) {
sched_report_shutdown();
} else {
void (*func)(uint32_t*) = cp->func;
func(SHARED_MEM->next_command_args);
}
writel(&SHARED_MEM->next_command, 0);
} }
DECL_TASK(console_task); DECL_TASK(console_task);
// Return an output buffer that the caller may fill with transmit messages
static char *
console_get_output(uint8_t len)
{
if (len > sizeof(SHARED_MEM->send_data[0].data))
return NULL;
uint32_t send_push_pos = SHARED_MEM->send_push_pos;
if (readl(&SHARED_MEM->send_data[send_push_pos].count))
// Queue full
return NULL;
return SHARED_MEM->send_data[send_push_pos].data;
}
// Accept the given number of bytes added to the transmit buffer
static void
console_push_output(uint8_t len)
{
uint32_t send_push_pos = SHARED_MEM->send_push_pos;
writel(&SHARED_MEM->send_data[send_push_pos].count, len);
write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU0_EVENT - R31_WRITE_IRQ_OFFSET));
SHARED_MEM->send_push_pos = (
(send_push_pos + 1) % ARRAY_SIZE(SHARED_MEM->send_data));
}
// Encode and transmit a "response" message // Encode and transmit a "response" message
void void
console_sendf(const struct command_encoder *ce, va_list args) console_sendf(const struct command_encoder *ce, va_list args)
{ {
uint8_t buf_len = ce->max_size; // Verify space for message
char *buf = console_get_output(buf_len); uint32_t max_size = ce->max_size;
if (!buf) if (max_size > sizeof(SHARED_MEM->send_data[0].data))
return; return;
uint8_t msglen = command_encodef(buf, buf_len, ce, args); uint32_t send_push_pos = SHARED_MEM->send_push_pos;
command_add_frame(buf, msglen); if (readl(&SHARED_MEM->send_data[send_push_pos].count))
console_push_output(msglen); // Queue full
return;
// Generate message
char *buf = SHARED_MEM->send_data[send_push_pos].data;
uint32_t msglen = command_encodef(buf, max_size, ce, args);
// Signal PRU0 to transmit message
writel(&SHARED_MEM->send_data[send_push_pos].count, msglen);
write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU0_EVENT - R31_WRITE_IRQ_OFFSET));
SHARED_MEM->send_push_pos = (
(send_push_pos + 1) % ARRAY_SIZE(SHARED_MEM->send_data));
} }
void
console_shutdown(void)
{
writel(&SHARED_MEM->next_command, 0);
}
DECL_SHUTDOWN(console_shutdown);
// Handle shutdown request from PRU0
static void
shutdown_handler(uint32_t *args)
{
shutdown("Request from PRU0");
}
// Empty message (for ack/nak transmission)
const struct command_parser shutdown_request = {
.func = shutdown_handler,
};
/**************************************************************** /****************************************************************
* Allocator * Allocator
@ -234,6 +229,9 @@ main(void)
// Wait for PRU0 to initialize // Wait for PRU0 to initialize
while (readl(&SHARED_MEM->signal) != SIGNAL_PRU0_WAITING) while (readl(&SHARED_MEM->signal) != SIGNAL_PRU0_WAITING)
; ;
SHARED_MEM->command_index = command_index;
SHARED_MEM->command_index_size = command_index_size;
SHARED_MEM->shutdown_handler = &shutdown_request;
writel(&SHARED_MEM->signal, SIGNAL_PRU1_READY); writel(&SHARED_MEM->signal, SIGNAL_PRU1_READY);
sched_main(); sched_main();

View File

@ -4,6 +4,7 @@
// //
// This file may be distributed under the terms of the GNU GPLv3 license. // This file may be distributed under the terms of the GNU GPLv3 license.
#include <setjmp.h> // setjmp
#include <stdint.h> // uint32_t #include <stdint.h> // uint32_t
#include <string.h> // memset #include <string.h> // memset
#include <pru/io.h> // write_r31 #include <pru/io.h> // write_r31
@ -14,8 +15,13 @@
#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG #include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
#include <rsc_types.h> // resource_table #include <rsc_types.h> // resource_table
#include "board/io.h" // readl #include "board/io.h" // readl
#include "command.h" // command_add_frame
#include "compiler.h" // __section #include "compiler.h" // __section
#include "internal.h" // SHARED_MEM #include "internal.h" // SHARED_MEM
#include "sched.h" // sched_shutdown
struct pru_rpmsg_transport transport;
static uint16_t transport_dst;
/**************************************************************** /****************************************************************
@ -26,10 +32,9 @@
#define CHAN_DESC "Channel 30" #define CHAN_DESC "Channel 30"
#define CHAN_PORT 30 #define CHAN_PORT 30
static uint16_t transport_dst; // Check if there is data to be sent from PRU1 to the host
static void static void
check_can_send(struct pru_rpmsg_transport *transport) check_can_send(void)
{ {
for (;;) { for (;;) {
uint32_t send_pop_pos = SHARED_MEM->send_pop_pos; uint32_t send_pop_pos = SHARED_MEM->send_pop_pos;
@ -37,8 +42,9 @@ check_can_send(struct pru_rpmsg_transport *transport)
if (!count) if (!count)
// Queue empty // Queue empty
break; break;
command_add_frame(SHARED_MEM->send_data[send_pop_pos].data, count);
pru_rpmsg_send( pru_rpmsg_send(
transport, CHAN_PORT, transport_dst &transport, CHAN_PORT, transport_dst
, &SHARED_MEM->send_data[send_pop_pos].data, count); , &SHARED_MEM->send_data[send_pop_pos].data, count);
writel(&SHARED_MEM->send_data[send_pop_pos].count, 0); writel(&SHARED_MEM->send_data[send_pop_pos].count, 0);
SHARED_MEM->send_pop_pos = ( SHARED_MEM->send_pop_pos = (
@ -46,34 +52,135 @@ check_can_send(struct pru_rpmsg_transport *transport)
} }
} }
// Wait for PRU1 to finish processing a command
static void static void
check_can_read(struct pru_rpmsg_transport *transport) wait_pru1_command(void)
{ {
if (readl(&SHARED_MEM->read_count)) while (readl(&SHARED_MEM->next_command))
// main processing pru is busy check_can_send();
return; check_can_send();
uint16_t dst, len; }
int16_t ret = pru_rpmsg_receive(
transport, &transport_dst, &dst, SHARED_MEM->read_data, &len); // Signal PRU1 that a new command is ready
if (ret || !len) static void
return; send_pru1_command(const struct command_parser *cp)
SHARED_MEM->read_pos = 0; {
writel(&SHARED_MEM->read_count, len); barrier();
SHARED_MEM->next_command = cp;
barrier();
write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU1_EVENT - R31_WRITE_IRQ_OFFSET)); write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU1_EVENT - R31_WRITE_IRQ_OFFSET));
} }
// Instruct PRU1 to shutdown
static void static void
process_io(struct pru_rpmsg_transport *transport) send_pru1_shutdown(void)
{
wait_pru1_command();
send_pru1_command(SHARED_MEM->shutdown_handler);
wait_pru1_command();
}
// Dispatch all the commands in a message block
static void
do_dispatch(char *buf, uint32_t msglen)
{
char *p = &buf[MESSAGE_HEADER_SIZE];
char *msgend = &buf[msglen-MESSAGE_TRAILER_SIZE];
while (p < msgend) {
// Parse command
uint8_t cmdid = *p++;
const struct command_parser *cp = &SHARED_MEM->command_index[cmdid];
if (!cmdid || cmdid >= SHARED_MEM->command_index_size
|| cp->num_args > ARRAY_SIZE(SHARED_MEM->next_command_args)) {
send_pru1_shutdown();
return;
}
p = command_parsef(p, msgend, cp, SHARED_MEM->next_command_args);
send_pru1_command(ALT_PRU_PTR(cp));
wait_pru1_command();
}
}
// See if there are commands from the host ready to be processed
static void
check_can_read(void)
{
// Read data
uint16_t dst, len;
char *p = SHARED_MEM->read_data;
int16_t ret = pru_rpmsg_receive(&transport, &transport_dst, &dst, p, &len);
if (ret)
return;
// Parse data into message blocks
for (;;) {
uint8_t pop_count, msglen = len > 64 ? 64 : len;
int8_t ret = command_find_block(p, msglen, &pop_count);
if (!ret)
break;
if (ret > 0)
do_dispatch(p, pop_count);
p += pop_count;
len -= pop_count;
}
}
// Main processing loop
static void
process_io(void)
{ {
for (;;) { for (;;) {
if (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET)))) if (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET))))
continue; continue;
CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT); CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT);
check_can_send(transport); check_can_send();
check_can_read(transport); check_can_read();
} }
} }
// Startup initialization
static void
setup_io(void)
{
// Fixup pointers in command_parsers
SHARED_MEM->command_index = ALT_PRU_PTR(SHARED_MEM->command_index);
struct command_parser *p = (void*)SHARED_MEM->command_index;
int i;
for (i=0; i<SHARED_MEM->command_index_size; i++, p++)
if (p->param_types)
p->param_types = ALT_PRU_PTR(p->param_types);
}
/****************************************************************
* Compatibility wrappers
****************************************************************/
// shutdown() compatibility code
uint8_t ctr_lookup_static_string(const char *str)
{
return 0;
}
static jmp_buf shutdown_jmp;
// Handle shutdown()
void
sched_shutdown(uint_fast8_t reason)
{
longjmp(shutdown_jmp, 1);
}
// Generate messages - only used for ack/nak messages
void
console_sendf(const struct command_encoder *ce, va_list args)
{
char buf[MESSAGE_MIN];
command_add_frame(buf, sizeof(buf));
pru_rpmsg_send(&transport, CHAN_PORT, transport_dst, buf, sizeof(buf));
}
/**************************************************************** /****************************************************************
* Peripheral reset * Peripheral reset
@ -241,7 +348,6 @@ main(void)
; ;
/* Initialize the RPMsg transport structure */ /* Initialize the RPMsg transport structure */
struct pru_rpmsg_transport transport;
pru_rpmsg_init(&transport, pru_rpmsg_init(&transport,
&resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring0,
&resourceTable.rpmsg_vring1, &resourceTable.rpmsg_vring1,
@ -265,5 +371,14 @@ main(void)
; ;
writel(&SHARED_MEM->signal, 0); writel(&SHARED_MEM->signal, 0);
process_io(&transport); // Setup incoming message parser
setup_io();
// Support shutdown
int ret = setjmp(shutdown_jmp);
if (ret)
send_pru1_shutdown();
// Main loop
process_io();
} }