跳转至

Sequential Building Blocks

Register

使能Reg

val enableReg = Reg(UInt(4.W))
when (enable) {
    enableReg := inVal
}

val enableReg2 = RegEnable(inVal, enable)

val resetEnableReg = RegInit(0.U(4.W))
when (enable) {
    resetEnableReg := inVal
}

val resetEnableReg2 = RegEnable(inVal, 0.U(4.W), enable)
表达式Reg
val risingEdge = din & !RegNext(din)
// RegNext(d)
// t时刻 RegNext(din) 为 t-1时刻 din 值

计数器

image.png

val cntReg = RegInit(0.U(4.W))
cntReg := cntReg + 1.U
数event
val cntEventsReg = RegInit(0.U(4.W))
when(event) {
    cntEventsReg := cntEventsReg + 1.U
}
范围计数 \([0,N]\)
val cntReg = RegInit(0.U(8.W))

cntReg := cntReg + 1.U
when(cntReg === N) {
    cntReg := 0.U
}

cntReg := Mux(cntReg === N, 0.U, cntReg + 1.U)

val cntReg = RegInit(N)

cntReg := cntReg - 1.U
when(cntReg === 0.U) {
    cntReg := N
}
生成函数
def genCounter(n: Int) = {
    val cntReg = RegInit(0.U(8.W))
    cntReg := Mux(cntReg === n.U, 0.U, cntReg + 1.U)
    cntReg  // return value
}

val count10 = genCounter(10)

计时器

原理:数\(f_{clock}/f_{tick}\)个周期,为其它器件提供使能信号(而不是次级时钟信号)

val tickCounterReg = RegInit(0.U(32.W))
val tick = tickCounterReg === (N-1).U

tickCounterReg := tickCounterReg + 1.U
when (tick) {
    tickCounterReg := 0.U
}

val lowFrequCntReg = RegInit(0.U(4.W))
when (tick) {
    lowFrequCntReg := lowFrequCntReg + 1.U
}

书呆子计数器

优化:通过判断符号位确定计数值是否为负,从而判断是否达到计数终点

val MAX = (N-2).S(8.W)
val cntReg = RegInit(MAX)
io.tick := false.B

cntReg := cntReg - 1.S
when(cntReg(7)) {
    cntReg := MAX
    io.tick := ture.B
}

定时器

val cntReg = RegInit(0.U(8.W))
val done = cntReg === 0.U

val next = WireDefault(0.U)
when (load) {
    next := din
} .elsewhen (!done) {
    next := cntReg - 1.U
}
cntReg := next

脉宽控制

每nrCycles CC有din CC高电平

import chisel3.util.unsignedBitLength

def pwm(nrCycles: Int, din: UInt) = {
    val cntReg = RegInit(0.U(unsignedBitLength(nrCycles-1).W))
    cntReg := Mux(cntReg === (nrCycles-1).U, 0.U, cntReg + 1.U)
    din > cntReg
}

val din = 3.U
val dout = pwm(10, din)

  • unsignedBitLength(n) = \(\lfloor\log_2(n)\rfloor+1\)

应用:呼吸灯

val FREQ = 100000000
val MAX = FREQ/1000

val modulationReg = RegInit(0.U(32.W))

val upReg = RegInit(true.B)

when (modulationReg < FREQ.U && upReg) {
    modulationReg := modulationReg + 1.U
}.elsewhen (modulationReg == FREQ.U && upReg) {
    upReg := false.B
}.elsewhen (modulationReg > 0.U && !upReg) {
    modulationReg := modulationReg - 1.U
}.otherwise {
    upReg := true.B
}

val sig = pwm(MAX, modulationReg >> 10)

  • 每周期中高电平占比上下浮动
  • modulationReg / 1000成本高,使用右移实现

移位寄存器

val shiftReg = Reg(UInt(4.W))
shiftReg := shiftReg(2, 0) ## din
val dout = shiftReg(3)
  • 左移,每次最高位被移出,向低位输入

串行转并行

val outReg = RegInit(0.U(4.W))
outReg := serIn ## outReg(3, 1)
val q = outReg
  • 右移,每次向高位输入

并行转串行

val loadReg = RegInit(0.U(4.W))
when (load) {
    loadReg := d
}.otherwise {
    loadReg := 0.U ## loadReg(3, 1)
}
val serOut = loadReg(0)

Memory

内存模型: image.png 实现:使用内置类型SyncReadMem

class Memory() extends Module {
    val io = IO(new Bundle {
        val rdAddr = Input(UInt(10.W))
        val rdData = Output(UInt(8.W))
        val wrAddr = Input(UInt(10.W))
        val wrData = Input(UInt(8.W))
        val wrEna = Input(Bool())
    })

    val mem = SyncReadMem(1024, UInt(8.W))

    io.rdData := mem.read(io.rdAddr)

    when(io.wrEna) {
        mem.write(io.wrAddr, io.wrData)
    }
}

  • SyncReadMem本质:reg [7:0] Memory[0:1023];

Faq

在同一周期对同一地址读写 在同一周期写入并读取同一地址,读出值未定义

Forward memory

读取到当前正在对该地址写入的值 原理图: image.png 实现:

class ForwardingMemory extends Module {
    val io = IO(new Bundle {
        val rdAddr = Input(UInt(10.W))
        val rdData = Output(UInt(8.W))
        val wrAddr = Input(UInt(10.W))
        val wrData = Input(UInt(8.W))
        val wrEna = Input(Bool())
    })

    val mem = SyncReadMem(1024, UInt(8.W))

    val wrDataReg = RegNext(io.wrData)
    val doForwardReg = RegNext(io.wrAddr === io.rdAddr && io.wrEna)

    val memData = mem.read(io.rdAddr)

    when(io.wrEna) {
        mem.write(io.wrAddr, io.wrData)
    }

    io.rdData := Mux(doForwardReg, wrDataReg, memData)
}

  • 将当前周期需要写入的值先缓存在wtDataReg
  • 读取时判断上一个周期是否发生了forward, 若发生了forward,则这个周期直接给出上个周期存入的wtDataReg的值

同步写非同步读Mem

语法:Mem 实现:触发器

  • 缺陷:消耗大,尽量使用同步Mem
  • 针对具体板卡,将相应的Mem作黑盒

设置Memory初始值:loadMemoryFromFile

loadMemoryFromFile(memory, fp)