Replacing cvirtos.elf with equivalent Arduino-style code

The example code below may be compiled using the SDK v1.0.9 plus the Arduino extension as documented at e.g. Introduction | Milk-V. I’ve not got loading working over a serial port, but using scp works well: if the file is renamed to arduino.elf it is automatically picked up at the next reboot.

#include "mailbox.h"
#define HOST_SERIAL Serial0

// These are from linux_5.10/drivers/soc/cvitek, https://milkv.io/docs/duo/getting-started/rtoscore etc.

enum SYS_CMD_ID {
    CMD_TEST_A  = 0x10,
    CMD_TEST_B,
    CMD_TEST_C,
    CMD_DUO_LED,
    SYS_CMD_INFO_LIMIT,
};

enum DUO_LED_STATUS {
  DUO_LED_ON  = 0x02,
  DUO_LED_OFF,
    DUO_LED_DONE,
};

struct valid_t {
        unsigned char linux_valid;
        unsigned char rtos_valid;
} __attribute__((packed));

typedef union resv_t {
        struct valid_t valid;
        unsigned short mstime; // 0 : noblock, -1 : block infinite
} resv_t;

typedef struct cmdqu_t cmdqu_t;
/* cmdqu size should be 8 bytes because of mailbox buffer size */
struct cmdqu_t {
        unsigned char ip_id;
        unsigned char cmd_id : 7;
        unsigned char block : 1;
        union resv_t resv;
        unsigned int  param_ptr;
} __attribute__((packed)) __attribute__((aligned(0x8)));


struct cmdqu_t cmd = {0};
bool dequeued = false;


void print_hex2(byte x) {
  if (x < 0x10)
    HOST_SERIAL.print("0");
  HOST_SERIAL.print(x, HEX);
}


// Function prototypes are from .arduino15/packages/sophgo/hardware/SG200X/0.2.3/cores/sg200x/include


void receive_callback(MailboxMsg msg) {
  memcpy(&cmd, msg.data, sizeof(cmd));
  *(msg.data) = 0; // Messages need to be cleared manually after receiving them
  dequeued = true;
}


void setup() {
  HOST_SERIAL.begin(115200); // Note: some boards do not support 9600 Baud etc.
  while (!HOST_SERIAL) ; // Wait for serial port to connect. Needed for Native USB only
  pinMode(LED_BUILTIN, OUTPUT);
  mailbox_init(false);
  mailbox_register(0, receive_callback);
  mailbox_enable_receive(0);
}


void loop() {
  HOST_SERIAL.println("Starting mailbox_target_demo...");
  while (1)
    if (dequeued) {

// A message was received by the ISR and copied into cmd.
      
      HOST_SERIAL.print("ip_id:  0x");
      print_hex2(cmd.ip_id);
      HOST_SERIAL.println();
      HOST_SERIAL.print("cmd_id: 0x");
      print_hex2(cmd.cmd_id);
      HOST_SERIAL.println();
      HOST_SERIAL.print("block:  0x");
      print_hex2(cmd.block);
      HOST_SERIAL.println();
      HOST_SERIAL.print("param:  ");
      HOST_SERIAL.println(cmd.param_ptr);

      switch (cmd.cmd_id) {
        case CMD_DUO_LED: if (cmd.param_ptr == DUO_LED_ON)
                            digitalWrite(LED_BUILTIN, HIGH);
                          else 
                            digitalWrite(LED_BUILTIN, LOW);
        default:

/* Code below isn't right. A response is needed here to prevent the sender        
 * (i.e. master, running on the Linux core) objecting about a timeout after the
 * first message has been sent using RTOS_CMDQU_SEND_WAIT. I don't know whether
 * this code is actually wrong, or if it merely needs to be put "nearer" the
 * ISR i.e. in the callback... and in that case whether it should be before or
 * after the pointer is zeroed.
 * 
          cmdqu_t rsp = {0};
          MailboxMsg msg = {0};
          memcpy(&rsp, &cmd, sizeof(rsp));
          cmd.param_ptr = DUO_LED_DONE;
          msg.data = (uint32_t *)&cmd;
          mailbox_write(msg);
*/ ;
      }
      dequeued = false;
    } else
      delay(1);
}

I believe it’s good enough to act as a foundation for other projects, for example the “small” processor looks very useful for applying PID to servo PWM control signals. As noted near the end however there’s a rough edge: I’ve not found how to send a response to the initial command so that there isn’t a timeout error. This isn’t a show-stopper, but if anybody else can make sense of the limited documentation and examples for this facility I’d be interested.

MarkMLl

2 Likes