引言

我们已经了解RISC-V指令集

 

本次课内容:

  • 单周期处理器的结构设计
  • 行为建模的问题
  • 编写可读可维护的RTL代码

单周期处理器的结构设计

单周期处理器

回顾指令周期: 执行一条指令的步骤

  • 取指(fetch): 从PC所指示的内存位置读取一条指令
  • 译码(decode): 按照手册解析指令的操作码(opcode)和操作数(operand)
  • 执行(execute): 按解析出的操作码, 对操作数进行处理
  • 更新PC: 让PC指向下一条指令

 

单周期处理器的思想 - 一个周期内完成上述步骤

  • YEMU就是一个用C语言实现的单周期处理器
    • 每一次while循环相当于一个周期

设计单周期处理器

  • 状态 - 时序逻辑电路
    • R - 寄存器, M - 存储器
  • 状态转移 - 组合逻辑电路
    • 数据通路 - 数据移动和计算所经过的路径及相关部件
      • 例如R, M, 选择器, 加法器, 移位器, 乘法器等
    • 控制信号 - 控制数据在数据通路中如何移动
      • 例如执行加法指令时, 需要将数据送入加法器, 而不是乘法器

 

在类似YEMU的模拟器中也存在数据通路和控制信号的概念

  • 只不过一部分融合到C语言特性里面了
    • 整个inst_cycle()可以看作是数据通路
    • 里面的if, switch-case就充当了控制信号的作用

设计数据通路和控制信号

  1. 分析功能需求
  2. 确定相应的部件, 并根据需求将其连接成完整的数据通路
  3. 确定用于控制数据通路的控制信号
  4. 汇总控制信号, 给出每种状态下的所有控制信号的取值
  5. 得到从状态到控制信号的逻辑表达式, 根据其设计控制信号

 

以上是通用的设计方法, 可以用来设计任意模块, 例如总线, cache

  • 功能需求=指令行为, 就得到了单周期处理器设计流程
    • 指令行为可RTFM得知

但实际情况中需求往往是变化的

  • 产品经理/客户/导师: 加个xxx功能
    • 你的内心: @#%!%@%#$!&%
    • 你的回答: 好的
  • 哪天你心血来潮想跑Linux, 发现需要实现虚存和一大堆CSR
  • 你想跑Debian, 发现又需要添加很多压缩指令

这对大家提出更高的要求

  • 学会将想法设计出来 - 画图纸
  • 学会编写易维护易扩展的RTL代码

很多书籍把图画好, 甚至把代码写好, 直接阅读这类书籍并不能帮助大家锻炼上述技能

  • 自己独立解决问题和直接参考解决方案, 效果是天差地别的
  • 总有一天你需要在没图没参考代码的情况下正确实现需求

从现在开始锻炼

import chisel3._
import chisel3.util._

class YEMU extends Module {
  val io = IO(new Bundle{ val halt = Output(Bool()) })
  val R  = Mem(32, UInt(64.W))
  val PC = RegInit(0.U(64.W))
  val M  = Mem(1024 / 4, UInt(32.W))
  def Rread(idx: UInt) = Mux(idx === 0.U, 0.U(64.W), R(idx))

  val Ibundle = new Bundle {
    val imm11_0 = UInt(12.W)
    val rs1     = UInt( 5.W)
    val funct3  = UInt( 3.W)
    val rd      = UInt( 5.W)
    val opcode  = UInt( 7.W)
  }
  def SignEXT(imm11_0: UInt) = Cat(Fill(52, imm11_0(11)), imm11_0)

  val inst = M(PC(63, 2)).asTypeOf(Ibundle)
  val isAddi = (inst.opcode === "b0010011".U) && (inst.funct3 === "b000".U)
  val isEbreak = inst.asUInt === "x00100073".U
  assert(isAddi || isEbreak, "Invalid instruction 0x%x", inst.asUInt)

  val rs1Val = Rread(Mux(isEbreak, 10.U(5.W), inst.rs1))
  val rs2Val = Rread(Mux(isEbreak, 11.U(5.W), 0.U(5.W)))
  when (isAddi) { R(inst.rd) := rs1Val + SignEXT(inst.imm11_0) }
  when (isEbreak && (rs1Val === 0.U)) { printf("%c", rs2Val(7,0)) }
  io.halt := isEbreak && (rs1Val === 1.U)
  PC := PC + 4.U
}

从现在开始锻炼(2)

  1. 根据上述代码画出架构图
    • 只有addiebreak两条指令的单周期处理器
    • ebreak输出字符的子功能需要仿真环境的支持, 不必在图中体现
  2. 在图中标出数据通路和控制信号
  3. 思考如何添加更多种类的指令

 

如果你之前从来没有了解过单周期处理器设计, 强烈建议不要看书

  • 把单周期处理器设计当做一个未知的新问题来解决
  • 自己思考过, 动手做过, 再回过头来看相关书籍, 对比双方的方案
    • 双方的方案有什么不同?
    • 为什么你这样设计, 而书上那样设计?
    • 究竟谁的方案(在某些情况下)更好?

Verilog的最大问题 - 引入了错误的RTL设计范式

Verilog是一门邪恶的语言 – 中国Chisel之父

大部分业界的RTL工程师都会本能地反对

  • 恰恰他们的大多数缺乏编程语言的专业基础, 因此沟通起来很吃力 😂

 

大家需要承认

  • 业界广泛使用的东西不一定是最好的
    • 比如x86商业上很成功, 但作为指令集有很多问题

 

既然Verilog是一门编程语言, 就应该允许用户从编程角度评价它

Verilog的本质是什么?

是RTL设计语言?

不, Verilog的本质是一门事件建模语言

Originally, Verilog was only intended to describe and allow simulation;
  • 不信的话RTFM, 然后搜索 “synth”
    • 正文里没有任何与 “可综合”相关的描述

 

用Verilog来设计可综合电路是后来的事情

  • 但很多建模用的语法无法对应到RTL电路, 例如延迟# 30
  • 于是大家从Verilog中划出一部分子集, 赋予它们 “可综合”的含义

 

最匪夷所思: “可综合”的内涵和外延都没有标准规范!

无规矩不成方圆

实际情况

  • EDA大厂 - 自己划定边界, 自己定义内涵, 自己设计综合器
    • 但大厂的标准并不公开, 签NDA之后才能获取
  • 开源社区 - 没有统一开放的标准, 很难建立起来
  • RTL工程师 - 永远无法保证换一个综合器后还能否得到正确的网表
    • 全靠没有标准依据的经验和直觉
    • 面向综合器的RTL设计
  • 高校教学 - 没有也无法给学生建立正确的认识
    • 计算机系统的行为是按照官方手册的描述精确发生的
      • 但对RTL设计来说不再成立, 因为不存在官方手册 😂
    • 最后只能摆烂: 业界怎么说, 老师就怎么讲

实际中的可综合定义

  • 结构化建模 - 连线
  • 数据流建模
    • assign - 连线
    • 运算符 - 功能部件
      • *, /%一般不用, 它们综合出的是在一个周期内得到结果的复杂电路, 面积, 功耗, 延迟都很大

尽管大部分综合器都这么约定, 但终究没有统一的标准依据

 

但为了描述时序逻辑元件, 只能用行为建模

  • 结构化建模和数据流建模都无法描述 “状态”
  • 加入行为建模之后, 一切开始失控

行为建模的原罪: 1. 使综合器变得复杂

Verilog本来是一门事件建模语言, 引入always是很自然的

always @(...) begin
  // 基于事件模型的编程
end

但用来做RTL设计, 麻烦就来了

  • 原则上always中可编写任意语法正确的语句, 但如何映射到RTL电路?
    • 完全不支持always是不行的, 否则无法描述时序逻辑元件
    • 如果支持简单但不支持复杂, 边界该如何划分? 如何让用户满意?
    • 结果只能全部支持, best effort地支持 😂
      • 比如=<=混用, 综合器是要支持的
      • 但通常RTL设计者不会这么用

 

这好比一些过时的x86指令已经不用了, 但硬件设计者还要实现它们

行为建模的原罪: 2. 让RTL设计者误入歧途

行为建模的设计过程:

  1. RTL设计者看着电路图的结构
  2. 心里想象电路的行为
  3. 用事件模型的方式重新思考电路行为
  4. 用行为建模方式来描述
  5. 综合器根据行为建模描述推导出相应的电路

 

从第2步就已经开始偏离RTL设计的初衷:

  • 直接描述电路结构不就行了吗 😂
  • 绕这么大一个弯, 白白增加出错的风险
    • 行为想错了, 事件模型思考错了, 行为建模代码写错了, 综合器不支持
      • 别忘了Verilog的可综合子集是没有统一标准的

行为建模的原罪: 2. 让RTL设计者误入歧途(2)

行为建模的设计过程:

  1. RTL设计者看着电路图的结构
  2. 心里想象电路的行为
  3. 用事件模型的方式重新思考电路行为
  4. 用行为建模方式来描述
  5. 综合器根据行为建模描述推导出相应的电路

 

更可怕的是直接从第2步开始

  • 拍脑袋想一个行为, 仿真过了就交给综合器来生成电路
    • 有点HLS(高层次综合)的味道了
    • 然而大多时候只会得到PPA质量很低的电路

本质区别: 用电路实现需求 vs. 用事件模型实现需求

行为建模的原罪: 3. 把老师绕晕, 让学生从课堂开始误入歧途

你可以很容易在课本等学习资料中找到类似的描述:

在Verilog中,各语句是并发执行的,模块中所有的assign语句、always语句块和实例化语句,其执行顺序不分
先后。而if语句是顺序执行的语句,其执行过程中必须先判断if后的条件,如果满足条件则执行if后的语句,否则
执行else后的语句。Verilog语法规定,顺序执行的语句必须包含中always块中,always块中的语句按照它们
在代码中出现的顺序执行。

如果你觉得它是对的(确实是对的), 你很有可能已经开始被误导了:

  • 在RTL设计中, 什么叫 “执行”?
  • 是谁在执行Verilog的语句? 是电路,综合器,还是其他的?
  • if的条件满足, 就不执行else后的语句, 这里的 “不执行”又是什么意思? 和描述电路有什么联系?
  • 并发执行, 又有顺序执行, 还有任何一个变量发生变化就立即执行, 以及在任何情况下都执行, 它们都是如何在电路中体现的?

估计大部分教Verilog的老师都无法对这些问题作出明确的回答 😂

现实中的解决方案: 妥协

  • 仿真建模: Verilog全集
  • RTL设计: Verilog可综合子集
    • 没有统一标准
  • 企业中的RTL设计: Verilog可综合子集的子集
    • 也没有统一标准

 

有的企业限制always中的编码风格, 有的企业甚至不允许写always

  • 例如蜂鸟E203的处理器代码中没有任何always语句

 

大部分RTL工程师认为Verilog的这些问题在实际中不是问题, 是因为他们无法全面地评价一门语言

  • 试试写个Verilog仿真器/综合器, 就体会到国产EDA发展的难处(之一)了

远离行为建模, 回归RTL设计的本质

当我们讨论PPA的时候, 针对的是电路, 而不是模型

  • 行为建模会使得电路的PPA变得不直观

 

RTL设计 = 直接描述电路结构 = 实例化 + 连线

  • 实例化 - 在电路板上放一个元件/模块, 可以是一个门电路, 或者是由门电路组成的模块
  • 连线 - 用导线将元件/模块的引脚正确地连起来

 

用数据流建模和结构化建模就可以实现

为了支持触发器/存储器, 可以写一个参数化的module

  • 内部用always实现(简单可控), 但通过结构化建模屏蔽实现细节
  • 例子 - reg-template.v

RTL设计选讲

指令译码器

负责从指令生成控制信号

  • 本质上是一个查找表
  • 表的规模会影响实现复杂度
    • 5条指令, 2个控制信号, 很简单!
    • 50条指令, 20个控制信号, 怎么办?
      • 你想实现一个部件/一种优化方法, 发现要加几个控制信号
      • 你还想加压缩指令

 

都是同一个设计, 但怎么写代码又是另一回事

道理我都懂, 但…

assign SB = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]);
assign SH = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]);
assign SWR = (IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign SWL = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]);
assign SRL = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & ~IR[0]);
assign SRA = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]);
assign Ext0 = (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign Wtrs = ((Zero == 1'b0) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0])) | ((Zero == 1'b1) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]));
assign LWLR1 = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]);
assign LWLR2 = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]);
assign LWLR3 = (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]);
assign LHU = (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]);
assign LH = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]);
assign LBU = (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]);
assign LB = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]);
assign ZeroJg = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & IR[16]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & ~IR[16]) | (IR[31:26] == 6'b000111);
assign RegDst = (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign ALUSrc = (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]);
assign PCSrc = ((~(Zero == 1)) & (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26])) | ((Zero == 1) & (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26])) | ((Address == 32'h00000000) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & IR[16])) | (((Address == 32'h00000001) | (Read1 == 32'h00000000)) & (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26])) | ((Address == 32'h00000001) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & ~IR[16])) | (Address == 32'h0 & ~(Read1 == 32'h0) & IR[31:26] == 6'b000111);
//  assign RegWrite = (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & ~IR[1] & IR[0]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | ((Zero == 1'b0) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0])) | ((Zero == 1'b1) & (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0])) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign Jmp = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]);
// assign MemWrite = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]); 
// assign MemRead = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]); 
assign MemtoReg = (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]);
assign SLL = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & ~IR[1] & ~IR[0]);
assign JAL1 = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & ~IR[1] & IR[0]);
assign JAL2 = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]);
assign JR = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & ~IR[1] & IR[0]);
assign Write_strb[3] = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (ALUResult[1] & ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (ALUResult[1] & ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign Write_strb[2] = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (ALUResult[1] & ~ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (ALUResult[1] & ~ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]);
assign Write_strb[1] = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~ALUResult[1] & ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (~ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~ALUResult[1] & ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]);
assign Write_strb[0] = (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~ALUResult[1] & ~ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (~ALUResult[1] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~ALUResult[1] & ~ALUResult[0] & IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]);
assign ALUctr[3] = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]);
assign ALUctr[2] = (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & IR[16]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & ~IR[16]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31:26] == 6'b000111);
assign ALUctr[1] = (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & IR[16]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & ~IR[16]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & ~IR[27] & IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31:26] == 6'b000111);
assign ALUctr[0] = (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & IR[29] & ~IR[28] & IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & IR[16]) | (~IR[31] & ~IR[30] & ~IR[29] & IR[28] & IR[27] & ~IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & IR[26] & ~IR[20] & ~IR[19] & ~IR[18] & ~IR[17] & ~IR[16]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & ~IR[27] & IR[26]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & ~IR[5] & ~IR[4] & ~IR[3] & IR[2] & ~IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & IR[3] & ~IR[2] & IR[1] & IR[0]) | (~IR[31] & ~IR[30] & ~IR[29] & ~IR[28] & ~IR[27] & ~IR[26] & IR[5] & ~IR[4] & ~IR[3] & IR[2] & IR[1] & ~IR[0]) | (~IR[31] & ~IR[30] & IR[29] & IR[28] & IR[27] & ~IR[26]) | (IR[31:26] == 6'b000111);

道理我都懂, 但……

6'b001010://slti 
  begin 
    RegDst <= 0;
    ALUSrc <= 1;
    Branch <= 0;
    RegWrite <= 0;
    RegRead <= 1;
    MemtoReg <= 0;
    //MemRead <= 0; 
    //MemWrite <= 0; 
    Write_strb <= 4'd0;
    ALUcon_in <= 3'b010;
    Jump <= 0;
    JumpReg <= 0;
    JAL <= 0;
    LUI <= 0;
    SL <= 0;
    s <= 0;
    Sign <= 1;
    BEQ <= 0;
    BGEZ <= 0;
    BGTZ <= 0;
    BLEZ <= 0;
    BLTZ <= 0;
    Logic <= 0;
    Left <= 0;
    JALR <= 0;
    Align <= 0;
    Part_data <= 32'd0;
  end 
6'b001011://sltiu  
  begin 
    RegDst <= 0;
    ALUSrc <= 1;
    Branch <= 0;
    RegWrite <= 0;
    RegRead <= 1;
    MemtoReg <= 0;
    //MemRead <= 0; 
    //MemWrite <= 0; 
    Write_strb <= 4'd0;
    ALUcon_in <= 3'b011;
    Jump <= 0;
    JumpReg <= 0;
    JAL <= 0;
    LUI <= 0;
    SL <= 0;
    s <= 0;
    Sign <= 0;
    BEQ <= 0;
    BGEZ <= 0;
    BGTZ <= 0;
    BLEZ <= 0;
    BLTZ <= 0;
    Logic <= 0;
    Left <= 0;
    JALR <= 0;
    Align <= 0;
    Part_data <= 32'd0;
  end 

类似的代码有1500行

编写不可维护的代码 = 给将来的自己挖坑

如何拥抱变化

  • 加一条指令
    • 逻辑表达式 - 给相关的assign各加一个最小项
    • case语句 - 加一个case
  • 加一个控制信号
    • 逻辑表达式 - 加一条很长的assign
    • case语句 - 每个case各加一行

 

灵魂拷问

  • 你愿意仔细看看你的代码是否正确吗?
    • 先仿真再说, 万一跑对了呢
  • 如果这是别人的代码, 你愿意仔细看看他的代码是否正确吗?

回顾 - 编写可读可维护代码

正确的代码 != 好代码

  • 好代码更大概率是正确的

好代码的两条重要准则

  • 不言自明 - 仅看代码就能明白是做什么的(specification)
  • 不言自证 - 仅看代码就能验证实现是对的(verification)

 

使用正确的编程模式写出好代码

  • 防御性编程 - 通过assert检查非预期行为
  • 减少代码中的隐含依赖 - 使得 “打破依赖”不会发生
    • 头文件 + 源文件
  • 编写可复用的代码 - 不要Copy-Paste
  • 使用合适的语言特性 - 把细节交给语言规范和编译器

写代码 != “写”代码

分析: 译码器的本质是查找表

 

需求: 如何 “低熵”地实现一张表?

  • 我们只需要维护好这张表
    • 除了表本身, 其他没有必要的信息尽量不出现
      • 上述case方案中, 控制信号名称是冗余的, 但如果有错, 就是bug
  • 让工具/综合器将它转换成逻辑表达式
    • 上述逻辑表达式方案中, 开发者成了工具人

 

我们需要寻找合适的语言特性来实现一张表

Verilog解决方案

太难了 😂

我们提供一个键值选择模板muxkey-template.v, 可以像真值表一样查询

module mux41(a,s,y);
  input  [3:0] a;
  input  [1:0] s;
  output y;

  // 通过MuxKeyWithDefault实现如下always代码
  // always @(*) begin
  //  case (s)
  //    2'b00: y = a[0];
  //    2'b01: y = a[1];
  //    2'b10: y = a[2];
  //    2'b11: y = a[3];
  //    default: y = 1'b0;
  //  endcase
  // end
  MuxKeyWithDefault #(4, 2, 1) i0 (y, s, 1'b0, {
    2'b00, a[0],
    2'b01, a[1],
    2'b10, a[2],
    2'b11, a[3]
  });
endmodule

Verilog解决方案(2)

但还是有很多不完善的地方

  • Verilog的类型系统很弱, 所有数据都是比特串
    • 表那么大, 哪里多了/少了1bit, 整个选择结果就错了
    • Verilog做不好/做不了的事情, 都丢给程序员了
  • 这个模板不支持模糊匹配
    • 只能先进行第一级译码, 得出指令类型
    • 再进行第二级译码得到控制信号
  • casexcasez在建模语义上支持模糊匹配
    • 但在可综合语义上是否支持, 就不好说了
    • 而且综合出来什么样的电路, 也很难想明白
      • 如果这里是关键路径, 怎么办?

借助外部工具

案例: 第一届龙芯杯(2017), 南京大学一队通过excel和python脚本, 实现译码器的敏捷开发和低熵维护

  • 但python生成的是Verilog的case语句, 行为建模的问题同样存在

Chisel解决方案 - ListLookup API

借助scala的强大类型系统, 把表格嵌入到Chisel代码中

  • 编译出级联Mux, 需要依赖综合器优化(级联树的平衡, 并行选择)
object RV64IInstr extends HasInstrType {
  def ADDIW   = BitPat("b???????_?????_?????_000_?????_0011011")
  def SLLIW   = BitPat("b0000000_?????_?????_001_?????_0011011")
  def SRLIW   = BitPat("b0000000_?????_?????_101_?????_0011011")
  def SRAIW   = BitPat("b0100000_?????_?????_101_?????_0011011")
  def SLLW    = BitPat("b0000000_?????_?????_001_?????_0111011")
  def SRLW    = BitPat("b0000000_?????_?????_101_?????_0111011")
  def SRAW    = BitPat("b0100000_?????_?????_101_?????_0111011")
  def ADDW    = BitPat("b0000000_?????_?????_000_?????_0111011")
  def SUBW    = BitPat("b0100000_?????_?????_000_?????_0111011")
  def LWU     = BitPat("b???????_?????_?????_110_?????_0000011")
  def LD      = BitPat("b???????_?????_?????_011_?????_0000011")
  def SD      = BitPat("b???????_?????_?????_011_?????_0100011")
  val table = Array(
    ADDIW          -> List(InstrI, FuType.alu, ALUOpType.addw),
    SLLIW          -> List(InstrI, FuType.alu, ALUOpType.sllw),
    SRLIW          -> List(InstrI, FuType.alu, ALUOpType.srlw),
    SRAIW          -> List(InstrI, FuType.alu, ALUOpType.sraw),
    SLLW           -> List(InstrR, FuType.alu, ALUOpType.sllw),
    SRLW           -> List(InstrR, FuType.alu, ALUOpType.srlw),
    SRAW           -> List(InstrR, FuType.alu, ALUOpType.sraw),
    ADDW           -> List(InstrR, FuType.alu, ALUOpType.addw),
    SUBW           -> List(InstrR, FuType.alu, ALUOpType.subw),
    LWU            -> List(InstrI, FuType.lsu, LSUOpType.lwu),
    LD             -> List(InstrI, FuType.lsu, LSUOpType.ld ),
    SD             -> List(InstrS, FuType.lsu, LSUOpType.sd)
  )
}

Chisel解决方案 - Decoder API

用法和ListLookup几乎相同, 但有额外好处

  • 通过算法生成逻辑表达式, 相当于工具来进行卡诺图化简
    • QMC(Quine-McCluskey)算法 - 得到精确最优解, 但算法时间复杂度较高, 适用于规模不大的表格
    • espresso算法 - 得到近似最优解, 算法时间复杂度较低, 适用于规模较大的表格
  • 表项中支持无关项(Don’t care)
    • 据说在Vivado上能提升10%的主频

 

核心思想: 借助工具生成精准的电路描述, 将不确定的综合器行为前移到设计阶段

译码器实现方案小结

方案 容易维护 综合精确
逻辑表达式 ✖️✖️ ✔️✔️
case语句 ✖️ ✖️
excel + python ✔️✔️ ✖️
Chisel ListLookup ✔️✔️ ✔️
Chisel Decoder ✔️✔️ ✔️✔️

总结

重新审视RTL设计

  • 结构设计 - 仅仅看图写代码是不够的
    • 接受解决未知问题的训练
    • 通过试错和思考理解细节

 

  • 回归RTL设计的本质 - 远离行为建模
    • RTL设计 = 直接描述电路结构 = 实例化 + 连线

 

  • 编写可读可维护的RTL代码 - 低熵实现
    • 选一个表达能力强的语言
    • 借助工具弥补语言的缺陷