您尚未登录。

楼主 # 2021-06-19 18:50:12

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

研究RISCV D1芯片已经一个月有余了,中间为了方便开发,也开发了一个烧写工具xfel,为方便大家研究学习,这里编写了一个简化的裸机程序,供大家参考,实现比较简单,但该有的都有了。(从xboot精简而来,想要研究高阶功能,就去参考xboot源码树)

在DDR中运行d1-baremetal.bin,这种方式比较适合快速开发调试。借助xfel工具就可以实现。

sudo xfel ddr ddr3;sudo xfel write 0x40000000 d1-baremetal.bin;sudo xfel exec 0x40000000;

烧写d1-baremetal.bin到spi nor flash,固化程序到spi nor flash, 然后上电,自动实现初始化ddr,并自拷贝到ddr中运行。

sudo xfel spinor write 0 d1-baremetal.bin

程序正确运行后,会打印如下信息;

D1 baremetal
count = 0
count = 1
count = 2
count = 3
count = 4
count = 5
count = 6
count = 7
count = 8
count = 9
count = 10
count = 11
count = 12
count = 13
count = 14
count = 15
count = 16






点击下载: d1-baremetal.7z

https://github.com/xboot/xfel
https://github.com/xboot/xboot

离线

楼主 #2 2021-06-19 19:54:19

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

	la t0, _start
	la t1, _image_start
	LREG t1, (t1)
	beq t0, t1, _avoid

	/* Initial system jtag, uart and clock */
	call sys_jtag_init
	call sys_uart_init
	call sys_clock_init

	/* Copy ddr bin to 0x00030000 */
	la t1, _ddr_bin_start
	LREG t1, (t1)
	la t2, _ddr_bin_end
	LREG t2, (t2)
	sub a2, t2, t1
	la t1, _image_start
	LREG t1, (t1)
	la t2, _ddr_bin_start
	LREG t2, (t2)
	sub t0, t2, t1
	la a1, _start
	add a1, a1, t0
	li a0, 0x00030000
	call memcpy

	/* Initial ddr controller */
	call sys_dram_init
_avoid:
	nop
	/* Copyself to link address */
	la t0, _start
	la t1, _image_start
	LREG t1, (t1)
	beq t0, t1, 1f
	call sys_copyself
1:	nop

核心就这个地方,判断当前运行地址是否是链接地址,nor flash启动的话,肯定不是链接地址,那么就会多做很多事,初始化时钟,DDR,自拷贝等,如果是链接地址,那么就跳过这些动作,直接在DDR中运行。

离线

楼主 #12 2021-06-21 14:39:02

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

目前支持所有sfdp协议 nor flash,不支持sfdp协议的,就需要自己填入flash信息,w25x40是不支持sfdp的

离线

楼主 #13 2021-06-21 14:40:48

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

你这边既然能正确获取flash容量,应该是支持sfdp协议的,但还是烧不进去,有点奇怪,但可以做个测试,重新上电后,仅写入64KB,然后再读出来,看是否一致。不应该出现这种现象的。

离线

楼主 #14 2021-06-21 14:43:23

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

你在虚拟机里跑,有没有可能是虚拟机相关问题了,可以真机或者windows烧录实验下。

离线

楼主 #20 2021-06-27 10:45:14

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

March 说:

请问楼主,关于中断向量表这部分的汇编代码,有点不太懂,主要是vectors:csrw mscratch, sp    addi sp, sp, -(37 * REGSZ)     SREG x1, 1 * REGSZ(x2)...这部分。

就是中断现场保护,没什么特别的。

离线

楼主 #21 2021-06-27 10:51:03

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

March 说:

楼主,还有这个问题: https://whycan.com/t_6706.html ,有时候DDR信息能输出完整,有时候输出又不完整,很奇怪。

这个估计你哪里异常了,DDR不要初始化两次,还有要注意cache类问题,需要你自己仔细对比自己的操作。

离线

楼主 #23 2021-06-28 11:01:10

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

看xboot代码里面都有的

离线

楼主 #26 2021-06-30 15:01:15

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

你这是在搞笑吗?中断控制器都没有初始化,还想触发中断

离线

楼主 #28 2021-06-30 15:25:18

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

先尝试用定时器做中断源吧,至少这个xboot是验证通过的,而且gpio中断,属于子中断,需要先开启父中断的

最近编辑记录 xboot (2021-06-30 15:26:17)

离线

楼主 #30 2021-07-02 12:53:38

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

父中段就是一个中断被多个子中段共享,gpio一般是一组对应一个父中断

离线

楼主 #33 2021-07-02 16:43:19

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

这是中断编号,手册里有的

#define D1_IRQ_GPIOB_NS			(85)
#define D1_IRQ_GPIOC_NS			(87)
#define D1_IRQ_GPIOD_NS			(89)
#define D1_IRQ_GPIOE_NS			(91)
#define D1_IRQ_GPIOF_NS			(93)
#define D1_IRQ_GPIOG_NS			(95)

离线

楼主 #44 2021-12-08 14:48:56

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

@woolen
这个测试demo仅支持spi nor flash引导,如果要支持spi nand引导,需要做两个工作,一个是实现spi nand自拷贝,另一个是,一个page只能使用前2k,后面的需要空着。

spi nand flash 引导是有点特殊的。但只要满足这两个要求,就可以实现spi nand 自举了。

离线

楼主 #47 2021-12-08 16:59:27

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

看这个帖子吧。
https://whycan.com/t_5527.html

离线

楼主 #49 2021-12-09 10:24:08

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

spl的大小不一定就是24k,大小由链接脚本中自动计算的__spl_size来决定,但mksunxi工具会将spl按8k的整数倍来填充,这是为了兼容各种引导介质。
xfel现在是没有做任何写1k跳1k的,后面会增加一个命令行参数来做这件事。

同样跟你有一样的疑惑,现在没有按1k跳为何能有ddr打印信息,checksum肯定是不正确的,理论上应该是禁止运行的才对。除非,对于nand 引导,checksum根本就不起作用,要确认这个问题,就需要去逆向brom了。

假设checksum不起作用,或者歪打正着,碰对了,那么后面的现象就是可解释的。
同样关注这个问题,争取有个明确的结论。

这里提供一个之前做的工作,用于判断启动的引导介质,已验证。

enum {
	BOOT_DEVICE_SPINOR,
	BOOT_DEVICE_SPINAND,
	BOOT_DEVICE_SDCARD,
};

static int get_boot_device(void)
{
	uint8_t s = *((volatile uint8_t *)(0x00020000 + 0x28));

	if(s == 0x3)
		return BOOT_DEVICE_SPINOR;
	else if(s == 0x4)
		return BOOT_DEVICE_SPINAND;
	else if(s == 0x0)
		return BOOT_DEVICE_SDCARD;
	return BOOT_DEVICE_SPINOR;
}

离线

楼主 #53 2021-12-09 13:46:42

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

@woolen
那很有可能brom spi nand flash 引导时的地址不是0,而是64KB,还有一个可能的地方,就是每个page使用的是2k,而不是1k。

两个方向查找原因:
1,spi nand flash引导地址
2,每page采用2k还是1k,checksum运算应该是必须的。

离线

楼主 #54 2021-12-09 13:53:00

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

猜测,brom里面自动搜索spi nand flash 有效spl,这个是合理的,而且也很有可能就是这样,但很有可能,D1 的brom是每个page读取2k,这样对于2k page的spi nand flash就无需留空了。

离线

楼主 #56 2021-12-09 16:06:27

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

那就写一个sys-spinand.c,测试下,是否能完成引导,既然是多个引导地址,那就从0开始写了。

关于spinand自拷贝,可以参考xfel里面的代码,当然,全志的boot0也可以参考。

离线

楼主 #57 2021-12-09 20:39:44

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

已实现spinand引导,确认每个page使用2k,自拷贝函数实现。

/*
 * sys-spinand.c
 *
 * Copyright(c) 2007-2021 Jianjun Jiang <8192542@qq.com>
 * Official site: http://xboot.org
 * Mobile phone: +86-18665388956
 * QQ: 8192542
 *
 * 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 <xboot.h>

/*
 * Default spi nand page size: 2048(11), 4096(12)
 */
#define SPINAND_PAGE_BITS	(11)
#define SPINAND_PAGE_MASK	((1 << SPINAND_PAGE_BITS) - 1)
#define SPINAND_PAGE_SIZE	(1 << SPINAND_PAGE_BITS)

enum {
	SPI_GCR	= 0x04,
	SPI_TCR	= 0x08,
	SPI_IER	= 0x10,
	SPI_ISR	= 0x14,
	SPI_FCR	= 0x18,
	SPI_FSR	= 0x1c,
	SPI_WCR	= 0x20,
	SPI_CCR	= 0x24,
	SPI_MBC	= 0x30,
	SPI_MTC	= 0x34,
	SPI_BCC	= 0x38,
	SPI_TXD	= 0x200,
	SPI_RXD	= 0x300,
};

void sys_spinand_init(void)
{
	virtual_addr_t addr;
	u32_t val;

	/* Config GPIOC2, GPIOC3, GPIOC4 and GPIOC5 */
	addr = 0x02000060 + 0x00;
	val = read32(addr);
	val &= ~(0xf << ((2 & 0x7) << 2));
	val |= ((0x2 & 0xf) << ((2 & 0x7) << 2));
	write32(addr, val);

	val = read32(addr);
	val &= ~(0xf << ((3 & 0x7) << 2));
	val |= ((0x2 & 0xf) << ((3 & 0x7) << 2));
	write32(addr, val);

	val = read32(addr);
	val &= ~(0xf << ((4 & 0x7) << 2));
	val |= ((0x2 & 0xf) << ((4 & 0x7) << 2));
	write32(addr, val);

	val = read32(addr);
	val &= ~(0xf << ((5 & 0x7) << 2));
	val |= ((0x2 & 0xf) << ((5 & 0x7) << 2));
	write32(addr, val);

	/* Deassert spi0 reset */
	addr = 0x0200196c;
	val = read32(addr);
	val |= (1 << 16);
	write32(addr, val);

	/* Open the spi0 gate */
	addr = 0x02001940;
	val = read32(addr);
	val |= (1 << 31);
	write32(addr, val);

	/* Open the spi0 bus gate */
	addr = 0x0200196c;
	val = read32(addr);
	val |= (1 << 0);
	write32(addr, val);

	/* Select pll-periph0 for spi0 clk */
	addr = 0x02001940;
	val = read32(addr);
	val &= ~(0x3 << 24);
	val |= 0x1 << 24;
	write32(addr, val);

	/* Set clock pre divide ratio, divided by 1 */
	addr = 0x02001940;
	val = read32(addr);
	val &= ~(0x3 << 8);
	val |= 0x0 << 8;
	write32(addr, val);

	/* Set clock divide ratio, divided by 6 */
	addr = 0x02001940;
	val = read32(addr);
	val &= ~(0xf << 0);
	val |= (6 - 1) << 0;
	write32(addr, val);

	/* Set spi clock rate control register, divided by 2 */
	addr = 0x04025000;
	write32(addr + SPI_CCR, 0x1000);

	/* Enable spi0 and do a soft reset */
	addr = 0x04025000;
	val = read32(addr + SPI_GCR);
	val |= (1 << 31) | (1 << 7) | (1 << 1) | (1 << 0);
	write32(addr + SPI_GCR, val);
	while(read32(addr + SPI_GCR) & (1 << 31));

	val = read32(addr + SPI_TCR);
	val &= ~(0x3 << 0);
	val |= (1 << 6) | (1 << 2);
	write32(addr + SPI_TCR, val);

	val = read32(addr + SPI_FCR);
	val |= (1 << 31) | (1 << 15);
	write32(addr + SPI_FCR, val);
}

void sys_spinand_exit(void)
{
	virtual_addr_t addr = 0x04025000;
	u32_t val;

	/* Disable the spi0 controller */
	val = read32(addr + SPI_GCR);
	val &= ~((1 << 1) | (1 << 0));
	write32(addr + SPI_GCR, val);
}

static void sys_spi_select(void)
{
	virtual_addr_t addr = 0x04025000;
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x0 << 7);
	write32(addr + SPI_TCR, val);
}

static void sys_spi_deselect(void)
{
	virtual_addr_t addr = 0x04025000;
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x1 << 7);
	write32(addr + SPI_TCR, val);
}

static void sys_spi_write_txbuf(u8_t * buf, int len)
{
	virtual_addr_t addr = 0x04025000;
	int i;

	write32(addr + SPI_MTC, len & 0xffffff);
	write32(addr + SPI_BCC, len & 0xffffff);
	if(buf)
	{
		for(i = 0; i < len; i++)
			write8(addr + SPI_TXD, *buf++);
	}
	else
	{
		for(i = 0; i < len; i++)
			write8(addr + SPI_TXD, 0xff);
	}
}

static int sys_spi_transfer(void * txbuf, void * rxbuf, int len)
{
	virtual_addr_t addr = 0x04025000;
	int count = len;
	u8_t * tx = txbuf;
	u8_t * rx = rxbuf;
	u8_t val;
	int n, i;

	while(count > 0)
	{
		n = (count <= 64) ? count : 64;
		write32(addr + SPI_MBC, n);
		sys_spi_write_txbuf(tx, n);
		write32(addr + SPI_TCR, read32(addr + SPI_TCR) | (1 << 31));

		while((read32(addr + SPI_FSR) & 0xff) < n);
		for(i = 0; i < n; i++)
		{
			val = read8(addr + SPI_RXD);
			if(rx)
				*rx++ = val;
		}

		if(tx)
			tx += n;
		count -= n;
	}
	return len;
}

static int sys_spi_write_then_read(void * txbuf, int txlen, void * rxbuf, int rxlen)
{
	if(sys_spi_transfer(txbuf, NULL, txlen) != txlen)
		return -1;
	if(sys_spi_transfer(NULL, rxbuf, rxlen) != rxlen)
		return -1;
	return 0;
}

static void sys_spinand_wait(void)
{
	u8_t tx[2];
	u8_t rx[1];

	tx[0] = 0x0f;
	tx[1] = 0xc0;
	do {
		sys_spi_select();
		sys_spi_write_then_read(tx, 2, rx, 1);
		sys_spi_deselect();
	} while((rx[0] & 0x1) == 0x1);
}

void sys_spinand_read(int addr, void * buf, int count)
{
	u8_t tx[4];
	u32_t pa, ca;
	int n;

	while(count > 0)
	{
		pa = addr >> SPINAND_PAGE_BITS;
		ca = addr & SPINAND_PAGE_MASK;
		n = count > (SPINAND_PAGE_SIZE - ca) ? (SPINAND_PAGE_SIZE - ca) : count;
		tx[0] = 0x13;
		tx[1] = (u8_t)(pa >> 16);
		tx[2] = (u8_t)(pa >> 8);
		tx[3] = (u8_t)(pa >> 0);
		sys_spi_select();
		sys_spi_write_then_read(tx, 4, 0, 0);
		sys_spi_deselect();
		sys_spinand_wait();
		tx[0] = 0x03;
		tx[1] = (u8_t)(ca >> 8);
		tx[2] = (u8_t)(ca >> 0);
		tx[3] = 0x0;
		sys_spi_select();
		sys_spi_write_then_read(tx, 4, buf, n);
		sys_spi_deselect();
		sys_spinand_wait();
		addr += n;
		buf += n;
		count -= n;
	}
}

其中,因spl代码需要实现成位置无关,就不支持芯片型号检测了,一般的spi nand flash page 为2k,4k,这些通过宏定义来设置了,不动态识别。

/*
 * Default spi nand page size: 2048(11), 4096(12)
 */
#define SPINAND_PAGE_BITS	(11)
#define SPINAND_PAGE_MASK	((1 << SPINAND_PAGE_BITS) - 1)
#define SPINAND_PAGE_SIZE	(1 << SPINAND_PAGE_BITS)

这个自拷贝函数,为连续拷贝,不做任何间隔拷贝,间隔拷贝函数仅BROM中的才是如此,xboot中,spl代码实现成位置无关的,且为一个独立的镜像。其镜像最前面就是spl代码。

所以烧录时,将xboot的前64k,烧录到0地址,这个烧录过程需要做间隔处理,再将完整xboot烧录到256k的位置,这个位置无需做任何间隔处理,由上面的函数来实现自拷贝。

2k page大小的flash时,spl代码烧写无需特殊处理。

离线

楼主 #58 2021-12-09 23:27:41

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

离线

楼主 #59 2021-12-09 23:29:41

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

离线

楼主 #60 2021-12-09 23:44:37

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

结合搜索的各种资料,大概明白了,spl的整个过程,brom引导时会从block0开始依次检测合法spl代码,spl会存在多个备份,直到找到合法的为止,各个备份spl按block来对齐,如果 SPL 超过 1 个 block, 单个备份起始 block 地址为偶数。最大可以有8个备份,一个page是2kb有效,但这个现在不敢肯定,手上没有4kpage的nand来测试,估计大概率是还是2k有效。

离线

楼主 #62 2021-12-10 18:25:31

xboot
会员
注册时间: 2019-10-15
已发帖子: 675
积分: 421

Re: 为方便大家研究全志RISCV D1芯片,花了点时间编写了一个精简版的裸机程序,方便大家学习参考。

将spl保留区域调整为1MB,并扩展了xfel,支持烧录spl镜像到spi nand flash,可以烧录普通镜像,以及压缩镜像,并能正常引导启动。

烧写普通镜像到SPI Nand Flash

sudo xfel spinand splwrite 2048 1048576 xboot.bin

烧写压缩镜像到SPI Nand Flash

sudo xfel spinand splwrite 2048 1048576 xboot.bin.z

xfel 1.2.2版本支持的命令

xfel(v1.2.2) - https://github.com/xboot/xfel
usage:
    xfel version                                             - Show chip version
    xfel hexdump <address> <length>                          - Dumps memory region in hex
    xfel dump <address> <length>                             - Binary memory dump to stdout
    xfel exec <address>                                      - Call function address
    xfel read32 <address>                                    - Read 32-bits value from device memory
    xfel write32 <address> <value>                           - Write 32-bits value to device memory
    xfel read <address> <length> <file>                      - Read memory to file
    xfel write <address> <file>                              - Write file to memory
    xfel reset                                               - Reset device using watchdog
    xfel sid                                                 - Show sid information
    xfel jtag                                                - Enable jtag debug
    xfel ddr [type]                                          - Initial ddr controller with optional type
    xfel spinor                                              - Detect spi nor flash
    xfel spinor read <address> <length> <file>               - Read spi nor flash to file
    xfel spinor write <address> <file>                       - Write file to spi nor flash
    xfel spinand                                             - Detect spi nand flash
    xfel spinand read <address> <length> <file>              - Read spi nand flash to file
    xfel spinand write <address> <file>                      - Write file to spi nand flash
    xfel spinand splwrite <valid-page-size> <address> <file> - Write file to spi nand flash with spl mode

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn