Skip to content

xinchen13/tinyriscv-modified

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tinyriscv-modified

  • liangkangnan/tinyriscv 进行修改, 添加了指令与外设, 调整核内流水
  • 在 FPGA (ALINX AX7035) 上进行功能验证
  • 在 tsmc 180nm 工艺下进行综合、仿真、后端等工作, 得到 DRC 与 LVS clean 的 gds 文件
  • 主要分支:
    • master: 对 tinyriscv 的修改, 具体内容见下
    • pipeline: 修改内核为4级流水线, 添加了 write back 阶段
    • to: 用于流片的 SoC, 1套总线与外设集成了2颗内核, 通过 chip_sel 片选, 通过 dc 综合得到网表, innovus 与 virtuoso 实现布局布线, DRC 和 LVS 等

GPIO 的引出

原有的 Tinyriscv 中引出了2个 GPIO, 现在需要将其修改为16个:

  • 修改 gpio.v 中的信号宽度, 为后14个引脚添加写入逻辑
  • 在顶层模块 tinyriscv_soc_top.v 中修改 io_in 信号的宽度, 并将gpio模块的所有输入输出引出到 SoC 的 gpio 管脚处

资源的删减

原有的 uart_debug 接收的 packet 大小为131,模块内部存在 132d x 8w 的寄存器组. 现需修改为 uart_debug 接收的 packet 大小为35,模块内部存在 35d x 8w 的寄存器组

  • 修改 uart_debug.vrx_data 的大小; 宏 UART_FIRST_PACKET_LENUART_REMAIN_PACKET_LEN 对应的值; 包大小 fw_file_size 及对应的 rx_data 位数索引也作出相应修改
  • 具体配置参数可以参考修改后的软件 tinyriscv_fw_downloader.py

原有的 ROM 大小为 4096d x 32w,现需修改为 256d x 32w

原有的 RAM 大小为 4096d x 32w,现需修改为 16d x 32w

  • 直接修改 defines.vh 中的定义即可
  • 在编译软件时需要修改链接脚本. 如在 link.lds 中需要修改 MEMORY 关键词中的 ram 大小并调整 stack 大小 __stack_size

PWM 外设的添加

  • rib.v 中给 rib 总线添加一个 slave interface, 按序号分配为 slave_6, 起始地址映射到 0x6000_0000
  • rtl/utils/ 路径下添加 gen_pulse.v 模块, 实现脉冲生成; rtl/perips/ 中添加 pwm.v 外设, 完成寄存器定义, 读写寄存器, 例化 gen_pulse 模块
  • 顶层模块中添加总线子模块接口, 例化 pwm 模块, 引出 io
  • compile_rtl.py 添加对应 .v 文件
  • 编写软件库 pwm.h, 定义寄存器地址和读写宏

I2C 外设的添加

  • I2C protocol 整理: i2c.md
  • rtl/perips/ 路径下添加 i2c.v, 实现了 iic 的读功能, 默认的读取地址是 ALINX AX7035 中的 LM75 温度传感器地址 8'b10010001, 实现的功能是当 iic_en 寄存器被写入 32'h1 时, 将 iic_device_addr 处的温度寄存器值读取到 iic_read_data 的低16位, 读取 LM75 温度寄存器的时序如下:

  • 添加了一个分频系数配置寄存器, 用于配置需要的 scl 时钟
  • rib.v 中给 rib 总线添加一个 slave interface, 按序号分配为 slave_7, 起始地址映射到 0x7000_0000
  • 顶层模块中添加总线子模块接口, 例化 i2c 模块, 引出 io
  • compile_rtl.py 添加对应 .v 文件
  • 编写软件库 i2c.h, 定义寄存器地址和读写宏

拓展指令: Send ID

  • 修改 uart.v 的写寄存器逻辑 (相当于添加了一个状态机器), 当寄存器 uart_ctrl[2] 和 uart_ctrl[0] 都被写入 1 时, 触发一次输出学号, 通过 uart 输出一次学号. 在此基础上该指令的运行过程简化为一次访存: 在地址 0x30000000 处写入一次 0x00000005
  • defines.vh 中添加拓展指令类型 INST_TYPE_EXT; 为 id.vex.v 模块添加对 sID 指令的支持

拓展指令: Read Temperature

  • 指令功能可以等效为一次访存, 但由于 i2c 读取温度读数有延迟, 需要为 i2c 的访问增加握手信号: 在 ex.v 中运行该指令会向外设发送 req 信号, 等待过程中暂停流水线; ack 到达握手成功后重启流水线
  • 由于 tinyriscv 原来的实现中暂停流水线使用的 hold 信号会默认产生气泡, 造成指令丢失, 因此修改了流水线寄存器 pc_reg.v, if_id.vid_ex.v, 当检测到 i2c 暂停流水线后执行模块会给出 stall 信号使流水线寄存器均保持当前的值
  • 修改 i2c.v, 读取完成后将数据右移7位, 满足读取要求
  • 为新指令添加 opcode 与 funct3 的宏, 方便译码

拓展指令: Integrated & Fire

  • 直接在译码和执行模块添加指令对应操作即可 (x_rs1 和 x_31 的比较采用有符号数比较)
  • 由于指令中用到了 uart, 推荐在指令执行前进行软件层面的 uart 状态检查

静态分支预测

  • 添加了静态分支预测, 采用的方法是 BTFN 预测, 即对于向后的跳转预测为跳, 向前的跳转则预测为不跳. 向后的跳转是指跳转的目标地. 依据是在实际的汇编程序中向后分支跳转的情形要多于向前跳转的情形, 譬如常见的 for 循环生成的汇编指令往往使用向后跳转的分支指令; 对于 jal 指令, 提前译码消除了流水线气泡
  • 提前译码模块见 pre_id.v, 分支预测策略见 bpu.v
  • 对于 simple 测试用例如下图所示, 效率提高了 10.66%; 且分支预测逻辑较为简单, 在取指模块中也不会影响关键路经, 目测不会对整体时钟频率造成影响

原用时 57770ns:

添加分支预测后用时 51610ns:

Uart 自适应波特率

  • 为了能够在任意时钟频率下通过 uart_debug 模块实现程序更新, 为 uart 模块设计了自适应波特率模块: 在初始化时上位机向 uart 模块发送一次 0x55 即可完成波特率 115200 下的 uart 分频系数设定, 之后即可通过上位机更新 rom
  • 禁用了 uart_debug 模块的波特率更新, 防止冲突
  • 在不同的时钟频率下软件需要使用 i2c 和 uart 的话只能通过软件(写分频寄存器)来进行配置

参考资料