有考虑过基于RISC-V指令集的吗?期待!
前言与愿景
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年9个月前 - 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,欢迎在评论区中竞猜指出,答对有奖
麻烦楼主了,,, 以前的烂摊子有劳楼主了收拾了,表示支持,前阵子SMT32片子用得舒服就把自产计划搁置了,刚用得舒服现在居然涨价
昨日进展:验证了cache的自动填充,Cache/总线状态机基本正确
可见处理器被置等待外设状态,状态机从总线RAM中读入数据装填入Cache RAM
Cache Entry装填完成,放开外设忙状态,CPU开始跑
当跳转后发生缺页(line_miss=1),重新装填Cache RAM,同时替换Cache Tag(refill_tag)
综上,该Cache工作基本正确,在单次读写上似乎还有Bug
200字以内,仅用于支线交流,主线讨论请采用回复功能。