esp32: add support for running and debuggin using qemu-esp32
Этот коммит содержится в:
родитель
bd56636d58
коммит
8568d4f625
5 изменённых файлов: 57 добавлений и 9 удалений
|
@ -37,8 +37,14 @@ import (
|
||||||
// BuildResult is the output of a build. This includes the binary itself and
|
// BuildResult is the output of a build. This includes the binary itself and
|
||||||
// some other metadata that is obtained while building the binary.
|
// some other metadata that is obtained while building the binary.
|
||||||
type BuildResult struct {
|
type BuildResult struct {
|
||||||
|
// The executable directly from the linker, usually including debug
|
||||||
|
// information. Used for GDB for example.
|
||||||
|
Executable string
|
||||||
|
|
||||||
// A path to the output binary. It will be removed after Build returns, so
|
// A path to the output binary. It will be removed after Build returns, so
|
||||||
// if it should be kept it must be copied or moved away.
|
// if it should be kept it must be copied or moved away.
|
||||||
|
// It is often the same as Executable, but differs if the output format is
|
||||||
|
// .hex for example (instead of the usual ELF).
|
||||||
Binary string
|
Binary string
|
||||||
|
|
||||||
// The directory of the main package. This is useful for testing as the test
|
// The directory of the main package. This is useful for testing as the test
|
||||||
|
@ -835,7 +841,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "esp32", "esp32c3", "esp8266":
|
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
||||||
// Special format for the ESP family of chips (parsed by the ROM
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
// bootloader).
|
// bootloader).
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
|
@ -867,6 +873,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
}
|
}
|
||||||
|
|
||||||
return action(BuildResult{
|
return action(BuildResult{
|
||||||
|
Executable: executable,
|
||||||
Binary: tmppath,
|
Binary: tmppath,
|
||||||
MainDir: lprogram.MainPkg().Dir,
|
MainDir: lprogram.MainPkg().Dir,
|
||||||
ModuleRoot: moduleroot,
|
ModuleRoot: moduleroot,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type espImageSegment struct {
|
type espImageSegment struct {
|
||||||
|
@ -78,15 +79,31 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||||
// An added benefit is that we don't need to check for errors all the time.
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
outf := &bytes.Buffer{}
|
outf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
|
||||||
|
// image, not just a binary to be flashed at 0x1000 for example.
|
||||||
|
chip := format
|
||||||
|
makeImage := false
|
||||||
|
if strings.HasSuffix(format, "-img") {
|
||||||
|
makeImage = true
|
||||||
|
chip = format[:len(format)-len("-img")]
|
||||||
|
}
|
||||||
|
|
||||||
|
if makeImage {
|
||||||
|
// The bootloader starts at 0x1000, or 4096.
|
||||||
|
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||||
|
// application in the bootloader location.
|
||||||
|
outf.Write(make([]byte, 4096))
|
||||||
|
}
|
||||||
|
|
||||||
// Chip IDs. Source:
|
// Chip IDs. Source:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||||
chip_id := map[string]uint16{
|
chip_id := map[string]uint16{
|
||||||
"esp32": 0x0000,
|
"esp32": 0x0000,
|
||||||
"esp32c3": 0x0005,
|
"esp32c3": 0x0005,
|
||||||
}[format]
|
}[chip]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch format {
|
switch chip {
|
||||||
case "esp32", "esp32c3":
|
case "esp32", "esp32c3":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
|
@ -155,12 +172,22 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||||
outf.WriteByte(checksum)
|
outf.WriteByte(checksum)
|
||||||
|
|
||||||
if format != "esp8266" {
|
if chip != "esp8266" {
|
||||||
// SHA256 hash (to protect against image corruption, not for security).
|
// SHA256 hash (to protect against image corruption, not for security).
|
||||||
hash := sha256.Sum256(outf.Bytes())
|
hash := sha256.Sum256(outf.Bytes())
|
||||||
outf.Write(hash[:])
|
outf.Write(hash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||||
|
// image to be a certain size.
|
||||||
|
if makeImage {
|
||||||
|
// Use a default image size of 4MB.
|
||||||
|
grow := 4096*1024 - outf.Len()
|
||||||
|
if grow > 0 {
|
||||||
|
outf.Write(make([]byte, grow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write the image to the output file.
|
// Write the image to the output file.
|
||||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,6 +395,13 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||||
return c.Target.BinaryFormat
|
return c.Target.BinaryFormat
|
||||||
}
|
}
|
||||||
return "bin"
|
return "bin"
|
||||||
|
case ".img":
|
||||||
|
// Image file. Only defined for the ESP32 at the moment, where it is a
|
||||||
|
// full (runnable) image that can be used in the Espressif QEMU fork.
|
||||||
|
if c.Target.BinaryFormat != "" {
|
||||||
|
return c.Target.BinaryFormat + "-img"
|
||||||
|
}
|
||||||
|
return "bin"
|
||||||
case ".hex":
|
case ".hex":
|
||||||
// Similar to bin, but includes the start address and is thus usually a
|
// Similar to bin, but includes the start address and is thus usually a
|
||||||
// better format.
|
// better format.
|
||||||
|
@ -507,10 +514,15 @@ func (c *Config) EmulatorName() string {
|
||||||
|
|
||||||
// EmulatorFormat returns the binary format for the emulator and the associated
|
// EmulatorFormat returns the binary format for the emulator and the associated
|
||||||
// file extension. An empty string means to pass directly whatever the linker
|
// file extension. An empty string means to pass directly whatever the linker
|
||||||
// produces directly without conversion.
|
// produces directly without conversion (usually ELF format).
|
||||||
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(c.Target.Emulator, "{img}"):
|
||||||
|
return "img", ".img"
|
||||||
|
default:
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Emulator returns a ready-to-run command to run the given binary in an
|
// Emulator returns a ready-to-run command to run the given binary in an
|
||||||
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
||||||
|
|
4
main.go
4
main.go
|
@ -634,7 +634,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||||
// Construct and execute a gdb or lldb command.
|
// Construct and execute a gdb or lldb command.
|
||||||
// By default: gdb -ex run <binary>
|
// By default: gdb -ex run <binary>
|
||||||
// Exit the debugger with Ctrl-D.
|
// Exit the debugger with Ctrl-D.
|
||||||
params := []string{result.Binary}
|
params := []string{result.Executable}
|
||||||
switch debugger {
|
switch debugger {
|
||||||
case "gdb":
|
case "gdb":
|
||||||
if port != "" {
|
if port != "" {
|
||||||
|
@ -668,7 +668,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
|
return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,5 +15,7 @@
|
||||||
"src/internal/task/task_stack_esp32.S"
|
"src/internal/task/task_stack_esp32.S"
|
||||||
],
|
],
|
||||||
"binary-format": "esp32",
|
"binary-format": "esp32",
|
||||||
"flash-command": "esptool.py --chip=esp32 --port {port} write_flash 0x1000 {bin} -ff 80m -fm dout"
|
"flash-command": "esptool.py --chip=esp32 --port {port} write_flash 0x1000 {bin} -ff 80m -fm dout",
|
||||||
|
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
||||||
|
"gdb": ["xtensa-esp32-elf-gdb"]
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче