diff options
-rw-r--r-- | builder/build.go | 9 | ||||
-rw-r--r-- | builder/esp.go | 33 | ||||
-rw-r--r-- | compileopts/config.go | 16 | ||||
-rw-r--r-- | main.go | 4 | ||||
-rw-r--r-- | targets/esp32.json | 4 |
5 files changed, 57 insertions, 9 deletions
diff --git a/builder/build.go b/builder/build.go index 45e03875b..6f5e73f9f 100644 --- a/builder/build.go +++ b/builder/build.go @@ -37,8 +37,14 @@ import ( // BuildResult is the output of a build. This includes the binary itself and // some other metadata that is obtained while building the binary. 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 // 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 // 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 { return err } - case "esp32", "esp32c3", "esp8266": + case "esp32", "esp32-img", "esp32c3", "esp8266": // Special format for the ESP family of chips (parsed by the ROM // bootloader). tmppath = filepath.Join(dir, "main"+outext) @@ -867,6 +873,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil } return action(BuildResult{ + Executable: executable, Binary: tmppath, MainDir: lprogram.MainPkg().Dir, ModuleRoot: moduleroot, diff --git a/builder/esp.go b/builder/esp.go index 98e375426..bd348b546 100644 --- a/builder/esp.go +++ b/builder/esp.go @@ -15,6 +15,7 @@ import ( "fmt" "io/ioutil" "sort" + "strings" ) 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. 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: // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22 chip_id := map[string]uint16{ "esp32": 0x0000, "esp32c3": 0x0005, - }[format] + }[chip] // Image header. - switch format { + switch chip { case "esp32", "esp32c3": // Header format: // 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.WriteByte(checksum) - if format != "esp8266" { + if chip != "esp8266" { // SHA256 hash (to protect against image corruption, not for security). hash := sha256.Sum256(outf.Bytes()) 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. return ioutil.WriteFile(outfile, outf.Bytes(), 0666) } diff --git a/compileopts/config.go b/compileopts/config.go index d7dbb41c3..b30e653e3 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -395,6 +395,13 @@ func (c *Config) BinaryFormat(ext string) string { return c.Target.BinaryFormat } 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": // Similar to bin, but includes the start address and is thus usually a // better format. @@ -507,9 +514,14 @@ func (c *Config) EmulatorName() string { // EmulatorFormat returns the binary format for the emulator and the associated // 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) { - return "", "" + switch { + case strings.Contains(c.Target.Emulator, "{img}"): + return "img", ".img" + default: + return "", "" + } } // Emulator returns a ready-to-run command to run the given binary in an @@ -634,7 +634,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option // Construct and execute a gdb or lldb command. // By default: gdb -ex run <binary> // Exit the debugger with Ctrl-D. - params := []string{result.Binary} + params := []string{result.Executable} switch debugger { case "gdb": if port != "" { @@ -668,7 +668,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option cmd.Stderr = os.Stderr err = cmd.Run() 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 }) diff --git a/targets/esp32.json b/targets/esp32.json index 78b04cc89..376d457c9 100644 --- a/targets/esp32.json +++ b/targets/esp32.json @@ -15,5 +15,7 @@ "src/internal/task/task_stack_esp32.S" ], "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"] } |