pru: Move communication code to second PRU
Perform input and output in the second PRU so that more space is available in the primary PRU. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
b6f24e78ac
commit
b85755c0ff
|
@ -6,22 +6,24 @@ CROSS_PREFIX=pru-
|
|||
dirs-y += src/pru src/generic
|
||||
dirs-y += lib/pru_rpmsg
|
||||
|
||||
CFLAGS += -Os -mmcu=am335x.pru0
|
||||
CFLAGS += -Os -mmcu=am335x.pru1
|
||||
CFLAGS += -Ilib/pru_rpmsg/include -Ilib/pru_rpmsg/include/am335x
|
||||
|
||||
CFLAGS_klipper.o := $(filter-out -mmcu=%, $(CFLAGS_klipper.o))
|
||||
CFLAGS_klipper.elf := $(CFLAGS) -minrt -T src/pru/pru.lds
|
||||
CFLAGS_pru1.elf := $(filter-out -mmcu=%, $(CFLAGS)) -mmcu=am335x.pru1
|
||||
CFLAGS_pru0.elf := $(filter-out -mmcu=%, $(CFLAGS)) -minrt -mmcu=am335x.pru0
|
||||
|
||||
# Add source files
|
||||
src-y := $(filter-out debugcmds.c, $(src-y))
|
||||
src-y += pru/main.c pru/console.c pru/gpio.c
|
||||
src-y += pru/main.c pru/gpio.c
|
||||
src-y += generic/crc16_ccitt.c generic/timer_irq.c
|
||||
src-y += ../lib/pru_rpmsg/pru_rpmsg.c ../lib/pru_rpmsg/pru_virtqueue.c
|
||||
|
||||
# Build the additional PRU1 binary
|
||||
target-y += $(OUT)pru1.elf
|
||||
pru0-y := pru/pru0.c
|
||||
pru0-y += ../lib/pru_rpmsg/pru_rpmsg.c ../lib/pru_rpmsg/pru_virtqueue.c
|
||||
|
||||
$(OUT)pru1.elf: $(OUT)src/pru/pru1.o
|
||||
# Build the additional PRU0 binary
|
||||
target-y += $(OUT)pru0.elf
|
||||
|
||||
$(OUT)pru0.elf: $(patsubst %.c, $(OUT)src/%.o,$(pru0-y))
|
||||
@echo " Linking $@"
|
||||
$(Q)$(CC) $(CFLAGS_pru1.elf) $^ -o $@
|
||||
$(Q)$(CC) $(CFLAGS_pru0.elf) $^ -o $@
|
||||
|
|
|
@ -2,17 +2,38 @@
|
|||
#define __PRU_INTERNAL_H
|
||||
// Local definitions for PRU code
|
||||
|
||||
#define IEP_IRQ 0
|
||||
#define GOT_ARM_IRQ 1
|
||||
#define WAKE_ARM_IRQ 2
|
||||
#include <stdint.h> // uint32_t
|
||||
|
||||
#define IEP_EVENT 7
|
||||
#define GOT_ARM_EVENT 17
|
||||
#define WAKE_ARM_EVENT 16
|
||||
#define IEP_EVENT 7
|
||||
#define KICK_ARM_EVENT 16
|
||||
#define KICK_PRU0_FROM_ARM_EVENT 17
|
||||
#define KICK_PRU0_EVENT 18
|
||||
#define KICK_PRU1_EVENT 19
|
||||
|
||||
#define WAKE_PRU0_IRQ 0
|
||||
#define WAKE_PRU1_IRQ 1
|
||||
#define WAKE_ARM_IRQ 2
|
||||
|
||||
#define R31_IRQ_OFFSET 30
|
||||
|
||||
// console.c
|
||||
void console_init(void);
|
||||
#define R31_WRITE_IRQ_SELECT (1<<5)
|
||||
#define R31_WRITE_IRQ_OFFSET 16
|
||||
|
||||
// Layout of shared memory
|
||||
struct shared_mem {
|
||||
uint32_t signal;
|
||||
uint32_t read_pos, read_count;
|
||||
char read_data[512];
|
||||
uint32_t send_push_pos, send_pop_pos;
|
||||
struct {
|
||||
uint32_t count;
|
||||
char data[64];
|
||||
} send_data[4];
|
||||
};
|
||||
|
||||
#define SIGNAL_PRU0_WAITING 0xefefefef
|
||||
#define SIGNAL_PRU1_READY 0xabababab
|
||||
|
||||
static struct shared_mem *SHARED_MEM = (void*)0x10000;
|
||||
|
||||
#endif // internal.h
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
#include <stdint.h> // uint32_t
|
||||
#include <string.h> // memset
|
||||
#include <pru/io.h> // read_r31
|
||||
#include <pru_cfg.h> // CT_CFG
|
||||
#include <pru_iep.h> // CT_IEP
|
||||
#include <pru_intc.h> // CT_INTC
|
||||
#include "autoconf.h" // CONFIG_CLOCK_FREQ
|
||||
#include "board/misc.h" // timer_from_us
|
||||
#include <rsc_types.h> // resource_table
|
||||
#include "board/misc.h" // alloc_chunk
|
||||
#include "board/io.h" // readl
|
||||
#include "board/irq.h" // irq_disable
|
||||
#include "command.h" // shutdown
|
||||
#include "generic/timer_irq.h" // timer_dispatch_many
|
||||
#include "internal.h" // IEP_IRQ
|
||||
#include "internal.h" // SHARED_MEM
|
||||
#include "sched.h" // sched_main
|
||||
|
||||
DECL_CONSTANT(MCU, "pru");
|
||||
|
@ -50,7 +50,6 @@ static void
|
|||
timer_set(uint32_t value)
|
||||
{
|
||||
CT_IEP.TMR_CMP0 = value;
|
||||
CT_INTC.SECR0 = 1 << IEP_EVENT;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
@ -62,14 +61,17 @@ timer_read_time(void)
|
|||
static void
|
||||
_irq_poll(void)
|
||||
{
|
||||
CT_IEP.TMR_CMP_STS = 0xff;
|
||||
uint32_t next = timer_dispatch_many();
|
||||
timer_set(next);
|
||||
if (CT_INTC.SECR0 & (1 << IEP_EVENT)) {
|
||||
CT_IEP.TMR_CMP_STS = 0xff;
|
||||
uint32_t next = timer_dispatch_many();
|
||||
timer_set(next);
|
||||
}
|
||||
CT_INTC.SECR0 = (1 << IEP_EVENT) | (1 << KICK_PRU1_EVENT);
|
||||
}
|
||||
void
|
||||
irq_poll(void)
|
||||
{
|
||||
if (read_r31() & (1 << (IEP_IRQ + R31_IRQ_OFFSET)))
|
||||
if (read_r31() & (1 << (WAKE_PRU1_IRQ + R31_IRQ_OFFSET)))
|
||||
_irq_poll();
|
||||
}
|
||||
|
||||
|
@ -87,13 +89,62 @@ DECL_SHUTDOWN(timer_shutdown);
|
|||
static void
|
||||
timer_init(void)
|
||||
{
|
||||
timer_set(0);
|
||||
CT_IEP.TMR_CNT = 0;
|
||||
CT_IEP.TMR_CMP_CFG = 0x01 << 1;
|
||||
CT_IEP.TMR_GLB_CFG = 0x11;
|
||||
timer_shutdown();
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Console IO
|
||||
****************************************************************/
|
||||
|
||||
// Return a buffer (and length) containing any incoming messages
|
||||
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
|
||||
void
|
||||
console_pop_input(uint8_t len)
|
||||
{
|
||||
barrier();
|
||||
writel(&SHARED_MEM->read_count, 0);
|
||||
}
|
||||
|
||||
// Return an output buffer that the caller may fill with transmit messages
|
||||
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
|
||||
void
|
||||
console_push_output(uint8_t len)
|
||||
{
|
||||
uint32_t send_push_pos = SHARED_MEM->send_push_pos;
|
||||
barrier();
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Allocator
|
||||
****************************************************************/
|
||||
|
@ -131,6 +182,24 @@ alloc_chunks(size_t size, size_t count, size_t *avail)
|
|||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Resource table
|
||||
****************************************************************/
|
||||
|
||||
struct my_resource_table {
|
||||
struct resource_table base;
|
||||
|
||||
uint32_t offset[1]; /* Should match 'num' in actual definition */
|
||||
} resourceTable __visible __section(".resource_table") = {
|
||||
{
|
||||
1, /* Resource table version: only version 1 is
|
||||
* supported by the current driver */
|
||||
0, /* number of entries in the table */
|
||||
{ 0, 0 }, /* reserved, must be zero */
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Startup
|
||||
****************************************************************/
|
||||
|
@ -139,10 +208,11 @@ alloc_chunks(size_t size, size_t count, size_t *avail)
|
|||
int
|
||||
main(void)
|
||||
{
|
||||
// allow access to external memory
|
||||
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
|
||||
// Wait for PRU0 to initialize
|
||||
while (readl(&SHARED_MEM->signal) != SIGNAL_PRU0_WAITING)
|
||||
;
|
||||
writel(&SHARED_MEM->signal, SIGNAL_PRU1_READY);
|
||||
|
||||
console_init();
|
||||
timer_init();
|
||||
|
||||
sched_main();
|
||||
|
|
|
@ -1,101 +1,83 @@
|
|||
// PRU input/output via RPMsg
|
||||
// Code to handle IO on PRU0 and pass the messages to PRU1
|
||||
//
|
||||
// Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <stdint.h> // uint32_t
|
||||
#include <string.h> // memcpy
|
||||
#include <pru/io.h> // read_r31
|
||||
#include <string.h> // memset
|
||||
#include <pru/io.h> // write_r31
|
||||
#include <pru_cfg.h> // CT_CFG
|
||||
#include <pru_intc.h> // CT_INTC
|
||||
#include <pru_rpmsg.h> // pru_rpmsg_send
|
||||
#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
|
||||
#include "board/misc.h" // console_get_input
|
||||
#include "internal.h" // WAKE_ARM_EVENT
|
||||
#include "sched.h" // DECL_INIT
|
||||
#include <rsc_types.h> // resource_table
|
||||
#include "board/io.h" // readl
|
||||
#include "compiler.h" // __section
|
||||
#include "internal.h" // SHARED_MEM
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* IO
|
||||
****************************************************************/
|
||||
|
||||
#define CHAN_NAME "rpmsg-pru"
|
||||
#define CHAN_DESC "Channel 30"
|
||||
#define CHAN_PORT 30
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Console interface
|
||||
****************************************************************/
|
||||
static uint16_t transport_dst;
|
||||
|
||||
static void
|
||||
clear_pending(void)
|
||||
check_can_send(struct pru_rpmsg_transport *transport)
|
||||
{
|
||||
CT_INTC.SECR0 = 1 << GOT_ARM_EVENT;
|
||||
}
|
||||
|
||||
static struct pru_rpmsg_transport transport;
|
||||
static uint16_t transport_dst;
|
||||
static char input_data[64];
|
||||
|
||||
// XXX
|
||||
struct pru_rpmsg_hdr {
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint32_t reserved;
|
||||
uint16_t len;
|
||||
uint16_t flags;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
// Return a buffer (and length) containing any incoming messages
|
||||
char *
|
||||
console_get_input(uint8_t *plen)
|
||||
{
|
||||
if (!(read_r31() & (1 << (GOT_ARM_IRQ + R31_IRQ_OFFSET))))
|
||||
goto nodata;
|
||||
struct pru_rpmsg_hdr *msg;
|
||||
uint32_t msg_len;
|
||||
int16_t head = pru_virtqueue_get_avail_buf(
|
||||
&transport.virtqueue1, (void**)&msg, &msg_len);
|
||||
if (head < 0) {
|
||||
clear_pending();
|
||||
goto nodata;
|
||||
for (;;) {
|
||||
uint32_t send_pop_pos = SHARED_MEM->send_pop_pos;
|
||||
uint32_t count = readl(&SHARED_MEM->send_data[send_pop_pos].count);
|
||||
if (!count)
|
||||
// Queue empty
|
||||
break;
|
||||
pru_rpmsg_send(
|
||||
transport, CHAN_PORT, transport_dst
|
||||
, &SHARED_MEM->send_data[send_pop_pos].data, count);
|
||||
barrier();
|
||||
writel(&SHARED_MEM->send_data[send_pop_pos].count, 0);
|
||||
SHARED_MEM->send_pop_pos = (
|
||||
(send_pop_pos + 1) % ARRAY_SIZE(SHARED_MEM->send_data));
|
||||
}
|
||||
transport_dst = msg->src;
|
||||
int len = msg->len < sizeof(input_data) ? msg->len : sizeof(input_data);
|
||||
memcpy(input_data, msg->data, len);
|
||||
pru_virtqueue_add_used_buf(&transport.virtqueue1, head, msg_len);
|
||||
pru_virtqueue_kick(&transport.virtqueue1);
|
||||
*plen = len;
|
||||
return input_data;
|
||||
nodata:
|
||||
*plen = 0;
|
||||
return input_data;
|
||||
}
|
||||
|
||||
// Remove from the receive buffer the given number of bytes
|
||||
void
|
||||
console_pop_input(uint8_t len)
|
||||
static void
|
||||
check_can_read(struct pru_rpmsg_transport *transport)
|
||||
{
|
||||
if (readl(&SHARED_MEM->read_count))
|
||||
// main processing pru is busy
|
||||
return;
|
||||
uint16_t dst, len;
|
||||
int16_t ret = pru_rpmsg_receive(
|
||||
transport, &transport_dst, &dst, SHARED_MEM->read_data, &len);
|
||||
if (ret || !len)
|
||||
return;
|
||||
SHARED_MEM->read_pos = 0;
|
||||
barrier();
|
||||
writel(&SHARED_MEM->read_count, len);
|
||||
write_r31(R31_WRITE_IRQ_SELECT | (KICK_PRU1_EVENT - R31_WRITE_IRQ_OFFSET));
|
||||
}
|
||||
|
||||
static char output_data[64];
|
||||
|
||||
// Return an output buffer that the caller may fill with transmit messages
|
||||
char *
|
||||
console_get_output(uint8_t len)
|
||||
static void
|
||||
process_io(struct pru_rpmsg_transport *transport)
|
||||
{
|
||||
if (len > sizeof(output_data))
|
||||
return NULL;
|
||||
return output_data;
|
||||
}
|
||||
|
||||
// Accept the given number of bytes added to the transmit buffer
|
||||
void
|
||||
console_push_output(uint8_t len)
|
||||
{
|
||||
pru_rpmsg_send(&transport, CHAN_PORT, transport_dst, output_data, len);
|
||||
for (;;) {
|
||||
if (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET))))
|
||||
continue;
|
||||
CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT);
|
||||
check_can_send(transport);
|
||||
check_can_read(transport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* resource table
|
||||
* Resource table
|
||||
****************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -118,9 +100,11 @@ console_push_output(uint8_t len)
|
|||
|
||||
/* Mapping sysevts to a channel. Each pair contains a sysevt, channel. */
|
||||
static struct ch_map pru_intc_map[] = {
|
||||
{IEP_EVENT, IEP_IRQ},
|
||||
{WAKE_ARM_EVENT, WAKE_ARM_IRQ},
|
||||
{GOT_ARM_EVENT, GOT_ARM_IRQ},
|
||||
{IEP_EVENT, WAKE_PRU1_IRQ},
|
||||
{KICK_ARM_EVENT, WAKE_ARM_IRQ},
|
||||
{KICK_PRU0_FROM_ARM_EVENT, WAKE_PRU0_IRQ},
|
||||
{KICK_PRU0_EVENT, WAKE_PRU0_IRQ},
|
||||
{KICK_PRU1_EVENT, WAKE_PRU1_IRQ},
|
||||
};
|
||||
|
||||
struct my_resource_table {
|
||||
|
@ -185,7 +169,7 @@ struct my_resource_table {
|
|||
0x0000,
|
||||
/* Channel-to-host mapping, 255 for unused */
|
||||
{
|
||||
IEP_IRQ, GOT_ARM_IRQ, WAKE_ARM_IRQ,
|
||||
WAKE_PRU0_IRQ, WAKE_PRU1_IRQ, WAKE_ARM_IRQ,
|
||||
HOST_UNUSED, HOST_UNUSED, HOST_UNUSED,
|
||||
HOST_UNUSED, HOST_UNUSED, HOST_UNUSED, HOST_UNUSED
|
||||
},
|
||||
|
@ -200,15 +184,20 @@ struct my_resource_table {
|
|||
|
||||
|
||||
/****************************************************************
|
||||
* RPMsg init
|
||||
* Startup
|
||||
****************************************************************/
|
||||
|
||||
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
||||
|
||||
void
|
||||
console_init(void)
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
clear_pending();
|
||||
// allow access to external memory
|
||||
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
|
||||
|
||||
// clear all irqs
|
||||
CT_INTC.SECR0 = 0xffffffff;
|
||||
CT_INTC.SECR1 = 0xffffffff;
|
||||
|
||||
/* Make sure the Linux drivers are ready for RPMsg communication */
|
||||
volatile uint8_t *status = &resourceTable.rpmsg_vdev.status;
|
||||
|
@ -216,15 +205,25 @@ console_init(void)
|
|||
;
|
||||
|
||||
/* Initialize the RPMsg transport structure */
|
||||
struct pru_rpmsg_transport transport;
|
||||
pru_rpmsg_init(&transport,
|
||||
&resourceTable.rpmsg_vring0,
|
||||
&resourceTable.rpmsg_vring1,
|
||||
WAKE_ARM_EVENT,
|
||||
GOT_ARM_EVENT);
|
||||
KICK_ARM_EVENT,
|
||||
KICK_PRU0_FROM_ARM_EVENT);
|
||||
|
||||
/* Create the RPMsg channel between the PRU and ARM user space
|
||||
* using the transport structure. */
|
||||
while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME
|
||||
, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS)
|
||||
;
|
||||
|
||||
// Wait for PRU1 to be ready
|
||||
memset(SHARED_MEM, 0, sizeof(*SHARED_MEM));
|
||||
writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING);
|
||||
while (readl(&SHARED_MEM->signal) != SIGNAL_PRU1_READY)
|
||||
;
|
||||
writel(&SHARED_MEM->signal, 0);
|
||||
|
||||
process_io(&transport);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// Code to halt the unneeded PRU1
|
||||
//
|
||||
// Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <stdint.h> // uint32_t
|
||||
#include <pru/io.h> // __halt
|
||||
#include <rsc_types.h> // resource_table
|
||||
#include "compiler.h" // __section
|
||||
|
||||
struct my_resource_table {
|
||||
struct resource_table base;
|
||||
|
||||
uint32_t offset[1]; /* Should match 'num' in actual definition */
|
||||
} resourceTable __section(".resource_table") = {
|
||||
{
|
||||
1, /* Resource table version: only version 1 is
|
||||
* supported by the current driver */
|
||||
0, /* number of entries in the table */
|
||||
{ 0, 0 }, /* reserved, must be zero */
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
__halt();
|
||||
}
|
Loading…
Reference in New Issue