个人学习作品,适合初学者食用。
一个极其简易的RV32I_Zicsr_Sdext指令集单核MCU,所有用户级与特权级指令支持,仅运行在机器模式或外部调试模式。
- RTL包含了所有Verilog代码,顶层文件在这里
- firmware_lib包含了本MCU的固件库,有C版本和Rust版本
rust_release/debug是rust程序默认的构建输出位置- demo是C语言的示例程序代码,同时也是C程序构建脚本默认寻找主程序源文件的位置
- ACT4包含了架构认证测试相关的内容(最新的ACT4框架)
处理器内核消耗约2061个LUT4(参考)
PLL等IP核适用于LCMXO2-4000HC-4MG132CFPGA器件,IP核仅包含ipx和lpc文件,请使用开发工具重新生成verilog文件。外设基本是围绕该核心板设计的,但是处理器内核可以很方便移植到其他FPGA上。
- 三级流水线:取指、译码、执行
- FENCE指令空实现(等效NOP)
- ECALL指令跳转异常
- WFI指令暂停流水线
- 精简的CSR寄存器(详见RTL代码)
- 支持外部调试(最小 RISC-V 调试规范)
- 调试模式时始终禁用中断
- 可配置为RV32E内核
- 支持标准中断:定时器中断、软件中断、外部中断
- 所有外部中断,由自定义的外部中断控制器进行重定向,完成中断向量跳转
- 不支持中断嵌套
- 遵循最小 RISC-V 调试规范
- Jtag调试传输模块
- 支持查询与控制内核运行状态(停止/恢复)
- 在内核停止时可执行访问寄存器抽象命令,可访问所有允许软件访问的寄存器
- 允许复位MCU上的所有非调试逻辑
时钟频率可根据实际情况修改。
- 内部振荡器频率2.15MHz,作为时钟监视器的独立时钟源
- 时钟监视器监控PLL状态,若出现脱锁将自动重置PLL并发出全局
RST信号 - 时钟监视器可设置上电等待时间、PLL锁定等待时间,拥有一个外部重置源,通过按钮可重置系统
- PLL输入时钟频率12MHz
- 核心、高速总线、WISHBONE总线位于同一时钟域,基准频率12MHz
- 机器计时器固定频率1MHz
- UART采样频率153.846KHz,过采样率为8时波特率19200误差0.16%
- 低速总线频率100KHz
- 支持最多32条电平敏感型外部中断
- 无优先级配置,但中断号越低越优先,0号外部中断先于其他外部中断触发
- 不直接处理中断清零,通过读写外设寄存器清零
- 无跨时钟域时序惩罚
- 硬件不处理非原子读取一致性问题,通过软件处理
- 自举控制器
- 外部中断控制器
- 64bit机器计时器
- UART通信接口
- 软件中断寄存器
- 复用GPIO
- 开关(3)与按钮(4)
- 8位LED灯
- 硬件解码7段LED数码管X2
该总线的外设为EFB硬核
- 左PLL动态配置 TODO
- 右PLL动态配置 TODO
- 1号I2C接口
- 2号I2C接口
- SPI接口
- 16bit定时器/计数器
- 程序存储Flash
XT_HB是内核访问外部数据必经的通道,承担内存地址映射、互联其他总线的职责。
- 主设备通过直接访存接口来访问总线
- 主设备还可以通过响应接口查询响应信息:仲裁准予、读写停顿请求等
- 从设备也通过接口连接总线
- 读与写独立,如果两个主设备读写不冲突,可以同时进行,读写启用信号本身就是总线请求信号
- 如果对同一个从设备同时读写,从设备自行决定读取旧数据还是通过旁路读取写入数据。(通常是读取旧数据)
仲裁器使用轮询仲裁器,读写仲裁是独立的。
一个完整访问地址由两部分组成:高位的设备识别符,低位的偏移量。识别符占用位数是参数配置的,决定了最多能区分多少设备。偏移量占用位数构成固定大小寻址空间。
每个设备至少占用一个识别符,可以连续占用多个,占用的第一个识别符是基准识别符。基准识别符与全0偏移量拼接为设备的基地址,基地址必须在识别符上对齐。
- 如果设备只占用一个识别符,可以直接使用偏移量作为访问地址。
- 如果设备连续占用多个识别符,偏移量寻址空间不足,使用完整地址的识别符部分减去基准识别符,再与偏移量拼接得到访问地址。
完整地址示例:
| 设备识别符 | 偏移量 | 说明 |
|---|---|---|
| 001 | 0000000000000 | 基地址/访问地址 |
| 101 | 0000000000000 | 基地址/访问地址 |
| 101 | 0000011100001 | 访问地址 |
设备也可以是其他总线,XT_HB只与各个抽象的设备进行数据交换,其他总线内部自行控制从设备。
为避免浪费过多的XT_HB识别符,将大多数高速外设挂载到32bit对齐总线上。对齐总线本身只占用一个识别符,上面的外设寄存器地址都是字对齐(32bit对齐)的。在32bit对齐总线内部,也使用识别符加偏移量的方式来分配地址,但每个设备只能占用一个识别符。
对齐总线对时序要求严格,在上面的设备,必须能够在一个周期完成写入,在读地址有效的下一个周期必须输出数据。
总线提供了一个访问等待机制,从设备激活finish信号来告知此次访问已完成,如果从设备支持无等待访问,可以把finish硬连线到激活状态。
当主设备未被仲裁准予,或者访问未完成时,总线向主设备激活读写停顿请求stall,读写停顿也是独立的。当主设备收到finish后,如果不再对总线进行访问,它必须立刻响应此次finish,并撤销对应的读写请求,避免重复读写导致的副效应。从设备的读写副效应,只应该在激活finish信号时发生一次。
CPU内核在EX阶段访存需要流水线停顿,如果访问其他总线,则停顿周期会更长,XT_HB接收其他总线的finish信号。
XT_HB通过CDC_MCP_Formulation模块跨时钟域,与XT_LB交换数据。
同样使用识别符+偏移量的地址分配策略,每个从设备平分地址空间,从设备只进行部分地址解码。
使用最简单的设计,半双工通信。若读写同时发生,先读后写。总线只输出部分地址。从机的读数据输出始终使用组合逻辑读取,不经过输出寄存器。总线内部根据识别符产生写片选独热码,分别路由到不同的从机,固定使用一个周期完成写入。
自举加载流程:
- 初始化加载ROM中的bootstrap程序
- 把FLASH中的数据逐个拷贝到指令存储器RAM中
- 指令地址跳转到0
- 触发脉冲将MUX切换到指令存储器RAM
SiFive riscv64-unknown-elf-gcc-8.3.0已停止更新但程序尺寸最小,自举程序由此编译
riscv-none-elf-gcc-xpack标准最新,默认情况下推荐使用
rust需要安装riscv32i-unknown-none-elf编译目标,同时为了在rust中使用异常处理函数,请选择nightly版本。
1页 == 16字节
1 page == 16 byte
最大页数由Flash决定,当前器件为767
综合前请使用自举程序和字符串文件重新生成rom_boot与rom_strIP核
emoji提示💾下载 🛫启动 🔛开始 ✅完成 ❌错误
- 将下载开关(拨码开关1)拨动到高电平位置
- 连接电脑并打开串口程序
- 选择下载操作;操作提示:💾:0x56,🛫:0xF1
- 发送程序的页数(共两字节,先高位);操作提示:Len=
- 确认开始下载;操作提示:🔛:0x78
- 发送程序二进制文件(确保已经补零对齐到页长度)
- 确认下载完成;操作提示:✅:0x57
- 启动程序;操作提示:💾:0x56,🛫:0xF1
引脚图来源于该核心板的参考资料
每个GPIO可以从四个复用功能中,选中某一个复用功能进行复用,允许多个引脚复用相同的功能
当多个端口使用相同的输入功能时,得到的结果为它们的逻辑或。当多个端口使用相同的输出功能时,复用功能将在多个端口输出
功能前有"~"的表示低电平有效
- 其他异常处理函数
- 修改XT_HB的时序,适配单周期原子读写的外设
-
用一个小型控制器来加载Flash,而不是ROM,ROM太占用资源了已通过高度优化自举程序解决 -
系统外设整合到一个模块中 - 为对齐的寄存器划分单独的地址空间,更加节省资源
- DMA控制器
- 尝试实现RV32C混合执行,把RV32C解码成RV32I指令
- Rust重写外设固件库
- 平台级中断控制器PLIC(考虑到资源消耗,可能性不大)
- 调试器支持
-
FreeRTOS支持不再考虑FreeRTOS,未来将支持Rust异步运行时(Embassy框架) - 跑分(低端设备没必要吧)
- 这综合器真是死了棍木了,参数无默认值有警告,写了= '{default: 0}默认值又变成类型不匹配警告(实际上是匹配的),直接改成字面量传入又没有警告了
- 害,这个核心板的下载程序有问题,EBR和UFM都无法初始化,JTAG被绑定到中介下载芯片上了,也无法使用逻辑分析仪等工具,唯一的下载方式就是使用模拟的USB大容量设备,并且只支持jed文件。
