研究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
离线
请问楼主怎么实现同一个固件,既能在ddr跑,又能在flash跑的?
离线
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中运行。
离线
原来如此,感谢大佬解惑。
离线
好东西,感谢xboot大佬分享裸机工程
离线
请问楼主,按照您的步骤,利用xfel工具直接在DDR中运行d1-baremetal.bin,串口输出运行信息,但是烧录到spi nor flash后,上电运行,串口并未输出信息,这是怎么回事呢?
离线
利用spinor read 读出spi nor中的内容 发现没有并没有将.bin写进去
离线
是d1,应该是焊接好了,spi nor flash的信息如下:
用xfel spinor这个命令输出的信息如下
离线
是d1,应该是焊接好了,spi nor flash的信息如下:
https://whycan.com/files/members/6619/111_20210621-1358.png
用xfel spinor这个命令输出的信息如下https://whycan.com/files/members/6619/捕获_20210621-1357.png
怪不得了,有两个选择
① 换用xfel代码里面支持的flash
② 修改 xfel代码兼容你的flash
离线
请问xfel支持的spi nor flash是什么型号的呢
离线
static const struct spinor_info_t spinor_infos[] = {
{ "w25x40", 0xef3013, 512 * 1024, 4096, 1, 256, 3, OPCODE_READ, OPCODE_PROG, OPCODE_WREN, OPCODE_E4K, 0, OPCODE_E64K, 0 },
};
https://github.com/xboot/xfel/blob/master/spinor.c
看代码, 应该目前只支持 w25x40
离线
目前支持所有sfdp协议 nor flash,不支持sfdp协议的,就需要自己填入flash信息,w25x40是不支持sfdp的
离线
你这边既然能正确获取flash容量,应该是支持sfdp协议的,但还是烧不进去,有点奇怪,但可以做个测试,重新上电后,仅写入64KB,然后再读出来,看是否一致。不应该出现这种现象的。
离线
你在虚拟机里跑,有没有可能是虚拟机相关问题了,可以真机或者windows烧录实验下。
离线
我再测试测试
离线
请问楼主可以分享一下D1的mksunxi工具的源码吗?
离线
请问楼主可以分享一下D1的mksunxi工具的源码吗?
mksunxi.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#if 0
static inline uint32_t __swab32(uint32_t x)
{
return ( (x<<24) | (x>>24) | \
((x & (uint32_t)0x0000ff00UL)<<8) | \
((x & (uint32_t)0x00ff0000UL)>>8) );
}
#define cpu_to_le32(x) (__swab32((uint32_t)(x)))
#define le32_to_cpu(x) (__swab32((uint32_t)(x)))
#else
#define cpu_to_le32(x) (x)
#define le32_to_cpu(x) (x)
#endif
struct boot_head_t {
uint32_t instruction;
uint8_t magic[8];
uint32_t checksum;
uint32_t length;
uint8_t spl_signature[4];
uint32_t fel_script_address;
uint32_t fel_uenv_length;
uint32_t dt_name_offset;
uint32_t reserved1;
uint32_t boot_media;
uint32_t string_pool[13];
};
int main (int argc, char *argv[])
{
struct boot_head_t * h;
FILE * fp;
char * buffer;
int buflen, filelen;
uint32_t * p;
uint32_t sum;
int i, l, loop;
if(argc != 2)
{
printf("Usage: mksunxi <bootloader>\n");
return -1;
}
fp = fopen(argv[1], "r+b");
if(fp == NULL)
{
printf("Open bootloader error\n");
return -1;
}
fseek(fp, 0L, SEEK_END);
filelen = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if(filelen <= sizeof(struct boot_head_t))
{
fclose(fp);
printf("The size of bootloader too small\n");
return -1;
}
buflen = (filelen + 0x2000) & ~(0x2000 - 1);
buffer = malloc(buflen);
memset(buffer, 0, buflen);
if(fread(buffer, 1, filelen, fp) != filelen)
{
printf("Can't read bootloader\n");
free(buffer);
fclose(fp);
return -1;
}
h = (struct boot_head_t *)buffer;
p = (uint32_t *)h;
l = le32_to_cpu(h->length);
h->checksum = cpu_to_le32(0x5F0A6C39);
loop = l >> 2;
for(i = 0, sum = 0; i < loop; i++)
sum += le32_to_cpu(p[i]);
h->checksum = cpu_to_le32(sum);
fseek(fp, 0L, SEEK_SET);
if(fwrite(buffer, 1, buflen, fp) != buflen)
{
printf("Write bootloader error\n");
free(buffer);
fclose(fp);
return -1;
}
fclose(fp);
printf("The bootloader head has been fixed\n");
return 0;
}
离线
请问楼主,关于中断向量表这部分的汇编代码,有点不太懂,主要是vectors:csrw mscratch, sp addi sp, sp, -(37 * REGSZ) SREG x1, 1 * REGSZ(x2)...这部分。
离线
楼主,还有这个问题: https://whycan.com/t_6706.html ,有时候DDR信息能输出完整,有时候输出又不完整,很奇怪。
离线
请问楼主,关于中断向量表这部分的汇编代码,有点不太懂,主要是vectors:csrw mscratch, sp addi sp, sp, -(37 * REGSZ) SREG x1, 1 * REGSZ(x2)...这部分。
就是中断现场保护,没什么特别的。
离线
楼主,还有这个问题: https://whycan.com/t_6706.html ,有时候DDR信息能输出完整,有时候输出又不完整,很奇怪。
这个估计你哪里异常了,DDR不要初始化两次,还有要注意cache类问题,需要你自己仔细对比自己的操作。
离线
March 说:请问楼主,关于中断向量表这部分的汇编代码,有点不太懂,主要是vectors:csrw mscratch, sp addi sp, sp, -(37 * REGSZ) SREG x1, 1 * REGSZ(x2)...这部分。
就是中断现场保护,没什么特别的。
好的,谢谢楼主,请问可以提供一份关于中断的裸机程序吗(如GPIO中断),中断这部分不知道该怎么处理。谢谢。
离线
看xboot代码里面都有的
离线
参考xboot的代码,加了GPIO中断的相关代码,但是触发不了中断,不知道是哪里出了问题,求帮助。d1-baremetal.rar
离线
离线
你这是在搞笑吗?中断控制器都没有初始化,还想触发中断
离线
中断控制器是PLIC吧,我初始化了啊, 望指点
离线
先尝试用定时器做中断源吧,至少这个xboot是验证通过的,而且gpio中断,属于子中断,需要先开启父中断的
最近编辑记录 xboot (2021-06-30 15:26:17)
离线
先尝试用定时器做中断源吧,至少这个xboot是验证通过的,而且gpio中断,属于子中断,需要先开启父中断的
定时器确实可以产生中断,我写的gpio中断的流程如下:
timer的处理是类似的,您看我的gpio中断流程哪里有问题啊,不知道您说的父中断指的什么呢?
离线
父中段就是一个中断被多个子中段共享,gpio一般是一组对应一个父中断
离线
在数据手册上没有找到开启gpio父中断的相关寄存器啊
离线
父中段就是一个中断被多个子中段共享,gpio一般是一组对应一个父中断
根据您的意思,,这个寄存器应该控制着是gpio的子中断,那PLIC_MIE_ REGn是控制着gpio的父中断吗?
离线
这是中断编号,手册里有的
#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)
离线
在D1裸机程序中的sys_spinor_init函数中:
在用户手册上并未找到SPI_CCR寄存器的相关说明啊,望指点。
离线
在D1裸机程序中的sys_spinor_init函数中:
https://whycan.com/files/members/6619/微信图片_20210706095934.png
在用户手册上并未找到SPI_CCR寄存器的相关说明啊,望指点。
本站精华帖汇总, 各种手册在线下载,各种开源pcb工程下载
https://whycan.com/t_3019.html#p25005
d1手册确实没有 spi ccr,你找下arm系列手册,偏移应该是0x24。
离线
关于D1裸机的系统软件复位,在用户手册有如下寄存器描述:
WDOG_SOFT_RST_REG[31:16]:要想改变WDOG_SOFT_RST_REG[0]的值,需将WDOG_SOFT_RST_REG[31:16]设置为0x16AA,
WDOG_SOFT_RST_REG[0]:写1清零,那我怎么才能将WDOG_SOFT_RST_REG[0]变为1使得复位整个系统呢?
是直接将将WDOG_SOFT_RST_REG[31:16]设置为0x16AA就能使得WDOG_SOFT_RST_REG[0]变为1吗?
离线
找到问题在哪了,因为该寄存器的有些位是只写的,所以要一次性赋值,如下:
离线
[ERROR DEBUG] auto scan dram rank&width fail !
DRAM only have internal ZQ!!
get_pmu_exist() = 4294967295
ddr_efuse_type: 0xa
ZQ calibration error,check external 240 ohm resistor.
[ERROR DEBUG] auto scan dram rank&width fail !
烧写进F133-A SPI-NOR DDR初始化失败!
最近编辑记录 ccl (2021-11-30 17:23:27)
离线
请问我用xfel工具直接在DDR中运行d1-baremetal.bin,可以正常运行,但是烧录到d1自带spinand的0地址后,重启只有ddr初始化成功的打印,没有main的count循环打印,这个正常吗?我测过从spinand读出来24k确认已经烧录成功了。
离线
请问我用xfel工具直接在DDR中运行d1-baremetal.bin,可以正常运行,但是烧录到d1自带spinand的0地址后,重启只有ddr初始化成功的打印,没有main的count循环打印,这个正常吗?我测过从spinand读出来24k确认已经烧录成功了。
估计是这个bin没有magic和crc,
可能brom不识别吧?
离线
@cube
从代码里面直接编译出来的bin,有执行mksunxi的,而且ddr初始化的部分有打印的,应该是已经被加载到了sram然后执行了一部分?
离线
@cube
从代码里面直接编译出来的bin,有执行mksunxi的,而且ddr初始化的部分有打印的,应该是已经被加载到了sram然后执行了一部分?
日志贴出来看看
离线
日志贴出来看看
编译和烧录:
woolen@ubuntu:/disk4/allwinner_d1/d1-baremetal$ make CROSS=riscv64-unknown-elf-
[AS] source/start.S
[AS] source/memcpy.S
[AS] source/memset.S
[CC] source/sys-clock.c
[CC] source/sys-dram.c
[CC] source/sys-uart.c
[CC] source/sys-spinor.c
[CC] source/main.c
[CC] source/sys-jtag.c
[CC] source/sys-copyself.c
[LD] Linking output/d1-baremetal.elf
[OC] Objcopying output/d1-baremetal.bin
copy from `output/d1-baremetal.elf' [elf64-littleriscv] to `output/d1-baremetal.bin' [binary]
Make header information for brom booting
The bootloader head has been fixed, spl size is 24576 bytes.
woolen@ubuntu:/disk4/allwinner_d1/d1-baremetal$ xfel version
AWUSBFEX soc=0x00185900(D1/F133) 0x00000001 ver=0x0001 0x44 0x08 scratchpad=0x00045000
woolen@ubuntu:/disk4/allwinner_d1/d1-baremetal$ xfel spinand write 0x0 output/d1-baremetal.bin
100% [================================================] 24.000 KB, 246.427 KB/s
重新上电后串口打印,只有ddr的部分。
DRAM only have internal ZQ!!
get_pmu_exist() = 4294967295
ddr_efuse_type: 0x0
[AUTO DEBUG] two rank and full DQ!
ddr_efuse_type: 0x0
[AUTO DEBUG] rank 0 row = 15
[AUTO DEBUG] rank 0 bank = 8
[AUTO DEBUG] rank 0 page size = 2 KB
[AUTO DEBUG] rank 1 row = 15
[AUTO DEBUG] rank 1 bank = 8
[AUTO DEBUG] rank 1 page size = 2 KB
rank1 config same as rank0
DRAM BOOT DRIVE INFO: %s
DRAM CLK = 792 MHz
DRAM Type = 3 (2:DDR2,3:DDR3)
DRAMC ZQ value: 0x7b7bfb
DRAM ODT value: 0x42.
ddr_efuse_type: 0x0
DRAM SIZE =1024 M
DRAM simple test OK.
离线
@woolen
这个测试demo仅支持spi nor flash引导,如果要支持spi nand引导,需要做两个工作,一个是实现spi nand自拷贝,另一个是,一个page只能使用前2k,后面的需要空着。
spi nand flash 引导是有点特殊的。但只要满足这两个要求,就可以实现spi nand 自举了。
离线
知道了,nand拷贝到mem的部分还没写,我自己改一下...
if(d == BOOT_DEVICE_SPINOR)
{
mem = (void *)__image_start;
size = __image_end - __image_start;
sys_spinor_init();
sys_spinor_read(0, mem, size);
sys_spinor_exit();
}
else if(d == BOOT_DEVICE_SPINAND)
{
}
离线
@woolen
这个测试demo仅支持spi nor flash引导,如果要支持spi nand引导,需要做两个工作,一个是实现spi nand自拷贝,另一个是,一个page只能使用前2k,后面的需要空着。spi nand flash 引导是有点特殊的。但只要满足这两个要求,就可以实现spi nand 自举了。
谢谢回复。
“一个page只能使用前2k,后面的需要空着” 这个我不太理解,能详细说一下吗?
离线
离线
我看了之后还有点疑问,现在的裸机程序是24k,我用xfel烧到spinand,xfel会写1k跳1k吗?如果不会的话,既然代码已经起来了,是不是说明d1的brom可以连续读取nand flash了?
最近编辑记录 woolen (2021-12-08 20:17:31)
离线
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;
}
离线
@xboot
我的板子只有spinand,加上了get_boot_device,处理一下返回值输出到串口,也确认是BOOT_DEVICE_SPINAND。flash型号是出厂的那个MX35LF2GE4AD,看了下page是2k。
然后做了点实验,对d1-baremetal.bin做了修改:crc替换一个字节、magic替换一个字节、bin末尾替换一个字节,这样分别生成了3个bin并用xfel烧录到spinand的0地址,发现启动都没有被brom加载。不过brom加载了64k偏移位置的另一个spl,怀疑是之前我用PhoneixSuit烧录的固件在nand的64k位置备份了一个spl,把这段再换掉就起不来了。(之前很疑惑起的spl打印不是我编的,在另外帖子看到下面的话然后去dump了一下flash内容发现64k位置还有一个spl)
Some SoCs can also boot from SPI NAND flash. Here the BROM tries to read a valid first stage bootloader starting from page number 0, 32, 64, 96, 128, 160, 192 and 224. It only reads the first 1024 bytes from every page.
所以我得出的结论是brom对spinand的magic和crc都是有校验的,并且看起来不是每个页只读1K,这个跟上面这段话有点矛盾~
离线
感谢大佬解惑。
离线
离线
@woolen
那很有可能brom spi nand flash 引导时的地址不是0,而是64KB,还有一个可能的地方,就是每个page使用的是2k,而不是1k。
两个方向查找原因:
1,spi nand flash引导地址
2,每page采用2k还是1k,checksum运算应该是必须的。
离线
猜测,brom里面自动搜索spi nand flash 有效spl,这个是合理的,而且也很有可能就是这样,但很有可能,D1 的brom是每个page读取2k,这样对于2k page的spi nand flash就无需留空了。
离线
猜测,brom里面自动搜索spi nand flash 有效spl,这个是合理的,而且也很有可能就是这样,但很有可能,D1 的brom是每个page读取2k,这样对于2k page的spi nand flash就无需留空了。
我重新用PhoenixSuit烧录了一个tina的固件(spl是64k),从spinand读一下内容发现spl在每个block上(0x0、0x20000、0x40000、0x60000...)都有一份,应该是为了防止坏块做的多备份,brom会从前往后找到可用的spl来加载。(不知道为什么我之前的spl备份是在0x10000上而且后面没有,暂时先不纠结了)
把d1-baremetal.bin再烧录spinand的0x0位置上,启动的就是d1-baremetal.bin。D1的brom每个page读取2k这个我感觉很有可能。
离线
那就写一个sys-spinand.c,测试下,是否能完成引导,既然是多个引导地址,那就从0开始写了。
关于spinand自拷贝,可以参考xfel里面的代码,当然,全志的boot0也可以参考。
离线
已实现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代码烧写无需特殊处理。
离线
离线
离线
结合搜索的各种资料,大概明白了,spl的整个过程,brom引导时会从block0开始依次检测合法spl代码,spl会存在多个备份,直到找到合法的为止,各个备份spl按block来对齐,如果 SPL 超过 1 个 block, 单个备份起始 block 地址为偶数。最大可以有8个备份,一个page是2kb有效,但这个现在不敢肯定,手上没有4kpage的nand来测试,估计大概率是还是2k有效。
离线
@xboot
感谢,我今天想试一下,发现大佬已经写好了,这边d1-baremetal用spinand已经测试OK。
离线
将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
离线
我的开发板是1G DDR的版本,在执行xfel write 0x40000000 d1-baremetal.bin的时候会提示usb bulk send error,是因为超出内存上限了吗?我后来试了一下把地址改成0x20000000,没有错误提示,但串口接收不到信息。是代码里有些地方也要做相应的修改吗?
另外执行初始化DDR会显示
xfel ddr d1 - Initial ddr controller for D1
xfel ddr f133 - Initial ddr controller for F133
Failed to initial ddr controller
这个是什么问题呢?
离线
@epsilon_f
好吧,我的问题解决了,来自问自答一下。DDR初始化的指令应该是xfel ddr d1或者xfel ddr f133,虽然我不是很明白为什么会有区别,是xfel对不同的开发板写了不同的机制吗?
初始化成功后是可以在0x40000000写文件的,串口也可以得到正确的输出。想问一下为什么1G DDR的开发板可以在0x40000000的位置写东西呢?是因为初始化的过程中引入了某种虚拟内存机制吗?
离线
现在的xfel ddr初始化命令参数变成了d1/f133了?
离线
好厉害啊,在T113上,我花了好多天才明白的道理,原来出处在这里。
在线
我在测试过程中又遇到了一个问题。从用户手册上看出,0x20000是开发板SRAM的地址。如果修改链接脚本把初始位置放在这里,不初始化DDR然后用xfel把bin写入这里,是可以正常运行的。但是先初始化DDR就不行了,不知道为什么。
离线
新人问一个弱智的问题,“裸机程序” 是什么意思?
离线
新人问一个弱智的问题,“裸机程序” 是什么意思?
裸机程序就是在没有操作系统的环境下运行的程序。
离线
@xboot
你好,我看xboot代码中有支持了TVD。 CSI-DVP接口的信息有么。
离线
请问裸机跑是不是主要是链接文件要写正确? 代码直接编译运行应该不行吧?
离线
请问在qemu上怎么跑?需要专门的支持D1的qemu吗?
离线
xboot大佬,我看了你代码里面关于DDR部分,为什么全部是16进制代码?没有关于寄存器的操作吗?手册上确实没有找到关于DDR部分的资料。
离线