yukiiiteru / DailySchedule

My daily schedule of 2021
8 stars 0 forks source link

信息交流用issue #1

Open MaxXSoft opened 3 years ago

MaxXSoft commented 3 years ago

同学你好,我是MaxXing(似乎没什么用的自我介绍环节

发现这个repo是因为之前注意到你fork了Fuxi、YuLang和GeeOS,于是好奇的我就找到了这里。从那以后,这个repo就成为我每天睡前必读的内容了,不看根本睡不着觉(bushi

考虑到Fuxi、YuLang和GeeOS在设计和实现上可能存在许多坑,并且它们的文档也不完全(根本就不存在吧喂),所以这些坑暂时只有我自己清楚。但这样是不好的,因为有些东西不提前说的话,你可能会走很多弯路。

仔细读了一下大赛的章程,似乎没有禁止“导师和参赛队员讨论和交流与大赛题目相关的内容”这件事,于是决定在此开一个issue。在文档还没写好之前,我会在此处写一些你可能需要了解的内容。

MaxXSoft commented 3 years ago

第一条,Fuxi SoC里的那个CFG Flash控制器可能会有些问题。

不太清楚这个控制器在你的板子上会表现如何,毕竟你可能自行做了一些调整。但至少这个玩意在龙芯的板子上是没办法正常工作的,我之前抄NonTrivialMIPS的作业的时候肯定有哪个地方抄错了。

之前本来想找个机会问一下NonTrivialMIPS的开发者们,然后把这个问题修好。但,我可是老拖延症晚期了……(躺平

所以,之前我在SoC上跑程序都是用串口上传的,串口的上位机是用Python写的,在GeeOS的repo里可以找到,你或许可以参考一下这种方式。

yukiiiteru commented 3 years ago

学长你好,其实我之前就有注意到您 star 了我的 repo,当时还激动了半天(x),也让我更有动力继续折腾下去了。

其实写这个主要还是受去年参加清华大学 OS Tutorial Summer of Code 活动的影响,感觉把日程都写下来,跟写日记一样,可以记录自己的进展,会更有成就感一些;也会像做每日任务一样,每天都要写点东西,可以有效避免摸鱼(?);甚至找工作的时候给 HR 看自己的 GitHub,也会给人留下一种比较好的印象吧,所以我就一直坚持下来了。

说到设计和实现上的坑,其实我目前还并没有感觉到,现阶段的话对这三个项目的感觉还只有佩服。我本来打算有时间的话我也实现一个 CPU、OS、Compiler,甚至 Router,但是我还是执行力略差。好在及时发现并有机会参与这个比赛,也算帮我实现了理想吧。

至于文档,我觉得有文档可以帮助读者更快地熟悉项目,没有的话也可以锻炼读代码的能力,这对我来说倒无所谓啦。说到底其实走弯路也是一个深入学习的过程,只是会更费时一些。当然,您特地过来指导,我还是非常感激的。

在我买的板子 AX7102 的文档里提到,这块板子上用到的似乎是一个普通的 QSPI Flash,如果读 Flash 有什么问题的话,我可以把读 CFG Flash 的 IP Core 改成 AXI Quad SPI 的默认配置,或者参考买板子送的配套资料改动一下试试看,谢谢您的提醒。

不过这边现在的主要问题是,我还不知道如何把 ELF 文件烧到 Flash 里面(毕竟我刚开始用 Vivado 十几天 orz),我正在查各种教程资料。在找到方法前,可以参考您的方式先用串口调试着,谢谢指点。

MaxXSoft commented 3 years ago

关于 Fuxi SoC:

yukiiiteru commented 3 years ago

我之前有看过 Fuxi/soc/README.md 以及 Vivado 的 Address Editor,上面提到 0x00000000~0x0000FFFF 是 On-chip ROM,但是我之前不知道那是啥(x),也没有注意过这方面,前几天一直默认那是 CFG Flash,刚刚又确认了一下才知道是 BRAM,似乎走了一些弯路,谢谢提醒。

我查资料了解了一下,Boot ROM 里固化的内容应该是 Fuxi/soc/soc.srcs/sources_1/new/ocm.coe 这个文件里的内容。看了一下 GeeOS/src/boot/linker.ld,起始位置是 0x00000200,这里应该就是 bootloader。GeeOS/src/Makefile 里也已经写好了生成 coe 文件的规则,至于拨码开关的问题,有了这些的话改起来也会比较方便。

然而之前由于我的理解错误,导致我把 Parameters.scala 里面的复位向量瞎改,改成了一个错误的值,最近在尝试仿真一直得不到正确的波形。(虽然改回去之后波形也没怎么变化,真的说明我改的 SoC 有问题了

经过您的提醒,解决了我不少疑惑。现在我已经有更明确的思路了,非常感谢。

MaxXSoft commented 3 years ago

关于SoC的仿真:

Fuxi SoC 中用到了一个 DDR3 内存控制器,而 DDR3 内存颗粒在上电之后是需要训练和校准的。为了保证 CPU 在上电之后,DDR3 总能初始化完毕,SoC 内部有一套逻辑,用来检查 DDR3 控制器回传的校准完毕信号,并保证在校准完毕之前,CPU 的复位信号一直有效。

所以,直接仿真 SoC 时,DDR3 内存控制器上什么也没接,DDR3 校准永远都不会完成,CPU 永远都不会停止复位……

解决方法:

yukiiiteru commented 3 years ago

我昨天有注意到 CPU 的 rst 信号一直是 1,而 CPU 的 rst 输入是 ACTIVE HIGH 的。我用了最简单粗暴的方法:把 const_low 直接连 CPU,但是也没什么用,仿真信号一片红,没想到是 DDR3 控制器的问题。

此外我刚刚也犯了一些低级错误,比如仿真默认只进行到 1s (1,000,000 ns) 的位置,如果需要看之后的波形的话还要再点一下 Run All 才可以,而 sys_reset 在 1s 之后才能让 mb_reset 信号变成 0,在我发现这个问题之前一直以为这个解决方法没用,又走了几个小时的弯路(x),不过还是解决了,非常感谢。

MaxXSoft commented 3 years ago

我用了最简单粗暴的方法:把 const_low 直接连 CPU,但是也没什么用,仿真信号一片红

你这么做是必然会出问题的: 任何带寄存器的电路, 必须要进行复位, 否则电路里寄存器的数据全都是乱的, 电路会处在一个不确定的状态之中. 表现在仿真软件的波形显示上, 就是大部分波形都是红色的.

如果你先前并不清楚这个问题的话, 建议你先学习/复习一下数字电路课程相关的内容, 然后学习一下如何使用 Verilog (或者 Chisel) 进行 FPGA 上的数字电路设计.

否则, 你可能会遇到更多让你不知所措的问题.

yukiiiteru commented 3 years ago

有学过数电,但是对于 Verilog 的开发仅限于参考一些资料然后抄代码,没有亲自上手折腾过数字电路设计(毕竟不是科班 orz)。

学习 Chisel 数字电路设计已经在我上半年的计划中了,有简单刷过 Chisel-Bootcomp,接下来打算从 Fuxi 的代码入手,一边实践一边学习,理论结合实践效果会更好吧。

此外我觉得我也有必要了解一下这些基本的 IP Core 的作用,理解了 IP Core 的作用才能更好地开发。

感谢您的指点,我以后也会尽量避免犯这种低级错误的。

MaxXSoft commented 3 years ago

有学过数电,但是对于 Verilog 的开发仅限于参考一些资料然后抄代码,没有亲自上手折腾过数字电路设计(毕竟不是科班 orz)。

学习 Chisel 数字电路设计已经在我上半年的计划中了,有简单刷过 Chisel-Bootcomp,接下来打算从 Fuxi 的代码入手,一边实践一边学习,理论结合实践效果会更好吧。

此外我觉得我也有必要了解一下这些基本的 IP Core 的作用,理解了 IP Core 的作用才能更好地开发。

感谢您的指点,我以后也会尽量避免犯这种低级错误的。

加油💪

yukiiiteru commented 3 years ago

加油

会的~

对了再问一个小问题,我之前就有注意到,在 GeeOS/src/arch/target/fuxi.yu 中,UART 相关的地址都是从 0x11041000 开始的,而 Fuxi/soc/README.md 里以及 Address Editor 里写 UART 的地址是从 0x11040000 开始的,这 0x1000 的偏差是怎么回事,为什么 virt.yu,也就是 QEMU 环境下可以直接使用 UART_ADDR,而 fuxi.yu 不能直接用 UART_ADDR 呢?

谢谢。

MaxXSoft commented 3 years ago

对了再问一个小问题,我之前就有注意到,在 GeeOS/src/arch/target/fuxi.yu 中,UART 相关的地址都是从 0x11041000 开始的,而 Fuxi/soc/README.md 里以及 Address Editor 里写 UART 的地址是从 0x11040000 开始的,这 0x1000 的偏差是怎么回事,为什么 virt.yu,也就是 QEMU 环境下可以直接使用 UART_ADDR,而 fuxi.yu 不能直接用 UART_ADDR 呢?

这个问题并不重要. 这么做单纯是因为: Xilinx 的 UART 控制器 IP (AXI UART 16550) 定义的地址, 就是从 0x1000 处开始的, 我只能这么做.

这并不能归结于某些特殊的原因, 纯粹是 Xilinx 那边人为定义的. 详情请参考 IP 核的文档, 第 12 页.

yukiiiteru commented 3 years ago

明白了,谢谢。

yukiiiteru commented 3 years ago

话说,经过了这么久的折腾,我终于发现 DDR3 初始化不了,问题出在哪了。

在 Vivado 里 Open Block Design 之后,展开 DDR3 的输出管脚(应该是这么叫吧),可以看到共有 15 个 ddr3_ 开头的信号,在您的 SoC.v 中,43~56 行,只写出了其中的 14 个,而缺少 ddr3_cs_n 会影响 DDR3 的初始化,把这个加上之后就可以正常初始化了。

适当修改 SoC_tb.v,然后参考 Vivado 仿真MIG核 这篇文章,就可以在 Vivado 中对带 DDR3 的 Fuxi SoC 进行仿真了。

还是感谢您之前及时的指点,为我节省了大量的时间,也让我学到了很多东西。谢谢。

MaxXSoft commented 3 years ago

祝贺!

在 Vivado 里 Open Block Design 之后,展开 DDR3 的输出管脚(应该是这么叫吧),可以看到共有 15 个 ddr3_ 开头的信号,在您的 SoC.v 中,43~56 行,只写出了其中的 14 个,而缺少 ddr3_cs_n 会影响 DDR3 的初始化,把这个加上之后就可以正常初始化了。

关于 Xilinx MIG DDR3 信号的问题, 在我的环境中 (Vivado 2020.1, FPGA 为 xc7a200t, MIG 配置参考 Fuxi repo), MIG DDR3 端的信号确实只有 14 个, 并且每个的连接都很正常. 你遇到这个问题的原因我并不清楚, 但我猜测可能是因为我们的 FPGA 具有不同的配置, 或者 DDR3 颗粒的型号不太一样, 导致 MIG 在不同的配置下出现了不同的管脚.

yukiiiteru commented 3 years ago

关于 Xilinx MIG DDR3 信号的问题, 在我的环境中 (Vivado 2020.1, FPGA 为 xc7a200t, MIG 配置参考 Fuxi repo), MIG DDR3 端的信号确实只有 14 个, 并且每个的连接都很正常. 你遇到这个问题的原因我并不清楚, 但我猜测可能是因为我们的 FPGA 具有不同的配置, 或者 DDR3 颗粒的型号不太一样, 导致 MIG 在不同的配置下出现了不同的管脚.

我认为是 DDR3 颗粒型号的问题,因为龙芯的板子用了比较旧的 DDR3,在 MIG 配置界面选择该型号时会提示这个 Memory Part 比较 obsolete。虽然我尝试了多种版本的 Vivado 以及多种型号的 FPGA、DDR3 的组合,最终都没有复现出没有 ddr3_cs_n 信号的 MIG,但是原 repo 中也确实是没有这个信号的,这个我一直搞不懂...

害,不管这么多了,运行起来了就是好的~

yukiiiteru commented 3 years ago

话说经过了将近一个月的研(mo)究(yu),读 Flash 的问题终于解决了!

我读了一下 SPI 核的代码,发现要使其读 Flash,需要多个信号同时满足条件,仿真看了一下只有 S_AXI4_ARBURST 不符合要求。我在 bus/Uncached.scala 里补上了一行 io.axi.readAddr.bits.burst := 1.U,SoC 就可以读 Flash 了。

此外,AXI Quad SPI 核的地址线可能也需要改成 24 bit。改成 24 bit 是为了把映射到内存中的地址 (0x10800000) 转换成在 Flash 中的绝对地址 (0x800000),这方面不知道还有没有什么其他的解决方案,我还在折腾。

MaxXSoft commented 3 years ago

话说经过了将近一个月的研(mo)究(yu),读 Flash 的问题终于解决了!

祝贺! 以及, 感谢你帮我找到了这个问题! 辛苦了! 我之前放着这个问题不管确实有点不负责任 (:з」∠)

我读了一下 SPI 核的代码,发现要使其读 Flash,需要多个信号同时满足条件,仿真看了一下只有 S_AXI4_ARBURST 不符合要求。我在 bus/Uncached.scala 里补上了一行 io.axi.readAddr.bits.burst := 1.U,SoC 就可以读 Flash 了。

对照了一下我曾参考过的开源代码 (比如 NonTrivialMIPS), 我这里的实现确实和他们有所区别, 而且从你的描述看的确是这个问题导致了 Quad SPI 不工作. 直接这么改的话看起来没什么问题, 但具体会不会对 Uncache 的状态机产生影响, 还有待进一步测试.

此外,AXI Quad SPI 核的地址线可能也需要改成 24 bit。改成 24 bit 是为了把映射到内存中的地址 (0x10800000) 转换成在 Flash 中的绝对地址 (0x800000),这方面不知道还有没有什么其他的解决方案,我还在折腾。

这部分我就没有研究过了, 之前因为读不出数据的问题搁置了这部分. 目前手头的电脑里没有 Vivado, 也没办法做进一步的确认, 抱歉!

yukiiiteru commented 3 years ago

祝贺! 以及, 感谢你帮我找到了这个问题! 辛苦了! 我之前放着这个问题不管确实有点不负责任 _(:з」∠)_

没关系啦其实,我在折腾的途中也学到了不少东西。

对照了一下我曾参考过的开源代码 (比如 NonTrivialMIPS), 我这里的实现确实和他们有所区别, 而且从你的描述看的确是这个问题导致了 Quad SPI 不工作. 直接这么改的话看起来没什么问题, 但具体会不会对 Uncache 的状态机产生影响, 还有待进一步测试.

我没怎么读过 NonTrivialMIPS 的代码,我感觉应该是对状态机没有影响的(?),毕竟状态机并不依赖这一信号,而且目前运行起来也没有出什么大问题。

现在,Flash 这边还是有一些小问题没有解决。比如读 Flash 的时候第一次读到的内容一定是 0xcccccccc,这个 reset 一下就好了;之后在加载 ELF 把段复制到内存中时,getLastPhdr 会出错,phdr.getPaddr 也会出错,使用 UART 则不会出现这个问题,这里的原因还在排查。

总之,参与这个项目让我收获了很多,让我这个假期过得很充实,感谢!

yukiiiteru commented 3 years ago

查出读 SPI 出错的问题所在了。

在执行 getLastPhdr 的时候,代码是这样的:

// get address of last program header
inline def getLastPhdr(this: Elf32Ehdr*): Elf32Phdr* {
  let phdr = this.getPhdr()
  (phdr as u32 + ((*this).e_phentsize *
                  (*this).e_phnum) as u32) as Elf32Phdr*
}

这里需要连续读三个内存中的值,在汇编中表现为连续几条读取指令:

10800b37            lui s6,0x10800
01cb2503            lw  a0,28(s6) # 1080001c
02ab1583            lh  a1,42(s6) # 1080002a
02cb1603            lh  a2,44(s6) # 1080002c

抓了下波形,发现只读取了 0x1080001c0x1080002c 的值,0x1080002c 被跳过了。

怀疑是 Uncache 的状态机出的问题,但是我没有证据(x)而且也不知道是不是由 burst 信号导致的。

总之,我该学 Chisel 了。

MaxXSoft commented 3 years ago

抓了下波形,发现只读取了 0x1080001c0x1080002c 的值,0x1080002c 被跳过了。

怀疑是 Uncache 的状态机出的问题,但是我没有证据(x)而且也不知道是不是由 burst 信号导致的。

总之,我该学 Chisel 了。

收到, 看了一下这段似乎确实是状态机的问题.

我近期会调试一下, 十分感谢你发现了这个问题! 辛苦了!

yukiiiteru commented 3 years ago

收到, 看了一下这段似乎确实是状态机的问题.

我近期会调试一下, 十分感谢你发现了这个问题! 辛苦了!

不辛苦,其实最近都在搞毕业论文,也没怎么折腾这个。调试什么的也麻烦您了。

(虽然我是想自己解决这个问题的,但是 Chisel 我还没怎么学,可能目前还无能为力 orz

这段时间我也尽量折腾一下吧,在实践中学习效果会更好的,希望这段时间能与您有更多的交流。

感谢。

yukiiiteru commented 3 years ago

问题解决了。

经过一系列的研究,我把 Uncached 状态机的状态 sEnd 拆分成 sReadEndsWriteEnd,然后把 sram.valid 改成:

io.sram.valid := state === sWriteEnd || rvalid

问题就解决了,烧到 Flash 里的 GeeOS 可以正常读取并运行。

只是,我感觉这样解决不太优雅 qwq,不知道您有没有更优雅的解决方案。

我睡一觉先,下午开始移植 xv6-riscv!

MaxXSoft commented 3 years ago

收到! 我回头再仿真看一下具体问题.

十分抱歉一直在咕咕咕, 因为近期我需要做某门课程的助教, 要给各位同学写一些文档之类的 (;´д`)ゞ

真的很不好意思!

yukiiiteru commented 3 years ago

没关系啦,写文档要紧。

再说 SoC 现有的问题我都已经解决了,只是感觉实现不太优雅(x)

总之,问题不大~