mirror of
https://gitee.com/wa-lang/wa.git
synced 2025-12-06 17:19:15 +08:00
完善esp32flash子命令
This commit is contained in:
@@ -52,11 +52,6 @@ var CmdESP32Build = &cli.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintln(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
parser.DebugMode = c.Bool("debug")
|
||||
|
||||
infile := c.Args().First()
|
||||
|
||||
@@ -15,11 +15,7 @@ var CmdESP32Dump = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "esp32dump",
|
||||
Usage: "dump esp32 image file",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "hello",
|
||||
},
|
||||
},
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintln(os.Stderr, "no input file")
|
||||
|
||||
63
internal/app/appesp32flash/appesp32flash.go
Normal file
63
internal/app/appesp32flash/appesp32flash.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2025 武汉凹语言科技有限公司
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
package appesp32flash
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/cli"
|
||||
"wa-lang.org/wa/internal/native/espflash"
|
||||
)
|
||||
|
||||
var CmdESP32Flash = &cli.Command{
|
||||
Hidden: true,
|
||||
Name: "esp32flash",
|
||||
Usage: "upload esp32 image file to board",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "port",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "set uart port",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "baudrate",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "set uart baudrate",
|
||||
Value: 115200,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "addr",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "set flash address",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
fmt.Fprintln(os.Stderr, "no input file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
portName := c.String("port")
|
||||
baudRate := c.Int("baudrate")
|
||||
flashAddr := c.Int("addr")
|
||||
filePath := c.Args().First()
|
||||
|
||||
client, err := espflash.Open(portName, baudRate)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
err = client.FlashFile(uint32(flashAddr), filePath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Done")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
257
internal/native/espflash/client.go
Normal file
257
internal/native/espflash/client.go
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright (C) 2025 武汉凹语言科技有限公司
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
package espflash
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/serial"
|
||||
"wa-lang.org/wa/internal/3rdparty/slip"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
port *serial.Port
|
||||
r *slip.Reader
|
||||
w *slip.Writer
|
||||
}
|
||||
|
||||
func Open(name string, baudrate int) (*Client, error) {
|
||||
p := new(Client)
|
||||
port, err := serial.OpenPort(
|
||||
&serial.Config{
|
||||
Name: name,
|
||||
Baud: baudrate,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.port = port
|
||||
p.r = slip.NewReader(port)
|
||||
p.w = slip.NewWriter(port)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Client) Close() error {
|
||||
return p.port.Close()
|
||||
}
|
||||
|
||||
func (c *Client) SendCommand(cmd CmdOpcode, payload []byte, checksum uint32) ([]byte, error) {
|
||||
if err := c.sendRequest(cmd, payload, checksum); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.readResponse()
|
||||
}
|
||||
|
||||
func (c *Client) sendFrame(data []byte) error {
|
||||
return c.w.WritePacket(data)
|
||||
}
|
||||
|
||||
func (c *Client) readFrame() (p []byte, isPrefix bool, err error) {
|
||||
return c.r.ReadPacket()
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(cmd CmdOpcode, payload []byte, checksum uint32) error {
|
||||
// Assemble request frame
|
||||
frame := make([]byte, 0, 8+len(payload))
|
||||
frame = append(frame, 0x00) // direction: 0x00 = host -> target
|
||||
frame = append(frame, byte(cmd))
|
||||
|
||||
// payload length
|
||||
lenb := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(lenb, uint16(len(payload)))
|
||||
frame = append(frame, lenb...)
|
||||
|
||||
// payload
|
||||
frame = append(frame, payload...)
|
||||
|
||||
// checksum
|
||||
cksum := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(cksum, checksum)
|
||||
frame = append(frame, cksum...)
|
||||
|
||||
return c.sendFrame(frame)
|
||||
}
|
||||
|
||||
// readResponse reads a response frame:
|
||||
//
|
||||
// Status(1) + Length(2) + Payload
|
||||
func (c *Client) readResponse() ([]byte, error) {
|
||||
resp, _, err := c.readFrame()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp) < 3 {
|
||||
return nil, errors.New("invalid response length")
|
||||
}
|
||||
|
||||
status := resp[0]
|
||||
if status != 0 {
|
||||
return nil, ErrorCode(status)
|
||||
}
|
||||
|
||||
respLen := binary.LittleEndian.Uint16(resp[1:3])
|
||||
|
||||
if int(respLen)+3 != len(resp) {
|
||||
return nil, errors.New("invalid response length field")
|
||||
}
|
||||
|
||||
if status != 0 {
|
||||
return nil, fmt.Errorf("rom loader error: 0x%02x", status)
|
||||
}
|
||||
|
||||
// ok
|
||||
return resp[3:], nil
|
||||
}
|
||||
|
||||
func (f *Client) CmdSync() error {
|
||||
payload := make([]byte, 36)
|
||||
payload[0] = 0x07
|
||||
payload[1] = 0x07
|
||||
payload[2] = 0x12
|
||||
payload[3] = 0x20
|
||||
for i := 4; i < 36; i++ {
|
||||
payload[i] = 0x55
|
||||
}
|
||||
|
||||
_, err := f.SendCommand(CmdSync, payload, 0)
|
||||
return err
|
||||
}
|
||||
func (f *Client) CmdEraseFlash() error {
|
||||
_, err := f.SendCommand(CmdEraseFlash, nil, 0)
|
||||
return err
|
||||
}
|
||||
func (f *Client) CmdBeginWrite(addr, totalSize, blockSize uint32) error {
|
||||
if blockSize == 0 {
|
||||
return errors.New("blockSize cannot be 0")
|
||||
}
|
||||
packets := (totalSize + blockSize - 1) / blockSize
|
||||
|
||||
eraseSize := packets * blockSize
|
||||
|
||||
payload := make([]byte, 5*4)
|
||||
binary.LittleEndian.PutUint32(payload[0:], eraseSize)
|
||||
binary.LittleEndian.PutUint32(payload[4:], packets)
|
||||
binary.LittleEndian.PutUint32(payload[8:], blockSize)
|
||||
binary.LittleEndian.PutUint32(payload[12:], addr)
|
||||
binary.LittleEndian.PutUint32(payload[16:], 0)
|
||||
|
||||
_, err := f.SendCommand(CmdFlashBegin, payload, 0)
|
||||
return err
|
||||
}
|
||||
func (f *Client) CmdWriteBlock(blockIndex uint32, data []byte) error {
|
||||
payload := make([]byte, 16+len(data))
|
||||
binary.LittleEndian.PutUint32(payload[0:], uint32(len(data)))
|
||||
binary.LittleEndian.PutUint32(payload[4:], blockIndex)
|
||||
binary.LittleEndian.PutUint32(payload[8:], 0)
|
||||
binary.LittleEndian.PutUint32(payload[12:], 0)
|
||||
copy(payload[16:], data)
|
||||
|
||||
// TODO: 计算 checksum, 按 ROM 协议
|
||||
var sum uint32
|
||||
for _, b := range data {
|
||||
sum += uint32(b)
|
||||
}
|
||||
|
||||
_, err := f.SendCommand(CmdFlashData, payload, sum)
|
||||
return err
|
||||
}
|
||||
func (f *Client) CmdEndWrite(reboot bool) error {
|
||||
payload := make([]byte, 4)
|
||||
|
||||
if reboot {
|
||||
binary.LittleEndian.PutUint32(payload, 0)
|
||||
} else {
|
||||
binary.LittleEndian.PutUint32(payload, 1)
|
||||
}
|
||||
|
||||
_, err := f.SendCommand(CmdFlashEnd, payload, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *Client) CmdFlashBinary(addr uint32, bin []byte) error {
|
||||
const blockSize = 0x400 // 1024 bytes
|
||||
|
||||
if err := f.CmdBeginWrite(addr, uint32(len(bin)), blockSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var blockIndex uint32
|
||||
for offset := 0; offset < len(bin); offset += blockSize {
|
||||
end := offset + blockSize
|
||||
if end > len(bin) {
|
||||
end = len(bin)
|
||||
}
|
||||
chunk := bin[offset:end]
|
||||
|
||||
if err := f.CmdWriteBlock(blockIndex, chunk); err != nil {
|
||||
return err
|
||||
}
|
||||
blockIndex++
|
||||
}
|
||||
|
||||
if err := f.CmdEndWrite(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) FlashFile(addr uint32, filePath string) error {
|
||||
bin, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.CmdSync(); err != nil {
|
||||
fmt.Println("Sync failed, retrying...")
|
||||
if err := c.CmdSync(); err != nil {
|
||||
return fmt.Errorf("sync failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.CmdFlashBinary(addr, bin); err != nil {
|
||||
return fmt.Errorf("flash failed: %v", err)
|
||||
}
|
||||
|
||||
ok, err := c.verifyFlashMD5(addr, bin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("flash verify failed: MD5 mismatch")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) verifyFlashMD5(addr uint32, bin []byte) (bool, error) {
|
||||
|
||||
const CmdSPIFlashMD5 = 0x13
|
||||
|
||||
payload := make([]byte, 16)
|
||||
binary.LittleEndian.PutUint32(payload[0:], addr)
|
||||
binary.LittleEndian.PutUint32(payload[4:], uint32(len(bin)))
|
||||
binary.LittleEndian.PutUint32(payload[8:], 0)
|
||||
binary.LittleEndian.PutUint32(payload[12:], 0)
|
||||
|
||||
resp, err := c.SendCommand(CmdSPIFlashMD5, payload, 0)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("SPI_FLASH_MD5 command failed: %v", err)
|
||||
}
|
||||
|
||||
if len(resp) < 16 {
|
||||
return false, errors.New("MD5 response too short")
|
||||
}
|
||||
|
||||
deviceMD5 := resp[:16]
|
||||
localMD5 := md5.Sum(bin)
|
||||
|
||||
return string(deviceMD5) == string(localMD5[:]), nil
|
||||
}
|
||||
32
internal/native/espflash/cmd.go
Normal file
32
internal/native/espflash/cmd.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2025 武汉凹语言科技有限公司
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
package espflash
|
||||
|
||||
type CmdOpcode byte
|
||||
|
||||
const (
|
||||
CmdFlashBegin CmdOpcode = 0x02
|
||||
CmdFlashData CmdOpcode = 0x03
|
||||
CmdFlashEnd CmdOpcode = 0x04
|
||||
CmdMemBegin CmdOpcode = 0x05
|
||||
CmdMemEnd CmdOpcode = 0x06
|
||||
CmdMemData CmdOpcode = 0x07
|
||||
CmdSync CmdOpcode = 0x08
|
||||
CmdWriteReg CmdOpcode = 0x09
|
||||
CmdReadReg CmdOpcode = 0x0A
|
||||
CmdSPISetParams CmdOpcode = 0x0B
|
||||
CmdSPIAttach CmdOpcode = 0x0D
|
||||
CmdChangeBaudrate CmdOpcode = 0x0F
|
||||
CmdFlashDeflBegin CmdOpcode = 0x10
|
||||
CmdFlashDeflData CmdOpcode = 0x11
|
||||
CmdFlashDeflEnd CmdOpcode = 0x12
|
||||
CmdSPIFlashMD5 CmdOpcode = 0x13
|
||||
CmdGetSecurityInfo CmdOpcode = 0x14
|
||||
|
||||
// Stub only
|
||||
CmdEraseFlash CmdOpcode = 0xd0
|
||||
CmdEraseRegion CmdOpcode = 0xd1
|
||||
CmdReadFlash CmdOpcode = 0xd2
|
||||
CmdRunUserCode CmdOpcode = 0xd3
|
||||
)
|
||||
71
internal/native/espflash/error.go
Normal file
71
internal/native/espflash/error.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2025 武汉凹语言科技有限公司
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
package espflash
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ErrorCode uint8
|
||||
|
||||
const (
|
||||
ErrROMUndefined ErrorCode = 0x00
|
||||
ErrROMInvalidParam ErrorCode = 0x01
|
||||
ErrROMMallocFailed ErrorCode = 0x02
|
||||
ErrROMSendFailed ErrorCode = 0x03
|
||||
ErrROMRecvFailed ErrorCode = 0x04
|
||||
ErrROMInvalidFormat ErrorCode = 0x05
|
||||
ErrROMResultError ErrorCode = 0x06
|
||||
ErrROMChecksumError ErrorCode = 0x07
|
||||
ErrROMFlashWriteError ErrorCode = 0x08
|
||||
ErrROMFlashReadError ErrorCode = 0x09
|
||||
ErrROMFlashReadLenError ErrorCode = 0x0a
|
||||
ErrROMDeflateFailed ErrorCode = 0x0b
|
||||
ErrROMDeflateAdlerError ErrorCode = 0x0c
|
||||
ErrROMDeflateParamError ErrorCode = 0x0d
|
||||
ErrROMInvalidRAMSize ErrorCode = 0x0e
|
||||
ErrROMInvalidRAMAddr ErrorCode = 0x0f
|
||||
|
||||
// Extended errors (0x64+)
|
||||
ErrROMInvalidParameter ErrorCode = 0x64
|
||||
ErrROMInvalidFmt ErrorCode = 0x65
|
||||
ErrROMDescriptionTooLong ErrorCode = 0x66
|
||||
ErrROMBadEncoding ErrorCode = 0x67
|
||||
ErrROMInsufficientStorage ErrorCode = 0x69
|
||||
)
|
||||
|
||||
// description map
|
||||
var romErrorDescriptions = map[ErrorCode]string{
|
||||
ErrROMUndefined: "Undefined errors",
|
||||
ErrROMInvalidParam: "The input parameter is invalid",
|
||||
ErrROMMallocFailed: "Failed to malloc memory from system",
|
||||
ErrROMSendFailed: "Failed to send out message",
|
||||
ErrROMRecvFailed: "Failed to receive message",
|
||||
ErrROMInvalidFormat: "The format of the received message is invalid",
|
||||
ErrROMResultError: "Message is ok, but the running result is wrong",
|
||||
ErrROMChecksumError: "Checksum error",
|
||||
ErrROMFlashWriteError: "Flash write error",
|
||||
ErrROMFlashReadError: "Flash read error",
|
||||
ErrROMFlashReadLenError: "Flash read length error",
|
||||
ErrROMDeflateFailed: "Deflate failed error",
|
||||
ErrROMDeflateAdlerError: "Deflate Adler32 error",
|
||||
ErrROMDeflateParamError: "Deflate parameter error",
|
||||
ErrROMInvalidRAMSize: "Invalid RAM binary size",
|
||||
ErrROMInvalidRAMAddr: "Invalid RAM binary address",
|
||||
|
||||
ErrROMInvalidParameter: "Invalid parameter",
|
||||
ErrROMInvalidFmt: "Invalid format",
|
||||
ErrROMDescriptionTooLong: "Description too long",
|
||||
ErrROMBadEncoding: "Bad encoding description",
|
||||
ErrROMInsufficientStorage: "Insufficient storage",
|
||||
}
|
||||
|
||||
func (e ErrorCode) String() string {
|
||||
if s, ok := romErrorDescriptions[e]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown ROM error 0x%02X", uint8(e))
|
||||
}
|
||||
|
||||
func (e ErrorCode) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/serial"
|
||||
)
|
||||
|
||||
// 烧写配置
|
||||
const (
|
||||
portName = "/dev/ttyUSB0" // 根据您的系统修改为正确的串口名称 (e.g., COM3 on Windows)
|
||||
baudRate = 115200
|
||||
dataFile = "your_firmware.bin" // 假设这是你的汇编器生成的固件
|
||||
flashAddr = 0x42000000 // ESP32-C3 Flash的典型起始地址之一
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 1. 打开串口
|
||||
c := &serial.Config{Name: portName, Baud: baudRate}
|
||||
port, err := serial.OpenPort(c)
|
||||
if err != nil {
|
||||
log.Fatalf("无法打开串口 %s: %v", portName, err)
|
||||
}
|
||||
defer port.Close()
|
||||
|
||||
log.Printf("成功打开串口 %s", portName)
|
||||
|
||||
// 2. 模拟 ROM Bootloader 握手 (SYNC)
|
||||
// 实际操作中,你需要发送一个复杂的命令序列,这里仅模拟发送
|
||||
fmt.Println("--- 模拟 ROM Bootloader 握手 ---")
|
||||
|
||||
// 假设的同步命令(实际请查阅文档)
|
||||
syncCmd := []byte{0x07, 0x07, 0x12, 0x20}
|
||||
|
||||
n, err := port.Write(syncCmd)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("发送了 %d 字节的同步命令。", n)
|
||||
|
||||
// 等待设备响应 (实际需要解析响应,这里只是等待)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// 3. 模拟发送固件
|
||||
fmt.Println("--- 模拟发送固件数据 ---")
|
||||
|
||||
firmware, err := os.ReadFile(dataFile)
|
||||
if err != nil {
|
||||
log.Fatalf("无法读取固件文件 %s: %v", dataFile, err)
|
||||
}
|
||||
|
||||
// 假设的写入 Flash 命令头 (实际会包含地址、长度、校验和等)
|
||||
// 在实际烧写中,会是 (Command Header + Data Segment Header + Firmware Chunk)
|
||||
|
||||
dataChunkSize := 256 // 假设每次发送256字节
|
||||
|
||||
for i := 0; i < len(firmware); i += dataChunkSize {
|
||||
end := i + dataChunkSize
|
||||
if end > len(firmware) {
|
||||
end = len(firmware)
|
||||
}
|
||||
|
||||
chunk := firmware[i:end]
|
||||
|
||||
// 实际烧写时,需要在发送数据块前发送一个带有目标地址和长度的命令包
|
||||
|
||||
// 模拟发送数据块
|
||||
n, err := port.Write(chunk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("发送数据块: 地址 0x%X, 长度 %d 字节", flashAddr+i, n)
|
||||
|
||||
// 实际烧写中,每发送一个块都需要等待设备的 ACK 确认
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 4. 模拟运行命令 (RUN)
|
||||
fmt.Println("--- 模拟 RUN 命令 ---")
|
||||
// 实际操作中,你需要发送一个 RUN 命令,指定程序入口地址
|
||||
|
||||
// 5. 切换到串口监视模式
|
||||
fmt.Println("--- 切换到串口监视模式,等待设备输出 ---")
|
||||
|
||||
// 启动一个 goroutine 来持续读取串口数据
|
||||
go func() {
|
||||
buf := make([]byte, 128)
|
||||
for {
|
||||
n, err := port.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Printf("\n串口读取错误: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if n > 0 {
|
||||
fmt.Printf("%s", buf[:n])
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 保持主程序运行,直到用户按下 Enter
|
||||
fmt.Println("烧写和监视器已启动。按下 Enter 键退出...")
|
||||
fmt.Scanln()
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package main
|
||||
|
||||
// ESP32-C3 固件映像头 (Image Header)
|
||||
// 结构体字段顺序和大小必须与硬件文档严格匹配
|
||||
type ImageHeader struct {
|
||||
Magic uint32 // 魔术字:必须是 0xE9
|
||||
SegmentCount byte // 段的数量 (对于单段程序为 1)
|
||||
SpiMode byte // SPI Flash 模式 (通常为 0x02)
|
||||
SpiSpeed byte // SPI Flash 速度 (通常为 0x01)
|
||||
SpiSize byte // SPI Flash 大小 (通常为 0x20)
|
||||
EntryAddr uint32 // 程序入口点地址 (例如: 0x42000000)
|
||||
Reserved [16]byte // 保留字段
|
||||
Checksum byte // 映像头和所有段头的校验和(通常放在最后计算)
|
||||
}
|
||||
|
||||
// 段头 (Segment Header)
|
||||
type SegmentHeader struct {
|
||||
LoadAddr uint32 // 该段数据将被加载到的 RAM/IRAM 地址
|
||||
DataLen uint32 // 该段数据的字节长度
|
||||
}
|
||||
|
||||
const (
|
||||
ESP_IMAGE_MAGIC = 0xE9 // ESP32-C3 固件映像魔术字
|
||||
// 假设的程序入口点地址 (Flash 地址)
|
||||
// ROM Bootloader 会将它作为入口点执行
|
||||
DEFAULT_ENTRY_ADDR = 0x42000000
|
||||
)
|
||||
|
||||
// 假设这是您的 Go 汇编器生成的原始机器指令
|
||||
var rawMachineCode = []byte{
|
||||
// 假设这是 RISC-V 指令的机器码序列
|
||||
0x13, 0x05, 0x00, 0x00, // li a0, 0
|
||||
0x67, 0x80, 0x00, 0x00, // ret
|
||||
// 实际代码会更长,包括 UART 操作
|
||||
}
|
||||
Reference in New Issue
Block a user