Skip to content

Commit

Permalink
Implement #157 - add a fake ESP32 to the mix.
Browse files Browse the repository at this point in the history
  • Loading branch information
vintagepc committed Mar 14, 2024
1 parent 7d80e33 commit d1c6194
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 36 deletions.
14 changes: 10 additions & 4 deletions hw/arm/prusa/launch-xl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@ esac
MAIN_FW="public.bin"
MODBED_BL="newbl/bootloader-v296-prusa_modular_bed-1.0.elf"
DWARF_BL="newbl/bootloader-v296-prusa_dwarf-1.0.elf"
ESP_SERIAL="-chardev serial,id=stm32uart7,path=/dev/ttyUSB0"
# ESP_SERIAL="-chardev pty,id=stm32uart7"
#ESP_SERIAL="-chardev serial,id=stm32uart7,path=/dev/ttyUSB1"
#ESP_SERIAL="-chardev socket,id=stm32uart7,path=/tmp/esp"
#ESP_SERIAL="-chardev pty,id=stm32uart7"
rm bed.log
rm e0.log
rm esp32.log
clear && ./qemu-system-buddy -machine prusa-xl-${XL} -kernel ${MAIN_FW} ${ESP_SERIAL} -chardev stdio,id=stm32_itm -drive id=usbstick,file=fat:rw:sd2 -device usb-storage,drive=usbstick -icount 1 -S -s & sleep 1 &&
# screen -SDm dwarf5 ./qemu-system-buddy -machine prusa-xl-extruder-g0-4 -kernel newbl/bootloader-v296-prusa_dwarf-1.0.elf -icount 5 &
# screen -SDm dwarf4 ./qemu-system-buddy -machine prusa-xl-extruder-g0-3 -kernel newbl/bootloader-v296-prusa_dwarf-1.0.elf -icount 5 &
# screen -SDm dwarf3 ./qemu-system-buddy -machine prusa-xl-extruder-g0-2 -kernel newbl/bootloader-v296-prusa_dwarf-1.0.elf -icount 5 &
# screen -SDm dwarf2 ./qemu-system-buddy -machine prusa-xl-extruder-g0-1 -kernel newbl/bootloader-v296-prusa_dwarf-1.0.elf -icount 5 &
screen -SDm dwarf ./qemu-system-buddy -machine prusa-xl-extruder-${DWARF}-0 -kernel ${DWARF_BL} -icount 2 &
sleep 2 && screen -SDm modbed ./qemu-system-buddy -machine prusa-xl-bed-${MODBED} -kernel ${MODBED_BL} -icount 6
screen -L -Logfile e0.log -SDm dwarf ./qemu-system-buddy -machine prusa-xl-extruder-${DWARF}-0 -kernel ${DWARF_BL} -icount 5 &
sleep 2 && screen -L -Logfile bed.log -SDm bed ./qemu-system-buddy -machine prusa-xl-bed-${MODBED} -kernel ${MODBED_BL} -icount 4 \
& sleep 1 && screen -L -Logfile esp32.log -SDm esp32 ./qemu-system-xtensa -machine prusa-xl-esp32
killall qemu-system-buddy
killall qemu-system-xtensa
98 changes: 73 additions & 25 deletions hw/arm/prusa/parts/xl_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ static const char* shm_names[XL_BRIDGE_COUNT] =
"PXL_E2",
"PXL_E3",
"PXL_E4",
"PXL_E5"
"PXL_E5",
"PXL_ESP32"
};

// Messages are composed of some GPIO status bits
Expand Down Expand Up @@ -82,7 +83,7 @@ struct XLBridgeState {
CharBackend chr[XL_BRIDGE_COUNT];
CharBackend gpio[XL_BRIDGE_COUNT];

qemu_irq byte_receive;
qemu_irq byte_receive[XLBRIDGE_UART_COUNT];

qemu_irq gpio_out[XLBRIDGE_PIN_COUNT];

Expand Down Expand Up @@ -160,8 +161,17 @@ static void xl_bridge_tx_assert(void *opaque, int n, int level)

static void xl_bridge_byte_send(void *opaque, int n, int level)
{
// TODO - construct the control byte.
XLBridgeState *s = XLBRIDGE(opaque);

// ESP uart isn't RS485, just forward on the data (this is from ESP to STM32)...
if (n == XLBRIDGE_UART_ESP32)
{
uint8_t data = level;
//printf("ESP32: %02x (%c)\n", data, data);
qemu_chr_fe_write_all(&s->chr[XL_DEV_ESP32], (uint8_t*)&data, 1);
return;
}

//uint16_t data = 0x00FF | (level & 0xFF)<<8; // swap the bytes here so they come in in the right order.
// Buffer up the data for a single-shot transmit.
s->buffer[s->buffer_level++] = level & 0xFF;
Expand Down Expand Up @@ -198,7 +208,7 @@ static int xl_bridge_gpio_can_receive(void *opaque)
return 1; // Currently only 1 byte increments.
}

static void xl_bridge_receive(void *opaque, const uint8_t *buf, int size)
static void _xl_bridge_receive(void *opaque, const uint8_t *buf, int size, int destination)
{
XLBridgeState *s = XLBRIDGE(opaque);
//#define FILTER size < 20 && s->id == XL_DEV_XBUDDY
Expand All @@ -207,12 +217,28 @@ static void xl_bridge_receive(void *opaque, const uint8_t *buf, int size)
if (FILTER) printf(" %u Received: ", s->id);
for (const uint8_t* p = buf; p<buf+size; p++)
{
qemu_set_irq(s->byte_receive, *p);
qemu_set_irq(s->byte_receive[destination], *p);
if (FILTER) printf("%02x ",*p);
}
if (FILTER) printf("\n");
}

static inline void xl_bridge_receive(void *opaque, const uint8_t *buf, int size)
{
_xl_bridge_receive(opaque, buf, size, XLBRIDGE_UART_PUPPY);
}

static int xl_bridge_esp_can_receive(void *opaque)
{
return 1; // Currently only 1 byte increments.
}

static inline void xl_bridge_esp_receive(void *opaque, const uint8_t *buf, int size)
{
// printf("ESP32 said: %02x (%c)\n", buf[0], buf[0]);
_xl_bridge_receive(opaque, buf, size, XLBRIDGE_UART_ESP32);
}

#define PROCESS_BIT(pin, field) \
if (s->gpio_states[s->id].bits.field != state.bits.field) \
{ \
Expand Down Expand Up @@ -340,7 +366,7 @@ static void xl_bridge_realize(DeviceState *dev, Error **errp)
XLBridgeState *s = XLBRIDGE(dev);
if (s->id == XL_DEV_XBUDDY)
{
for (int i=XL_DEV_T4; i>=XL_DEV_BED; i--) // just two tools for now, because of the "server" wait.
for (int i=XL_DEV_ESP32; i>=XL_DEV_BED; i--) // just two tools for now, because of the "server" wait.
{
Chardev* d=qemu_chr_find(shm_names[i]);
gchar* io_name = g_strdup_printf("%s-io",shm_names[i]);
Expand All @@ -356,18 +382,19 @@ static void xl_bridge_realize(DeviceState *dev, Error **errp)
printf("Socket ID %s - not found, creating it instead.\n", shm_names[i]);
QemuOpts *opts;
// Now create the IO (GPIO) channel.
opts = qemu_opts_create(qemu_find_opts("chardev"), g_strdup_printf("%s-io",shm_names[i]), 1, NULL);
qemu_opt_set(opts, "backend","socket", errp);
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s-io", shm_names[i]), errp);
qemu_opt_set(opts, "server", "on", errp);
qemu_opt_set_bool(opts, "wait", false, errp);
d2 = qemu_chr_new_from_opts(opts, NULL, errp);
qemu_opts_del(opts);
opts = qemu_opts_create(qemu_find_opts("chardev"), g_strdup_printf("%s-io",shm_names[i]), 1, NULL);
qemu_opt_set(opts, "backend","socket", errp);
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s-io", shm_names[i]), errp);
qemu_opt_set(opts, "server", "on", errp);
qemu_opt_set_bool(opts, "wait", false, errp);
d2 = qemu_chr_new_from_opts(opts, NULL, errp);
qemu_opts_del(opts);

opts = qemu_opts_create(qemu_find_opts("chardev"), g_strdup(shm_names[i]), 1, NULL);
qemu_opt_set(opts, "backend","socket", errp);
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s", shm_names[i]), errp);
qemu_opt_set(opts, "server", "on", errp);
if (i > XL_DEV_T0) // Only force wait for required items, namely tool 0 and the bed.
if (i > XL_DEV_T0 && i != XL_DEV_ESP32) // Only force wait for required items, namely tool 0 and the bed.
{
qemu_opt_set_bool(opts, "wait", false, errp);
}
Expand All @@ -376,8 +403,16 @@ static void xl_bridge_realize(DeviceState *dev, Error **errp)

}
qemu_chr_fe_init(&s->chr[i],d, errp);
qemu_chr_fe_set_handlers(&s->chr[i], xl_bridge_can_receive, xl_bridge_receive, NULL,
NULL,s,NULL,true);
if (i != XL_DEV_ESP32)
{
qemu_chr_fe_set_handlers(&s->chr[i], xl_bridge_can_receive, xl_bridge_receive, NULL,
NULL,s,NULL,true);
}
else
{
qemu_chr_fe_set_handlers(&s->chr[i], xl_bridge_esp_can_receive, xl_bridge_esp_receive, NULL,
NULL,s,NULL,true);
}
qemu_chr_fe_accept_input(&s->chr[i]);

qemu_chr_fe_init(&s->gpio[i],d2, errp);
Expand Down Expand Up @@ -406,15 +441,28 @@ static void xl_bridge_realize(DeviceState *dev, Error **errp)
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s-io", shm_names[s->id]), errp);
d2 = qemu_chr_new_from_opts(opts, NULL, errp);
qemu_opts_del(opts);
opts = qemu_opts_create(qemu_find_opts("chardev"), g_strdup(shm_names[s->id]), 1, NULL);
qemu_opt_set(opts, "backend","socket", errp);
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s", shm_names[s->id]), errp);
d = qemu_chr_new_from_opts(opts, NULL, errp);
qemu_opts_del(opts);
// ESP can't really use this mecahnism right now. Maybe in the future if it's cleaned up...
if (s->id != XL_DEV_ESP32)
{
opts = qemu_opts_create(qemu_find_opts("chardev"), g_strdup(shm_names[s->id]), 1, NULL);
qemu_opt_set(opts, "backend","socket", errp);
qemu_opt_set(opts, "path", g_strdup_printf("/tmp/%s", shm_names[s->id]), errp);
d = qemu_chr_new_from_opts(opts, NULL, errp);
qemu_opts_del(opts);
}
}
qemu_chr_fe_init(&s->chr[s->id],d, errp);
qemu_chr_fe_set_handlers(&s->chr[s->id], xl_bridge_can_receive, xl_bridge_receive, NULL,
NULL,s,NULL,true);
if (s->id != XL_DEV_ESP32)
{
qemu_chr_fe_set_handlers(&s->chr[s->id], xl_bridge_can_receive, xl_bridge_receive, NULL,
NULL,s,NULL,true);
}
else
{
qemu_chr_fe_set_handlers(&s->chr[s->id], xl_bridge_esp_can_receive, xl_bridge_esp_receive, NULL,
NULL,s,NULL,true);
}

qemu_chr_fe_accept_input(&s->chr[s->id]);

qemu_chr_fe_init(&s->gpio[s->id],d2, errp);
Expand Down Expand Up @@ -449,9 +497,9 @@ static void xl_bridge_init(Object *obj)
XLBridgeState *s = XLBRIDGE(obj);
// Serial I/O IRQs
DeviceState* dev = DEVICE(obj);
qdev_init_gpio_in_named(dev, xl_bridge_byte_send, "byte-send", 1);
qdev_init_gpio_in_named(dev, xl_bridge_byte_send, "byte-send", XLBRIDGE_UART_COUNT);
qdev_init_gpio_in_named(dev, xl_bridge_tx_assert, "tx-assert", 1);
qdev_init_gpio_out_named(dev, &s->byte_receive, "byte-receive", 1);
qdev_init_gpio_out_named(dev, s->byte_receive, "byte-receive", XLBRIDGE_UART_COUNT);

qdev_init_gpio_in_named(dev, xl_bridge_gpio_in, "gpio-in",XLBRIDGE_PIN_COUNT);
qdev_init_gpio_in_named(dev, xl_bridge_reset_in, "reset-in", XL_BRIDGE_COUNT);
Expand Down
10 changes: 9 additions & 1 deletion hw/arm/prusa/parts/xl_bridge.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
xl_bridge.h - include with defines for bridge properties.
Copyright 2022-3 VintagePC <https://github.com/vintagepc/>
Copyright 2022-4 VintagePC <https://github.com/vintagepc/>
This file is part of Mini404.
Expand Down Expand Up @@ -30,6 +30,7 @@ enum {
XL_DEV_T3,
XL_DEV_T4,
XL_DEV_T5,
XL_DEV_ESP32,
XL_BRIDGE_COUNT
};

Expand All @@ -47,5 +48,12 @@ enum {
XLBRIDGE_PIN_nAC_FAULT,
XLBRIDGE_PIN_RESET,
XLBRIDGE_PIN_Z_UM,
XLBRIDGE_PIN_ESP_GPIO0,
XLBRIDGE_PIN_COUNT
};

enum {
XLBRIDGE_UART_PUPPY = 0,
XLBRIDGE_UART_ESP32,
XLBRIDGE_UART_COUNT
};
148 changes: 148 additions & 0 deletions hw/arm/prusa/prusa-xl-esp32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Prusa XL embedded ESP32 machine model.
*
* Copyright 2024 VintagePC <github.com/vintagepc>
*
* It's just a derived version that adds some convenience wrapping
* for the control lines and the automatic UART connection from xl-bridge
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/sysbus.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "chardev/char.h"
#include "hw/qdev-properties.h"
#include "hw/core/split-irq.h"
#include "qemu/error-report.h"
#include "parts/xl_bridge.h"

#define FLASH_FN "Prusa_XL_ESP32_flash.bin"
#define FLASH_ID "prusa-xl-esp32-flash"

// Yes, I'm being really lazy about this right now.
// Long term, this shuold be consolidated with the version in stm32_common.

static bool create_if_not_exist(const char* default_name, uint32_t file_size)
{
bool exists = true;
if (access(default_name, R_OK | W_OK) == -1)
{
#ifndef CONFIG_GCOV
printf("%s not found - creating it.\n",default_name);
#endif
// Create it.
int fd = creat(default_name, S_IRUSR | S_IWUSR);
exists = (ftruncate(fd, file_size) != -1);
close(fd);
}
return exists;
}

static BlockBackend* get_or_create_drive(BlockInterfaceType interface, int index, const char* default_name, const char* label, uint32_t file_size, Error** errp)
{
BlockBackend *blk = blk_by_name(label);
if (blk)
{
return blk;
}
DriveInfo* dinfo = drive_get(interface, 0, index);
if (!dinfo)
{
if (create_if_not_exist(default_name, file_size))
{
#ifndef CONFIG_GCOV
printf("No -%s drive specified, using default %s\n",
interface==IF_MTD? "mtdblock" : "pflash",
default_name);
#endif
QemuOpts* drive_opts = drive_add(interface, index, default_name, "format=raw");
dinfo = drive_new(drive_opts, interface, errp);
}
}
return blk_by_legacy_dinfo(dinfo);
}


static void prusa_esp32_init(MachineState *machine)
{
// Set strap mode so it's not needed on command line.
GlobalProperty *g;
g = g_malloc0(sizeof(*g));
g->driver = "esp32.gpio";
g->property = "strap_mode";
g->value = "0x0f";
qdev_prop_register_global(g);

// Create chardev handler. We can't pipe bytes directly into the UART like the STM32 side, so it's a bit more complex.
if (!serial_hd(0))
{
QemuOpts *opts;
opts = qemu_opts_create(qemu_find_opts("chardev"), "prusaxl-esp32", 1, NULL);
qemu_opt_set(opts, "backend","socket", &error_fatal);
qemu_opt_set(opts, "path", "/tmp/PXL_ESP32", &error_fatal);
qemu_chr_new_from_opts(opts, NULL, &error_fatal);
qemu_opts_del(opts);
}

// Create flash storage:
get_or_create_drive(IF_MTD, 0, FLASH_FN, FLASH_ID, 4U*MiB, &error_fatal);

DeviceState *dev = qdev_new("xl-bridge");
qdev_prop_set_uint8(dev, "device", XL_DEV_ESP32);

sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

// Invoke parent
ObjectClass *oc = object_class_by_name(MACHINE_TYPE_NAME("esp32"));
if (oc != NULL)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->init(machine);
}


// qdev_connect_gpio_out_named(stm32_soc_get_periph(dev_soc, STM32_P_UART1),"byte-out", 0, qdev_get_gpio_in_named(dev, "byte-send",0));
// qdev_connect_gpio_out_named(dev, "byte-receive", 0, qdev_get_gpio_in_named(stm32_soc_get_periph(dev_soc, STM32_P_UART1),"byte-in", 0));
};

static void xlbuddy_esp32_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Prusa XL embedded ESP32";
mc->family = "xlbuddy-machine",
mc->no_parallel = true;
mc->no_serial = true;
mc->init = prusa_esp32_init;
}

static const TypeInfo xlbuddy_esp32_machine_types[] = {
{
.name = MACHINE_TYPE_NAME("prusa-xl-esp32"),
.parent = MACHINE_TYPE_NAME("esp32"),
.class_init = xlbuddy_esp32_class_init,
}
};

DEFINE_TYPES(xlbuddy_esp32_machine_types)
Loading

0 comments on commit d1c6194

Please sign in to comment.