我们有基于RISC-V设计并长期维护的SoC,
四级流水线RISC-V64IMA XXXXXXXXXXXXXXXXXX/RV-AT/PVS464
(在建)基于SIMD扩展的RISC-V GPU XXXXXXXXXXXXXXXXXX/RPG-7/HGA101_GPU/
前言与愿景
KC-LS1u这一74处理器实现是本论坛七年之前的重磅级作品,一定程度上来说算是国内74逻辑门自制CPU的巅峰之作,但非常可惜的一点是,原作者并未计划实现该处理器的FPGA实现。作为切换到了基于HDL的设计并从中极大受益的爱好者,在此本人建议各位数字电路爱好者自学基础的硬件描述语言,这可以帮助各位脱离繁琐的基于逻辑门和触发器的设计风格,并极大的加速各位的设计和验证过程。
本工程主要面向74CPU爱好者,旨在作为一个简单完整,基于Verilog HDL并包含了绝大多数工程中常用HDL语法的CPU及SoC实现的教学性工程,起到抛砖引玉帮助更多的爱好者用上用好现代化的FPGA/数字集成电路设计验证流程的作用。本文也将着重于在接下来陆续更新该设计的解析,并将基于各模块相关代码穿插对应Verilog HDL的语法点讲解。
鉴于本工程的的SoC框架将具备良好的可扩展性和高度的灵活性,如果可能,该框架将作为一个相对完善,开箱即用的SoC框架,供各位74爱好者快速迁移设计到一个相对现代的微处理器框架中,体验数字前端的高效设计流程。
本处理器特性
本处理器实现实现了完整的74-LS1u基本指令集
本处理器实现扩展了C/D两个GPR
本处理器实现了简单的中断、现场保存、返回机制
本处理器具备固定64K页面的MMU及32位物理地址扩展
本处理器具备可配置的直接映射式L1指令缓存/全相连L1指令-数据一体缓存/无缓存模式
本处理器具备一个基于Wishbone简化的同步SRAM总线接口,可以稍加修改挂接XXXXXXXXXXXXX上的诸多软核实现
从龙少原处理器设计说起
龙少原处理器指令可以大致拆分为如下的部分:
[15:11] | [10:8] | [7:0] |
处理器功能选择(数据源选择) | 目标寄存器选择/跳转源选择 | 立即数/ALU功能选择 |
对于发布了硬件的KC-LS1U处理器而言,其译码真值表如下:
功能选择译码值 | 功能 | 备注 |
0x01 | 跳转大类 | 无需写总线 |
0x02 | ALU功能大类 | 244×1 (U9) |
0x04 | 加载内存 | 244×1 (?) |
0x06 | 加载立即数 | 244×1 (U16) |
0x07 | A << 1; | 244×1(U5) |
0x08 | {A[6:0],B[7]} | U5 |
0x09 | A>>1 | 244×1(U6) |
0x0a | {A[7],A[7:1]} | U6 |
0x0b | B<<1 | 244×1(U7) |
0x0c | B>>1 | 244×1(U8) |
0x0d | {B[7],B[7:1]} | U8 |
0x0e | {A[0],B[7:1]} | U8 |
本处理器(KC-LS1u+)扩展功能:
功能选择译码值 | 功能 | 备注 |
0x05 | 读入C寄存器 | GPR,无自动保存 |
0x07 | 读入D寄存器 | GPR,无自动保存 |
0x03 | 读取XREG | 现场保存寄存器/核心配置寄存器 |
0x08 | 将A/B寄存器写入XREG |
目标寄存器选择译码:
码值 | 目标寄存器 | 跳转模式 |
0x0 | N/A (C) | N/A(中断返回) |
0x1 | A | A[7]==0 |
0x2 | B | B[7]==0 |
0x3 | A0 | A!=B |
0x4 | A1 | ALU不进位 |
0x5 | A2 | 无条件跳转 |
0x6 | N/A (D) | N/A |
0x7 | 内存写 | N/A |
ALU选择为ALU功能选择/运算-逻辑模式切换/进位输入端,直通指令接口中[7:2]位。
龙少处理器中存在的怪东西
谜之244
大多数244的存在可以解释得通(作为数据源选择器的组成部分),但是U20/U21这两个直通的244,不知道是拿来干嘛的
2.“阻塞”寄存器堆
将138的输出直接怼进273时钟输入端,导致不能连续写入同一个寄存器
3.兼做跳转寄存器的内存地址寄存器
LS1U没法高效存取内存有一大半的锅得这个设计背,跳转是你,存取还是你,还只写不读,我是A0/A1/A2,我TM很心累
本处理器的层次
处理器核KC_LS1u_plus
名称 | 位宽 | 作用 |
clk | 1 | 时钟输入 |
rst | 1 | 高有效复位 |
IVEC_addr | [23:0] | 中断向量地址 |
INT | 1 | 中断触发 |
IN_ISP | 1 | 中断服务指示输出 |
iaddr | [23:0] | 指令地址 |
iaddr_next | [23:0] | 下一条指令地址 |
instr | [15:0] | 指令输入 |
daddr | [23:0] | 数据地址 |
dread | 1 | 读数据指示 |
dwrite | 1 | 写数据指示 |
WAIT | 1 | 处理器等待输入 |
ddata_i | [7:0] | 处理器数据输入 |
ddata_o | [7:0] | 处理器数据输出 |
-------------WIP----------------------
本处理器设计中使用的部分Verilog语法案例
建议的多路器写法
//KC_LS1u_plus.v reg [7:0]XREGr;//扩展寄存器组读取端口 always@(*) begin case(xreg_addr)//使用case的可读性相对assign好得多 3'h0:XREGr=RET0; 3'h1:XREGr=RET1; 3'h2:XREGr=RET1; 3'h3:XREGr=RTA0; 3'h4:XREGr=RTA1; 3'h5:XREGr=RTA2; default :XREGr=8'hxx;//default状态下为X而非定值,更节约逻辑 endcase //(综合器放弃default情况,传入非法值可能输出任意输入而非专门产生给定值情况) end
异步握手 WIP
WIP
WIP
defparam或#(.所改参数(传入参数)) 配合 #(parameter x=123456)灵活变更工程参数&使用generate for完成批量例化
//l1.v /*****该模块为Cache内存的顶层模块,展示了使用defparam改变模块内部参数的语法******/ defparam l1_ram.datawidth=DATA_WIDTH; defparam l1_ram.cache_depth=CACHE_DEPTH; cachemem l1_ram ( .raddr (read_addr), .waddr (write_addr), .di (write_data), .we (we), .bsel (byte_sel), .dato (cache_read), .clk (clk) ); //cachemem.v module cachemem # ( parameter datawidth=64, cache_depth=2048 ) ( input cwait, input [addr_wid-1:0]raddr, input [addr_wid-1:0]waddr, input [datawidth-1:0]di, input we, input [cswidth-1:0]bsel, output [datawidth-1:0]dato, input rclk, input wclk ); localparam cswidth=datawidth/8; //localparam是不可从外部改变的局部参数 localparam addr_wid =$clog2(cache_depth); localparam addr_lsb=$clog2(cswidth); genvar i; generate //generate批量例化的时候只能用#(.所改参数(传入参数))的语法 for(i=0;i<cswidth;i=i+1) begin : cacheblk cachemem8 #(.memdepth(cache_depth)) cacheunit //注意该行,更建议使用该语法 ( .cwait(cwait), .rclk(rclk), .wclk(wclk), .raddr(raddr),//[addr_wid+addr_lsb-1:addr_lsb] .waddr(waddr),//[addr_wid+addr_lsb-1:addr_lsb] .di(di[7+8*i:0+8*i]), .dato(dato[7+8*i:0+8*i]), .we(we&bsel[i]) ); end endgenerate endmodule module cachemem8 //一块8b位宽同步SRAM #( parameter memdepth = 1024, memaddr=$clog2(memdepth) ) ( input cwait, input rclk, input wclk, input [memaddr-1:0]raddr, input [memaddr-1:0]waddr, input [7:0]di, output reg[7:0]dato, input we ); reg [7:0]memcell[memdepth-1:0]; //begin //: GENERIC_SSRAM always @(posedge rclk) begin if(cwait)dato<=dato; else dato<=memcell[raddr]; end always @(posedge wclk) if(we)memcell[waddr]<=di; endmodule
术语表
术语/信号缩写 | 全称 | 译名 |
ISP | Interrupt Service Program | 中断服务程序 |
Entry/Line | Cache Entry/ Cache Line | 高速缓存表项 |
Tag | Cache Tag | 高速缓存标签 |
截至2021-02-17的项目进展
KC-LS1u/KC-LS1u+的处理器RTL实现完成,并部分指令完成验证
L1 Cache模块设计完成,并通过简单的测试
完成了MMU,在处理器中扩展简单的中断/异常/线程处理机制
最小化的SoC框架设计完成(FSB8片外总线/中断和时钟控制器/Systick中断计时器/Syscall系统调用控制器/2KB Scratchpad RAM)
最小化的SoC非常轻量,可以塞入GW1N-1芯片中
完成了部分外设(SPI/UART/GPIO/Timer)
完成了SoC部分外设的简单验证
未来计划
完成对处理器的完整验证,有条件则使用UVM验证框架
解耦CPU/总线时钟,尝试实现核心倍频/睿频机制
基于现有的CPU访存状态机扩展DMA功能
基于安路AL3S10NG88芯片实现对原版设计部分兼容的完整SoC设计,并绘制开发板
对称多处理机(俺寻思没有D-Cache约等于几乎没有多核一致性问题,那么SMP应该相对好做)
基于本项目仍在进行并且将长期更新的考量,核心和工程选择暂时仅在Github发布,在设计/验证结束前暂不上传至科创论坛。
项目地址:XXXXXXXXXXXXXXXXXX/RPG-7/SoC-KC-LS1u
[修改于 3年8个月前 - 2021/04/09 03:26:10]
有考虑过基于RISC-V指令集的吗?期待!
我们有基于RISC-V设计并长期维护的SoC,
四级流水线RISC-V64IMA XXXXXXXXXXXXXXXXXX/RV-AT/PVS464
(在建)基于SIMD扩展的RISC-V GPU XXXXXXXXXXXXXXXXXX/RPG-7/HGA101_GPU/
Debug手记:处理器中断响应Bug
Bug仿真波形如下:
图中观察可以发现,当中断信号到来时,处理器中中断响应组件并未按照期待完成当前指令后将ISP地址填充至PC
可能的bug来源:
1.下一条指令选择器中对等待状态的处理,我们可以考虑让中断来时强行跳转至ISP,但这样不符合我们对处理器行为的期待
2.中断握手流程未考虑到等待状态
查询代码可发现处理器中断处理流程如下:
当复位时清除ISP标志int_service
当中断来临(INT=1且ISP标志=0),保存现场(PC+1/A0A1A2),ISP标志置位
将PC置为ISP地址(在PC_NEXT处实现)
如果返回,清除中断标志位并从RET/RTA寄存器中弹出PC+1/A0A1A2(未在此展示)
看到这里,这里修改代码,通过在进入ISP前检查WAIT状态,仿真故障不再复现
可见,在WAIT状态结束后,正常跳转进入ISP
有奖竞猜:这段中断处理流程的描述中,还有一个Bug,欢迎在评论区中竞猜指出,答对有奖
昨日进展:验证了cache的自动填充,Cache/总线状态机基本正确
可见处理器被置等待外设状态,状态机从总线RAM中读入数据装填入Cache RAM
Cache Entry装填完成,放开外设忙状态,CPU开始跑
当跳转后发生缺页(line_miss=1),重新装填Cache RAM,同时替换Cache Tag(refill_tag)
综上,该Cache工作基本正确,在单次读写上似乎还有Bug
时段 | 个数 |
---|---|
{{f.startingTime}}点 - {{f.endTime}}点 | {{f.fileCount}} |
200字以内,仅用于支线交流,主线讨论请采用回复功能。