Skip to content

Commit

Permalink
Add the CsrManager for the Post-Processing SIMD datapath (#6)
Browse files Browse the repository at this point in the history
* add simd top and test

* testNum from 1 to 100

* address commnets for 3+1 csr
  • Loading branch information
xiaoling-yi authored Jan 24, 2024
1 parent 382770a commit c69eee0
Show file tree
Hide file tree
Showing 4 changed files with 440 additions and 0 deletions.
143 changes: 143 additions & 0 deletions src/main/scala/simd/CsrManager.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package simd

import chisel3._
import chisel3.util._

/** simplified csr read/write cmd
*
* @param csrAddrWidth
* csr registers address width
*/
class CsrReq(csrAddrWidth: Int) extends Bundle {
val data = UInt(32.W)
val addr = UInt(csrAddrWidth.W)
val write = Bool()
}

class CsrRsp extends Bundle {
val data = UInt(32.W)
}

/** This class represents the csr input and output ports of the streamer top
* module
*
* @param csrAddrWidth
* csr registers address width
*/
class CsrReqRspIO(csrAddrWidth: Int) extends Bundle {

val req = Flipped(Decoupled(new CsrReq(csrAddrWidth)))
val rsp = Decoupled(new CsrRsp)

}

/** This class represents the input and output ports of the CsrManager module.
* The input is connected to the SNAX CSR port. The output is connected to the
* streamer configuration port.
* @param csrNum
* the number of csr registers
* @param csrAddrWidth
* the width of the address
*/
class CsrManagerIO(
csrNum: Int,
csrAddrWidth: Int
) extends Bundle {

val csr_config_in = new CsrReqRspIO(csrAddrWidth)
val csr_config_out = Decoupled(Vec(csrNum, UInt(32.W)))

}

/** This class represents the CsrManager module. It contains the csr registers
* and the read and write control logic.
* @param csrNum
* the number of csr registers
* @param csrAddrWidth
* the width of the address
*/
class CsrManager(
csrNum: Int,
csrAddrWidth: Int
) extends Module
with RequireAsyncReset {

val io = IO(new CsrManagerIO(csrNum, csrAddrWidth))

// generate a vector of registers to store the csr state
val csr = RegInit(VecInit(Seq.fill(csrNum)(0.U(32.W))))

// read and write csr cmd
val read_csr = io.csr_config_in.req.fire && !io.csr_config_in.req.bits.write
val write_csr = io.csr_config_in.req.fire && io.csr_config_in.req.bits.write

// keep sending response to a read request until we receive the response ready signal
val keep_sending_csr_rsp = RegNext(
io.csr_config_in.rsp.valid && !io.csr_config_in.rsp.ready
)
// a register to store the read request response data until the request is successful
val csr_rsp_data_reg = RegInit(0.U(32.W))

// store the csr data for later output because the address only valid when io.csr.fire
csr_rsp_data_reg := Mux(
read_csr,
csr(io.csr_config_in.req.bits.addr),
csr_rsp_data_reg
)

// streamer configuration valid signal
val config_valid = WireInit(0.B)

// check if the csr address overflow (access certain csr that doesn't exist)
def startCsrAddr = (csrNum - 1).U

when(io.csr_config_in.req.fire) {

assert(
io.csr_config_in.req.bits.addr <= startCsrAddr,
"csr address overflow!"
)

}

// write req
when(write_csr) {
csr(io.csr_config_in.req.bits.addr) := io.csr_config_in.req.bits.data
}

// handle read requests: keep sending response data until the request succeeds
when(read_csr) {
io.csr_config_in.rsp.bits.data := csr(io.csr_config_in.req.bits.addr)
io.csr_config_in.rsp.valid := 1.B
}.elsewhen(keep_sending_csr_rsp) {
io.csr_config_in.rsp.bits.data := csr_rsp_data_reg
io.csr_config_in.rsp.valid := 1.B
}.otherwise {
io.csr_config_in.rsp.valid := 0.B
io.csr_config_in.rsp.bits.data := 0.U
}

// we are ready for a new request if two conditions hold:
// if we write to the config_valid register (the last one), the streamer must not be busy (io.csr_config_out.ready)
// if there is a read request in progress, we only accept new write requests
io.csr_config_in.req.ready := (io.csr_config_out.ready || !(io.csr_config_in.req.bits.addr === startCsrAddr)) && (!keep_sending_csr_rsp || io.csr_config_in.req.bits.write)

// a write/read to the last csr means the config is valid
config_valid := io.csr_config_in.req.fire && (io.csr_config_in.req.bits.addr === startCsrAddr)

// signals connected to the output ports
io.csr_config_out.bits <> csr
io.csr_config_out.valid <> config_valid

}

// Scala main function for generating CsrManager system verilog file
object CsrManager extends App {
emitVerilog(
new CsrManager(
SIMDConstant.csrNum,
SIMDConstant.csrAddrWidth
),
Array("--target-dir", "generated/csr_manager")
)
}
5 changes: 5 additions & 0 deletions src/main/scala/simd/Parameter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ object SIMDConstant {

// SIMD parallelism
def laneLen = 64

// csrManager parameters, we use 3 CSR for Post-processing SIMD configuration, + 1 for status CSR
def csrNum: Int = 3 + 1
def csrAddrWidth: Int = log2Up(csrNum)

}
64 changes: 64 additions & 0 deletions src/main/scala/simd/SIMDTop.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package simd

import chisel3._
import chisel3.util._

class SIMDTopIO() extends Bundle {
val csr = new CsrReqRspIO(SIMDConstant.csrAddrWidth)
val data = new SIMDDataIO()
}

// post-processing SIMD with uniformed interface: csr (snax side) and data ports (streamer side)
class SIMDTop() extends Module with RequireAsyncReset {

val io = IO(new SIMDTopIO())

val csrManager = Module(
new CsrManager(SIMDConstant.csrNum, SIMDConstant.csrAddrWidth)
)
val simd = Module(new SIMD())

// io.csr and csrManager input connection
csrManager.io.csr_config_in <> io.csr

// csrManager output and simd control port connection
// control signals
simd.io.ctrl.valid := csrManager.io.csr_config_out.valid
csrManager.io.csr_config_out.ready := simd.io.ctrl.ready

// splitting csrManager data ports to the simd configuration ports
// the meanings of these ports can be found at PE.scala
// the order is also the same as at PE.scala
// as each control input port is 8 bits so 4 control input port shares 1 csr
simd.io.ctrl.bits.input_zp_i := csrManager.io.csr_config_out
.bits(0)(7, 0)
.asSInt
simd.io.ctrl.bits.output_zp_i := csrManager.io.csr_config_out
.bits(0)(15, 8)
.asSInt

// this control input port is 32 bits, so it needs 1 csr
simd.io.ctrl.bits.multiplier_i := csrManager.io.csr_config_out.bits(2).asSInt

simd.io.ctrl.bits.shift_i :=
csrManager.io.csr_config_out.bits(0)(23, 16).asSInt
simd.io.ctrl.bits.max_int_i :=
csrManager.io.csr_config_out.bits(0)(31, 24).asSInt

simd.io.ctrl.bits.min_int_i :=
csrManager.io.csr_config_out.bits(1)(7, 0).asSInt

// this control input port is only 1 bit
simd.io.ctrl.bits.double_round_i :=
csrManager.io.csr_config_out.bits(1)(8).asBool

io.data <> simd.io.data

}

object SIMDTop extends App {
emitVerilog(
new (SIMDTop),
Array("--target-dir", "generated/simd")
)
}
Loading

0 comments on commit c69eee0

Please sign in to comment.